wallet: simplify payments lookup so sqlite3 uses index.

Filtering by status is rare, so we can do it in the caller; just let sqlite3
filter by payment_hash.

With ~650,000 payments in db:

Before:
```
129/300000 complete 5.60/sec (33078 invs, 169 pays, 0 retries) in 30 seconds. 19 hours-14 hours remaining.
201/300000 complete 7.20/sec (43519 invs, 241 pays, 0 retries) in 40 seconds. 16 hours-11 hours remaining.
257/300000 complete 5.60/sec (54568 invs, 289 pays, 0 retries) in 50 seconds. 16 hours-14 hours remaining.
305/300000 complete 4.80/sec (65772 invs, 337 pays, 0 retries) in 60 seconds. 16 hours-17 hours remaining.
361/300000 complete 5.60/sec (75875 invs, 401 pays, 0 retries) in 70 seconds. 16 hours-14 hours remaining.
```

After:
```
760/300000 complete 40.00/sec (19955 invs, 824 pays, 0 retries) in 20 seconds. 2 hours-2 hours remaining.
1176/300000 complete 41.60/sec (30082 invs, 1224 pays, 0 retries) in 30 seconds. 2 hours-119 minutes remaining.
1584/300000 complete 40.80/sec (40224 invs, 1640 pays, 0 retries) in 40 seconds. 2 hours-2 hours remaining.
1984/300000 complete 40.00/sec (49938 invs, 2048 pays, 0 retries) in 50 seconds. 2 hours-2 hours remaining.
```

Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
This commit is contained in:
Rusty Russell 2022-09-19 10:19:53 +09:30 committed by Christian Decker
parent 2a5660b3bc
commit 2022e4a7a9
4 changed files with 91 additions and 72 deletions

View file

@ -285,7 +285,7 @@ static struct command_result *prev_payment(struct command *cmd,
bool prev_paid = false;
assert(!invreq->payer_info);
payments = wallet_payment_list(cmd, cmd->ld->wallet, NULL, NULL);
payments = wallet_payment_list(cmd, cmd->ld->wallet, NULL);
for (size_t i = 0; i < tal_count(payments); i++) {
const struct tlv_invoice *inv;

View file

@ -37,15 +37,16 @@ struct sendpay_command {
struct command *cmd;
};
static bool string_to_payment_status(const char *status_str, enum wallet_payment_status *status)
static bool string_to_payment_status(const char *status_str, size_t len,
enum wallet_payment_status *status)
{
if (streq(status_str, "complete")) {
if (memeqstr(status_str, len, "complete")) {
*status = PAYMENT_COMPLETE;
return true;
} else if (streq(status_str, "pending")) {
} else if (memeqstr(status_str, len, "pending")) {
*status = PAYMENT_PENDING;
return true;
} else if (streq(status_str, "failed")) {
} else if (memeqstr(status_str, len, "failed")) {
*status = PAYMENT_FAILED;
return true;
}
@ -883,7 +884,7 @@ send_payment_core(struct lightningd *ld,
bool have_complete = false;
/* Now, do we already have one or more payments? */
payments = wallet_payment_list(tmpctx, ld->wallet, rhash, NULL);
payments = wallet_payment_list(tmpctx, ld->wallet, rhash);
for (size_t i = 0; i < tal_count(payments); i++) {
log_debug(ld->log, "Payment %zu/%zu: %s %s",
i, tal_count(payments),
@ -1545,6 +1546,22 @@ static const struct json_command waitsendpay_command = {
};
AUTODATA(json_command, &waitsendpay_command);
static struct command_result *param_payment_status(struct command *cmd,
const char *name,
const char *buffer,
const jsmntok_t *tok,
enum wallet_payment_status **status)
{
*status = tal(cmd, enum wallet_payment_status);
if (string_to_payment_status(buffer + tok->start,
tok->end - tok->start,
*status))
return NULL;
return command_fail_badparam(cmd, name, buffer, tok,
"should be an invoice status");
}
static struct command_result *json_listsendpays(struct command *cmd,
const char *buffer,
const jsmntok_t *obj UNNEEDED,
@ -1553,13 +1570,14 @@ static struct command_result *json_listsendpays(struct command *cmd,
const struct wallet_payment **payments;
struct json_stream *response;
struct sha256 *rhash;
const char *invstring, *status_str;
const char *invstring;
enum wallet_payment_status *status;
if (!param(cmd, buffer, params,
/* FIXME: parameter should be invstring now */
p_opt("bolt11", param_string, &invstring),
p_opt("payment_hash", param_sha256, &rhash),
p_opt("status", param_string, &status_str),
p_opt("status", param_payment_status, &status),
NULL))
return command_param_failed();
@ -1591,19 +1609,13 @@ static struct command_result *json_listsendpays(struct command *cmd,
}
}
if (status_str) {
enum wallet_payment_status status;
if (!string_to_payment_status(status_str, &status))
return command_fail(cmd, JSONRPC2_INVALID_PARAMS, "Unrecognized status: %s", status_str);
payments = wallet_payment_list(cmd, cmd->ld->wallet, rhash, &status);
} else
payments = wallet_payment_list(cmd, cmd->ld->wallet, rhash, NULL);
payments = wallet_payment_list(cmd, cmd->ld->wallet, rhash);
response = json_stream_success(cmd);
json_array_start(response, "payments");
for (size_t i = 0; i < tal_count(payments); i++) {
if (status && payments[i]->status != *status)
continue;
json_object_start(response, NULL);
json_add_payment_fields(response, payments[i]);
json_object_end(response);
@ -1629,40 +1641,36 @@ static struct command_result *json_delpay(struct command *cmd,
{
struct json_stream *response;
const struct wallet_payment **payments;
const char *status_str;
enum wallet_payment_status status;
enum wallet_payment_status *status;
struct sha256 *payment_hash;
if (!param(cmd, buffer, params,
p_req("payment_hash", param_sha256, &payment_hash),
p_req("status", param_string, &status_str),
p_req("status", param_payment_status, &status),
NULL))
return command_param_failed();
if (!string_to_payment_status(status_str, &status))
return command_fail(cmd, JSONRPC2_INVALID_PARAMS, "Unrecognized status: %s", status_str);
switch(status){
switch (*status) {
case PAYMENT_COMPLETE:
case PAYMENT_FAILED:
break;
case PAYMENT_PENDING:
return command_fail(cmd, JSONRPC2_INVALID_PARAMS, "Invalid status: %s",
payment_status_to_string(status));
payment_status_to_string(*status));
}
payments = wallet_payment_list(cmd, cmd->ld->wallet, payment_hash, NULL);
payments = wallet_payment_list(cmd, cmd->ld->wallet, payment_hash);
if (tal_count(payments) == 0)
return command_fail(cmd, PAY_NO_SUCH_PAYMENT, "Unknown payment with payment_hash: %s",
type_to_string(tmpctx, struct sha256, payment_hash));
for (int i = 0; i < tal_count(payments); i++) {
if (payments[i]->status != status) {
if (payments[i]->status != *status) {
return command_fail(cmd, PAY_STATUS_UNEXPECTED, "Payment with hash %s has %s status but it should be %s",
type_to_string(tmpctx, struct sha256, payment_hash),
payment_status_to_string(payments[i]->status),
payment_status_to_string(status));
payment_status_to_string(*status));
}
}

View file

@ -3518,8 +3518,7 @@ void wallet_payment_set_failinfo(struct wallet *wallet,
const struct wallet_payment **
wallet_payment_list(const tal_t *ctx,
struct wallet *wallet,
const struct sha256 *payment_hash,
enum wallet_payment_status *status)
const struct sha256 *payment_hash)
{
const struct wallet_payment **payments;
struct db_stmt *stmt;
@ -3528,9 +3527,7 @@ wallet_payment_list(const tal_t *ctx,
payments = tal_arr(ctx, const struct wallet_payment *, 0);
u8 enable_payment_hash = payment_hash != NULL ? 0 : 1;
u8 enable_status = status != NULL ? 0 : 1;
if (payment_hash) {
stmt = db_prepare_v2(wallet->db, SQL("SELECT"
" id"
", status"
@ -3554,21 +3551,34 @@ wallet_payment_list(const tal_t *ctx,
", completed_at"
" FROM payments"
" WHERE"
" (payment_hash = ? OR 1=?) AND"
" (status = ? OR 1=?)"
" payment_hash = ?"
" ORDER BY id;"));
if (payment_hash)
db_bind_sha256(stmt, 0, payment_hash);
else
db_bind_null(stmt, 0);
db_bind_int(stmt, 1, enable_payment_hash);
if (status)
db_bind_int(stmt, 2, wallet_payment_status_in_db(*status));
else
db_bind_null(stmt, 2);
db_bind_int(stmt, 3, enable_status);
} else {
stmt = db_prepare_v2(wallet->db, SQL("SELECT"
" id"
", status"
", destination"
", msatoshi"
", payment_hash"
", timestamp"
", payment_preimage"
", path_secrets"
", route_nodes"
", route_channels"
", msatoshi_sent"
", description"
", bolt11"
", paydescription"
", failonionreply"
", total_msat"
", partid"
", local_offer_id"
", groupid"
", completed_at"
" FROM payments"
" ORDER BY id;"));
}
db_query_prepared(stmt);
for (i = 0; db_step(stmt); i++) {

View file

@ -1183,8 +1183,9 @@ void wallet_payment_set_failinfo(struct wallet *wallet,
*/
const struct wallet_payment **wallet_payment_list(const tal_t *ctx,
struct wallet *wallet,
const struct sha256 *payment_hash,
enum wallet_payment_status *status);
const struct sha256 *payment_hash)
NON_NULL_ARGS(2);
/**
* wallet_payments_by_offer - Retrieve a list of payments for this local_offer_id