[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[taler-wallet-core] branch master updated: schedule exchange updating
From: |
gnunet |
Subject: |
[taler-wallet-core] branch master updated: schedule exchange updating |
Date: |
Wed, 02 Sep 2020 11:14:48 +0200 |
This is an automated email from the git hooks/post-receive script.
dold pushed a commit to branch master
in repository wallet-core.
The following commit(s) were added to refs/heads/master by this push:
new 8a3ac7f0 schedule exchange updating
8a3ac7f0 is described below
commit 8a3ac7f08b114360118bf58a38983401107a62cf
Author: Florian Dold <florian.dold@gmail.com>
AuthorDate: Wed Sep 2 14:44:36 2020 +0530
schedule exchange updating
---
packages/taler-wallet-core/src/db.ts | 2 +-
.../taler-wallet-core/src/operations/exchanges.ts | 66 +++++++++++++---------
.../taler-wallet-core/src/operations/pending.ts | 28 +++++++--
packages/taler-wallet-core/src/types/dbTypes.ts | 6 ++
packages/taler-wallet-core/src/util/http.ts | 19 +++++--
packages/taler-wallet-core/src/util/time.ts | 32 +++++++++++
6 files changed, 113 insertions(+), 40 deletions(-)
diff --git a/packages/taler-wallet-core/src/db.ts
b/packages/taler-wallet-core/src/db.ts
index a55d9bb1..d5ebdb6c 100644
--- a/packages/taler-wallet-core/src/db.ts
+++ b/packages/taler-wallet-core/src/db.ts
@@ -8,7 +8,7 @@ import { IDBFactory, IDBDatabase } from "idb-bridge";
* with each major change. When incrementing the major version,
* the wallet should import data from the previous version.
*/
-const TALER_DB_NAME = "taler-walletdb-v8";
+const TALER_DB_NAME = "taler-walletdb-v9";
/**
* Current database minor version, should be incremented
diff --git a/packages/taler-wallet-core/src/operations/exchanges.ts
b/packages/taler-wallet-core/src/operations/exchanges.ts
index 618b1cf4..d162ca3b 100644
--- a/packages/taler-wallet-core/src/operations/exchanges.ts
+++ b/packages/taler-wallet-core/src/operations/exchanges.ts
@@ -30,6 +30,8 @@ import {
WireFee,
ExchangeUpdateReason,
ExchangeUpdatedEventRecord,
+ initRetryInfo,
+ updateRetryInfoTimeout,
} from "../types/dbTypes";
import { canonicalizeBaseUrl } from "../util/helpers";
import * as Amounts from "../util/amounts";
@@ -43,7 +45,12 @@ import {
WALLET_CACHE_BREAKER_CLIENT_VERSION,
WALLET_EXCHANGE_PROTOCOL_VERSION,
} from "./versions";
-import { getTimestampNow, Duration, isTimestampExpired } from "../util/time";
+import {
+ getTimestampNow,
+ Duration,
+ isTimestampExpired,
+ durationFromSpec,
+} from "../util/time";
import { compare } from "../util/libtoolVersion";
import { createRecoupGroup, processRecoupGroup } from "./recoup";
import { TalerErrorCode } from "../TalerErrorCode";
@@ -56,6 +63,7 @@ import { Logger } from "../util/logging";
import { URL } from "../util/url";
import { reconcileReserveHistory } from "../util/reserveHistoryUtil";
import { checkDbInvariant } from "../util/invariants";
+import { NotificationType } from "../types/notifications";
const logger = new Logger("exchanges.ts");
@@ -86,17 +94,23 @@ async function denominationRecordFromKeys(
return d;
}
-async function setExchangeError(
+async function handleExchangeUpdateError(
ws: InternalWalletState,
baseUrl: string,
err: TalerErrorDetails,
): Promise<void> {
- logger.warn(`last error for exchange ${baseUrl}:`, err);
- const mut = (exchange: ExchangeRecord): ExchangeRecord => {
+ await ws.db.runWithWriteTransaction([Stores.exchanges], async (tx) => {
+ const exchange = await tx.get(Stores.exchanges, baseUrl);
+ if (!exchange) {
+ return;
+ }
+ exchange.retryInfo.retryCounter++;
+ updateRetryInfoTimeout(exchange.retryInfo);
exchange.lastError = err;
- return exchange;
- };
- await ws.db.mutate(Stores.exchanges, baseUrl, mut);
+ });
+ if (err) {
+ ws.notify({ type: NotificationType.ExchangeOperationError, error: err });
+ }
}
function getExchangeRequestTimeout(e: ExchangeRecord): Duration {
@@ -142,7 +156,7 @@ async function updateExchangeWithKeys(
exchangeBaseUrl: baseUrl,
},
);
- await setExchangeError(ws, baseUrl, opErr);
+ await handleExchangeUpdateError(ws, baseUrl, opErr);
throw new OperationFailedAndReportedError(opErr);
}
@@ -158,7 +172,7 @@ async function updateExchangeWithKeys(
walletProtocolVersion: WALLET_EXCHANGE_PROTOCOL_VERSION,
},
);
- await setExchangeError(ws, baseUrl, opErr);
+ await handleExchangeUpdateError(ws, baseUrl, opErr);
throw new OperationFailedAndReportedError(opErr);
}
@@ -198,10 +212,13 @@ async function updateExchangeWithKeys(
masterPublicKey: exchangeKeysJson.master_public_key,
protocolVersion: protocolVersion,
signingKeys: exchangeKeysJson.signkeys,
- nextUpdateTime: getExpiryTimestamp(resp),
+ nextUpdateTime: getExpiryTimestamp(resp, {
+ minDuration: durationFromSpec({ hours: 1 }),
+ }),
};
r.updateStatus = ExchangeUpdateStatus.FetchWire;
r.lastError = undefined;
+ r.retryInfo = initRetryInfo(false);
await tx.put(Stores.exchanges, r);
for (const newDenom of newDenominations) {
@@ -433,6 +450,7 @@ async function updateExchangeWithWireInfo(
};
r.updateStatus = ExchangeUpdateStatus.FetchTerms;
r.lastError = undefined;
+ r.retryInfo = initRetryInfo(false);
await tx.put(Stores.exchanges, r);
});
}
@@ -443,7 +461,7 @@ export async function updateExchangeFromUrl(
forceNow = false,
): Promise<ExchangeRecord> {
const onOpErr = (e: TalerErrorDetails): Promise<void> =>
- setExchangeError(ws, baseUrl, e);
+ handleExchangeUpdateError(ws, baseUrl, e);
return await guardOperationException(
() => updateExchangeFromUrlImpl(ws, baseUrl, forceNow),
onOpErr,
@@ -460,6 +478,7 @@ async function updateExchangeFromUrlImpl(
baseUrl: string,
forceNow = false,
): Promise<ExchangeRecord> {
+ logger.trace(`updating exchange info for ${baseUrl}`);
const now = getTimestampNow();
baseUrl = canonicalizeBaseUrl(baseUrl);
@@ -480,6 +499,7 @@ async function updateExchangeFromUrlImpl(
termsOfServiceAcceptedTimestamp: undefined,
termsOfServiceLastEtag: undefined,
termsOfServiceText: undefined,
+ retryInfo: initRetryInfo(false),
};
await ws.db.put(Stores.exchanges, newExchangeRecord);
} else {
@@ -488,8 +508,11 @@ async function updateExchangeFromUrlImpl(
if (!rec) {
return;
}
- if (rec.updateStatus != ExchangeUpdateStatus.FetchKeys && !forceNow) {
- return;
+ if (rec.updateStatus != ExchangeUpdateStatus.FetchKeys) {
+ const t = rec.details?.nextUpdateTime;
+ if (!forceNow && t && !isTimestampExpired(t)) {
+ return;
+ }
}
if (rec.updateStatus != ExchangeUpdateStatus.FetchKeys && forceNow) {
rec.updateReason = ExchangeUpdateReason.Forced;
@@ -497,31 +520,18 @@ async function updateExchangeFromUrlImpl(
rec.updateStarted = now;
rec.updateStatus = ExchangeUpdateStatus.FetchKeys;
rec.lastError = undefined;
+ rec.retryInfo = initRetryInfo(false);
t.put(Stores.exchanges, rec);
});
}
- r = await ws.db.get(Stores.exchanges, baseUrl);
- checkDbInvariant(!!r);
-
-
- const t = r.details?.nextUpdateTime;
- if (!forceNow && t && !isTimestampExpired(t)) {
- logger.trace("using cached exchange info");
- return r;
- }
-
await updateExchangeWithKeys(ws, baseUrl);
await updateExchangeWithWireInfo(ws, baseUrl);
await updateExchangeWithTermsOfService(ws, baseUrl);
await updateExchangeFinalize(ws, baseUrl);
const updatedExchange = await ws.db.get(Stores.exchanges, baseUrl);
-
- if (!updatedExchange) {
- // This should practically never happen
- throw Error("exchange not found");
- }
+ checkDbInvariant(!!updatedExchange);
return updatedExchange;
}
diff --git a/packages/taler-wallet-core/src/operations/pending.ts
b/packages/taler-wallet-core/src/operations/pending.ts
index 88196162..8cbc5e56 100644
--- a/packages/taler-wallet-core/src/operations/pending.ts
+++ b/packages/taler-wallet-core/src/operations/pending.ts
@@ -56,10 +56,6 @@ async function gatherExchangePending(
resp: PendingOperationsResponse,
onlyDue = false,
): Promise<void> {
- if (onlyDue) {
- // FIXME: exchanges should also be updated regularly
- return;
- }
await tx.iter(Stores.exchanges).forEach((e) => {
switch (e.updateStatus) {
case ExchangeUpdateStatus.Finished:
@@ -79,7 +75,7 @@ async function gatherExchangePending(
type: PendingOperationType.Bug,
givesLifeness: false,
message:
- "Exchange record does not have details, but no update in
progress.",
+ "Exchange record does not have details, but no update finished.",
details: {
exchangeBaseUrl: e.baseUrl,
},
@@ -90,14 +86,28 @@ async function gatherExchangePending(
type: PendingOperationType.Bug,
givesLifeness: false,
message:
- "Exchange record does not have wire info, but no update in
progress.",
+ "Exchange record does not have wire info, but no update
finished.",
details: {
exchangeBaseUrl: e.baseUrl,
},
});
}
+ if (e.details && e.details.nextUpdateTime.t_ms < now.t_ms) {
+ resp.pendingOperations.push({
+ type: PendingOperationType.ExchangeUpdate,
+ givesLifeness: false,
+ stage: ExchangeUpdateOperationStage.FetchKeys,
+ exchangeBaseUrl: e.baseUrl,
+ lastError: e.lastError,
+ reason: "scheduled",
+ });
+ break;
+ }
break;
case ExchangeUpdateStatus.FetchKeys:
+ if (onlyDue && e.retryInfo.nextRetry.t_ms > now.t_ms) {
+ return;
+ }
resp.pendingOperations.push({
type: PendingOperationType.ExchangeUpdate,
givesLifeness: false,
@@ -108,6 +118,9 @@ async function gatherExchangePending(
});
break;
case ExchangeUpdateStatus.FetchWire:
+ if (onlyDue && e.retryInfo.nextRetry.t_ms > now.t_ms) {
+ return;
+ }
resp.pendingOperations.push({
type: PendingOperationType.ExchangeUpdate,
givesLifeness: false,
@@ -118,6 +131,9 @@ async function gatherExchangePending(
});
break;
case ExchangeUpdateStatus.FinalizeUpdate:
+ if (onlyDue && e.retryInfo.nextRetry.t_ms > now.t_ms) {
+ return;
+ }
resp.pendingOperations.push({
type: PendingOperationType.ExchangeUpdate,
givesLifeness: false,
diff --git a/packages/taler-wallet-core/src/types/dbTypes.ts
b/packages/taler-wallet-core/src/types/dbTypes.ts
index b74a9ce3..801bb449 100644
--- a/packages/taler-wallet-core/src/types/dbTypes.ts
+++ b/packages/taler-wallet-core/src/types/dbTypes.ts
@@ -113,6 +113,7 @@ export function updateRetryInfoTimeout(
r.nextRetry = { t_ms: "never" };
return;
}
+ r.active = true;
const t =
now.t_ms + p.backoffDelta.d_ms * Math.pow(p.backoffBase, r.retryCounter);
r.nextRetry = { t_ms: t };
@@ -642,6 +643,11 @@ export interface ExchangeRecord {
updateReason?: ExchangeUpdateReason;
lastError?: TalerErrorDetails;
+
+ /**
+ * Retry status for fetching updated information about the exchange.
+ */
+ retryInfo: RetryInfo;
}
diff --git a/packages/taler-wallet-core/src/util/http.ts
b/packages/taler-wallet-core/src/util/http.ts
index 0977b429..e050efe6 100644
--- a/packages/taler-wallet-core/src/util/http.ts
+++ b/packages/taler-wallet-core/src/util/http.ts
@@ -26,7 +26,7 @@ import { Codec } from "./codec";
import { OperationFailedError, makeErrorDetails } from "../operations/errors";
import { TalerErrorCode } from "../TalerErrorCode";
import { Logger } from "./logging";
-import { Duration, Timestamp, getTimestampNow } from "./time";
+import { Duration, Timestamp, getTimestampNow, timestampAddDuration,
timestampMin, timestampMax } from "./time";
const logger = new Logger("http.ts");
@@ -257,15 +257,24 @@ export async function readSuccessResponseTextOrThrow<T>(
/**
* Get the timestamp at which the response's content is considered expired.
*/
-export function getExpiryTimestamp(httpResponse: HttpResponse): Timestamp {
+export function getExpiryTimestamp(
+ httpResponse: HttpResponse,
+ opt: { minDuration?: Duration },
+): Timestamp {
const expiryDateMs = new Date(
httpResponse.headers.get("expiry") ?? "",
).getTime();
+ let t: Timestamp;
if (Number.isNaN(expiryDateMs)) {
- return getTimestampNow();
+ t = getTimestampNow();
} else {
- return {
+ t = {
t_ms: expiryDateMs,
- }
+ };
+ }
+ if (opt.minDuration) {
+ const t2 = timestampAddDuration(getTimestampNow(), opt.minDuration);
+ return timestampMax(t, t2);
}
+ return t;
}
diff --git a/packages/taler-wallet-core/src/util/time.ts
b/packages/taler-wallet-core/src/util/time.ts
index ff4c1885..1641924a 100644
--- a/packages/taler-wallet-core/src/util/time.ts
+++ b/packages/taler-wallet-core/src/util/time.ts
@@ -76,6 +76,38 @@ export function timestampMin(t1: Timestamp, t2: Timestamp):
Timestamp {
return { t_ms: Math.min(t1.t_ms, t2.t_ms) };
}
+export function timestampMax(t1: Timestamp, t2: Timestamp): Timestamp {
+ if (t1.t_ms === "never") {
+ return { t_ms: "never" };
+ }
+ if (t2.t_ms === "never") {
+ return { t_ms: "never" };
+ }
+ return { t_ms: Math.max(t1.t_ms, t2.t_ms) };
+}
+
+const SECONDS = 1000
+const MINUTES = SECONDS * 60;
+const HOURS = MINUTES * 60;
+
+export function durationFromSpec(spec: {
+ seconds?: number,
+ hours?: number,
+ minutes?: number,
+}): Duration {
+ let d_ms = 0;
+ if (spec.seconds) {
+ d_ms += spec.seconds * SECONDS;
+ }
+ if (spec.minutes) {
+ d_ms += spec.minutes * MINUTES;
+ }
+ if (spec.hours) {
+ d_ms += spec.hours * HOURS;
+ }
+ return { d_ms };
+}
+
/**
* Truncate a timestamp so that that it represents a multiple
* of seconds. The timestamp is always rounded down.
--
To stop receiving notification emails like this one, please contact
gnunet@gnunet.org.
[Prev in Thread] |
Current Thread |
[Next in Thread] |
- [taler-wallet-core] branch master updated: schedule exchange updating,
gnunet <=