libblobd_lite/
ctx.rs

1use crate::allocator::Allocator;
2use crate::bucket::Buckets;
3use crate::deleted_list::DeletedList;
4use crate::incomplete_list::IncompleteList;
5use crate::metrics::BlobdMetrics;
6use crate::object_id::ObjectIdSerial;
7use crate::page::Pages;
8use crate::stream::Stream;
9use crate::stream::StreamInMemory;
10#[cfg(test)]
11use crate::test_util::device::TestSeekableAsyncFile as SeekableAsyncFile;
12#[cfg(test)]
13use crate::test_util::journal::TestWriteJournal as WriteJournal;
14#[cfg(not(test))]
15use seekable_async_file::SeekableAsyncFile;
16use std::sync::Arc;
17use tokio::sync::Mutex;
18#[cfg(not(test))]
19use write_journal::WriteJournal;
20
21// We must lock these together instead of individually. Inside a transaction, it will make mutation calls to these subsystems, and transactions get committed in the order they started. However, it's possible during the transaction that the earliest transaction does not reach all subsystems first, which would mean that the changes for some subsystems may get written out of order. For example, consider that request 1 may update incomplete list before request 0, even though request 0 came first, created an earlier transaction, and returned from its call to the free list before request 1, purely because of unfortunate luck with lock acquisition or the Tokio or Linux thread scheduler. (A simpler example would be if request 0 updates incomplete list first then allocator second, while request 1 updates allocator first then incomplete list second.) Request 0's transaction is always committed before request 1's (enforced by WriteJournal), but request 0 contains changes to incomplete list that depend on request 1's changes, so writing request 1 will clobber request 0's changes and corrupt the state.
22pub(crate) struct State {
23  pub allocator: Allocator,
24  pub deleted_list: DeletedList,
25  pub incomplete_list: IncompleteList,
26  pub object_id_serial: ObjectIdSerial,
27  pub stream: Stream,
28}
29
30pub(crate) struct Ctx {
31  pub buckets: Buckets,
32  pub device: SeekableAsyncFile,
33  pub journal: Arc<WriteJournal>,
34  pub metrics: Arc<BlobdMetrics>,
35  /// WARNING: Do not call methods that mutate data on the device from outside a transactionand locked `State`. This isn't enforced via `&mut self` methods to save some hassle with the Rust borrow checker.
36  pub pages: Arc<Pages>,
37  // This value controls:
38  // - How long tokens live for, which forces the object to live at least that long (even if marked as deleted).
39  // - How long before incomplete objects are automatically sent for deletion.
40  pub reap_objects_after_secs: u64,
41  /// WARNING: Begin transaction AFTER acquiring lock, as otherwise state change data will be written out of order. The journal will always write transactions in order (even if committed out of order), which means transactions must be started in the order that state is changed, and that's not guaranteed if lock hasn't been acquired yet.
42  pub state: Mutex<State>,
43  pub stream_in_memory: Arc<StreamInMemory>,
44  pub versioning: bool,
45}