mirror of
https://github.com/permissionlesstech/bitchat.git
synced 2026-10-25 07:41:41 +00:00
Narrow dedup cache fix to generation correctness
This commit is contained in:
parent
75223bf266
commit
4816a13244
@ -28,7 +28,6 @@ final class LRUDeduplicationCache<Value> {
|
||||
private var map: [String: StoredEntry] = [:]
|
||||
private var order: [OrderEntry] = []
|
||||
private var head: Int = 0
|
||||
private var staleNodeCount: Int = 0
|
||||
private var nextGeneration: UInt64 = 0
|
||||
private let capacity: Int
|
||||
|
||||
@ -44,11 +43,6 @@ final class LRUDeduplicationCache<Value> {
|
||||
map.count
|
||||
}
|
||||
|
||||
#if DEBUG
|
||||
/// Order-node storage exposed only for bounded-storage regression tests.
|
||||
var _orderStorageCountForTesting: Int { order.count }
|
||||
#endif
|
||||
|
||||
/// Checks if a key exists in the cache
|
||||
func contains(_ key: String) -> Bool {
|
||||
map[key] != nil
|
||||
@ -71,14 +65,11 @@ final class LRUDeduplicationCache<Value> {
|
||||
}
|
||||
|
||||
trimIfNeeded()
|
||||
compactIfNeeded()
|
||||
}
|
||||
|
||||
/// Removes a specific key from the cache
|
||||
func remove(_ key: String) {
|
||||
guard map.removeValue(forKey: key) != nil else { return }
|
||||
staleNodeCount += 1
|
||||
compactIfNeeded()
|
||||
map.removeValue(forKey: key)
|
||||
}
|
||||
|
||||
/// Clears all entries from the cache
|
||||
@ -86,7 +77,6 @@ final class LRUDeduplicationCache<Value> {
|
||||
map.removeAll()
|
||||
order.removeAll()
|
||||
head = 0
|
||||
staleNodeCount = 0
|
||||
nextGeneration = 0
|
||||
}
|
||||
|
||||
@ -103,11 +93,15 @@ final class LRUDeduplicationCache<Value> {
|
||||
let candidate = order[head]
|
||||
head += 1
|
||||
|
||||
// Periodically compact the consumed prefix, matching the previous
|
||||
// cache behavior without scanning unconsumed order nodes.
|
||||
if head >= 32 && head * 2 >= order.count {
|
||||
order.removeFirst(head)
|
||||
head = 0
|
||||
}
|
||||
|
||||
guard let liveEntry = map[candidate.key],
|
||||
liveEntry.generation == candidate.generation else {
|
||||
if staleNodeCount > 0 {
|
||||
staleNodeCount -= 1
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
@ -116,24 +110,6 @@ final class LRUDeduplicationCache<Value> {
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
private func compactIfNeeded() {
|
||||
let unconsumedCount = order.count - head
|
||||
let shouldDropConsumedPrefix = head >= 32 && head * 2 >= order.count
|
||||
let shouldRemoveStaleNodes = staleNodeCount >= 32 && staleNodeCount * 2 >= unconsumedCount
|
||||
|
||||
guard shouldDropConsumedPrefix || shouldRemoveStaleNodes else { return }
|
||||
|
||||
if shouldRemoveStaleNodes {
|
||||
order = order[head...].filter { candidate in
|
||||
map[candidate.key]?.generation == candidate.generation
|
||||
}
|
||||
staleNodeCount = 0
|
||||
} else {
|
||||
order.removeFirst(head)
|
||||
}
|
||||
head = 0
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Content Normalizer
|
||||
|
||||
@ -166,31 +166,6 @@ struct LRUDeduplicationCacheTests {
|
||||
#expect(cache.contains("c"))
|
||||
}
|
||||
|
||||
@Test func repeatedRemovalAndReinsertionCompactsStaleNodes() {
|
||||
let cache = LRUDeduplicationCache<Int>(capacity: 3)
|
||||
cache.record("a", value: 0)
|
||||
|
||||
for value in 1...1_001 {
|
||||
cache.remove("a")
|
||||
cache.record("a", value: value)
|
||||
}
|
||||
|
||||
#expect(cache.count == 1)
|
||||
#expect(cache.value(for: "a") == 1_001)
|
||||
#if DEBUG
|
||||
#expect(cache._orderStorageCountForTesting <= 32)
|
||||
#endif
|
||||
|
||||
cache.record("b", value: 1)
|
||||
cache.record("c", value: 2)
|
||||
cache.record("d", value: 3)
|
||||
|
||||
#expect(!cache.contains("a"))
|
||||
#expect(cache.contains("b"))
|
||||
#expect(cache.contains("c"))
|
||||
#expect(cache.contains("d"))
|
||||
}
|
||||
|
||||
// MARK: - Edge Cases
|
||||
|
||||
@Test func emptyKey_works() {
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user