Skip to main content

fxfs/object_store/
transaction.rs

1// Copyright 2021 The Fuchsia Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5use crate::checksum::Checksum;
6use crate::errors::FxfsError;
7use crate::filesystem::Filesystem;
8use crate::log::*;
9use crate::lsm_tree::types::Item;
10use crate::object_handle::INVALID_OBJECT_ID;
11use crate::object_store::allocator::{AllocatorItem, Hold, Reservation};
12use crate::object_store::object_manager::{ObjectManager, reserved_space_from_journal_usage};
13use crate::object_store::object_record::{
14    BytesAndNodes, FxfsKey, FxfsKeyV49, ObjectItem, ObjectItemV56, ObjectItemV59, ObjectItemV62,
15    ObjectKey, ObjectKeyData, ObjectValue, ProjectProperty,
16};
17use crate::object_store::{AttributeId, AttributeKey, ProjectId};
18use crate::serialized_types::{Migrate, Versioned, migrate_nodefault, migrate_to_version};
19use anyhow::Error;
20use async_trait::async_trait;
21use either::{Either, Left, Right};
22use event_listener::Event;
23use fprint::TypeFingerprint;
24use fuchsia_sync::Mutex;
25use futures::future::poll_fn;
26use futures::pin_mut;
27use rustc_hash::FxHashMap as HashMap;
28use scopeguard::ScopeGuard;
29use serde::{Deserialize, Serialize};
30use std::cell::UnsafeCell;
31use std::cmp::Ordering;
32use std::collections::hash_map::Entry;
33use std::collections::{BTreeSet, btree_set};
34use std::iter::Peekable;
35use std::marker::PhantomPinned;
36use std::ops::{Deref, DerefMut, Range};
37use std::sync::Arc;
38use std::sync::atomic::{AtomicU64, Ordering as AtomicOrdering};
39use std::task::{Poll, Waker};
40use std::{fmt, mem};
41
42/// How metadata and allocator reservations should be handled for a transaction.
43///
44/// A transaction consumes space in two different ways:
45///
46/// 1. The transaction will consume space in the journal and eventually in an LSM tree (the
47///    "metadata" cost of the transaction).
48/// 2. The transaction might also include additional allocations or deallocations.  These may come
49///    directly from the global allocator, or they might come from a Reservation object, depending
50///    on the context.  (See specific enum cases for details.)
51///
52/// The basic decision tree for which variant to use is as follows:
53///
54/// - Is there an existing reservation which the transaction should draw from?
55///   - YES: Use [`ReservationOptions::Hold`]
56///   -  NO: Is the transaction allocating or deallocating extents for internal filesystem metadata
57///          (e.g. LSM tree compaction, journal/superblock growth, or graveyard tombstoning in the
58///          root/root-parent stores) or expected to be space-neutral or space-saving?
59///          - YES: Use [`ReservationOptions::Borrowed`]
60///          -  NO: Use [`ReservationOptions::New`]
61#[derive(Clone, Copy, Default)]
62pub enum ReservationOptions<'a> {
63    /// A new reservation is created to accommodate the metadata of a maximally-sized transaction,
64    /// and the transaction's metadata is paid for from this reservation.  Allocations and
65    /// deallocations within the transaction will go directly to the global allocator (and therefore
66    /// might fail if there's insufficient space).
67    ///
68    /// This should be the default case for most transactions which permit failure when there's no
69    /// space left in the global allocator.
70    #[default]
71    New,
72
73    /// Metadata space is borrowed directly from the global reservation held by the ObjectManager.
74    ///
75    /// For system stores (`root_parent_store` and `root_store`), extent allocations and
76    /// deallocations in the transaction also draw from and return space to the global metadata
77    /// reservation (e.g. journal and super-block growth/shrink, LSM tree compaction, and graveyard
78    /// tombstoning of layer files).
79    ///
80    /// For child stores, extent allocations are not permitted, and extent deallocations are
81    /// returned to the global allocator pool.  This must only be used for child-store transactions
82    /// that will either not affect net space usage after compaction (e.g. setting attributes on an
83    /// object) or will reduce space (e.g. unlinking a file or deleting a range of extents).
84    ///
85    /// When this is used appropriately, it is guaranteed that transactions will not fail due to
86    /// lack of space (barring any bugs resulting in a deficit in the global reservation).
87    Borrowed,
88
89    /// Use the specified reservation for the transaction's metadata (placing a hold on it) as well
90    /// as any allocations or deallocations in the transaction.
91    Hold(&'a Reservation),
92}
93
94/// This allows for special handling of certain transactions such as deletes and the
95/// extension of Journal extents. For most other use cases it is appropriate to use
96/// `default()` here.
97#[derive(Clone, Copy, Default)]
98pub struct Options<'a> {
99    /// If true, don't check for low journal space.  This should be true for any transactions that
100    /// might alleviate journal space (i.e. compaction).
101    pub skip_journal_checks: bool,
102
103    /// How metadata and allocator reservations should be handled for the transaction.
104    pub reservation: ReservationOptions<'a>,
105
106    /// If set, indicates that this transaction is nested within `parent_transaction` and shares
107    /// its in-flight transaction slot.  The use case for this is a nested transaction, where a
108    /// separate transaction must be committed while constructing another transaction (for example,
109    /// when rolling the object ID cipher).
110    ///
111    /// NOTE: The locks acquired by the nested transaction do not cooperate in any way with the
112    /// locks already held by parent_transaction, so care must still be taken to avoid deadlocks.
113    pub parent_transaction: Option<&'a Transaction<'a>>,
114}
115
116// This is the amount of space that we reserve for metadata when we are creating a new transaction.
117// A transaction should not take more than this.  This is expressed in terms of space occupied in
118// the journal; transactions must not take up more space in the journal than the number below.  The
119// amount chosen here must be large enough for the maximum possible transaction that can be created,
120// so transactions always need to be bounded which might involve splitting an operation up into
121// smaller transactions.
122pub const TRANSACTION_MAX_JOURNAL_USAGE: u64 = 24_576;
123pub const TRANSACTION_METADATA_MAX_AMOUNT: u64 =
124    reserved_space_from_journal_usage(TRANSACTION_MAX_JOURNAL_USAGE);
125
126#[must_use]
127pub struct TransactionLocks<'a>(pub WriteGuard<'a>);
128
129/// Limits the number of in-flight transactions to `limit`.
130pub struct TransactionConcurrencyLimiter {
131    limit: u64,
132    // The number of in-flight transactions which we will limit to `limit`.
133    in_flight_transactions: AtomicU64,
134    // An event that is used to wake up tasks that are blocked due to the in-flight transaction
135    // limit.
136    transaction_limit_event: Event,
137}
138
139impl TransactionConcurrencyLimiter {
140    pub fn new(limit: u64) -> Self {
141        Self {
142            limit,
143            in_flight_transactions: AtomicU64::new(0),
144            transaction_limit_event: Event::new(),
145        }
146    }
147
148    pub async fn add(&self, skip_journal_checks: bool) {
149        if skip_journal_checks {
150            self.in_flight_transactions.fetch_add(1, AtomicOrdering::Relaxed);
151        } else {
152            let inc = || {
153                let mut in_flights = self.in_flight_transactions.load(AtomicOrdering::Relaxed);
154                while in_flights < self.limit {
155                    match self.in_flight_transactions.compare_exchange_weak(
156                        in_flights,
157                        in_flights + 1,
158                        AtomicOrdering::Relaxed,
159                        AtomicOrdering::Relaxed,
160                    ) {
161                        Ok(_) => return true,
162                        Err(x) => in_flights = x,
163                    }
164                }
165                false
166            };
167            while !inc() {
168                let listener = self.transaction_limit_event.listen();
169                if inc() {
170                    break;
171                }
172                debug_assert_not_too_long!(listener);
173            }
174        }
175    }
176
177    pub fn sub(&self) {
178        let old = self.in_flight_transactions.fetch_sub(1, AtomicOrdering::Relaxed);
179        assert!(old != 0);
180        if old <= self.limit {
181            self.transaction_limit_event.notify(usize::MAX);
182        }
183    }
184}
185
186/// An object into which transactions are committed.  Usually, this is the journal.
187///
188/// This exists as an abstraction for remotely mounted volumes, which need to submit transactions to
189/// the remote filesystem rather than directly writing into the journal.
190#[async_trait]
191pub trait TransactionSink: Send + Sync + 'static {
192    /// Creates a new transaction, acquiring `txn_locks`.  See [`Options`] for details.
193    async fn new_transaction<'a>(
194        self: Arc<Self>,
195        txn_locks: LockKeys,
196        options: Options<'a>,
197    ) -> Result<Transaction<'a>, Error>;
198    /// Checks that there is sufficient space in the journal to write a transaction, blocking if
199    /// necessary until space is available.
200    async fn check_space(&self) -> Result<(), Error>;
201    /// Reserves space for a transaction's metadata.  Returns `None` if there is insufficient space.
202    fn reserve(&self, owner_object_id: Option<u64>, amount: u64) -> Option<Reservation>;
203    /// Returns true if the sink is in image builder mode.
204    fn image_builder_mode(&self) -> bool {
205        false
206    }
207    /// Commits a transaction.  Returns a monotonically increasing offset (usually, the journal
208    /// offset of the transaction) on success.
209    async fn commit(&self, transaction: &mut Transaction<'_>) -> Result<u64, Error>;
210    /// Like [`Self::commit`], but invokes a callback upon completion, before dropping locks.
211    async fn commit_with_callback<'a>(
212        &self,
213        transaction: &mut Transaction<'_>,
214        callback: Box<dyn FnOnce(u64) + Send + 'a>,
215    ) -> Result<u64, Error>;
216    /// Downgrades locks from the WriteLock state to Locked state.  This will panic if the locks are
217    /// not in the WriteLock state.
218    fn downgrade_locks(&self, lock_keys: &LockKeys);
219    /// A function to be called by [`Transaction::drop`], allowing for additional work to be
220    /// performed on drop.
221    fn drop_transaction(&self, transaction: &mut Transaction<'_>);
222    /// Returns the lock manager instance.
223    fn lock_manager(&self) -> &LockManager;
224    /// Returns the transaction concurrency limiter.
225    fn transaction_limiter(&self) -> &TransactionConcurrencyLimiter;
226    /// Returns the global metadata reservation if supported by this sink.
227    fn metadata_reservation(&self) -> Option<&Reservation> {
228        None
229    }
230    /// Returns true if `store_id` is a system store (`root_store` or `root_parent_store`).
231    fn is_system_store(&self, _store_id: u64) -> bool {
232        false
233    }
234}
235
236/// The journal consists of these records which will be replayed at mount time.  Within a
237/// transaction, these are stored as a set which allows some mutations to be deduplicated and found
238/// (and we require custom comparison functions below).  For example, we need to be able to find
239/// object size changes.
240pub type Mutation = MutationV62;
241
242#[derive(
243    Clone, Debug, Eq, Ord, PartialEq, PartialOrd, Serialize, Deserialize, TypeFingerprint, Versioned,
244)]
245#[cfg_attr(fuzz, derive(arbitrary::Arbitrary))]
246pub enum MutationV62 {
247    ObjectStore(ObjectStoreMutationV62),
248    EncryptedObjectStore(#[serde(with = "crate::zerocopy_serialization")] Box<[u8]>),
249    Allocator(AllocatorMutationV32),
250
251    /// Indicates the beginning of a flush. This would typically involve sealing a tree.
252    BeginFlush,
253
254    /// Indicates the end of a flush. This would typically involve replacing the immutable layers
255    /// with compacted ones. The `u64` records the child store's new flushed layer borrowing
256    /// (`store_borrowed`, or `0` for full compactions and system objects).
257    EndFlush(u64),
258
259    /// Volume has been deleted. Requires we remove it from the set of managed ObjectStore.
260    DeleteVolume,
261    UpdateMutationsKey(UpdateMutationsKey),
262    CreateInternalDir(u64),
263
264    /// Legacy `< V61` mutation only produced in memory when migrating `MutationV59` during journal
265    /// replay (`#[serde(skip)]` prevents serializing or deserializing this variant in V61+
266    /// streams). This can be removed once all `< V61` versions are removed; it must remain the
267    /// *last* variant so that the serialized variant tags of the preceding variants do not change
268    /// when it is removed.
269    #[serde(skip)]
270    #[cfg_attr(fuzz, arbitrary(skip))]
271    UpdateBorrowed(u64),
272}
273
274#[derive(Migrate, Clone, Debug, PartialEq, Serialize, Deserialize, TypeFingerprint, Versioned)]
275#[migrate_to_version(MutationV62)]
276pub enum MutationV61 {
277    ObjectStore(ObjectStoreMutationV59),
278    EncryptedObjectStore(#[serde(with = "crate::zerocopy_serialization")] Box<[u8]>),
279    Allocator(AllocatorMutationV32),
280    BeginFlush,
281    EndFlush(u64),
282    DeleteVolume,
283    UpdateMutationsKey(UpdateMutationsKey),
284    CreateInternalDir(u64),
285    #[serde(skip)]
286    UpdateBorrowed(u64),
287}
288
289#[derive(Clone, Debug, PartialEq, Serialize, Deserialize, TypeFingerprint, Versioned)]
290pub enum MutationV59 {
291    ObjectStore(ObjectStoreMutationV59),
292    EncryptedObjectStore(#[serde(with = "crate::zerocopy_serialization")] Box<[u8]>),
293    Allocator(AllocatorMutationV32),
294    BeginFlush,
295    EndFlush,
296    DeleteVolume,
297    UpdateBorrowed(u64),
298    UpdateMutationsKey(UpdateMutationsKey),
299    CreateInternalDir(u64),
300}
301
302impl From<MutationV59> for MutationV61 {
303    fn from(mutation: MutationV59) -> Self {
304        match mutation {
305            MutationV59::ObjectStore(m) => Self::ObjectStore(m),
306            MutationV59::EncryptedObjectStore(m) => Self::EncryptedObjectStore(m),
307            MutationV59::Allocator(m) => Self::Allocator(m),
308            MutationV59::BeginFlush => Self::BeginFlush,
309            MutationV59::EndFlush => Self::EndFlush(0),
310            MutationV59::DeleteVolume => Self::DeleteVolume,
311            MutationV59::UpdateBorrowed(b) => Self::UpdateBorrowed(b),
312            MutationV59::UpdateMutationsKey(k) => Self::UpdateMutationsKey(k),
313            MutationV59::CreateInternalDir(id) => Self::CreateInternalDir(id),
314        }
315    }
316}
317
318#[derive(Migrate, Clone, Debug, PartialEq, Serialize, Deserialize, TypeFingerprint, Versioned)]
319#[migrate_to_version(MutationV59)]
320pub enum MutationV57 {
321    ObjectStore(ObjectStoreMutationV56),
322    EncryptedObjectStore(#[serde(with = "crate::zerocopy_serialization")] Box<[u8]>),
323    Allocator(AllocatorMutationV32),
324    BeginFlush,
325    EndFlush,
326    DeleteVolume,
327    UpdateBorrowed(u64),
328    UpdateMutationsKey(UpdateMutationsKey),
329    CreateInternalDir(u64),
330}
331
332#[derive(Migrate, Clone, Debug, PartialEq, Serialize, Deserialize, TypeFingerprint, Versioned)]
333#[migrate_to_version(MutationV57)]
334pub enum MutationV56 {
335    ObjectStore(ObjectStoreMutationV56),
336    EncryptedObjectStore(#[serde(with = "crate::zerocopy_serialization")] Box<[u8]>),
337    Allocator(AllocatorMutationV32),
338    BeginFlush,
339    EndFlush,
340    DeleteVolume,
341    UpdateBorrowed(u64),
342    UpdateMutationsKey(UpdateMutationsKey),
343    CreateInternalDir(u64),
344}
345
346impl Mutation {
347    pub fn insert_object(key: ObjectKey, value: ObjectValue) -> Self {
348        Mutation::ObjectStore(ObjectStoreMutation {
349            item: Item::new(key, value),
350            op: Operation::Insert,
351        })
352    }
353
354    pub fn replace_or_insert_object(key: ObjectKey, value: ObjectValue) -> Self {
355        Mutation::ObjectStore(ObjectStoreMutation {
356            item: Item::new(key, value),
357            op: Operation::ReplaceOrInsert,
358        })
359    }
360
361    pub fn merge_object(key: ObjectKey, value: ObjectValue) -> Self {
362        Mutation::ObjectStore(ObjectStoreMutation {
363            item: Item::new(key, value),
364            op: Operation::Merge,
365        })
366    }
367
368    pub fn update_mutations_key(key: FxfsKey) -> Self {
369        Mutation::UpdateMutationsKey(key.into())
370    }
371
372    /// Returns true if this mutation's serialized journal bytes should be accounted against the
373    /// target object's per-store journal usage (`J_S`) rather than system journal usage (`J_sys`).
374    ///
375    /// Lifecycle control records (`BeginFlush`, `EndFlush`, and `DeleteVolume`) are not written
376    /// into an object store's layer files, and `BeginFlush`/`EndFlush` are committed in
377    /// `Borrowed` transactions where `apply_mutation` transitions
378    /// `journal_checkpoints` and `store_borrowed` before post-transaction usage accounting runs.
379    /// Accounting their bytes in `J_sys` ensures they do not re-dirty `journal_checkpoints` or add
380    /// post-flush borrowed debt to `store_borrowed`.
381    pub fn counts_towards_object_journal_usage(&self) -> bool {
382        !matches!(self, Mutation::BeginFlush | Mutation::EndFlush(_) | Mutation::DeleteVolume)
383    }
384}
385
386// We have custom comparison functions for mutations that just use the key, rather than the key and
387// value that would be used by default so that we can deduplicate and find mutations (see
388// get_object_mutation below).
389pub type ObjectStoreMutation = ObjectStoreMutationV62;
390
391#[derive(Clone, Debug, Serialize, Deserialize, TypeFingerprint)]
392#[cfg_attr(fuzz, derive(arbitrary::Arbitrary))]
393pub struct ObjectStoreMutationV62 {
394    pub item: ObjectItemV62,
395    pub op: Operation,
396}
397
398#[derive(Migrate, Clone, Debug, PartialEq, Serialize, Deserialize, TypeFingerprint, Versioned)]
399#[migrate_to_version(ObjectStoreMutationV62)]
400#[migrate_nodefault]
401pub struct ObjectStoreMutationV59 {
402    pub item: ObjectItemV59,
403    pub op: Operation,
404}
405
406#[derive(Migrate, Clone, Debug, PartialEq, Serialize, Deserialize, TypeFingerprint, Versioned)]
407#[migrate_to_version(ObjectStoreMutationV59)]
408#[migrate_nodefault]
409pub struct ObjectStoreMutationV56 {
410    pub item: ObjectItemV56,
411    pub op: Operation,
412}
413
414/// The different LSM tree operations that can be performed as part of a mutation.
415pub type Operation = OperationV32;
416
417#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize, TypeFingerprint)]
418#[cfg_attr(fuzz, derive(arbitrary::Arbitrary))]
419pub enum OperationV32 {
420    Insert,
421    ReplaceOrInsert,
422    Merge,
423}
424
425impl Ord for ObjectStoreMutation {
426    fn cmp(&self, other: &Self) -> Ordering {
427        self.item.key.cmp(&other.item.key)
428    }
429}
430
431impl PartialOrd for ObjectStoreMutation {
432    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
433        Some(self.cmp(other))
434    }
435}
436
437impl PartialEq for ObjectStoreMutation {
438    fn eq(&self, other: &Self) -> bool {
439        self.item.key.eq(&other.item.key)
440    }
441}
442
443impl Eq for ObjectStoreMutation {}
444
445impl Ord for AllocatorItem {
446    fn cmp(&self, other: &Self) -> Ordering {
447        self.key.cmp(&other.key)
448    }
449}
450
451impl PartialOrd for AllocatorItem {
452    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
453        Some(self.cmp(other))
454    }
455}
456
457/// Same as std::ops::Range but with Ord and PartialOrd support, sorted first by start of the range,
458/// then by the end.
459pub type DeviceRange = DeviceRangeV32;
460
461#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize, TypeFingerprint)]
462#[cfg_attr(fuzz, derive(arbitrary::Arbitrary))]
463pub struct DeviceRangeV32(pub Range<u64>);
464
465impl Deref for DeviceRange {
466    type Target = Range<u64>;
467
468    fn deref(&self) -> &Self::Target {
469        &self.0
470    }
471}
472
473impl DerefMut for DeviceRange {
474    fn deref_mut(&mut self) -> &mut Self::Target {
475        &mut self.0
476    }
477}
478
479impl From<Range<u64>> for DeviceRange {
480    fn from(range: Range<u64>) -> Self {
481        Self(range)
482    }
483}
484
485impl Into<Range<u64>> for DeviceRange {
486    fn into(self) -> Range<u64> {
487        self.0
488    }
489}
490
491impl Ord for DeviceRange {
492    fn cmp(&self, other: &Self) -> Ordering {
493        self.start.cmp(&other.start).then(self.end.cmp(&other.end))
494    }
495}
496
497impl PartialOrd for DeviceRange {
498    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
499        Some(self.cmp(other))
500    }
501}
502
503pub type AllocatorMutation = AllocatorMutationV32;
504
505// LINT.IfChange
506#[derive(Clone, Debug, Eq, Ord, PartialEq, PartialOrd, Serialize, Deserialize, TypeFingerprint)]
507#[cfg_attr(fuzz, derive(arbitrary::Arbitrary))]
508pub enum AllocatorMutationV32 {
509    Allocate {
510        device_range: DeviceRangeV32,
511        owner_object_id: u64,
512    },
513    Deallocate {
514        device_range: DeviceRangeV32,
515        owner_object_id: u64,
516    },
517    SetLimit {
518        owner_object_id: u64,
519        bytes: u64,
520    },
521    /// Marks all extents with a given owner_object_id for deletion.
522    /// Used to free space allocated to encrypted ObjectStore where we may not have the key.
523    /// Note that the actual deletion time is undefined so this should never be used where an
524    /// ObjectStore is still in use due to a high risk of corruption. Similarly, owner_object_id
525    /// should never be reused for the same reasons.
526    MarkForDeletion(u64),
527}
528// LINT.ThenChange(//src/storage/fxfs/src/object_store/extent_record.rs)
529
530pub type UpdateMutationsKey = UpdateMutationsKeyV49;
531
532#[derive(Clone, Debug, Serialize, Deserialize, TypeFingerprint)]
533pub struct UpdateMutationsKeyV49(pub FxfsKeyV49);
534
535impl From<UpdateMutationsKey> for FxfsKey {
536    fn from(outer: UpdateMutationsKey) -> Self {
537        outer.0
538    }
539}
540
541impl From<FxfsKey> for UpdateMutationsKey {
542    fn from(inner: FxfsKey) -> Self {
543        Self(inner)
544    }
545}
546
547#[cfg(fuzz)]
548impl<'a> arbitrary::Arbitrary<'a> for UpdateMutationsKey {
549    fn arbitrary(u: &mut arbitrary::Unstructured<'a>) -> arbitrary::Result<Self> {
550        Ok(UpdateMutationsKey::from(FxfsKey::arbitrary(u).unwrap()))
551    }
552}
553
554impl Ord for UpdateMutationsKey {
555    fn cmp(&self, other: &Self) -> Ordering {
556        (self as *const UpdateMutationsKey).cmp(&(other as *const _))
557    }
558}
559
560impl PartialOrd for UpdateMutationsKey {
561    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
562        Some(self.cmp(other))
563    }
564}
565
566impl Eq for UpdateMutationsKey {}
567
568impl PartialEq for UpdateMutationsKey {
569    fn eq(&self, other: &Self) -> bool {
570        std::ptr::eq(self, other)
571    }
572}
573
574/// When creating a transaction, locks typically need to be held to prevent two or more writers
575/// trying to make conflicting mutations at the same time.  LockKeys are used for this.
576/// NOTE: Ordering is important here!  The lock manager sorts the list of locks in a transaction
577/// to acquire them in a consistent order, but there is a special case for the Flush lock.
578/// The Flush lock is taken when we flush an LSM tree (e.g. an object store), and is held for
579/// several transactions.  As such, it must come first in the lock acquisition ordering, so that
580/// other transactions using the Flush lock have the same ordering as in flushing.
581#[derive(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd, Copy)]
582pub enum LockKey {
583    /// Used to lock flushing an object.
584    Flush {
585        object_id: u64,
586    },
587
588    /// Used to lock changes to a particular object attribute (e.g. writes).
589    ObjectAttribute {
590        store_object_id: u64,
591        object_id: u64,
592        attribute_id: AttributeId,
593    },
594
595    /// Used to lock changes to a particular object (e.g. adding a child to a directory).
596    Object {
597        store_object_id: u64,
598        object_id: u64,
599    },
600
601    ProjectId {
602        store_object_id: u64,
603        project_id: ProjectId,
604    },
605
606    /// Used to lock any truncate operations for a file.
607    Truncate {
608        store_object_id: u64,
609        object_id: u64,
610    },
611
612    /// A lock used when getting or creating the internal directory.
613    InternalDirectory {
614        store_object_id: u64,
615    },
616
617    /// Used to serialize pre caching of keys.  The lock ordering is different for this: it is
618    /// acquired in `Filesystem::commit_transaction` and happens *after* other keys have been
619    /// promoted to write locks, but before the commit lock.
620    PreCacheKeys {
621        store_object_id: u64,
622    },
623}
624
625impl LockKey {
626    pub const fn object_attribute(
627        store_object_id: u64,
628        object_id: u64,
629        attribute_id: AttributeId,
630    ) -> Self {
631        LockKey::ObjectAttribute { store_object_id, object_id, attribute_id }
632    }
633
634    pub const fn object(store_object_id: u64, object_id: u64) -> Self {
635        LockKey::Object { store_object_id, object_id }
636    }
637
638    pub const fn flush(object_id: u64) -> Self {
639        LockKey::Flush { object_id }
640    }
641
642    pub const fn truncate(store_object_id: u64, object_id: u64) -> Self {
643        LockKey::Truncate { store_object_id, object_id }
644    }
645
646    pub const fn pre_cache_keys(store_object_id: u64) -> Self {
647        LockKey::PreCacheKeys { store_object_id }
648    }
649}
650
651/// A container for holding `LockKey` objects. Can store a single `LockKey` inline.
652#[derive(Clone, Debug)]
653pub enum LockKeys {
654    None,
655    Inline(LockKey),
656    Vec(Vec<LockKey>),
657}
658
659impl LockKeys {
660    pub fn with_capacity(capacity: usize) -> Self {
661        if capacity > 1 { LockKeys::Vec(Vec::with_capacity(capacity)) } else { LockKeys::None }
662    }
663
664    pub fn push(&mut self, key: LockKey) {
665        match self {
666            Self::None => *self = LockKeys::Inline(key),
667            Self::Inline(inline) => {
668                *self = LockKeys::Vec(vec![*inline, key]);
669            }
670            Self::Vec(vec) => vec.push(key),
671        }
672    }
673
674    pub fn truncate(&mut self, len: usize) {
675        match self {
676            Self::None => {}
677            Self::Inline(_) => {
678                if len == 0 {
679                    *self = Self::None;
680                }
681            }
682            Self::Vec(vec) => vec.truncate(len),
683        }
684    }
685
686    fn len(&self) -> usize {
687        match self {
688            Self::None => 0,
689            Self::Inline(_) => 1,
690            Self::Vec(vec) => vec.len(),
691        }
692    }
693
694    fn contains(&self, key: &LockKey) -> bool {
695        match self {
696            Self::None => false,
697            Self::Inline(single) => single == key,
698            Self::Vec(vec) => vec.contains(key),
699        }
700    }
701
702    fn sort_unstable(&mut self) {
703        match self {
704            Self::Vec(vec) => vec.sort_unstable(),
705            _ => {}
706        }
707    }
708
709    fn dedup(&mut self) {
710        match self {
711            Self::Vec(vec) => vec.dedup(),
712            _ => {}
713        }
714    }
715
716    fn iter(&self) -> LockKeysIter<'_> {
717        match self {
718            LockKeys::None => LockKeysIter::None,
719            LockKeys::Inline(key) => LockKeysIter::Inline(key),
720            LockKeys::Vec(keys) => LockKeysIter::Vec(keys.iter()),
721        }
722    }
723}
724
725enum LockKeysIter<'a> {
726    None,
727    Inline(&'a LockKey),
728    Vec(std::slice::Iter<'a, LockKey>),
729}
730
731impl<'a> Iterator for LockKeysIter<'a> {
732    type Item = &'a LockKey;
733    fn next(&mut self) -> Option<Self::Item> {
734        match self {
735            Self::None => None,
736            Self::Inline(inline) => {
737                let next = *inline;
738                *self = Self::None;
739                Some(next)
740            }
741            Self::Vec(vec) => vec.next(),
742        }
743    }
744}
745
746impl Default for LockKeys {
747    fn default() -> Self {
748        LockKeys::None
749    }
750}
751
752#[macro_export]
753macro_rules! lock_keys {
754    () => {
755        $crate::object_store::transaction::LockKeys::None
756    };
757    ($lock_key:expr $(,)?) => {
758        $crate::object_store::transaction::LockKeys::Inline($lock_key)
759    };
760    ($($lock_keys:expr),+ $(,)?) => {
761        $crate::object_store::transaction::LockKeys::Vec(vec![$($lock_keys),+])
762    };
763}
764pub use lock_keys;
765
766/// Mutations in a transaction can be associated with an object so that when mutations are applied,
767/// updates can be applied to in-memory structures. For example, we cache object sizes, so when a
768/// size change is applied, we can update the cached object size.
769pub trait AssociatedObject: Send + Sync {
770    fn will_apply_mutation(&self, _mutation: &Mutation, _object_id: u64, _manager: &ObjectManager) {
771    }
772}
773
774pub enum AssocObj<'a> {
775    None,
776    Borrowed(&'a dyn AssociatedObject),
777    Owned(Box<dyn AssociatedObject>),
778}
779
780impl AssocObj<'_> {
781    pub fn map<R, F: FnOnce(&dyn AssociatedObject) -> R>(&self, f: F) -> Option<R> {
782        match self {
783            AssocObj::None => None,
784            AssocObj::Borrowed(b) => Some(f(*b)),
785            AssocObj::Owned(o) => Some(f(o.as_ref())),
786        }
787    }
788}
789
790pub struct TxnMutation<'a> {
791    // This, at time of writing, is either the object ID of an object store, or the object ID of the
792    // allocator.  In the case of an object mutation, there's another object ID in the mutation
793    // record that would be for the object actually being changed.
794    pub object_id: u64,
795
796    // The actual mutation.  This gets serialized to the journal.
797    pub mutation: Mutation,
798
799    // An optional associated object for the mutation.  During replay, there will always be no
800    // associated object.
801    pub associated_object: AssocObj<'a>,
802}
803
804// We store TxnMutation in a set, and for that, we only use object_id and mutation and not the
805// associated object or checksum.
806//
807// WARNING: It is critical that `object_id` (which corresponds to the store ID for store mutations)
808// remains the primary key for sorting. The commit pipeline (`commit_transaction`) relies on
809// the mutations being sorted by `object_id` to acquire store locks in a consistent order,
810// preventing deadlocks.
811impl Ord for TxnMutation<'_> {
812    fn cmp(&self, other: &Self) -> Ordering {
813        self.object_id.cmp(&other.object_id).then_with(|| self.mutation.cmp(&other.mutation))
814    }
815}
816
817impl PartialOrd for TxnMutation<'_> {
818    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
819        Some(self.cmp(other))
820    }
821}
822
823impl PartialEq for TxnMutation<'_> {
824    fn eq(&self, other: &Self) -> bool {
825        self.object_id.eq(&other.object_id) && self.mutation.eq(&other.mutation)
826    }
827}
828
829impl Eq for TxnMutation<'_> {}
830
831impl std::fmt::Debug for TxnMutation<'_> {
832    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
833        f.debug_struct("TxnMutation")
834            .field("object_id", &self.object_id)
835            .field("mutation", &self.mutation)
836            .finish()
837    }
838}
839
840/// An iterator over mutations belonging to a single object store within a transaction.
841/// It wraps a mutable reference to a peekable iterator over transaction mutations and yields
842/// mutations until the object ID changes from the object ID peeked at creation time.
843pub struct ObjectMutationIterator<'a, 'b> {
844    iter: &'a mut Peekable<btree_set::Iter<'b, TxnMutation<'b>>>,
845    object_id: u64,
846}
847
848impl<'a, 'b> ObjectMutationIterator<'a, 'b> {
849    pub fn new(iter: &'a mut Peekable<btree_set::Iter<'b, TxnMutation<'b>>>) -> Option<Self> {
850        let object_id = iter.peek()?.object_id;
851        Some(Self { iter, object_id })
852    }
853
854    pub fn object_id(&self) -> u64 {
855        self.object_id
856    }
857}
858
859impl<'b> Iterator for ObjectMutationIterator<'_, 'b> {
860    type Item = &'b Mutation;
861
862    fn next(&mut self) -> Option<Self::Item> {
863        if self.iter.peek().is_some_and(|m| m.object_id == self.object_id) {
864            Some(&self.iter.next().unwrap().mutation)
865        } else {
866            None
867        }
868    }
869}
870
871impl Drop for ObjectMutationIterator<'_, '_> {
872    // Need to have a drop to iterate to the end so that we always finish the current object before
873    // releasing the borrow.
874    fn drop(&mut self) {
875        for _ in self.by_ref() {}
876    }
877}
878
879#[derive(Debug)]
880pub enum MetadataReservation<'a> {
881    /// Metadata space for this transaction is being borrowed from ObjectManager's metadata
882    /// reservation (and for system stores, extent allocations/deallocations also use it).
883    Borrowed,
884
885    /// A metadata reservation was made when the transaction was created.
886    Reservation(Reservation),
887
888    /// The metadata space is being _held_ within an existing reservation.
889    Hold(Hold<'a>),
890}
891
892impl MetadataReservation<'_> {
893    /// Returns true if this transaction borrows metadata space from `ObjectManager`'s metadata
894    /// reservation (`MetadataReservation::Borrowed`).
895    pub fn is_borrowed(&self) -> bool {
896        matches!(self, Self::Borrowed)
897    }
898}
899
900/// A transaction groups mutation records to be committed as a group.
901pub struct Transaction<'a> {
902    sink: Arc<dyn TransactionSink>,
903
904    // The mutations that make up this transaction.
905    mutations: BTreeSet<TxnMutation<'a>>,
906
907    // The locks that this transaction currently holds.
908    txn_locks: LockKeys,
909
910    /// The reservation for the metadata for this transaction.
911    pub metadata_reservation: MetadataReservation<'a>,
912
913    // Keep track of objects explicitly created by this transaction. No locks are required for them.
914    // Addressed by (owner_object_id, object_id).
915    new_objects: BTreeSet<(u64, u64)>,
916
917    /// Any data checksums which should be evaluated when replaying this transaction.
918    checksums: Vec<(u64, Range<u64>, Vec<Checksum>, bool)>,
919
920    /// Set if this transaction contains data (i.e. includes any extent mutations).
921    includes_write: bool,
922
923    /// If true, don't check for low journal space.
924    pub skip_journal_checks: bool,
925
926    /// If set, indicates that this transaction is nested within `parent_transaction` and shares
927    /// its in-flight transaction slot.
928    pub(crate) parent_transaction: Option<&'a Transaction<'a>>,
929}
930
931impl<'a> Transaction<'a> {
932    /// Creates a new transaction.  `txn_locks` are read locks that can be upgraded to write locks
933    /// at commit time.
934    pub async fn new(
935        sink: Arc<dyn TransactionSink>,
936        options: Options<'a>,
937        txn_locks: LockKeys,
938    ) -> Result<Transaction<'a>, Error> {
939        if options.parent_transaction.is_none() {
940            sink.transaction_limiter().add(options.skip_journal_checks).await;
941        }
942        // We support three options for metadata space reservation:
943        //
944        //   1. We can borrow from the filesystem's metadata reservation.  This should only be
945        //      be used on the understanding that eventually, potentially after a full compaction,
946        //      there should be no net increase in space used.  For example, unlinking an object
947        //      should eventually decrease the amount of space used and setting most attributes
948        //      should not result in any change.
949        //
950        //   2. A reservation is provided in which case we'll place a hold on some of it for
951        //      metadata.
952        //
953        //   3. No reservation is supplied, so we try and reserve space with the allocator now,
954        //      and will return NoSpace if that fails.
955        let metadata_reservation = if sink.image_builder_mode() {
956            MetadataReservation::Borrowed
957        } else {
958            match options.reservation {
959                ReservationOptions::New => {
960                    MetadataReservation::Reservation(sink.reserve(None, 0).unwrap())
961                }
962                ReservationOptions::Borrowed => MetadataReservation::Borrowed,
963                ReservationOptions::Hold(reservation) => {
964                    MetadataReservation::Hold(reservation.reserve(0).unwrap())
965                }
966            }
967        };
968        let mut transaction = Transaction {
969            sink: sink.clone(),
970            mutations: BTreeSet::new(),
971            txn_locks: LockKeys::default(),
972            metadata_reservation,
973            new_objects: BTreeSet::new(),
974            checksums: Vec::new(),
975            includes_write: false,
976            skip_journal_checks: options.skip_journal_checks,
977            parent_transaction: options.parent_transaction,
978        };
979        transaction.add_transaction_reservation().await?;
980
981        transaction.txn_locks = {
982            let lock_manager = sink.lock_manager();
983            let mut write_guard = lock_manager.txn_lock(txn_locks).await;
984            std::mem::take(&mut write_guard.0.lock_keys)
985        };
986        Ok(transaction)
987    }
988
989    /// Tops up the transaction's metadata_reservation to ensure the reservation is large enough to
990    /// accommodate a maximally sized transaction.  This must be called before the transaction is
991    /// used.
992    ///
993    /// If `skip_journal_checks` is unset, this function also ensures that there is sufficient
994    /// space in the journal to write the transaction, and will block if the journal needs to grow.
995    async fn add_transaction_reservation(&mut self) -> Result<(), Error> {
996        if self.sink.image_builder_mode() {
997            // Image builder mode avoids the journal so reservation tracking for metadata overheads
998            // doesn't make sense and so we essentially have 'all or nothing' semantics instead.
999            return Ok(());
1000        }
1001        if !self.skip_journal_checks {
1002            self.sink.check_space().await?;
1003        }
1004
1005        match &mut self.metadata_reservation {
1006            MetadataReservation::Borrowed => {}
1007            MetadataReservation::Hold(hold) => {
1008                let amount = TRANSACTION_METADATA_MAX_AMOUNT.saturating_sub(hold.amount());
1009                hold.add(hold.owner().reserve(amount).ok_or(FxfsError::NoSpace)?.forget());
1010            }
1011            MetadataReservation::Reservation(txn_reservation) => {
1012                txn_reservation.add(
1013                    self.sink
1014                        .reserve(
1015                            txn_reservation.owner_object_id(),
1016                            TRANSACTION_METADATA_MAX_AMOUNT
1017                                .saturating_sub(txn_reservation.amount()),
1018                        )
1019                        .ok_or(FxfsError::NoSpace)?
1020                        .forget(),
1021                );
1022            }
1023        }
1024        Ok(())
1025    }
1026
1027    /// Returns the allocator reservation to be used for allocations and deallocations for
1028    /// `owner_object_id` in this transaction, if any.
1029    pub fn allocator_reservation(&self, owner_object_id: u64) -> Option<&Reservation> {
1030        match &self.metadata_reservation {
1031            MetadataReservation::Borrowed => (!self.sink.image_builder_mode()
1032                && self.sink.is_system_store(owner_object_id))
1033            .then(|| self.sink.metadata_reservation())
1034            .flatten(),
1035            MetadataReservation::Hold(hold) => Some(hold.owner()),
1036            MetadataReservation::Reservation(_) => None,
1037        }
1038    }
1039
1040    pub fn mutations(&self) -> &BTreeSet<TxnMutation<'a>> {
1041        &self.mutations
1042    }
1043
1044    pub fn take_mutations(&mut self) -> BTreeSet<TxnMutation<'a>> {
1045        self.new_objects.clear();
1046        mem::take(&mut self.mutations)
1047    }
1048
1049    /// Adds a mutation to this transaction.  If the mutation already exists, it is replaced and the
1050    /// old mutation is returned.
1051    pub fn add(&mut self, object_id: u64, mutation: Mutation) -> Option<Mutation> {
1052        self.add_with_object(object_id, mutation, AssocObj::None)
1053    }
1054
1055    /// Adds `delta` to a `BytesAndNodes` merge mutation for `key`, folding into any delta already
1056    /// staged in this transaction.  Mutations in a transaction are deduplicated by `ObjectKey`
1057    /// alone, so adding a second delta for the same key would otherwise replace the first.
1058    pub fn merge_bytes_and_nodes(
1059        &mut self,
1060        store_object_id: u64,
1061        key: ObjectKey,
1062        delta: BytesAndNodes,
1063    ) {
1064        let delta = match self.get_object_mutation(store_object_id, key.clone()) {
1065            Some(ObjectStoreMutation {
1066                item: Item { value: ObjectValue::BytesAndNodes { bytes, nodes }, .. },
1067                ..
1068            }) => delta + BytesAndNodes { bytes: *bytes, nodes: *nodes },
1069            _ => delta,
1070        };
1071        if delta.is_zero() {
1072            self.remove(store_object_id, Mutation::merge_object(key, ObjectValue::None));
1073        } else {
1074            self.add_with_object_internal(
1075                store_object_id,
1076                Mutation::merge_object(key, delta.into()),
1077                AssocObj::None,
1078            );
1079        }
1080    }
1081
1082    /// Removes a mutation that matches `mutation`.
1083    pub fn remove(&mut self, object_id: u64, mutation: Mutation) {
1084        let txn_mutation = TxnMutation { object_id, mutation, associated_object: AssocObj::None };
1085        if self.mutations.remove(&txn_mutation) {
1086            if let Mutation::ObjectStore(ObjectStoreMutation {
1087                item:
1088                    ObjectItem {
1089                        key: ObjectKey { object_id: new_object_id, data: ObjectKeyData::Object },
1090                        ..
1091                    },
1092                op: Operation::Insert,
1093            }) = txn_mutation.mutation
1094            {
1095                self.new_objects.remove(&(object_id, new_object_id));
1096            }
1097        }
1098    }
1099
1100    /// Adds a mutation with an associated object. If the mutation already exists, it is replaced
1101    /// and the old mutation is returned.
1102    pub fn add_with_object(
1103        &mut self,
1104        object_id: u64,
1105        mutation: Mutation,
1106        associated_object: AssocObj<'a>,
1107    ) -> Option<Mutation> {
1108        debug_assert!(
1109            !matches!(
1110                mutation,
1111                Mutation::ObjectStore(ObjectStoreMutation {
1112                    item: ObjectItem {
1113                        key: ObjectKey {
1114                            data: ObjectKeyData::Project { property: ProjectProperty::Usage, .. },
1115                            ..
1116                        },
1117                        ..
1118                    },
1119                    ..
1120                })
1121            ),
1122            "Use merge_bytes_and_nodes"
1123        );
1124        self.add_with_object_internal(object_id, mutation, associated_object)
1125    }
1126
1127    /// Adds a mutation with an associated object. If the mutation already exists, it is replaced
1128    /// and the old mutation is returned.
1129    fn add_with_object_internal(
1130        &mut self,
1131        object_id: u64,
1132        mutation: Mutation,
1133        associated_object: AssocObj<'a>,
1134    ) -> Option<Mutation> {
1135        assert!(object_id != INVALID_OBJECT_ID);
1136        match &mutation {
1137            Mutation::ObjectStore(ObjectStoreMutation {
1138                item:
1139                    Item {
1140                        key:
1141                            ObjectKey {
1142                                data: ObjectKeyData::Attribute(_, AttributeKey::Extent(_)), ..
1143                            },
1144                        ..
1145                    },
1146                ..
1147            }) => {
1148                self.includes_write = true;
1149            }
1150            Mutation::Allocator(AllocatorMutation::Allocate { owner_object_id, .. }) => {
1151                assert!(
1152                    !self.metadata_reservation.is_borrowed()
1153                        || self.sink.image_builder_mode()
1154                        || self.sink.is_system_store(*owner_object_id),
1155                    "Allocations for non-system stores are not allowed in Borrowed transactions"
1156                );
1157            }
1158            _ => {}
1159        }
1160        let txn_mutation = TxnMutation { object_id, mutation, associated_object };
1161        self.verify_locks(&txn_mutation);
1162        self.mutations.replace(txn_mutation).map(|m| m.mutation)
1163    }
1164
1165    pub fn add_checksum(
1166        &mut self,
1167        owner_object_id: u64,
1168        range: Range<u64>,
1169        checksums: Vec<Checksum>,
1170        first_write: bool,
1171    ) {
1172        self.checksums.push((owner_object_id, range, checksums, first_write));
1173    }
1174
1175    pub fn includes_write(&self) -> bool {
1176        self.includes_write
1177    }
1178
1179    pub fn checksums(&self) -> &[(u64, Range<u64>, Vec<Checksum>, bool)] {
1180        &self.checksums
1181    }
1182
1183    pub fn take_checksums(&mut self) -> Vec<(u64, Range<u64>, Vec<Checksum>, bool)> {
1184        std::mem::replace(&mut self.checksums, Vec::new())
1185    }
1186
1187    fn verify_locks(&mut self, mutation: &TxnMutation<'_>) {
1188        // It was considered to change the locks from Vec to BTreeSet since we'll now be searching
1189        // through it, but given the small set that these locks usually comprise, it probably isn't
1190        // worth it.
1191        match mutation {
1192            TxnMutation {
1193                mutation:
1194                    Mutation::ObjectStore {
1195                        0: ObjectStoreMutation { item: ObjectItem { key, .. }, op },
1196                    },
1197                object_id: store_object_id,
1198                ..
1199            } => {
1200                match &key.data {
1201                    ObjectKeyData::Attribute(..) => {
1202                        // TODO(https://fxbug.dev/42073914): Check lock requirements.
1203                    }
1204                    ObjectKeyData::Child { .. }
1205                    | ObjectKeyData::EncryptedChild(_)
1206                    | ObjectKeyData::EncryptedCasefoldChild(_)
1207                    | ObjectKeyData::CasefoldChild { .. }
1208                    | ObjectKeyData::LegacyCasefoldChild(_) => {
1209                        let id = key.object_id;
1210                        if !self.txn_locks.contains(&LockKey::object(*store_object_id, id))
1211                            && !self.new_objects.contains(&(*store_object_id, id))
1212                        {
1213                            debug_assert!(
1214                                false,
1215                                "Not holding required lock for object {id} \
1216                                in store {store_object_id}"
1217                            );
1218                            error!(
1219                                "Not holding required lock for object {id} in store \
1220                                {store_object_id}"
1221                            )
1222                        }
1223                    }
1224                    ObjectKeyData::GraveyardEntry { .. } => {
1225                        // TODO(https://fxbug.dev/42073911): Check lock requirements.
1226                    }
1227                    ObjectKeyData::GraveyardAttributeEntry { .. } => {
1228                        // TODO(https://fxbug.dev/122974): Check lock requirements.
1229                    }
1230                    ObjectKeyData::Keys => {
1231                        let id = key.object_id;
1232                        if !self.txn_locks.contains(&LockKey::object(*store_object_id, id))
1233                            && !self.new_objects.contains(&(*store_object_id, id))
1234                        {
1235                            debug_assert!(
1236                                false,
1237                                "Not holding required lock for object {id} \
1238                                in store {store_object_id}"
1239                            );
1240                            error!(
1241                                "Not holding required lock for object {id} in store \
1242                                {store_object_id}"
1243                            )
1244                        }
1245                    }
1246                    ObjectKeyData::Object => match op {
1247                        // Insert implies the caller expects no object with which to race
1248                        Operation::Insert => {
1249                            self.new_objects.insert((*store_object_id, key.object_id));
1250                        }
1251                        Operation::Merge | Operation::ReplaceOrInsert => {
1252                            let id = key.object_id;
1253                            if !self.txn_locks.contains(&LockKey::object(*store_object_id, id))
1254                                && !self.new_objects.contains(&(*store_object_id, id))
1255                            {
1256                                debug_assert!(
1257                                    false,
1258                                    "Not holding required lock for object {id} \
1259                                    in store {store_object_id}"
1260                                );
1261                                error!(
1262                                    "Not holding required lock for object {id} in store \
1263                                    {store_object_id}"
1264                                )
1265                            }
1266                        }
1267                    },
1268                    ObjectKeyData::Project { project_id, property: ProjectProperty::Limit } => {
1269                        if !self.txn_locks.contains(&LockKey::ProjectId {
1270                            store_object_id: *store_object_id,
1271                            project_id: *project_id,
1272                        }) {
1273                            debug_assert!(
1274                                false,
1275                                "Not holding required lock for project limit id {project_id} \
1276                                in store {store_object_id}"
1277                            );
1278                            error!(
1279                                "Not holding required lock for project limit id {project_id} in \
1280                                store {store_object_id}"
1281                            )
1282                        }
1283                    }
1284                    ObjectKeyData::Project { property: ProjectProperty::Usage, .. } => match op {
1285                        Operation::Insert | Operation::ReplaceOrInsert => {
1286                            panic!(
1287                                "Project usage is all handled by merging deltas, no inserts or \
1288                                replacements should be used"
1289                            );
1290                        }
1291                        // Merges are all handled like atomic +/- and serialized by the tree locks.
1292                        Operation::Merge => {}
1293                    },
1294                    ObjectKeyData::ExtendedAttribute { .. } => {
1295                        let id = key.object_id;
1296                        if !self.txn_locks.contains(&LockKey::object(*store_object_id, id))
1297                            && !self.new_objects.contains(&(*store_object_id, id))
1298                        {
1299                            debug_assert!(
1300                                false,
1301                                "Not holding required lock for object {id} \
1302                                in store {store_object_id} while mutating extended attribute"
1303                            );
1304                            error!(
1305                                "Not holding required lock for object {id} in store \
1306                                {store_object_id} while mutating extended attribute"
1307                            )
1308                        }
1309                    }
1310                }
1311            }
1312            TxnMutation { mutation: Mutation::DeleteVolume, object_id, .. } => {
1313                if !self.txn_locks.contains(&LockKey::flush(*object_id)) {
1314                    debug_assert!(false, "Not holding required lock for DeleteVolume");
1315                    error!("Not holding required lock for DeleteVolume");
1316                }
1317            }
1318            _ => {}
1319        }
1320    }
1321
1322    /// Returns true if this transaction has no mutations.
1323    pub fn is_empty(&self) -> bool {
1324        self.mutations.is_empty()
1325    }
1326
1327    /// Searches for an existing object mutation within the transaction that has the given key and
1328    /// returns it if found.
1329    pub fn get_object_mutation(
1330        &self,
1331        store_object_id: u64,
1332        key: ObjectKey,
1333    ) -> Option<&ObjectStoreMutation> {
1334        if let Some(TxnMutation { mutation: Mutation::ObjectStore(mutation), .. }) =
1335            self.mutations.get(&TxnMutation {
1336                object_id: store_object_id,
1337                mutation: Mutation::insert_object(key, ObjectValue::None),
1338                associated_object: AssocObj::None,
1339            })
1340        {
1341            Some(mutation)
1342        } else {
1343            None
1344        }
1345    }
1346
1347    /// Commits a transaction.  If successful, returns the journal offset of the transaction.
1348    pub async fn commit(mut self) -> Result<u64, Error> {
1349        debug!(txn:? = &self; "Commit");
1350        let sink = self.sink.clone();
1351        sink.commit(&mut self).await
1352    }
1353
1354    /// Commits and then runs the callback whilst locks are held.  The callback accepts a single
1355    /// parameter which is the journal offset of the transaction.
1356    pub async fn commit_with_callback<R: Send>(
1357        mut self,
1358        f: impl FnOnce(u64) -> R + Send,
1359    ) -> Result<R, Error> {
1360        debug!(txn:? = &self; "Commit");
1361        let mut res = None;
1362        let sink = self.sink.clone();
1363        sink.commit_with_callback(
1364            &mut self,
1365            Box::new(|offset| {
1366                res = Some(f(offset));
1367            }),
1368        )
1369        .await?;
1370        Ok(res.expect("callback should have been called"))
1371    }
1372
1373    /// Commits the transaction, but allows the transaction to be used again.  The locks are not
1374    /// dropped (but transaction locks will get downgraded to read locks).
1375    pub async fn commit_and_continue(&mut self) -> Result<(), Error> {
1376        debug!(txn:? = self; "Commit");
1377        let sink = self.sink.clone();
1378        sink.commit(self).await?;
1379        assert!(self.mutations.is_empty());
1380        assert!(self.new_objects.is_empty());
1381        assert!(self.checksums.is_empty());
1382        self.includes_write = false;
1383        sink.downgrade_locks(&self.txn_locks);
1384        self.add_transaction_reservation().await
1385    }
1386
1387    /// Prepares to commit by upgrading transaction locks to write locks and waiting for active
1388    /// readers to finish.
1389    pub async fn commit_prepare(&self) {
1390        self.sink.lock_manager().commit_prepare(self).await;
1391    }
1392}
1393
1394impl Drop for Transaction<'_> {
1395    fn drop(&mut self) {
1396        // Call the filesystem implementation of drop_transaction which should, as a minimum, call
1397        // LockManager's drop_transaction to ensure the locks are released.
1398        debug!(txn:? = &self; "Drop");
1399        let sink = self.sink.clone();
1400        if self.parent_transaction.is_none() {
1401            sink.transaction_limiter().sub();
1402        }
1403        sink.drop_transaction(self);
1404    }
1405}
1406
1407impl std::fmt::Debug for Transaction<'_> {
1408    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1409        f.debug_struct("Transaction")
1410            .field("mutations", &self.mutations)
1411            .field("txn_locks", &self.txn_locks)
1412            .field("reservation", &self.metadata_reservation)
1413            .finish()
1414    }
1415}
1416
1417pub enum BorrowedOrOwned<'a, T> {
1418    Borrowed(&'a T),
1419    Owned(T),
1420}
1421
1422impl<T> Deref for BorrowedOrOwned<'_, T> {
1423    type Target = T;
1424
1425    fn deref(&self) -> &Self::Target {
1426        match self {
1427            BorrowedOrOwned::Borrowed(b) => b,
1428            BorrowedOrOwned::Owned(o) => &o,
1429        }
1430    }
1431}
1432
1433impl<'a, T> From<&'a T> for BorrowedOrOwned<'a, T> {
1434    fn from(value: &'a T) -> Self {
1435        BorrowedOrOwned::Borrowed(value)
1436    }
1437}
1438
1439impl<T> From<T> for BorrowedOrOwned<'_, T> {
1440    fn from(value: T) -> Self {
1441        BorrowedOrOwned::Owned(value)
1442    }
1443}
1444
1445/// LockManager holds the locks that transactions might have taken.  A TransactionManager
1446/// implementation would typically have one of these.
1447///
1448/// Three different kinds of locks are supported.  There are read locks and write locks, which are
1449/// as one would expect.  The third kind of lock is a _transaction_ lock (which is also known as an
1450/// upgradeable read lock).  When first acquired, these block other writes (including other
1451/// transaction locks) but do not block reads.  When it is time to commit a transaction, these locks
1452/// are upgraded to full write locks (without ever dropping the lock) and then dropped after
1453/// committing (unless commit_and_continue is used).  This way, reads are only blocked for the
1454/// shortest possible time.  It follows that write locks should be used sparingly.  Locks are
1455/// granted in order with one exception: when a lock is in the initial _transaction_ lock state
1456/// (LockState::Locked), all read locks are allowed even if there are other tasks waiting for the
1457/// lock.  The reason for this is because we allow read locks to be taken by tasks that have taken a
1458/// _transaction_ lock (i.e. recursion is allowed).  In other cases, such as when a writer is
1459/// waiting and there are only readers, readers will queue up behind the writer.
1460///
1461/// To summarize:
1462///
1463/// +-------------------------+-----------------+----------------+------------------+
1464/// |                         | While read_lock | While txn_lock | While write_lock |
1465/// |                         | is held         | is held        | is held          |
1466/// +-------------------------+-----------------+----------------+------------------+
1467/// | Can acquire read_lock?  | true            | true           | false            |
1468/// +-------------------------+-----------------+----------------+------------------+
1469/// | Can acquire txn_lock?   | true            | false          | false            |
1470/// +-------------------------+-----------------+----------------+------------------+
1471/// | Can acquire write_lock? | false           | false          | false            |
1472/// +-------------------------+-----------------+----------------+------------------+
1473#[derive(Default)]
1474pub struct LockManager {
1475    locks: Mutex<Locks>,
1476}
1477
1478#[derive(Default)]
1479struct Locks {
1480    keys: HashMap<LockKey, LockEntry>,
1481}
1482
1483impl Locks {
1484    fn drop_lock(&mut self, key: LockKey, state: LockState) {
1485        if let Entry::Occupied(mut occupied) = self.keys.entry(key) {
1486            let entry = occupied.get_mut();
1487            let wake = match state {
1488                LockState::ReadLock => {
1489                    entry.read_count -= 1;
1490                    entry.read_count == 0
1491                }
1492                // drop_write_locks currently depends on us treating Locked and WriteLock the same.
1493                LockState::Locked | LockState::WriteLock => {
1494                    entry.state = LockState::ReadLock;
1495                    true
1496                }
1497            };
1498            if wake {
1499                // SAFETY: The lock in `LockManager::locks` is held.
1500                unsafe {
1501                    entry.wake();
1502                }
1503                if entry.can_remove() {
1504                    occupied.remove_entry();
1505                }
1506            }
1507        } else {
1508            unreachable!();
1509        }
1510    }
1511
1512    fn drop_read_locks(&mut self, lock_keys: LockKeys) {
1513        for lock in lock_keys.iter() {
1514            self.drop_lock(*lock, LockState::ReadLock);
1515        }
1516    }
1517
1518    fn drop_write_locks(&mut self, lock_keys: LockKeys) {
1519        for lock in lock_keys.iter() {
1520            // This is a bit hacky, but this works for locks in either the Locked or WriteLock
1521            // states.
1522            self.drop_lock(*lock, LockState::WriteLock);
1523        }
1524    }
1525
1526    // Downgrades locks from WriteLock to Locked.
1527    fn downgrade_locks(&mut self, lock_keys: &LockKeys) {
1528        for lock in lock_keys.iter() {
1529            // SAFETY: The lock in `LockManager::locks` is held.
1530            unsafe {
1531                self.keys.get_mut(lock).unwrap().downgrade_lock();
1532            }
1533        }
1534    }
1535}
1536
1537#[derive(Debug)]
1538struct LockEntry {
1539    // In the states that allow readers (ReadLock, Locked), this count can be non-zero
1540    // to indicate the number of active readers.
1541    read_count: u64,
1542
1543    // The state of the lock (see below).
1544    state: LockState,
1545
1546    // A doubly-linked list of wakers that should be woken when they have been granted the lock.
1547    // New wakers are usually chained on to tail, with the exception being the case where a lock in
1548    // state Locked is to be upgraded to WriteLock, but can't because there are readers.  It might
1549    // be possible to use intrusive-collections in the future.
1550    head: *const LockWaker,
1551    tail: *const LockWaker,
1552}
1553
1554unsafe impl Send for LockEntry {}
1555
1556// Represents a node in the waker list.  It is only safe to access the members wrapped by UnsafeCell
1557// when LockManager's `locks` member is locked.
1558struct LockWaker {
1559    // The next and previous pointers in the doubly-linked list.
1560    next: UnsafeCell<*const LockWaker>,
1561    prev: UnsafeCell<*const LockWaker>,
1562
1563    // Holds the lock key for this waker.  This is required so that we can find the associated
1564    // `LockEntry`.
1565    key: LockKey,
1566
1567    // The underlying waker that should be used to wake the task.
1568    waker: UnsafeCell<WakerState>,
1569
1570    // The target state for this waker.
1571    target_state: LockState,
1572
1573    // True if this is an upgrade.
1574    is_upgrade: bool,
1575
1576    // We need to be pinned because these form part of the linked list.
1577    _pin: PhantomPinned,
1578}
1579
1580enum WakerState {
1581    // This is the initial state before the waker has been first polled.
1582    Pending,
1583
1584    // Once polled, this contains the actual waker.
1585    Registered(Waker),
1586
1587    // The waker has been woken and has been granted the lock.
1588    Woken,
1589}
1590
1591impl WakerState {
1592    fn is_woken(&self) -> bool {
1593        matches!(self, WakerState::Woken)
1594    }
1595}
1596
1597unsafe impl Send for LockWaker {}
1598unsafe impl Sync for LockWaker {}
1599
1600impl LockWaker {
1601    // Waits for the waker to be woken.
1602    async fn wait(&self, manager: &LockManager) {
1603        // We must guard against the future being dropped.
1604        let waker_guard = scopeguard::guard((), |_| {
1605            let mut locks = manager.locks.lock();
1606            // SAFETY: We've acquired the lock.
1607            unsafe {
1608                if (*self.waker.get()).is_woken() {
1609                    // We were woken, but didn't actually run, so we must drop the lock.
1610                    if self.is_upgrade {
1611                        locks.keys.get_mut(&self.key).unwrap().downgrade_lock();
1612                    } else {
1613                        locks.drop_lock(self.key, self.target_state);
1614                    }
1615                } else {
1616                    // We haven't been woken but we've been dropped so we must remove ourself from
1617                    // the waker list.
1618                    locks.keys.get_mut(&self.key).unwrap().remove_waker(self);
1619                }
1620            }
1621        });
1622
1623        poll_fn(|cx| {
1624            let _locks = manager.locks.lock();
1625            // SAFETY: We've acquired the lock.
1626            unsafe {
1627                if (*self.waker.get()).is_woken() {
1628                    Poll::Ready(())
1629                } else {
1630                    *self.waker.get() = WakerState::Registered(cx.waker().clone());
1631                    Poll::Pending
1632                }
1633            }
1634        })
1635        .await;
1636
1637        ScopeGuard::into_inner(waker_guard);
1638    }
1639}
1640
1641#[derive(Copy, Clone, Debug, PartialEq)]
1642enum LockState {
1643    // In this state, there are only readers.
1644    ReadLock,
1645
1646    // This state is used for transactions to lock other writers (including other transactions), but
1647    // it still allows readers.
1648    Locked,
1649
1650    // A writer has exclusive access; all other readers and writers are blocked.
1651    WriteLock,
1652}
1653
1654impl LockManager {
1655    /// Acquires the locks.  It is the caller's responsibility to ensure that drop_transaction is
1656    /// called when a transaction is dropped i.e. the filesystem's drop_transaction method should
1657    /// call LockManager's drop_transaction method.
1658    pub async fn txn_lock<'a>(&'a self, lock_keys: LockKeys) -> TransactionLocks<'a> {
1659        TransactionLocks(
1660            debug_assert_not_too_long!(self.lock(lock_keys, LockState::Locked)).right().unwrap(),
1661        )
1662    }
1663
1664    // `state` indicates the kind of lock required.  ReadLock means acquire a read lock.  Locked
1665    // means lock other writers, but still allow readers.  WriteLock means acquire a write lock.
1666    async fn lock<'a>(
1667        &'a self,
1668        mut lock_keys: LockKeys,
1669        target_state: LockState,
1670    ) -> Either<ReadGuard<'a>, WriteGuard<'a>> {
1671        let mut guard = match &target_state {
1672            LockState::ReadLock => Left(ReadGuard {
1673                manager: self.into(),
1674                lock_keys: LockKeys::with_capacity(lock_keys.len()),
1675            }),
1676            LockState::Locked | LockState::WriteLock => Right(WriteGuard {
1677                manager: self.into(),
1678                lock_keys: LockKeys::with_capacity(lock_keys.len()),
1679            }),
1680        };
1681        let guard_keys = match &mut guard {
1682            Left(g) => &mut g.lock_keys,
1683            Right(g) => &mut g.lock_keys,
1684        };
1685        lock_keys.sort_unstable();
1686        lock_keys.dedup();
1687        for lock in lock_keys.iter() {
1688            let lock_waker = None;
1689            pin_mut!(lock_waker);
1690            {
1691                let mut locks = self.locks.lock();
1692                match locks.keys.entry(*lock) {
1693                    Entry::Vacant(vacant) => {
1694                        vacant.insert(LockEntry {
1695                            read_count: if let LockState::ReadLock = target_state {
1696                                guard_keys.push(*lock);
1697                                1
1698                            } else {
1699                                guard_keys.push(*lock);
1700                                0
1701                            },
1702                            state: target_state,
1703                            head: std::ptr::null(),
1704                            tail: std::ptr::null(),
1705                        });
1706                    }
1707                    Entry::Occupied(mut occupied) => {
1708                        let entry = occupied.get_mut();
1709                        // SAFETY: We've acquired the lock.
1710                        if unsafe { entry.is_allowed(target_state, entry.head.is_null()) } {
1711                            if let LockState::ReadLock = target_state {
1712                                entry.read_count += 1;
1713                                guard_keys.push(*lock);
1714                            } else {
1715                                entry.state = target_state;
1716                                guard_keys.push(*lock);
1717                            }
1718                        } else {
1719                            // Initialise a waker and push it on the tail of the list.
1720                            // SAFETY: `lock_waker` isn't used prior to this point.
1721                            unsafe {
1722                                *lock_waker.as_mut().get_unchecked_mut() = Some(LockWaker {
1723                                    next: UnsafeCell::new(std::ptr::null()),
1724                                    prev: UnsafeCell::new(entry.tail),
1725                                    key: *lock,
1726                                    waker: UnsafeCell::new(WakerState::Pending),
1727                                    target_state: target_state,
1728                                    is_upgrade: false,
1729                                    _pin: PhantomPinned,
1730                                });
1731                            }
1732                            let waker = (*lock_waker).as_ref().unwrap();
1733                            if entry.tail.is_null() {
1734                                entry.head = waker;
1735                            } else {
1736                                // SAFETY: We've acquired the lock.
1737                                unsafe {
1738                                    *(*entry.tail).next.get() = waker;
1739                                }
1740                            }
1741                            entry.tail = waker;
1742                        }
1743                    }
1744                }
1745            }
1746            if let Some(waker) = &*lock_waker {
1747                waker.wait(self).await;
1748                guard_keys.push(*lock);
1749            }
1750        }
1751        guard
1752    }
1753
1754    /// This should be called by the filesystem's drop_transaction implementation.
1755    pub fn drop_transaction(&self, transaction: &mut Transaction<'_>) {
1756        let mut locks = self.locks.lock();
1757        locks.drop_write_locks(std::mem::take(&mut transaction.txn_locks));
1758    }
1759
1760    /// Prepares to commit by waiting for readers to finish.
1761    pub async fn commit_prepare(&self, transaction: &Transaction<'_>) {
1762        self.commit_prepare_keys(&transaction.txn_locks).await;
1763    }
1764
1765    async fn commit_prepare_keys(&self, lock_keys: &LockKeys) {
1766        for lock in lock_keys.iter() {
1767            let lock_waker = None;
1768            pin_mut!(lock_waker);
1769            {
1770                let mut locks = self.locks.lock();
1771                let entry = locks.keys.get_mut(lock).unwrap();
1772                // Callers may invoke `Transaction::commit_prepare` explicitly before committing
1773                // (e.g. to upgrade locks and verify invariants after disk I/O has finished).
1774                // Skipping keys already in `LockState::WriteLock` makes `commit_prepare`
1775                // idempotent when called again during `commit_transaction`.
1776                if entry.state == LockState::WriteLock {
1777                    continue;
1778                }
1779                assert_eq!(entry.state, LockState::Locked);
1780
1781                if entry.read_count == 0 {
1782                    entry.state = LockState::WriteLock;
1783                } else {
1784                    // Initialise a waker and push it on the head of the list.
1785                    // SAFETY: `lock_waker` isn't used prior to this point.
1786                    unsafe {
1787                        *lock_waker.as_mut().get_unchecked_mut() = Some(LockWaker {
1788                            next: UnsafeCell::new(entry.head),
1789                            prev: UnsafeCell::new(std::ptr::null()),
1790                            key: *lock,
1791                            waker: UnsafeCell::new(WakerState::Pending),
1792                            target_state: LockState::WriteLock,
1793                            is_upgrade: true,
1794                            _pin: PhantomPinned,
1795                        });
1796                    }
1797                    let waker = (*lock_waker).as_ref().unwrap();
1798                    if entry.head.is_null() {
1799                        entry.tail = (*lock_waker).as_ref().unwrap();
1800                    } else {
1801                        // SAFETY: We've acquired the lock.
1802                        unsafe {
1803                            *(*entry.head).prev.get() = waker;
1804                        }
1805                    }
1806                    entry.head = waker;
1807                }
1808            }
1809
1810            if let Some(waker) = &*lock_waker {
1811                waker.wait(self).await;
1812            }
1813        }
1814    }
1815
1816    /// Acquires a read lock for the given keys.  Read locks are only blocked whilst a transaction
1817    /// is being committed for the same locks.  They are only necessary where consistency is
1818    /// required between different mutations within a transaction.  For example, a write might
1819    /// change the size and extents for an object, in which case a read lock is required so that
1820    /// observed size and extents are seen together or not at all.
1821    pub async fn read_lock<'a>(&'a self, lock_keys: LockKeys) -> ReadGuard<'a> {
1822        debug_assert_not_too_long!(self.lock(lock_keys, LockState::ReadLock)).left().unwrap()
1823    }
1824
1825    /// Acquires a write lock for the given keys.  Write locks provide exclusive access to the
1826    /// requested lock keys.
1827    pub async fn write_lock<'a>(&'a self, lock_keys: LockKeys) -> WriteGuard<'a> {
1828        debug_assert_not_too_long!(self.lock(lock_keys, LockState::WriteLock)).right().unwrap()
1829    }
1830
1831    /// Downgrades locks from the WriteLock state to Locked state.  This will panic if the locks are
1832    /// not in the WriteLock state.
1833    pub fn downgrade_locks(&self, lock_keys: &LockKeys) {
1834        self.locks.lock().downgrade_locks(lock_keys);
1835    }
1836}
1837
1838// These unsafe functions require that `locks` in LockManager is locked.
1839impl LockEntry {
1840    unsafe fn wake(&mut self) {
1841        // If the lock's state is WriteLock, or there's nothing waiting, return early.
1842        if self.head.is_null() || self.state == LockState::WriteLock {
1843            return;
1844        }
1845
1846        let waker = unsafe { &*self.head };
1847
1848        if waker.is_upgrade {
1849            if self.read_count > 0 {
1850                return;
1851            }
1852        } else if !unsafe { self.is_allowed(waker.target_state, true) } {
1853            return;
1854        }
1855
1856        unsafe { self.pop_and_wake() };
1857
1858        // If the waker was a write lock, we can't wake any more up, but otherwise, we can keep
1859        // waking up readers.
1860        if waker.target_state == LockState::WriteLock {
1861            return;
1862        }
1863
1864        while !self.head.is_null() && unsafe { (*self.head).target_state } == LockState::ReadLock {
1865            unsafe { self.pop_and_wake() };
1866        }
1867    }
1868
1869    unsafe fn pop_and_wake(&mut self) {
1870        let waker = unsafe { &*self.head };
1871
1872        // Pop the waker.
1873        self.head = unsafe { *waker.next.get() };
1874        if self.head.is_null() {
1875            self.tail = std::ptr::null()
1876        } else {
1877            unsafe { *(*self.head).prev.get() = std::ptr::null() };
1878        }
1879
1880        // Adjust our state accordingly.
1881        if waker.target_state == LockState::ReadLock {
1882            self.read_count += 1;
1883        } else {
1884            self.state = waker.target_state;
1885        }
1886
1887        // Now wake the task.
1888        if let WakerState::Registered(waker) =
1889            std::mem::replace(unsafe { &mut *waker.waker.get() }, WakerState::Woken)
1890        {
1891            waker.wake();
1892        }
1893    }
1894
1895    fn can_remove(&self) -> bool {
1896        self.state == LockState::ReadLock && self.read_count == 0
1897    }
1898
1899    unsafe fn remove_waker(&mut self, waker: &LockWaker) {
1900        unsafe {
1901            let is_first = (*waker.prev.get()).is_null();
1902            if is_first {
1903                self.head = *waker.next.get();
1904            } else {
1905                *(**waker.prev.get()).next.get() = *waker.next.get();
1906            }
1907            if (*waker.next.get()).is_null() {
1908                self.tail = *waker.prev.get();
1909            } else {
1910                *(**waker.next.get()).prev.get() = *waker.prev.get();
1911            }
1912            if is_first {
1913                // We must call wake in case we erased a pending write lock and readers can now
1914                // proceed.
1915                self.wake();
1916            }
1917        }
1918    }
1919
1920    // Returns whether or not a lock with given `target_state` can proceed.  `is_head` should be
1921    // true if this is something at the head of the waker list (or the waker list is empty) and
1922    // false if there are other items on the waker list that are prior.
1923    unsafe fn is_allowed(&self, target_state: LockState, is_head: bool) -> bool {
1924        match self.state {
1925            LockState::ReadLock => {
1926                // Allow ReadLock and Locked so long as nothing else is waiting.
1927                (self.read_count == 0
1928                    || target_state == LockState::Locked
1929                    || target_state == LockState::ReadLock)
1930                    && is_head
1931            }
1932            LockState::Locked => {
1933                // Always allow reads unless there's an upgrade waiting.  We have to
1934                // always allow reads in this state because tasks that have locks in
1935                // the Locked state can later try and acquire ReadLock.
1936                target_state == LockState::ReadLock
1937                    && (is_head || unsafe { !(*self.head).is_upgrade })
1938            }
1939            LockState::WriteLock => false,
1940        }
1941    }
1942
1943    unsafe fn downgrade_lock(&mut self) {
1944        assert_eq!(std::mem::replace(&mut self.state, LockState::Locked), LockState::WriteLock);
1945        unsafe { self.wake() };
1946    }
1947}
1948
1949#[must_use]
1950pub struct ReadGuard<'a> {
1951    manager: LockManagerRef<'a>,
1952    lock_keys: LockKeys,
1953}
1954
1955impl ReadGuard<'_> {
1956    pub fn fs(&self) -> Option<&Arc<dyn Filesystem>> {
1957        if let LockManagerRef::Owned(fs) = &self.manager { Some(fs) } else { None }
1958    }
1959
1960    pub fn into_owned(mut self, fs: Arc<dyn Filesystem>) -> ReadGuard<'static> {
1961        ReadGuard {
1962            manager: LockManagerRef::Owned(fs),
1963            lock_keys: std::mem::replace(&mut self.lock_keys, LockKeys::None),
1964        }
1965    }
1966}
1967
1968impl Drop for ReadGuard<'_> {
1969    fn drop(&mut self) {
1970        let mut locks = self.manager.locks.lock();
1971        locks.drop_read_locks(std::mem::take(&mut self.lock_keys));
1972    }
1973}
1974
1975impl fmt::Debug for ReadGuard<'_> {
1976    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1977        f.debug_struct("ReadGuard")
1978            .field("manager", &(&self.manager as *const _))
1979            .field("lock_keys", &self.lock_keys)
1980            .finish()
1981    }
1982}
1983
1984#[must_use]
1985pub struct WriteGuard<'a> {
1986    manager: LockManagerRef<'a>,
1987    lock_keys: LockKeys,
1988}
1989
1990impl WriteGuard<'_> {
1991    pub fn into_owned(mut self, fs: Arc<dyn Filesystem>) -> WriteGuard<'static> {
1992        WriteGuard {
1993            manager: LockManagerRef::Owned(fs),
1994            lock_keys: std::mem::replace(&mut self.lock_keys, LockKeys::None),
1995        }
1996    }
1997}
1998
1999impl Drop for WriteGuard<'_> {
2000    fn drop(&mut self) {
2001        let mut locks = self.manager.locks.lock();
2002        locks.drop_write_locks(std::mem::take(&mut self.lock_keys));
2003    }
2004}
2005
2006impl fmt::Debug for WriteGuard<'_> {
2007    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
2008        f.debug_struct("WriteGuard")
2009            .field("manager", &(&self.manager as *const _))
2010            .field("lock_keys", &self.lock_keys)
2011            .finish()
2012    }
2013}
2014
2015enum LockManagerRef<'a> {
2016    Borrowed(&'a LockManager),
2017    Owned(Arc<dyn Filesystem>),
2018}
2019
2020impl Deref for LockManagerRef<'_> {
2021    type Target = LockManager;
2022
2023    fn deref(&self) -> &Self::Target {
2024        match self {
2025            LockManagerRef::Borrowed(m) => m,
2026            LockManagerRef::Owned(f) => f.lock_manager(),
2027        }
2028    }
2029}
2030
2031impl<'a> From<&'a LockManager> for LockManagerRef<'a> {
2032    fn from(value: &'a LockManager) -> Self {
2033        LockManagerRef::Borrowed(value)
2034    }
2035}
2036
2037#[cfg(test)]
2038mod tests {
2039    use super::{
2040        AssocObj, AttributeId, LockKey, LockKeys, LockManager, LockState, Mutation,
2041        ObjectMutationIterator, Options, ReservationOptions, Transaction,
2042        TransactionConcurrencyLimiter, TransactionSink, TxnMutation,
2043    };
2044    use crate::filesystem::FxFilesystem;
2045    use crate::object_store::allocator::{Reservation, ReservationOwner};
2046    use crate::object_store::{BytesAndNodes, ObjectKey};
2047    use anyhow::Error;
2048    use async_trait::async_trait;
2049    use fuchsia_async as fasync;
2050    use fuchsia_sync::Mutex;
2051    use futures::channel::oneshot::channel;
2052    use futures::future::FutureExt;
2053    use futures::stream::FuturesUnordered;
2054    use futures::{StreamExt, join, pin_mut};
2055    use std::collections::BTreeSet;
2056    use std::sync::Arc;
2057    use std::sync::atomic::{AtomicBool, AtomicU64, Ordering};
2058    use std::task::Poll;
2059    use std::time::Duration;
2060    use storage_device::DeviceHolder;
2061    use storage_device::fake_device::FakeDevice;
2062
2063    #[fuchsia::test]
2064    async fn test_simple() {
2065        let device = DeviceHolder::new(FakeDevice::new(4096, 1024));
2066        let fs = FxFilesystem::new_empty(device).await.expect("new_empty failed");
2067        let mut t = fs
2068            .root_store()
2069            .new_transaction(lock_keys![], Options::default())
2070            .await
2071            .expect("new_transaction failed");
2072        t.add(1, Mutation::BeginFlush);
2073        assert!(!t.is_empty());
2074    }
2075
2076    #[fuchsia::test]
2077    async fn test_locks() {
2078        let device = DeviceHolder::new(FakeDevice::new(4096, 1024));
2079        let fs = FxFilesystem::new_empty(device).await.expect("new_empty failed");
2080        let (send1, recv1) = channel();
2081        let (send2, recv2) = channel();
2082        let (send3, recv3) = channel();
2083        let done = Mutex::new(false);
2084        let mut futures = FuturesUnordered::new();
2085        futures.push(
2086            async {
2087                let _t = fs
2088                    .root_store()
2089                    .new_transaction(
2090                        lock_keys![LockKey::object_attribute(1, 2, AttributeId::TEST_ID)],
2091                        Options::default(),
2092                    )
2093                    .await
2094                    .expect("new_transaction failed");
2095                send1.send(()).unwrap(); // Tell the next future to continue.
2096                send3.send(()).unwrap(); // Tell the last future to continue.
2097                recv2.await.unwrap();
2098                // This is a halting problem so all we can do is sleep.
2099                fasync::Timer::new(Duration::from_millis(100)).await;
2100                assert!(!*done.lock());
2101            }
2102            .boxed(),
2103        );
2104        futures.push(
2105            async {
2106                recv1.await.unwrap();
2107                // This should not block since it is a different key.
2108                let _t = fs
2109                    .root_store()
2110                    .new_transaction(
2111                        lock_keys![LockKey::object_attribute(2, 2, AttributeId::TEST_ID)],
2112                        Options::default(),
2113                    )
2114                    .await
2115                    .expect("new_transaction failed");
2116                // Tell the first future to continue.
2117                send2.send(()).unwrap();
2118            }
2119            .boxed(),
2120        );
2121        futures.push(
2122            async {
2123                // This should block until the first future has completed.
2124                recv3.await.unwrap();
2125                let _t = fs
2126                    .root_store()
2127                    .new_transaction(
2128                        lock_keys![LockKey::object_attribute(1, 2, AttributeId::TEST_ID)],
2129                        Options::default(),
2130                    )
2131                    .await;
2132                *done.lock() = true;
2133            }
2134            .boxed(),
2135        );
2136        while let Some(()) = futures.next().await {}
2137    }
2138
2139    #[fuchsia::test]
2140    async fn test_read_lock_after_write_lock() {
2141        let device = DeviceHolder::new(FakeDevice::new(4096, 1024));
2142        let fs = FxFilesystem::new_empty(device).await.expect("new_empty failed");
2143        let (send1, recv1) = channel();
2144        let (send2, recv2) = channel();
2145        let done = Mutex::new(false);
2146        join!(
2147            async {
2148                let t = fs
2149                    .root_store()
2150                    .new_transaction(
2151                        lock_keys![LockKey::object_attribute(1, 2, AttributeId::TEST_ID)],
2152                        Options::default(),
2153                    )
2154                    .await
2155                    .expect("new_transaction failed");
2156                send1.send(()).unwrap(); // Tell the next future to continue.
2157                recv2.await.unwrap();
2158                t.commit().await.expect("commit failed");
2159                *done.lock() = true;
2160            },
2161            async {
2162                recv1.await.unwrap();
2163                // Reads should not be blocked until the transaction is committed.
2164                let _guard = fs
2165                    .lock_manager()
2166                    .read_lock(lock_keys![LockKey::object_attribute(1, 2, AttributeId::TEST_ID)])
2167                    .await;
2168                // Tell the first future to continue.
2169                send2.send(()).unwrap();
2170                // It shouldn't proceed until we release our read lock, but it's a halting
2171                // problem, so sleep.
2172                fasync::Timer::new(Duration::from_millis(100)).await;
2173                assert!(!*done.lock());
2174            },
2175        );
2176    }
2177
2178    #[fuchsia::test]
2179    async fn test_write_lock_after_read_lock() {
2180        let device = DeviceHolder::new(FakeDevice::new(4096, 1024));
2181        let fs = FxFilesystem::new_empty(device).await.expect("new_empty failed");
2182        let (send1, recv1) = channel();
2183        let (send2, recv2) = channel();
2184        let done = Mutex::new(false);
2185        join!(
2186            async {
2187                // Reads should not be blocked until the transaction is committed.
2188                let _guard = fs
2189                    .lock_manager()
2190                    .read_lock(lock_keys![LockKey::object_attribute(1, 2, AttributeId::TEST_ID)])
2191                    .await;
2192                // Tell the next future to continue and then wait.
2193                send1.send(()).unwrap();
2194                recv2.await.unwrap();
2195                // It shouldn't proceed until we release our read lock, but it's a halting
2196                // problem, so sleep.
2197                fasync::Timer::new(Duration::from_millis(100)).await;
2198                assert!(!*done.lock());
2199            },
2200            async {
2201                recv1.await.unwrap();
2202                let t = fs
2203                    .root_store()
2204                    .new_transaction(
2205                        lock_keys![LockKey::object_attribute(1, 2, AttributeId::TEST_ID)],
2206                        Options::default(),
2207                    )
2208                    .await
2209                    .expect("new_transaction failed");
2210                send2.send(()).unwrap(); // Tell the first future to continue;
2211                t.commit().await.expect("commit failed");
2212                *done.lock() = true;
2213            },
2214        );
2215    }
2216
2217    #[fuchsia::test]
2218    async fn test_drop_uncommitted_transaction() {
2219        let device = DeviceHolder::new(FakeDevice::new(4096, 1024));
2220        let fs = FxFilesystem::new_empty(device).await.expect("new_empty failed");
2221        let key = lock_keys![LockKey::object(1, 1)];
2222
2223        // Dropping while there's a reader.
2224        {
2225            let _write_lock = fs
2226                .root_store()
2227                .new_transaction(key.clone(), Options::default())
2228                .await
2229                .expect("new_transaction failed");
2230            let _read_lock = fs.lock_manager().read_lock(key.clone()).await;
2231        }
2232        // Dropping while there's no reader.
2233        {
2234            let _write_lock = fs
2235                .root_store()
2236                .new_transaction(key.clone(), Options::default())
2237                .await
2238                .expect("new_transaction failed");
2239        }
2240        // Make sure we can take the lock again (i.e. it was actually released).
2241        fs.root_store()
2242            .new_transaction(key.clone(), Options::default())
2243            .await
2244            .expect("new_transaction failed");
2245    }
2246
2247    #[fuchsia::test]
2248    async fn test_drop_waiting_write_lock() {
2249        let manager = LockManager::default();
2250        let keys = lock_keys![LockKey::object(1, 1)];
2251        {
2252            let _guard = manager.lock(keys.clone(), LockState::ReadLock).await;
2253            if let Poll::Ready(_) =
2254                futures::poll!(manager.lock(keys.clone(), LockState::WriteLock).boxed())
2255            {
2256                assert!(false);
2257            }
2258        }
2259        let _ = manager.lock(keys, LockState::WriteLock).await;
2260    }
2261
2262    #[fuchsia::test]
2263    async fn test_write_lock_blocks_everything() {
2264        let manager = LockManager::default();
2265        let keys = lock_keys![LockKey::object(1, 1)];
2266        {
2267            let _guard = manager.lock(keys.clone(), LockState::WriteLock).await;
2268            if let Poll::Ready(_) =
2269                futures::poll!(manager.lock(keys.clone(), LockState::WriteLock).boxed())
2270            {
2271                assert!(false);
2272            }
2273            if let Poll::Ready(_) =
2274                futures::poll!(manager.lock(keys.clone(), LockState::ReadLock).boxed())
2275            {
2276                assert!(false);
2277            }
2278        }
2279        {
2280            let _guard = manager.lock(keys.clone(), LockState::WriteLock).await;
2281        }
2282        {
2283            let _guard = manager.lock(keys, LockState::ReadLock).await;
2284        }
2285    }
2286
2287    #[fuchsia::test]
2288    async fn test_downgrade_locks() {
2289        let manager = LockManager::default();
2290        let keys = lock_keys![LockKey::object(1, 1)];
2291        let _guard = manager.txn_lock(keys.clone()).await;
2292        manager.commit_prepare_keys(&keys).await;
2293
2294        // Use FuturesUnordered so that we can check that the waker is woken.
2295        let mut read_lock: FuturesUnordered<_> =
2296            std::iter::once(manager.read_lock(keys.clone())).collect();
2297
2298        // Trying to acquire a read lock now should be blocked.
2299        assert!(futures::poll!(read_lock.next()).is_pending());
2300
2301        manager.downgrade_locks(&keys);
2302
2303        // After downgrading, it should be possible to take a read lock.
2304        assert!(futures::poll!(read_lock.next()).is_ready());
2305    }
2306
2307    #[fuchsia::test]
2308    async fn test_dropped_write_lock_wakes() {
2309        let manager = LockManager::default();
2310        let keys = lock_keys![LockKey::object(1, 1)];
2311        let _guard = manager.lock(keys.clone(), LockState::ReadLock).await;
2312        let mut read_lock = FuturesUnordered::new();
2313        read_lock.push(manager.lock(keys.clone(), LockState::ReadLock));
2314
2315        {
2316            let write_lock = manager.lock(keys, LockState::WriteLock);
2317            pin_mut!(write_lock);
2318
2319            // The write lock should be blocked because of the read lock.
2320            assert!(futures::poll!(write_lock).is_pending());
2321
2322            // Another read lock should be blocked because of the write lock.
2323            assert!(futures::poll!(read_lock.next()).is_pending());
2324        }
2325
2326        // Dropping the write lock should allow the read lock to proceed.
2327        assert!(futures::poll!(read_lock.next()).is_ready());
2328    }
2329
2330    #[fuchsia::test]
2331    async fn test_drop_upgrade() {
2332        let manager = LockManager::default();
2333        let keys = lock_keys![LockKey::object(1, 1)];
2334        let _guard = manager.lock(keys.clone(), LockState::Locked).await;
2335
2336        {
2337            let commit_prepare = manager.commit_prepare_keys(&keys);
2338            pin_mut!(commit_prepare);
2339            let _read_guard = manager.lock(keys.clone(), LockState::ReadLock).await;
2340            assert!(futures::poll!(commit_prepare).is_pending());
2341
2342            // Now we test dropping read_guard which should wake commit_prepare and
2343            // then dropping commit_prepare.
2344        }
2345
2346        // We should be able to still commit_prepare.
2347        manager.commit_prepare_keys(&keys).await;
2348    }
2349
2350    #[fuchsia::test]
2351    async fn test_woken_upgrade_blocks_reads() {
2352        let manager = LockManager::default();
2353        let keys = lock_keys![LockKey::object(1, 1)];
2354        // Start with a transaction lock.
2355        let guard = manager.lock(keys.clone(), LockState::Locked).await;
2356
2357        // Take a read lock.
2358        let read1 = manager.lock(keys.clone(), LockState::ReadLock).await;
2359
2360        // Try and upgrade the transaction lock, which should not be possible because of the read.
2361        let commit_prepare = manager.commit_prepare_keys(&keys);
2362        pin_mut!(commit_prepare);
2363        assert!(futures::poll!(commit_prepare.as_mut()).is_pending());
2364
2365        // Taking another read should also be blocked.
2366        let read2 = manager.lock(keys.clone(), LockState::ReadLock);
2367        pin_mut!(read2);
2368        assert!(futures::poll!(read2.as_mut()).is_pending());
2369
2370        // Drop the first read and the upgrade should complete.
2371        std::mem::drop(read1);
2372        assert!(futures::poll!(commit_prepare).is_ready());
2373
2374        // But the second read should still be blocked.
2375        assert!(futures::poll!(read2.as_mut()).is_pending());
2376
2377        // If we drop the write lock now, the read should be unblocked.
2378        std::mem::drop(guard);
2379        assert!(futures::poll!(read2).is_ready());
2380    }
2381
2382    static LOCK_KEY_1: LockKey = LockKey::flush(1);
2383    static LOCK_KEY_2: LockKey = LockKey::flush(2);
2384    static LOCK_KEY_3: LockKey = LockKey::flush(3);
2385
2386    // The keys, storage method, and capacity must all match.
2387    fn assert_lock_keys_equal(value: &LockKeys, expected: &LockKeys) {
2388        match (value, expected) {
2389            (LockKeys::None, LockKeys::None) => {}
2390            (LockKeys::Inline(key1), LockKeys::Inline(key2)) => {
2391                if key1 != key2 {
2392                    panic!("{key1:?} != {key2:?}");
2393                }
2394            }
2395            (LockKeys::Vec(vec1), LockKeys::Vec(vec2)) => {
2396                if vec1 != vec2 {
2397                    panic!("{vec1:?} != {vec2:?}");
2398                }
2399                if vec1.capacity() != vec2.capacity() {
2400                    panic!(
2401                        "LockKeys have different capacity: {} != {}",
2402                        vec1.capacity(),
2403                        vec2.capacity()
2404                    );
2405                }
2406            }
2407            (_, _) => panic!("{value:?} != {expected:?}"),
2408        }
2409    }
2410
2411    // Only the keys must match. Storage method and capacity don't matter.
2412    fn assert_lock_keys_equivalent(value: &LockKeys, expected: &LockKeys) {
2413        let value: Vec<_> = value.iter().collect();
2414        let expected: Vec<_> = expected.iter().collect();
2415        assert_eq!(value, expected);
2416    }
2417
2418    #[test]
2419    fn test_lock_keys_macro() {
2420        assert_lock_keys_equal(&lock_keys![], &LockKeys::None);
2421        assert_lock_keys_equal(&lock_keys![LOCK_KEY_1], &LockKeys::Inline(LOCK_KEY_1));
2422        assert_lock_keys_equal(
2423            &lock_keys![LOCK_KEY_1, LOCK_KEY_2],
2424            &LockKeys::Vec(vec![LOCK_KEY_1, LOCK_KEY_2]),
2425        );
2426    }
2427
2428    #[test]
2429    fn test_lock_keys_with_capacity() {
2430        assert_lock_keys_equal(&LockKeys::with_capacity(0), &LockKeys::None);
2431        assert_lock_keys_equal(&LockKeys::with_capacity(1), &LockKeys::None);
2432        assert_lock_keys_equal(&LockKeys::with_capacity(2), &LockKeys::Vec(Vec::with_capacity(2)));
2433    }
2434
2435    #[test]
2436    fn test_lock_keys_len() {
2437        assert_eq!(lock_keys![].len(), 0);
2438        assert_eq!(lock_keys![LOCK_KEY_1].len(), 1);
2439        assert_eq!(lock_keys![LOCK_KEY_1, LOCK_KEY_2].len(), 2);
2440    }
2441
2442    #[test]
2443    fn test_lock_keys_contains() {
2444        assert_eq!(lock_keys![].contains(&LOCK_KEY_1), false);
2445        assert_eq!(lock_keys![LOCK_KEY_1].contains(&LOCK_KEY_1), true);
2446        assert_eq!(lock_keys![LOCK_KEY_1].contains(&LOCK_KEY_2), false);
2447        assert_eq!(lock_keys![LOCK_KEY_1, LOCK_KEY_2].contains(&LOCK_KEY_1), true);
2448        assert_eq!(lock_keys![LOCK_KEY_1, LOCK_KEY_2].contains(&LOCK_KEY_2), true);
2449        assert_eq!(lock_keys![LOCK_KEY_1, LOCK_KEY_2].contains(&LOCK_KEY_3), false);
2450    }
2451
2452    #[test]
2453    fn test_lock_keys_push() {
2454        let mut keys = lock_keys![];
2455        keys.push(LOCK_KEY_1);
2456        assert_lock_keys_equal(&keys, &LockKeys::Inline(LOCK_KEY_1));
2457        keys.push(LOCK_KEY_2);
2458        assert_lock_keys_equal(&keys, &LockKeys::Vec(vec![LOCK_KEY_1, LOCK_KEY_2]));
2459        keys.push(LOCK_KEY_3);
2460        assert_lock_keys_equivalent(
2461            &keys,
2462            &LockKeys::Vec(vec![LOCK_KEY_1, LOCK_KEY_2, LOCK_KEY_3]),
2463        );
2464    }
2465
2466    #[test]
2467    fn test_lock_keys_sort_unstable() {
2468        let mut keys = lock_keys![];
2469        keys.sort_unstable();
2470        assert_lock_keys_equal(&keys, &lock_keys![]);
2471
2472        let mut keys = lock_keys![LOCK_KEY_1];
2473        keys.sort_unstable();
2474        assert_lock_keys_equal(&keys, &lock_keys![LOCK_KEY_1]);
2475
2476        let mut keys = lock_keys![LOCK_KEY_2, LOCK_KEY_1];
2477        keys.sort_unstable();
2478        assert_lock_keys_equal(&keys, &lock_keys![LOCK_KEY_1, LOCK_KEY_2]);
2479    }
2480
2481    #[test]
2482    fn test_lock_keys_dedup() {
2483        let mut keys = lock_keys![];
2484        keys.dedup();
2485        assert_lock_keys_equal(&keys, &lock_keys![]);
2486
2487        let mut keys = lock_keys![LOCK_KEY_1];
2488        keys.dedup();
2489        assert_lock_keys_equal(&keys, &lock_keys![LOCK_KEY_1]);
2490
2491        let mut keys = lock_keys![LOCK_KEY_1, LOCK_KEY_1];
2492        keys.dedup();
2493        assert_lock_keys_equivalent(&keys, &lock_keys![LOCK_KEY_1]);
2494    }
2495
2496    #[test]
2497    fn test_lock_keys_truncate() {
2498        let mut keys = lock_keys![];
2499        keys.truncate(5);
2500        assert_lock_keys_equal(&keys, &lock_keys![]);
2501        keys.truncate(0);
2502        assert_lock_keys_equal(&keys, &lock_keys![]);
2503
2504        let mut keys = lock_keys![LOCK_KEY_1];
2505        keys.truncate(5);
2506        assert_lock_keys_equal(&keys, &lock_keys![LOCK_KEY_1]);
2507        keys.truncate(0);
2508        assert_lock_keys_equal(&keys, &lock_keys![]);
2509
2510        let mut keys = lock_keys![LOCK_KEY_1, LOCK_KEY_2];
2511        keys.truncate(5);
2512        assert_lock_keys_equal(&keys, &lock_keys![LOCK_KEY_1, LOCK_KEY_2]);
2513        keys.truncate(1);
2514        // Although there's only 1 key after truncate the key is not stored inline.
2515        assert_lock_keys_equivalent(&keys, &lock_keys![LOCK_KEY_1]);
2516    }
2517
2518    #[test]
2519    fn test_lock_keys_iter() {
2520        assert_eq!(lock_keys![].iter().collect::<Vec<_>>(), Vec::<&LockKey>::new());
2521
2522        assert_eq!(lock_keys![LOCK_KEY_1].iter().collect::<Vec<_>>(), vec![&LOCK_KEY_1]);
2523
2524        assert_eq!(
2525            lock_keys![LOCK_KEY_1, LOCK_KEY_2].iter().collect::<Vec<_>>(),
2526            vec![&LOCK_KEY_1, &LOCK_KEY_2]
2527        );
2528    }
2529
2530    #[test]
2531    fn test_object_mutation_iterator() {
2532        let mut mutations = BTreeSet::new();
2533        mutations.insert(TxnMutation {
2534            object_id: 1,
2535            mutation: Mutation::BeginFlush,
2536            associated_object: AssocObj::None,
2537        });
2538        mutations.insert(TxnMutation {
2539            object_id: 1,
2540            mutation: Mutation::EndFlush(0),
2541            associated_object: AssocObj::None,
2542        });
2543        mutations.insert(TxnMutation {
2544            object_id: 2,
2545            mutation: Mutation::DeleteVolume,
2546            associated_object: AssocObj::None,
2547        });
2548
2549        let mut iter = mutations.iter().peekable();
2550
2551        {
2552            let mut obj_iter = ObjectMutationIterator::new(&mut iter).expect("expected object 1");
2553            assert_eq!(obj_iter.object_id(), 1);
2554            assert_eq!(obj_iter.next(), Some(&Mutation::BeginFlush));
2555            assert_eq!(obj_iter.next(), Some(&Mutation::EndFlush(0)));
2556            assert_eq!(obj_iter.next(), None);
2557        }
2558
2559        {
2560            let mut obj_iter = ObjectMutationIterator::new(&mut iter).expect("expected object 2");
2561            assert_eq!(obj_iter.object_id(), 2);
2562            assert_eq!(obj_iter.next(), Some(&Mutation::DeleteVolume));
2563            assert_eq!(obj_iter.next(), None);
2564        }
2565
2566        // No more objects
2567        assert!(ObjectMutationIterator::new(&mut iter).is_none());
2568    }
2569
2570    #[test]
2571    fn test_object_mutation_iterator_drop_drains_remaining() {
2572        let mut mutations = BTreeSet::new();
2573        // Object 1 with 3 mutations
2574        mutations.insert(TxnMutation {
2575            object_id: 1,
2576            mutation: Mutation::BeginFlush,
2577            associated_object: AssocObj::None,
2578        });
2579        mutations.insert(TxnMutation {
2580            object_id: 1,
2581            mutation: Mutation::EndFlush(0),
2582            associated_object: AssocObj::None,
2583        });
2584        mutations.insert(TxnMutation {
2585            object_id: 1,
2586            mutation: Mutation::DeleteVolume,
2587            associated_object: AssocObj::None,
2588        });
2589        // Object 2 with 2 mutations
2590        mutations.insert(TxnMutation {
2591            object_id: 2,
2592            mutation: Mutation::BeginFlush,
2593            associated_object: AssocObj::None,
2594        });
2595        mutations.insert(TxnMutation {
2596            object_id: 2,
2597            mutation: Mutation::EndFlush(0),
2598            associated_object: AssocObj::None,
2599        });
2600
2601        let mut iter = mutations.iter().peekable();
2602
2603        {
2604            let mut obj_iter = ObjectMutationIterator::new(&mut iter).expect("expected object 1");
2605            assert_eq!(obj_iter.object_id(), 1);
2606            assert_eq!(obj_iter.next(), Some(&Mutation::BeginFlush));
2607            // Drop without reading EndFlush or DeleteVolume
2608        }
2609
2610        {
2611            let obj_iter = ObjectMutationIterator::new(&mut iter).expect("expected object 2");
2612            assert_eq!(obj_iter.object_id(), 2);
2613            // Drop immediately without reading any mutations for object 2
2614        }
2615
2616        // Dropping obj_iter for object 2 should have drained all object 2 mutations as well.
2617        assert!(ObjectMutationIterator::new(&mut iter).is_none());
2618    }
2619
2620    #[fuchsia::test]
2621    async fn test_merge_bytes_and_nodes() {
2622        let device = DeviceHolder::new(FakeDevice::new(4096, 1024));
2623        let fs = FxFilesystem::new_empty(device).await.expect("new_empty failed");
2624        let root_store = fs.root_store();
2625        let store_id = root_store.store_object_id();
2626        let mut t = root_store
2627            .new_transaction(lock_keys![], Options::default())
2628            .await
2629            .expect("new_transaction failed");
2630
2631        let key = ObjectKey::project_usage(
2632            root_store.root_directory_object_id(),
2633            crate::object_store::ProjectId::new(1).unwrap(),
2634        );
2635
2636        // Initial delta.
2637        t.merge_bytes_and_nodes(store_id, key.clone(), BytesAndNodes { bytes: 100, nodes: 2 });
2638        let mutation = t.get_object_mutation(store_id, key.clone()).expect("mutation expected");
2639        assert_eq!(mutation.op, super::Operation::Merge);
2640        assert_eq!(
2641            mutation.item.value,
2642            crate::object_store::object_record::ObjectValue::BytesAndNodes { bytes: 100, nodes: 2 }
2643        );
2644
2645        // Second delta for the same key folds into the first rather than replacing it.
2646        t.merge_bytes_and_nodes(store_id, key.clone(), BytesAndNodes { bytes: 50, nodes: 1 });
2647        let mutation = t.get_object_mutation(store_id, key.clone()).expect("mutation expected");
2648        assert_eq!(mutation.op, super::Operation::Merge);
2649        assert_eq!(
2650            mutation.item.value,
2651            crate::object_store::object_record::ObjectValue::BytesAndNodes { bytes: 150, nodes: 3 }
2652        );
2653
2654        // Negative delta brings it back down.
2655        t.merge_bytes_and_nodes(store_id, key.clone(), BytesAndNodes { bytes: -50, nodes: -1 });
2656        let mutation = t.get_object_mutation(store_id, key.clone()).expect("mutation expected");
2657        assert_eq!(mutation.op, super::Operation::Merge);
2658        assert_eq!(
2659            mutation.item.value,
2660            crate::object_store::object_record::ObjectValue::BytesAndNodes { bytes: 100, nodes: 2 }
2661        );
2662
2663        // Folding to zero removes the mutation completely.
2664        t.merge_bytes_and_nodes(store_id, key.clone(), BytesAndNodes { bytes: -100, nodes: -2 });
2665        assert!(t.get_object_mutation(store_id, key.clone()).is_none());
2666
2667        // Calling with (0, 0) when no mutation exists is a no-op.
2668        t.merge_bytes_and_nodes(store_id, key.clone(), BytesAndNodes { bytes: 0, nodes: 0 });
2669        assert!(t.get_object_mutation(store_id, key.clone()).is_none());
2670    }
2671
2672    #[fuchsia::test]
2673    async fn test_commit_and_continue_checks_journal_space() {
2674        use crate::filesystem::FxFilesystemBuilder;
2675        use crate::hooks::Hooks;
2676        use crate::object_store::journal::JournalOptions;
2677        use crate::object_store::volume::root_volume;
2678        use crate::object_store::{NewChildStoreOptions, ObjectKey, ObjectValue};
2679        use storage_device::DeviceHolder;
2680        use storage_device::fake_device::FakeDevice;
2681
2682        let reclaim_size = 65536;
2683        let device = DeviceHolder::new(FakeDevice::new(8192, 4096));
2684        let (mut hooks, fs_hooks) = Hooks::new();
2685        let fs = FxFilesystemBuilder::new()
2686            .hooks(fs_hooks)
2687            .journal_options(JournalOptions { reclaim_size, ..Default::default() })
2688            .format(true)
2689            .open(device)
2690            .await
2691            .expect("open failed");
2692
2693        let root_volume = root_volume(fs.clone()).await.expect("root_volume failed");
2694        let store = root_volume
2695            .new_volume("test", NewChildStoreOptions::default())
2696            .await
2697            .expect("new_volume failed");
2698
2699        fs.journal().force_compact().await.expect("force_compact failed");
2700        fs.journal().pause_compactions().await;
2701
2702        let journal_clone = fs.journal().clone();
2703        hooks.set_waiting_for_journal_space(move || {
2704            journal_clone.resume_compactions();
2705        });
2706
2707        // Reserve all remaining free space so the journal has no free space to grow into beyond
2708        // the metadata reservation.
2709        let _reservation = fs.allocator().reserve_with(None, |limit| limit);
2710
2711        let mut transaction = store
2712            .new_transaction(
2713                lock_keys![LockKey::object(store.store_object_id(), 1000)],
2714                Options { reservation: ReservationOptions::Borrowed, ..Default::default() },
2715            )
2716            .await
2717            .expect("new_transaction failed");
2718
2719        // Repeatedly write mutations and call commit_and_continue so the journal must extend past
2720        // its reserved space unless commit_and_continue checks for journal space and waits for
2721        // compaction.
2722        for _ in 0..64 {
2723            for i in 0..16 {
2724                transaction.add(
2725                    store.store_object_id(),
2726                    Mutation::replace_or_insert_object(
2727                        ObjectKey::extended_attribute(1000, format!("attr_{i}").into_bytes()),
2728                        ObjectValue::inline_extended_attribute(vec![0u8; 1024]),
2729                    ),
2730                );
2731            }
2732            transaction.commit_and_continue().await.expect("commit_and_continue failed");
2733        }
2734        transaction.commit().await.expect("commit failed");
2735
2736        fs.close().await.expect("close failed");
2737    }
2738
2739    #[fuchsia::test]
2740    async fn test_commit_and_continue_resets_state() {
2741        use crate::object_store::volume::root_volume;
2742        use crate::object_store::{ExtentValue, NewChildStoreOptions, ObjectKey, ObjectValue};
2743        use storage_device::DeviceHolder;
2744        use storage_device::fake_device::FakeDevice;
2745
2746        let device = DeviceHolder::new(FakeDevice::new(8192, 4096));
2747        let fs = FxFilesystem::new_empty(device).await.expect("new_empty failed");
2748        let root_volume = root_volume(fs.clone()).await.expect("root_volume failed");
2749        let store = root_volume
2750            .new_volume("test", NewChildStoreOptions::default())
2751            .await
2752            .expect("new_volume failed");
2753
2754        let mut transaction = store
2755            .new_transaction(
2756                lock_keys![LockKey::object(store.store_object_id(), 1000)],
2757                Options::default(),
2758            )
2759            .await
2760            .expect("new_transaction failed");
2761
2762        assert!(!transaction.includes_write());
2763        transaction.add(
2764            store.store_object_id(),
2765            Mutation::merge_object(
2766                ObjectKey::extent(1000, AttributeId::DATA, 0..4096),
2767                ObjectValue::Extent(ExtentValue::deleted_extent()),
2768            ),
2769        );
2770        transaction.add_checksum(store.store_object_id(), 0..4096, vec![0], true);
2771        assert!(transaction.includes_write());
2772        assert!(!transaction.checksums().is_empty());
2773
2774        transaction.commit_and_continue().await.expect("commit_and_continue failed");
2775
2776        assert!(!transaction.includes_write());
2777        assert!(transaction.checksums().is_empty());
2778
2779        transaction.commit().await.expect("commit failed");
2780        fs.close().await.expect("close failed");
2781    }
2782
2783    struct MockSink {
2784        commits: AtomicU64,
2785        callbacks: AtomicU64,
2786        drops: AtomicU64,
2787        downgrades: AtomicU64,
2788        should_fail: AtomicBool,
2789        lock_manager: LockManager,
2790        transaction_limiter: TransactionConcurrencyLimiter,
2791    }
2792
2793    impl Default for MockSink {
2794        fn default() -> Self {
2795            Self {
2796                commits: Default::default(),
2797                callbacks: Default::default(),
2798                drops: Default::default(),
2799                downgrades: Default::default(),
2800                should_fail: Default::default(),
2801                lock_manager: LockManager::default(),
2802                transaction_limiter: TransactionConcurrencyLimiter::new(u64::MAX),
2803            }
2804        }
2805    }
2806
2807    struct MockReservationOwner;
2808
2809    impl ReservationOwner for MockReservationOwner {
2810        fn release_reservation(&self, _owner_object_id: Option<u64>, _amount: u64) {}
2811    }
2812
2813    #[async_trait]
2814    impl TransactionSink for MockSink {
2815        async fn new_transaction<'a>(
2816            self: Arc<Self>,
2817            txn_locks: LockKeys,
2818            options: Options<'a>,
2819        ) -> Result<Transaction<'a>, Error> {
2820            Transaction::new(self, options, txn_locks).await
2821        }
2822
2823        async fn check_space(&self) -> Result<(), Error> {
2824            Ok(())
2825        }
2826
2827        fn reserve(&self, owner_object_id: Option<u64>, amount: u64) -> Option<Reservation> {
2828            Some(Reservation::new(Arc::new(MockReservationOwner), owner_object_id, amount))
2829        }
2830
2831        async fn commit(&self, _transaction: &mut Transaction<'_>) -> Result<u64, Error> {
2832            if self.should_fail.load(Ordering::Relaxed) {
2833                anyhow::bail!("mock commit failed");
2834            }
2835            let count = self.commits.fetch_add(1, Ordering::Relaxed);
2836            Ok(count + 1)
2837        }
2838
2839        async fn commit_with_callback<'a>(
2840            &self,
2841            _transaction: &mut Transaction<'_>,
2842            callback: Box<dyn FnOnce(u64) + Send + 'a>,
2843        ) -> Result<u64, Error> {
2844            if self.should_fail.load(Ordering::Relaxed) {
2845                anyhow::bail!("mock commit_with_callback failed");
2846            }
2847            let count = self.commits.fetch_add(1, Ordering::Relaxed);
2848            let offset = count + 1;
2849            callback(offset);
2850            self.callbacks.fetch_add(1, Ordering::Relaxed);
2851            Ok(offset)
2852        }
2853
2854        fn downgrade_locks(&self, _lock_keys: &LockKeys) {
2855            self.downgrades.fetch_add(1, Ordering::Relaxed);
2856        }
2857
2858        fn drop_transaction(&self, transaction: &mut Transaction<'_>) {
2859            self.lock_manager.drop_transaction(transaction);
2860            self.drops.fetch_add(1, Ordering::Relaxed);
2861        }
2862
2863        fn lock_manager(&self) -> &LockManager {
2864            &self.lock_manager
2865        }
2866
2867        fn transaction_limiter(&self) -> &TransactionConcurrencyLimiter {
2868            &self.transaction_limiter
2869        }
2870    }
2871
2872    #[fuchsia::test]
2873    async fn test_mock_transaction_sink_lifecycle_and_stress() {
2874        let sink = Arc::new(MockSink::default());
2875
2876        // 1. Test standard commit
2877        {
2878            let txn = sink
2879                .clone()
2880                .new_transaction(lock_keys![LOCK_KEY_1], Options::default())
2881                .await
2882                .unwrap();
2883            let offset = txn.commit().await.expect("commit should succeed");
2884            assert_eq!(offset, 1);
2885            assert_eq!(sink.commits.load(Ordering::Relaxed), 1);
2886            assert_eq!(sink.drops.load(Ordering::Relaxed), 1);
2887        }
2888
2889        // 2. Test commit_with_callback
2890        {
2891            let txn = sink
2892                .clone()
2893                .new_transaction(lock_keys![LOCK_KEY_1], Options::default())
2894                .await
2895                .unwrap();
2896            let res = txn
2897                .commit_with_callback(|offset| {
2898                    assert_eq!(offset, 2);
2899                    "callback_executed"
2900                })
2901                .await
2902                .expect("commit_with_callback should succeed");
2903            assert_eq!(res, "callback_executed");
2904            assert_eq!(sink.commits.load(Ordering::Relaxed), 2);
2905            assert_eq!(sink.callbacks.load(Ordering::Relaxed), 1);
2906            assert_eq!(sink.drops.load(Ordering::Relaxed), 2);
2907        }
2908
2909        // 3. Test commit_and_continue across multiple iterations
2910        {
2911            let mut txn = sink
2912                .clone()
2913                .new_transaction(lock_keys![LOCK_KEY_1], Options::default())
2914                .await
2915                .unwrap();
2916            for i in 0..100 {
2917                txn.commit_and_continue().await.expect("commit_and_continue should succeed");
2918                assert_eq!(sink.commits.load(Ordering::Relaxed), 3 + i);
2919                assert_eq!(sink.downgrades.load(Ordering::Relaxed), 1 + i);
2920            }
2921            std::mem::drop(txn);
2922            assert_eq!(sink.drops.load(Ordering::Relaxed), 3);
2923        }
2924
2925        // 4. Test drop without commit
2926        {
2927            let txn = sink
2928                .clone()
2929                .new_transaction(lock_keys![LOCK_KEY_1], Options::default())
2930                .await
2931                .unwrap();
2932            std::mem::drop(txn);
2933            assert_eq!(sink.drops.load(Ordering::Relaxed), 4);
2934        }
2935
2936        // 5. Test failure mode
2937        {
2938            sink.should_fail.store(true, Ordering::Relaxed);
2939            let txn = sink
2940                .clone()
2941                .new_transaction(lock_keys![LOCK_KEY_1], Options::default())
2942                .await
2943                .unwrap();
2944            assert!(txn.commit().await.is_err());
2945            assert_eq!(sink.drops.load(Ordering::Relaxed), 5);
2946        }
2947    }
2948}