Skip to content

Commit 7b4573a

Browse files
committed
feat: add file::Store::set_packed_buffer_mmap_threshold(). (#1183)
That way it's possible to control under which circumstances, if at all, memory maps will be used.
1 parent 55d386a commit 7b4573a

File tree

5 files changed

+280
-227
lines changed

5 files changed

+280
-227
lines changed

gix-ref/src/store/file/loose/mod.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ mod init {
3636
pub fn at(git_dir: PathBuf, write_reflog: file::WriteReflog, object_hash: gix_hash::Kind) -> Self {
3737
file::Store {
3838
git_dir,
39+
packed_buffer_mmap_threshold: 32 * 1024,
3940
common_dir: None,
4041
write_reflog,
4142
namespace: None,
@@ -54,6 +55,7 @@ mod init {
5455
) -> Self {
5556
file::Store {
5657
git_dir,
58+
packed_buffer_mmap_threshold: 32 * 1024,
5759
common_dir: Some(common_dir),
5860
write_reflog,
5961
namespace: None,

gix-ref/src/store/file/mod.rs

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@ pub struct Store {
2020
/// The kind of hash to assume in a couple of situations. Note that currently we are able to read any valid hash from files
2121
/// which might want to change one day.
2222
object_hash: gix_hash::Kind,
23+
/// The amount of bytes needed for `mmap` to be used to open packed refs.
24+
packed_buffer_mmap_threshold: u64,
2325

2426
/// The way to handle reflog edits
2527
pub write_reflog: WriteReflog,
@@ -34,8 +36,19 @@ pub struct Store {
3436
mod access {
3537
use std::path::Path;
3638

39+
/// Mutation
40+
impl file::Store {
41+
/// Set the amount of `bytes` needed for the `.git/packed-refs` file to be memory mapped.
42+
/// Returns the previous value, which is always 32KB.
43+
pub fn set_packed_buffer_mmap_threshold(&mut self, mut bytes: u64) -> u64 {
44+
std::mem::swap(&mut self.packed_buffer_mmap_threshold, &mut bytes);
45+
bytes
46+
}
47+
}
48+
3749
use crate::file;
3850

51+
/// Access
3952
impl file::Store {
4053
/// Return the `.git` directory at which all references are loaded.
4154
///

gix-ref/src/store/file/packed.rs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,9 +20,11 @@ impl file::Store {
2020
}
2121

2222
/// Try to open a new packed buffer. It's not an error if it doesn't exist, but yields `Ok(None)`.
23+
///
24+
/// Note that it will automatically be memory mapped if it exceeds the default threshold of 32KB.
25+
/// Change the threshold with [file::Store::set_packed_buffer_mmap_threshold()].
2326
pub fn open_packed_buffer(&self) -> Result<Option<packed::Buffer>, packed::buffer::open::Error> {
24-
let need_more_than_this_many_bytes_to_use_mmap = 32 * 1024;
25-
match packed::Buffer::open(self.packed_refs_path(), need_more_than_this_many_bytes_to_use_mmap) {
27+
match packed::Buffer::open(self.packed_refs_path(), self.packed_buffer_mmap_threshold) {
2628
Ok(buf) => Ok(Some(buf)),
2729
Err(packed::buffer::open::Error::Io(err)) if err.kind() == std::io::ErrorKind::NotFound => Ok(None),
2830
Err(err) => Err(err),

gix-ref/tests/file/transaction/prepare_and_commit/create_or_update/mod.rs

Lines changed: 156 additions & 135 deletions
Original file line numberDiff line numberDiff line change
@@ -653,47 +653,52 @@ fn write_reference_to_which_head_points_to_does_not_update_heads_reflog_even_tho
653653

654654
#[test]
655655
fn packed_refs_are_looked_up_when_checking_existing_values() -> crate::Result {
656-
let (_keep, store) = store_writable("make_packed_ref_repository.sh")?;
657-
assert!(
658-
store.try_find_loose("main")?.is_none(),
659-
"no loose main available, it's packed"
660-
);
661-
let new_id = hex_to_id("0000000000000000000000000000000000000001");
662-
let old_id = hex_to_id("134385f6d781b7e97062102c6a483440bfda2a03");
663-
let edits = store
664-
.transaction()
665-
.prepare(
666-
Some(RefEdit {
667-
change: Change::Update {
668-
log: LogChange {
669-
mode: RefLog::AndReference,
670-
force_create_reflog: false,
671-
message: "for pack".into(),
656+
for use_mmap in [false, true] {
657+
let (_keep, mut store) = store_writable("make_packed_ref_repository.sh")?;
658+
if use_mmap {
659+
store.set_packed_buffer_mmap_threshold(0);
660+
}
661+
assert!(
662+
store.try_find_loose("main")?.is_none(),
663+
"no loose main available, it's packed"
664+
);
665+
let new_id = hex_to_id("0000000000000000000000000000000000000001");
666+
let old_id = hex_to_id("134385f6d781b7e97062102c6a483440bfda2a03");
667+
let edits = store
668+
.transaction()
669+
.prepare(
670+
Some(RefEdit {
671+
change: Change::Update {
672+
log: LogChange {
673+
mode: RefLog::AndReference,
674+
force_create_reflog: false,
675+
message: "for pack".into(),
676+
},
677+
expected: PreviousValue::MustExistAndMatch(Target::Peeled(old_id)),
678+
new: Target::Peeled(new_id),
672679
},
673-
expected: PreviousValue::MustExistAndMatch(Target::Peeled(old_id)),
674-
new: Target::Peeled(new_id),
675-
},
676-
name: "refs/heads/main".try_into()?,
677-
deref: false,
678-
}),
679-
Fail::Immediately,
680-
Fail::Immediately,
681-
)?
682-
.commit(committer().to_ref())?;
680+
name: "refs/heads/main".try_into()?,
681+
deref: false,
682+
}),
683+
Fail::Immediately,
684+
Fail::Immediately,
685+
)?
686+
.commit(committer().to_ref())?;
683687

684-
assert_eq!(edits.len(), 1, "only one edit was performed in the loose refs store");
688+
assert_eq!(edits.len(), 1, "only one edit was performed in the loose refs store");
685689

686-
let packed = store.open_packed_buffer().unwrap().expect("packed refs is available");
687-
assert_eq!(
688-
packed.find("main")?.target(),
689-
old_id,
690-
"packed refs aren't rewritten, the change goes into the loose ref instead which shadows packed refs of same name"
690+
let packed = store.open_packed_buffer().unwrap().expect("packed refs is available");
691+
assert_eq!(
692+
packed.find("main")?.target(),
693+
old_id,
694+
"packed refs aren't rewritten, the change goes into the loose ref instead which shadows packed refs of same name"
691695
);
692-
assert_eq!(
693-
store.find_loose("main")?.target.try_id(),
694-
Some(new_id.as_ref()),
695-
"the new id was written to the loose ref"
696-
);
696+
assert_eq!(
697+
store.find_loose("main")?.target.try_id(),
698+
Some(new_id.as_ref()),
699+
"the new id was written to the loose ref"
700+
);
701+
}
697702
Ok(())
698703
}
699704

@@ -704,114 +709,130 @@ fn packed_refs_creation_with_tag_loop_are_not_handled_and_cannot_exist_due_to_ob
704709

705710
#[test]
706711
fn packed_refs_creation_with_packed_refs_mode_prune_removes_original_loose_refs() -> crate::Result {
707-
let (_keep, store) = store_writable("make_ref_repository.sh")?;
708-
assert!(
709-
store.open_packed_buffer()?.is_none(),
710-
"there should be no packed refs to start out with"
711-
);
712-
let odb = gix_odb::at(store.git_dir().join("objects"))?;
713-
let edits = store
714-
.transaction()
715-
.packed_refs(PackedRefs::DeletionsAndNonSymbolicUpdatesRemoveLooseSourceReference(
716-
Box::new(odb),
717-
))
718-
.prepare(
719-
store
720-
.loose_iter()?
721-
.filter_map(|r| r.ok().filter(|r| r.kind() == gix_ref::Kind::Peeled))
722-
.map(|r| RefEdit {
723-
change: Change::Update {
724-
log: LogChange::default(),
725-
expected: PreviousValue::MustExistAndMatch(r.target.clone()),
726-
new: r.target,
727-
},
728-
name: r.name,
729-
deref: false,
730-
}),
731-
Fail::Immediately,
732-
Fail::Immediately,
733-
)?
734-
.commit(committer().to_ref())?;
712+
for use_mmap in [false, true] {
713+
let (_keep, mut store) = store_writable("make_ref_repository.sh")?;
714+
if use_mmap {
715+
let prev = store.set_packed_buffer_mmap_threshold(0);
716+
assert_eq!(prev, 32 * 1024, "the default is the value that Git uses as well");
717+
assert_eq!(
718+
store.set_packed_buffer_mmap_threshold(0),
719+
0,
720+
"it actually sets the value"
721+
);
722+
}
723+
assert!(
724+
store.open_packed_buffer()?.is_none(),
725+
"there should be no packed refs to start out with"
726+
);
727+
let odb = gix_odb::at(store.git_dir().join("objects"))?;
728+
let edits = store
729+
.transaction()
730+
.packed_refs(PackedRefs::DeletionsAndNonSymbolicUpdatesRemoveLooseSourceReference(
731+
Box::new(odb),
732+
))
733+
.prepare(
734+
store
735+
.loose_iter()?
736+
.filter_map(|r| r.ok().filter(|r| r.kind() == gix_ref::Kind::Peeled))
737+
.map(|r| RefEdit {
738+
change: Change::Update {
739+
log: LogChange::default(),
740+
expected: PreviousValue::MustExistAndMatch(r.target.clone()),
741+
new: r.target,
742+
},
743+
name: r.name,
744+
deref: false,
745+
}),
746+
Fail::Immediately,
747+
Fail::Immediately,
748+
)?
749+
.commit(committer().to_ref())?;
735750

736-
assert_eq!(
737-
edits.len(),
738-
8,
739-
"there are a certain amount of loose refs that are packed"
740-
);
751+
assert_eq!(
752+
edits.len(),
753+
8,
754+
"there are a certain amount of loose refs that are packed"
755+
);
741756

742-
assert!(
743-
store
744-
.loose_iter()?
745-
.filter_map(Result::ok)
746-
.all(|r| r.kind() == gix_ref::Kind::Symbolic),
747-
"only symbolic refs are left"
748-
);
757+
assert!(
758+
store
759+
.loose_iter()?
760+
.filter_map(Result::ok)
761+
.all(|r| r.kind() == gix_ref::Kind::Symbolic),
762+
"only symbolic refs are left"
763+
);
749764

750-
let other_store = store_with_packed_refs()?;
751-
let expected_pack_data: BString = std::fs::read(other_store.packed_refs_path())?.into();
752-
let actual_packed_data: BString = std::fs::read(store.packed_refs_path())?.into();
753-
assert_eq!(
754-
actual_packed_data, expected_pack_data,
755-
"both gitoxide and git must agree on the packed refs file perfectly"
756-
);
765+
let other_store = store_with_packed_refs()?;
766+
let expected_pack_data: BString = std::fs::read(other_store.packed_refs_path())?.into();
767+
let actual_packed_data: BString = std::fs::read(store.packed_refs_path())?.into();
768+
assert_eq!(
769+
actual_packed_data, expected_pack_data,
770+
"both gitoxide and git must agree on the packed refs file perfectly"
771+
);
772+
}
757773
Ok(())
758774
}
759775

760776
#[test]
761777
fn packed_refs_creation_with_packed_refs_mode_leave_keeps_original_loose_refs() -> crate::Result {
762-
let (_keep, store) = store_writable("make_packed_ref_repository_for_overlay.sh")?;
763-
let branch = store.find("newer-as-loose")?;
764-
let packed = store.open_packed_buffer()?.expect("packed-refs");
765-
assert_ne!(
766-
packed.find("newer-as-loose")?.target(),
767-
branch.target.try_id().expect("peeled"),
768-
"the packed ref is outdated"
769-
);
770-
let previous_reflog_entries = branch.log_iter(&store).all()?.expect("log").count();
771-
let previous_packed_refs = packed.iter()?.filter_map(Result::ok).count();
772-
773-
let edits = store.loose_iter()?.map(|r| r.expect("valid ref")).map(|r| RefEdit {
774-
change: Change::Update {
775-
log: LogChange::default(),
776-
expected: PreviousValue::MustExistAndMatch(r.target.clone()),
777-
new: r.target,
778-
},
779-
name: r.name,
780-
deref: false,
781-
});
778+
for use_mmap in [false, true] {
779+
let (_keep, mut store) = store_writable("make_packed_ref_repository_for_overlay.sh")?;
780+
if use_mmap {
781+
store.set_packed_buffer_mmap_threshold(0);
782+
}
783+
let branch = store.find("newer-as-loose")?;
784+
let packed = store.open_packed_buffer()?.expect("packed-refs");
785+
assert_ne!(
786+
packed.find("newer-as-loose")?.target(),
787+
branch.target.try_id().expect("peeled"),
788+
"the packed ref is outdated"
789+
);
790+
let previous_reflog_entries = branch.log_iter(&store).all()?.expect("log").count();
791+
let previous_packed_refs = packed.iter()?.filter_map(Result::ok).count();
782792

783-
let edits = store
784-
.transaction()
785-
.packed_refs(PackedRefs::DeletionsAndNonSymbolicUpdates(Box::new(EmptyCommit)))
786-
.prepare(edits, Fail::Immediately, Fail::Immediately)?
787-
.commit(committer().to_ref())?;
788-
assert_eq!(
789-
edits.len(),
790-
2,
791-
"it claims to have performed all desired operations, even though some don't make it into the pack as 'side-car'"
792-
);
793+
let edits = store.loose_iter()?.map(|r| r.expect("valid ref")).map(|r| RefEdit {
794+
change: Change::Update {
795+
log: LogChange::default(),
796+
expected: PreviousValue::MustExistAndMatch(r.target.clone()),
797+
new: r.target,
798+
},
799+
name: r.name,
800+
deref: false,
801+
});
793802

794-
assert_eq!(
795-
store.loose_iter()?.filter_map(Result::ok).count(),
796-
edits.len(),
797-
"the amount of loose refs didn't change and having symbolic ones isn't a problem"
798-
);
799-
assert_eq!(
800-
branch.log_iter(&store).all()?.expect("log").count(),
801-
previous_reflog_entries,
802-
"reflog isn't adjusted as there is no change"
803-
);
803+
let edits = store
804+
.transaction()
805+
.packed_refs(PackedRefs::DeletionsAndNonSymbolicUpdates(Box::new(EmptyCommit)))
806+
.prepare(edits, Fail::Immediately, Fail::Immediately)?
807+
.commit(committer().to_ref())?;
808+
assert_eq!(
809+
edits.len(),
810+
2,
811+
"it claims to have performed all desired operations, even though some don't make it into the pack as 'side-car'"
812+
);
804813

805-
let packed = store.open_packed_buffer()?.expect("packed-refs");
806-
assert_eq!(
807-
packed.iter()?.filter_map(Result::ok).count(),
808-
previous_packed_refs,
809-
"the amount of packed refs doesn't change"
810-
);
811-
assert_eq!(
812-
packed.find("newer-as-loose")?.target(),
813-
store.find("newer-as-loose")?.target.into_id(),
814-
"the packed ref is now up to date and the loose ref definitely still exists"
815-
);
814+
assert_eq!(
815+
store.loose_iter()?.filter_map(Result::ok).count(),
816+
edits.len(),
817+
"the amount of loose refs didn't change and having symbolic ones isn't a problem"
818+
);
819+
assert_eq!(
820+
branch.log_iter(&store).all()?.expect("log").count(),
821+
previous_reflog_entries,
822+
"reflog isn't adjusted as there is no change"
823+
);
824+
825+
let packed = store.open_packed_buffer()?.expect("packed-refs");
826+
assert_eq!(
827+
packed.iter()?.filter_map(Result::ok).count(),
828+
previous_packed_refs,
829+
"the amount of packed refs doesn't change"
830+
);
831+
assert_eq!(
832+
packed.find("newer-as-loose")?.target(),
833+
store.find("newer-as-loose")?.target.into_id(),
834+
"the packed ref is now up to date and the loose ref definitely still exists"
835+
);
836+
}
816837
Ok(())
817838
}

0 commit comments

Comments
 (0)