fun refactorMakeLinkTablesConsistent()

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
                }
            }
        }
    }