pytest: test for 1M JSONRPC calls which don't need transactions.
What changed, and why it matters
This commit adds a new internal test to Core Lightning that exercises one million JSON-RPC plugin calls. It is purely a performance benchmark and test infrastructure change; it does not fix any bug, change production behavior, or introduce any security-relevant code path that an external user could reach.
No security action required. Treat as routine test/performance infrastructure.
Security signals we found
No strong security signals were identified.
Evidence from the diff
The diff adds a spamcommand RPC to the test_libplugin.c test plugin and a matching test_spam_commands pytest that issues 1,000,000 batched batching RPC calls. The stated goal is to measure the performance improvement from avoiding empty database transactions. The new code lives only under tests/ and is gated by @pytest.mark.slow_test, so it is not part of normal runtime or release behavior.
Changed components
tests/plugins/test_libplugin.ctests/test_plugin.pyInspect captured patch +62 / −0
diff --git a/tests/plugins/test_libplugin.c b/tests/plugins/test_libplugin.c
index f73378e6..2abf1f7f 100644
--- a/tests/plugins/test_libplugin.c
+++ b/tests/plugins/test_libplugin.c
@@ -197,6 +197,44 @@ static struct command_result *json_checkthis(struct command *cmd,
return send_outreq(req);
}
+static struct command_result *spam_done(struct command *cmd, void *unused)
+{
+ return command_success(cmd, json_out_obj(cmd, NULL, NULL));
+}
+
+static struct command_result *spam_errcb(struct command *cmd,
+ const char *method,
+ const char *buf,
+ const jsmntok_t *tok,
+ void *unused)
+{
+ plugin_err(cmd->plugin, "%.*s",
+ json_tok_full_len(tok),
+ json_tok_full(buf, tok));
+}
+
+static struct command_result *json_spamcommand(struct command *cmd,
+ const char *buf,
+ const jsmntok_t *params)
+{
+ u64 *iterations;
+ struct request_batch *batch;
+
+ if (!param(cmd, buf, params,
+ p_req("iterations", param_u64, &iterations),
+ NULL))
+ return command_param_failed();
+
+ batch = request_batch_new(cmd, NULL, spam_errcb, spam_done, NULL);
+ for (size_t i = 0; i < *iterations; i++) {
+ struct out_req *req = add_to_batch(cmd, batch, "batching");
+ json_add_bool(req->js, "enable", true);
+ send_outreq(req);
+ }
+ return batch_done(cmd, batch);
+}
+
+
static char *set_dynamic(struct plugin *plugin,
const char *arg,
bool check_only,
@@ -270,6 +308,10 @@ static const struct plugin_command commands[] = { {
"checkthis",
json_checkthis,
},
+ {
+ "spamcommand",
+ json_spamcommand,
+ },
};
static const char *before[] = { "dummy", NULL };
diff --git a/tests/test_plugin.py b/tests/test_plugin.py
index a036ecc2..b7c2242c 100644
--- a/tests/test_plugin.py
+++ b/tests/test_plugin.py
@@ -4719,3 +4719,23 @@ def test_openchannel_hook_channel_type(node_factory, bitcoind):
l2.daemon.wait_for_log(r"plugin-openchannel_hook_accepter.py: accept by design: channel_type {'bits': \[12, 22\], 'names': \['static_remotekey/even', 'anchors/even'\]}")
else:
l2.daemon.wait_for_log(r"plugin-openchannel_hook_accepter.py: accept by design: channel_type {'bits': \[12\], 'names': \['static_remotekey/even'\]}")
+
+
+@pytest.mark.slow_test
+def test_spam_commands(node_factory, bitcoind):
+ plugin = os.path.join(os.getcwd(), "tests/plugins/test_libplugin")
+ l1 = node_factory.get_node(options={"plugin": plugin, 'log-level': 'info'},
+ start=False)
+
+ # Memleak detection here creates significant overhead!
+ del l1.daemon.env["LIGHTNINGD_DEV_MEMLEAK"]
+ # Don't bother recording all our io.
+ del l1.daemon.opts['dev-save-plugin-io']
+ l1.start()
+
+ start_time = time.time()
+ l1.rpc.spamcommand(1_000_000)
+ duration = time.time() - start_time
+
+ # Change 100 to 0 to get test to fail so you can see the result!
+ assert duration < 100
Why this scored 15/100
Community notes
Notes can correct, qualify, or add evidence to the AI analysis. Every note shown here has been validated by a human moderator.
The AI analysis stands alone for now. Submit a note if you can add evidence or important context.