commit af6c5877c41937eb580b2090ddedfde4212302b2
parent 34b2d212ca47409358764bfc12bf2045523ad66f
Author: Florian Dold <dold@taler.net>
Date: Sun, 19 Jul 2026 00:06:09 +0200
db: move the exchange purge cascade behind the DAL
Diffstat:
3 files changed, 153 insertions(+), 21 deletions(-)
diff --git a/packages/taler-wallet-core/src/dbtx-indexeddb.ts b/packages/taler-wallet-core/src/dbtx-indexeddb.ts
@@ -354,6 +354,88 @@ export class IdbWalletTransaction implements WalletDbTransaction {
});
}
+ async getCoinsByExchange(exchangeBaseUrl: string): Promise<WalletCoin[]> {
+ const tx = this.tx;
+ return await tx.coins.indexes.byBaseUrl.getAll(exchangeBaseUrl);
+ }
+
+ async countCoinsByExchange(exchangeBaseUrl: string): Promise<number> {
+ const tx = this.tx;
+ return await tx.coins.indexes.byBaseUrl.count(exchangeBaseUrl);
+ }
+
+ async getCoinsByDenomPubHash(denomPubHash: string): Promise<WalletCoin[]> {
+ const tx = this.tx;
+ return await tx.coins.indexes.byDenomPubHash.getAll(denomPubHash);
+ }
+
+ async deleteCoin(coinPub: string): Promise<void> {
+ const tx = this.tx;
+ await tx.coins.delete(coinPub);
+ }
+
+ async deleteCoinHistory(coinPub: string): Promise<void> {
+ const tx = this.tx;
+ await tx.coinHistory.delete(coinPub);
+ }
+
+ async getCoinAvailabilityByExchange(
+ exchangeBaseUrl: string,
+ ): Promise<WalletCoinAvailability[]> {
+ const tx = this.tx;
+ return await tx.coinAvailability.indexes.byExchangeBaseUrl.getAll(
+ exchangeBaseUrl,
+ );
+ }
+
+ async deleteCoinAvailability(
+ exchangeBaseUrl: string,
+ denomPubHash: string,
+ maxAge: number,
+ ): Promise<void> {
+ const tx = this.tx;
+ await tx.coinAvailability.delete([exchangeBaseUrl, denomPubHash, maxAge]);
+ }
+
+ async getRecoupGroupsByExchange(
+ exchangeBaseUrl: string,
+ ): Promise<WalletRecoupGroup[]> {
+ const tx = this.tx;
+ return await tx.recoupGroups.indexes.byExchangeBaseUrl.getAll(
+ exchangeBaseUrl,
+ );
+ }
+
+ async listAllRefreshGroups(): Promise<WalletRefreshGroup[]> {
+ const tx = this.tx;
+ return await tx.refreshGroups.iter().toArray();
+ }
+
+ async listAllDepositGroups(): Promise<WalletDepositGroup[]> {
+ const tx = this.tx;
+ return await tx.depositGroups.iter().toArray();
+ }
+
+ async listAllPeerPullCredits(): Promise<WalletPeerPullCredit[]> {
+ const tx = this.tx;
+ return await tx.peerPullCredit.iter().toArray();
+ }
+
+ async listAllPeerPullDebits(): Promise<WalletPeerPullDebit[]> {
+ const tx = this.tx;
+ return await tx.peerPullDebit.iter().toArray();
+ }
+
+ async listAllPeerPushCredits(): Promise<WalletPeerPushCredit[]> {
+ const tx = this.tx;
+ return await tx.peerPushCredit.iter().toArray();
+ }
+
+ async listAllPeerPushDebits(): Promise<WalletPeerPushDebit[]> {
+ const tx = this.tx;
+ return await tx.peerPushDebit.iter().toArray();
+ }
+
async getDenominationFamilyByParams(
params: WalletDenomFamilyParams,
): Promise<WalletDenominationFamily | undefined> {
diff --git a/packages/taler-wallet-core/src/dbtx.ts b/packages/taler-wallet-core/src/dbtx.ts
@@ -251,6 +251,52 @@ export interface WalletDbTransaction {
/**
* Look up a denomination family by its parameters.
*/
+ getCoinsByExchange(exchangeBaseUrl: string): Promise<WalletCoin[]>;
+
+ countCoinsByExchange(exchangeBaseUrl: string): Promise<number>;
+
+ getCoinsByDenomPubHash(denomPubHash: string): Promise<WalletCoin[]>;
+
+ deleteCoin(coinPub: string): Promise<void>;
+
+ deleteCoinHistory(coinPub: string): Promise<void>;
+
+ getCoinAvailabilityByExchange(
+ exchangeBaseUrl: string,
+ ): Promise<WalletCoinAvailability[]>;
+
+ deleteCoinAvailability(
+ exchangeBaseUrl: string,
+ denomPubHash: string,
+ maxAge: number,
+ ): Promise<void>;
+
+ getRecoupGroupsByExchange(
+ exchangeBaseUrl: string,
+ ): Promise<WalletRecoupGroup[]>;
+
+ /**
+ * List every refresh group, in any state.
+ *
+ * Used by exchange purge and base-URL migration, which filter on
+ * infoPerExchange -- a field with no index.
+ */
+ listAllRefreshGroups(): Promise<WalletRefreshGroup[]>;
+
+ /**
+ * List every deposit group, in any state. As above, filtered on
+ * infoPerExchange by the caller.
+ */
+ listAllDepositGroups(): Promise<WalletDepositGroup[]>;
+
+ listAllPeerPullCredits(): Promise<WalletPeerPullCredit[]>;
+
+ listAllPeerPullDebits(): Promise<WalletPeerPullDebit[]>;
+
+ listAllPeerPushCredits(): Promise<WalletPeerPushCredit[]>;
+
+ listAllPeerPushDebits(): Promise<WalletPeerPushDebit[]>;
+
getDenominationFamilyByParams(
params: WalletDenomFamilyParams,
): Promise<WalletDenominationFamily | undefined>;
diff --git a/packages/taler-wallet-core/src/exchanges.ts b/packages/taler-wallet-core/src/exchanges.ts
@@ -3181,23 +3181,21 @@ async function purgeExchange(
{
const coinAvailabilityRecs =
- await tx.coinAvailability.indexes.byExchangeBaseUrl.getAll(
- exchangeBaseUrl,
- );
+ await tx.wtx.getCoinAvailabilityByExchange(exchangeBaseUrl);
for (const rec of coinAvailabilityRecs) {
- await tx.coinAvailability.delete([
+ await tx.wtx.deleteCoinAvailability(
exchangeBaseUrl,
rec.denomPubHash,
rec.maxAge,
- ]);
+ );
}
}
{
- const coinRecs = await tx.coins.indexes.byBaseUrl.getAll(exchangeBaseUrl);
+ const coinRecs = await tx.wtx.getCoinsByExchange(exchangeBaseUrl);
for (const rec of coinRecs) {
- await tx.coins.delete(rec.coinPub);
- await tx.coinHistory.delete(rec.coinPub);
+ await tx.wtx.deleteCoin(rec.coinPub);
+ await tx.wtx.deleteCoinHistory(rec.coinPub);
}
}
@@ -3239,17 +3237,18 @@ async function purgeExchange(
if (purgeTransactions) {
// Remove from refreshGroups and refreshSessions
{
- await tx.refreshGroups.iter().forEachAsync(async (rg) => {
+ const refreshGroups = await tx.wtx.listAllRefreshGroups();
+ for (const rg of refreshGroups) {
if (rg.infoPerExchange && rg.infoPerExchange[exchangeBaseUrl] != null) {
const ctx = new RefreshTransactionContext(wex, rg.refreshGroupId);
await ctx.deleteTransactionInTx(tx);
}
- });
+ }
}
// Remove from recoupGroups
{
const recoupGroups =
- await tx.recoupGroups.indexes.byExchangeBaseUrl.getAll(exchangeBaseUrl);
+ await tx.wtx.getRecoupGroupsByExchange(exchangeBaseUrl);
for (const rg of recoupGroups) {
const ctx = new RecoupTransactionContext(wex, rg.recoupGroupId);
await ctx.deleteTransactionInTx(tx);
@@ -3258,12 +3257,13 @@ async function purgeExchange(
// Remove from deposits
{
// FIXME: Index would be useful here
- await tx.depositGroups.iter().forEachAsync(async (dg) => {
+ const depositGroups = await tx.wtx.listAllDepositGroups();
+ for (const dg of depositGroups) {
if (dg.infoPerExchange && dg.infoPerExchange[exchangeBaseUrl]) {
const ctx = new DepositTransactionContext(wex, dg.depositGroupId);
await ctx.deleteTransactionInTx(tx);
}
- });
+ }
}
// Remove from purchases, refundGroups, refundItems
{
@@ -3283,16 +3283,18 @@ async function purgeExchange(
}
// Remove from peerPullCredit
{
- await tx.peerPullCredit.iter().forEachAsync(async (rec) => {
+ const recs = await tx.wtx.listAllPeerPullCredits();
+ for (const rec of recs) {
if (rec.exchangeBaseUrl === exchangeBaseUrl) {
const ctx = new PeerPullCreditTransactionContext(wex, rec.pursePub);
await ctx.deleteTransactionInTx(tx);
}
- });
+ }
}
// Remove from peerPullDebit
{
- await tx.peerPullDebit.iter().forEachAsync(async (rec) => {
+ const recs = await tx.wtx.listAllPeerPullDebits();
+ for (const rec of recs) {
if (rec.exchangeBaseUrl === exchangeBaseUrl) {
const ctx = new PeerPullDebitTransactionContext(
wex,
@@ -3300,11 +3302,12 @@ async function purgeExchange(
);
await ctx.deleteTransactionInTx(tx);
}
- });
+ }
}
// Remove from peerPushCredit
{
- await tx.peerPushCredit.iter().forEachAsync(async (rec) => {
+ const recs = await tx.wtx.listAllPeerPushCredits();
+ for (const rec of recs) {
if (rec.exchangeBaseUrl === exchangeBaseUrl) {
const ctx = new PeerPushCreditTransactionContext(
wex,
@@ -3312,16 +3315,17 @@ async function purgeExchange(
);
await ctx.deleteTransactionInTx(tx);
}
- });
+ }
}
// Remove from peerPushDebit
{
- await tx.peerPushDebit.iter().forEachAsync(async (rec) => {
+ const recs = await tx.wtx.listAllPeerPushDebits();
+ for (const rec of recs) {
if (rec.exchangeBaseUrl === exchangeBaseUrl) {
const ctx = new PeerPushDebitTransactionContext(wex, rec.pursePub);
await ctx.deleteTransactionInTx(tx);
}
- });
+ }
}
}