diff options
author | Florian Dold <florian@dold.me> | 2022-09-05 18:12:30 +0200 |
---|---|---|
committer | Florian Dold <florian@dold.me> | 2022-09-13 16:10:41 +0200 |
commit | 13e7a674778754c0ed641dfd428e3d6b2b71ab2d (patch) | |
tree | f2a0e5029305a9b818416fd94908ef77cdd7446f /packages/taler-wallet-core/src/util/retries.ts | |
parent | f9f2911c761af1c8ed1c323dcd414cbaa9eeae7c (diff) | |
download | wallet-core-13e7a674778754c0ed641dfd428e3d6b2b71ab2d.tar.xz |
wallet-core: uniform retry handling
Diffstat (limited to 'packages/taler-wallet-core/src/util/retries.ts')
-rw-r--r-- | packages/taler-wallet-core/src/util/retries.ts | 116 |
1 files changed, 115 insertions, 1 deletions
diff --git a/packages/taler-wallet-core/src/util/retries.ts b/packages/taler-wallet-core/src/util/retries.ts index 13a05b385..3a41e8348 100644 --- a/packages/taler-wallet-core/src/util/retries.ts +++ b/packages/taler-wallet-core/src/util/retries.ts @@ -21,7 +21,29 @@ /** * Imports. */ -import { AbsoluteTime, Duration } from "@gnu-taler/taler-util"; +import { + AbsoluteTime, + Duration, + TalerErrorDetail, +} from "@gnu-taler/taler-util"; +import { + BackupProviderRecord, + DepositGroupRecord, + ExchangeRecord, + OperationAttemptResult, + OperationAttemptResultType, + ProposalRecord, + PurchaseRecord, + RecoupGroupRecord, + RefreshGroupRecord, + TipRecord, + WalletStoresV1, + WithdrawalGroupRecord, +} from "../db.js"; +import { TalerError } from "../errors.js"; +import { InternalWalletState } from "../internal-wallet-state.js"; +import { PendingTaskType } from "../pending-types.js"; +import { GetReadWriteAccess } from "./query.js"; export interface RetryInfo { firstTry: AbsoluteTime; @@ -108,3 +130,95 @@ export namespace RetryInfo { return r2; } } + +export namespace RetryTags { + export function forWithdrawal(wg: WithdrawalGroupRecord): string { + return `${PendingTaskType.Withdraw}:${wg.withdrawalGroupId}`; + } + export function forExchangeUpdate(exch: ExchangeRecord): string { + return `${PendingTaskType.ExchangeUpdate}:${exch.baseUrl}`; + } + export function forExchangeCheckRefresh(exch: ExchangeRecord): string { + return `${PendingTaskType.ExchangeCheckRefresh}:${exch.baseUrl}`; + } + export function forProposalClaim(pr: ProposalRecord): string { + return `${PendingTaskType.ProposalDownload}:${pr.proposalId}`; + } + export function forTipPickup(tipRecord: TipRecord): string { + return `${PendingTaskType.TipPickup}:${tipRecord.walletTipId}`; + } + export function forRefresh(refreshGroupRecord: RefreshGroupRecord): string { + return `${PendingTaskType.TipPickup}:${refreshGroupRecord.refreshGroupId}`; + } + export function forPay(purchaseRecord: PurchaseRecord): string { + return `${PendingTaskType.Pay}:${purchaseRecord.proposalId}`; + } + export function forRefundQuery(purchaseRecord: PurchaseRecord): string { + return `${PendingTaskType.RefundQuery}:${purchaseRecord.proposalId}`; + } + export function forRecoup(recoupRecord: RecoupGroupRecord): string { + return `${PendingTaskType.Recoup}:${recoupRecord.recoupGroupId}`; + } + export function forDeposit(depositRecord: DepositGroupRecord): string { + return `${PendingTaskType.Deposit}:${depositRecord.depositGroupId}`; + } + export function forBackup(backupRecord: BackupProviderRecord): string { + return `${PendingTaskType.Backup}:${backupRecord.baseUrl}`; + } +} + +export async function scheduleRetryInTx( + ws: InternalWalletState, + tx: GetReadWriteAccess<{ + operationRetries: typeof WalletStoresV1.operationRetries; + }>, + opId: string, + errorDetail?: TalerErrorDetail, +): Promise<void> { + let retryRecord = await tx.operationRetries.get(opId); + if (!retryRecord) { + retryRecord = { + id: opId, + retryInfo: RetryInfo.reset(), + }; + if (errorDetail) { + retryRecord.lastError = errorDetail; + } + } else { + retryRecord.retryInfo = RetryInfo.increment(retryRecord.retryInfo); + if (errorDetail) { + retryRecord.lastError = errorDetail; + } else { + delete retryRecord.lastError; + } + } + await tx.operationRetries.put(retryRecord); +} + +export async function scheduleRetry( + ws: InternalWalletState, + opId: string, + errorDetail?: TalerErrorDetail, +): Promise<void> { + return await ws.db + .mktx((x) => ({ operationRetries: x.operationRetries })) + .runReadWrite(async (tx) => { + scheduleRetryInTx(ws, tx, opId, errorDetail); + }); +} + +/** + * Run an operation handler, expect a success result and extract the success value. + */ +export async function runOperationHandlerForResult<T>( + res: OperationAttemptResult<T>, +): Promise<T> { + switch (res.type) { + case OperationAttemptResultType.Finished: + return res.result; + case OperationAttemptResultType.Error: + throw TalerError.fromUncheckedDetail(res.errorDetail); + default: + throw Error(`unexpected operation result (${res.type})`); + } +} |