Skip to content

Commit 34197e7

Browse files
committed
fix: improve handling of overflows when there are more pack than we can hold.
Internally, there is a statically allocated vec which holds opened packs and indices. When reconciling the disk-state with what's currently loaded, it was possible to get into a situation where there were more files than we could fit into the slotmap. The system now fails loudly with a specific Error so callers can deal with it.
1 parent 167f49f commit 34197e7

File tree

3 files changed

+11
-9
lines changed

3 files changed

+11
-9
lines changed

gix-odb/src/lib.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -119,7 +119,7 @@ pub struct Store {
119119

120120
/// The below state acts like a slot-map with each slot is mutable when the write lock is held, but readable independently of it.
121121
/// This allows multiple file to be loaded concurrently if there is multiple handles requesting to load packs or additional indices.
122-
/// The map is static and cannot typically change.
122+
/// The map is static and cannot change.
123123
/// It's read often and changed rarely.
124124
pub(crate) files: Vec<types::MutableIndexAndPack>,
125125

gix-odb/src/store_impls/dynamic/load_index.rs

+4-3
Original file line numberDiff line numberDiff line change
@@ -266,7 +266,7 @@ impl super::Store {
266266
Option::as_ref(&files_guard).expect("slot is set or we wouldn't know it points to this file");
267267
if index_info.is_multi_index() && files.mtime() != mtime {
268268
// we have a changed multi-pack index. We can't just change the existing slot as it may alter slot indices
269-
// that are currently available. Instead we have to move what's there into a new slot, along with the changes,
269+
// that are currently available. Instead, we have to move what's there into a new slot, along with the changes,
270270
// and later free the slot or dispose of the index in the slot (like we do for removed/missing files).
271271
index_paths_to_add.push_back((index_info, mtime, Some(slot_idx)));
272272
// If the current slot is loaded, the soon-to-be copied multi-index path will be loaded as well.
@@ -295,10 +295,11 @@ impl super::Store {
295295
.map_or(0, |idx| (idx + 1) % self.files.len());
296296
let mut num_indices_checked = 0;
297297
let mut needs_generation_change = false;
298+
dbg!(idx_by_index_path.len());
298299
let mut slot_indices_to_remove: Vec<_> = idx_by_index_path.into_values().collect();
299300
while let Some((mut index_info, mtime, move_from_slot_idx)) = index_paths_to_add.pop_front() {
300301
'increment_slot_index: loop {
301-
if num_indices_checked == self.files.len() {
302+
if dbg!(num_indices_checked) == dbg!(self.files.len()) {
302303
return Err(Error::InsufficientSlots {
303304
current: self.files.len(),
304305
needed: index_paths_to_add.len() + 1, /*the one currently popped off*/
@@ -502,7 +503,7 @@ impl super::Store {
502503
}
503504
// Unlike libgit2, do not sort by modification date, but by size and put the biggest indices first. That way
504505
// the chance to hit an object should be higher. We leave it to the handle to sort by LRU.
505-
// Git itself doesn't change the order which may safe time, but we want it to be stable which also helps some tests.
506+
// Git itself doesn't change the order which may save time, but we want it to be stable which also helps some tests.
506507
// NOTE: this will work well for well-packed repos or those using geometric repacking, but force us to open a lot
507508
// of files when dealing with new objects, as there is no notion of recency here as would be with unmaintained
508509
// repositories. Different algorithms should be provided, like newest packs first, and possibly a mix of both

gix/tests/gix/remote/fetch.rs

+6-5
Original file line numberDiff line numberDiff line change
@@ -100,12 +100,13 @@ mod blocking_and_async_io {
100100
)?;
101101
Ok(())
102102
}
103-
let remote_dir = tempfile::tempdir()?;
104-
let remote_repo = gix::init_bare(remote_dir.path())?;
105-
create_empty_commit(&remote_repo)?;
106-
107103
for max_packs in 1..=2 {
104+
let remote_dir = tempfile::tempdir()?;
105+
let remote_repo = gix::init_bare(remote_dir.path())?;
106+
create_empty_commit(&remote_repo)?;
107+
108108
let local_dir = tempfile::tempdir()?;
109+
dbg!(max_packs);
109110
let (local_repo, _) = gix::clone::PrepareFetch::new(
110111
remote_repo.path(),
111112
local_dir.path(),
@@ -122,7 +123,7 @@ mod blocking_and_async_io {
122123
)
123124
.expect("remote is configured after clone")?;
124125
for round in 1.. {
125-
eprintln!("Fetch number {round}…");
126+
dbg!(round);
126127
create_empty_commit(&remote_repo)?;
127128
match remote
128129
.connect(Fetch)?

0 commit comments

Comments
 (0)