Skip to content
Open
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
8 changes: 7 additions & 1 deletion builtin/fsck.c
Original file line number Diff line number Diff line change
Expand Up @@ -937,7 +937,13 @@ static int check_pack_rev_indexes(struct repository *r, int show_progress)
int load_error = load_pack_revindex_from_disk(p);

if (load_error < 0) {
error(_("unable to load rev-index for pack '%s'"), p->pack_name);
if (access(p->pack_name, F_OK) < 0 && errno == ENOENT)
error(_("pack '%s' disappeared while fsck was "
"running; retry after concurrent "
"maintenance completes"), p->pack_name);
else
error(_("unable to load rev-index for pack '%s'"),
p->pack_name);
res = ERROR_PACK_REV_INDEX;
} else if (!load_error &&
!load_pack_revindex(r, p) &&
Expand Down
106 changes: 100 additions & 6 deletions midx.c
Original file line number Diff line number Diff line change
Expand Up @@ -896,6 +896,80 @@ static void midx_report(const char *fmt, ...)
va_end(ap);
}

/*
* Set once we have blamed a verification failure on a pack that the midx
* references having vanished -- the signature of a concurrent repack. The
* objects are not lost (they moved to the replacement pack) and a quiescent
* retry succeeds, so we say so rather than implying midx corruption.
*/
static int verify_midx_race;

/*
* Has the ".pack" backing the midx entry at pack_int_id been removed? A
* concurrent repack unlinks a redundant pack's ".idx" before its ".pack", so
* a missing ".pack" is the tell-tale of that removal race.
*/
static int midx_pack_vanished(struct multi_pack_index *m, uint32_t pack_int_id)
{
struct multi_pack_index *cur = m;
struct strbuf path = STRBUF_INIT;
int vanished;

pack_int_id = midx_for_pack(&cur, pack_int_id);
strbuf_addf(&path, "%s/pack/%s", cur->source->base.path,
cur->pack_names[pack_int_id]);
strbuf_strip_suffix(&path, ".idx");
strbuf_addstr(&path, ".pack");
vanished = access(path.buf, F_OK) < 0 && errno == ENOENT;
strbuf_release(&path);
return vanished;
}

/*
* Like midx_report(), but for a failure to load the pack for pack_int_id. If
* that pack simply vanished, add a one-time hint to retry when quiescent, so a
* concurrent repack is not mistaken for midx corruption.
*/
__attribute__((format (printf, 3, 4)))
static void midx_report_pack_load(struct multi_pack_index *m,
uint32_t pack_int_id,
const char *fmt, ...)
{
va_list ap;

verify_midx_error = 1;
va_start(ap, fmt);
vfprintf(stderr, fmt, ap);
fprintf(stderr, "\n");
va_end(ap);

if (!verify_midx_race && midx_pack_vanished(m, pack_int_id)) {
verify_midx_race = 1;
fprintf(stderr, "%s\n",
_("a pack referenced by the multi-pack-index is "
"missing; concurrent maintenance may have replaced "
"it; retry when quiescent"));
}
}

/* Pin at most 256 packs, reserving 64 fds and budgeting two per pack. */
static uint32_t midx_verify_pin_budget(void)
{
uint32_t budget = 256;
unsigned int max_fds = get_max_fd_limit();

if (max_fds > 64) {
uint32_t avail = (max_fds - 64) / 2;
if (avail < budget)
budget = avail;
} else {
budget = 1;
}
if (budget < 1)
budget = 1;
return budget;
}

struct pair_pos_vs_id
{
uint32_t pos;
Expand Down Expand Up @@ -927,10 +1001,13 @@ int verify_midx_file(struct odb_source_packed *source, unsigned flags)
struct repository *r = source->base.odb->repo;
struct pair_pos_vs_id *pairs = NULL;
uint32_t i;
uint32_t total_packs;
int pin_packs;
struct progress *progress = NULL;
struct multi_pack_index *m = load_multi_pack_index(source);
struct multi_pack_index *curr;
verify_midx_error = 0;
verify_midx_race = 0;

if (!m) {
int result = 0;
Expand All @@ -950,16 +1027,30 @@ int verify_midx_file(struct odb_source_packed *source, unsigned flags)
if (!midx_checksum_valid(m))
midx_report(_("incorrect checksum"));

total_packs = m->num_packs + m->num_packs_in_base;

/* Reopening by name races with repack pack removal; avoid it when possible. */
pin_packs = total_packs <= midx_verify_pin_budget();

if (flags & MIDX_PROGRESS)
progress = start_delayed_progress(r,
_("Looking for referenced packfiles"),
m->num_packs + m->num_packs_in_base);
for (i = 0; i < m->num_packs + m->num_packs_in_base; i++) {
total_packs);
for (i = 0; i < total_packs; i++) {
if (prepare_midx_pack(m, i))
midx_report("failed to load pack in position %d", i);
midx_report_pack_load(m, i,
"failed to load pack in position %d", i);

display_progress(progress, i + 1);
}

if (pin_packs) {
for (i = 0; i < total_packs; i++) {
struct packed_git *p = nth_midxed_pack(m, i);
if (p)
is_pack_valid(p);
}
}
stop_progress(&progress);

if (m->num_objects == 0) {
Expand Down Expand Up @@ -1021,7 +1112,8 @@ int verify_midx_file(struct odb_source_packed *source, unsigned flags)
struct pack_entry e;
off_t m_offset, p_offset;

if (i > 0 && pairs[i-1].pack_int_id != pairs[i].pack_int_id &&
if (!pin_packs &&
i > 0 && pairs[i-1].pack_int_id != pairs[i].pack_int_id &&
nth_midxed_pack(m, pairs[i-1].pack_int_id)) {
uint32_t pack_int_id = pairs[i-1].pack_int_id;
struct packed_git *p = nth_midxed_pack(m, pack_int_id);
Expand All @@ -1033,13 +1125,15 @@ int verify_midx_file(struct odb_source_packed *source, unsigned flags)
nth_midxed_object_oid(&oid, m, pairs[i].pos);

if (!fill_midx_entry(m, &oid, &e, NULL)) {
midx_report(_("failed to load pack entry for oid[%d] = %s"),
midx_report_pack_load(m, pairs[i].pack_int_id,
_("failed to load pack entry for oid[%d] = %s"),
pairs[i].pos, oid_to_hex(&oid));
continue;
}

if (open_pack_index(e.p)) {
midx_report(_("failed to load pack-index for packfile %s"),
midx_report_pack_load(m, pairs[i].pack_int_id,
_("failed to load pack-index for packfile %s"),
e.p->pack_name);
break;
}
Expand Down
2 changes: 1 addition & 1 deletion packfile.c
Original file line number Diff line number Diff line change
Expand Up @@ -471,7 +471,7 @@ static int close_one_pack(struct repository *r)
return 0;
}

static unsigned int get_max_fd_limit(void)
unsigned int get_max_fd_limit(void)
{
#ifdef RLIMIT_NOFILE
{
Expand Down
3 changes: 3 additions & 0 deletions packfile.h
Original file line number Diff line number Diff line change
Expand Up @@ -234,6 +234,9 @@ void close_pack_index(struct packed_git *);

int close_pack_fd(struct packed_git *p);

/* Return the platform's maximum number of open file descriptors. */
unsigned int get_max_fd_limit(void);

uint32_t get_pack_fanout(struct packed_git *p, uint32_t value);

struct object_database;
Expand Down
Loading