···11package rs.averyrive.darkfeed.api
2233+import com.mayakapps.kache.InMemoryKache
34import io.ktor.client.*
45import io.ktor.client.call.*
56import io.ktor.client.engine.cio.*
···1415import kotlinx.serialization.json.Json
1516import org.slf4j.LoggerFactory
1617import rs.averyrive.darkfeed.api.model.*
1818+import kotlin.time.Duration.Companion.hours
1919+2020+// Specify size of each cache
2121+const val CACHE_SIZE: Long = 1 * 1024 * 1024 // 1 MB
17221823/** Client that handles identity related requests for ATProto accounts. */
1924class AtpIdentityClient {
···3237 install(Logging)
3338 }
34394040+ /** Cache of handle to DID relationships. */
4141+ private val handleDidCache = InMemoryKache<String, Did>(CACHE_SIZE) {
4242+ expireAfterWriteDuration = 1.hours
4343+ }
4444+4545+ /** Cache of DID to PDS host relationships. */
4646+ private val didPdsCache = InMemoryKache<Did, String>(CACHE_SIZE) {
4747+ expireAfterWriteDuration = 1.hours
4848+ }
4949+3550 /**
3651 * Resolve a handle's DID.
3752 *
···4661 */
4762 suspend fun resolveDidFromHandle(handle: String): Did {
4863 log.debug("Resolving DID for '{}'...", handle)
6464+6565+ // Return cached DID if it exists.
6666+ handleDidCache.get(handle)?.let { did ->
6767+ log.debug("Returning cached DID for '{}': {}", handle, did.toString())
6868+ return did
6969+ }
49705071 @Serializable
5172 data class ResolveHandleResponse(val did: String)
···101122 else -> throw RuntimeException("Received unexpected response: ${resolveHandleResponse.bodyAsText()}")
102123 }
103124104104- log.debug("Resolved DID for '{}': {}", handle, did.toString())
125125+ // Cache the DID for this handle.
126126+ handleDidCache.put(handle, did)
127127+ log.debug("Resolved and cached DID for '{}': {}", handle, did.toString())
105128106129 return did
107130 }
···120143 */
121144 suspend fun resolvePdsFromDid(did: Did): String {
122145 log.debug("Resolving PDS for '{}'...", did.toString())
146146+147147+ // Return cached PDS host if it exists.
148148+ didPdsCache.get(did)?.let { pdsHost ->
149149+ log.debug("Returning cached PDS host for '{}': {}", did.toString(), pdsHost)
150150+ return pdsHost
151151+ }
123152124153 // Get the DID document, using either `plc.directory` or
125154 // `/.well-known/did.json` depending on the type of DID.
···166195 ?.serviceEndpoint
167196 ?: throw PdsNotFoundException(did)
168197169169- log.debug("Resolved PDS for '{}': {}", did.toString(), pdsHost)
198198+ // Cache the PDS for this DID.
199199+ didPdsCache.put(did, pdsHost)
200200+ log.debug("Resolved and cached PDS for '{}': {}", did.toString(), pdsHost)
170201171202 return pdsHost
203203+ }
204204+205205+ /**
206206+ * Invalidate the PDS associated with the given DID.
207207+ *
208208+ * This should be used when a call to the PDS fails due to the PDS no longer
209209+ * being available. This may indicate that the PDS has moved and should be
210210+ * resolved again.
211211+ *
212212+ * @param did DID to invalidate.
213213+ */
214214+ suspend fun invalidateCachedPds(did: Did) {
215215+ didPdsCache.remove(did)
172216 }
173217}
174218
+2
darkfeed/src/main/kotlin/api/BskyApi.kt
···9292 val record: Generator,
9393 )
94949595+ // TODO: If this PDS is not accessible, invalidate cache and retry.
9596 val ownerPdsHost = atpIdentityClient.resolvePdsFromDid(authManager.authAccountDid.toDid())
96979798 val response = httpClient.post {
···116117 @Serializable
117118 data class Response(val cursor: String?, val records: List<LikeRef>)
118119120120+ // TODO: If this PDS is not accessible, invalidate cache and retry.
119121 val requestorPdsHost = atpIdentityClient.resolvePdsFromDid(actor.toDid())
120122121123 val response = httpClient.get {
+2
gradle/libs.versions.toml
···44ktor = "3.0.1"
55auth0 = "4.4.0"
66clikt = "5.0.1"
77+kache = "2.1.1"
7889[libraries]
910logback-classic = { group = "ch.qos.logback", name = "logback-classic", version.ref = "logback" }
···1819ktor-serialization-kotlinx-json = { group = "io.ktor", name = "ktor-serialization-kotlinx-json", version.ref = "ktor" }
1920java-jwt = { group = "com.auth0", name = "java-jwt", version.ref = "auth0" }
2021clikt = { group = "com.github.ajalt.clikt", name = "clikt", version.ref = "clikt" }
2222+kache = { group = "com.mayakapps.kache", name = "kache", version.ref = "kache" }
21232224[plugins]
2325kotlin-jvm = { id = "org.jetbrains.kotlin.jvm", version.ref = "kotlin" }