feat: cache not found relay lists to avoid redundant queries

This commit is contained in:
codytseng
2025-05-24 20:42:46 +08:00
parent 7552499a76
commit ffdc6fd0c8
2 changed files with 53 additions and 8 deletions

View File

@@ -723,7 +723,7 @@ class ClientService extends EventTarget {
const relayEvents = await indexedDb.getManyReplaceableEvents(pubkeys, kinds.RelayList) const relayEvents = await indexedDb.getManyReplaceableEvents(pubkeys, kinds.RelayList)
const nonExistingPubkeyIndexMap = new Map<string, number>() const nonExistingPubkeyIndexMap = new Map<string, number>()
pubkeys.forEach((pubkey, i) => { pubkeys.forEach((pubkey, i) => {
if (!relayEvents[i]) { if (relayEvents[i] === undefined) {
nonExistingPubkeyIndexMap.set(pubkey, i) nonExistingPubkeyIndexMap.set(pubkey, i)
} }
}) })
@@ -1024,7 +1024,14 @@ class ClientService extends EventTarget {
eventsMap.set(pubkey, event) eventsMap.set(pubkey, event)
} }
} }
Array.from(eventsMap.values()).forEach((evt) => indexedDb.putReplaceableEvent(evt)) pubkeys.forEach((pubkey) => {
const event = eventsMap.get(pubkey)
if (event) {
indexedDb.putReplaceableEvent(event)
} else {
indexedDb.putNullReplaceableEvent(pubkey, kinds.RelayList)
}
})
return pubkeys.map((pubkey) => eventsMap.get(pubkey)) return pubkeys.map((pubkey) => eventsMap.get(pubkey))
} }

View File

@@ -84,6 +84,45 @@ class IndexedDbService {
return this.initPromise return this.initPromise
} }
async putNullReplaceableEvent(pubkey: string, kind: number) {
const storeName = this.getStoreNameByKind(kind)
if (!storeName) {
return Promise.reject('store name not found')
}
await this.initPromise
return new Promise((resolve, reject) => {
if (!this.db) {
return reject('database not initialized')
}
const transaction = this.db.transaction(storeName, 'readwrite')
const store = transaction.objectStore(storeName)
const getRequest = store.get(pubkey)
getRequest.onsuccess = () => {
const oldValue = getRequest.result as TValue<Event> | undefined
if (oldValue) {
transaction.commit()
return resolve(oldValue.value)
}
const putRequest = store.put(this.formatValue(pubkey, null))
putRequest.onsuccess = () => {
transaction.commit()
resolve(null)
}
putRequest.onerror = (event) => {
transaction.commit()
reject(event)
}
}
getRequest.onerror = (event) => {
transaction.commit()
reject(event)
}
})
}
async putReplaceableEvent(event: Event): Promise<Event> { async putReplaceableEvent(event: Event): Promise<Event> {
const storeName = this.getStoreNameByKind(event.kind) const storeName = this.getStoreNameByKind(event.kind)
if (!storeName) { if (!storeName) {
@@ -154,7 +193,7 @@ class IndexedDbService {
async getManyReplaceableEvents( async getManyReplaceableEvents(
pubkeys: readonly string[], pubkeys: readonly string[],
kind: number kind: number
): Promise<(Event | undefined)[]> { ): Promise<(Event | undefined | null)[]> {
const storeName = this.getStoreNameByKind(kind) const storeName = this.getStoreNameByKind(kind)
if (!storeName) { if (!storeName) {
return Promise.reject('store name not found') return Promise.reject('store name not found')
@@ -166,14 +205,14 @@ class IndexedDbService {
} }
const transaction = this.db.transaction(storeName, 'readonly') const transaction = this.db.transaction(storeName, 'readonly')
const store = transaction.objectStore(storeName) const store = transaction.objectStore(storeName)
const events: Event[] = new Array(pubkeys.length).fill(undefined) const events: (Event | null)[] = new Array(pubkeys.length).fill(undefined)
let count = 0 let count = 0
pubkeys.forEach((pubkey, i) => { pubkeys.forEach((pubkey, i) => {
const request = store.get(pubkey) const request = store.get(pubkey)
request.onsuccess = () => { request.onsuccess = () => {
const event = (request.result as TValue<Event>)?.value const event = (request.result as TValue<Event | null>)?.value
if (event) { if (event || event === null) {
events[i] = event events[i] = event
} }
@@ -384,8 +423,7 @@ class IndexedDbService {
const cursor = (event.target as IDBRequest).result const cursor = (event.target as IDBRequest).result
if (cursor) { if (cursor) {
const value: TValue = cursor.value const value: TValue = cursor.value
// 10% chance to delete if (value.addedAt < expirationTimestamp) {
if (value.addedAt < expirationTimestamp && Math.random() < 0.1) {
cursor.delete() cursor.delete()
} }
cursor.continue() cursor.continue()