commit 38ce7fa7d360786da460493fb1887daf19199976
parent 29b412cea0846c416eed2c83b8236162b7fbeb61
Author: Antoine A <>
Date: Tue, 4 Mar 2025 12:42:00 +0100
common: fix bench db
Diffstat:
3 files changed, 245 insertions(+), 241 deletions(-)
diff --git a/bank/src/test/kotlin/bench.kt b/bank/src/test/kotlin/bench.kt
@@ -115,237 +115,226 @@ class Bench {
}
@Test
- fun benchDb() {
- val ITER = System.getenv("BENCH_ITER")?.toIntOrNull() ?: 1
- val AMOUNT = System.getenv("BENCH_AMOUNT")?.toIntOrNull() ?: 10
+ fun benchDb() = bench { AMOUNT -> bankSetup { db ->
+ // Prepare custoemr accounts
+ fillCashoutInfo("customer")
+ setMaxDebt("customer", "KUDOS:1000000")
- if (ITER == 0) {
- println("Skip benchmark, missing BENCH_ITER")
- return
+ // Generate data
+ db.conn { genData(it, AMOUNT) }
+
+ // Warm HTTP client
+ client.get("/config").assertOk()
+
+ // Accounts
+ measureAction("account_create") {
+ client.post("/accounts") {
+ json {
+ "username" to "account_bench_$it"
+ "password" to "account_bench_$it-password"
+ "name" to "Bench Account $it"
+ }
+ }.assertOkJson<RegisterAccountResponse>().internal_payto_uri
+ }
+ measureAction("account_reconfig") {
+ client.patchA("/accounts/account_bench_$it") {
+ json {
+ "name" to "New Bench Account $it"
+ }
+ }.assertNoContent()
+ }
+ measureAction("account_reconfig_auth") {
+ client.patchA("/accounts/account_bench_$it/auth") {
+ json {
+ "old_password" to "account_bench_$it-password"
+ "new_password" to "account_bench_$it-password"
+ }
+ }.assertNoContent()
+ }
+ measureAction("account_list") {
+ client.getAdmin("/accounts").assertOk()
+ }
+ measureAction("account_list_public") {
+ client.get("/public-accounts").assertOk()
+ }
+ measureAction("account_get") {
+ client.getA("/accounts/account_bench_$it").assertOk()
+ }
+
+ // Tokens
+ val tokens = measureAction("token_create") {
+ client.postPw("/accounts/customer/token") {
+ json {
+ "scope" to "readonly"
+ "refreshable" to true
+ }
+ }.assertOkJson<TokenSuccessResponse>().access_token
+ }
+ measureAction("token_refresh") {
+ client.post("/accounts/customer/token") {
+ headers[HttpHeaders.Authorization] = "Bearer ${tokens[it]}"
+ json { "scope" to "readonly" }
+ }.assertOk()
+ }
+ measureAction("token_list") {
+ client.getA("/accounts/customer/tokens").assertOk()
+ }
+ measureAction("token_delete") {
+ client.delete("/accounts/customer/token") {
+ headers[HttpHeaders.Authorization] = "Bearer ${tokens[it]}"
+ }.assertNoContent()
}
- println("Bench $ITER times with $AMOUNT rows")
- bench(ITER) { bankSetup { db ->
- // Prepare custoemr accounts
- fillCashoutInfo("customer")
- setMaxDebt("customer", "KUDOS:1000000")
+ // Transaction
+ val transactions = measureAction("transaction_create") {
+ client.postA("/accounts/customer/transactions") {
+ json {
+ "payto_uri" to "$merchantPayto?receiver-name=Test&message=payout"
+ "amount" to "KUDOS:0.0001"
+ }
+ }.assertOkJson<TransactionCreateResponse>().row_id
+ }
+ measureAction("transaction_get") {
+ client.getA("/accounts/customer/transactions/${transactions[it]}").assertOk()
+ }
+ measureAction("transaction_history") {
+ client.getA("/accounts/customer/transactions").assertOk()
+ }
+ measureAction("transaction_revenue") {
+ client.getA("/accounts/merchant/taler-revenue/history").assertOk()
+ }
- // Generate data
- db.conn { genData(it, AMOUNT) }
+ // Withdrawal
+ val withdrawals = measureAction("withdrawal_create") {
+ client.postA("/accounts/customer/withdrawals") {
+ json {
+ "amount" to "KUDOS:0.0001"
+ }
+ }.assertOkJson<BankAccountCreateWithdrawalResponse>().withdrawal_id
+ }
+ measureAction("withdrawal_get") {
+ client.get("/withdrawals/${withdrawals[it]}").assertOk()
+ }
+ measureAction("withdrawal_status") {
+ client.get("/taler-integration/withdrawal-operation/${withdrawals[it]}").assertOk()
+ }
+ measureAction("withdrawal_select") {
+ client.post("/taler-integration/withdrawal-operation/${withdrawals[it]}") {
+ json {
+ "reserve_pub" to EddsaPublicKey.rand()
+ "selected_exchange" to exchangePayto
+ }
+ }.assertOk()
+ }
+ measureAction("withdrawal_confirm") {
+ client.postA("/accounts/customer/withdrawals/${withdrawals[it]}/confirm")
+ .assertNoContent()
+ }
+ measureAction("withdrawal_abort") {
+ val uuid = client.postA("/accounts/customer/withdrawals") {
+ json {
+ "amount" to "KUDOS:0.0001"
+ }
+ }.assertOkJson<BankAccountCreateWithdrawalResponse>().withdrawal_id
+ client.postA("/accounts/customer/withdrawals/$uuid/abort")
+ .assertNoContent()
+ }
- // Warm HTTP client
- client.get("/config").assertOk()
-
- // Accounts
- measureAction("account_create") {
- client.post("/accounts") {
- json {
- "username" to "account_bench_$it"
- "password" to "account_bench_$it-password"
- "name" to "Bench Account $it"
- }
- }.assertOkJson<RegisterAccountResponse>().internal_payto_uri
- }
- measureAction("account_reconfig") {
- client.patchA("/accounts/account_bench_$it") {
- json {
- "name" to "New Bench Account $it"
- }
- }.assertNoContent()
- }
- measureAction("account_reconfig_auth") {
- client.patchA("/accounts/account_bench_$it/auth") {
- json {
- "old_password" to "account_bench_$it-password"
- "new_password" to "account_bench_$it-password"
- }
- }.assertNoContent()
- }
- measureAction("account_list") {
- client.getAdmin("/accounts").assertOk()
- }
- measureAction("account_list_public") {
- client.get("/public-accounts").assertOk()
- }
- measureAction("account_get") {
- client.getA("/accounts/account_bench_$it").assertOk()
- }
-
- // Tokens
- val tokens = measureAction("token_create") {
- client.postPw("/accounts/customer/token") {
- json {
- "scope" to "readonly"
- "refreshable" to true
- }
- }.assertOkJson<TokenSuccessResponse>().access_token
- }
- measureAction("token_refresh") {
- client.post("/accounts/customer/token") {
- headers[HttpHeaders.Authorization] = "Bearer ${tokens[it]}"
- json { "scope" to "readonly" }
- }.assertOk()
- }
- measureAction("token_list") {
- client.getA("/accounts/customer/tokens").assertOk()
- }
- measureAction("token_delete") {
- client.delete("/accounts/customer/token") {
- headers[HttpHeaders.Authorization] = "Bearer ${tokens[it]}"
- }.assertNoContent()
- }
-
- // Transaction
- val transactions = measureAction("transaction_create") {
- client.postA("/accounts/customer/transactions") {
- json {
- "payto_uri" to "$merchantPayto?receiver-name=Test&message=payout"
- "amount" to "KUDOS:0.0001"
- }
- }.assertOkJson<TransactionCreateResponse>().row_id
- }
- measureAction("transaction_get") {
- client.getA("/accounts/customer/transactions/${transactions[it]}").assertOk()
- }
- measureAction("transaction_history") {
- client.getA("/accounts/customer/transactions").assertOk()
- }
- measureAction("transaction_revenue") {
- client.getA("/accounts/merchant/taler-revenue/history").assertOk()
- }
-
- // Withdrawal
- val withdrawals = measureAction("withdrawal_create") {
- client.postA("/accounts/customer/withdrawals") {
- json {
- "amount" to "KUDOS:0.0001"
- }
- }.assertOkJson<BankAccountCreateWithdrawalResponse>().withdrawal_id
- }
- measureAction("withdrawal_get") {
- client.get("/withdrawals/${withdrawals[it]}").assertOk()
- }
- measureAction("withdrawal_status") {
- client.get("/taler-integration/withdrawal-operation/${withdrawals[it]}").assertOk()
- }
- measureAction("withdrawal_select") {
- client.post("/taler-integration/withdrawal-operation/${withdrawals[it]}") {
- json {
- "reserve_pub" to EddsaPublicKey.rand()
- "selected_exchange" to exchangePayto
- }
- }.assertOk()
- }
- measureAction("withdrawal_confirm") {
- client.postA("/accounts/customer/withdrawals/${withdrawals[it]}/confirm")
- .assertNoContent()
- }
- measureAction("withdrawal_abort") {
- val uuid = client.postA("/accounts/customer/withdrawals") {
- json {
- "amount" to "KUDOS:0.0001"
- }
- }.assertOkJson<BankAccountCreateWithdrawalResponse>().withdrawal_id
- client.postA("/accounts/customer/withdrawals/$uuid/abort")
- .assertNoContent()
- }
-
- // Cashout
- convert("KUDOS:0.1")
- val cashouts = measureAction("cashout_create") {
- client.postA("/accounts/customer/cashouts") {
- json {
- "request_uid" to ShortHashCode.rand()
- "amount_debit" to "KUDOS:0.1"
- "amount_credit" to convert("KUDOS:0.1")
- }
- }.assertOkJson<CashoutResponse>().cashout_id
- }
- measureAction("cashout_get") {
- client.getA("/accounts/customer/cashouts/${cashouts[it]}").assertOk()
- }
- measureAction("cashout_history") {
- client.getA("/accounts/customer/cashouts").assertOk()
- }
- measureAction("cashout_history_admin") {
- client.getAdmin("/cashouts").assertOk()
- }
+ // Cashout
+ convert("KUDOS:0.1")
+ val cashouts = measureAction("cashout_create") {
+ client.postA("/accounts/customer/cashouts") {
+ json {
+ "request_uid" to ShortHashCode.rand()
+ "amount_debit" to "KUDOS:0.1"
+ "amount_credit" to convert("KUDOS:0.1")
+ }
+ }.assertOkJson<CashoutResponse>().cashout_id
+ }
+ measureAction("cashout_get") {
+ client.getA("/accounts/customer/cashouts/${cashouts[it]}").assertOk()
+ }
+ measureAction("cashout_history") {
+ client.getA("/accounts/customer/cashouts").assertOk()
+ }
+ measureAction("cashout_history_admin") {
+ client.getAdmin("/cashouts").assertOk()
+ }
- // Wire gateway
- val transfers = measureAction("wg_transfer") {
- client.postA("/accounts/exchange/taler-wire-gateway/transfer") {
- json {
- "request_uid" to HashCode.rand()
- "amount" to "KUDOS:0.0001"
- "exchange_base_url" to "http://exchange.example.com/"
- "wtid" to ShortHashCode.rand()
- "credit_account" to customerPayto.canonical
- }
- }.assertOkJson<TransferResponse>().row_id
- }
- measureAction("wg_transfer_get") {
- client.getA("/accounts/exchange/taler-wire-gateway/transfers/${transfers[it]}").assertOk()
- }
- measureAction("wg_transfer_page") {
- client.getA("/accounts/exchange/taler-wire-gateway/transfers").assertOk()
- }
- measureAction("wg_transfer_page_filter") {
- client.getA("/accounts/exchange/taler-wire-gateway/transfers?status=success").assertOk()
- }
- measureAction("wg_add") {
- client.postA("/accounts/exchange/taler-wire-gateway/admin/add-incoming") {
- json {
- "amount" to "KUDOS:0.0001"
- "reserve_pub" to EddsaPublicKey.rand()
- "debit_account" to customerPayto.canonical
- }
- }.assertOk()
- }
- measureAction("wg_incoming") {
- client.getA("/accounts/exchange/taler-wire-gateway/history/incoming")
- .assertOk()
- }
- measureAction("wg_outgoing") {
- client.getA("/accounts/exchange/taler-wire-gateway/history/outgoing")
- .assertOk()
- }
+ // Wire gateway
+ val transfers = measureAction("wg_transfer") {
+ client.postA("/accounts/exchange/taler-wire-gateway/transfer") {
+ json {
+ "request_uid" to HashCode.rand()
+ "amount" to "KUDOS:0.0001"
+ "exchange_base_url" to "http://exchange.example.com/"
+ "wtid" to ShortHashCode.rand()
+ "credit_account" to customerPayto.canonical
+ }
+ }.assertOkJson<TransferResponse>().row_id
+ }
+ measureAction("wg_transfer_get") {
+ client.getA("/accounts/exchange/taler-wire-gateway/transfers/${transfers[it]}").assertOk()
+ }
+ measureAction("wg_transfer_page") {
+ client.getA("/accounts/exchange/taler-wire-gateway/transfers").assertOk()
+ }
+ measureAction("wg_transfer_page_filter") {
+ client.getA("/accounts/exchange/taler-wire-gateway/transfers?status=success").assertOk()
+ }
+ measureAction("wg_add") {
+ client.postA("/accounts/exchange/taler-wire-gateway/admin/add-incoming") {
+ json {
+ "amount" to "KUDOS:0.0001"
+ "reserve_pub" to EddsaPublicKey.rand()
+ "debit_account" to customerPayto.canonical
+ }
+ }.assertOk()
+ }
+ measureAction("wg_incoming") {
+ client.getA("/accounts/exchange/taler-wire-gateway/history/incoming")
+ .assertOk()
+ }
+ measureAction("wg_outgoing") {
+ client.getA("/accounts/exchange/taler-wire-gateway/history/outgoing")
+ .assertOk()
+ }
- // TAN challenges
- val challenges = measureAction("tan_send") {
- val id = client.patchA("/accounts/account_bench_$it") {
- json {
- "contact_data" to obj {
- "phone" to "+99"
- "email" to "email@example.com"
- }
- "tan_channel" to "sms"
+ // TAN challenges
+ val challenges = measureAction("tan_send") {
+ val id = client.patchA("/accounts/account_bench_$it") {
+ json {
+ "contact_data" to obj {
+ "phone" to "+99"
+ "email" to "email@example.com"
}
- }.assertAcceptedJson<TanChallenge>().challenge_id
- val res = client.postA("/accounts/account_bench_$it/challenge/$id").assertOkJson<TanTransmission>()
- val code = tanCode(res.tan_info)
- Pair(id, code)
- }
- measureAction("tan_confirm") {
- val (id, code) = challenges[it]
- client.postA("/accounts/account_bench_$it/challenge/$id/confirm") {
- json { "tan" to code }
- }.assertNoContent()
- }
+ "tan_channel" to "sms"
+ }
+ }.assertAcceptedJson<TanChallenge>().challenge_id
+ val res = client.postA("/accounts/account_bench_$it/challenge/$id").assertOkJson<TanTransmission>()
+ val code = tanCode(res.tan_info)
+ Pair(id, code)
+ }
+ measureAction("tan_confirm") {
+ val (id, code) = challenges[it]
+ client.postA("/accounts/account_bench_$it/challenge/$id/confirm") {
+ json { "tan" to code }
+ }.assertNoContent()
+ }
- // Delete accounts
+ // Delete accounts
- measureAction("account_delete") {
- client.deleteA("/accounts/account_bench_$it").assertNoContent()
- }
+ measureAction("account_delete") {
+ client.deleteA("/accounts/account_bench_$it").assertNoContent()
+ }
- // Other
- measureAction("monitor") {
- client.getAdmin("/monitor").assertOk()
- }
+ // Other
+ measureAction("monitor") {
+ client.getAdmin("/monitor").assertOk()
+ }
+ db.gc.collect(Instant.now(), java.time.Duration.ZERO, java.time.Duration.ZERO, java.time.Duration.ZERO)
+ measureAction("gc") {
db.gc.collect(Instant.now(), java.time.Duration.ZERO, java.time.Duration.ZERO, java.time.Duration.ZERO)
- measureAction("gc") {
- db.gc.collect(Instant.now(), java.time.Duration.ZERO, java.time.Duration.ZERO, java.time.Duration.ZERO)
- }
- } }
- }
+ }
+ } }
}
\ No newline at end of file
diff --git a/common/src/main/kotlin/test/bench.kt b/common/src/main/kotlin/test/bench.kt
@@ -83,9 +83,14 @@ class Benchmark(private val iter: Int) {
}
}
-fun bench(iter: Int, lambda: Benchmark.() -> Unit) {
- val bench = Benchmark(iter)
- lambda(bench)
+fun bench(lambda: Benchmark.(Int) -> Unit) {
+ val ITER = System.getenv("BENCH_ITER")?.toIntOrNull() ?: 10
+ val AMOUNT = System.getenv("BENCH_AMOUNT")?.toIntOrNull() ?: 100
+
+ println("Bench $ITER times with $AMOUNT rows")
+
+ val bench = Benchmark(ITER)
+ bench.lambda(AMOUNT)
printTable(
listOf("benchmark", "min", "mean", "max", "std").map { ANSI.bold(it) },
bench.measures,
diff --git a/nexus/src/test/kotlin/bench.kt b/nexus/src/test/kotlin/bench.kt
@@ -24,6 +24,7 @@ import tech.libeufin.common.test.*
import tech.libeufin.nexus.*
import tech.libeufin.nexus.cli.*
import kotlin.math.max
+import java.util.UUID;
class Bench {
@@ -34,14 +35,32 @@ class Bench {
val token64 = ByteArray(64)
conn.genData(amount, sequenceOf(
- "incoming_transactions(amount, subject, execution_time, debit_payto, bank_id)" to {
- "(42,0)\tsubject\t0\tdebit_payto\tBANK_ID${it*2}\n" +
- "(42,0)\tsubject\t0\tdebit_payto\tBANK_ID${it*2+1}\n" +
- "(42,0)\tsubject\t0\tdebit_payto\t\\N\n"
+ "incoming_transactions(amount, subject, execution_time, debit_payto, uetr, tx_id, acct_svcr_ref)" to {
+ val subject = if (it % 4 == 0) null else "subject ${it}"
+ val debtor = if (it % 3 == 0) null else "debit_payto"
+ if (it % 3 == 0) {
+ "(20,0)\t$subject\t0\t$debtor\t${UUID.randomUUID()}\t\\N\t\\N\n" +
+ "(21,0)\t$subject\t0\t$debtor\t\\N\tTX_ID${it*2}\t\\N\n" +
+ "(22,0)\t$subject\t0\t$debtor\t\\N\t\\N\tREF${it*2}\n"
+ } else if (it % 3 == 1) {
+ "(30,0)\t$subject\t0\t$debtor\t${UUID.randomUUID()}\tTX_ID${it*2}\t\\N\n" +
+ "(31,0)\t$subject\t0\t$debtor\t\\N\tTX_ID${it*2+1}\tREF${it*2}\n" +
+ "(32,0)\t$subject\t0\t$debtor\t${UUID.randomUUID()}\t\\N\tREF${it*2+1}\n"
+ } else {
+ "(40,0)\t$subject\t0\t$debtor\t${UUID.randomUUID()}\tTX_ID${it*2}\tREF${it*2}\n" +
+ "(41,0)\t$subject\t0\t$debtor\t${UUID.randomUUID()}\tTX_ID${it*2+1}\tREF${it*2+1}\n"
+ }
},
- "outgoing_transactions(amount, subject, execution_time, credit_payto, end_to_end_id)" to {
- "(42,0)\tsubject\t0\tcredit_payto\tE2E_ID${it*2}\n" +
- "(42,0)\tsubject\t0\tcredit_payto\tE2E_ID${it*2+1}\n"
+ "outgoing_transactions(amount, subject, execution_time, credit_payto, end_to_end_id, acct_svcr_ref)" to {
+ val subject = if (it % 4 == 0) null else "subject ${it}"
+ val creditor = if (it % 3 == 0) null else "credit_payto"
+ if (it % 2 == 0) {
+ "(30,0)\t$subject\t0\t$creditor\t\\N\tREF${it*2}\n" +
+ "(31,0)\t$subject\t0\t$creditor\tE2E_ID${it*2}\t\\N\n"
+ } else {
+ "(30,0)\t$subject\t0\t$creditor\tE2E_ID${it*2}\tREF${it*2}\n" +
+ "(31,0)\t$subject\t0\t$creditor\tE2E_ID${it*2+1}\tREF${it*2+1}\n"
+ }
},
"initiated_outgoing_transactions(amount, subject, initiation_time, credit_payto, outgoing_transaction_id, end_to_end_id)" to {
"(42,0)\tsubject\t0\tcredit_payto\t${it*2}\tE2E_ID$it\n"
@@ -68,18 +87,9 @@ class Bench {
@Test
fun benchDb() {
- val ITER = System.getenv("BENCH_ITER")?.toIntOrNull() ?: 0
- val AMOUNT = System.getenv("BENCH_AMOUNT")?.toIntOrNull() ?: 0
-
- if (ITER == 0) {
- println("Skip benchmark, missing BENCH_ITER")
- return
- }
- println("Bench $ITER times with $AMOUNT rows")
-
val ingestCfg = NexusIngestConfig.default(AccountType.exchange)
- bench(ITER) { serverSetup { db ->
+ bench { AMOUNT -> serverSetup { db ->
// Generate data
db.conn { genData(it, AMOUNT) }