in core/src/main/java/com/jetbrains/youtrackdb/internal/core/db/tool/GraphRepair.java [311:482]
protected void repairVertices(
final DatabaseSession session,
final RepairStats stats,
final CommandOutputListener outputListener,
final Map<String, List<String>> options,
final boolean checkOnly) {
final var db = (DatabaseSessionInternal) session;
final Metadata metadata = db.getMetadata();
final Schema schema = metadata.getSchema();
final var vertexClass = schema.getClass(SchemaClass.VERTEX_CLASS_NAME);
if (vertexClass != null) {
final var countVertices = db.countClass(vertexClass.getName());
session.executeInTx(
transaction -> {
var skipVertices = 0L;
if (options != null && options.get("-skipVertices") != null) {
skipVertices = Long.parseLong(options.get("-skipVertices").getFirst());
}
message(outputListener, "Scanning " + countVertices + " vertices...\n");
var parsedVertices = new long[]{0L};
final var beginTime = System.currentTimeMillis();
try (var vertexIterator = db.browseClass(vertexClass.getName())) {
while (vertexIterator.hasNext() && !Thread.currentThread().isInterrupted()) {
var vertex = vertexIterator.next();
parsedVertices[0]++;
if (skipVertices > 0 && parsedVertices[0] <= skipVertices) {
continue;
}
var vertexCorrupted = false;
stats.scannedVertices++;
if (eventListener != null) {
eventListener.onScannedVertex(vertex);
}
if (outputListener != null && stats.scannedVertices % 100000 == 0) {
var speedPerSecond =
(long)
(parsedVertices[0] / ((System.currentTimeMillis() - beginTime) / 1000.0));
if (speedPerSecond < 1) {
speedPerSecond = 1;
}
final var remaining = (countVertices - parsedVertices[0]) / speedPerSecond;
message(
outputListener,
"+ vertices: scanned "
+ stats.scannedVertices
+ ", repaired "
+ stats.repairedVertices
+ " (estimated remaining time "
+ remaining
+ " secs)\n");
}
if (!vertex.isVertex()) {
return;
}
for (var fieldName : vertex.getPropertyNamesInternal(false, false)) {
final var connection =
VertexEntityImpl.getConnection(
db.getMetadata().getSchema(), Direction.BOTH, fieldName);
if (connection == null) {
continue;
}
final var fieldValue = vertex.getPropertyInternal(fieldName);
if (fieldValue != null) {
switch (fieldValue) {
case Identifiable identifiable -> {
if (isEdgeBroken(db,
vertex,
fieldName,
connection.getKey(),
identifiable,
stats)) {
vertexCorrupted = true;
if (!checkOnly) {
vertex.setProperty(fieldName, null);
} else {
message(
outputListener,
"+ found corrupted vertex "
+ vertex
+ " the property "
+ fieldName
+ " could be removed\n");
}
}
}
case Collection<?> coll -> {
for (var it = coll.iterator(); it.hasNext(); ) {
final var o = it.next();
if (isEdgeBroken(db,
vertex, fieldName, connection.getKey(), (Identifiable) o,
stats)) {
vertexCorrupted = true;
if (!checkOnly) {
it.remove();
} else {
message(
outputListener,
"+ found corrupted vertex "
+ vertex
+ " the edge should be removed from property "
+ fieldName
+ " (collection)\n");
}
}
}
}
case LinkBag ridbag -> {
// In case of ridbags force save for trigger eventual conversions
if (ridbag.isEmpty()) {
vertex.removePropertyInternal(fieldName);
} else if (!ridbag.isEmbedded()
&& ridbag.size()
< GlobalConfiguration.LINK_COLLECTION_BTREE_TO_EMBEDDED_THRESHOLD
.getValueAsInteger()) {
vertex.setDirty();
}
for (Iterator<?> it = ridbag.iterator(); it.hasNext(); ) {
final var o = it.next();
if (isEdgeBroken(db,
vertex, fieldName, connection.getKey(), (Identifiable) o,
stats)) {
vertexCorrupted = true;
if (!checkOnly) {
it.remove();
} else {
message(
outputListener,
"+ found corrupted vertex "
+ vertex
+ " the edge should be removed from property "
+ fieldName
+ " (ridbag)\n");
}
}
}
}
default -> {
}
}
}
}
if (vertexCorrupted) {
stats.repairedVertices++;
if (eventListener != null) {
eventListener.onRepairedVertex(vertex);
}
message(outputListener, "+ repaired corrupted vertex " + vertex + "\n");
} else if (vertex.isDirty() && !checkOnly) {
message(outputListener, "+ optimized vertex " + vertex + "\n");
}
}
}
message(outputListener, "Scanning vertices completed\n");
});
}
}