Skip to content

Commit b4c00b2

Browse files
authored
Mark txns durable when persistence completes (#590)
* Mark txns durable when persistence completes * Turn off all commit flags in metadata before setting the commit decision * Simplify logic by restricting txn state transitions
1 parent 69a06c1 commit b4c00b2

File tree

3 files changed

+48
-28
lines changed

3 files changed

+48
-28
lines changed

production/db/core/inc/txn_metadata.hpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@ class txn_metadata_t
5555
static void set_active_txn_submitted(gaia_txn_id_t begin_ts, gaia_txn_id_t commit_ts);
5656
static void set_active_txn_terminated(gaia_txn_id_t begin_ts);
5757
static void update_txn_decision(gaia_txn_id_t commit_ts, bool is_committed);
58+
static void set_txn_durable(gaia_txn_id_t commit_ts);
5859
static bool set_txn_gc_complete(gaia_txn_id_t commit_ts);
5960

6061
static gaia_txn_id_t txn_begin();

production/db/core/src/db_server.cpp

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2315,6 +2315,11 @@ bool server_t::txn_commit()
23152315
{
23162316
txn_name = rdb->begin_txn(s_txn_id);
23172317
// Prepare log for transaction.
2318+
// This is effectively asynchronous with validation, because if it takes
2319+
// too long, then another thread may recursively validate this txn,
2320+
// before the committing thread has a chance to do so.
2321+
// NB: We only mark the txn as durable after validation, to simplify
2322+
// reasoning about txn state transitions.
23182323
rdb->prepare_wal_for_write(s_log, txn_name);
23192324
}
23202325

@@ -2326,9 +2331,20 @@ bool server_t::txn_commit()
23262331
// Update the txn metadata with our commit decision.
23272332
txn_metadata_t::update_txn_decision(commit_ts, is_committed);
23282333

2329-
// Append commit or rollback marker to the WAL.
2334+
// Persist the commit decision.
2335+
// Eventually, we will return a decision to the client asynchronously with
2336+
// the decision being persisted (because the decision can be reconstructed
2337+
// from the durable log itself, without the decision record).
23302338
if (rdb)
23312339
{
2340+
// Mark txn as durable in metadata so we can GC the txn log.
2341+
// The txn may be considered durable even if it hasn't yet been
2342+
// validated, since the decision can be reconstructed from the durable
2343+
// log, but we only mark it durable after validation to simplify the
2344+
// state transitions: we only allow
2345+
// TXN_VALIDATING -> TXN_DECIDED -> TXN_DURABLE.
2346+
txn_metadata_t::set_txn_durable(commit_ts);
2347+
23322348
if (is_committed)
23332349
{
23342350
rdb->append_wal_commit_marker(txn_name);

production/db/core/src/txn_metadata.cpp

Lines changed: 30 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -217,53 +217,56 @@ void txn_metadata_t::update_txn_decision(gaia_txn_id_t commit_ts, bool is_commit
217217
// The commit_ts metadata must be in state TXN_VALIDATING or TXN_DECIDED.
218218
// We allow the latter to enable idempotent concurrent validation.
219219
txn_metadata_t commit_ts_metadata(commit_ts);
220+
220221
common::retail_assert(
221222
commit_ts_metadata.is_validating() || commit_ts_metadata.is_decided(),
222223
"commit_ts metadata must be in validating or decided state!");
223224

224-
uint64_t decided_status_flags
225-
= is_committed ? c_txn_status_committed : c_txn_status_aborted;
225+
uint64_t decided_status_flags{is_committed ? c_txn_status_committed : c_txn_status_aborted};
226226

227-
// We can just reuse the log fd and begin_ts from the existing metadata.
228-
//
229-
// REVIEW: This condition is probably rare enough that it's not worth optimizing
230-
// and we can just let the CAS fail if another thread validated the txn before we did.
231-
//
232-
// We may have already been validated by another committing txn.
233-
if (commit_ts_metadata.is_decided())
234-
{
235-
// If another txn validated before us, they should have reached the same decision.
236-
common::retail_assert(
237-
commit_ts_metadata.is_committed() == is_committed,
238-
"Inconsistent txn decision detected!");
227+
txn_metadata_t decided_commit_ts_metadata = commit_ts_metadata;
239228

240-
return;
241-
}
229+
// This masks out just the commit_ts flag bits.
230+
constexpr uint64_t c_commit_flags_mask = ~(~c_txn_status_commit_ts << c_txn_status_flags_shift);
242231

243-
txn_metadata_t expected_metadata = commit_ts_metadata;
232+
// Turn off all commit flag bits before turning on the bits for this decision.
233+
decided_commit_ts_metadata.m_value &= c_commit_flags_mask;
244234

245-
// It's safe to just OR in the new flags because the preceding states don't set
246-
// any bits not present in the flags.
247-
commit_ts_metadata.m_value |= (decided_status_flags << c_txn_status_flags_shift);
235+
// Now set the decision flags.
236+
decided_commit_ts_metadata.m_value |= (decided_status_flags << c_txn_status_flags_shift);
237+
238+
bool has_set_metadata = compare_exchange_strong(commit_ts_metadata, decided_commit_ts_metadata);
248239

249-
bool has_set_metadata = compare_exchange_strong(
250-
expected_metadata, commit_ts_metadata);
251240
if (!has_set_metadata)
252241
{
253-
// NB: expected_metadata is an inout argument holding the previous value on failure!
242+
// The only state transition allowed from TXN_VALIDATING is to TXN_DECIDED.
254243
common::retail_assert(
255-
expected_metadata.is_decided(),
244+
commit_ts_metadata.is_decided(),
256245
"commit_ts metadata in validating state can only transition to a decided state!");
257246

258247
// If another txn validated before us, they should have reached the same decision.
259248
common::retail_assert(
260-
expected_metadata.is_committed() == is_committed,
249+
commit_ts_metadata.is_committed() == is_committed,
261250
"Inconsistent txn decision detected!");
262-
263-
return;
264251
}
265252
}
266253

254+
void txn_metadata_t::set_txn_durable(gaia_txn_id_t commit_ts)
255+
{
256+
txn_metadata_t commit_ts_metadata(commit_ts);
257+
258+
txn_metadata_t durable_commit_ts_metadata;
259+
do
260+
{
261+
// NB: commit_ts_metadata is an inout argument holding the previous value
262+
// on failure!
263+
durable_commit_ts_metadata = commit_ts_metadata;
264+
265+
durable_commit_ts_metadata.m_value |= (c_txn_persistence_complete << c_txn_persistence_flags_shift);
266+
267+
} while (!compare_exchange_weak(commit_ts_metadata, durable_commit_ts_metadata));
268+
}
269+
267270
bool txn_metadata_t::set_txn_gc_complete(gaia_txn_id_t commit_ts)
268271
{
269272
txn_metadata_t commit_ts_metadata(commit_ts);

0 commit comments

Comments
 (0)