commit 540cd68e6ef2d56559d91657ecde3b789a441119
parent f6b59b8b96fcdd4d87fb1e76f32f6b03dcb506be
Author: Florian Dold <dold@taler.net>
Date: Thu, 6 Aug 2026 19:02:40 +0200
wallet: look up denomination families once per exchange update
An exchange has a handful of families but lists a denomination per family and
rotation period, so querying per denomination was a query per row of /keys.
Issue: https://bugs.taler.net/n/11334
Diffstat:
1 file changed, 63 insertions(+), 23 deletions(-)
diff --git a/packages/taler-wallet-core/src/exchanges.ts b/packages/taler-wallet-core/src/exchanges.ts
@@ -952,6 +952,27 @@ async function checkExchangeEntryOutdated(
}
/**
+ * Key identifying a denomination family, for looking one up without a query
+ * per denomination. Mirrors the unique index the DB layer has on these
+ * columns; amounts are canonical strings, so comparing them as text is what
+ * the query would do anyway.
+ *
+ * Joined on a character none of the components can contain, so that two
+ * different tuples cannot spell the same key.
+ */
+function denomFamilyParamsKey(p: WalletDenomFamilyParams): string {
+ return [
+ p.exchangeBaseUrl,
+ p.exchangeMasterPub,
+ p.value,
+ p.feeWithdraw,
+ p.feeDeposit,
+ p.feeRefresh,
+ p.feeRefund,
+ ].join("\u0000");
+}
+
+/**
* Timestamp (in seconds) to ask the exchange to cherry-pick /keys by, or
* undefined when the whole response should be downloaded.
*
@@ -2143,33 +2164,54 @@ export async function updateExchangeFromUrlHandler(
logger.trace("updating denominations in database");
+ // One lookup for all of them: an exchange has a handful of families but
+ // its /keys lists a denomination per family and rotation period, so a
+ // query per denomination is a query per row of the response.
+ const familySerialByParams = new Map<string, number>();
+ for (const fam of await tx.getDenominationFamiliesByExchange(
+ exchangeBaseUrl,
+ )) {
+ checkDbInvariant(
+ typeof fam.denominationFamilySerial === "number",
+ "denominationFamilySerial",
+ );
+ familySerialByParams.set(
+ denomFamilyParamsKey(fam.familyParams),
+ fam.denominationFamilySerial,
+ );
+ }
+
for (const currentDenom of denomInfos) {
- // FIXME: Check if we really already need the denomination.
- let fpRec: WalletDenominationFamily | undefined =
- await tx.getDenominationFamilyByParams({
- exchangeBaseUrl: currentDenom.exchangeBaseUrl,
- exchangeMasterPub: currentDenom.exchangeMasterPub,
- value: currentDenom.value,
- feeWithdraw: currentDenom.feeWithdraw,
- feeDeposit: currentDenom.feeDeposit,
- feeRefresh: currentDenom.feeRefresh,
- feeRefund: currentDenom.feeRefund,
- });
+ const familyParams: WalletDenomFamilyParams = {
+ exchangeBaseUrl: currentDenom.exchangeBaseUrl,
+ exchangeMasterPub: currentDenom.exchangeMasterPub,
+ value: currentDenom.value,
+ feeWithdraw: currentDenom.feeWithdraw,
+ feeDeposit: currentDenom.feeDeposit,
+ feeRefresh: currentDenom.feeRefresh,
+ feeRefund: currentDenom.feeRefund,
+ };
+ let fpRec: WalletDenominationFamily | undefined = undefined;
+ const knownSerial = familySerialByParams.get(
+ denomFamilyParamsKey(familyParams),
+ );
+ if (knownSerial != null) {
+ fpRec = { familyParams, denominationFamilySerial: knownSerial };
+ }
let denominationFamilySerial;
if (fpRec == null) {
- const fp: WalletDenomFamilyParams = {
- exchangeBaseUrl: exchangeBaseUrl,
- exchangeMasterPub: keysInfo.master_public_key,
- feeDeposit: currentDenom.feeDeposit,
- feeRefresh: currentDenom.feeRefresh,
- feeRefund: currentDenom.feeRefund,
- feeWithdraw: currentDenom.feeWithdraw,
- value: currentDenom.value,
- };
fpRec = {
- familyParams: fp,
+ familyParams,
};
denominationFamilySerial = await tx.upsertDenominationFamily(fpRec);
+ checkDbInvariant(
+ typeof denominationFamilySerial === "number",
+ "denominationFamilySerial",
+ );
+ familySerialByParams.set(
+ denomFamilyParamsKey(familyParams),
+ denominationFamilySerial,
+ );
} else {
denominationFamilySerial = fpRec.denominationFamilySerial;
}
@@ -2179,8 +2221,6 @@ export async function updateExchangeFromUrlHandler(
"denominationFamilySerial",
);
- // First, find denom family
-
const denomRec: WalletDenomination = {
currency: keysInfo.currency,
denominationFamilySerial,