Skip to content

Commit aec622c

Browse files
committed
db: don't start transactions unless we really need to.
We always start a transaction before processing, but there are cases where we don't need to. Switch to doing it on-demand. This doesn't make a big difference for sqlite3, but it can for Postgres because of the latency: 12% or so. Every bit helps! 30 runs, min-max(mean+/-stddev): Postgres before: 8.842773-9.769030(9.19531+/-0.21) Postgres after: 8.007967-8.321856(8.14172+/-0.066) sqlite3 before: 7.486042-8.371831(8.15544+/-0.19) sqlite3 after: 7.973411-8.576135(8.3025+/-0.12) Signed-off-by: Rusty Russell <[email protected]>
1 parent 41f254d commit aec622c

File tree

5 files changed

+39
-4
lines changed

5 files changed

+39
-4
lines changed

db/common.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,8 @@
2929
struct db {
3030
char *filename;
3131
const char *in_transaction;
32+
/* For lazy transaction activation */
33+
bool transaction_started;
3234

3335
/* DB-specific context */
3436
void *conn;

db/db_sqlite3.c

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -474,12 +474,14 @@ static char **prepare_table_manip(const tal_t *ctx,
474474

475475
/* But core insists we're "in a transaction" for all ops, so fake it */
476476
db->in_transaction = "Not really";
477+
db->transaction_started = true;
477478
/* Turn off foreign keys first. */
478479
db_prepare_for_changes(db);
479480
db_exec_prepared_v2(take(db_prepare_untranslated(db,
480481
"PRAGMA foreign_keys = OFF;")));
481482
db_report_changes(db, NULL, 0);
482483
db->in_transaction = NULL;
484+
db->transaction_started = false;
483485

484486
db_begin_transaction(db);
485487
cmd = tal_fmt(tmpctx, "ALTER TABLE %s RENAME TO temp_%s;",
@@ -525,11 +527,13 @@ static bool complete_table_manip(struct db *db,
525527

526528
/* Allow links between them (esp. cascade deletes!) */
527529
db->in_transaction = "Not really";
530+
db->transaction_started = true;
528531
db_prepare_for_changes(db);
529532
db_exec_prepared_v2(take(db_prepare_untranslated(db,
530533
"PRAGMA foreign_keys = ON;")));
531534
db_report_changes(db, NULL, 0);
532535
db->in_transaction = NULL;
536+
db->transaction_started = false;
533537

534538
/* migrations are performed inside transactions, so start one. */
535539
db_begin_transaction(db);

db/exec.c

Lines changed: 21 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -121,19 +121,29 @@ static void db_data_version_incr(struct db *db)
121121

122122
void db_begin_transaction_(struct db *db, const char *location)
123123
{
124-
bool ok;
125124
if (db->in_transaction)
126125
db_fatal(db, "Already in transaction from %s", db->in_transaction);
127126

127+
db->in_transaction = location;
128128
/* No writes yet. */
129129
db->dirty = false;
130+
}
131+
132+
void db_need_transaction(struct db *db, const char *location)
133+
{
134+
bool ok;
135+
136+
if (!db->in_transaction)
137+
db_fatal(db, "Not in a transaction for %s", location);
138+
139+
if (db->transaction_started)
140+
return;
130141

131142
db_prepare_for_changes(db);
132143
ok = db->config->begin_tx_fn(db);
133144
if (!ok)
134145
db_fatal(db, "Failed to start DB transaction: %s", db->error);
135-
136-
db->in_transaction = location;
146+
db->transaction_started = true;
137147
}
138148

139149
bool db_in_transaction(struct db *db)
@@ -150,6 +160,13 @@ void db_commit_transaction(struct db *db)
150160
{
151161
bool ok;
152162
assert(db->in_transaction);
163+
164+
if (!db->transaction_started) {
165+
db->in_transaction = NULL;
166+
assert(!db->dirty);
167+
return;
168+
}
169+
153170
db_assert_no_outstanding_statements(db);
154171

155172
/* Increment before reporting changes to an eventual plugin. */
@@ -164,4 +181,5 @@ void db_commit_transaction(struct db *db)
164181

165182
db->in_transaction = NULL;
166183
db->dirty = false;
184+
db->transaction_started = false;
167185
}

db/exec.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,11 @@ void db_begin_transaction_(struct db *db, const char *location);
4040

4141
bool db_in_transaction(struct db *db);
4242

43+
/**
44+
* db_need_transaction: we now need to actually enable the transaction, if not
45+
* already. */
46+
void db_need_transaction(struct db *db, const char *location);
47+
4348
/**
4449
* db_commit_transaction - Commit a running transaction
4550
*

db/utils.c

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
#include <ccan/tal/str/str.h>
44
#include <common/trace.h>
55
#include <db/common.h>
6+
#include <db/exec.h>
67
#include <db/utils.h>
78

89
/* Matches the hash function used in devtools/sql-rewrite.py */
@@ -143,6 +144,7 @@ bool db_query_prepared_canfail(struct db_stmt *stmt)
143144
assert(stmt->query->readonly);
144145
trace_span_start("db_query_prepared", stmt);
145146
trace_span_tag(stmt, "query", stmt->query->query);
147+
db_need_transaction(stmt->db, stmt->query->query);
146148
ret = stmt->db->config->query_fn(stmt);
147149
stmt->executed = true;
148150
list_del_from(&stmt->db->pending_statements, &stmt->list);
@@ -174,9 +176,12 @@ bool db_step(struct db_stmt *stmt)
174176

175177
void db_exec_prepared_v2(struct db_stmt *stmt TAKES)
176178
{
179+
bool ret;
180+
181+
db_need_transaction(stmt->db, stmt->query->query);
177182
trace_span_start("db_exec_prepared", stmt);
178183
trace_span_tag(stmt, "query", stmt->query->query);
179-
bool ret = stmt->db->config->exec_fn(stmt);
184+
ret = stmt->db->config->exec_fn(stmt);
180185
trace_span_end(stmt);
181186

182187
if (stmt->db->readonly)
@@ -358,6 +363,7 @@ struct db *db_open_(const tal_t *ctx, const char *filename,
358363
db_fatal(db, "Unable to find DB queries for %s", db->config->name);
359364

360365
db->in_transaction = NULL;
366+
db->transaction_started = false;
361367
db->changes = NULL;
362368

363369
/* This must be outside a transaction, so catch it */

0 commit comments

Comments
 (0)