mirror of
https://github.com/minibits-cash/minibits_wallet.git
synced 2026-10-05 19:28:26 +00:00
perf(nwc): coalesce concurrent KeyChain reads in getCachedWalletKeys [Stage 4a]
Several NWC pushes can wake the app simultaneously (e.g. a wallet connecting fires get_balance + list_transactions together, or rapid zaps). Each caller previously raced past the walletKeys cache check before any populated it, so each triggered its own ~2s cold KeyChain read. Park the in-flight read in a volatile promise; concurrent callers await the same fetch instead of hitting secure storage independently. The shared promise resolves to validated keys so the originator and all awaiters get identical success/error. Volatile (never persisted) so a fresh cold start still does exactly one fresh read. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.8
parent
0d43095108
commit
24a9a85b9f
+40
-15
@@ -169,29 +169,54 @@ export const WalletStoreModel = types
|
||||
resetExchangeRate () {
|
||||
self.exchangeRate = undefined
|
||||
}
|
||||
}))
|
||||
}))
|
||||
.volatile(() => ({
|
||||
// In-flight KeyChain read shared by concurrent getCachedWalletKeys callers.
|
||||
// Reading secure storage is slow (~2s cold on Android), and several NWC
|
||||
// pushes can wake the app at once — without this, each push would trigger
|
||||
// its own KeyChain read. Volatile (never persisted), so it resets on a
|
||||
// fresh cold start, which is exactly when we want a single fresh read.
|
||||
walletKeysInFlight: null as Promise<WalletKeys> | null,
|
||||
}))
|
||||
.actions(self => ({
|
||||
getCachedWalletKeys: flow(function* getWalletKeys() {
|
||||
if (self.walletKeys) {
|
||||
getCachedWalletKeys: flow(function* getWalletKeys() {
|
||||
if (self.walletKeys) {
|
||||
log.trace('[getCachedWalletKeys]', 'Returning cached walletKeys')
|
||||
return self.walletKeys
|
||||
}
|
||||
|
||||
const keys: WalletKeys | undefined = yield KeyChain.getWalletKeys()
|
||||
|
||||
if (!keys) {
|
||||
return self.walletKeys
|
||||
}
|
||||
|
||||
// Coalesce concurrent cold reads onto a single KeyChain fetch.
|
||||
if (self.walletKeysInFlight) {
|
||||
log.trace('[getCachedWalletKeys]', 'Awaiting in-flight KeyChain read')
|
||||
return yield self.walletKeysInFlight
|
||||
}
|
||||
|
||||
// The shared promise resolves to validated keys so that both this
|
||||
// originator and any concurrent awaiters get identical success/error.
|
||||
const fetch = (async (): Promise<WalletKeys> => {
|
||||
const keys: WalletKeys | undefined = await KeyChain.getWalletKeys()
|
||||
if (!keys) {
|
||||
throw new AppError(
|
||||
Err.NOTFOUND_ERROR,
|
||||
Err.NOTFOUND_ERROR,
|
||||
'Device secure storage could not return wallet keys, please reinstall and use your seed phrase to recover wallet.'
|
||||
)
|
||||
}
|
||||
return keys
|
||||
})()
|
||||
|
||||
self.walletKeysInFlight = fetch
|
||||
|
||||
try {
|
||||
const keys: WalletKeys = yield fetch
|
||||
self.walletKeys = keys
|
||||
return keys
|
||||
} finally {
|
||||
self.walletKeysInFlight = null
|
||||
}
|
||||
|
||||
self.walletKeys = keys
|
||||
return keys
|
||||
}),
|
||||
cleanCachedWalletKeys() {
|
||||
cleanCachedWalletKeys() {
|
||||
self.walletKeys = undefined
|
||||
},
|
||||
},
|
||||
}))
|
||||
.actions(self => ({
|
||||
getCachedSeed: flow(function* getCachedSeed() {
|
||||
|
||||
Reference in New Issue
Block a user