[ostree] pull: use a single per-transaction syncfs instead of fsync
- From: Colin Walters <walters src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [ostree] pull: use a single per-transaction syncfs instead of fsync
- Date: Fri, 30 Jan 2015 19:03:55 +0000 (UTC)
commit 27a45e2edbd1c4551b801a957cafcb27b3417fd3
Author: Giuseppe Scrivano <gscrivan redhat com>
Date: Thu Jan 22 01:05:13 2015 +0100
pull: use a single per-transaction syncfs instead of fsync
Do not write directly to objects/ but maintain pulled files under tmp/
with a "tmpobject-$CHECKSUM.$OBJTYPE" name until they are syncfs'ed to
disk.
Move them under objects/ at ostree_repo_commit_transaction cleanup
time.
Before (test done on a local network):
$ LANG=C sudo time ./ostree --repo=repo pull origin master
0 metadata, 3 content objects fetched; 83820 KiB; 4 delta parts
fetched, transferred in 417 seconds
16.42user 6.73system 6:57.19elapsed 5%CPU (0avgtext+0avgdata
248428maxresident)k
24inputs+794472outputs (0major+233968minor)pagefaults 0swaps
After:
$ LANG=C sudo time ./ostree --repo=repo pull origin master
0 metadata, 3 content objects fetched; 83820 KiB; 4 delta parts
fetched, transferred in 9 seconds
14.70user 2.87system 0:09.99elapsed 175%CPU (0avgtext+0avgdata
256168maxresident)k
0inputs+794472outputs (0major+164333minor)pagefaults 0swaps
https://bugzilla.gnome.org/show_bug.cgi?id=728065
Signed-off-by: Giuseppe Scrivano <gscrivan redhat com>
src/libostree/ostree-repo-commit.c | 148 +++++++++++++++++++++++++++++++----
src/libostree/ostree-repo-private.h | 8 ++
src/libostree/ostree-repo.c | 79 +++++++++++++-----
3 files changed, 196 insertions(+), 39 deletions(-)
---
diff --git a/src/libostree/ostree-repo-commit.c b/src/libostree/ostree-repo-commit.c
index a149c2e..c6ac572 100644
--- a/src/libostree/ostree-repo-commit.c
+++ b/src/libostree/ostree-repo-commit.c
@@ -35,6 +35,7 @@
#include "ostree-mutable-tree.h"
#include "ostree-varint.h"
#include <attr/xattr.h>
+#include <glib/gprintf.h>
gboolean
_ostree_repo_ensure_loose_objdir_at (int dfd,
@@ -59,6 +60,19 @@ _ostree_repo_ensure_loose_objdir_at (int dfd,
return TRUE;
}
+void
+_ostree_repo_get_tmpobject_path (OstreeRepo *repo,
+ char *output,
+ const char *checksum,
+ OstreeObjectType objtype)
+{
+ g_sprintf (output,
+ "%s/tmpobject-%s.%s",
+ repo->boot_id,
+ checksum,
+ ostree_object_type_to_string (objtype));
+}
+
static GVariant *
create_file_metadata (GFileInfo *file_info,
GVariant *xattrs)
@@ -108,6 +122,7 @@ write_file_metadata_to_xattr (int fd,
static gboolean
commit_loose_object_trusted (OstreeRepo *self,
+ const char *checksum,
OstreeObjectType objtype,
const char *loose_path,
GFile *temp_file,
@@ -251,7 +266,7 @@ commit_loose_object_trusted (OstreeRepo *self,
/* Ensure that in case of a power cut, these files have the data we
* want. See http://lwn.net/Articles/322823/
*/
- if (!self->disable_fsync)
+ if (!self->in_transaction && !self->disable_fsync)
{
if (fsync (fd) == -1)
{
@@ -267,20 +282,39 @@ commit_loose_object_trusted (OstreeRepo *self,
if (!_ostree_repo_ensure_loose_objdir_at (self->objects_dir_fd, loose_path,
cancellable, error))
goto out;
-
- if (G_UNLIKELY (renameat (self->tmp_dir_fd, temp_filename,
- self->objects_dir_fd, loose_path) == -1))
- {
- if (errno != EEXIST)
- {
- gs_set_error_from_errno (error, errno);
- g_prefix_error (error, "Storing file '%s': ", temp_filename);
- goto out;
- }
- else
- (void) unlinkat (self->tmp_dir_fd, temp_filename, 0);
- }
+ {
+ gs_free gchar *tmp_dest = NULL;
+ int dir;
+ const char *dest;
+
+ if (self->in_transaction)
+ {
+ char tmpbuf[_OSTREE_LOOSE_PATH_MAX];
+ _ostree_repo_get_tmpobject_path (self, tmpbuf, checksum, objtype);
+ tmp_dest = g_strdup (tmpbuf);
+ dir = self->tmp_dir_fd;
+ dest = tmp_dest;
+ }
+ else
+ {
+ dir = self->objects_dir_fd;
+ dest = loose_path;
+ }
+
+ if (G_UNLIKELY (renameat (self->tmp_dir_fd, temp_filename,
+ dir, dest) == -1))
+ {
+ if (errno != EEXIST)
+ {
+ gs_set_error_from_errno (error, errno);
+ g_prefix_error (error, "Storing file '%s': ", temp_filename);
+ goto out;
+ }
+ else
+ (void) unlinkat (self->tmp_dir_fd, temp_filename, 0);
+ }
+ }
ret = TRUE;
out:
return ret;
@@ -474,7 +508,7 @@ write_object (OstreeRepo *self,
{
if (!_ostree_repo_has_loose_object (self, expected_checksum, objtype,
&have_obj, loose_objpath,
- cancellable, error))
+ NULL, cancellable, error))
goto out;
if (have_obj)
{
@@ -655,7 +689,7 @@ write_object (OstreeRepo *self,
}
if (!_ostree_repo_has_loose_object (self, actual_checksum, objtype,
- &have_obj, loose_objpath,
+ &have_obj, loose_objpath, NULL,
cancellable, error))
goto out;
@@ -663,7 +697,8 @@ write_object (OstreeRepo *self,
if (do_commit)
{
- if (!commit_loose_object_trusted (self, objtype, loose_objpath,
+ if (!commit_loose_object_trusted (self, actual_checksum,
+ objtype, loose_objpath,
temp_file, temp_filename,
object_is_symlink, file_info,
xattrs, temp_out,
@@ -930,6 +965,17 @@ ostree_repo_prepare_transaction (OstreeRepo *self,
if (!ot_gfile_ensure_unlinked (self->transaction_lock_path, cancellable, error))
goto out;
}
+
+ if (mkdirat (self->tmp_dir_fd, self->boot_id, 0777) == -1)
+ {
+ int errsv = errno;
+ if (G_UNLIKELY (errsv != EEXIST))
+ {
+ gs_set_error_from_errno (error, errsv);
+ 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))
@@ -943,6 +989,65 @@ ostree_repo_prepare_transaction (OstreeRepo *self,
}
static gboolean
+rename_pending_loose_objects (OstreeRepo *self,
+ GCancellable *cancellable,
+ GError **error)
+{
+ gboolean ret = FALSE;
+ gs_dirfd_iterator_cleanup GSDirFdIterator child_dfd_iter = { 0, };
+
+ if (!gs_dirfd_iterator_init_at (self->tmp_dir_fd, self->boot_id, FALSE, &child_dfd_iter, error))
+ goto out;
+
+ while (TRUE)
+ {
+ struct dirent *out_dent;
+
+ if (!gs_dirfd_iterator_next_dent (&child_dfd_iter, &out_dent, cancellable, error))
+ goto out;
+
+ if (out_dent == NULL)
+ break;
+
+ if (strncmp (out_dent->d_name, "tmpobject-", 10) == 0)
+ {
+ char loose_path[_OSTREE_LOOSE_PATH_MAX];
+ gs_free gchar *checksum = NULL;
+ OstreeObjectType type;
+ ostree_object_from_string (out_dent->d_name + 10,
+ &checksum,
+ &type);
+
+ _ostree_loose_path (loose_path, checksum, type, self->mode);
+
+ if (!_ostree_repo_ensure_loose_objdir_at (self->objects_dir_fd, loose_path,
+ cancellable, error))
+ goto out;
+
+ if (G_UNLIKELY (renameat (child_dfd_iter.fd, out_dent->d_name,
+ self->objects_dir_fd, loose_path) < 0))
+ {
+ (void) unlinkat (self->tmp_dir_fd, out_dent->d_name, 0);
+ if (errno != EEXIST)
+ {
+ gs_set_error_from_errno (error, errno);
+ g_prefix_error (error, "Storing file '%s': ", loose_path);
+ goto out;
+ }
+ }
+ continue;
+ }
+ }
+
+ if (!gs_shutil_rm_rf_at (self->tmp_dir_fd, self->boot_id, cancellable, error))
+ goto out;
+
+ ret = TRUE;
+ out:
+ return ret;
+}
+
+static gboolean
cleanup_tmpdir (OstreeRepo *self,
GCancellable *cancellable,
GError **error)
@@ -1109,6 +1214,15 @@ ostree_repo_commit_transaction (OstreeRepo *self,
g_return_val_if_fail (self->in_transaction == TRUE, FALSE);
+ if (syncfs (self->tmp_dir_fd) < 0)
+ {
+ gs_set_error_from_errno (error, errno);
+ goto out;
+ }
+
+ if (! rename_pending_loose_objects (self, cancellable, error))
+ goto out;
+
if (!cleanup_tmpdir (self, cancellable, error))
goto out;
diff --git a/src/libostree/ostree-repo-private.h b/src/libostree/ostree-repo-private.h
index b36e6d9..b7f926d 100644
--- a/src/libostree/ostree-repo-private.h
+++ b/src/libostree/ostree-repo-private.h
@@ -34,6 +34,8 @@ G_BEGIN_DECLS
struct OstreeRepo {
GObject parent;
+ char *boot_id;
+
GFile *repodir;
GFile *tmp_dir;
int tmp_dir_fd;
@@ -82,6 +84,11 @@ _ostree_repo_ensure_loose_objdir_at (int dfd,
const char *loose_path,
GCancellable *cancellable,
GError **error);
+void
+_ostree_repo_get_tmpobject_path (OstreeRepo *repo,
+ char *output,
+ const char *checksum,
+ OstreeObjectType objtype);
gboolean
_ostree_repo_find_object (OstreeRepo *self,
@@ -101,6 +108,7 @@ _ostree_repo_has_loose_object (OstreeRepo *self,
OstreeObjectType objtype,
gboolean *out_is_stored,
char *loose_path_buf,
+ GFile **out_stored_path,
GCancellable *cancellable,
GError **error);
diff --git a/src/libostree/ostree-repo.c b/src/libostree/ostree-repo.c
index f27911d..d98ea0b 100644
--- a/src/libostree/ostree-repo.c
+++ b/src/libostree/ostree-repo.c
@@ -326,6 +326,7 @@ ostree_repo_finalize (GObject *object)
g_clear_object (&self->parent_repo);
+ g_free (self->boot_id);
g_clear_object (&self->repodir);
g_clear_object (&self->tmp_dir);
if (self->tmp_dir_fd)
@@ -1277,6 +1278,16 @@ ostree_repo_open (OstreeRepo *self,
if (self->inited)
return TRUE;
+ /* We use a per-boot identifier to keep track of which file contents
+ * possibly haven't been sync'd to disk.
+ */
+ if (!g_file_get_contents ("/proc/sys/kernel/random/boot_id",
+ &self->boot_id,
+ NULL,
+ error))
+ goto out;
+ g_strdelimit (self->boot_id, "\n", '\0');
+
if (!gs_file_open_dir_fd (self->objects_dir, &self->objects_dir_fd, cancellable, error))
{
g_prefix_error (error, "Reading objects/ directory: ");
@@ -1715,6 +1726,13 @@ load_metadata_internal (OstreeRepo *self,
cancellable, error))
goto out;
+ if (self->in_transaction && fd < 0)
+ {
+ _ostree_repo_get_tmpobject_path (self, loose_path_buf, sha256, objtype);
+ if (!openat_allow_noent (self->tmp_dir_fd, loose_path_buf, &fd, cancellable, error))
+ goto out;
+ }
+
if (fd != -1)
{
if (out_variant)
@@ -2111,27 +2129,55 @@ _ostree_repo_has_loose_object (OstreeRepo *self,
OstreeObjectType objtype,
gboolean *out_is_stored,
char *loose_path_buf,
+ GFile **out_stored_path,
GCancellable *cancellable,
GError **error)
{
gboolean ret = FALSE;
struct stat stbuf;
- int res;
+ int res = -1;
+ gboolean tmp_file = FALSE;
- _ostree_loose_path (loose_path_buf, checksum, objtype, self->mode);
+ if (self->in_transaction)
+ {
+ _ostree_repo_get_tmpobject_path (self, loose_path_buf, checksum, objtype);
+ do
+ res = fstatat (self->tmp_dir_fd, loose_path_buf, &stbuf, AT_SYMLINK_NOFOLLOW);
+ while (G_UNLIKELY (res == -1 && errno == EINTR));
+ if (res == -1 && errno != ENOENT)
+ {
+ gs_set_error_from_errno (error, errno);
+ goto out;
+ }
+ }
- do
- res = fstatat (self->objects_dir_fd, loose_path_buf, &stbuf, AT_SYMLINK_NOFOLLOW);
- while (G_UNLIKELY (res == -1 && errno == EINTR));
- if (res == -1 && errno != ENOENT)
+ if (res == 0)
+ tmp_file = TRUE;
+ else
{
- gs_set_error_from_errno (error, errno);
- goto out;
+ _ostree_loose_path (loose_path_buf, checksum, objtype, self->mode);
+
+ do
+ res = fstatat (self->objects_dir_fd, loose_path_buf, &stbuf, AT_SYMLINK_NOFOLLOW);
+ while (G_UNLIKELY (res == -1 && errno == EINTR));
+ if (res == -1 && errno != ENOENT)
+ {
+ gs_set_error_from_errno (error, errno);
+ goto out;
+ }
}
ret = TRUE;
*out_is_stored = (res != -1);
- out:
+
+ if (out_stored_path)
+ {
+ if (res != -1)
+ *out_stored_path = g_file_resolve_relative_path (tmp_file ? self->tmp_dir : self->objects_dir,
loose_path_buf);
+ else
+ *out_stored_path = NULL;
+ }
+out:
return ret;
}
@@ -2143,21 +2189,10 @@ _ostree_repo_find_object (OstreeRepo *self,
GCancellable *cancellable,
GError **error)
{
- gboolean ret = FALSE;
gboolean has_object;
char loose_path[_OSTREE_LOOSE_PATH_MAX];
-
- if (!_ostree_repo_has_loose_object (self, checksum, objtype, &has_object, loose_path,
- cancellable, error))
- goto out;
-
- ret = TRUE;
- if (has_object)
- *out_stored_path = g_file_resolve_relative_path (self->objects_dir, loose_path);
- else
- *out_stored_path = NULL;
-out:
- return ret;
+ return _ostree_repo_has_loose_object (self, checksum, objtype, &has_object, loose_path,
+ out_stored_path, cancellable, error);
}
/**
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]