reflog_resources/
reflog_resources.rs1use std::sync::atomic::AtomicBool;
4use std::time::Instant;
5
6use girt::refs::{Backend, ImportedRecord, RefName, ReflogLimits};
7use girt::{InitKind, ObjectFormat, ObjectId, Repository};
8
9fn descriptors() -> Option<usize> {
10 #[cfg(target_os = "linux")]
11 let path = "/proc/self/fd";
12 #[cfg(target_os = "macos")]
13 let path = "/dev/fd";
14 #[cfg(any(target_os = "linux", target_os = "macos"))]
15 return std::fs::read_dir(path).ok().map(Iterator::count);
16 #[cfg(not(any(target_os = "linux", target_os = "macos")))]
17 None
18}
19
20fn main() -> Result<(), Box<dyn std::error::Error>> {
21 for format in [ObjectFormat::Sha1, ObjectFormat::Sha256] {
22 for count in [100, 10_000] {
23 probe(format, count, Backend::Files)?;
24 probe(format, count, Backend::Reftable)?;
25 }
26 }
27 Ok(())
28}
29
30fn probe(
31 format: ObjectFormat,
32 count: usize,
33 backend: Backend,
34) -> Result<(), Box<dyn std::error::Error>> {
35 let root = tempfile::tempdir()?;
36 let repo =
37 Repository::init_with_backend(format, root.path().join("repo"), InitKind::Bare, backend)?;
38
39 let tip = ObjectId::from_hex(format, &"12".repeat(format.digest_len()))?;
40 let record = format!(
41 "{} {tip} A <a@b> 1700000000 +0000\tfixture\n",
42 ObjectId::null(format)
43 );
44 let bytes = record.repeat(count);
45
46 if backend == Backend::Files {
47 std::fs::create_dir_all(repo.git_dir().join("logs"))?;
48 std::fs::write(repo.git_dir().join("logs/HEAD"), &bytes)?;
49 } else {
50 write_binary(&repo, tip, count)?;
51 }
52
53 let refs = repo.references()?;
54 let name = RefName::HEAD;
55 let cancel = AtomicBool::new(false);
56 let before = descriptors();
57 let retained = refs
58 .imported_reflog(&name, ReflogLimits::default(), &cancel)?
59 .unwrap();
60 let during = descriptors();
61 assert!(retained.is_complete());
62
63 let payload: usize = retained
64 .records()
65 .iter()
66 .map(|r| match r {
67 ImportedRecord::File { bytes, .. } => Ok(bytes.len()),
68 ImportedRecord::Reftable { value, .. } => Ok(value.name.len()
69 + value.email.len()
70 + value.message.len()
71 + 2 * format.digest_len()
72 + 18),
73 _ => Err("unsupported imported reflog record"),
74 })
75 .sum::<Result<usize, _>>()?;
76
77 drop(retained);
78 let after = descriptors();
79
80 let mut samples = Vec::new();
81 for _ in 0..30 {
82 let start = Instant::now();
83 let log = refs
84 .imported_reflog(&name, ReflogLimits::default(), &cancel)?
85 .unwrap();
86 assert!(log.is_complete());
87 assert_eq!(log.recoverable_roots().count(), count);
88 drop(log);
89 samples.push(start.elapsed().as_nanos());
90 }
91
92 samples.sort_unstable();
93 println!(
94 "backend={backend:?} format={format} records={count} payload={payload} min_ns={} median_ns={} max_ns={} descriptors={before:?}/{during:?}/{after:?}",
95 samples[0], samples[15], samples[29]
96 );
97
98 Ok(())
99}
100
101fn write_binary(
102 repo: &Repository,
103 tip: ObjectId,
104 count: usize,
105) -> Result<(), Box<dyn std::error::Error>> {
106 use girt::refs::reftable::{Limits, LogRecord, LogValue, RecordName, Table};
107
108 let table = Table {
109 format: tip.format(),
110 min_update_index: 1,
111 max_update_index: count as u64,
112 references: Vec::new(),
113 logs: (1..=count as u64)
114 .rev()
115 .map(|index| LogRecord {
116 name: RecordName::new(b"HEAD").unwrap(),
117 update_index: index,
118 value: Some(LogValue {
119 old: ObjectId::null(tip.format()),
120 new: tip,
121 name: b"A".to_vec(),
122 email: b"a@b".to_vec(),
123 seconds: 1700000000,
124 offset_minutes: 0,
125 message: b"fixture\n".to_vec(),
126 }),
127 })
128 .collect(),
129 };
130
131 std::fs::write(
132 repo.git_dir().join("reftable/original.ref"),
133 table.encode(Limits::default())?,
134 )?;
135 std::fs::write(
136 repo.git_dir().join("reftable/tables.list"),
137 b"original.ref\n",
138 )?;
139
140 Ok(())
141}