commit f847430fd1bf458215541b96416cb342a4c4c213
parent 736778f85efd2cbe3208d24d486e4d9da69cac61
Author: Florian Dold <dold@taler.net>
Date: Sun, 19 Jul 2026 03:16:26 +0200
db: add getRecordCounts, move diagnostics behind the DAL
handleGetDiagnostics reached into object stores directly.
Diffstat:
4 files changed, 61 insertions(+), 12 deletions(-)
diff --git a/packages/taler-wallet-core/src/dbtx-conformance-cases.ts b/packages/taler-wallet-core/src/dbtx-conformance-cases.ts
@@ -464,6 +464,23 @@ export const conformanceCases: ConformanceCase[] = [
},
{
+ name: "getRecordCounts reflects what was written",
+ async run(t, runner) {
+ const before = await runner.runTx((tx) => tx.getRecordCounts());
+ await runner.runTx(async (tx) => {
+ await tx.upsertDenomination(makeDenomination("https://cnt/", "c-1"));
+ await tx.upsertDenomination(makeDenomination("https://cnt/", "c-2"));
+ });
+ const after = await runner.runTx((tx) => tx.getRecordCounts());
+ t.equal(
+ after.denominations - before.denominations,
+ 2,
+ "counts must track inserts",
+ );
+ },
+ },
+
+ {
name: "upsert overwrites an existing row rather than duplicating it",
async run(t, runner) {
await runner.runTx(async (tx) => {
diff --git a/packages/taler-wallet-core/src/dbtx-indexeddb.ts b/packages/taler-wallet-core/src/dbtx-indexeddb.ts
@@ -89,6 +89,7 @@ import {
import type {} from "./db-indexeddb.js";
import { WalletIndexedDbTransaction } from "./db-indexeddb.js";
import type {
+ WalletDbRecordCounts,
GetCurrencyInfoDbResult,
StoreCurrencyInfoDbRequest,
WalletDbTransaction,
@@ -494,6 +495,19 @@ export class IdbWalletTransaction implements WalletDbTransaction {
await this.tx.bankAccountsV2.put(rec);
}
+ async getRecordCounts(): Promise<WalletDbRecordCounts> {
+ const tx = this.tx;
+ return {
+ coins: await tx.coins.count(),
+ coinAvailability: await tx.coinAvailability.count(),
+ denominations: await tx.denominations.count(),
+ denominationFamilies: await tx.denominationFamilies.count(),
+ exchanges: await tx.exchanges.count(),
+ exchangeDetails: await tx.exchangeDetails.count(),
+ exchangeSignKeys: await tx.exchangeSignKeys.count(),
+ };
+ }
+
async listAllCoins(): Promise<WalletCoin[]> {
return await this.tx.coins.iter().toArray();
}
diff --git a/packages/taler-wallet-core/src/dbtx.ts b/packages/taler-wallet-core/src/dbtx.ts
@@ -103,6 +103,22 @@ export interface StoreCurrencyInfoDbRequest {
source: "exchange" | "user" | "preset";
}
+/**
+ * Record counts for diagnostics.
+ *
+ * Named per entity rather than per object store, so the numbers mean the same
+ * thing whichever backend produced them.
+ */
+export interface WalletDbRecordCounts {
+ coins: number;
+ coinAvailability: number;
+ denominations: number;
+ denominationFamilies: number;
+ exchanges: number;
+ exchangeDetails: number;
+ exchangeSignKeys: number;
+}
+
export interface WalletDbTransaction {
/** Get the currency specification for a scope, if one is stored. */
getCurrencyInfo(
@@ -343,6 +359,9 @@ export interface WalletDbTransaction {
/** Create or update a known bank account. */
upsertBankAccount(rec: WalletBankAccount): Promise<void>;
+ /** Count stored records per entity, for diagnostics. */
+ getRecordCounts(): Promise<WalletDbRecordCounts>;
+
/** List every coin in the wallet. */
listAllCoins(): Promise<WalletCoin[]>;
diff --git a/packages/taler-wallet-core/src/requests.ts b/packages/taler-wallet-core/src/requests.ts
@@ -1903,18 +1903,17 @@ export async function handleGetDiagnostics(
): Promise<TestingGetDiagnosticsResponse> {
const cnt: Record<string, number> = {};
const exchangeEntries: TestingGetDiagnosticsResponse["exchangeEntries"] = [];
- await wex.db.runAllStoresReadWriteTx({}, async (tx) => {
- cnt["coinAvailability"] = await tx.coinAvailability.count();
- cnt["coins"] = await tx.coins.count();
- cnt["denominationFamilies"] = await tx.denominationFamilies.count();
- cnt["denominations"] = await tx.denominations.count();
- cnt["exchangeDetails"] = await tx.exchangeDetails.count();
- cnt["exchangeSignKeys"] = await tx.exchangeSignKeys.count();
- cnt["exchanges"] = await tx.exchanges.count();
- for (const exch of await tx.exchanges.getAll()) {
- const denoms = await tx.denominations.indexes.byExchangeBaseUrl.getAll(
- exch.baseUrl,
- );
+ await wex.runWalletDbTx(async (tx) => {
+ const counts = await tx.getRecordCounts();
+ cnt["coinAvailability"] = counts.coinAvailability;
+ cnt["coins"] = counts.coins;
+ cnt["denominationFamilies"] = counts.denominationFamilies;
+ cnt["denominations"] = counts.denominations;
+ cnt["exchangeDetails"] = counts.exchangeDetails;
+ cnt["exchangeSignKeys"] = counts.exchangeSignKeys;
+ cnt["exchanges"] = counts.exchanges;
+ for (const exch of await tx.getExchanges()) {
+ const denoms = await tx.getDenominationsByExchange(exch.baseUrl);
let numWithdrawableDenoms = 0;
let numCandidateWithdrawableDenoms = 0;
for (let i = 0; i < denoms.length; i++) {