perf(lid-mapping): integrate request coalescing in single-item lookups (M3)
PROBLEM: Request coalescing infrastructure (inflightLIDLookups, inflightPNLookups Maps and coalesceRequest() method) was implemented in commita9374bdbut never integrated into the public API. The Maps remained empty and the coalescing method was never called, wasting the optimization potential. In message burst scenarios (10+ concurrent messages from same user), each concurrent call to getLIDForPN() would execute a separate database lookup for the same user, causing: - Redundant database queries (9 wasted queries in 10 concurrent calls) - Increased database load during high traffic - Higher latency for duplicate lookups - Missed optimization opportunity SOLUTION: Integrated request coalescing into single-item lookup methods: 1. getLIDForPN(pn): Now uses coalesceRequest() with inflightLIDLookups Map - First concurrent call creates Promise and stores in Map - Subsequent calls for same PN return existing Promise - Result: 1 DB lookup shared by all concurrent calls 2. getPNForLID(lid): Now uses coalesceRequest() with inflightPNLookups Map - Same deduplication pattern for reverse lookups - Optimizes LID→PN translation in message processing 3. Updated Maps documentation to reflect active usage - Clarified that Maps are now actively used (not "future optimization") - Documented usage in getLIDForPN() and getPNForLID() Implementation Details: - Both methods now use trackOperation() wrapper (ensures thread safety via V4) - Early validation before coalescing (isAnyPnUser/isAnyLidUser, jidDecode) - Coalescing keyed by user (not full JID) for maximum deduplication - Batch methods (getLIDsForPNs, getPNsForLIDs) unchanged (already optimized) BENEFITS: - Reduces DB load by 90% in 10-concurrent-call burst scenarios - Improves latency for duplicate lookups (instant return from existing Promise) - No downside: if no concurrency, behaves exactly as before - Completes optimization work started in commita9374bdSAFETY: ✓ Protected by trackOperation() (V4 fix - prevents UAF during destroy) ✓ Maps cleared in destroy() (V5 fix - prevents memory leaks) ✓ No TOCTOU in coalesceRequest() (V6 fix - single check at operation start) ✓ Thread-safe: Maps protected by operationsInProgress counter VALIDATION: ✓ Protocolo de Análise complete (5 steps) ✓ TypeScript compilation verified (no new errors) ✓ E2E scenarios simulated (burst of 10 concurrent calls) ✓ Backward compatible (batch methods unchanged, single methods enhanced) FILES MODIFIED: - src/Signal/lid-mapping.ts:165-181 - Updated Maps documentation (now active) - src/Signal/lid-mapping.ts:470-502 - Integrated coalescing in getLIDForPN() - src/Signal/lid-mapping.ts:659-691 - Integrated coalescing in getPNForLID() https://claude.ai/code/session_01NTVq3RHgGpgKL289JGvw55
This commit is contained in:
+61
-12
@@ -165,9 +165,14 @@ export class LIDMappingStore {
|
||||
/**
|
||||
* Request coalescing Maps - deduplicates concurrent lookups
|
||||
*
|
||||
* MEMORY SAFETY: These MUST be cleared in destroy() to prevent memory leaks
|
||||
* USAGE: Active in getLIDForPN() and getPNForLID() to deduplicate
|
||||
* concurrent lookups for the same user. In message bursts, multiple
|
||||
* concurrent calls share a single database lookup.
|
||||
*
|
||||
* THREAD SAFETY: Protected by operationsInProgress counter.
|
||||
* MEMORY SAFETY: Cleared in destroy() to prevent memory leaks.
|
||||
* Pending Promises complete but won't be returned to new callers.
|
||||
*
|
||||
* THREAD SAFETY: Protected by operationsInProgress counter (V4 fix).
|
||||
* - Maps are only cleared when operationsInProgress === 0
|
||||
* - Operations using coalesceRequest() MUST be wrapped with trackOperation()
|
||||
* - This ensures maps won't be cleared while coalesceRequest() is accessing them
|
||||
@@ -470,13 +475,35 @@ export class LIDMappingStore {
|
||||
/**
|
||||
* Get LID for PN - Returns device-specific LID based on user mapping
|
||||
*
|
||||
* NOTE: Request coalescing infrastructure (inflightLIDLookups Map) is available
|
||||
* for future optimization of concurrent lookups. Current implementation already
|
||||
* benefits from batch operations and LRU caching.
|
||||
* OPTIMIZATION: Uses request coalescing to deduplicate concurrent lookups
|
||||
* for the same PN. In message bursts, multiple concurrent calls for the same
|
||||
* user will share a single database lookup, reducing load and improving latency.
|
||||
*
|
||||
* Thread Safety: Protected by trackOperation() wrapper (V4 fix)
|
||||
*/
|
||||
async getLIDForPN(pn: string): Promise<string | null> {
|
||||
const results = await this.getLIDsForPNs([pn])
|
||||
return results?.[0]?.lid || null
|
||||
this.checkDestroyed()
|
||||
|
||||
return this.trackOperation(async () => {
|
||||
// Early validation
|
||||
if (!isAnyPnUser(pn)) return null
|
||||
|
||||
const decoded = jidDecode(pn)
|
||||
if (!decoded) return null
|
||||
|
||||
const pnUser = decoded.user
|
||||
|
||||
// Use request coalescing to deduplicate concurrent lookups
|
||||
// Safe because: wrapped in trackOperation() prevents resource cleanup
|
||||
return this.coalesceRequest(
|
||||
pnUser,
|
||||
this.inflightLIDLookups,
|
||||
async () => {
|
||||
const results = await this.getLIDsForPNs([pn])
|
||||
return results?.[0]?.lid || null
|
||||
}
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -637,13 +664,35 @@ export class LIDMappingStore {
|
||||
/**
|
||||
* Get PN for LID - USER LEVEL with device construction
|
||||
*
|
||||
* NOTE: Request coalescing infrastructure (inflightPNLookups Map) is available
|
||||
* for future optimization of concurrent lookups. Current implementation already
|
||||
* benefits from batch operations and LRU caching.
|
||||
* OPTIMIZATION: Uses request coalescing to deduplicate concurrent lookups
|
||||
* for the same LID. In message bursts, multiple concurrent calls for the same
|
||||
* user will share a single database lookup, reducing load and improving latency.
|
||||
*
|
||||
* Thread Safety: Protected by trackOperation() wrapper (V4 fix)
|
||||
*/
|
||||
async getPNForLID(lid: string): Promise<string | null> {
|
||||
const results = await this.getPNsForLIDs([lid])
|
||||
return results?.[0]?.pn || null
|
||||
this.checkDestroyed()
|
||||
|
||||
return this.trackOperation(async () => {
|
||||
// Early validation
|
||||
if (!isAnyLidUser(lid)) return null
|
||||
|
||||
const decoded = jidDecode(lid)
|
||||
if (!decoded) return null
|
||||
|
||||
const lidUser = decoded.user
|
||||
|
||||
// Use request coalescing to deduplicate concurrent lookups
|
||||
// Safe because: wrapped in trackOperation() prevents resource cleanup
|
||||
return this.coalesceRequest(
|
||||
lidUser,
|
||||
this.inflightPNLookups,
|
||||
async () => {
|
||||
const results = await this.getPNsForLIDs([lid])
|
||||
return results?.[0]?.pn || null
|
||||
}
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
Reference in New Issue
Block a user