in entity-store/src/main/kotlin/jetbrains/exodus/entitystore/PersistentEntityStoreRefactorings.kt [441:575]
fun refactorMakeLinkTablesConsistent(internalSettings: Store) {
store.executeInReadonlyTransaction { txn ->
txn as PersistentStoreTransaction
for (entityType in store.getEntityTypes(txn)) {
logInfo("Refactoring making links' tables consistent for [$entityType]")
runReadonlyTransactionSafeForEntityType(entityType) {
val redundantLinks = ArrayList<Pair<ByteIterable, ByteIterable>>()
val deleteLinks = ArrayList<Pair<ByteIterable, ByteIterable>>()
val entityTypeId = store.getEntityTypeId(txn, entityType, false)
val linksTable = store.getLinksTable(txn, entityTypeId)
val envTxn = txn.environmentTransaction
val all = (txn.getAll(entityType) as EntityIterableBase).toSet(txn)
val linkFilter = PackedLongHashSet()
val redundantLinkTypes = IntHashSet()
val deletedLinkTypes = IntHashSet()
val deletedLinkIds = IntHashSet()
linksTable.getFirstIndexCursor(envTxn).use { cursor ->
while (cursor.next) {
val first = cursor.key
val second = cursor.value
var linkValue: LinkValue? = null
val localId = LongBinding.compressedEntryToLong(first)
if (!all.contains(entityTypeId, localId)) {
try {
linkValue = LinkValue.entryToLinkValue(second)
deletedLinkTypes.add(linkValue.entityId.typeId)
deletedLinkIds.add(linkValue.linkId)
} catch (_: ExodusException) {
} catch (_: ArrayIndexOutOfBoundsException) {
}
do {
deleteLinks.add(ArrayByteIterable(first) to ArrayByteIterable(second))
} while (cursor.nextDup)
continue
} else {
linkFilter.add((first.hashCode().toLong() shl 31) + second.hashCode().toLong())
}
if (linkValue == null) {
try {
linkValue = LinkValue.entryToLinkValue(second)
} catch (ignore: ArrayIndexOutOfBoundsException) {
deleteLinks.add(ArrayByteIterable(first) to ArrayByteIterable(second))
}
}
if (linkValue != null) {
val targetEntityId = linkValue.entityId
// if target doesn't exist
if (store.getLastVersion(txn, targetEntityId) < 0) {
deletedLinkTypes.add(targetEntityId.typeId)
deletedLinkIds.add(linkValue.linkId)
deleteLinks.add(ArrayByteIterable(first) to ArrayByteIterable(second))
continue
} else {
linkFilter.add((first.hashCode().toLong() shl 31) + second.hashCode().toLong())
}
if (!linksTable.contains2(envTxn, first, second)) {
redundantLinkTypes.add(targetEntityId.typeId)
redundantLinks.add(ArrayByteIterable(first) to ArrayByteIterable(second))
}
}
}
}
if (redundantLinks.isNotEmpty()) {
store.environment.executeInExclusiveTransaction { txn ->
for (badLink in redundantLinks) {
linksTable.put(txn, badLink.first, badLink.second)
}
}
logInfo(redundantLinks.size.toString() + " missing links found for [" + entityType + ']')
redundantLinks.clear()
}
linksTable.getSecondIndexCursor(envTxn).use { cursor ->
while (cursor.next) {
val second = cursor.key
val first = cursor.value
if (!linkFilter.contains((first.hashCode().toLong() shl 31) + second.hashCode().toLong())) {
if (!linksTable.contains(envTxn, first, second)) {
redundantLinks.add(ArrayByteIterable(first) to ArrayByteIterable(second))
}
}
}
}
val redundantLinksSize = redundantLinks.size
val deleteLinksSize = deleteLinks.size
if (redundantLinksSize > 0 || deleteLinksSize > 0) {
store.environment.executeInExclusiveTransaction { txn ->
for (redundantLink in redundantLinks) {
deletePair(
linksTable.getSecondIndexCursor(txn),
redundantLink.first,
redundantLink.second
)
}
for (deleteLink in deleteLinks) {
deletePair(linksTable.getFirstIndexCursor(txn), deleteLink.first, deleteLink.second)
deletePair(linksTable.getSecondIndexCursor(txn), deleteLink.second, deleteLink.first)
}
}
if (logger.isInfoEnabled) {
if (redundantLinksSize > 0) {
val redundantLinkTypeNames = ArrayList<String>(redundantLinkTypes.size)
for (typeId in redundantLinkTypes) {
redundantLinkTypeNames.add(store.getEntityType(txn, typeId))
}
logInfo("$redundantLinksSize redundant links found and fixed for [$entityType] and targets: $redundantLinkTypeNames")
}
if (deleteLinksSize > 0) {
val deletedLinkTypeNames = ArrayList<String>(deletedLinkTypes.size)
for (typeId in deletedLinkTypes) {
try {
val entityTypeName = store.getEntityType(txn, typeId)
deletedLinkTypeNames.add(entityTypeName)
} catch (t: Throwable) {
// ignore
}
}
val deletedLinkIdsNames = ArrayList<String>(deletedLinkIds.size)
for (typeId in deletedLinkIds) {
try {
val linkName = store.getLinkName(txn, typeId)
linkName?.let { deletedLinkIdsNames.add(linkName) }
} catch (t: Throwable) {
// ignore
}
}
logInfo("$deleteLinksSize phantom links found and fixed for [$entityType] and targets: $deletedLinkTypeNames")
logInfo("Link types: $deletedLinkIdsNames")
}
}
}
Settings.delete(internalSettings, "Link null-indices present") // reset link null indices
}
}
}
}