diff options
Diffstat (limited to 'packages/taler-wallet-core/src/util')
| -rw-r--r-- | packages/taler-wallet-core/src/util/retries.ts | 306 | 
1 files changed, 0 insertions, 306 deletions
| diff --git a/packages/taler-wallet-core/src/util/retries.ts b/packages/taler-wallet-core/src/util/retries.ts index e85eb0a6b..e602d4702 100644 --- a/packages/taler-wallet-core/src/util/retries.ts +++ b/packages/taler-wallet-core/src/util/retries.ts @@ -50,309 +50,3 @@ import { assertUnreachable } from "./assertUnreachable.js";  const logger = new Logger("util/retries.ts"); -export enum OperationAttemptResultType { -  Finished = "finished", -  Pending = "pending", -  Error = "error", -  Longpoll = "longpoll", -} - -export type OperationAttemptResult<TSuccess = unknown, TPending = unknown> = -  | OperationAttemptFinishedResult<TSuccess> -  | OperationAttemptErrorResult -  | OperationAttemptLongpollResult -  | OperationAttemptPendingResult<TPending>; - -export namespace OperationAttemptResult { -  export function finishedEmpty(): OperationAttemptResult<unknown, unknown> { -    return { -      type: OperationAttemptResultType.Finished, -      result: undefined, -    }; -  } -  export function pendingEmpty(): OperationAttemptResult<unknown, unknown> { -    return { -      type: OperationAttemptResultType.Pending, -      result: undefined, -    }; -  } -  export function longpoll(): OperationAttemptResult<unknown, unknown> { -    return { -      type: OperationAttemptResultType.Longpoll, -    }; -  } -} - -export interface OperationAttemptFinishedResult<T> { -  type: OperationAttemptResultType.Finished; -  result: T; -} - -export interface OperationAttemptPendingResult<T> { -  type: OperationAttemptResultType.Pending; -  result: T; -} - -export interface OperationAttemptErrorResult { -  type: OperationAttemptResultType.Error; -  errorDetail: TalerErrorDetail; -} - -export interface OperationAttemptLongpollResult { -  type: OperationAttemptResultType.Longpoll; -} - -export interface RetryInfo { -  firstTry: AbsoluteTime; -  nextRetry: AbsoluteTime; -  retryCounter: number; -} - -export interface RetryPolicy { -  readonly backoffDelta: Duration; -  readonly backoffBase: number; -  readonly maxTimeout: Duration; -} - -const defaultRetryPolicy: RetryPolicy = { -  backoffBase: 1.5, -  backoffDelta: Duration.fromSpec({ seconds: 1 }), -  maxTimeout: Duration.fromSpec({ minutes: 2 }), -}; - -function updateTimeout( -  r: RetryInfo, -  p: RetryPolicy = defaultRetryPolicy, -): void { -  const now = AbsoluteTime.now(); -  if (now.t_ms === "never") { -    throw Error("assertion failed"); -  } -  if (p.backoffDelta.d_ms === "forever") { -    r.nextRetry = AbsoluteTime.never(); -    return; -  } - -  const nextIncrement = -    p.backoffDelta.d_ms * Math.pow(p.backoffBase, r.retryCounter); - -  const t = -    now.t_ms + -    (p.maxTimeout.d_ms === "forever" -      ? nextIncrement -      : Math.min(p.maxTimeout.d_ms, nextIncrement)); -  r.nextRetry = AbsoluteTime.fromMilliseconds(t); -} - -export namespace RetryInfo { -  export function getDuration( -    r: RetryInfo | undefined, -    p: RetryPolicy = defaultRetryPolicy, -  ): Duration { -    if (!r) { -      // If we don't have any retry info, run immediately. -      return { d_ms: 0 }; -    } -    if (p.backoffDelta.d_ms === "forever") { -      return { d_ms: "forever" }; -    } -    const t = p.backoffDelta.d_ms * Math.pow(p.backoffBase, r.retryCounter); -    return { -      d_ms: -        p.maxTimeout.d_ms === "forever" ? t : Math.min(p.maxTimeout.d_ms, t), -    }; -  } - -  export function reset(p: RetryPolicy = defaultRetryPolicy): RetryInfo { -    const now = AbsoluteTime.now(); -    const info = { -      firstTry: now, -      nextRetry: now, -      retryCounter: 0, -    }; -    updateTimeout(info, p); -    return info; -  } - -  export function increment( -    r: RetryInfo | undefined, -    p: RetryPolicy = defaultRetryPolicy, -  ): RetryInfo { -    if (!r) { -      return reset(p); -    } -    const r2 = { ...r }; -    r2.retryCounter++; -    updateTimeout(r2, p); -    return r2; -  } -} - -/** - * Parsed representation of task identifiers. - */ -export type ParsedTaskIdentifier = -  | { -      tag: PendingTaskType.Withdraw; -      withdrawalGroupId: string; -    } -  | { tag: PendingTaskType.ExchangeUpdate; exchangeBaseUrl: string } -  | { tag: PendingTaskType.Backup; backupProviderBaseUrl: string } -  | { tag: PendingTaskType.Deposit; depositGroupId: string } -  | { tag: PendingTaskType.ExchangeCheckRefresh; exchangeBaseUrl: string } -  | { tag: PendingTaskType.ExchangeUpdate; exchangeBaseUrl: string } -  | { tag: PendingTaskType.PeerPullDebit; peerPullPaymentIncomingId: string } -  | { tag: PendingTaskType.PeerPullCredit; pursePub: string } -  | { tag: PendingTaskType.PeerPushCredit; peerPushPaymentIncomingId: string } -  | { tag: PendingTaskType.PeerPushDebit; pursePub: string } -  | { tag: PendingTaskType.Purchase; proposalId: string } -  | { tag: PendingTaskType.Recoup; recoupGroupId: string } -  | { tag: PendingTaskType.TipPickup; walletTipId: string } -  | { tag: PendingTaskType.Refresh; refreshGroupId: string }; - -export function parseTaskIdentifier(x: string): ParsedTaskIdentifier { -  throw Error("not yet implemented"); -} - -export function constructTaskIdentifier(p: ParsedTaskIdentifier): TaskId { -  switch (p.tag) { -    case PendingTaskType.Backup: -      return `${p.tag}:${p.backupProviderBaseUrl}` as TaskId; -    case PendingTaskType.Deposit: -      return `${p.tag}:${p.depositGroupId}` as TaskId; -    case PendingTaskType.ExchangeCheckRefresh: -      return `${p.tag}:${p.exchangeBaseUrl}` as TaskId; -    case PendingTaskType.ExchangeUpdate: -      return `${p.tag}:${p.exchangeBaseUrl}` as TaskId; -    case PendingTaskType.PeerPullDebit: -      return `${p.tag}:${p.peerPullPaymentIncomingId}` as TaskId; -    case PendingTaskType.PeerPushCredit: -      return `${p.tag}:${p.peerPushPaymentIncomingId}` as TaskId; -    case PendingTaskType.PeerPullCredit: -      return `${p.tag}:${p.pursePub}` as TaskId; -    case PendingTaskType.PeerPushDebit: -      return `${p.tag}:${p.pursePub}` as TaskId; -    case PendingTaskType.Purchase: -      return `${p.tag}:${p.proposalId}` as TaskId; -    case PendingTaskType.Recoup: -      return `${p.tag}:${p.recoupGroupId}` as TaskId; -    case PendingTaskType.Refresh: -      return `${p.tag}:${p.refreshGroupId}` as TaskId; -    case PendingTaskType.TipPickup: -      return `${p.tag}:${p.walletTipId}` as TaskId; -    case PendingTaskType.Withdraw: -      return `${p.tag}:${p.withdrawalGroupId}` as TaskId; -    default: -      assertUnreachable(p); -  } -} - -export namespace TaskIdentifiers { -  export function forWithdrawal(wg: WithdrawalGroupRecord): TaskId { -    return `${PendingTaskType.Withdraw}:${wg.withdrawalGroupId}` as TaskId; -  } -  export function forExchangeUpdate(exch: ExchangeRecord): TaskId { -    return `${PendingTaskType.ExchangeUpdate}:${exch.baseUrl}` as TaskId; -  } -  export function forExchangeUpdateFromUrl(exchBaseUrl: string): TaskId { -    return `${PendingTaskType.ExchangeUpdate}:${exchBaseUrl}` as TaskId; -  } -  export function forExchangeCheckRefresh(exch: ExchangeRecord): TaskId { -    return `${PendingTaskType.ExchangeCheckRefresh}:${exch.baseUrl}` as TaskId; -  } -  export function forTipPickup(tipRecord: TipRecord): TaskId { -    return `${PendingTaskType.TipPickup}:${tipRecord.walletTipId}` as TaskId; -  } -  export function forRefresh(refreshGroupRecord: RefreshGroupRecord): TaskId { -    return `${PendingTaskType.Refresh}:${refreshGroupRecord.refreshGroupId}` as TaskId; -  } -  export function forPay(purchaseRecord: PurchaseRecord): TaskId { -    return `${PendingTaskType.Purchase}:${purchaseRecord.proposalId}` as TaskId; -  } -  export function forRecoup(recoupRecord: RecoupGroupRecord): TaskId { -    return `${PendingTaskType.Recoup}:${recoupRecord.recoupGroupId}` as TaskId; -  } -  export function forDeposit(depositRecord: DepositGroupRecord): TaskId { -    return `${PendingTaskType.Deposit}:${depositRecord.depositGroupId}` as TaskId; -  } -  export function forBackup(backupRecord: BackupProviderRecord): TaskId { -    return `${PendingTaskType.Backup}:${backupRecord.baseUrl}` as TaskId; -  } -  export function forPeerPushPaymentInitiation( -    ppi: PeerPushPaymentInitiationRecord, -  ): TaskId { -    return `${PendingTaskType.PeerPushDebit}:${ppi.pursePub}` as TaskId; -  } -  export function forPeerPullPaymentInitiation( -    ppi: PeerPullPaymentInitiationRecord, -  ): TaskId { -    return `${PendingTaskType.PeerPullCredit}:${ppi.pursePub}` as TaskId; -  } -  export function forPeerPullPaymentDebit( -    ppi: PeerPullPaymentIncomingRecord, -  ): TaskId { -    return `${PendingTaskType.PeerPullDebit}:${ppi.peerPullPaymentIncomingId}` as TaskId; -  } -  export function forPeerPushCredit( -    ppi: PeerPushPaymentIncomingRecord, -  ): TaskId { -    return `${PendingTaskType.PeerPushCredit}:${ppi.peerPushPaymentIncomingId}` as TaskId; -  } -} - -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) => [x.operationRetries]) -    .runReadWrite(async (tx) => { -      tx.operationRetries; -      scheduleRetryInTx(ws, tx, opId, errorDetail); -    }); -} - -/** - * Run an operation handler, expect a success result and extract the success value. - */ -export async function unwrapOperationHandlerResultOrThrow<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})`); -  } -} | 
