perf: cache following favorite relays
This commit is contained in:
@@ -74,10 +74,7 @@ class ClientService extends EventTarget {
|
||||
max: 2000,
|
||||
fetchMethod: this._fetchFollowListEvent.bind(this)
|
||||
})
|
||||
private fetchFollowingFavoriteRelaysCache = new LRUCache<
|
||||
string,
|
||||
Promise<Map<string, Set<string>>>
|
||||
>({
|
||||
private fetchFollowingFavoriteRelaysCache = new LRUCache<string, Promise<[string, string[]][]>>({
|
||||
max: 10,
|
||||
fetchMethod: this._fetchFollowingFavoriteRelays.bind(this)
|
||||
})
|
||||
@@ -823,40 +820,54 @@ class ClientService extends EventTarget {
|
||||
}
|
||||
|
||||
private async _fetchFollowingFavoriteRelays(pubkey: string) {
|
||||
const followings = await this.fetchFollowings(pubkey)
|
||||
const events = await this.fetchEvents(BIG_RELAY_URLS, {
|
||||
authors: followings,
|
||||
kinds: [ExtendedKind.FAVORITE_RELAYS, kinds.Relaysets],
|
||||
limit: 1000
|
||||
})
|
||||
const alreadyExistsFavoriteRelaysPubkeySet = new Set<string>()
|
||||
const alreadyExistsRelaySetsPubkeySet = new Set<string>()
|
||||
const uniqueEvents: NEvent[] = []
|
||||
events
|
||||
.sort((a, b) => b.created_at - a.created_at)
|
||||
.forEach((event) => {
|
||||
if (event.kind === ExtendedKind.FAVORITE_RELAYS) {
|
||||
if (alreadyExistsFavoriteRelaysPubkeySet.has(event.pubkey)) return
|
||||
alreadyExistsFavoriteRelaysPubkeySet.add(event.pubkey)
|
||||
} else if (event.kind === kinds.Relaysets) {
|
||||
if (alreadyExistsRelaySetsPubkeySet.has(event.pubkey)) return
|
||||
alreadyExistsRelaySetsPubkeySet.add(event.pubkey)
|
||||
} else {
|
||||
return
|
||||
}
|
||||
uniqueEvents.push(event)
|
||||
const fetchNewData = async () => {
|
||||
const followings = await this.fetchFollowings(pubkey)
|
||||
const events = await this.fetchEvents(BIG_RELAY_URLS, {
|
||||
authors: followings,
|
||||
kinds: [ExtendedKind.FAVORITE_RELAYS, kinds.Relaysets],
|
||||
limit: 1000
|
||||
})
|
||||
const alreadyExistsFavoriteRelaysPubkeySet = new Set<string>()
|
||||
const alreadyExistsRelaySetsPubkeySet = new Set<string>()
|
||||
const uniqueEvents: NEvent[] = []
|
||||
events
|
||||
.sort((a, b) => b.created_at - a.created_at)
|
||||
.forEach((event) => {
|
||||
if (event.kind === ExtendedKind.FAVORITE_RELAYS) {
|
||||
if (alreadyExistsFavoriteRelaysPubkeySet.has(event.pubkey)) return
|
||||
alreadyExistsFavoriteRelaysPubkeySet.add(event.pubkey)
|
||||
} else if (event.kind === kinds.Relaysets) {
|
||||
if (alreadyExistsRelaySetsPubkeySet.has(event.pubkey)) return
|
||||
alreadyExistsRelaySetsPubkeySet.add(event.pubkey)
|
||||
} else {
|
||||
return
|
||||
}
|
||||
uniqueEvents.push(event)
|
||||
})
|
||||
|
||||
const relayMap = new Map<string, Set<string>>()
|
||||
uniqueEvents.forEach((event) => {
|
||||
event.tags.forEach(([tagName, tagValue]) => {
|
||||
if (tagName === 'relay' && tagValue && isWebsocketUrl(tagValue)) {
|
||||
const url = normalizeUrl(tagValue)
|
||||
relayMap.set(url, (relayMap.get(url) || new Set()).add(event.pubkey))
|
||||
}
|
||||
const relayMap = new Map<string, Set<string>>()
|
||||
uniqueEvents.forEach((event) => {
|
||||
event.tags.forEach(([tagName, tagValue]) => {
|
||||
if (tagName === 'relay' && tagValue && isWebsocketUrl(tagValue)) {
|
||||
const url = normalizeUrl(tagValue)
|
||||
relayMap.set(url, (relayMap.get(url) || new Set()).add(event.pubkey))
|
||||
}
|
||||
})
|
||||
})
|
||||
})
|
||||
return relayMap
|
||||
const relayMapEntries = Array.from(relayMap.entries())
|
||||
.sort((a, b) => b[1].size - a[1].size)
|
||||
.map(([url, pubkeys]) => [url, Array.from(pubkeys)]) as [string, string[]][]
|
||||
|
||||
indexedDb.putFollowingFavoriteRelays(pubkey, relayMapEntries)
|
||||
return relayMapEntries
|
||||
}
|
||||
|
||||
const cached = await indexedDb.getFollowingFavoriteRelays(pubkey)
|
||||
if (cached) {
|
||||
fetchNewData()
|
||||
return cached
|
||||
}
|
||||
return fetchNewData()
|
||||
}
|
||||
|
||||
updateFollowListCache(event: NEvent) {
|
||||
|
||||
@@ -17,7 +17,8 @@ const StoreNames = {
|
||||
MUTE_DECRYPTED_TAGS: 'muteDecryptedTags',
|
||||
RELAY_INFO_EVENTS: 'relayInfoEvents',
|
||||
FAVORITE_RELAYS: 'favoriteRelays',
|
||||
RELAY_SETS: 'relaySets'
|
||||
RELAY_SETS: 'relaySets',
|
||||
FOLLOWING_FAVORITE_RELAYS: 'followingFavoriteRelays'
|
||||
}
|
||||
|
||||
class IndexedDbService {
|
||||
@@ -36,7 +37,7 @@ class IndexedDbService {
|
||||
init(): Promise<void> {
|
||||
if (!this.initPromise) {
|
||||
this.initPromise = new Promise((resolve, reject) => {
|
||||
const request = window.indexedDB.open('jumble', 4)
|
||||
const request = window.indexedDB.open('jumble', 5)
|
||||
|
||||
request.onerror = (event) => {
|
||||
reject(event)
|
||||
@@ -76,6 +77,9 @@ class IndexedDbService {
|
||||
if (!db.objectStoreNames.contains(StoreNames.RELAY_SETS)) {
|
||||
db.createObjectStore(StoreNames.RELAY_SETS, { keyPath: 'key' })
|
||||
}
|
||||
if (!db.objectStoreNames.contains(StoreNames.FOLLOWING_FAVORITE_RELAYS)) {
|
||||
db.createObjectStore(StoreNames.FOLLOWING_FAVORITE_RELAYS, { keyPath: 'key' })
|
||||
}
|
||||
this.db = db
|
||||
}
|
||||
})
|
||||
@@ -363,6 +367,50 @@ class IndexedDbService {
|
||||
})
|
||||
}
|
||||
|
||||
async putFollowingFavoriteRelays(pubkey: string, relays: [string, string[]][]): Promise<void> {
|
||||
await this.initPromise
|
||||
return new Promise((resolve, reject) => {
|
||||
if (!this.db) {
|
||||
return reject('database not initialized')
|
||||
}
|
||||
const transaction = this.db.transaction(StoreNames.FOLLOWING_FAVORITE_RELAYS, 'readwrite')
|
||||
const store = transaction.objectStore(StoreNames.FOLLOWING_FAVORITE_RELAYS)
|
||||
|
||||
const putRequest = store.put(this.formatValue(pubkey, relays))
|
||||
putRequest.onsuccess = () => {
|
||||
transaction.commit()
|
||||
resolve()
|
||||
}
|
||||
|
||||
putRequest.onerror = (event) => {
|
||||
transaction.commit()
|
||||
reject(event)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
async getFollowingFavoriteRelays(pubkey: string): Promise<[string, string[]][] | null> {
|
||||
await this.initPromise
|
||||
return new Promise((resolve, reject) => {
|
||||
if (!this.db) {
|
||||
return reject('database not initialized')
|
||||
}
|
||||
const transaction = this.db.transaction(StoreNames.FOLLOWING_FAVORITE_RELAYS, 'readonly')
|
||||
const store = transaction.objectStore(StoreNames.FOLLOWING_FAVORITE_RELAYS)
|
||||
const request = store.get(pubkey)
|
||||
|
||||
request.onsuccess = () => {
|
||||
transaction.commit()
|
||||
resolve((request.result as TValue<[string, string[]][]>)?.value)
|
||||
}
|
||||
|
||||
request.onerror = (event) => {
|
||||
transaction.commit()
|
||||
reject(event)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
private getReplaceableEventKey(event: Event): string {
|
||||
if (
|
||||
[kinds.Metadata, kinds.Contacts].includes(event.kind) ||
|
||||
|
||||
Reference in New Issue
Block a user