Index: oak-segment-tar/src/main/java/org/apache/jackrabbit/oak/segment/SegmentBufferWriter.java =================================================================== --- oak-segment-tar/src/main/java/org/apache/jackrabbit/oak/segment/SegmentBufferWriter.java (revision 1839678) +++ oak-segment-tar/src/main/java/org/apache/jackrabbit/oak/segment/SegmentBufferWriter.java (working copy) @@ -37,10 +37,12 @@ import static org.apache.jackrabbit.oak.segment.SegmentId.isDataSegmentId; import static org.apache.jackrabbit.oak.segment.SegmentVersion.LATEST_VERSION; +import java.io.ByteArrayOutputStream; import java.io.IOException; import java.util.Collection; import java.util.Set; +import org.apache.commons.io.HexDump; import org.apache.jackrabbit.oak.segment.RecordNumbers.Entry; import org.apache.jackrabbit.oak.segment.file.tar.GCGeneration; import org.jetbrains.annotations.NotNull; @@ -283,6 +285,63 @@ dirty = true; } + private String dumpSegmentBuffer() { + StringBuilder builder = new StringBuilder(); + builder.append("Segment buffer corruption detected\n"); + builder.append("Segment ID: "); + + if (segment == null) { + builder.append("None"); + } else { + builder.append(segment.getSegmentId()); + } + + builder.append("\n"); + builder.append("Full generation: ").append(gcGeneration.getFullGeneration()).append("\n"); + builder.append("Tail generation: ").append(gcGeneration.getGeneration()).append("\n"); + builder.append("Compacted: ").append(gcGeneration.isCompacted()).append("\n"); + builder.append("References:\n"); + + if (segmentReferences.size() == 0) { + builder.append("\tNone\n"); + } else { + for (SegmentId segmentId : segmentReferences) { + builder.append("\t").append(segmentId).append("\n"); + } + } + + builder.append("Records:\n"); + + if (recordNumbers.size() == 0) { + builder.append("\tNone\n"); + } else { + for (Entry entry : recordNumbers) { + builder + .append("\t") + .append("number=") + .append(entry.getRecordNumber()) + .append(", type=") + .append(entry.getType()) + .append(", offset=") + .append(entry.getOffset()) + .append("\n"); + } + } + + builder.append("Length: ").append(length).append("\n"); + + try { + ByteArrayOutputStream bos = new ByteArrayOutputStream(); + HexDump.dump(buffer, 0, bos, 0); + builder.append(bos.toString()); + } catch (IOException e) { + builder.append("Buffer contents not available"); + LOG.warn("Unable to dump buffer contents", e); + } + + return builder.toString(); + } + /** * Adds a segment header to the buffer and writes a segment to the segment * store. This is done automatically (called from prepare) when there is not @@ -302,6 +361,7 @@ int totalLength = align(HEADER_SIZE + referencedSegmentIdCount * SEGMENT_REFERENCE_SIZE + recordNumberCount * RECORD_SIZE + length, 16); if (totalLength > buffer.length) { + LOG.warn(dumpSegmentBuffer()); throw new IllegalStateException(String.format( "Too much data for a segment %s (referencedSegmentIdCount=%d, recordNumberCount=%d, length=%d, totalLength=%d)", segment.getSegmentId(), referencedSegmentIdCount, recordNumberCount, length, totalLength));