in core/src/main/java/com/jetbrains/youtrackdb/internal/core/db/tool/DatabaseCompare.java [654:864]
private void compareRecords(EntityHelper.RIDMapper ridMapper) {
listener.onMessage(
"\nStarting deep comparison record by record. This may take a few minutes. Wait please...");
var collectionNames1 = sessionOne.getCollectionNames();
for (final var collectionName : collectionNames1) {
// CHECK IF THE COLLECTION IS INCLUDED
final var collectionId1 = sessionOne.getCollectionIdByName(collectionName);
RecordIdInternal rid1 = null;
var physicalPositions = sessionOne.getStorage()
.ceilingPhysicalPositions(sessionOne, collectionId1, new PhysicalPosition(0),
Integer.MAX_VALUE);
var configuration1 = sessionOne.getStorageInfo().getConfiguration();
var configuration2 = sessionTwo.getStorageInfo().getConfiguration();
var storageType1 = sessionOne.getStorage().getType();
var storageType2 = sessionTwo.getStorage().getType();
long recordsCounter = 0;
while (physicalPositions.length > 0) {
for (var physicalPosition : physicalPositions) {
try {
recordsCounter++;
final var entity1 = new EntityImpl(sessionOne,
new RecordId(RID.COLLECTION_ID_INVALID, RID.COLLECTION_POS_INVALID));
final var entity2 = new EntityImpl(sessionTwo,
new RecordId(RID.COLLECTION_ID_INVALID, RID.COLLECTION_POS_INVALID));
final var position = physicalPosition.collectionPosition;
rid1 = new RecordId(collectionId1, position);
final RecordIdInternal rid2;
if (ridMapper == null) {
rid2 = rid1;
} else {
final var newRid = ridMapper.map(rid1);
if (newRid == null) {
rid2 = rid1;
} else
//noinspection ObjectAllocationInLoop
{
rid2 = new RecordId(newRid);
}
}
if (skipRecord(
rid1, rid2, configuration1, configuration2, storageType1, storageType2)) {
continue;
}
final var buffer1 = sessionOne.getStorage().readRecord(rid1);
final var buffer2 = sessionTwo.getStorage().readRecord(rid2);
if (buffer1.recordType() != buffer2.recordType()) {
listener.onMessage(
"\n- ERR: RID="
+ collectionId1
+ ":"
+ position
+ " recordType is different: "
+ (char) buffer1.recordType()
+ " <-> "
+ (char) buffer2.recordType());
++differences;
}
//noinspection StatementWithEmptyBody
if (buffer1.buffer() == null && buffer2.buffer() == null) {
// Both null so both equals
} else {
if (buffer1.buffer() == null) {
listener.onMessage(
"\n- ERR: RID="
+ collectionId1
+ ":"
+ position
+ " content is different: null <-> "
+ buffer2.buffer().length);
++differences;
} else {
if (buffer2.buffer() == null) {
listener.onMessage(
"\n- ERR: RID="
+ collectionId1
+ ":"
+ position
+ " content is different: "
+ buffer1.buffer().length
+ " <-> null");
++differences;
} else {
if (EntityHelper.isEntity(buffer1.recordType())) {
// ENTITY: TRY TO INSTANTIATE AND COMPARE
final var rec1 = (RecordAbstract) entity1;
rec1.unsetDirty();
final var rec3 = (RecordAbstract) entity1;
rec3.fromStream(buffer1.buffer());
final var rec = (RecordAbstract) entity2;
rec.unsetDirty();
final var rec2 = (RecordAbstract) entity2;
rec2.fromStream(buffer2.buffer());
if (rid1.toString().equals(configuration1.getSchemaRecordId())
&& rid1.toString().equals(configuration2.getSchemaRecordId())) {
convertSchemaDoc(entity1);
convertSchemaDoc(entity2);
}
if (!EntityHelper.hasSameContentOf(
entity1, sessionOne, entity2, sessionTwo, ridMapper)) {
listener.onMessage(
"\n- ERR: RID="
+ collectionId1
+ ":"
+ position
+ " entity content is different");
listener.onMessage("\n--- REC1: " + new String(buffer1.buffer()));
listener.onMessage("\n--- REC2: " + new String(buffer2.buffer()));
listener.onMessage("\n");
++differences;
}
} else {
if (buffer1.buffer().length != buffer2.buffer().length) {
// CHECK IF THE TRIMMED SIZE IS THE SAME
@SuppressWarnings("ObjectAllocationInLoop") final var rec1 = new String(
buffer1.buffer()).trim();
@SuppressWarnings("ObjectAllocationInLoop") final var rec2 = new String(
buffer2.buffer()).trim();
if (rec1.length() != rec2.length()) {
listener.onMessage(
"\n- ERR: RID="
+ collectionId1
+ ":"
+ position
+ " content length is different: "
+ buffer1.buffer().length
+ " <-> "
+ buffer2.buffer().length);
if (EntityHelper.isEntity(buffer2.recordType())) {
listener.onMessage("\n--- REC2: " + rec2);
}
listener.onMessage("\n");
++differences;
}
} else {
// CHECK BYTE PER BYTE
for (var b = 0; b < buffer1.buffer().length; ++b) {
if (buffer1.buffer()[b] != buffer2.buffer()[b]) {
listener.onMessage(
"\n- ERR: RID="
+ collectionId1
+ ":"
+ position
+ " content is different at byte #"
+ b
+ ": "
+ buffer1.buffer()[b]
+ " <-> "
+ buffer2.buffer()[b]);
listener.onMessage("\n--- REC1: " + new String(buffer1.buffer()));
listener.onMessage("\n--- REC2: " + new String(buffer2.buffer()));
listener.onMessage("\n");
++differences;
break;
}
}
}
}
}
}
}
} catch (RuntimeException e) {
LogManager.instance()
.error(this, "Error during data comparison of records with rid " + rid1, e);
throw e;
}
}
final var curPosition = physicalPositions;
physicalPositions = sessionOne.getStorage()
.higherPhysicalPositions(sessionOne, collectionId1,
curPosition[curPosition.length - 1], Integer.MAX_VALUE);
if (recordsCounter % 10000 == 0) {
listener.onMessage(
"\n"
+ recordsCounter
+ " records were processed for collection "
+ collectionName
+ " ...");
}
}
listener.onMessage(
"\nCollection comparison was finished, "
+ recordsCounter
+ " records were processed for collection "
+ collectionName
+ " ...");
}
}