diff options
Diffstat (limited to 'packages')
| -rw-r--r-- | packages/taler-wallet-cli/src/bench2.ts | 2 | ||||
| -rw-r--r-- | packages/taler-wallet-cli/src/bench3.ts | 204 | ||||
| -rw-r--r-- | packages/taler-wallet-cli/src/benchMerchantIDGenerator.ts | 83 | ||||
| -rw-r--r-- | packages/taler-wallet-cli/src/index.ts | 16 | 
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",    })  | 
