Skip to content

Commit

Permalink
fix: improve handling of overflows when there are more pack than we c…
Browse files Browse the repository at this point in the history
…an 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 and got into an invalid state.

The system now generously trashes existing slots to be able to load more rencent ones,
which seems to help in this situation.
It's probably still leading to strange situations where not all objects can be read.
  • Loading branch information
Byron committed Jan 13, 2025
1 parent 3de8819 commit 56a1e8f
Show file tree
Hide file tree
Showing 2 changed files with 10 additions and 4 deletions.
2 changes: 1 addition & 1 deletion gix-odb/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -119,7 +119,7 @@ pub struct Store {

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

Expand Down
12 changes: 9 additions & 3 deletions gix-odb/src/store_impls/dynamic/load_index.rs
Original file line number Diff line number Diff line change
Expand Up @@ -266,7 +266,7 @@ impl super::Store {
Option::as_ref(&files_guard).expect("slot is set or we wouldn't know it points to this file");
if index_info.is_multi_index() && files.mtime() != mtime {
// we have a changed multi-pack index. We can't just change the existing slot as it may alter slot indices
// that are currently available. Instead we have to move what's there into a new slot, along with the changes,
// that are currently available. Instead, we have to move what's there into a new slot, along with the changes,
// and later free the slot or dispose of the index in the slot (like we do for removed/missing files).
index_paths_to_add.push_back((index_info, mtime, Some(slot_idx)));
// If the current slot is loaded, the soon-to-be copied multi-index path will be loaded as well.
Expand Down Expand Up @@ -304,6 +304,12 @@ impl super::Store {
needed: index_paths_to_add.len() + 1, /*the one currently popped off*/
});
}
// Don't allow duplicate indicates, we need a 1:1 mapping.
if new_slot_map_indices.contains(&next_possibly_free_index) {
next_possibly_free_index = (next_possibly_free_index + 1) % self.files.len();
num_indices_checked += 1;
continue 'increment_slot_index;
}
let slot_index = next_possibly_free_index;
let slot = &self.files[slot_index];
next_possibly_free_index = (next_possibly_free_index + 1) % self.files.len();
Expand Down Expand Up @@ -502,7 +508,7 @@ impl super::Store {
}
// Unlike libgit2, do not sort by modification date, but by size and put the biggest indices first. That way
// the chance to hit an object should be higher. We leave it to the handle to sort by LRU.
// Git itself doesn't change the order which may safe time, but we want it to be stable which also helps some tests.
// Git itself doesn't change the order which may save time, but we want it to be stable which also helps some tests.
// NOTE: this will work well for well-packed repos or those using geometric repacking, but force us to open a lot
// of files when dealing with new objects, as there is no notion of recency here as would be with unmaintained
// repositories. Different algorithms should be provided, like newest packs first, and possibly a mix of both
Expand All @@ -512,7 +518,7 @@ impl super::Store {
Ok(indices_by_modification_time)
}

/// returns Ok<dest slot was empty> if the copy could happen because dest-slot was actually free or disposable , and Some(true) if it was empty
/// returns `Ok(dest_slot_was_empty)` if the copy could happen because dest-slot was actually free or disposable.
#[allow(clippy::too_many_arguments)]
fn try_set_index_slot(
lock: &parking_lot::MutexGuard<'_, ()>,
Expand Down

0 comments on commit 56a1e8f

Please sign in to comment.