private void shiftHashLookupEntries() { VanillaChronicleHash<?, ?, ?, ?> h = mh.h(); CompactOffHeapLinearHashTable hl = h.hashLookup; long hlAddr = s.tierBaseAddr; long hlPos = 0; long steps = 0; do { long hlEntry = hl.readEntry(hlAddr, hlPos); if (!hl.empty(hlEntry)) { long searchKey = hl.key(hlEntry); long hlHolePos = hl.hlPos(searchKey); while (hlHolePos != hlPos) { long hlHoleEntry = hl.readEntry(hlAddr, hlHolePos); if (hl.empty(hlHoleEntry)) { hl.writeEntry(hlAddr, hlHolePos, hlEntry); if (hl.remove(hlAddr, hlPos) != hlPos) { hlPos = hl.stepBack(hlPos); steps--; } break; } hlHolePos = hl.step(hlHolePos); } } hlPos = hl.step(hlPos); steps++; } while (hlPos != 0 || steps == 0); }
public int recoverTier(int segmentIndex) { s.freeList.clearAll(); Logger log = lh.LOG; VanillaChronicleHash<?, ?, ?, ?> h = mh.h(); CompactOffHeapLinearHashTable hl = h.hashLookup; long hlAddr = s.tierBaseAddr; long validEntries = 0; long hlPos = 0; do { long hlEntry = hl.readEntry(hlAddr, hlPos); nextHlPos: if (!hl.empty(hlEntry)) { // (*) hl.clearEntry(hlAddr, hlPos); if (validEntries >= h.maxEntriesPerHashLookup) { log.error( "Too many entries in tier with index {}, max is {}", s.tierIndex, h.maxEntriesPerHashLookup); break nextHlPos; } long searchKey = hl.key(hlEntry); long entryPos = hl.value(hlEntry); int si = checkEntry(searchKey, entryPos, segmentIndex); if (si < 0) { break nextHlPos; } else { s.freeList.setRange(entryPos, entryPos + e.entrySizeInChunks); segmentIndex = si; } // The entry has passed all checks, re-insert: long startInsertPos = hl.hlPos(searchKey); long insertPos = startInsertPos; do { long hlInsertEntry = hl.readEntry(hlAddr, insertPos); if (hl.empty(hlInsertEntry)) { hl.writeEntry(hlAddr, insertPos, hl.entry(searchKey, entryPos)); validEntries++; break nextHlPos; } if (insertPos == hlPos) { // means we made a whole loop, without finding a hole to re-insert entry, // even if hashLookup was corrupted and all slots are dirty now, at least // the slot cleared at (*) should be clear, if it is dirty, only // a concurrent modification thread could occupy it throw new ChronicleHashRecoveryFailedException( "Concurrent modification of ChronicleMap at " + h.file() + " while recovery procedure is in progress"); } checkDuplicateKeys: if (hl.key(hlInsertEntry) == searchKey) { long anotherEntryPos = hl.value(hlInsertEntry); if (anotherEntryPos == entryPos) { validEntries++; break nextHlPos; } long currentKeyOffset = e.keyOffset; long currentKeySize = e.keySize; int currentEntrySizeInChunks = e.entrySizeInChunks; if (insertPos >= 0 && insertPos < hlPos) { // insertPos already checked e.readExistingEntry(anotherEntryPos); } else if (checkEntry(searchKey, anotherEntryPos, segmentIndex) < 0) { break checkDuplicateKeys; } if (e.keySize == currentKeySize && BytesUtil.bytesEqual( s.segmentBS, currentKeyOffset, s.segmentBS, e.keyOffset, currentKeySize)) { log.error( "Entries with duplicate keys within a tier: " + "at pos {} and {} with key {}, first value is {}", entryPos, anotherEntryPos, e.key(), e.value()); s.freeList.clearRange(entryPos, entryPos + currentEntrySizeInChunks); break nextHlPos; } } insertPos = hl.step(insertPos); } while (insertPos != startInsertPos); throw new ChronicleHashRecoveryFailedException( "HashLookup overflow should never occur. " + "It might also be concurrent access to ChronicleMap at " + h.file() + " while recovery procedure is in progress"); } hlPos = hl.step(hlPos); } while (hlPos != 0); shiftHashLookupEntries(); return segmentIndex; }