From 89b2a273a06fc59bfd90713c528ae1e6f129d6d1 Mon Sep 17 00:00:00 2001 From: nrobi144 Date: Wed, 29 Jul 2026 12:26:18 +0300 Subject: [PATCH] refactor: move EOSEAccountFast to commons, KMP-purified (Phase 2 prep) EOSEAccountFast is used by the user/event finder assemblers that will move to commonMain. Relocate it to commons.relays with KmpLock instead of synchronized(...) so it passes the iOS verifyKmpPurity gate. The old amethyst.service.relays location keeps a typealias, so its 7 existing importers are untouched. (SincePerRelayMap/MutableTime/EOSERelayList were already commons typealiases.) Co-Authored-By: Claude Opus 4.8 --- .../amethyst/service/relays/EOSE.kt | 58 +----------- .../commons/relays/EOSEAccountFast.kt | 94 +++++++++++++++++++ 2 files changed, 95 insertions(+), 57 deletions(-) create mode 100644 commons/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/relays/EOSEAccountFast.kt diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/service/relays/EOSE.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/service/relays/EOSE.kt index 403fcaf39f..f6032b8800 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/service/relays/EOSE.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/service/relays/EOSE.kt @@ -28,6 +28,7 @@ import com.vitorpamplona.quartz.nip01Core.relay.normalizer.NormalizedRelayUrl typealias EOSERelayList = com.vitorpamplona.amethyst.commons.relays.EOSERelayList typealias SincePerRelayMap = com.vitorpamplona.amethyst.commons.relays.SincePerRelayMap typealias MutableTime = com.vitorpamplona.amethyst.commons.relays.MutableTime +typealias EOSEAccountFast = com.vitorpamplona.amethyst.commons.relays.EOSEAccountFast open class EOSEByKey( cacheSize: Int = 200, @@ -113,60 +114,3 @@ open class EOSEAccountKey( time: Long, ) = addOrUpdate(user, listCode, relayUrl, time) } - -class EOSEAccountFast( - cacheSize: Int = 20, -) { - private val users: LruCache = LruCache(cacheSize) - private val lock = Any() - - fun addOrUpdate( - user: T, - relayUrl: NormalizedRelayUrl, - time: Long, - ) { - synchronized(lock) { - val relayList = users[user] - if (relayList == null) { - val newList = EOSERelayList() - users.put(user, newList) - - newList.addOrUpdate(relayUrl, time) - } else { - relayList.addOrUpdate(relayUrl, time) - } - } - } - - fun removeEveryoneBut(list: Set) { - synchronized(lock) { - users.snapshot().forEach { - if (it.key !in list) { - users.remove(it.key) - } - } - } - } - - fun removeDataFor(user: T) { - synchronized(lock) { - users.remove(user) - } - } - - fun since(key: T): SincePerRelayMap? = - synchronized(lock) { - users[key]?.relayList?.toMutableMap() - } - - fun sinceRelaySet(key: T): Set? = - synchronized(lock) { - users[key]?.relayList?.keys?.toSet() - } - - fun newEose( - user: T, - relayUrl: NormalizedRelayUrl, - time: Long, - ) = addOrUpdate(user, relayUrl, time) -} diff --git a/commons/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/relays/EOSEAccountFast.kt b/commons/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/relays/EOSEAccountFast.kt new file mode 100644 index 0000000000..84d5e90773 --- /dev/null +++ b/commons/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/relays/EOSEAccountFast.kt @@ -0,0 +1,94 @@ +/* + * Copyright (c) 2025 Vitor Pamplona + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the + * Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +package com.vitorpamplona.amethyst.commons.relays + +import androidx.collection.LruCache +import com.vitorpamplona.amethyst.commons.util.KmpLock +import com.vitorpamplona.amethyst.commons.util.withLock +import com.vitorpamplona.quartz.nip01Core.relay.normalizer.NormalizedRelayUrl + +/** + * Per-key EOSE tracker keyed by an arbitrary [T] (a `User`, a pubkey, …), used + * by the relay-subscription assemblers to remember which relays already EOSE'd + * for a given key so the next filter assembly can add a `since` and avoid a full + * re-download. + * + * KMP-pure: uses [KmpLock] instead of `synchronized(...)` so it compiles for the + * iOS targets `commons` builds for. Moved out of `amethyst.service.relays` so the + * shared user/event finder assemblers can live in `commonMain`. The old location + * keeps a `typealias` for source compatibility. + */ +class EOSEAccountFast( + cacheSize: Int = 20, +) { + private val users: LruCache = LruCache(cacheSize) + private val lock = KmpLock() + + fun addOrUpdate( + user: T, + relayUrl: NormalizedRelayUrl, + time: Long, + ) { + lock.withLock { + val relayList = users[user] + if (relayList == null) { + val newList = EOSERelayList() + users.put(user, newList) + + newList.addOrUpdate(relayUrl, time) + } else { + relayList.addOrUpdate(relayUrl, time) + } + } + } + + fun removeEveryoneBut(list: Set) { + lock.withLock { + users.snapshot().forEach { + if (it.key !in list) { + users.remove(it.key) + } + } + } + } + + fun removeDataFor(user: T) { + lock.withLock { + users.remove(user) + } + } + + fun since(key: T): SincePerRelayMap? = + lock.withLock { + users[key]?.relayList?.toMutableMap() + } + + fun sinceRelaySet(key: T): Set? = + lock.withLock { + users[key]?.relayList?.keys?.toSet() + } + + fun newEose( + user: T, + relayUrl: NormalizedRelayUrl, + time: Long, + ) = addOrUpdate(user, relayUrl, time) +}