mirror of
https://github.com/permissionlesstech/bitchat-android.git
synced 2026-09-12 04:53:25 +00:00
Merge pull request #866 from Chessing234/fix-dedup-total-checks-race
fix: synchronize totalChecks increment in NostrEventDeduplicator
This commit is contained in:
commit
945c9e025b
@ -79,9 +79,9 @@ class NostrEventDeduplicator(
|
||||
* @return true if the event is a duplicate (already seen), false if it's new
|
||||
*/
|
||||
fun isDuplicate(eventId: String): Boolean {
|
||||
totalChecks++
|
||||
|
||||
synchronized(lruLock) {
|
||||
totalChecks++
|
||||
|
||||
val existingNode = nodeMap[eventId]
|
||||
|
||||
if (existingNode != null) {
|
||||
|
||||
@ -0,0 +1,66 @@
|
||||
package com.bitchat.android.nostr
|
||||
|
||||
import org.junit.Assert.assertEquals
|
||||
import org.junit.Assert.assertTrue
|
||||
import org.junit.Test
|
||||
import java.util.concurrent.CountDownLatch
|
||||
import java.util.concurrent.Executors
|
||||
import java.util.concurrent.TimeUnit
|
||||
|
||||
class NostrEventDeduplicatorTest {
|
||||
@Test
|
||||
fun `isDuplicate flags a repeated event id`() {
|
||||
val deduplicator = NostrEventDeduplicator(maxCapacity = 10)
|
||||
|
||||
assertEquals(false, deduplicator.isDuplicate("event-1"))
|
||||
assertEquals(true, deduplicator.isDuplicate("event-1"))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `capacity evicts the least recently used event id`() {
|
||||
val deduplicator = NostrEventDeduplicator(maxCapacity = 2)
|
||||
|
||||
deduplicator.isDuplicate("a")
|
||||
deduplicator.isDuplicate("b")
|
||||
deduplicator.isDuplicate("c") // evicts "a"
|
||||
|
||||
assertTrue(deduplicator.contains("b"))
|
||||
assertTrue(deduplicator.contains("c"))
|
||||
assertEquals(false, deduplicator.contains("a"))
|
||||
}
|
||||
|
||||
/**
|
||||
* totalChecks used to be incremented outside the lruLock that guards every other
|
||||
* mutation in this class, so concurrent callers could race on the read-modify-write
|
||||
* and lose increments. Every other counter (duplicateCount, evictionCount) was
|
||||
* already incremented under the lock, which is why only this one drifted.
|
||||
*/
|
||||
@Test
|
||||
fun `totalChecks counts every call exactly once under concurrent access`() {
|
||||
val deduplicator = NostrEventDeduplicator(maxCapacity = 10_000)
|
||||
val threadCount = 8
|
||||
val checksPerThread = 2_000
|
||||
val executor = Executors.newFixedThreadPool(threadCount)
|
||||
val start = CountDownLatch(1)
|
||||
val done = CountDownLatch(threadCount)
|
||||
|
||||
repeat(threadCount) { threadIndex ->
|
||||
executor.submit {
|
||||
start.await()
|
||||
repeat(checksPerThread) { callIndex ->
|
||||
deduplicator.isDuplicate("thread-$threadIndex-event-$callIndex")
|
||||
}
|
||||
done.countDown()
|
||||
}
|
||||
}
|
||||
|
||||
start.countDown()
|
||||
assertTrue(done.await(10, TimeUnit.SECONDS))
|
||||
executor.shutdown()
|
||||
|
||||
assertEquals(
|
||||
(threadCount * checksPerThread).toLong(),
|
||||
deduplicator.getStats().totalChecks
|
||||
)
|
||||
}
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user