mirror of
https://github.com/vitorpamplona/amethyst.git
synced 2026-10-06 11:48:24 +00:00
refactor(commons): EventCache.filter returns a List, not a SortedSet
java.util.SortedSet has no common equivalent, and it was in the cache's own signature plus both *ListMatchingFilter observables. The ripple was smaller than the plan assumed: ICacheProvider never declared filter and DesktopLocalCache has no equivalent, so the type was EventCache-local. The result is now a List carrying the same order the SortedSet did — CreatedAtIdHexComparator, newest first. That order is load-bearing rather than incidental: the napplet gateway answers REQs out of this call, and NIP-01 has relays return events newest first. The comparator was also doing something the type change had to preserve. It defines uniqueness by reference, so the sorted set collapsed duplicate references — which a filter repeating a kind produces, since the addressable scan runs once per kind. toSet() before sorting keeps exactly that: Note declares no equals(), so a plain Set de-duplicates by reference too. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01KkULS5SVq4GHDdoCzajKi8
This commit is contained in:
+10
-5
@@ -422,7 +422,6 @@ import kotlinx.coroutines.flow.buffer
|
||||
import kotlinx.coroutines.flow.callbackFlow
|
||||
import kotlinx.coroutines.flow.map
|
||||
import kotlinx.coroutines.launch
|
||||
import java.util.SortedSet
|
||||
|
||||
/**
|
||||
* The in-memory event store: every `Note`, `User` and `Channel` the app has consumed, plus the
|
||||
@@ -590,7 +589,7 @@ open class EventCache :
|
||||
}
|
||||
}
|
||||
|
||||
fun filter(filter: Filter): SortedSet<Note> = filter(filter) { true }
|
||||
fun filter(filter: Filter): List<Note> = filter(filter) { true }
|
||||
|
||||
/**
|
||||
* Every note matching [filter]'s NIP-01 fields that also satisfies [predicate].
|
||||
@@ -603,7 +602,7 @@ open class EventCache :
|
||||
fun filter(
|
||||
filter: Filter,
|
||||
predicate: (Note) -> Boolean,
|
||||
): SortedSet<Note> {
|
||||
): List<Note> {
|
||||
val byKinds = filter.kinds?.filter { it.isAddressable() || it.isReplaceable() }
|
||||
|
||||
val addressableMatches =
|
||||
@@ -642,14 +641,20 @@ open class EventCache :
|
||||
}
|
||||
}
|
||||
|
||||
val all = (addressableMatches + noteMatches).toSortedSet(CreatedAtIdHexComparator)
|
||||
// toSet() before sorting: a filter that repeats a kind scans that kind twice, and the
|
||||
// SortedSet this used to return collapsed the repeats. Note declares no equals(), so a
|
||||
// plain Set is the same reference-identity de-duplication that comparator gave.
|
||||
//
|
||||
// The order is the comparator's, newest first, and it is load-bearing: the napplet
|
||||
// gateway answers REQs out of this, and NIP-01 has relays return events newest first.
|
||||
val all = (addressableMatches + noteMatches).toSet().sortedWith(CreatedAtIdHexComparator)
|
||||
val limit = filter.limit ?: return all
|
||||
|
||||
// Sorted first, then cut. Both halves arrive in hash-walk order, so taking before sorting
|
||||
// dropped whichever matches the walk happened to reach last — the newest ones as often as
|
||||
// not — and a query with 200 addressable matches never showed a single regular note.
|
||||
if (all.size <= limit) return all
|
||||
return all.asSequence().take(limit).toCollection(sortedSetOf(CreatedAtIdHexComparator))
|
||||
return all.take(limit)
|
||||
}
|
||||
|
||||
fun observeNotes(filter: Filter): Flow<List<Note>> =
|
||||
|
||||
+1
-2
@@ -26,7 +26,6 @@ import com.vitorpamplona.quartz.nip01Core.core.AddressableEvent
|
||||
import com.vitorpamplona.quartz.nip01Core.core.Event
|
||||
import com.vitorpamplona.quartz.nip01Core.core.HexKey
|
||||
import com.vitorpamplona.quartz.nip01Core.relay.filters.Filter
|
||||
import java.util.SortedSet
|
||||
import java.util.concurrent.ConcurrentHashMap
|
||||
import java.util.concurrent.ConcurrentSkipListSet
|
||||
|
||||
@@ -69,7 +68,7 @@ import java.util.concurrent.ConcurrentSkipListSet
|
||||
*/
|
||||
class EventListMatchingFilter<T : Event>(
|
||||
private val filter: Filter,
|
||||
private val atOnce: (filter: Filter) -> SortedSet<Note>,
|
||||
private val atOnce: (filter: Filter) -> List<Note>,
|
||||
private val update: (List<T>) -> Unit,
|
||||
) : Observable {
|
||||
/** A note plus the sort key captured at insertion time, so ordering never depends on mutable state. */
|
||||
|
||||
+1
-2
@@ -26,7 +26,6 @@ import com.vitorpamplona.quartz.nip01Core.core.AddressableEvent
|
||||
import com.vitorpamplona.quartz.nip01Core.core.Event
|
||||
import com.vitorpamplona.quartz.nip01Core.core.HexKey
|
||||
import com.vitorpamplona.quartz.nip01Core.relay.filters.Filter
|
||||
import java.util.SortedSet
|
||||
import java.util.concurrent.ConcurrentHashMap
|
||||
import java.util.concurrent.ConcurrentSkipListSet
|
||||
|
||||
@@ -68,7 +67,7 @@ import java.util.concurrent.ConcurrentSkipListSet
|
||||
*/
|
||||
class NoteListMatchingFilter(
|
||||
private val filter: Filter,
|
||||
private val atOnce: (filter: Filter) -> SortedSet<Note>,
|
||||
private val atOnce: (filter: Filter) -> List<Note>,
|
||||
private val update: (List<Note>) -> Unit,
|
||||
) : Observable {
|
||||
/** A note plus the sort key captured at insertion time, so ordering never depends on mutable state. */
|
||||
|
||||
+1
-2
@@ -27,7 +27,6 @@ import com.vitorpamplona.quartz.nip01Core.core.Event
|
||||
import com.vitorpamplona.quartz.nip01Core.relay.filters.Filter
|
||||
import com.vitorpamplona.quartz.nip89AppHandlers.definition.AppDefinitionEvent
|
||||
import com.vitorpamplona.quartz.utils.EventFactory
|
||||
import java.util.TreeSet
|
||||
import java.util.concurrent.CountDownLatch
|
||||
import java.util.concurrent.atomic.AtomicReference
|
||||
import kotlin.concurrent.thread
|
||||
@@ -65,7 +64,7 @@ class EventListMatchingFilterTest {
|
||||
sink: (List<Event>) -> Unit,
|
||||
) = EventListMatchingFilter<Event>(
|
||||
filter = withFilter,
|
||||
atOnce = { TreeSet(CreatedAtIdHexComparator) },
|
||||
atOnce = { emptyList() },
|
||||
update = sink,
|
||||
)
|
||||
|
||||
|
||||
+1
-2
@@ -27,7 +27,6 @@ import com.vitorpamplona.quartz.nip01Core.core.Event
|
||||
import com.vitorpamplona.quartz.nip01Core.relay.filters.Filter
|
||||
import com.vitorpamplona.quartz.nip89AppHandlers.definition.AppDefinitionEvent
|
||||
import com.vitorpamplona.quartz.utils.EventFactory
|
||||
import java.util.TreeSet
|
||||
import java.util.concurrent.CountDownLatch
|
||||
import java.util.concurrent.atomic.AtomicReference
|
||||
import kotlin.concurrent.thread
|
||||
@@ -67,7 +66,7 @@ class NoteListMatchingFilterTest {
|
||||
sink: (List<Note>) -> Unit,
|
||||
) = NoteListMatchingFilter(
|
||||
filter = withFilter,
|
||||
atOnce = { TreeSet(CreatedAtIdHexComparator) },
|
||||
atOnce = { emptyList() },
|
||||
update = sink,
|
||||
)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user