Browse Source

db: Implemented poor mans nested transactions

Nesting is provided by only actually performing the outermost
transaction and simulating the nested ones. This still allows us to
ensure on lower levels that we are in the context of a transaction
without having to resort to keeping explicitly track of it in the
calling code.

Signed-off-by: Christian Decker <decker.christian@gmail.com>
ppa-0.6.1
Christian Decker 7 years ago
parent
commit
77789bb705
  1. 5
      wallet/db.c
  2. 8
      wallet/db.h
  3. 19
      wallet/wallet.c

5
wallet/db.c

@ -227,12 +227,15 @@ static void close_db(struct db *db) { sqlite3_close(db->sql); }
bool db_begin_transaction(struct db *db) bool db_begin_transaction(struct db *db)
{ {
assert(!db->in_transaction); if (!db->in_transaction) {
/* Clear any errors from previous transactions and /* Clear any errors from previous transactions and
* non-transactional queries */ * non-transactional queries */
db_clear_error(db); db_clear_error(db);
db->in_transaction = db_exec(__func__, db, "BEGIN TRANSACTION;"); db->in_transaction = db_exec(__func__, db, "BEGIN TRANSACTION;");
assert(db->in_transaction);
return db->in_transaction; return db->in_transaction;
}
return false;
} }
bool db_commit_transaction(struct db *db) bool db_commit_transaction(struct db *db)

8
wallet/db.h

@ -44,9 +44,11 @@ bool PRINTF_FMT(3, 4)
/** /**
* db_begin_transaction - Begin a transaction * db_begin_transaction - Begin a transaction
* *
* We do not support nesting multiple transactions, so make sure that * Begin a new DB transaction if we aren't already in one. Returns
* we are not in a transaction when calling this. Returns true if we * true if the call started a transaction, i.e., the caller MUST take
* succeeded in starting a transaction. * care to either commit or rollback. If false, this is a nested
* transaction and the caller MUST not commit/rollback, since the
* transaction is handled at a higher level in the callstack.
*/ */
bool db_begin_transaction(struct db *db); bool db_begin_transaction(struct db *db);

19
wallet/wallet.c

@ -139,6 +139,7 @@ const struct utxo **wallet_select_coins(const tal_t *ctx, struct wallet *w,
u64 *fee_estimate, u64 *changesatoshi) u64 *fee_estimate, u64 *changesatoshi)
{ {
size_t i = 0; size_t i = 0;
bool should_commit;
struct utxo **available; struct utxo **available;
const struct utxo **utxos = tal_arr(ctx, const struct utxo *, 0); const struct utxo **utxos = tal_arr(ctx, const struct utxo *, 0);
*fee_estimate = 0; *fee_estimate = 0;
@ -147,9 +148,8 @@ const struct utxo **wallet_select_coins(const tal_t *ctx, struct wallet *w,
u64 satoshi_in = 0, weight = (4 + (8 + 22) * 2 + 4) * 4; u64 satoshi_in = 0, weight = (4 + (8 + 22) * 2 + 4) * 4;
tal_add_destructor2(utxos, destroy_utxos, w); tal_add_destructor2(utxos, destroy_utxos, w);
if (!db_begin_transaction(w->db)) { should_commit = db_begin_transaction(w->db);
fatal("Unable to begin transaction: %s", w->db->err);
}
available = wallet_get_utxos(ctx, w, output_state_available); available = wallet_get_utxos(ctx, w, output_state_available);
for (i = 0; i < tal_count(available); i++) { for (i = 0; i < tal_count(available); i++) {
@ -177,9 +177,11 @@ const struct utxo **wallet_select_coins(const tal_t *ctx, struct wallet *w,
if (satoshi_in < *fee_estimate + value) { if (satoshi_in < *fee_estimate + value) {
/* Could not collect enough inputs, cleanup and bail */ /* Could not collect enough inputs, cleanup and bail */
utxos = tal_free(utxos); utxos = tal_free(utxos);
if (should_commit)
db_rollback_transaction(w->db); db_rollback_transaction(w->db);
} else { } else {
/* Commit the db transaction to persist markings */ /* Commit the db transaction to persist markings */
if (should_commit)
db_commit_transaction(w->db); db_commit_transaction(w->db);
*changesatoshi = satoshi_in - value - *fee_estimate; *changesatoshi = satoshi_in - value - *fee_estimate;
@ -274,6 +276,7 @@ bool wallet_shachain_add_hash(struct wallet *wallet,
const struct sha256 *hash) const struct sha256 *hash)
{ {
sqlite3_stmt *stmt; sqlite3_stmt *stmt;
bool should_commit;
bool ok = true; bool ok = true;
u32 pos = count_trailing_zeroes(index); u32 pos = count_trailing_zeroes(index);
assert(index < SQLITE_MAX_UINT); assert(index < SQLITE_MAX_UINT);
@ -281,7 +284,7 @@ bool wallet_shachain_add_hash(struct wallet *wallet,
return false; return false;
} }
db_begin_transaction(wallet->db); should_commit = db_begin_transaction(wallet->db);
stmt = db_prepare(wallet->db, "UPDATE shachains SET num_valid=?, min_index=? WHERE id=?"); stmt = db_prepare(wallet->db, "UPDATE shachains SET num_valid=?, min_index=? WHERE id=?");
sqlite3_bind_int(stmt, 1, chain->chain.num_valid); sqlite3_bind_int(stmt, 1, chain->chain.num_valid);
@ -298,10 +301,12 @@ bool wallet_shachain_add_hash(struct wallet *wallet,
sqlite3_bind_blob(stmt, 4, hash, sizeof(*hash), SQLITE_TRANSIENT); sqlite3_bind_blob(stmt, 4, hash, sizeof(*hash), SQLITE_TRANSIENT);
ok &= db_exec_prepared(wallet->db, stmt); ok &= db_exec_prepared(wallet->db, stmt);
if (should_commit) {
if (ok) if (ok)
ok &= db_commit_transaction(wallet->db); ok &= db_commit_transaction(wallet->db);
else else
db_rollback_transaction(wallet->db); db_rollback_transaction(wallet->db);
}
return ok; return ok;
} }
@ -625,12 +630,12 @@ bool wallet_channel_config_load(struct wallet *w, const u64 id,
} }
bool wallet_channel_save(struct wallet *w, struct wallet_channel *chan){ bool wallet_channel_save(struct wallet *w, struct wallet_channel *chan){
bool ok = true; bool should_commit, ok = true;
struct peer *p = chan->peer; struct peer *p = chan->peer;
tal_t *tmpctx = tal_tmpctx(w); tal_t *tmpctx = tal_tmpctx(w);
sqlite3_stmt *stmt; sqlite3_stmt *stmt;
db_begin_transaction(w->db); should_commit = db_begin_transaction(w->db);
if (p->dbid == 0) { if (p->dbid == 0) {
/* Need to store the peer first */ /* Need to store the peer first */
@ -751,10 +756,12 @@ bool wallet_channel_save(struct wallet *w, struct wallet_channel *chan){
ok &= db_exec_prepared(w->db, stmt); ok &= db_exec_prepared(w->db, stmt);
} }
if (should_commit) {
if (ok) if (ok)
ok &= db_commit_transaction(w->db); ok &= db_commit_transaction(w->db);
else else
db_rollback_transaction(w->db); db_rollback_transaction(w->db);
}
tal_free(tmpctx); tal_free(tmpctx);
return ok; return ok;
} }

Loading…
Cancel
Save