git_odb/store_impls/dynamic/
load_one.rs

1use std::{
2    path::Path,
3    sync::{atomic::Ordering, Arc},
4};
5
6use crate::store::{handle, types};
7
8impl super::Store {
9    /// If Ok(None) is returned, the pack-id was stale and referred to an unloaded pack or a pack which couldn't be
10    /// loaded as its file didn't exist on disk anymore.
11    /// If the oid is known, just load indices again to continue
12    /// (objects rarely ever removed so should be present, maybe in another pack though),
13    /// and redo the entire lookup for a valid pack id whose pack can probably be loaded next time.
14    pub(crate) fn load_pack(
15        &self,
16        id: types::PackId,
17        marker: types::SlotIndexMarker,
18    ) -> std::io::Result<Option<Arc<git_pack::data::File>>> {
19        let index = self.index.load();
20        if index.generation != marker.generation {
21            return Ok(None);
22        }
23        fn load_pack(
24            path: &Path,
25            id: types::PackId,
26            object_hash: git_hash::Kind,
27        ) -> std::io::Result<Arc<git_pack::data::File>> {
28            git_pack::data::File::at(path, object_hash)
29                .map(|mut pack| {
30                    pack.id = id.to_intrinsic_pack_id();
31                    Arc::new(pack)
32                })
33                .map_err(|err| match err {
34                    git_pack::data::header::decode::Error::Io { source, .. } => source,
35                    other => std::io::Error::new(std::io::ErrorKind::Other, other),
36                })
37        }
38
39        let slot = &self.files[id.index];
40        // pin the current state before loading in the generation. That way we won't risk seeing the wrong value later.
41        let slot_files = &**slot.files.load();
42        if slot.generation.load(Ordering::SeqCst) > marker.generation {
43            // There is a disk consolidation in progress which just overwrote a slot that could be disposed with some other
44            // pack, one we didn't intend to load.
45            // Hope that when the caller returns/retries the new index is set so they can fetch it and retry.
46            return Ok(None);
47        }
48        match id.multipack_index {
49            None => {
50                match slot_files {
51                    Some(types::IndexAndPacks::Index(bundle)) => {
52                        match bundle.data.loaded() {
53                            Some(pack) => Ok(Some(pack.clone())),
54                            None => {
55                                let _lock = slot.write.lock();
56                                let mut files = slot.files.load_full();
57                                let files_mut = Arc::make_mut(&mut files);
58                                let pack = match files_mut {
59                                    Some(types::IndexAndPacks::Index(bundle)) => bundle
60                                        .data
61                                        .load_with_recovery(|path| load_pack(path, id, self.object_hash))?,
62                                    Some(types::IndexAndPacks::MultiIndex(_)) => {
63                                        // something changed between us getting the lock, trigger a complete index refresh.
64                                        None
65                                    }
66                                    None => {
67                                        unreachable!("BUG: must set this handle to be stable to avoid slots to be cleared/changed")
68                                    }
69                                };
70                                slot.files.store(files);
71                                Ok(pack)
72                            }
73                        }
74                    }
75                    // This can also happen if they use an old index into our new and refreshed data which might have a multi-index
76                    // here.
77                    Some(types::IndexAndPacks::MultiIndex(_)) => Ok(None),
78                    None => {
79                        unreachable!("BUG: must set this handle to be stable to avoid slots to be cleared/changed")
80                    }
81                }
82            }
83            Some(pack_index) => {
84                match slot_files {
85                    Some(types::IndexAndPacks::MultiIndex(bundle)) => {
86                        match bundle.data.get(pack_index as usize) {
87                            None => Ok(None), // somewhat unexpected, data must be stale
88                            Some(on_disk_pack) => match on_disk_pack.loaded() {
89                                Some(pack) => Ok(Some(pack.clone())),
90                                None => {
91                                    let _lock = slot.write.lock();
92                                    let mut files = slot.files.load_full();
93                                    let files_mut = Arc::make_mut(&mut files);
94                                    let pack = match files_mut {
95                                        Some(types::IndexAndPacks::Index(_)) => {
96                                            // something changed between us getting the lock, trigger a complete index refresh.
97                                            None
98                                        }
99                                        Some(types::IndexAndPacks::MultiIndex(bundle)) => bundle
100                                            .data
101                                            .get_mut(pack_index as usize)
102                                            .expect("BUG: must set this handle to be stable")
103                                            .load_with_recovery(|path| load_pack(path, id, self.object_hash))?,
104                                        None => {
105                                            unreachable!("BUG: must set this handle to be stable to avoid slots to be cleared/changed")
106                                        }
107                                    };
108                                    slot.files.store(files);
109                                    Ok(pack)
110                                }
111                            },
112                        }
113                    }
114                    // This can also happen if they use an old index into our new and refreshed data which might have a multi-index
115                    // here.
116                    Some(types::IndexAndPacks::Index(_)) => Ok(None),
117                    None => {
118                        unreachable!("BUG: must set this handle to be stable to avoid slots to be cleared/changed")
119                    }
120                }
121            }
122        }
123    }
124
125    /// Similar to `.load_pack()`, but for entire indices, bypassing the index entirely and going solely by marker and id.
126    /// Returns `None` if the index wasn't available anymore or could otherwise not be loaded, which can be considered a bug
127    /// as we should always keep needed indices available.
128    pub(crate) fn index_by_id(&self, id: types::PackId, marker: types::SlotIndexMarker) -> Option<handle::IndexLookup> {
129        let slot = self.files.get(id.index)?;
130        // Pin this value before we check the generation to avoid seeing something newer later.
131        let slot_files = &**slot.files.load();
132        if slot.generation.load(Ordering::SeqCst) > marker.generation {
133            // This means somebody just overwrote our trashed slot with a new (or about to be stored) index, which means the slot isn't
134            // what we need it to be.
135            // This shouldn't as we won't overwrite slots while handles need stable indices.
136            return None;
137        }
138        let lookup = match (slot_files).as_ref()? {
139            types::IndexAndPacks::Index(bundle) => handle::SingleOrMultiIndex::Single {
140                index: bundle.index.loaded()?.clone(),
141                data: bundle.data.loaded().cloned(),
142            },
143            types::IndexAndPacks::MultiIndex(multi) => handle::SingleOrMultiIndex::Multi {
144                index: multi.multi_index.loaded()?.clone(),
145                data: multi.data.iter().map(|f| f.loaded().cloned()).collect(),
146            },
147        };
148        handle::IndexLookup {
149            id: id.index,
150            file: lookup,
151        }
152        .into()
153    }
154}