aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMarco Boss <bossm8@bfh.ch>2022-05-09 14:28:12 +0200
committerMarco Boss <bossm8@bfh.ch>2022-05-09 14:28:12 +0200
commit935a119621a9ff7a79c410d6cc1bf0c9daa89216 (patch)
tree422103e50578861d6dee9dd3226207edef5a4fa5
parent37690dd22c1c4cd63f3fa4b1d600b3da1cbae152 (diff)
add bench3 for zipf and random merchant selection (by id)
-rw-r--r--packages/taler-wallet-cli/src/bench2.ts2
-rw-r--r--packages/taler-wallet-cli/src/bench3.ts204
-rw-r--r--packages/taler-wallet-cli/src/benchMerchantIDGenerator.ts83
-rw-r--r--packages/taler-wallet-cli/src/index.ts16
4 files changed, 304 insertions, 1 deletions
diff --git a/packages/taler-wallet-cli/src/bench2.ts b/packages/taler-wallet-cli/src/bench2.ts
index 2ee53329a..a51b98c15 100644
--- a/packages/taler-wallet-cli/src/bench2.ts
+++ b/packages/taler-wallet-cli/src/bench2.ts
@@ -44,7 +44,7 @@ import {
* set up its own services.
*/
export async function runBench2(configJson: any): Promise<void> {
- const logger = new Logger("Bench1");
+ const logger = new Logger("Bench2");
// Validate the configuration file for this benchmark.
const benchConf = codecForBench2Config().decode(configJson);
diff --git a/packages/taler-wallet-cli/src/bench3.ts b/packages/taler-wallet-cli/src/bench3.ts
new file mode 100644
index 000000000..1d3c86cd6
--- /dev/null
+++ b/packages/taler-wallet-cli/src/bench3.ts
@@ -0,0 +1,204 @@
+/*
+ This file is part of GNU Taler
+ (C) 2021 Taler Systems S.A.
+
+ GNU Taler is free software; you can redistribute it and/or modify it under the
+ terms of the GNU General Public License as published by the Free Software
+ Foundation; either version 3, or (at your option) any later version.
+
+ GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY
+ WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+ A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License along with
+ GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
+ */
+
+/**
+ * Imports.
+ */
+import {
+ buildCodecForObject,
+ codecForNumber,
+ codecForString,
+ codecOptional,
+ j2s,
+ Logger,
+} from "@gnu-taler/taler-util";
+import {
+ getDefaultNodeWallet2,
+ NodeHttpLib,
+ WalletApiOperation,
+ Wallet,
+ AccessStats,
+} from "@gnu-taler/taler-wallet-core";
+import benchMerchantIDGenerator from "./benchMerchantIDGenerator.js";
+
+/**
+ * Entry point for the benchmark.
+ *
+ * The benchmark runs against an existing Taler deployment and does not
+ * set up its own services.
+ */
+export async function runBench3(configJson: any): Promise<void> {
+ const logger = new Logger("Bench3");
+
+ // Validate the configuration file for this benchmark.
+ const b3conf = codecForBench3Config().decode(configJson);
+
+ if (!b3conf.paytoTemplate.includes("${id")) {
+ throw new Error("Payto template url must contain '${id}' placeholder");
+ }
+
+ const myHttpLib = new NodeHttpLib();
+ myHttpLib.setThrottling(false);
+
+ const numIter = b3conf.iterations ?? 1;
+ const numDeposits = b3conf.deposits ?? 5;
+ const restartWallet = b3conf.restartAfter ?? 20;
+
+ const withdrawAmount = (numDeposits + 1) * 10;
+
+ const IDGenerator = benchMerchantIDGenerator(b3conf.randomAlg, b3conf.numMerchants ?? 100);
+
+ logger.info(
+ `Starting Benchmark iterations=${numIter} deposits=${numDeposits} with ${b3conf.randomAlg} merchant selection`,
+ );
+
+ const trustExchange = !!process.env["TALER_WALLET_INSECURE_TRUST_EXCHANGE"];
+ if (trustExchange) {
+ logger.info("trusting exchange (not validating signatures)");
+ } else {
+ logger.info("not trusting exchange (validating signatures)");
+ }
+
+ let wallet = {} as Wallet;
+ let getDbStats: () => AccessStats;
+
+ for (let i = 0; i < numIter; i++) {
+ // Create a new wallet in each iteration
+ // otherwise the TPS go down
+ // my assumption is that the in-memory db file gets too large
+ if (i % restartWallet == 0) {
+ if (Object.keys(wallet).length !== 0) {
+ wallet.stop();
+ console.log("wallet DB stats", j2s(getDbStats!()));
+ }
+
+ const res = await getDefaultNodeWallet2({
+ // No persistent DB storage.
+ persistentStoragePath: undefined,
+ httpLib: myHttpLib,
+ });
+ wallet = res.wallet;
+ getDbStats = res.getDbStats;
+ if (trustExchange) {
+ wallet.setInsecureTrustExchange();
+ }
+ wallet.setBatchWithdrawal(true);
+ await wallet.client.call(WalletApiOperation.InitWallet, {});
+ }
+
+ logger.trace(`Starting withdrawal amount=${withdrawAmount}`);
+ let start = Date.now();
+
+ await wallet.client.call(WalletApiOperation.WithdrawFakebank, {
+ amount: b3conf.currency + ":" + withdrawAmount,
+ bank: b3conf.bank,
+ exchange: b3conf.exchange,
+ });
+
+ await wallet.runTaskLoop({
+ stopWhenDone: true,
+ });
+
+ logger.info(
+ `Finished withdrawal amount=${withdrawAmount} time=${Date.now() - start}`,
+ );
+
+ for (let i = 0; i < numDeposits; i++) {
+ logger.trace(`Starting deposit amount=10`);
+ start = Date.now();
+
+ let merchID = IDGenerator.getRandomMerchantID();
+ let payto = b3conf.paytoTemplate.replace("${id}", merchID.toString());
+
+ await wallet.client.call(WalletApiOperation.CreateDepositGroup, {
+ amount: b3conf.currency + ":10",
+ depositPaytoUri: payto,
+ });
+
+ await wallet.runTaskLoop({
+ stopWhenDone: true,
+ });
+
+ logger.info(`Finished deposit amount=10 time=${Date.now() - start}`);
+ }
+ }
+
+ wallet.stop();
+ console.log("wallet DB stats", j2s(getDbStats!()));
+}
+
+/**
+ * Format of the configuration file passed to the benchmark
+ */
+interface Bench3Config {
+ /**
+ * Base URL of the bank.
+ */
+ bank: string;
+
+ /**
+ * Payto url template for deposits, must contain '${id}' for replacements.
+ */
+ paytoTemplate: string;
+
+ /**
+ * Base URL of the exchange.
+ */
+ exchange: string;
+
+ /**
+ * How many withdraw/deposit iterations should be made?
+ * Defaults to 1.
+ */
+ iterations?: number;
+
+ currency: string;
+
+ deposits?: number;
+
+ /**
+ * How any iterations run until the wallet db gets purged
+ * Defaults to 20.
+ */
+ restartAfter?: number;
+
+ /**
+ * Number of merchants to select from randomly
+ */
+ numMerchants?: number;
+
+ /**
+ * Which random generator to use.
+ * Possible values: 'zipf', 'rand'
+ */
+ randomAlg: string;
+}
+
+/**
+ * Schema validation codec for Bench1Config.
+ */
+const codecForBench3Config = () =>
+ buildCodecForObject<Bench3Config>()
+ .property("bank", codecForString())
+ .property("paytoTemplate", codecForString())
+ .property("numMerchants", codecOptional(codecForNumber()))
+ .property("randomAlg", codecForString())
+ .property("exchange", codecForString())
+ .property("iterations", codecOptional(codecForNumber()))
+ .property("deposits", codecOptional(codecForNumber()))
+ .property("currency", codecForString())
+ .property("restartAfter", codecOptional(codecForNumber()))
+ .build("Bench1Config");
diff --git a/packages/taler-wallet-cli/src/benchMerchantIDGenerator.ts b/packages/taler-wallet-cli/src/benchMerchantIDGenerator.ts
new file mode 100644
index 000000000..b83c12bb8
--- /dev/null
+++ b/packages/taler-wallet-cli/src/benchMerchantIDGenerator.ts
@@ -0,0 +1,83 @@
+/*
+ This file is part of GNU Taler
+ (C) 2022 Taler Systems S.A.
+
+ GNU Taler is free software; you can redistribute it and/or modify it under the
+ terms of the GNU General Public License as published by the Free Software
+ Foundation; either version 3, or (at your option) any later version.
+
+ GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY
+ WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+ A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License along with
+ GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
+
+ @author: Boss Marco
+ */
+
+const getRandomInt = function(max: number) {
+ return Math.floor(Math.random() * max);
+}
+
+abstract class BenchMerchantIDGenerator {
+ abstract getRandomMerchantID(): number
+}
+
+class ZipfGenerator extends BenchMerchantIDGenerator {
+
+ weights: number[];
+ total_weight: number;
+
+ constructor(numMerchants: number) {
+ super();
+ this.weights = new Array<number>(numMerchants);
+ for (var i = 0; i < this.weights.length; i++) {
+ /* we use integers (floor), make sure we have big enough values
+ * by multiplying with
+ * numMerchants again */
+ this.weights[i] = Math.floor((numMerchants/(i+1)) * numMerchants);
+ }
+ this.total_weight = this.weights.reduce((p, n) => p + n);
+ }
+
+ getRandomMerchantID(): number {
+ let random = getRandomInt(this.total_weight);
+ let current = 0;
+
+ for (var i = 0; i < this.weights.length; i++) {
+ current += this.weights[i];
+ if (random <= current) {
+ return i+1;
+ }
+ }
+
+ /* should never come here */
+ return getRandomInt(this.weights.length);
+ }
+}
+
+class RandomGenerator extends BenchMerchantIDGenerator {
+
+ max: number
+
+ constructor(numMerchants: number) {
+ super();
+ this.max = numMerchants
+ }
+
+ getRandomMerchantID() {
+ return getRandomInt(this.max);
+ }
+}
+
+export default function(type: string, maxID: number): BenchMerchantIDGenerator {
+ switch (type) {
+ case "zipf":
+ return new ZipfGenerator(maxID);
+ case "rand":
+ return new RandomGenerator(maxID);
+ default:
+ throw new Error("Valid types are 'zipf' and 'rand'");
+ }
+}
diff --git a/packages/taler-wallet-cli/src/index.ts b/packages/taler-wallet-cli/src/index.ts
index 5ba6e4bf2..43bed3cc1 100644
--- a/packages/taler-wallet-cli/src/index.ts
+++ b/packages/taler-wallet-cli/src/index.ts
@@ -65,6 +65,7 @@ import { runBench1 } from "./bench1.js";
import { runEnv1 } from "./env1.js";
import { GlobalTestState, runTestWithState } from "./harness/harness.js";
import { runBench2 } from "./bench2.js";
+import { runBench3 } from "./bench3.js";
import {
TalerCryptoInterface,
TalerCryptoInterfaceR,
@@ -694,6 +695,21 @@ advancedCli
});
advancedCli
+ .subcommand("bench3", "bench3", {
+ help: "Run the 'bench3' benchmark",
+ })
+ .requiredOption("configJson", ["--config-json"], clk.STRING)
+ .action(async (args) => {
+ let config: any;
+ try {
+ config = JSON.parse(args.bench3.configJson);
+ } catch (e) {
+ console.log("Could not parse config JSON");
+ }
+ await runBench3(config);
+ });
+
+advancedCli
.subcommand("env1", "env1", {
help: "Run a test environment for bench1",
})