Skip to content

Commit fb9e5da

Browse files
adityamaruclaude
andauthored
fix(blacksmith-cache): prevent git gc background daemon from blocking unmount (#22)
git gc --auto defaults to gc.autoDetach=true, which forks a background daemon for the actual garbage collection work. The parent returns immediately with exit code 0, but the daemonized child (and its sub-processes) keep running with cwd and mmap'd pack files on the mirror mount. This causes subsequent umount attempts to fail with EBUSY (exit code 32). Root cause identified in descriptinc/descript: 4 git processes (PIDs 10316-10319) with cwd inside the mirror, preventing unmount despite 3 retry attempts with exponential backoff. Fix: Pass -c gc.autoDetach=false to force synchronous GC execution so it fully completes before proceeding to mount cleanup. Also increase GC_TIMEOUT_SECS from 60s to 120s: measured GC on this repo takes ~74 seconds to repack 51 packs + 1019 loose objects. Co-authored-by: Claude Haiku 4.5 <noreply@anthropic.com>
1 parent 2f7f0dc commit fb9e5da

2 files changed

Lines changed: 30 additions & 4 deletions

File tree

dist/index.js

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@ const GRPC_PORT = process.env.BLACKSMITH_STICKY_DISK_GRPC_PORT || '5557';
6060
const MOUNT_BASE = '/blacksmith-git-mirror';
6161
const MIRROR_VERSION = 'v1';
6262
const REFRESH_TIMEOUT_SECS = 120; // 2 minutes
63-
const GC_TIMEOUT_SECS = 60; // 60 seconds
63+
const GC_TIMEOUT_SECS = 120; // 2 minutes
6464
const FLUSH_TIMEOUT_SECS = 10; // 10 seconds for durability flush
6565
const UMOUNT_TIMEOUT_SECS = 10; // 10 seconds for unmount
6666
const UMOUNT_MAX_RETRIES = 3; // Number of unmount retry attempts
@@ -417,7 +417,20 @@ function runMirrorGC(mirrorPath_1) {
417417
try {
418418
// --auto: only run if thresholds exceeded (default: 6700 loose objects or 50 packs)
419419
// This is much faster than a full gc when not needed
420-
const result = yield exec.getExecOutput('timeout', [String(timeoutSecs), 'git', '-C', mirrorPath, 'gc', '--auto'], { ignoreReturnCode: true });
420+
// gc.autoDetach=false: prevent git from forking a background daemon for GC.
421+
// Without this, the parent `git gc --auto` returns immediately while the
422+
// daemonized child keeps running with cwd and mmap'd pack files on the
423+
// mirror mount, causing the subsequent `umount` to fail with EBUSY.
424+
const result = yield exec.getExecOutput('timeout', [
425+
String(timeoutSecs),
426+
'git',
427+
'-c',
428+
'gc.autoDetach=false',
429+
'-C',
430+
mirrorPath,
431+
'gc',
432+
'--auto'
433+
], { ignoreReturnCode: true });
421434
if (result.exitCode === TIMEOUT_EXIT_CODE) {
422435
core.warning(`[git-mirror] GC timed out after ${timeoutSecs}s`);
423436
return {

src/blacksmith-cache.ts

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ const MOUNT_BASE = '/blacksmith-git-mirror'
1212
const MIRROR_VERSION = 'v1'
1313

1414
const REFRESH_TIMEOUT_SECS = 120 // 2 minutes
15-
const GC_TIMEOUT_SECS = 60 // 60 seconds
15+
const GC_TIMEOUT_SECS = 120 // 2 minutes
1616
const FLUSH_TIMEOUT_SECS = 10 // 10 seconds for durability flush
1717
const UMOUNT_TIMEOUT_SECS = 10 // 10 seconds for unmount
1818
const UMOUNT_MAX_RETRIES = 3 // Number of unmount retry attempts
@@ -479,9 +479,22 @@ async function runMirrorGC(
479479
try {
480480
// --auto: only run if thresholds exceeded (default: 6700 loose objects or 50 packs)
481481
// This is much faster than a full gc when not needed
482+
// gc.autoDetach=false: prevent git from forking a background daemon for GC.
483+
// Without this, the parent `git gc --auto` returns immediately while the
484+
// daemonized child keeps running with cwd and mmap'd pack files on the
485+
// mirror mount, causing the subsequent `umount` to fail with EBUSY.
482486
const result = await exec.getExecOutput(
483487
'timeout',
484-
[String(timeoutSecs), 'git', '-C', mirrorPath, 'gc', '--auto'],
488+
[
489+
String(timeoutSecs),
490+
'git',
491+
'-c',
492+
'gc.autoDetach=false',
493+
'-C',
494+
mirrorPath,
495+
'gc',
496+
'--auto'
497+
],
485498
{ignoreReturnCode: true}
486499
)
487500
if (result.exitCode === TIMEOUT_EXIT_CODE) {

0 commit comments

Comments
 (0)