Skip to content

Commit 04a0843

Browse files
authored
Remove LMDB backend and ReadTransaction abstractions from turbo-tasks-backend (#91284)
## Summary Remove the unused LMDB database backend and the `ReadTransaction` abstraction layer from `turbo-tasks-backend`. This is a pure cleanup that deletes ~1,500 lines of code and eliminates all `unsafe` blocks related to transaction lifetime management. **Motivation:** - This was much more useful in the early days of the persistence layer where comparing against a 'known good' db was useful, but this has rotted and turbo-persistence has gained lots of tests and usage so that this is not needed. - Carrying the code added a lot of complexity to APIs e.g. - The `ReadTransaction` abstraction existed solely to support LMDB's transaction-based read model - The only remaining backend (`TurboKeyValueDatabase` via `turbo-persistence`) already used `()` as its `ReadTransaction` type, making the entire abstraction dead weight - The transaction plumbing required `unsafe` transmute-based lifetime extension in `ExecuteContextImpl`, adding complexity and risk for no benefit - The WriteBatch::Serial branches complicated already subtle code paths
1 parent a65c3c8 commit 04a0843

File tree

25 files changed

+222
-2112
lines changed

25 files changed

+222
-2112
lines changed

Cargo.lock

Lines changed: 0 additions & 36 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

crates/next-napi-bindings/src/next_api/turbopack_ctx.rs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -23,13 +23,13 @@ use turbo_tasks::{
2323
message_queue::{CompilationEvent, Severity},
2424
};
2525
use turbo_tasks_backend::{
26-
BackendOptions, DefaultBackingStorage, GitVersionInfo, NoopBackingStorage, StartupCacheState,
27-
TurboTasksBackend, db_invalidation::invalidation_reasons, default_backing_storage,
28-
noop_backing_storage,
26+
BackendOptions, GitVersionInfo, NoopBackingStorage, StartupCacheState, TurboBackingStorage,
27+
TurboTasksBackend, db_invalidation::invalidation_reasons, noop_backing_storage,
28+
turbo_backing_storage,
2929
};
3030

3131
pub type NextTurboTasks =
32-
Arc<TurboTasks<TurboTasksBackend<Either<DefaultBackingStorage, NoopBackingStorage>>>>;
32+
Arc<TurboTasks<TurboTasksBackend<Either<TurboBackingStorage, NoopBackingStorage>>>>;
3333

3434
/// A value often wrapped in [`napi::bindgen_prelude::External`] that retains the [TurboTasks]
3535
/// instance used by Next.js, and [various napi helpers that are passed to us from
@@ -222,7 +222,7 @@ pub fn create_turbo_tasks(
222222
dirty: option_env!("CI").is_none_or(|value| value.is_empty())
223223
&& env!("VERGEN_GIT_DIRTY") == "true",
224224
};
225-
let (backing_storage, cache_state) = default_backing_storage(
225+
let (backing_storage, cache_state) = turbo_backing_storage(
226226
&output_path.join("cache/turbopack"),
227227
&version_info,
228228
is_ci,

turbopack/crates/turbo-tasks-backend/Cargo.toml

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -30,20 +30,16 @@ trace_task_modification = []
3030
trace_task_dirty = ["trace_aggregation_update_queue"]
3131
trace_task_output_dependencies = []
3232
trace_task_details = []
33-
lmdb = ["dep:lmdb-rkv"]
3433

3534
[dependencies]
3635
anyhow = { workspace = true }
3736
bincode = { workspace = true }
38-
arc-swap = { version = "1.8.2" }
3937
auto-hash-map = { workspace = true }
4038
bitfield = { workspace = true }
41-
byteorder = { workspace = true }
4239
dashmap = { workspace = true, features = ["raw-api"]}
4340
either = { workspace = true }
4441
hashbrown = { workspace = true, features = ["raw"] }
4542
indexmap = { workspace = true }
46-
lmdb-rkv = { version = "0.14.0", optional = true }
4743
lzzzz = { workspace = true, optional = true }
4844
once_cell = { workspace = true }
4945
parking_lot = { workspace = true }
@@ -56,7 +52,6 @@ serde_path_to_error = { workspace = true }
5652
smallvec = { workspace = true }
5753
tokio = { workspace = true }
5854
tracing = { workspace = true }
59-
thread_local = { workspace = true }
6055
turbo-bincode = { workspace = true }
6156
turbo-persistence = { workspace = true }
6257
turbo-rcstr = { workspace = true }

turbopack/crates/turbo-tasks-backend/src/backend/mod.rs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -183,7 +183,8 @@ struct TurboTasksBackendInner<B: BackingStorage> {
183183
storage: Storage,
184184

185185
/// When true, the backing_storage has data that is not in the local storage.
186-
local_is_partial: AtomicBool,
186+
/// This is determined once at startup and never changes.
187+
local_is_partial: bool,
187188

188189
/// Number of executing operations + Highest bit is set when snapshot is
189190
/// requested. When that bit is set, operations should pause until the
@@ -258,7 +259,7 @@ impl<B: BackingStorage> TurboTasksBackendInner<B> {
258259
),
259260
persisted_task_cache_log: need_log.then(|| Sharded::new(shard_amount)),
260261
task_cache: FxDashMap::default(),
261-
local_is_partial: AtomicBool::new(next_task_id != TaskId::MIN),
262+
local_is_partial: next_task_id != TaskId::MIN,
262263
storage: Storage::new(shard_amount, small_preallocation),
263264
in_progress_operations: AtomicUsize::new(0),
264265
snapshot_request: Mutex::new(SnapshotRequest::new()),

turbopack/crates/turbo-tasks-backend/src/backend/operation/mod.rs

Lines changed: 25 additions & 78 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@ mod update_cell;
99
mod update_collectible;
1010
use std::{
1111
fmt::{Debug, Display, Formatter},
12-
mem::transmute,
1312
sync::{Arc, atomic::Ordering},
1413
};
1514

@@ -26,19 +25,14 @@ use crate::{
2625
storage::{SpecificTaskDataCategory, StorageWriteGuard},
2726
storage_schema::{TaskStorage, TaskStorageAccessors},
2827
},
29-
backing_storage::{BackingStorage, BackingStorageSealed},
28+
backing_storage::BackingStorage,
3029
data::{ActivenessState, CollectibleRef, Dirtyness, InProgressState, TransientTask},
3130
};
3231

3332
pub trait Operation: Encode + Decode<()> + Default + TryFrom<AnyOperation, Error = ()> {
3433
fn execute(self, ctx: &mut impl ExecuteContext<'_>);
3534
}
3635

37-
enum TransactionState<'tx, B: BackingStorage> {
38-
None,
39-
Owned(Option<B::ReadTransaction<'tx>>),
40-
}
41-
4236
pub trait ExecuteContext<'e>: Sized {
4337
type TaskGuardImpl: TaskGuard + 'e;
4438
fn child_context<'l, 'r>(&'r self) -> impl ChildExecuteContext<'l> + use<'e, 'l, Self>
@@ -104,23 +98,15 @@ pub trait ChildExecuteContext<'e>: Send + Sized {
10498
fn create(self) -> impl ExecuteContext<'e>;
10599
}
106100

107-
pub struct ExecuteContextImpl<'e, 'tx, B: BackingStorage>
108-
where
109-
Self: 'e,
110-
'tx: 'e,
111-
{
101+
pub struct ExecuteContextImpl<'e, B: BackingStorage> {
112102
backend: &'e TurboTasksBackendInner<B>,
113103
turbo_tasks: &'e dyn TurboTasksBackendApi<TurboTasksBackend<B>>,
114104
_operation_guard: Option<OperationGuard<'e, B>>,
115-
transaction: TransactionState<'tx, B>,
116105
#[cfg(debug_assertions)]
117106
active_task_locks: std::sync::Arc<std::sync::atomic::AtomicU8>,
118107
}
119108

120-
impl<'e, 'tx, B: BackingStorage> ExecuteContextImpl<'e, 'tx, B>
121-
where
122-
'tx: 'e,
123-
{
109+
impl<'e, B: BackingStorage> ExecuteContextImpl<'e, B> {
124110
pub(super) fn new(
125111
backend: &'e TurboTasksBackendInner<B>,
126112
turbo_tasks: &'e dyn TurboTasksBackendApi<TurboTasksBackend<B>>,
@@ -129,50 +115,29 @@ where
129115
backend,
130116
turbo_tasks,
131117
_operation_guard: Some(backend.start_operation()),
132-
transaction: TransactionState::None,
133118
#[cfg(debug_assertions)]
134119
active_task_locks: std::sync::Arc::new(std::sync::atomic::AtomicU8::new(0)),
135120
}
136121
}
137122

138-
fn ensure_transaction(&mut self) -> bool {
139-
if matches!(self.transaction, TransactionState::None) {
140-
let check_backing_storage = self.backend.should_restore()
141-
&& self.backend.local_is_partial.load(Ordering::Acquire);
142-
if !check_backing_storage {
143-
return false;
144-
}
145-
let tx = self.backend.backing_storage.start_read_transaction();
146-
let tx = tx.map(|tx| {
147-
// Safety: `self.backend.backing_storage` lives for at least `'e`. The
148-
// transaction returned by `start_read_transaction()` borrows it for `'e`.
149-
// Since `'tx: 'e`, transmuting to `'tx` is sound because the transaction
150-
// is stored in `self.transaction` (as `Owned`) and dropped with `self`,
151-
// while `self.backend` remains alive for `'e`.
152-
unsafe { transmute::<B::ReadTransaction<'_>, B::ReadTransaction<'tx>>(tx) }
153-
});
154-
self.transaction = TransactionState::Owned(tx);
155-
}
156-
true
123+
fn should_check_backing_storage(&self) -> bool {
124+
self.backend.should_restore() && self.backend.local_is_partial
157125
}
158126

159127
fn restore_task_data(
160-
&mut self,
128+
&self,
161129
task_id: TaskId,
162130
category: SpecificTaskDataCategory,
163131
) -> TaskStorage {
164-
if !self.ensure_transaction() {
132+
if !self.should_check_backing_storage() {
165133
// If we don't need to restore, we can just return an empty storage
166134
return TaskStorage::default();
167135
}
168-
let tx = self.get_tx();
169136
let mut storage = TaskStorage::default();
170-
// Safety: `tx` is a valid transaction from `self.backend.backing_storage`.
171-
let result = unsafe {
172-
self.backend
173-
.backing_storage
174-
.lookup_data(tx, task_id, category, &mut storage)
175-
};
137+
let result = self
138+
.backend
139+
.backing_storage
140+
.lookup_data(task_id, category, &mut storage);
176141

177142
match result {
178143
Ok(()) => storage,
@@ -186,25 +151,22 @@ where
186151
}
187152

188153
fn restore_task_data_batch(
189-
&mut self,
154+
&self,
190155
task_ids: &[TaskId],
191156
category: SpecificTaskDataCategory,
192157
) -> Option<Vec<TaskStorage>> {
193158
debug_assert!(
194159
task_ids.len() > 1,
195160
"Use restore_task_data_typed for single task"
196161
);
197-
if !self.ensure_transaction() {
162+
if !self.should_check_backing_storage() {
198163
// If we don't need to restore, we return None
199164
return None;
200165
}
201-
let tx = self.get_tx();
202-
// Safety: `tx` is a valid transaction from `self.backend.backing_storage`.
203-
let result = unsafe {
204-
self.backend
205-
.backing_storage
206-
.batch_lookup_data(tx, task_ids, category)
207-
};
166+
let result = self
167+
.backend
168+
.backing_storage
169+
.batch_lookup_data(task_ids, category);
208170
match result {
209171
Ok(result) => Some(result),
210172
Err(e) => {
@@ -219,13 +181,6 @@ where
219181
}
220182
}
221183

222-
fn get_tx(&self) -> Option<&<B as BackingStorageSealed>::ReadTransaction<'tx>> {
223-
match &self.transaction {
224-
TransactionState::None => unreachable!(),
225-
TransactionState::Owned(tx) => tx.as_ref(),
226-
}
227-
}
228-
229184
fn prepare_tasks_with_callback(
230185
&mut self,
231186
task_ids: impl IntoIterator<Item = (TaskId, TaskDataCategory)>,
@@ -396,13 +351,10 @@ where
396351
}
397352
}
398353

399-
impl<'e, 'tx, B: BackingStorage> ExecuteContext<'e> for ExecuteContextImpl<'e, 'tx, B>
400-
where
401-
'tx: 'e,
402-
{
354+
impl<'e, B: BackingStorage> ExecuteContext<'e> for ExecuteContextImpl<'e, B> {
403355
type TaskGuardImpl = TaskGuardImpl<'e>;
404356

405-
fn child_context<'l, 'r>(&'r self) -> impl ChildExecuteContext<'l> + use<'e, 'tx, 'l, B>
357+
fn child_context<'l, 'r>(&'r self) -> impl ChildExecuteContext<'l> + use<'e, 'l, B>
406358
where
407359
'e: 'l,
408360
{
@@ -636,20 +588,16 @@ where
636588
}
637589

638590
fn task_by_type(&mut self, task_type: &CachedTaskType) -> Option<TaskId> {
639-
// Ensure we have a transaction (this will be reused by subsequent task() calls)
640-
if !self.ensure_transaction() {
591+
if !self.should_check_backing_storage() {
641592
return None;
642593
}
643-
let tx = self.get_tx();
644594

645595
// Get candidates from backing storage (hash-based lookup may return multiple)
646-
// Safety: `tx` is a valid transaction from `self.backend.backing_storage`.
647-
let candidates = unsafe {
648-
self.backend
649-
.backing_storage
650-
.lookup_task_candidates(tx, task_type)
651-
.expect("Failed to lookup task ids")
652-
};
596+
let candidates = self
597+
.backend
598+
.backing_storage
599+
.lookup_task_candidates(task_type)
600+
.expect("Failed to lookup task ids");
653601

654602
// Verify each candidate by comparing the stored persistent_task_type.
655603
// Only rarely is there more than one candidate, so no need for parallelization.
@@ -676,7 +624,6 @@ impl<'e, B: BackingStorage> ChildExecuteContext<'e> for ChildExecuteContextImpl<
676624
backend: self.backend,
677625
turbo_tasks: self.turbo_tasks,
678626
_operation_guard: None,
679-
transaction: TransactionState::None,
680627
#[cfg(debug_assertions)]
681628
active_task_locks: std::sync::Arc::new(std::sync::atomic::AtomicU8::new(0)),
682629
}

0 commit comments

Comments
 (0)