Commit d5f7aad8 by Vicent Marti

Merge pull request #3468 from libgit2/vmg/odb-lookups

Fix pathological performance in ODB lookups
parents ac7e50dd a0a1b19a
...@@ -374,10 +374,14 @@ static int backend_sort_cmp(const void *a, const void *b) ...@@ -374,10 +374,14 @@ static int backend_sort_cmp(const void *a, const void *b)
const backend_internal *backend_a = (const backend_internal *)(a); const backend_internal *backend_a = (const backend_internal *)(a);
const backend_internal *backend_b = (const backend_internal *)(b); const backend_internal *backend_b = (const backend_internal *)(b);
if (backend_a->is_alternate == backend_b->is_alternate) if (backend_b->priority == backend_a->priority) {
return (backend_b->priority - backend_a->priority); if (backend_a->is_alternate)
return -1;
return backend_a->is_alternate ? 1 : -1; if (backend_b->is_alternate)
return 1;
return 0;
}
return (backend_b->priority - backend_a->priority);
} }
int git_odb_new(git_odb **out) int git_odb_new(git_odb **out)
...@@ -620,23 +624,18 @@ void git_odb_free(git_odb *db) ...@@ -620,23 +624,18 @@ void git_odb_free(git_odb *db)
GIT_REFCOUNT_DEC(db, odb_free); GIT_REFCOUNT_DEC(db, odb_free);
} }
int git_odb_exists(git_odb *db, const git_oid *id) static int odb_exists_1(git_odb *db, const git_oid *id, bool only_refreshed)
{ {
git_odb_object *object;
size_t i; size_t i;
bool found = false; bool found = false;
assert(db && id);
if ((object = git_cache_get_raw(odb_cache(db), id)) != NULL) {
git_odb_object_free(object);
return (int)true;
}
for (i = 0; i < db->backends.length && !found; ++i) { for (i = 0; i < db->backends.length && !found; ++i) {
backend_internal *internal = git_vector_get(&db->backends, i); backend_internal *internal = git_vector_get(&db->backends, i);
git_odb_backend *b = internal->backend; git_odb_backend *b = internal->backend;
if (only_refreshed && !b->refresh)
continue;
if (b->exists != NULL) if (b->exists != NULL)
found = (bool)b->exists(b, id); found = (bool)b->exists(b, id);
} }
...@@ -644,43 +643,45 @@ int git_odb_exists(git_odb *db, const git_oid *id) ...@@ -644,43 +643,45 @@ int git_odb_exists(git_odb *db, const git_oid *id)
return (int)found; return (int)found;
} }
int git_odb_exists_prefix( int git_odb_exists(git_odb *db, const git_oid *id)
git_oid *out, git_odb *db, const git_oid *short_id, size_t len)
{ {
int error = GIT_ENOTFOUND, num_found = 0; git_odb_object *object;
size_t i;
git_oid key = {{0}}, last_found = {{0}}, found;
assert(db && short_id);
if (len < GIT_OID_MINPREFIXLEN) assert(db && id);
return git_odb__error_ambiguous("prefix length too short");
if (len > GIT_OID_HEXSZ)
len = GIT_OID_HEXSZ;
if (len == GIT_OID_HEXSZ) { if ((object = git_cache_get_raw(odb_cache(db), id)) != NULL) {
if (git_odb_exists(db, short_id)) { git_odb_object_free(object);
if (out) return (int)true;
git_oid_cpy(out, short_id);
return 0;
} else {
return git_odb__error_notfound("no match for id prefix", short_id);
}
} }
/* just copy valid part of short_id */ if (odb_exists_1(db, id, false))
memcpy(&key.id, short_id->id, (len + 1) / 2); return 1;
if (len & 1)
key.id[len / 2] &= 0xF0; if (!git_odb_refresh(db))
return odb_exists_1(db, id, true);
/* Failed to refresh, hence not found */
return 0;
}
static int odb_exists_prefix_1(git_oid *out, git_odb *db,
const git_oid *key, size_t len, bool only_refreshed)
{
size_t i;
int error = GIT_ENOTFOUND, num_found = 0;
git_oid last_found = {{0}}, found;
for (i = 0; i < db->backends.length; ++i) { for (i = 0; i < db->backends.length; ++i) {
backend_internal *internal = git_vector_get(&db->backends, i); backend_internal *internal = git_vector_get(&db->backends, i);
git_odb_backend *b = internal->backend; git_odb_backend *b = internal->backend;
if (only_refreshed && !b->refresh)
continue;
if (!b->exists_prefix) if (!b->exists_prefix)
continue; continue;
error = b->exists_prefix(&found, b, &key, len); error = b->exists_prefix(&found, b, key, len);
if (error == GIT_ENOTFOUND || error == GIT_PASSTHROUGH) if (error == GIT_ENOTFOUND || error == GIT_PASSTHROUGH)
continue; continue;
if (error) if (error)
...@@ -697,13 +698,53 @@ int git_odb_exists_prefix( ...@@ -697,13 +698,53 @@ int git_odb_exists_prefix(
} }
if (!num_found) if (!num_found)
return git_odb__error_notfound("no match for id prefix", &key); return GIT_ENOTFOUND;
if (out) if (out)
git_oid_cpy(out, &last_found); git_oid_cpy(out, &last_found);
return 0; return 0;
} }
int git_odb_exists_prefix(
git_oid *out, git_odb *db, const git_oid *short_id, size_t len)
{
int error;
git_oid key = {{0}};
assert(db && short_id);
if (len < GIT_OID_MINPREFIXLEN)
return git_odb__error_ambiguous("prefix length too short");
if (len > GIT_OID_HEXSZ)
len = GIT_OID_HEXSZ;
if (len == GIT_OID_HEXSZ) {
if (git_odb_exists(db, short_id)) {
if (out)
git_oid_cpy(out, short_id);
return 0;
} else {
return git_odb__error_notfound("no match for id prefix", short_id);
}
}
/* just copy valid part of short_id */
memcpy(&key.id, short_id->id, (len + 1) / 2);
if (len & 1)
key.id[len / 2] &= 0xF0;
error = odb_exists_prefix_1(out, db, &key, len, false);
if (error == GIT_ENOTFOUND && !git_odb_refresh(db))
error = odb_exists_prefix_1(out, db, &key, len, true);
if (error == GIT_ENOTFOUND)
return git_odb__error_notfound("no match for id prefix", &key);
return error;
}
int git_odb_read_header(size_t *len_p, git_otype *type_p, git_odb *db, const git_oid *id) int git_odb_read_header(size_t *len_p, git_otype *type_p, git_odb *db, const git_oid *id)
{ {
int error; int error;
...@@ -783,36 +824,38 @@ static int hardcoded_objects(git_rawobj *raw, const git_oid *id) ...@@ -783,36 +824,38 @@ static int hardcoded_objects(git_rawobj *raw, const git_oid *id)
} }
} }
int git_odb_read(git_odb_object **out, git_odb *db, const git_oid *id) static int odb_read_1(git_odb_object **out, git_odb *db, const git_oid *id,
bool only_refreshed)
{ {
size_t i, reads = 0; size_t i;
int error;
git_rawobj raw; git_rawobj raw;
git_odb_object *object; git_odb_object *object;
bool found = false;
assert(out && db && id); if (!hardcoded_objects(&raw, id))
found = true;
*out = git_cache_get_raw(odb_cache(db), id);
if (*out != NULL)
return 0;
error = hardcoded_objects(&raw, id);
for (i = 0; i < db->backends.length && error < 0; ++i) { for (i = 0; i < db->backends.length && !found; ++i) {
backend_internal *internal = git_vector_get(&db->backends, i); backend_internal *internal = git_vector_get(&db->backends, i);
git_odb_backend *b = internal->backend; git_odb_backend *b = internal->backend;
if (only_refreshed && !b->refresh)
continue;
if (b->read != NULL) { if (b->read != NULL) {
++reads; int error = b->read(&raw.data, &raw.len, &raw.type, b, id);
error = b->read(&raw.data, &raw.len, &raw.type, b, id); if (error == GIT_PASSTHROUGH || error == GIT_ENOTFOUND)
continue;
if (error < 0)
return error;
found = true;
} }
} }
if (error && error != GIT_PASSTHROUGH) { if (!found)
if (!reads) return GIT_ENOTFOUND;
return git_odb__error_notfound("no match for id", id);
return error;
}
giterr_clear(); giterr_clear();
if ((object = odb_object__alloc(id, &raw)) == NULL) if ((object = odb_object__alloc(id, &raw)) == NULL)
...@@ -822,42 +865,48 @@ int git_odb_read(git_odb_object **out, git_odb *db, const git_oid *id) ...@@ -822,42 +865,48 @@ int git_odb_read(git_odb_object **out, git_odb *db, const git_oid *id)
return 0; return 0;
} }
int git_odb_read_prefix( int git_odb_read(git_odb_object **out, git_odb *db, const git_oid *id)
git_odb_object **out, git_odb *db, const git_oid *short_id, size_t len) {
int error;
assert(out && db && id);
*out = git_cache_get_raw(odb_cache(db), id);
if (*out != NULL)
return 0;
error = odb_read_1(out, db, id, false);
if (error == GIT_ENOTFOUND && !git_odb_refresh(db))
error = odb_read_1(out, db, id, true);
if (error == GIT_ENOTFOUND)
return git_odb__error_notfound("no match for id", id);
return error;
}
static int read_prefix_1(git_odb_object **out, git_odb *db,
const git_oid *key, size_t len, bool only_refreshed)
{ {
size_t i; size_t i;
int error = GIT_ENOTFOUND; int error = GIT_ENOTFOUND;
git_oid key = {{0}}, found_full_oid = {{0}}; git_oid found_full_oid = {{0}};
git_rawobj raw; git_rawobj raw;
void *data = NULL; void *data = NULL;
bool found = false; bool found = false;
git_odb_object *object; git_odb_object *object;
assert(out && db);
if (len < GIT_OID_MINPREFIXLEN)
return git_odb__error_ambiguous("prefix length too short");
if (len > GIT_OID_HEXSZ)
len = GIT_OID_HEXSZ;
if (len == GIT_OID_HEXSZ) {
*out = git_cache_get_raw(odb_cache(db), short_id);
if (*out != NULL)
return 0;
}
/* just copy valid part of short_id */
memcpy(&key.id, short_id->id, (len + 1) / 2);
if (len & 1)
key.id[len / 2] &= 0xF0;
for (i = 0; i < db->backends.length; ++i) { for (i = 0; i < db->backends.length; ++i) {
backend_internal *internal = git_vector_get(&db->backends, i); backend_internal *internal = git_vector_get(&db->backends, i);
git_odb_backend *b = internal->backend; git_odb_backend *b = internal->backend;
if (only_refreshed && !b->refresh)
continue;
if (b->read_prefix != NULL) { if (b->read_prefix != NULL) {
git_oid full_oid; git_oid full_oid;
error = b->read_prefix(&full_oid, &raw.data, &raw.len, &raw.type, b, &key, len); error = b->read_prefix(&full_oid, &raw.data, &raw.len, &raw.type, b, key, len);
if (error == GIT_ENOTFOUND || error == GIT_PASSTHROUGH) if (error == GIT_ENOTFOUND || error == GIT_PASSTHROUGH)
continue; continue;
...@@ -878,7 +927,7 @@ int git_odb_read_prefix( ...@@ -878,7 +927,7 @@ int git_odb_read_prefix(
} }
if (!found) if (!found)
return git_odb__error_notfound("no match for prefix", &key); return GIT_ENOTFOUND;
if ((object = odb_object__alloc(&found_full_oid, &raw)) == NULL) if ((object = odb_object__alloc(&found_full_oid, &raw)) == NULL)
return -1; return -1;
...@@ -887,6 +936,42 @@ int git_odb_read_prefix( ...@@ -887,6 +936,42 @@ int git_odb_read_prefix(
return 0; return 0;
} }
int git_odb_read_prefix(
git_odb_object **out, git_odb *db, const git_oid *short_id, size_t len)
{
git_oid key = {{0}};
int error;
assert(out && db);
if (len < GIT_OID_MINPREFIXLEN)
return git_odb__error_ambiguous("prefix length too short");
if (len > GIT_OID_HEXSZ)
len = GIT_OID_HEXSZ;
if (len == GIT_OID_HEXSZ) {
*out = git_cache_get_raw(odb_cache(db), short_id);
if (*out != NULL)
return 0;
}
/* just copy valid part of short_id */
memcpy(&key.id, short_id->id, (len + 1) / 2);
if (len & 1)
key.id[len / 2] &= 0xF0;
error = read_prefix_1(out, db, &key, len, false);
if (error == GIT_ENOTFOUND && !git_odb_refresh(db))
error = read_prefix_1(out, db, &key, len, true);
if (error == GIT_ENOTFOUND)
return git_odb__error_notfound("no match for prefix", &key);
return error;
}
int git_odb_foreach(git_odb *db, git_odb_foreach_cb cb, void *payload) int git_odb_foreach(git_odb *db, git_odb_foreach_cb cb, void *payload)
{ {
unsigned int i; unsigned int i;
......
...@@ -346,7 +346,7 @@ static int pack_backend__refresh(git_odb_backend *backend_) ...@@ -346,7 +346,7 @@ static int pack_backend__refresh(git_odb_backend *backend_)
return error; return error;
} }
static int pack_backend__read_header_internal( static int pack_backend__read_header(
size_t *len_p, git_otype *type_p, size_t *len_p, git_otype *type_p,
struct git_odb_backend *backend, const git_oid *oid) struct git_odb_backend *backend, const git_oid *oid)
{ {
...@@ -361,24 +361,7 @@ static int pack_backend__read_header_internal( ...@@ -361,24 +361,7 @@ static int pack_backend__read_header_internal(
return git_packfile_resolve_header(len_p, type_p, e.p, e.offset); return git_packfile_resolve_header(len_p, type_p, e.p, e.offset);
} }
static int pack_backend__read_header( static int pack_backend__read(
size_t *len_p, git_otype *type_p,
struct git_odb_backend *backend, const git_oid *oid)
{
int error;
error = pack_backend__read_header_internal(len_p, type_p, backend, oid);
if (error != GIT_ENOTFOUND)
return error;
if ((error = pack_backend__refresh(backend)) < 0)
return error;
return pack_backend__read_header_internal(len_p, type_p, backend, oid);
}
static int pack_backend__read_internal(
void **buffer_p, size_t *len_p, git_otype *type_p, void **buffer_p, size_t *len_p, git_otype *type_p,
git_odb_backend *backend, const git_oid *oid) git_odb_backend *backend, const git_oid *oid)
{ {
...@@ -397,24 +380,7 @@ static int pack_backend__read_internal( ...@@ -397,24 +380,7 @@ static int pack_backend__read_internal(
return 0; return 0;
} }
static int pack_backend__read( static int pack_backend__read_prefix(
void **buffer_p, size_t *len_p, git_otype *type_p,
git_odb_backend *backend, const git_oid *oid)
{
int error;
error = pack_backend__read_internal(buffer_p, len_p, type_p, backend, oid);
if (error != GIT_ENOTFOUND)
return error;
if ((error = pack_backend__refresh(backend)) < 0)
return error;
return pack_backend__read_internal(buffer_p, len_p, type_p, backend, oid);
}
static int pack_backend__read_prefix_internal(
git_oid *out_oid, git_oid *out_oid,
void **buffer_p, void **buffer_p,
size_t *len_p, size_t *len_p,
...@@ -451,45 +417,9 @@ static int pack_backend__read_prefix_internal( ...@@ -451,45 +417,9 @@ static int pack_backend__read_prefix_internal(
return error; return error;
} }
static int pack_backend__read_prefix(
git_oid *out_oid,
void **buffer_p,
size_t *len_p,
git_otype *type_p,
git_odb_backend *backend,
const git_oid *short_oid,
size_t len)
{
int error;
error = pack_backend__read_prefix_internal(
out_oid, buffer_p, len_p, type_p, backend, short_oid, len);
if (error != GIT_ENOTFOUND)
return error;
if ((error = pack_backend__refresh(backend)) < 0)
return error;
return pack_backend__read_prefix_internal(
out_oid, buffer_p, len_p, type_p, backend, short_oid, len);
}
static int pack_backend__exists(git_odb_backend *backend, const git_oid *oid) static int pack_backend__exists(git_odb_backend *backend, const git_oid *oid)
{ {
struct git_pack_entry e; struct git_pack_entry e;
int error;
error = pack_entry_find(&e, (struct pack_backend *)backend, oid);
if (error != GIT_ENOTFOUND)
return error == 0;
if ((error = pack_backend__refresh(backend)) < 0) {
giterr_clear();
return (int)false;
}
return pack_entry_find(&e, (struct pack_backend *)backend, oid) == 0; return pack_entry_find(&e, (struct pack_backend *)backend, oid) == 0;
} }
...@@ -501,12 +431,7 @@ static int pack_backend__exists_prefix( ...@@ -501,12 +431,7 @@ static int pack_backend__exists_prefix(
struct git_pack_entry e = {0}; struct git_pack_entry e = {0};
error = pack_entry_find_prefix(&e, pb, short_id, len); error = pack_entry_find_prefix(&e, pb, short_id, len);
if (error == GIT_ENOTFOUND && !(error = pack_backend__refresh(backend)))
error = pack_entry_find_prefix(&e, pb, short_id, len);
git_oid_cpy(out, &e.sha1); git_oid_cpy(out, &e.sha1);
return error; return error;
} }
...@@ -674,7 +599,6 @@ int git_odb_backend_pack(git_odb_backend **backend_out, const char *objects_dir) ...@@ -674,7 +599,6 @@ int git_odb_backend_pack(git_odb_backend **backend_out, const char *objects_dir)
git_path_isdir(git_buf_cstr(&path))) git_path_isdir(git_buf_cstr(&path)))
{ {
backend->pack_folder = git_buf_detach(&path); backend->pack_folder = git_buf_detach(&path);
error = pack_backend__refresh((git_odb_backend *)backend); error = pack_backend__refresh((git_odb_backend *)backend);
} }
......
...@@ -57,14 +57,14 @@ void test_odb_sorting__basic_backends_sorting(void) ...@@ -57,14 +57,14 @@ void test_odb_sorting__basic_backends_sorting(void)
void test_odb_sorting__alternate_backends_sorting(void) void test_odb_sorting__alternate_backends_sorting(void)
{ {
cl_git_pass(git_odb_add_backend(_odb, new_backend(0), 5)); cl_git_pass(git_odb_add_backend(_odb, new_backend(1), 5));
cl_git_pass(git_odb_add_backend(_odb, new_backend(2), 3)); cl_git_pass(git_odb_add_backend(_odb, new_backend(5), 3));
cl_git_pass(git_odb_add_backend(_odb, new_backend(1), 4)); cl_git_pass(git_odb_add_backend(_odb, new_backend(3), 4));
cl_git_pass(git_odb_add_backend(_odb, new_backend(3), 1)); cl_git_pass(git_odb_add_backend(_odb, new_backend(7), 1));
cl_git_pass(git_odb_add_alternate(_odb, new_backend(4), 5)); cl_git_pass(git_odb_add_alternate(_odb, new_backend(0), 5));
cl_git_pass(git_odb_add_alternate(_odb, new_backend(6), 3)); cl_git_pass(git_odb_add_alternate(_odb, new_backend(4), 3));
cl_git_pass(git_odb_add_alternate(_odb, new_backend(5), 4)); cl_git_pass(git_odb_add_alternate(_odb, new_backend(2), 4));
cl_git_pass(git_odb_add_alternate(_odb, new_backend(7), 1)); cl_git_pass(git_odb_add_alternate(_odb, new_backend(6), 1));
check_backend_sorting(_odb); check_backend_sorting(_odb);
} }
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment