refactor(cache): P3 simplifications — remove dead code, reduce abstractions

- Replace SubscriptionHealth map + data class with single lastEventAt
  Long? timestamp (only consumer was RelayHealthIndicator taking max())
- Replace consumer HashMap registry with when block (safe casts,
  compiler-checked, 9 kinds doesn't benefit from O(1) lookup)
- Remove dead loadReactionsForNotes() (superseded by requestInteractions)
- Remove unused BoundedLargeCache methods: containsKey, isEmpty,
  mapNotNull, forEach (zero external callers)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
nrobi144
2026-03-23 12:07:22 +02:00
parent 4f5665a2bd
commit 0621e8f7c2
5 changed files with 55 additions and 83 deletions
@@ -736,7 +736,7 @@ fun MainContent(
Row(Modifier.fillMaxSize().weight(1f)) {
when (layoutMode) {
LayoutMode.SINGLE_PANE -> {
val healthMap by subscriptionsCoordinator.subscriptionHealth.collectAsState()
val lastRelayEvent by subscriptionsCoordinator.lastEventAt.collectAsState()
SinglePaneLayout(
relayManager = relayManager,
localCache = localCache,
@@ -753,7 +753,7 @@ fun MainContent(
onZapFeedback = onZapFeedback,
signerConnectionState = signerConnectionState,
lastPingTimeSec = lastPingTimeSec,
subscriptionHealth = healthMap,
lastRelayEventAt = lastRelayEvent,
modifier = Modifier.weight(1f),
)
}
@@ -72,12 +72,8 @@ class BoundedLargeCache<K : Comparable<K>, V>(
return removed
}
fun containsKey(key: K): Boolean = inner.containsKey(key)
fun size(): Int = sizeCounter.get()
fun isEmpty(): Boolean = sizeCounter.get() == 0
fun clear() {
inner.clear()
sizeCounter.set(0)
@@ -89,10 +85,6 @@ class BoundedLargeCache<K : Comparable<K>, V>(
fun filterIntoSet(consumer: CacheCollectors.BiFilter<K, V>): Set<V> = inner.filterIntoSet(consumer)
fun <R> mapNotNull(consumer: CacheCollectors.BiMapper<K, V, R?>): List<R> = inner.mapNotNull(consumer)
fun forEach(consumer: java.util.function.BiConsumer<K, V>) = inner.forEach(consumer)
fun count(consumer: CacheCollectors.BiFilter<K, V>): Int = inner.count(consumer)
private fun enforceSize() {
@@ -82,25 +82,6 @@ class DesktopLocalCache : ICacheProvider {
val paymentTracker = NwcPaymentTracker()
// ----- Kind-based event consumer registry -----
private val consumers = HashMap<Int, (Event, NormalizedRelayUrl?) -> Boolean>()
init {
consumers[MetadataEvent.KIND] = { e, _ ->
consumeMetadata(e as MetadataEvent)
true
}
consumers[TextNoteEvent.KIND] = { e, r -> consumeTextNote(e as TextNoteEvent, r) }
consumers[ReactionEvent.KIND] = { e, r -> consumeReaction(e as ReactionEvent, r) }
consumers[LnZapRequestEvent.KIND] = { e, r -> consumeZapRequest(e as LnZapRequestEvent, r) }
consumers[LnZapEvent.KIND] = { e, r -> consumeZap(e as LnZapEvent, r) }
consumers[RepostEvent.KIND] = { e, r -> consumeRepost(e as RepostEvent, r) }
consumers[ContactListEvent.KIND] = { e, _ -> consumeContactList(e as ContactListEvent) }
consumers[LongTextNoteEvent.KIND] = { e, r -> consumeLongTextNote(e as LongTextNoteEvent, r) }
consumers[BookmarkListEvent.KIND] = { e, _ -> consumeBookmarkList(e as BookmarkListEvent) }
}
// ----- User operations -----
override fun getUserIfExists(pubkey: HexKey): User? = users.get(pubkey)
@@ -170,16 +151,58 @@ class DesktopLocalCache : ICacheProvider {
}
}
// ----- Event consumption (kind-based registry) -----
// ----- Event consumption -----
/**
* Routes an event to the appropriate consume method via kind-based registry.
* O(1) dispatch. Returns true if the event was consumed (new), false if already seen.
* Routes an event to the appropriate consume method.
* Returns true if the event was consumed (new), false if already seen.
*/
fun consume(
event: Event,
relay: NormalizedRelayUrl?,
): Boolean = consumers[event.kind]?.invoke(event, relay) ?: false
): Boolean =
when (event) {
is MetadataEvent -> {
consumeMetadata(event)
true
}
is TextNoteEvent -> {
consumeTextNote(event, relay)
}
is ReactionEvent -> {
consumeReaction(event, relay)
}
is LnZapRequestEvent -> {
consumeZapRequest(event, relay)
}
is LnZapEvent -> {
consumeZap(event, relay)
}
is RepostEvent -> {
consumeRepost(event, relay)
}
is ContactListEvent -> {
consumeContactList(event)
}
is LongTextNoteEvent -> {
consumeLongTextNote(event, relay)
}
is BookmarkListEvent -> {
consumeBookmarkList(event)
}
else -> {
false
}
}
/**
* Consumes a kind 1 text note event.
@@ -40,7 +40,6 @@ import kotlinx.coroutines.Job
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.flow.update
import kotlinx.coroutines.launch
import java.util.concurrent.ConcurrentHashMap
@@ -68,11 +67,6 @@ import java.util.concurrent.ConcurrentHashMap
* }
* ```
*/
data class SubscriptionHealth(
val lastEventReceivedAt: Long? = null,
val eoseReceived: Boolean = false,
)
class DesktopRelaySubscriptionsCoordinator(
private val client: INostrClient,
private val scope: CoroutineScope,
@@ -112,26 +106,9 @@ class DesktopRelaySubscriptionsCoordinator(
// Screen-triggered subscription Jobs — keyed by subId for proper cancellation
private val screenSubscriptions = ConcurrentHashMap<String, Job>()
// Subscription health tracking
private val _subscriptionHealth = MutableStateFlow<Map<String, SubscriptionHealth>>(emptyMap())
val subscriptionHealth: StateFlow<Map<String, SubscriptionHealth>> = _subscriptionHealth.asStateFlow()
private fun updateHealth(
subId: String,
lastEventReceivedAt: Long? = null,
eoseReceived: Boolean? = null,
) {
_subscriptionHealth.update { current ->
current.toMutableMap().apply {
val existing = this[subId] ?: SubscriptionHealth()
this[subId] =
existing.copy(
lastEventReceivedAt = lastEventReceivedAt ?: existing.lastEventReceivedAt,
eoseReceived = eoseReceived ?: existing.eoseReceived,
)
}
}
}
// Last event received from any subscription — drives RelayHealthIndicator
private val _lastEventAt = MutableStateFlow<Long?>(null)
val lastEventAt: StateFlow<Long?> = _lastEventAt.asStateFlow()
/**
* Central event router — consumes an event into the cache and emits to event stream.
@@ -146,6 +123,7 @@ class DesktopRelaySubscriptionsCoordinator(
try {
val consumed = localCache.consume(event, relay)
if (consumed) {
_lastEventAt.value = System.currentTimeMillis()
val note = localCache.getNoteIfExists(event.id) ?: return@launch
eventBundler.invalidateList(note) { batch ->
localCache.eventStream.emitNewNotes(batch)
@@ -202,14 +180,6 @@ class DesktopRelaySubscriptionsCoordinator(
forFilters: List<Filter>?,
) {
consumeEvent(event, relay)
updateHealth(subId, lastEventReceivedAt = System.currentTimeMillis())
}
override fun onEose(
relay: NormalizedRelayUrl,
forFilters: List<Filter>?,
) {
updateHealth(subId, eoseReceived = true)
}
}
@@ -232,7 +202,6 @@ class DesktopRelaySubscriptionsCoordinator(
fun releaseInteractions(subId: String) {
screenSubscriptions.remove(subId)?.cancel()
client.close(subId)
_subscriptionHealth.update { it - subId }
}
/**
@@ -276,13 +245,6 @@ class DesktopRelaySubscriptionsCoordinator(
feedMetadata.loadMetadataForPubkeys(pubkeys)
}
/**
* Load reactions for specific notes.
*/
fun loadReactionsForNotes(noteIds: List<HexKey>) {
feedMetadata.loadReactionsForNotes(noteIds)
}
// -- DM Subscription Support --
/** Active DM subscription IDs for cleanup */
@@ -389,7 +351,7 @@ class DesktopRelaySubscriptionsCoordinator(
client.close(subId)
}
screenSubscriptions.clear()
_subscriptionHealth.value = emptyMap()
_lastEventAt.value = null
unsubscribeFromDms()
feedMetadata.clear()
@@ -65,7 +65,6 @@ import com.vitorpamplona.amethyst.desktop.cache.DesktopLocalCache
import com.vitorpamplona.amethyst.desktop.network.DesktopRelayConnectionManager
import com.vitorpamplona.amethyst.desktop.service.highlights.DesktopHighlightStore
import com.vitorpamplona.amethyst.desktop.subscriptions.DesktopRelaySubscriptionsCoordinator
import com.vitorpamplona.amethyst.desktop.subscriptions.SubscriptionHealth
import com.vitorpamplona.amethyst.desktop.ui.ZapFeedback
import com.vitorpamplona.amethyst.desktop.ui.components.RelayHealthIndicator
import com.vitorpamplona.amethyst.desktop.ui.media.LocalIsImmersiveFullscreen
@@ -110,7 +109,7 @@ fun SinglePaneLayout(
onZapFeedback: (ZapFeedback) -> Unit,
signerConnectionState: SignerConnectionState,
lastPingTimeSec: Long?,
subscriptionHealth: Map<String, SubscriptionHealth> = emptyMap(),
lastRelayEventAt: Long? = null,
modifier: Modifier = Modifier,
) {
var currentColumnType by remember { mutableStateOf<DeckColumnType>(DeckColumnType.HomeFeed) }
@@ -154,12 +153,8 @@ fun SinglePaneLayout(
Spacer(Modifier.weight(1f))
// Relay health — shows elapsed time since last event (hidden when <30s)
val latestEvent =
subscriptionHealth.values
.mapNotNull { it.lastEventReceivedAt }
.maxOrNull()
RelayHealthIndicator(
lastEventReceivedAt = latestEvent,
lastEventReceivedAt = lastRelayEventAt,
modifier = Modifier.padding(bottom = 4.dp),
)