final ReplayFilter replayFilter = ReplayFilter.create();
logger.info("Replaying {}", file.getPath());
CommitLogDescriptor desc = CommitLogDescriptor.fromFileName(file.getName());
final long segmentId = desc.id;
int version = desc.getMessagingVersion();
RandomAccessReader reader = RandomAccessReader.open(new File(file.getAbsolutePath()));
try
{
assert reader.length() <= Integer.MAX_VALUE;
int offset = getStartOffset(segmentId, version);
if (offset < 0)
{
logger.debug("skipping replay of fully-flushed {}", file);
return;
}
int prevEnd = 0;
main: while (true)
{
int end = prevEnd;
if (version < CommitLogDescriptor.VERSION_21)
end = Integer.MAX_VALUE;
else
{
do { end = readHeader(segmentId, end, reader); }
while (end < offset && end > prevEnd);
}
if (end < prevEnd)
break;
if (logger.isDebugEnabled())
logger.debug("Replaying {} between {} and {}", file, offset, end);
reader.seek(offset);
/* read the logs populate Mutation and apply */
while (reader.getPosition() < end && !reader.isEOF())
{
if (logger.isDebugEnabled())
logger.debug("Reading mutation at {}", reader.getFilePointer());
long claimedCRC32;
int serializedSize;
try
{
// any of the reads may hit EOF
serializedSize = reader.readInt();
if (serializedSize == LEGACY_END_OF_SEGMENT_MARKER)
{
logger.debug("Encountered end of segment marker at {}", reader.getFilePointer());
break main;
}
// Mutation must be at LEAST 10 bytes:
// 3 each for a non-empty Keyspace and Key (including the
// 2-byte length from writeUTF/writeWithShortLength) and 4 bytes for column count.
// This prevents CRC by being fooled by special-case garbage in the file; see CASSANDRA-2128
if (serializedSize < 10)
break main;
long claimedSizeChecksum = reader.readLong();
checksum.reset();
if (version < CommitLogDescriptor.VERSION_20)
checksum.update(serializedSize);
else
FBUtilities.updateChecksumInt(checksum, serializedSize);
if (checksum.getValue() != claimedSizeChecksum)
break main; // entry wasn't synced correctly/fully. that's
// ok.
if (serializedSize > buffer.length)
buffer = new byte[(int) (1.2 * serializedSize)];
reader.readFully(buffer, 0, serializedSize);
claimedCRC32 = reader.readLong();
}
catch (EOFException eof)
{
break main; // last CL entry didn't get completely written. that's ok.
}
checksum.update(buffer, 0, serializedSize);
if (claimedCRC32 != checksum.getValue())
{
// this entry must not have been fsynced. probably the rest is bad too,
// but just in case there is no harm in trying them (since we still read on an entry boundary)
continue;
}
/* deserialize the commit log entry */
FastByteArrayInputStream bufIn = new FastByteArrayInputStream(buffer, 0, serializedSize);
final Mutation mutation;
try
{
// assuming version here. We've gone to lengths to make sure what gets written to the CL is in
// the current version. so do make sure the CL is drained prior to upgrading a node.
mutation = Mutation.serializer.deserialize(new DataInputStream(bufIn), version, ColumnSerializer.Flag.LOCAL);
// doublecheck that what we read is [still] valid for the current schema
for (ColumnFamily cf : mutation.getColumnFamilies())
for (Cell cell : cf)
cf.getComparator().validate(cell.name());
}
catch (UnknownColumnFamilyException ex)
{
if (ex.cfId == null)
continue;
AtomicInteger i = invalidMutations.get(ex.cfId);
if (i == null)
{
i = new AtomicInteger(1);
invalidMutations.put(ex.cfId, i);
}
else
i.incrementAndGet();
continue;
}
catch (Throwable t)
{
File f = File.createTempFile("mutation", "dat");
DataOutputStream out = new DataOutputStream(new FileOutputStream(f));
try
{
out.write(buffer, 0, serializedSize);
}
finally
{
out.close();
}
String st = String.format("Unexpected error deserializing mutation; saved to %s and ignored. This may be caused by replaying a mutation against a table with the same name but incompatible schema. Exception follows: ",
f.getAbsolutePath());
logger.error(st, t);
continue;
}
if (logger.isDebugEnabled())
logger.debug("replaying mutation for {}.{}: {}", mutation.getKeyspaceName(), ByteBufferUtil.bytesToHex(mutation.key()), "{" + StringUtils.join(mutation.getColumnFamilies().iterator(), ", ") + "}");
final long entryLocation = reader.getFilePointer();
Runnable runnable = new WrappedRunnable()
{
public void runMayThrow() throws IOException
{
if (Schema.instance.getKSMetaData(mutation.getKeyspaceName()) == null)