mirror of
https://github.com/ElementsProject/lightning.git
synced 2025-03-10 17:19:15 +01:00
lightingnd: wrap all JSON reply callbacks in db transactions.
It was always a bit weird they weren't, and it seems a premature optimization to make the callbacks to this themselves. Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
This commit is contained in:
parent
524eff0e93
commit
8170aba75f
5 changed files with 40 additions and 39 deletions
|
@ -404,10 +404,7 @@ static void sendrawtx_callback(const char *buf, const jsmntok_t *toks,
|
||||||
err);
|
err);
|
||||||
}
|
}
|
||||||
|
|
||||||
db_begin_transaction(call->bitcoind->ld->wallet->db);
|
|
||||||
call->cb(call->bitcoind, success, errmsg, call->cb_arg);
|
call->cb(call->bitcoind, success, errmsg, call->cb_arg);
|
||||||
db_commit_transaction(call->bitcoind->ld->wallet->db);
|
|
||||||
|
|
||||||
tal_free(call);
|
tal_free(call);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -472,9 +469,7 @@ getrawblockbyheight_callback(const char *buf, const jsmntok_t *toks,
|
||||||
* with NULL values. */
|
* with NULL values. */
|
||||||
err = json_scan(tmpctx, buf, toks, "{result:{blockhash:null}}");
|
err = json_scan(tmpctx, buf, toks, "{result:{blockhash:null}}");
|
||||||
if (!err) {
|
if (!err) {
|
||||||
db_begin_transaction(call->bitcoind->ld->wallet->db);
|
|
||||||
call->cb(call->bitcoind, NULL, NULL, call->cb_arg);
|
call->cb(call->bitcoind, NULL, NULL, call->cb_arg);
|
||||||
db_commit_transaction(call->bitcoind->ld->wallet->db);
|
|
||||||
goto clean;
|
goto clean;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -493,9 +488,7 @@ getrawblockbyheight_callback(const char *buf, const jsmntok_t *toks,
|
||||||
"getrawblockbyheight",
|
"getrawblockbyheight",
|
||||||
"bad block");
|
"bad block");
|
||||||
|
|
||||||
db_begin_transaction(call->bitcoind->ld->wallet->db);
|
|
||||||
call->cb(call->bitcoind, &blkid, blk, call->cb_arg);
|
call->cb(call->bitcoind, &blkid, blk, call->cb_arg);
|
||||||
db_commit_transaction(call->bitcoind->ld->wallet->db);
|
|
||||||
|
|
||||||
clean:
|
clean:
|
||||||
tal_free(call);
|
tal_free(call);
|
||||||
|
@ -574,10 +567,8 @@ static void getchaininfo_callback(const char *buf, const jsmntok_t *toks,
|
||||||
bitcoin_plugin_error(call->bitcoind, buf, toks, "getchaininfo",
|
bitcoin_plugin_error(call->bitcoind, buf, toks, "getchaininfo",
|
||||||
"bad 'result' field: %s", err);
|
"bad 'result' field: %s", err);
|
||||||
|
|
||||||
db_begin_transaction(call->bitcoind->ld->wallet->db);
|
|
||||||
call->cb(call->bitcoind, chain, headers, blocks, ibd,
|
call->cb(call->bitcoind, chain, headers, blocks, ibd,
|
||||||
call->first_call, call->cb_arg);
|
call->first_call, call->cb_arg);
|
||||||
db_commit_transaction(call->bitcoind->ld->wallet->db);
|
|
||||||
|
|
||||||
tal_free(call);
|
tal_free(call);
|
||||||
}
|
}
|
||||||
|
@ -640,9 +631,7 @@ static void getutxout_callback(const char *buf, const jsmntok_t *toks,
|
||||||
|
|
||||||
err = json_scan(tmpctx, buf, toks, "{result:{script:null}}");
|
err = json_scan(tmpctx, buf, toks, "{result:{script:null}}");
|
||||||
if (!err) {
|
if (!err) {
|
||||||
db_begin_transaction(call->bitcoind->ld->wallet->db);
|
|
||||||
call->cb(call->bitcoind, NULL, call->cb_arg);
|
call->cb(call->bitcoind, NULL, call->cb_arg);
|
||||||
db_commit_transaction(call->bitcoind->ld->wallet->db);
|
|
||||||
goto clean;
|
goto clean;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -654,9 +643,7 @@ static void getutxout_callback(const char *buf, const jsmntok_t *toks,
|
||||||
bitcoin_plugin_error(call->bitcoind, buf, toks, "getutxout",
|
bitcoin_plugin_error(call->bitcoind, buf, toks, "getutxout",
|
||||||
"bad 'result' field: %s", err);
|
"bad 'result' field: %s", err);
|
||||||
|
|
||||||
db_begin_transaction(call->bitcoind->ld->wallet->db);
|
|
||||||
call->cb(call->bitcoind, &txout, call->cb_arg);
|
call->cb(call->bitcoind, &txout, call->cb_arg);
|
||||||
db_commit_transaction(call->bitcoind->ld->wallet->db);
|
|
||||||
|
|
||||||
clean:
|
clean:
|
||||||
tal_free(call);
|
tal_free(call);
|
||||||
|
|
|
@ -921,7 +921,6 @@ static void listincoming_done(const char *buffer,
|
||||||
const jsmntok_t *idtok UNUSED,
|
const jsmntok_t *idtok UNUSED,
|
||||||
struct invoice_info *info)
|
struct invoice_info *info)
|
||||||
{
|
{
|
||||||
struct lightningd *ld = info->cmd->ld;
|
|
||||||
struct command_result *ret;
|
struct command_result *ret;
|
||||||
bool warning_mpp, warning_capacity, warning_deadends, warning_offline, warning_private_unused;
|
bool warning_mpp, warning_capacity, warning_deadends, warning_offline, warning_private_unused;
|
||||||
|
|
||||||
|
@ -934,8 +933,6 @@ static void listincoming_done(const char *buffer,
|
||||||
if (ret)
|
if (ret)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
/* We're actually outside a db transaction here: spooky! */
|
|
||||||
db_begin_transaction(ld->wallet->db);
|
|
||||||
invoice_complete(info,
|
invoice_complete(info,
|
||||||
false,
|
false,
|
||||||
warning_mpp,
|
warning_mpp,
|
||||||
|
@ -943,7 +940,6 @@ static void listincoming_done(const char *buffer,
|
||||||
warning_deadends,
|
warning_deadends,
|
||||||
warning_offline,
|
warning_offline,
|
||||||
warning_private_unused);
|
warning_private_unused);
|
||||||
db_commit_transaction(ld->wallet->db);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Since this is a dev-only option, we will crash if dev-routes is not
|
/* Since this is a dev-only option, we will crash if dev-routes is not
|
||||||
|
|
|
@ -17,6 +17,7 @@
|
||||||
#include <common/timeout.h>
|
#include <common/timeout.h>
|
||||||
#include <common/version.h>
|
#include <common/version.h>
|
||||||
#include <connectd/connectd_wiregen.h>
|
#include <connectd/connectd_wiregen.h>
|
||||||
|
#include <db/exec.h>
|
||||||
#include <dirent.h>
|
#include <dirent.h>
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
#include <lightningd/io_loop_with_timers.h>
|
#include <lightningd/io_loop_with_timers.h>
|
||||||
|
@ -84,6 +85,7 @@ struct plugins *plugins_new(const tal_t *ctx, struct log_book *log_book,
|
||||||
p->blacklist = tal_arr(p, const char *, 0);
|
p->blacklist = tal_arr(p, const char *, 0);
|
||||||
p->plugin_idx = 0;
|
p->plugin_idx = 0;
|
||||||
p->dev_builtin_plugins_unimportant = false;
|
p->dev_builtin_plugins_unimportant = false;
|
||||||
|
p->want_db_transaction = true;
|
||||||
|
|
||||||
return p;
|
return p;
|
||||||
}
|
}
|
||||||
|
@ -619,6 +621,7 @@ static const char *plugin_response_handle(struct plugin *plugin,
|
||||||
|
|
||||||
/* We expect the request->cb to copy if needed */
|
/* We expect the request->cb to copy if needed */
|
||||||
pd = plugin_detect_destruction(plugin);
|
pd = plugin_detect_destruction(plugin);
|
||||||
|
|
||||||
request->response_cb(plugin->buffer, toks, idtok, request->response_cb_arg);
|
request->response_cb(plugin->buffer, toks, idtok, request->response_cb_arg);
|
||||||
|
|
||||||
/* Note that in the case of 'plugin stop' this can free request (since
|
/* Note that in the case of 'plugin stop' this can free request (since
|
||||||
|
@ -639,12 +642,14 @@ static const char *plugin_response_handle(struct plugin *plugin,
|
||||||
* If @destroyed was set, it means the plugin called plugin stop on itself.
|
* If @destroyed was set, it means the plugin called plugin stop on itself.
|
||||||
*/
|
*/
|
||||||
static const char *plugin_read_json_one(struct plugin *plugin,
|
static const char *plugin_read_json_one(struct plugin *plugin,
|
||||||
|
bool want_transaction,
|
||||||
bool *complete,
|
bool *complete,
|
||||||
bool *destroyed)
|
bool *destroyed)
|
||||||
{
|
{
|
||||||
const jsmntok_t *jrtok, *idtok;
|
const jsmntok_t *jrtok, *idtok;
|
||||||
struct plugin_destroyed *pd;
|
struct plugin_destroyed *pd;
|
||||||
const char *err;
|
const char *err;
|
||||||
|
struct wallet *wallet = plugin->plugins->ld->wallet;
|
||||||
|
|
||||||
*destroyed = false;
|
*destroyed = false;
|
||||||
/* Note that in the case of 'plugin stop' this can free request (since
|
/* Note that in the case of 'plugin stop' this can free request (since
|
||||||
|
@ -687,6 +692,11 @@ static const char *plugin_read_json_one(struct plugin *plugin,
|
||||||
"JSON-RPC message does not contain \"jsonrpc\" field");
|
"JSON-RPC message does not contain \"jsonrpc\" field");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* We can be called extremely early, or as db hook, or for
|
||||||
|
* fake "terminated" request. */
|
||||||
|
if (want_transaction)
|
||||||
|
db_begin_transaction(wallet->db);
|
||||||
|
|
||||||
pd = plugin_detect_destruction(plugin);
|
pd = plugin_detect_destruction(plugin);
|
||||||
if (!idtok) {
|
if (!idtok) {
|
||||||
/* A Notification is a Request object without an "id"
|
/* A Notification is a Request object without an "id"
|
||||||
|
@ -732,6 +742,8 @@ static const char *plugin_read_json_one(struct plugin *plugin,
|
||||||
*/
|
*/
|
||||||
err = plugin_response_handle(plugin, plugin->toks, idtok);
|
err = plugin_response_handle(plugin, plugin->toks, idtok);
|
||||||
}
|
}
|
||||||
|
if (want_transaction)
|
||||||
|
db_commit_transaction(wallet->db);
|
||||||
|
|
||||||
/* Corner case: rpc_command hook can destroy plugin for 'plugin
|
/* Corner case: rpc_command hook can destroy plugin for 'plugin
|
||||||
* stop'! */
|
* stop'! */
|
||||||
|
@ -753,6 +765,9 @@ static struct io_plan *plugin_read_json(struct io_conn *conn,
|
||||||
{
|
{
|
||||||
bool success;
|
bool success;
|
||||||
bool have_full;
|
bool have_full;
|
||||||
|
/* wallet is NULL in really early code */
|
||||||
|
bool want_transaction = (plugin->plugins->want_db_transaction
|
||||||
|
&& plugin->plugins->ld->wallet != NULL);
|
||||||
|
|
||||||
log_io(plugin->log, LOG_IO_IN, NULL, "",
|
log_io(plugin->log, LOG_IO_IN, NULL, "",
|
||||||
plugin->buffer + plugin->used, plugin->len_read);
|
plugin->buffer + plugin->used, plugin->len_read);
|
||||||
|
@ -774,8 +789,9 @@ static struct io_plan *plugin_read_json(struct io_conn *conn,
|
||||||
do {
|
do {
|
||||||
bool destroyed;
|
bool destroyed;
|
||||||
const char *err;
|
const char *err;
|
||||||
err =
|
|
||||||
plugin_read_json_one(plugin, &success, &destroyed);
|
err = plugin_read_json_one(plugin, want_transaction,
|
||||||
|
&success, &destroyed);
|
||||||
|
|
||||||
/* If it's destroyed, conn is already freed! */
|
/* If it's destroyed, conn is already freed! */
|
||||||
if (destroyed)
|
if (destroyed)
|
||||||
|
|
|
@ -108,6 +108,10 @@ struct plugins {
|
||||||
struct list_head plugins;
|
struct list_head plugins;
|
||||||
bool startup;
|
bool startup;
|
||||||
|
|
||||||
|
/* Normally we want to wrap callbacks in a db transaction, but
|
||||||
|
* not for the db hook servicing */
|
||||||
|
bool want_db_transaction;
|
||||||
|
|
||||||
struct logger *log;
|
struct logger *log;
|
||||||
|
|
||||||
struct lightningd *ld;
|
struct lightningd *ld;
|
||||||
|
|
|
@ -134,7 +134,10 @@ static void plugin_hook_killed(struct plugin_hook_call_link *link)
|
||||||
/* Call next will unlink, so we don't need to. This is treated
|
/* Call next will unlink, so we don't need to. This is treated
|
||||||
* equivalent to the plugin returning a continue-result.
|
* equivalent to the plugin returning a continue-result.
|
||||||
*/
|
*/
|
||||||
|
struct db *db = link->plugin->plugins->ld->wallet->db;
|
||||||
|
db_begin_transaction(db);
|
||||||
plugin_hook_callback(NULL, NULL, NULL, link->req);
|
plugin_hook_callback(NULL, NULL, NULL, link->req);
|
||||||
|
db_commit_transaction(db);
|
||||||
} else {
|
} else {
|
||||||
/* The plugin is in the list waiting to be called, just remove
|
/* The plugin is in the list waiting to be called, just remove
|
||||||
* it from the list. */
|
* it from the list. */
|
||||||
|
@ -159,9 +162,7 @@ static void plugin_hook_callback(const char *buffer, const jsmntok_t *toks,
|
||||||
struct plugin_hook_request *r)
|
struct plugin_hook_request *r)
|
||||||
{
|
{
|
||||||
const jsmntok_t *resulttok;
|
const jsmntok_t *resulttok;
|
||||||
struct db *db = r->db;
|
|
||||||
struct plugin_hook_call_link *last, *it;
|
struct plugin_hook_call_link *last, *it;
|
||||||
bool in_transaction = false;
|
|
||||||
|
|
||||||
/* Pop the head off the call chain and continue with the next */
|
/* Pop the head off the call chain and continue with the next */
|
||||||
last = list_pop(&r->call_chain, struct plugin_hook_call_link, list);
|
last = list_pop(&r->call_chain, struct plugin_hook_call_link, list);
|
||||||
|
@ -187,31 +188,22 @@ static void plugin_hook_callback(const char *buffer, const jsmntok_t *toks,
|
||||||
r->hook->name, toks->end - toks->start,
|
r->hook->name, toks->end - toks->start,
|
||||||
buffer + toks->start);
|
buffer + toks->start);
|
||||||
|
|
||||||
db_begin_transaction(db);
|
|
||||||
if (!r->hook->deserialize_cb(r->cb_arg, buffer,
|
if (!r->hook->deserialize_cb(r->cb_arg, buffer,
|
||||||
resulttok)) {
|
resulttok)) {
|
||||||
tal_free(r->cb_arg);
|
tal_free(r->cb_arg);
|
||||||
db_commit_transaction(db);
|
|
||||||
goto cleanup;
|
goto cleanup;
|
||||||
}
|
}
|
||||||
in_transaction = true;
|
|
||||||
} else {
|
} else {
|
||||||
/* plugin died */
|
/* plugin died */
|
||||||
resulttok = NULL;
|
resulttok = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!list_empty(&r->call_chain)) {
|
if (!list_empty(&r->call_chain)) {
|
||||||
if (in_transaction)
|
|
||||||
db_commit_transaction(db);
|
|
||||||
plugin_hook_call_next(r);
|
plugin_hook_call_next(r);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* We optimize for the case where we already called deserialize_cb */
|
|
||||||
if (!in_transaction)
|
|
||||||
db_begin_transaction(db);
|
|
||||||
r->hook->final_cb(r->cb_arg);
|
r->hook->final_cb(r->cb_arg);
|
||||||
db_commit_transaction(db);
|
|
||||||
|
|
||||||
cleanup:
|
cleanup:
|
||||||
/* We need to remove the destructors from the remaining
|
/* We need to remove the destructors from the remaining
|
||||||
|
@ -349,7 +341,8 @@ void plugin_hook_db_sync(struct db *db)
|
||||||
struct jsonrpc_request *req;
|
struct jsonrpc_request *req;
|
||||||
struct plugin_hook_request *ph_req;
|
struct plugin_hook_request *ph_req;
|
||||||
void *ret;
|
void *ret;
|
||||||
struct plugin **plugins;
|
struct plugin **plugin_arr;
|
||||||
|
struct plugins *plugins;
|
||||||
size_t i;
|
size_t i;
|
||||||
size_t num_hooks;
|
size_t num_hooks;
|
||||||
|
|
||||||
|
@ -358,11 +351,12 @@ void plugin_hook_db_sync(struct db *db)
|
||||||
if (num_hooks == 0)
|
if (num_hooks == 0)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
plugins = notleak(tal_arr(NULL, struct plugin *,
|
plugin_arr = notleak(tal_arr(NULL, struct plugin *,
|
||||||
num_hooks));
|
num_hooks));
|
||||||
for (i = 0; i < num_hooks; ++i)
|
for (i = 0; i < num_hooks; ++i)
|
||||||
plugins[i] = hook->hooks[i]->plugin;
|
plugin_arr[i] = hook->hooks[i]->plugin;
|
||||||
|
|
||||||
|
plugins = plugin_arr[0]->plugins;
|
||||||
ph_req = notleak(tal(hook->hooks, struct plugin_hook_request));
|
ph_req = notleak(tal(hook->hooks, struct plugin_hook_request));
|
||||||
ph_req->hook = hook;
|
ph_req->hook = hook;
|
||||||
ph_req->db = db;
|
ph_req->db = db;
|
||||||
|
@ -372,7 +366,7 @@ void plugin_hook_db_sync(struct db *db)
|
||||||
/* Create an object for this plugin. */
|
/* Create an object for this plugin. */
|
||||||
struct db_write_hook_req *dwh_req;
|
struct db_write_hook_req *dwh_req;
|
||||||
dwh_req = tal(ph_req, struct db_write_hook_req);
|
dwh_req = tal(ph_req, struct db_write_hook_req);
|
||||||
dwh_req->plugin = plugins[i];
|
dwh_req->plugin = plugin_arr[i];
|
||||||
dwh_req->ph_req = ph_req;
|
dwh_req->ph_req = ph_req;
|
||||||
dwh_req->num_hooks = &num_hooks;
|
dwh_req->num_hooks = &num_hooks;
|
||||||
|
|
||||||
|
@ -393,21 +387,25 @@ void plugin_hook_db_sync(struct db *db)
|
||||||
json_array_end(req->stream);
|
json_array_end(req->stream);
|
||||||
jsonrpc_request_end(req);
|
jsonrpc_request_end(req);
|
||||||
|
|
||||||
plugin_request_send(plugins[i], req);
|
plugin_request_send(plugin_arr[i], req);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* We don't want to try to open another transaction: we're in one! */
|
||||||
|
plugins->want_db_transaction = false;
|
||||||
|
ret = plugins_exclusive_loop(plugin_arr);
|
||||||
|
plugins->want_db_transaction = true;
|
||||||
|
|
||||||
/* We can be called on way out of an io_loop, which is already breaking.
|
/* We can be called on way out of an io_loop, which is already breaking.
|
||||||
* That will make this immediately return; save the break value and call
|
* That will make this immediately return; save the break value and call
|
||||||
* again, then hand it onwards. */
|
* again, then hand it onwards. */
|
||||||
ret = plugins_exclusive_loop(plugins);
|
|
||||||
if (ret != ph_req) {
|
if (ret != ph_req) {
|
||||||
void *ret2 = plugins_exclusive_loop(plugins);
|
void *ret2 = plugins_exclusive_loop(plugin_arr);
|
||||||
assert(ret2 == ph_req);
|
assert(ret2 == ph_req);
|
||||||
log_debug(plugins[0]->plugins->ld->log, "io_break: %s", __func__);
|
log_debug(plugins->ld->log, "io_break: %s", __func__);
|
||||||
io_break(ret);
|
io_break(ret);
|
||||||
}
|
}
|
||||||
assert(num_hooks == 0);
|
assert(num_hooks == 0);
|
||||||
tal_free(plugins);
|
tal_free(plugin_arr);
|
||||||
tal_free(ph_req);
|
tal_free(ph_req);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Add table
Reference in a new issue