implement db garbage collection (fixes #4526, #4188)

This commit is contained in:
Florian Dold 2017-12-10 21:34:56 +01:00
parent b855c547fb
commit 6947e79bbc
No known key found for this signature in database
GPG Key ID: D2E4F00F29D02A4B
2 changed files with 95 additions and 5 deletions

View File

@ -505,6 +505,13 @@ export interface ExchangeRecord {
*/
lastUpdateTime: number;
/**
* When did we actually use this exchange last (in milliseconds). If we
* never used the exchange for anything but just updated its info, this is
* set to 0. (Currently only updated when reserves are created.)
*/
lastUsedTime: number;
/**
* Last observed protocol version.
*/

View File

@ -660,13 +660,17 @@ export class Wallet {
this.badge = badge;
this.notifier = notifier;
this.cryptoApi = new CryptoApi();
this.fillDefaults();
this.resumePendingFromDb();
this.timerGroup = new TimerGroup();
this.timerGroup.every(1000 * 60 * 15, () => this.updateExchanges());
const init = async () => {
await this.fillDefaults().catch((e) => console.log(e));
await this.collectGarbage().catch((e) => console.log(e));
this.updateExchanges();
this.resumePendingFromDb();
this.timerGroup.every(1000 * 60 * 15, () => this.updateExchanges());
};
init();
}
private async fillDefaults() {
@ -1215,6 +1219,19 @@ export class Wallet {
}
/**
* Update the timestamp of when an exchange was used.
*/
async updateExchangeUsedTime(exchangeBaseUrl: string): Promise<void> {
const now = (new Date()).getTime();
const update = (r: ExchangeRecord) => {
r.lastUsedTime = now;
return r;
};
await this.q().mutate(Stores.exchanges, exchangeBaseUrl, update).finish();
}
/**
* Create a reserve, but do not flag it as confirmed yet.
*
@ -1240,6 +1257,7 @@ export class Wallet {
timestamp_depleted: 0,
};
await this.updateExchangeUsedTime(req.exchange);
const exchangeInfo = await this.updateExchangeFromUrl(req.exchange);
const {isAudited, isTrusted} = await this.getExchangeTrust(exchangeInfo);
let currencyRecord = await this.q().get(Stores.currencies, exchangeInfo.currency);
@ -1734,6 +1752,7 @@ export class Wallet {
baseUrl,
currency: exchangeKeysJson.denoms[0].value.currency,
lastUpdateTime: updateTimeSec,
lastUsedTime: 0,
masterPublicKey: exchangeKeysJson.master_public_key,
};
console.log("making fresh exchange");
@ -2949,4 +2968,68 @@ export class Wallet {
};
return tipStatus;
}
/**
* Remove unreferenced / expired data from the wallet's database
* based on the current system time.
*/
async collectGarbage() {
const nowMilli = (new Date()).getTime();
const nowSec = Math.floor(nowMilli / 1000);
const gcReserve = (r: ReserveRecord, n: number) => {
// This rule to purge reserves is a bit over-eager, since we still might
// receive an emergency payback from the exchange. In this case we need
// to wait for the exchange to wire the money back or change this rule to
// wait until all coins from the reserve were spent.
if (r.timestamp_depleted) {
return true;
}
return false;
};
await this.q().deleteIf(Stores.reserves, gcReserve).finish();
const gcProposal = (d: ProposalRecord, n: number) => {
// Delete proposal after 60 minutes or 5 minutes before pay deadline,
// whatever comes first.
let deadlinePayMilli = getTalerStampSec(d.contractTerms.pay_deadline)! * 1000;
let deadlineExpireMilli = nowMilli + (1000 * 60 * 60);
return d.timestamp < Math.min(deadlinePayMilli, deadlineExpireMilli);
};
await this.q().deleteIf(Stores.proposals, gcProposal).finish();
const activeExchanges: string[] = [];
const gcExchange = (d: ExchangeRecord, n: number) => {
// Delete if if unused and last update more than 20 minutes ago
if (!d.lastUsedTime && nowMilli > d.lastUpdateTime + (1000 * 60 * 20)) {
return true;
}
activeExchanges.push(d.baseUrl);
return false;
}
await this.q().deleteIf(Stores.exchanges, gcExchange).finish();
const gcDenominations = (d: DenominationRecord, n: number) => {
if (nowSec > getTalerStampSec(d.stampExpireDeposit)!) {
return true;
}
if (activeExchanges.indexOf(d.exchangeBaseUrl) < 0) {
return true;
}
return false;
};
await this.q().deleteIf(Stores.denominations, gcDenominations).finish();
const gcWireFees = (r: ExchangeWireFeesRecord, n: number) => {
if (activeExchanges.indexOf(r.exchangeBaseUrl) < 0) {
return true;
}
return false;
};
await this.q().deleteIf(Stores.exchangeWireFees, gcWireFees).finish();
// FIXME(#5210) also GC coins
}
}