mirror of
https://github.com/ElementsProject/lightning.git
synced 2025-03-04 11:08:03 +01:00
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:
parent
2a5660b3bc
commit
2022e4a7a9
4 changed files with 91 additions and 72 deletions
|
@ -285,7 +285,7 @@ static struct command_result *prev_payment(struct command *cmd,
|
||||||
bool prev_paid = false;
|
bool prev_paid = false;
|
||||||
|
|
||||||
assert(!invreq->payer_info);
|
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++) {
|
for (size_t i = 0; i < tal_count(payments); i++) {
|
||||||
const struct tlv_invoice *inv;
|
const struct tlv_invoice *inv;
|
||||||
|
|
|
@ -37,15 +37,16 @@ struct sendpay_command {
|
||||||
struct command *cmd;
|
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;
|
*status = PAYMENT_COMPLETE;
|
||||||
return true;
|
return true;
|
||||||
} else if (streq(status_str, "pending")) {
|
} else if (memeqstr(status_str, len, "pending")) {
|
||||||
*status = PAYMENT_PENDING;
|
*status = PAYMENT_PENDING;
|
||||||
return true;
|
return true;
|
||||||
} else if (streq(status_str, "failed")) {
|
} else if (memeqstr(status_str, len, "failed")) {
|
||||||
*status = PAYMENT_FAILED;
|
*status = PAYMENT_FAILED;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -883,7 +884,7 @@ send_payment_core(struct lightningd *ld,
|
||||||
bool have_complete = false;
|
bool have_complete = false;
|
||||||
|
|
||||||
/* Now, do we already have one or more payments? */
|
/* 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++) {
|
for (size_t i = 0; i < tal_count(payments); i++) {
|
||||||
log_debug(ld->log, "Payment %zu/%zu: %s %s",
|
log_debug(ld->log, "Payment %zu/%zu: %s %s",
|
||||||
i, tal_count(payments),
|
i, tal_count(payments),
|
||||||
|
@ -1545,6 +1546,22 @@ static const struct json_command waitsendpay_command = {
|
||||||
};
|
};
|
||||||
AUTODATA(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,
|
static struct command_result *json_listsendpays(struct command *cmd,
|
||||||
const char *buffer,
|
const char *buffer,
|
||||||
const jsmntok_t *obj UNNEEDED,
|
const jsmntok_t *obj UNNEEDED,
|
||||||
|
@ -1553,13 +1570,14 @@ static struct command_result *json_listsendpays(struct command *cmd,
|
||||||
const struct wallet_payment **payments;
|
const struct wallet_payment **payments;
|
||||||
struct json_stream *response;
|
struct json_stream *response;
|
||||||
struct sha256 *rhash;
|
struct sha256 *rhash;
|
||||||
const char *invstring, *status_str;
|
const char *invstring;
|
||||||
|
enum wallet_payment_status *status;
|
||||||
|
|
||||||
if (!param(cmd, buffer, params,
|
if (!param(cmd, buffer, params,
|
||||||
/* FIXME: parameter should be invstring now */
|
/* FIXME: parameter should be invstring now */
|
||||||
p_opt("bolt11", param_string, &invstring),
|
p_opt("bolt11", param_string, &invstring),
|
||||||
p_opt("payment_hash", param_sha256, &rhash),
|
p_opt("payment_hash", param_sha256, &rhash),
|
||||||
p_opt("status", param_string, &status_str),
|
p_opt("status", param_payment_status, &status),
|
||||||
NULL))
|
NULL))
|
||||||
return command_param_failed();
|
return command_param_failed();
|
||||||
|
|
||||||
|
@ -1591,19 +1609,13 @@ static struct command_result *json_listsendpays(struct command *cmd,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (status_str) {
|
payments = wallet_payment_list(cmd, cmd->ld->wallet, rhash);
|
||||||
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);
|
|
||||||
|
|
||||||
response = json_stream_success(cmd);
|
response = json_stream_success(cmd);
|
||||||
|
|
||||||
json_array_start(response, "payments");
|
json_array_start(response, "payments");
|
||||||
for (size_t i = 0; i < tal_count(payments); i++) {
|
for (size_t i = 0; i < tal_count(payments); i++) {
|
||||||
|
if (status && payments[i]->status != *status)
|
||||||
|
continue;
|
||||||
json_object_start(response, NULL);
|
json_object_start(response, NULL);
|
||||||
json_add_payment_fields(response, payments[i]);
|
json_add_payment_fields(response, payments[i]);
|
||||||
json_object_end(response);
|
json_object_end(response);
|
||||||
|
@ -1629,40 +1641,36 @@ static struct command_result *json_delpay(struct command *cmd,
|
||||||
{
|
{
|
||||||
struct json_stream *response;
|
struct json_stream *response;
|
||||||
const struct wallet_payment **payments;
|
const struct wallet_payment **payments;
|
||||||
const char *status_str;
|
enum wallet_payment_status *status;
|
||||||
enum wallet_payment_status status;
|
|
||||||
struct sha256 *payment_hash;
|
struct sha256 *payment_hash;
|
||||||
|
|
||||||
if (!param(cmd, buffer, params,
|
if (!param(cmd, buffer, params,
|
||||||
p_req("payment_hash", param_sha256, &payment_hash),
|
p_req("payment_hash", param_sha256, &payment_hash),
|
||||||
p_req("status", param_string, &status_str),
|
p_req("status", param_payment_status, &status),
|
||||||
NULL))
|
NULL))
|
||||||
return command_param_failed();
|
return command_param_failed();
|
||||||
|
|
||||||
if (!string_to_payment_status(status_str, &status))
|
switch (*status) {
|
||||||
return command_fail(cmd, JSONRPC2_INVALID_PARAMS, "Unrecognized status: %s", status_str);
|
|
||||||
|
|
||||||
switch(status){
|
|
||||||
case PAYMENT_COMPLETE:
|
case PAYMENT_COMPLETE:
|
||||||
case PAYMENT_FAILED:
|
case PAYMENT_FAILED:
|
||||||
break;
|
break;
|
||||||
case PAYMENT_PENDING:
|
case PAYMENT_PENDING:
|
||||||
return command_fail(cmd, JSONRPC2_INVALID_PARAMS, "Invalid status: %s",
|
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)
|
if (tal_count(payments) == 0)
|
||||||
return command_fail(cmd, PAY_NO_SUCH_PAYMENT, "Unknown payment with payment_hash: %s",
|
return command_fail(cmd, PAY_NO_SUCH_PAYMENT, "Unknown payment with payment_hash: %s",
|
||||||
type_to_string(tmpctx, struct sha256, payment_hash));
|
type_to_string(tmpctx, struct sha256, payment_hash));
|
||||||
|
|
||||||
for (int i = 0; i < tal_count(payments); i++) {
|
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",
|
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),
|
type_to_string(tmpctx, struct sha256, payment_hash),
|
||||||
payment_status_to_string(payments[i]->status),
|
payment_status_to_string(payments[i]->status),
|
||||||
payment_status_to_string(status));
|
payment_status_to_string(*status));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -3518,8 +3518,7 @@ void wallet_payment_set_failinfo(struct wallet *wallet,
|
||||||
const struct wallet_payment **
|
const struct wallet_payment **
|
||||||
wallet_payment_list(const tal_t *ctx,
|
wallet_payment_list(const tal_t *ctx,
|
||||||
struct wallet *wallet,
|
struct wallet *wallet,
|
||||||
const struct sha256 *payment_hash,
|
const struct sha256 *payment_hash)
|
||||||
enum wallet_payment_status *status)
|
|
||||||
{
|
{
|
||||||
const struct wallet_payment **payments;
|
const struct wallet_payment **payments;
|
||||||
struct db_stmt *stmt;
|
struct db_stmt *stmt;
|
||||||
|
@ -3528,9 +3527,7 @@ wallet_payment_list(const tal_t *ctx,
|
||||||
|
|
||||||
payments = tal_arr(ctx, const struct wallet_payment *, 0);
|
payments = tal_arr(ctx, const struct wallet_payment *, 0);
|
||||||
|
|
||||||
u8 enable_payment_hash = payment_hash != NULL ? 0 : 1;
|
if (payment_hash) {
|
||||||
u8 enable_status = status != NULL ? 0 : 1;
|
|
||||||
|
|
||||||
stmt = db_prepare_v2(wallet->db, SQL("SELECT"
|
stmt = db_prepare_v2(wallet->db, SQL("SELECT"
|
||||||
" id"
|
" id"
|
||||||
", status"
|
", status"
|
||||||
|
@ -3554,21 +3551,34 @@ wallet_payment_list(const tal_t *ctx,
|
||||||
", completed_at"
|
", completed_at"
|
||||||
" FROM payments"
|
" FROM payments"
|
||||||
" WHERE"
|
" WHERE"
|
||||||
" (payment_hash = ? OR 1=?) AND"
|
" payment_hash = ?"
|
||||||
" (status = ? OR 1=?)"
|
|
||||||
" ORDER BY id;"));
|
" ORDER BY id;"));
|
||||||
|
|
||||||
if (payment_hash)
|
|
||||||
db_bind_sha256(stmt, 0, payment_hash);
|
db_bind_sha256(stmt, 0, payment_hash);
|
||||||
else
|
} else {
|
||||||
db_bind_null(stmt, 0);
|
stmt = db_prepare_v2(wallet->db, SQL("SELECT"
|
||||||
db_bind_int(stmt, 1, enable_payment_hash);
|
" id"
|
||||||
if (status)
|
", status"
|
||||||
db_bind_int(stmt, 2, wallet_payment_status_in_db(*status));
|
", destination"
|
||||||
else
|
", msatoshi"
|
||||||
db_bind_null(stmt, 2);
|
", payment_hash"
|
||||||
db_bind_int(stmt, 3, enable_status);
|
", 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);
|
db_query_prepared(stmt);
|
||||||
|
|
||||||
for (i = 0; db_step(stmt); i++) {
|
for (i = 0; db_step(stmt); i++) {
|
||||||
|
|
|
@ -1183,8 +1183,9 @@ void wallet_payment_set_failinfo(struct wallet *wallet,
|
||||||
*/
|
*/
|
||||||
const struct wallet_payment **wallet_payment_list(const tal_t *ctx,
|
const struct wallet_payment **wallet_payment_list(const tal_t *ctx,
|
||||||
struct wallet *wallet,
|
struct wallet *wallet,
|
||||||
const struct sha256 *payment_hash,
|
const struct sha256 *payment_hash)
|
||||||
enum wallet_payment_status *status);
|
NON_NULL_ARGS(2);
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* wallet_payments_by_offer - Retrieve a list of payments for this local_offer_id
|
* wallet_payments_by_offer - Retrieve a list of payments for this local_offer_id
|
||||||
|
|
Loading…
Add table
Reference in a new issue