Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 26 additions & 1 deletion rust/crates/server/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -263,7 +263,32 @@ fn main() {

// Open the object database once as a thread-safe handle; each request thread
// takes a cheap local handle from it (see `handle`).
let repo = match gix::open(&git_dir) {
// Packed uploads accumulate while automatic repacking is disabled. gix
// fixes its slot capacity at open time; its client default (at least 32)
// cannot accommodate a running server. Reserve its supported maximum.
// This is headroom, not pack maintenance: 32,767 is still a hard limit.
// TODO: consolidate packs during coordinated downtime, preserving ALL CAS
// objects (including unreferenced ones), before this capacity is exhausted.
// Do not enable live gc/repack: see the shared-reader constraints above.
// Given skips gix's initial scan, so diagnose an already-full store here.
let packs = std::fs::read_dir(std::path::Path::new(&git_dir).join("objects/pack"))
.map(|entries| {
entries
.filter_map(Result::ok)
.filter(|entry| entry.path().extension().is_some_and(|ext| ext == "idx"))
.count()
})
.unwrap_or(0);
if packs >= (1 << 15) - 1 {
eprintln!("fatal: object store has {packs} packs; consolidate packs offline, preserving unreferenced CAS objects, before restarting");
std::process::exit(1);
}
if packs >= 30_000 {
eprintln!("warning: object store has {packs} packs, approaching the 32767-pack limit; schedule offline consolidation");
}
let options = gix::open::Options::default()
.object_store_slots(gix::odb::store::init::Slots::Given((1 << 15) - 1));
let repo = match gix::open_opts(&git_dir, options) {
Ok(repo) => repo.into_sync(),
Err(err) => {
eprintln!("fatal: cannot open git repo at {git_dir}: {err}");
Expand Down
7 changes: 5 additions & 2 deletions rust/crates/server/src/storage.rs
Original file line number Diff line number Diff line change
Expand Up @@ -123,14 +123,17 @@ fn validate_commit_with_git(config: &Config, content: &[u8]) -> Result<(), HttpE
format!("creating commit validation repository: {error}"),
)
})?;
// Stage the candidate before attaching the live store. Only Git needs
// the alternate for validation; this one-object gix handle must not try
// to load all of the server's packs into its startup-sized slot table.
let id = gix::objs::Write::write_buf(&repo.objects, gix::object::Kind::Commit, content)
.map_err(|error| HttpError::new(500, format!("staging commit: {error}")))?;
let objects = std::fs::canonicalize(std::path::Path::new(&config.git_dir).join("objects"))?;
std::fs::create_dir_all(staged.0.join("objects/info"))?;
std::fs::write(
staged.0.join("objects/info/alternates"),
format!("{}\n", objects.display()),
)?;
let id = gix::objs::Write::write_buf(&repo.objects, gix::object::Kind::Commit, content)
.map_err(|error| HttpError::new(500, format!("staging commit: {error}")))?;
let git = || {
let mut command = std::process::Command::new("git");
command.env_clear();
Expand Down
14 changes: 14 additions & 0 deletions tests/git-import/fixture.py
Original file line number Diff line number Diff line change
Expand Up @@ -460,6 +460,20 @@ def visible(commit):
invalid = subprocess.run([cli, "import-git", *args], env=cli_env, capture_output=True)
assert invalid.returncode != 0
assert token.encode() not in invalid.stdout + invalid.stderr
# Small uploads now stay packed too. The live reader must discover
# more packs than gix's default capacity of 32 without a restart.
for index in range(40):
packed = advance()
run("git", "--git-dir", str(origin), "push", "-q", base,
packed[0] + f":refs/heads/pack-growth-{index}")
for oid in packed:
object_request(oid)
assert len(list((odb / "objects/pack").glob("*.pack"))) > 32
# Commit staging must also work once its live-store alternate
# contains more than 32 packs.
staged_tip = advance(packed[0])
for kind, oid in [("blob", staged_tip[2]), ("tree", staged_tip[1]), ("commit", staged_tip[0])]:
post_from_origin(kind, oid)
stop(server); server = None
server = start()
visible(posted_child[0])
Expand Down
Loading