repo: Store transaction file, use it to optimize for pull resumes

If pull is interrupted, we may have downloaded an arbitrary subset of
the requested objects.  Previously, we handled this by scanning for
all objects each time.

However, there's an easy optimization - this patch creates a lock file
in the repo.  If we don't see that file when starting a pull, we know
we don't need to stat() every file; presence of a dirtree object for
example implies the existence of everything it references.
This commit is contained in:
Colin Walters 2013-07-08 09:05:34 -04:00
parent 305cd02e0c
commit 56f8584fe4
5 changed files with 63 additions and 25 deletions

View File

@ -54,6 +54,8 @@ struct OstreeRepo {
GFile *remote_cache_dir; GFile *remote_cache_dir;
GFile *config_file; GFile *config_file;
GFile *transaction_lock_path;
GMutex cache_lock; GMutex cache_lock;
GPtrArray *cached_meta_indexes; GPtrArray *cached_meta_indexes;
GPtrArray *cached_content_indexes; GPtrArray *cached_content_indexes;
@ -106,6 +108,9 @@ ostree_repo_finalize (GObject *object)
g_clear_object (&self->uncompressed_objects_dir); g_clear_object (&self->uncompressed_objects_dir);
g_clear_object (&self->remote_cache_dir); g_clear_object (&self->remote_cache_dir);
g_clear_object (&self->config_file); g_clear_object (&self->config_file);
g_clear_object (&self->transaction_lock_path);
if (self->loose_object_devino_hash) if (self->loose_object_devino_hash)
g_hash_table_destroy (self->loose_object_devino_hash); g_hash_table_destroy (self->loose_object_devino_hash);
if (self->updated_uncompressed_dirs) if (self->updated_uncompressed_dirs)
@ -1357,14 +1362,34 @@ devino_cache_lookup (OstreeRepo *self,
gboolean gboolean
ostree_repo_prepare_transaction (OstreeRepo *self, ostree_repo_prepare_transaction (OstreeRepo *self,
gboolean enable_commit_hardlink_scan, gboolean enable_commit_hardlink_scan,
gboolean *out_transaction_resume,
GCancellable *cancellable, GCancellable *cancellable,
GError **error) GError **error)
{ {
gboolean ret = FALSE; gboolean ret = FALSE;
gboolean ret_transaction_resume = FALSE;
gs_free char *transaction_str = NULL;
g_return_val_if_fail (self->in_transaction == FALSE, FALSE); g_return_val_if_fail (self->in_transaction == FALSE, FALSE);
if (self->transaction_lock_path == NULL)
self->transaction_lock_path = g_file_resolve_relative_path (self->repodir, "transaction");
if (g_file_query_file_type (self->transaction_lock_path, G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS, NULL) == G_FILE_TYPE_SYMBOLIC_LINK)
ret_transaction_resume = TRUE;
else
ret_transaction_resume = FALSE;
self->in_transaction = TRUE; self->in_transaction = TRUE;
if (ret_transaction_resume)
{
if (!ot_gfile_ensure_unlinked (self->transaction_lock_path, cancellable, error))
goto out;
}
transaction_str = g_strdup_printf ("pid=%llu", (unsigned long long) getpid ());
if (!g_file_make_symbolic_link (self->transaction_lock_path, transaction_str,
cancellable, error))
goto out;
if (enable_commit_hardlink_scan) if (enable_commit_hardlink_scan)
{ {
@ -1376,6 +1401,8 @@ ostree_repo_prepare_transaction (OstreeRepo *self,
} }
ret = TRUE; ret = TRUE;
if (out_transaction_resume)
*out_transaction_resume = ret_transaction_resume;
out: out:
return ret; return ret;
} }
@ -1389,12 +1416,15 @@ ostree_repo_commit_transaction (OstreeRepo *self,
g_return_val_if_fail (self->in_transaction == TRUE, FALSE); g_return_val_if_fail (self->in_transaction == TRUE, FALSE);
ret = TRUE; if (!ot_gfile_ensure_unlinked (self->transaction_lock_path, cancellable, error))
/* out: */ goto out;
self->in_transaction = FALSE;
if (self->loose_object_devino_hash) if (self->loose_object_devino_hash)
g_hash_table_remove_all (self->loose_object_devino_hash); g_hash_table_remove_all (self->loose_object_devino_hash);
self->in_transaction = FALSE;
ret = TRUE;
out:
return ret; return ret;
} }

View File

@ -78,6 +78,7 @@ GFile * ostree_repo_get_file_object_path (OstreeRepo *self,
gboolean ostree_repo_prepare_transaction (OstreeRepo *self, gboolean ostree_repo_prepare_transaction (OstreeRepo *self,
gboolean enable_commit_hardlink_scan, gboolean enable_commit_hardlink_scan,
gboolean *out_transaction_resume,
GCancellable *cancellable, GCancellable *cancellable,
GError **error); GError **error);

View File

@ -108,6 +108,7 @@ typedef struct {
GMainLoop *loop; GMainLoop *loop;
GCancellable *cancellable; GCancellable *cancellable;
gboolean transaction_resuming;
volatile gint n_scanned_metadata; volatile gint n_scanned_metadata;
guint outstanding_uri_requests; guint outstanding_uri_requests;
@ -822,7 +823,7 @@ scan_one_metadata_object (OtPullData *pull_data,
if (!ostree_repo_has_object (pull_data->repo, objtype, tmp_checksum, &is_stored, if (!ostree_repo_has_object (pull_data->repo, objtype, tmp_checksum, &is_stored,
cancellable, error)) cancellable, error))
goto out; goto out;
if (!is_stored && !is_requested) if (!is_stored && !is_requested)
{ {
char *duped_checksum = g_strdup (tmp_checksum); char *duped_checksum = g_strdup (tmp_checksum);
@ -834,23 +835,26 @@ scan_one_metadata_object (OtPullData *pull_data,
} }
else if (is_stored) else if (is_stored)
{ {
switch (objtype) if (pull_data->transaction_resuming || is_requested)
{ {
case OSTREE_OBJECT_TYPE_COMMIT: switch (objtype)
if (!scan_commit_object (pull_data, tmp_checksum, recursion_depth, {
pull_data->cancellable, error)) case OSTREE_OBJECT_TYPE_COMMIT:
goto out; if (!scan_commit_object (pull_data, tmp_checksum, recursion_depth,
break; pull_data->cancellable, error))
case OSTREE_OBJECT_TYPE_DIR_META: goto out;
break; break;
case OSTREE_OBJECT_TYPE_DIR_TREE: case OSTREE_OBJECT_TYPE_DIR_META:
if (!scan_dirtree_object (pull_data, tmp_checksum, recursion_depth, break;
pull_data->cancellable, error)) case OSTREE_OBJECT_TYPE_DIR_TREE:
goto out; if (!scan_dirtree_object (pull_data, tmp_checksum, recursion_depth,
break; pull_data->cancellable, error))
case OSTREE_OBJECT_TYPE_FILE: goto out;
g_assert_not_reached (); break;
break; case OSTREE_OBJECT_TYPE_FILE:
g_assert_not_reached ();
break;
}
} }
g_hash_table_insert (pull_data->scanned_metadata, g_variant_ref (object), object); g_hash_table_insert (pull_data->scanned_metadata, g_variant_ref (object), object);
g_atomic_int_inc (&pull_data->n_scanned_metadata); g_atomic_int_inc (&pull_data->n_scanned_metadata);
@ -1347,7 +1351,8 @@ ostree_builtin_pull (int argc, char **argv, GFile *repo_path, GError **error)
} }
} }
if (!ostree_repo_prepare_transaction (pull_data->repo, FALSE, NULL, error)) if (!ostree_repo_prepare_transaction (pull_data->repo, FALSE, &pull_data->transaction_resuming,
cancellable, error))
goto out; goto out;
pull_data->metadata_objects_to_fetch = ot_waitable_queue_new (); pull_data->metadata_objects_to_fetch = ot_waitable_queue_new ();

View File

@ -351,7 +351,7 @@ ostree_builtin_commit (int argc, char **argv, GFile *repo_path, GError **error)
goto out; goto out;
} }
if (!ostree_repo_prepare_transaction (repo, TRUE, cancellable, error)) if (!ostree_repo_prepare_transaction (repo, TRUE, NULL, cancellable, error))
goto out; goto out;
in_transaction = TRUE; in_transaction = TRUE;

View File

@ -170,6 +170,7 @@ ostree_builtin_pull_local (int argc, char **argv, GFile *repo_path, GError **err
int i; int i;
GHashTableIter hash_iter; GHashTableIter hash_iter;
gpointer key, value; gpointer key, value;
gboolean transaction_resuming = FALSE;
gs_unref_hashtable GHashTable *objects = NULL; gs_unref_hashtable GHashTable *objects = NULL;
gs_unref_object GFile *src_f = NULL; gs_unref_object GFile *src_f = NULL;
gs_unref_object GFile *src_repo_dir = NULL; gs_unref_object GFile *src_repo_dir = NULL;
@ -247,6 +248,10 @@ ostree_builtin_pull_local (int argc, char **argv, GFile *repo_path, GError **err
} }
} }
if (!ostree_repo_prepare_transaction (data->dest_repo, FALSE, &transaction_resuming,
cancellable, error))
goto out;
g_print ("Enumerating objects...\n"); g_print ("Enumerating objects...\n");
source_objects = ostree_traverse_new_reachable (); source_objects = ostree_traverse_new_reachable ();
@ -275,9 +280,6 @@ ostree_builtin_pull_local (int argc, char **argv, GFile *repo_path, GError **err
} }
} }
if (!ostree_repo_prepare_transaction (data->dest_repo, FALSE, cancellable, error))
goto out;
data->n_objects_to_check = g_hash_table_size (source_objects); data->n_objects_to_check = g_hash_table_size (source_objects);
g_hash_table_iter_init (&hash_iter, source_objects); g_hash_table_iter_init (&hash_iter, source_objects);
while (g_hash_table_iter_next (&hash_iter, &key, &value)) while (g_hash_table_iter_next (&hash_iter, &key, &value))