Commit 4a863c06 by Vicent Marti

Sane refresh logic

All the ODB backends have a specific refresh interface. When reading an
object, first we attempt every single backend: if the read fails, then
we refresh all the backends and retry the read one more time to see if
the object has appeared.
parent a22ad9fd
...@@ -192,6 +192,26 @@ GIT_EXTERN(int) git_odb_read_header(size_t *len_out, git_otype *type_out, git_od ...@@ -192,6 +192,26 @@ GIT_EXTERN(int) git_odb_read_header(size_t *len_out, git_otype *type_out, git_od
GIT_EXTERN(int) git_odb_exists(git_odb *db, const git_oid *id); GIT_EXTERN(int) git_odb_exists(git_odb *db, const git_oid *id);
/** /**
* Refresh the object database to load newly added files.
*
* If the object databases have changed on disk while the library
* is running, this function will force a reload of the underlying
* indexes.
*
* Use this function when you're confident that an external
* application has tampered with the ODB.
*
* NOTE that it is not necessary to call this function at all. The
* library will automatically attempt to refresh the ODB
* when a lookup fails, to see if the looked up object exists
* on disk but hasn't been loaded yet.
*
* @param db database to refresh
* @return 0 on success, error code otherwise
*/
GIT_EXTERN(int) git_odb_refresh(struct git_odb *db);
/**
* List all objects available in the database * List all objects available in the database
* *
* The callback will be called for each object available in the * The callback will be called for each object available in the
......
...@@ -89,6 +89,8 @@ struct git_odb_backend { ...@@ -89,6 +89,8 @@ struct git_odb_backend {
struct git_odb_backend *, struct git_odb_backend *,
const git_oid *); const git_oid *);
int (* refresh)(struct git_odb_backend *);
int (* foreach)( int (* foreach)(
struct git_odb_backend *, struct git_odb_backend *,
git_odb_foreach_cb cb, git_odb_foreach_cb cb,
......
...@@ -609,14 +609,22 @@ int git_odb_read(git_odb_object **out, git_odb *db, const git_oid *id) ...@@ -609,14 +609,22 @@ int git_odb_read(git_odb_object **out, git_odb *db, const git_oid *id)
{ {
unsigned int i; unsigned int i;
int error = GIT_ENOTFOUND; int error = GIT_ENOTFOUND;
bool refreshed = false;
git_rawobj raw; git_rawobj raw;
assert(out && db && id); assert(out && db && id);
if (db->backends.length == 0) {
giterr_set(GITERR_ODB, "Failed to lookup object: no backends loaded");
return GIT_ENOTFOUND;
}
*out = git_cache_get(&db->cache, id); *out = git_cache_get(&db->cache, id);
if (*out != NULL) if (*out != NULL)
return 0; return 0;
attempt_lookup:
for (i = 0; i < db->backends.length && error < 0; ++i) { for (i = 0; i < db->backends.length && error < 0; ++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;
...@@ -625,9 +633,13 @@ int git_odb_read(git_odb_object **out, git_odb *db, const git_oid *id) ...@@ -625,9 +633,13 @@ int git_odb_read(git_odb_object **out, git_odb *db, const git_oid *id)
error = b->read(&raw.data, &raw.len, &raw.type, b, id); error = b->read(&raw.data, &raw.len, &raw.type, b, id);
} }
/* TODO: If no backends are configured, this returns GIT_ENOTFOUND but if (error == GIT_ENOTFOUND && !refreshed) {
* will never have called giterr_set(). if ((error = git_odb_refresh(db)) < 0)
*/ return error;
refreshed = true;
goto attempt_lookup;
}
if (error && error != GIT_PASSTHROUGH) if (error && error != GIT_PASSTHROUGH)
return error; return error;
...@@ -644,7 +656,7 @@ int git_odb_read_prefix( ...@@ -644,7 +656,7 @@ int git_odb_read_prefix(
git_oid 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, refreshed = false;
assert(out && db); assert(out && db);
...@@ -660,11 +672,13 @@ int git_odb_read_prefix( ...@@ -660,11 +672,13 @@ int git_odb_read_prefix(
return 0; return 0;
} }
attempt_lookup:
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 (b->read != 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, short_id, len); error = b->read_prefix(&full_oid, &raw.data, &raw.len, &raw.type, b, short_id, len);
if (error == GIT_ENOTFOUND || error == GIT_PASSTHROUGH) if (error == GIT_ENOTFOUND || error == GIT_PASSTHROUGH)
...@@ -675,13 +689,23 @@ int git_odb_read_prefix( ...@@ -675,13 +689,23 @@ int git_odb_read_prefix(
git__free(data); git__free(data);
data = raw.data; data = raw.data;
if (found && git_oid_cmp(&full_oid, &found_full_oid)) if (found && git_oid_cmp(&full_oid, &found_full_oid))
return git_odb__error_ambiguous("multiple matches for prefix"); return git_odb__error_ambiguous("multiple matches for prefix");
found_full_oid = full_oid; found_full_oid = full_oid;
found = true; found = true;
} }
} }
if (!found && !refreshed) {
if ((error = git_odb_refresh(db)) < 0)
return error;
refreshed = true;
goto attempt_lookup;
}
if (!found) if (!found)
return git_odb__error_notfound("no match for prefix", short_id); return git_odb__error_notfound("no match for prefix", short_id);
...@@ -820,12 +844,31 @@ int git_odb_write_pack(struct git_odb_writepack **out, git_odb *db, git_transfer ...@@ -820,12 +844,31 @@ int git_odb_write_pack(struct git_odb_writepack **out, git_odb *db, git_transfer
return error; return error;
} }
void * git_odb_backend_malloc(git_odb_backend *backend, size_t len) void *git_odb_backend_malloc(git_odb_backend *backend, size_t len)
{ {
GIT_UNUSED(backend); GIT_UNUSED(backend);
return git__malloc(len); return git__malloc(len);
} }
int git_odb_refresh(struct git_odb *db)
{
unsigned int i;
assert(db);
for (i = 0; i < db->backends.length; ++i) {
backend_internal *internal = git_vector_get(&db->backends, i);
git_odb_backend *b = internal->backend;
if (b->refresh != NULL) {
int error = b->refresh(b);
if (error < 0)
return error;
}
}
return 0;
}
int git_odb__error_notfound(const char *message, const git_oid *oid) int git_odb__error_notfound(const char *message, const git_oid *oid)
{ {
if (oid != NULL) { if (oid != NULL) {
......
...@@ -138,7 +138,6 @@ static int pack_window_contains(git_mwindow *win, off_t offset); ...@@ -138,7 +138,6 @@ static int pack_window_contains(git_mwindow *win, off_t offset);
static int packfile_sort__cb(const void *a_, const void *b_); static int packfile_sort__cb(const void *a_, const void *b_);
static int packfile_load__cb(void *_data, git_buf *path); static int packfile_load__cb(void *_data, git_buf *path);
static int packfile_refresh_all(struct pack_backend *backend);
static int pack_entry_find(struct git_pack_entry *e, static int pack_entry_find(struct git_pack_entry *e,
struct pack_backend *backend, const git_oid *oid); struct pack_backend *backend, const git_oid *oid);
...@@ -237,33 +236,6 @@ static int packfile_load__cb(void *_data, git_buf *path) ...@@ -237,33 +236,6 @@ static int packfile_load__cb(void *_data, git_buf *path)
return git_vector_insert(&backend->packs, pack); return git_vector_insert(&backend->packs, pack);
} }
static int packfile_refresh_all(struct pack_backend *backend)
{
int error;
struct stat st;
git_buf path = GIT_BUF_INIT;
if (backend->pack_folder == NULL)
return 0;
if (p_stat(backend->pack_folder, &st) < 0 || !S_ISDIR(st.st_mode))
return git_odb__error_notfound("failed to refresh packfiles", NULL);
git_buf_sets(&path, backend->pack_folder);
/* reload all packs */
error = git_path_direach(&path, packfile_load__cb, (void *)backend);
git_buf_free(&path);
if (error < 0)
return error;
git_vector_sort(&backend->packs);
return 0;
}
static int pack_entry_find_inner( static int pack_entry_find_inner(
struct git_pack_entry *e, struct git_pack_entry *e,
struct pack_backend *backend, struct pack_backend *backend,
...@@ -294,7 +266,6 @@ static int pack_entry_find_inner( ...@@ -294,7 +266,6 @@ static int pack_entry_find_inner(
static int pack_entry_find(struct git_pack_entry *e, struct pack_backend *backend, const git_oid *oid) static int pack_entry_find(struct git_pack_entry *e, struct pack_backend *backend, const git_oid *oid)
{ {
int error;
struct git_pack_file *last_found = backend->last_found; struct git_pack_file *last_found = backend->last_found;
if (backend->last_found && if (backend->last_found &&
...@@ -303,10 +274,6 @@ static int pack_entry_find(struct git_pack_entry *e, struct pack_backend *backen ...@@ -303,10 +274,6 @@ static int pack_entry_find(struct git_pack_entry *e, struct pack_backend *backen
if (!pack_entry_find_inner(e, backend, oid, last_found)) if (!pack_entry_find_inner(e, backend, oid, last_found))
return 0; return 0;
if ((error = packfile_refresh_all(backend)) < 0)
return error;
if (!pack_entry_find_inner(e, backend, oid, last_found))
return 0;
return git_odb__error_notfound("failed to find pack entry", oid); return git_odb__error_notfound("failed to find pack entry", oid);
} }
...@@ -356,17 +323,9 @@ static int pack_entry_find_prefix( ...@@ -356,17 +323,9 @@ static int pack_entry_find_prefix(
const git_oid *short_oid, const git_oid *short_oid,
size_t len) size_t len)
{ {
unsigned found = 0;
int error;
struct git_pack_file *last_found = backend->last_found; struct git_pack_file *last_found = backend->last_found;
unsigned int found = pack_entry_find_prefix_inner(e, backend, short_oid, len, last_found);
if ((found = pack_entry_find_prefix_inner(e, backend, short_oid, len, last_found)) > 0)
goto cleanup;
if ((error = packfile_refresh_all(backend)) < 0)
return error;
found = pack_entry_find_prefix_inner(e, backend, short_oid, len, last_found);
cleanup:
if (!found) if (!found)
return git_odb__error_notfound("no matching pack entry for prefix", short_oid); return git_odb__error_notfound("no matching pack entry for prefix", short_oid);
else if (found > 1) else if (found > 1)
...@@ -383,6 +342,34 @@ cleanup: ...@@ -383,6 +342,34 @@ cleanup:
* Implement the git_odb_backend API calls * Implement the git_odb_backend API calls
* *
***********************************************************/ ***********************************************************/
static int pack_backend__refresh(git_odb_backend *_backend)
{
struct pack_backend *backend = (struct pack_backend *)_backend;
int error;
struct stat st;
git_buf path = GIT_BUF_INIT;
if (backend->pack_folder == NULL)
return 0;
if (p_stat(backend->pack_folder, &st) < 0 || !S_ISDIR(st.st_mode))
return git_odb__error_notfound("failed to refresh packfiles", NULL);
git_buf_sets(&path, backend->pack_folder);
/* reload all packs */
error = git_path_direach(&path, packfile_load__cb, (void *)backend);
git_buf_free(&path);
if (error < 0)
return error;
git_vector_sort(&backend->packs);
return 0;
}
static int pack_backend__read_header(size_t *len_p, git_otype *type_p, struct git_odb_backend *backend, const git_oid *oid) static int pack_backend__read_header(size_t *len_p, git_otype *type_p, struct git_odb_backend *backend, const git_oid *oid)
{ {
...@@ -468,7 +455,7 @@ static int pack_backend__foreach(git_odb_backend *_backend, git_odb_foreach_cb c ...@@ -468,7 +455,7 @@ static int pack_backend__foreach(git_odb_backend *_backend, git_odb_foreach_cb c
backend = (struct pack_backend *)_backend; backend = (struct pack_backend *)_backend;
/* Make sure we know about the packfiles */ /* Make sure we know about the packfiles */
if ((error = packfile_refresh_all(backend)) < 0) if ((error = pack_backend__refresh(_backend)) < 0)
return error; return error;
git_vector_foreach(&backend->packs, i, p) { git_vector_foreach(&backend->packs, i, p) {
...@@ -581,6 +568,7 @@ int git_odb_backend_one_pack(git_odb_backend **backend_out, const char *idx) ...@@ -581,6 +568,7 @@ int git_odb_backend_one_pack(git_odb_backend **backend_out, const char *idx)
backend->parent.read_prefix = &pack_backend__read_prefix; backend->parent.read_prefix = &pack_backend__read_prefix;
backend->parent.read_header = &pack_backend__read_header; backend->parent.read_header = &pack_backend__read_header;
backend->parent.exists = &pack_backend__exists; backend->parent.exists = &pack_backend__exists;
backend->parent.refresh = &pack_backend__refresh;
backend->parent.foreach = &pack_backend__foreach; backend->parent.foreach = &pack_backend__foreach;
backend->parent.free = &pack_backend__free; backend->parent.free = &pack_backend__free;
...@@ -619,6 +607,7 @@ int git_odb_backend_pack(git_odb_backend **backend_out, const char *objects_dir) ...@@ -619,6 +607,7 @@ int git_odb_backend_pack(git_odb_backend **backend_out, const char *objects_dir)
backend->parent.read_prefix = &pack_backend__read_prefix; backend->parent.read_prefix = &pack_backend__read_prefix;
backend->parent.read_header = &pack_backend__read_header; backend->parent.read_header = &pack_backend__read_header;
backend->parent.exists = &pack_backend__exists; backend->parent.exists = &pack_backend__exists;
backend->parent.refresh = &pack_backend__refresh;
backend->parent.foreach = &pack_backend__foreach; backend->parent.foreach = &pack_backend__foreach;
backend->parent.writepack = &pack_backend__writepack; backend->parent.writepack = &pack_backend__writepack;
backend->parent.free = &pack_backend__free; backend->parent.free = &pack_backend__free;
......
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