fix: optimize LID migration performance and introduce cache (#1782)
* chore: lid-map cache, bulk migration, fixes * fix: type error * fix: signalrepository with lid type, remove check from assertsessions, lid-mapping store level deduplication * fix: migrations logs * fix: remove migrationsops lenght check * fix: skip deletesession if cached or existing migrations * chore: remove check migrated session * fix: remove return null loadsession * fix: getLIDforPN call and jidsrequiringfetch * fix: longer lid map cache TTL * fix: lint * fix: lid type * fix: lid socket type
This commit is contained in:
+66
-23
@@ -1,9 +1,14 @@
|
||||
import { LRUCache } from 'lru-cache'
|
||||
import type { SignalKeyStoreWithTransaction } from '../Types'
|
||||
import logger from '../Utils/logger'
|
||||
import { isLidUser, isPnUser, jidDecode } from '../WABinary'
|
||||
|
||||
//TODO: Caching
|
||||
export class LIDMappingStore {
|
||||
private readonly mappingCache = new LRUCache<string, string>({
|
||||
ttl: 7 * 24 * 60 * 60 * 1000, // 7 days
|
||||
ttlAutopurge: true,
|
||||
updateAgeOnGet: true
|
||||
})
|
||||
private readonly keys: SignalKeyStoreWithTransaction
|
||||
private onWhatsAppFunc?: (...jids: string[]) => Promise<
|
||||
| {
|
||||
@@ -29,13 +34,6 @@ export class LIDMappingStore {
|
||||
this.onWhatsAppFunc = onWhatsAppFunc // needed to get LID from PN if not found
|
||||
}
|
||||
|
||||
/**
|
||||
* Store LID-PN mapping - USER LEVEL
|
||||
*/
|
||||
async storeLIDPNMapping(lid: string, pn: string): Promise<void> {
|
||||
return this.storeLIDPNMappings([{ lid, pn }])
|
||||
}
|
||||
|
||||
/**
|
||||
* Store LID-PN mapping - USER LEVEL
|
||||
*/
|
||||
@@ -57,6 +55,25 @@ export class LIDMappingStore {
|
||||
|
||||
const pnUser = pnDecoded.user
|
||||
const lidUser = lidDecoded.user
|
||||
|
||||
// Check if mapping already exists (cache first, then database)
|
||||
let existingLidUser = this.mappingCache.get(`pn:${pnUser}`)
|
||||
if (!existingLidUser) {
|
||||
// Cache miss - check database
|
||||
const stored = await this.keys.get('lid-mapping', [pnUser])
|
||||
existingLidUser = stored[pnUser]
|
||||
if (existingLidUser) {
|
||||
// Update cache with database value
|
||||
this.mappingCache.set(`pn:${pnUser}`, existingLidUser)
|
||||
this.mappingCache.set(`lid:${existingLidUser}`, pnUser)
|
||||
}
|
||||
}
|
||||
|
||||
if (existingLidUser === lidUser) {
|
||||
logger.debug({ pnUser, lidUser }, 'LID mapping already exists, skipping')
|
||||
continue
|
||||
}
|
||||
|
||||
pairMap[pnUser] = lidUser
|
||||
}
|
||||
|
||||
@@ -70,6 +87,10 @@ export class LIDMappingStore {
|
||||
[`${lidUser}_reverse`]: pnUser // "102765716062358_reverse" -> "554396160286"
|
||||
}
|
||||
})
|
||||
|
||||
// Update cache with both directions
|
||||
this.mappingCache.set(`pn:${pnUser}`, lidUser)
|
||||
this.mappingCache.set(`lid:${lidUser}`, pnUser)
|
||||
}
|
||||
}, 'lid-mapping')
|
||||
}
|
||||
@@ -83,22 +104,38 @@ export class LIDMappingStore {
|
||||
const decoded = jidDecode(pn)
|
||||
if (!decoded) return null
|
||||
|
||||
// Look up user-level mapping (whatsmeow approach)
|
||||
// Check cache first for PN → LID mapping
|
||||
const pnUser = decoded.user
|
||||
const stored = await this.keys.get('lid-mapping', [pnUser])
|
||||
let lidUser = stored[pnUser]
|
||||
let lidUser = this.mappingCache.get(`pn:${pnUser}`)
|
||||
|
||||
if (!lidUser) {
|
||||
logger.trace(`No LID mapping found for PN user ${pnUser}; getting from USync`)
|
||||
const { exists, lid } = (await this.onWhatsAppFunc?.(pn))?.[0]! // this function already adds LIDs to mapping
|
||||
if (exists) {
|
||||
lidUser = jidDecode(lid)?.user
|
||||
// Cache miss - check database
|
||||
const stored = await this.keys.get('lid-mapping', [pnUser])
|
||||
lidUser = stored[pnUser]
|
||||
|
||||
if (lidUser) {
|
||||
// Cache the database result
|
||||
this.mappingCache.set(`pn:${pnUser}`, lidUser)
|
||||
} else {
|
||||
return null
|
||||
// Not in database - try USync
|
||||
logger.trace(`No LID mapping found for PN user ${pnUser}; getting from USync`)
|
||||
const { exists, lid } = (await this.onWhatsAppFunc?.(pn))?.[0]! // this function already adds LIDs to mapping
|
||||
if (exists && lid) {
|
||||
lidUser = jidDecode(lid)?.user
|
||||
if (lidUser) {
|
||||
// Cache the USync result
|
||||
this.mappingCache.set(`pn:${pnUser}`, lidUser)
|
||||
}
|
||||
} else {
|
||||
return null
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (typeof lidUser !== 'string') return null
|
||||
if (typeof lidUser !== 'string' || !lidUser) {
|
||||
logger.warn(`Invalid or empty LID user for PN ${pn}: lidUser = "${lidUser}"`)
|
||||
return null
|
||||
}
|
||||
|
||||
// Push the PN device ID to the LID to maintain device separation
|
||||
const pnDevice = decoded.device !== undefined ? decoded.device : 0
|
||||
@@ -117,15 +154,21 @@ export class LIDMappingStore {
|
||||
const decoded = jidDecode(lid)
|
||||
if (!decoded) return null
|
||||
|
||||
// Look up reverse user mapping
|
||||
// Check cache first for LID → PN mapping
|
||||
const lidUser = decoded.user
|
||||
// TODO: remove this style and instead load all mappings somehow, and then assign them in the map
|
||||
const stored = await this.keys.get('lid-mapping', [`${lidUser}_reverse`])
|
||||
const pnUser = stored[`${lidUser}_reverse`]
|
||||
let pnUser = this.mappingCache.get(`lid:${lidUser}`)
|
||||
|
||||
if (!pnUser || typeof pnUser !== 'string') {
|
||||
logger.trace(`No reverse mapping found for LID user: ${lidUser}`)
|
||||
return null
|
||||
// Cache miss - check database
|
||||
const stored = await this.keys.get('lid-mapping', [`${lidUser}_reverse`])
|
||||
pnUser = stored[`${lidUser}_reverse`]
|
||||
|
||||
if (!pnUser || typeof pnUser !== 'string') {
|
||||
logger.trace(`No reverse mapping found for LID user: ${lidUser}`)
|
||||
return null
|
||||
}
|
||||
|
||||
this.mappingCache.set(`lid:${lidUser}`, pnUser)
|
||||
}
|
||||
|
||||
// Construct device-specific PN JID
|
||||
|
||||
Reference in New Issue
Block a user