Skip to content

Commit cbaacf1

Browse files
committed
split with_tx into self and try_with_tx
1 parent f8cad91 commit cbaacf1

2 files changed

Lines changed: 64 additions & 19 deletions

File tree

crates/bindings/src/lib.rs

Lines changed: 63 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1121,12 +1121,44 @@ impl ProcedureContext {
11211121
self.timestamp = new_time;
11221122
}
11231123

1124+
/// Acquire a mutable transaction
1125+
/// and execute `body` with read-write access to the database.
1126+
///
1127+
/// When a panic occurs,
1128+
/// the transaction will be rolled back and its mutations discarded.
1129+
/// Otherwise, the transaction will be committed and its mutations persisted.
1130+
///
1131+
/// Regardless of the transaction's success or failure,
1132+
/// the return value of `body` is not persisted to the commitlog
1133+
/// or broadcast to subscribed clients.
1134+
/// Clients attribute mutations performed by this transaction to `Event::UnknownTransaction`.
1135+
///
1136+
/// If the transaction fails to commit after `body` returns,
1137+
/// e.g., due to a conflict with a concurrent transaction,
1138+
/// this method will re-invoke `body` with a new transaction in order to retry.
1139+
/// The transaction will be retried at most once.
1140+
/// If it fails to commit a second time, this method will panic.
1141+
///
1142+
/// Because `body` may be run multiple times,
1143+
/// and is expected to perform the same set of database operations
1144+
/// and return the same result on each invocation,
1145+
/// callers should avoid writing to any captured mutable state within `body`,
1146+
/// This includes interior mutability through types like [`std::cell::Cell`].
1147+
#[cfg(feature = "unstable")]
1148+
pub fn with_tx<T>(&mut self, body: impl Fn(&TxContext) -> T) -> T {
1149+
use core::convert::Infallible;
1150+
match self.try_with_tx::<T, Infallible>(|tx| Ok(body(tx))) {
1151+
Ok(v) => v,
1152+
Err(e) => match e {},
1153+
}
1154+
}
1155+
11241156
/// Acquire a mutable transaction
11251157
/// and execute `body` with read-write access to the database.
11261158
///
11271159
/// When `body().is_ok()`,
11281160
/// the transaction will be committed and its mutations persisted.
1129-
/// When `!body().is_ok()`,
1161+
/// When `!body().is_ok()` or a panic occurs in `body`,
11301162
/// the transaction will be rolled back and its mutations discarded.
11311163
///
11321164
/// Regardless of the transaction's success or failure,
@@ -1146,43 +1178,57 @@ impl ProcedureContext {
11461178
/// callers should avoid writing to any captured mutable state within `body`,
11471179
/// This includes interior mutability through types like [`std::cell::Cell`].
11481180
#[cfg(feature = "unstable")]
1149-
pub fn with_tx<R: IsOk>(&mut self, body: impl Fn(&TxContext) -> R) -> R {
1181+
pub fn try_with_tx<T, E>(&mut self, body: impl Fn(&TxContext) -> Result<T, E>) -> Result<T, E> {
1182+
let abort = || {
1183+
sys::procedure::procedure_abort_mut_tx()
1184+
.expect("should have a pending mutable anon tx as `procedure_start_mut_tx` preceded")
1185+
};
1186+
11501187
let run = || {
11511188
// Start the transaction.
11521189
let timestamp = sys::procedure::procedure_start_mut_tx().expect(
11531190
"holding `&mut ProcedureContext`, so should not be in a tx already; called manually elsewhere?",
11541191
);
11551192
let timestamp = Timestamp::from_micros_since_unix_epoch(timestamp);
11561193

1157-
// We've resumed, so do the work.
1194+
// We've resumed, so let's do the work, but first prepare the context.
11581195
let tx = ReducerContext::new(Local {}, self.sender, self.connection_id, timestamp);
11591196
let tx = TxContext(tx);
1160-
body(&tx)
1197+
1198+
// Guard the execution of `body` with a scope-guard that `abort`s on panic.
1199+
// Wasmtime now supports unwinding, so we need to protect against that.
1200+
// We're not using `scopeguard::guard` here to avoid an extra dependency.
1201+
struct DoOnDrop<F: Fn()>(F);
1202+
impl<F: Fn()> Drop for DoOnDrop<F> {
1203+
fn drop(&mut self) {
1204+
(self.0)();
1205+
}
1206+
}
1207+
let abort_guard = DoOnDrop(abort);
1208+
let res = body(&tx);
1209+
// Defuse the bomb.
1210+
let DoOnDrop(_) = abort_guard;
1211+
res
11611212
};
11621213

11631214
let mut res = run();
1164-
let abort = || {
1165-
sys::procedure::procedure_abort_mut_tx()
1166-
.expect("should have a pending mutable anon tx as `procedure_start_mut_tx` preceded")
1167-
};
11681215

11691216
// Commit or roll back?
1170-
if res.is_ok() {
1171-
if sys::procedure::procedure_commit_mut_tx().is_err() {
1217+
match res {
1218+
Ok(_) if sys::procedure::procedure_commit_mut_tx().is_err() => {
1219+
// Tried to commit, but couldn't. Retry once.
11721220
log::warn!("committing anonymous transaction failed");
1173-
11741221
// NOTE(procedure,centril): there's no actual guarantee that `body`
11751222
// does the exact same as the time before, as the timestamps differ
11761223
// and due to interior mutability.
11771224
res = run();
1178-
if res.is_ok() {
1179-
sys::procedure::procedure_commit_mut_tx().expect("transaction retry failed again")
1180-
} else {
1181-
abort();
1225+
match res {
1226+
Ok(_) => sys::procedure::procedure_commit_mut_tx().expect("transaction retry failed again"),
1227+
Err(_) => abort(),
11821228
}
11831229
}
1184-
} else {
1185-
abort();
1230+
Ok(_) => {}
1231+
Err(_) => abort(),
11861232
}
11871233

11881234
res

crates/core/src/host/wasmtime/wasm_instance_env.rs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@ use crate::database_logger::{BacktraceFrame, BacktraceProvider, ModuleBacktrace,
55
use crate::error::NodesError;
66
use crate::host::instance_env::{ChunkPool, InstanceEnv};
77
use crate::host::module_host::{DatabaseUpdate, EventStatus, ModuleEvent, ModuleFunctionCall};
8-
use crate::host::wasm_common::instrumentation::noop::CallSpanStart;
98
use crate::host::wasm_common::instrumentation::{span, CallTimes};
109
use crate::host::wasm_common::module_host_actor::ExecutionTimings;
1110
use crate::host::wasm_common::{err_to_errno_and_log, RowIterIdx, RowIters, TimingSpan, TimingSpanIdx, TimingSpanSet};
@@ -295,7 +294,7 @@ impl WasmInstanceEnv {
295294
}
296295

297296
/// Record a span with `start`.
298-
fn end_span(mut caller: Caller<'_, Self>, start: CallSpanStart) {
297+
fn end_span(mut caller: Caller<'_, Self>, start: span::CallSpanStart) {
299298
let span = start.end();
300299
span::record_span(&mut caller.data_mut().call_times, span);
301300
}

0 commit comments

Comments
 (0)