perf: reserve history in separate object store
This commit is contained in:
parent
277a513a8f
commit
857a2b9dca
@ -7,7 +7,7 @@ import { openDatabase, Database, Store, Index } from "./util/query";
|
||||
* with each major change. When incrementing the major version,
|
||||
* the wallet should import data from the previous version.
|
||||
*/
|
||||
const TALER_DB_NAME = "taler-walletdb-v2";
|
||||
const TALER_DB_NAME = "taler-walletdb-v3";
|
||||
|
||||
/**
|
||||
* Current database minor version, should be incremented
|
||||
|
@ -145,7 +145,7 @@ export async function getBalances(
|
||||
): Promise<WalletBalance> {
|
||||
logger.trace("starting to compute balance");
|
||||
|
||||
return await ws.db.runWithReadTransaction(
|
||||
const wbal = await ws.db.runWithReadTransaction(
|
||||
[
|
||||
Stores.coins,
|
||||
Stores.refreshGroups,
|
||||
@ -157,4 +157,8 @@ export async function getBalances(
|
||||
return getBalancesInsideTransaction(ws, tx);
|
||||
},
|
||||
);
|
||||
|
||||
logger.trace("finished computing wallet balance");
|
||||
|
||||
return wbal;
|
||||
}
|
||||
|
@ -172,6 +172,7 @@ export async function getHistory(
|
||||
Stores.purchases,
|
||||
Stores.refreshGroups,
|
||||
Stores.reserves,
|
||||
Stores.reserveHistory,
|
||||
Stores.tips,
|
||||
Stores.withdrawalGroups,
|
||||
Stores.payEvents,
|
||||
@ -384,8 +385,12 @@ export async function getHistory(
|
||||
type: ReserveType.Manual,
|
||||
};
|
||||
}
|
||||
const hist = await tx.get(Stores.reserveHistory, reserve.reservePub);
|
||||
if (!hist) {
|
||||
throw Error("inconsistent database");
|
||||
}
|
||||
const s = summarizeReserveHistory(
|
||||
reserve.reserveTransactions,
|
||||
hist.reserveTransactions,
|
||||
reserve.currency,
|
||||
);
|
||||
history.push({
|
||||
|
@ -33,8 +33,8 @@ import {
|
||||
updateRetryInfoTimeout,
|
||||
ReserveUpdatedEventRecord,
|
||||
WalletReserveHistoryItemType,
|
||||
PlanchetRecord,
|
||||
WithdrawalSourceType,
|
||||
ReserveHistoryRecord,
|
||||
} from "../types/dbTypes";
|
||||
import { Logger } from "../util/logging";
|
||||
import { Amounts } from "../util/amounts";
|
||||
@ -114,11 +114,15 @@ export async function createReserve(
|
||||
lastSuccessfulStatusQuery: undefined,
|
||||
retryInfo: initRetryInfo(),
|
||||
lastError: undefined,
|
||||
reserveTransactions: [],
|
||||
currency: req.amount.currency,
|
||||
};
|
||||
|
||||
reserveRecord.reserveTransactions.push({
|
||||
const reserveHistoryRecord: ReserveHistoryRecord = {
|
||||
reservePub: keypair.pub,
|
||||
reserveTransactions: [],
|
||||
};
|
||||
|
||||
reserveHistoryRecord.reserveTransactions.push({
|
||||
type: WalletReserveHistoryItemType.Credit,
|
||||
expectedAmount: req.amount,
|
||||
});
|
||||
@ -161,7 +165,12 @@ export async function createReserve(
|
||||
const cr: CurrencyRecord = currencyRecord;
|
||||
|
||||
const resp = await ws.db.runWithWriteTransaction(
|
||||
[Stores.currencies, Stores.reserves, Stores.bankWithdrawUris],
|
||||
[
|
||||
Stores.currencies,
|
||||
Stores.reserves,
|
||||
Stores.reserveHistory,
|
||||
Stores.bankWithdrawUris,
|
||||
],
|
||||
async (tx) => {
|
||||
// Check if we have already created a reserve for that bankWithdrawStatusUrl
|
||||
if (reserveRecord.bankWithdrawStatusUrl) {
|
||||
@ -188,6 +197,7 @@ export async function createReserve(
|
||||
}
|
||||
await tx.put(Stores.currencies, cr);
|
||||
await tx.put(Stores.reserves, reserveRecord);
|
||||
await tx.put(Stores.reserveHistory, reserveHistoryRecord);
|
||||
const r: CreateReserveResponse = {
|
||||
exchange: canonExchange,
|
||||
reservePub: keypair.pub,
|
||||
@ -462,7 +472,7 @@ async function updateReserve(
|
||||
const balance = Amounts.parseOrThrow(reserveInfo.balance);
|
||||
const currency = balance.currency;
|
||||
await ws.db.runWithWriteTransaction(
|
||||
[Stores.reserves, Stores.reserveUpdatedEvents],
|
||||
[Stores.reserves, Stores.reserveUpdatedEvents, Stores.reserveHistory],
|
||||
async (tx) => {
|
||||
const r = await tx.get(Stores.reserves, reservePub);
|
||||
if (!r) {
|
||||
@ -472,14 +482,19 @@ async function updateReserve(
|
||||
return;
|
||||
}
|
||||
|
||||
const hist = await tx.get(Stores.reserveHistory, reservePub);
|
||||
if (!hist) {
|
||||
throw Error("inconsistent database");
|
||||
}
|
||||
|
||||
const newHistoryTransactions = reserveInfo.history.slice(
|
||||
r.reserveTransactions.length,
|
||||
hist.reserveTransactions.length,
|
||||
);
|
||||
|
||||
const reserveUpdateId = encodeCrock(getRandomBytes(32));
|
||||
|
||||
const reconciled = reconcileReserveHistory(
|
||||
r.reserveTransactions,
|
||||
hist.reserveTransactions,
|
||||
reserveInfo.history,
|
||||
);
|
||||
|
||||
@ -514,9 +529,10 @@ async function updateReserve(
|
||||
r.retryInfo = initRetryInfo(false);
|
||||
}
|
||||
r.lastSuccessfulStatusQuery = getTimestampNow();
|
||||
r.reserveTransactions = reconciled.updatedLocalHistory;
|
||||
hist.reserveTransactions = reconciled.updatedLocalHistory;
|
||||
r.lastError = undefined;
|
||||
await tx.put(Stores.reserves, r);
|
||||
await tx.put(Stores.reserveHistory, hist);
|
||||
},
|
||||
);
|
||||
ws.notify({ type: NotificationType.ReserveUpdated });
|
||||
@ -602,17 +618,29 @@ async function depleteReserve(
|
||||
ws: InternalWalletState,
|
||||
reservePub: string,
|
||||
): Promise<void> {
|
||||
const reserve = await ws.db.get(Stores.reserves, reservePub);
|
||||
let reserve: ReserveRecord | undefined;
|
||||
let hist: ReserveHistoryRecord | undefined;
|
||||
await ws.db.runWithReadTransaction(
|
||||
[Stores.reserves, Stores.reserveHistory],
|
||||
async (tx) => {
|
||||
reserve = await tx.get(Stores.reserves, reservePub);
|
||||
hist = await tx.get(Stores.reserveHistory, reservePub);
|
||||
},
|
||||
);
|
||||
|
||||
if (!reserve) {
|
||||
return;
|
||||
}
|
||||
if (!hist) {
|
||||
throw Error("inconsistent database");
|
||||
}
|
||||
if (reserve.reserveStatus !== ReserveRecordStatus.WITHDRAWING) {
|
||||
return;
|
||||
}
|
||||
logger.trace(`depleting reserve ${reservePub}`);
|
||||
|
||||
const summary = summarizeReserveHistory(
|
||||
reserve.reserveTransactions,
|
||||
hist.reserveTransactions,
|
||||
reserve.currency,
|
||||
);
|
||||
|
||||
@ -674,7 +702,12 @@ async function depleteReserve(
|
||||
};
|
||||
|
||||
const success = await ws.db.runWithWriteTransaction(
|
||||
[Stores.withdrawalGroups, Stores.reserves, Stores.planchets],
|
||||
[
|
||||
Stores.withdrawalGroups,
|
||||
Stores.reserves,
|
||||
Stores.reserveHistory,
|
||||
Stores.planchets,
|
||||
],
|
||||
async (tx) => {
|
||||
const newReserve = await tx.get(Stores.reserves, reservePub);
|
||||
if (!newReserve) {
|
||||
@ -683,8 +716,12 @@ async function depleteReserve(
|
||||
if (newReserve.reserveStatus !== ReserveRecordStatus.WITHDRAWING) {
|
||||
return false;
|
||||
}
|
||||
const newHist = await tx.get(Stores.reserveHistory, reservePub);
|
||||
if (!newHist) {
|
||||
throw Error("inconsistent database");
|
||||
}
|
||||
const newSummary = summarizeReserveHistory(
|
||||
newReserve.reserveTransactions,
|
||||
newHist.reserveTransactions,
|
||||
newReserve.currency,
|
||||
);
|
||||
if (
|
||||
@ -703,7 +740,7 @@ async function depleteReserve(
|
||||
const sd = denomsForWithdraw.selectedDenoms[i];
|
||||
for (let j = 0; j < sd.count; j++) {
|
||||
const amt = Amounts.add(sd.denom.value, sd.denom.feeWithdraw).amount;
|
||||
newReserve.reserveTransactions.push({
|
||||
newHist.reserveTransactions.push({
|
||||
type: WalletReserveHistoryItemType.Withdraw,
|
||||
expectedAmount: amt,
|
||||
});
|
||||
@ -712,6 +749,7 @@ async function depleteReserve(
|
||||
newReserve.reserveStatus = ReserveRecordStatus.DORMANT;
|
||||
newReserve.retryInfo = initRetryInfo(false);
|
||||
await tx.put(Stores.reserves, newReserve);
|
||||
await tx.put(Stores.reserveHistory, newHist);
|
||||
await tx.put(Stores.withdrawalGroups, withdrawalRecord);
|
||||
return true;
|
||||
},
|
||||
|
@ -564,7 +564,7 @@ async function processWithdrawGroupImpl(
|
||||
|
||||
// Withdraw coins in batches.
|
||||
// The batch size is relatively large
|
||||
await processInBatches(genWork(), 50);
|
||||
await processInBatches(genWork(), 10);
|
||||
}
|
||||
|
||||
export async function getExchangeWithdrawalInfo(
|
||||
|
@ -210,6 +210,11 @@ export type WalletReserveHistoryItem =
|
||||
| WalletReserveHistoryRecoupItem
|
||||
| WalletReserveHistoryClosingItem;
|
||||
|
||||
export interface ReserveHistoryRecord {
|
||||
reservePub: string;
|
||||
reserveTransactions: WalletReserveHistoryItem[];
|
||||
}
|
||||
|
||||
/**
|
||||
* A reserve record as stored in the wallet's database.
|
||||
*/
|
||||
@ -295,8 +300,6 @@ export interface ReserveRecord {
|
||||
* (either talking to the bank or the exchange).
|
||||
*/
|
||||
lastError: OperationError | undefined;
|
||||
|
||||
reserveTransactions: WalletReserveHistoryItem[];
|
||||
}
|
||||
|
||||
/**
|
||||
@ -1639,6 +1642,12 @@ export namespace Stores {
|
||||
}
|
||||
}
|
||||
|
||||
class ReserveHistoryStore extends Store<ReserveHistoryRecord> {
|
||||
constructor() {
|
||||
super("reserveHistory", { keyPath: "reservePub" });
|
||||
}
|
||||
}
|
||||
|
||||
class TipsStore extends Store<TipRecord> {
|
||||
constructor() {
|
||||
super("tips", { keyPath: "tipId" });
|
||||
@ -1725,6 +1734,7 @@ export namespace Stores {
|
||||
keyPath: "recoupGroupId",
|
||||
});
|
||||
export const reserves = new ReservesStore();
|
||||
export const reserveHistory = new ReserveHistoryStore();
|
||||
export const purchases = new PurchasesStore();
|
||||
export const tips = new TipsStore();
|
||||
export const senderWires = new SenderWiresStore();
|
||||
|
@ -177,6 +177,7 @@ class WalletBalanceView extends React.Component<any, any> {
|
||||
private gotError = false;
|
||||
private canceler: (() => void) | undefined = undefined;
|
||||
private unmount = false;
|
||||
private updateBalanceRunning = false;
|
||||
|
||||
componentWillMount(): void {
|
||||
this.canceler = wxApi.onUpdateNotification(() => this.updateBalance());
|
||||
@ -192,6 +193,10 @@ class WalletBalanceView extends React.Component<any, any> {
|
||||
}
|
||||
|
||||
async updateBalance(): Promise<void> {
|
||||
if (this.updateBalanceRunning) {
|
||||
return;
|
||||
}
|
||||
this.updateBalanceRunning = true;
|
||||
let balance: WalletBalance;
|
||||
try {
|
||||
balance = await wxApi.getBalance();
|
||||
@ -203,6 +208,8 @@ class WalletBalanceView extends React.Component<any, any> {
|
||||
console.error("could not retrieve balances", e);
|
||||
this.setState({});
|
||||
return;
|
||||
} finally {
|
||||
this.updateBalanceRunning = false;
|
||||
}
|
||||
if (this.unmount) {
|
||||
return;
|
||||
|
Loading…
Reference in New Issue
Block a user