odb: only freshen pack files every 2 seconds Since writing multiple objects may all already exist in a single packfile, avoid freshening that packfile repeatedly in a tight loop. Instead, only freshen pack files every 2 seconds.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136
diff --git a/src/fileops.c b/src/fileops.c
index ce64934..fcc0301 100644
--- a/src/fileops.c
+++ b/src/fileops.c
@@ -837,16 +837,13 @@ int git_futils_cp(const char *from, const char *to, mode_t filemode)
return cp_by_fd(ifd, ofd, true);
}
-int git_futils_touch(const char *path)
+int git_futils_touch(const char *path, time_t *when)
{
struct p_timeval times[2];
- time_t now = time(NULL);
int ret;
- times[0].tv_sec = now;
- times[0].tv_usec = 0;
- times[1].tv_sec = now;
- times[1].tv_usec = 0;
+ times[0].tv_sec = times[1].tv_sec = when ? *when : time(NULL);
+ times[0].tv_usec = times[1].tv_usec = 0;
ret = p_utimes(path, times);
diff --git a/src/fileops.h b/src/fileops.h
index e055400..54e3bd4 100644
--- a/src/fileops.h
+++ b/src/fileops.h
@@ -185,9 +185,10 @@ extern int git_futils_cp(
mode_t filemode);
/**
- * Set the files atime and mtime to the current time.
+ * Set the files atime and mtime to the given time, or the current time
+ * if `ts` is NULL.
*/
-extern int git_futils_touch(const char *path);
+extern int git_futils_touch(const char *path, time_t *when);
/**
* Flags that can be passed to `git_futils_cp_r`.
diff --git a/src/odb_loose.c b/src/odb_loose.c
index 1653e27..f312b9c 100644
--- a/src/odb_loose.c
+++ b/src/odb_loose.c
@@ -929,7 +929,7 @@ static int loose_backend__freshen(
if (object_file_name(&path, backend, oid) < 0)
return -1;
- error = git_futils_touch(path.ptr);
+ error = git_futils_touch(path.ptr, NULL);
git_buf_free(&path);
return error;
diff --git a/src/odb_pack.c b/src/odb_pack.c
index 3b52b6b..b80d033 100644
--- a/src/odb_pack.c
+++ b/src/odb_pack.c
@@ -20,6 +20,9 @@
#include "git2/odb_backend.h"
+/* re-freshen pack files no more than every 2 seconds */
+#define FRESHEN_FREQUENCY 2
+
struct pack_backend {
git_odb_backend parent;
git_vector packs;
@@ -367,12 +370,22 @@ static int pack_backend__freshen(
git_odb_backend *backend, const git_oid *oid)
{
struct git_pack_entry e;
+ time_t now;
int error;
if ((error = pack_entry_find(&e, (struct pack_backend *)backend, oid)) < 0)
return error;
- return git_futils_touch(e.p->pack_name);
+ now = time(NULL);
+
+ if (e.p->last_freshen > now - FRESHEN_FREQUENCY)
+ return 0;
+
+ if ((error = git_futils_touch(e.p->pack_name, &now)) < 0)
+ return error;
+
+ e.p->last_freshen = now;
+ return 0;
}
static int pack_backend__read(
diff --git a/src/pack.h b/src/pack.h
index d15247b..5302db5 100644
--- a/src/pack.h
+++ b/src/pack.h
@@ -102,6 +102,8 @@ struct git_pack_file {
git_pack_cache bases; /* delta base cache */
+ time_t last_freshen; /* last time the packfile was freshened */
+
/* something like ".git/objects/pack/xxxxx.pack" */
char pack_name[GIT_FLEX_ARRAY]; /* more */
};
diff --git a/tests/odb/freshen.c b/tests/odb/freshen.c
index 0f2e0d0..d8d6c02 100644
--- a/tests/odb/freshen.c
+++ b/tests/odb/freshen.c
@@ -68,6 +68,7 @@ void test_odb_freshen__packed_object(void)
cl_must_pass(p_utimes("testrepo.git/objects/pack/" PACKED_FN, old_times));
cl_must_pass(p_lstat("testrepo.git/objects/pack/" PACKED_FN, &before));
+ /* ensure that packfile is freshened */
cl_git_pass(git_odb_write(&id, odb, PACKED_STR,
CONST_STRLEN(PACKED_STR), GIT_OBJ_BLOB));
cl_assert_equal_oid(&expected_id, &id);
@@ -75,5 +76,18 @@ void test_odb_freshen__packed_object(void)
cl_assert(before.st_atime < after.st_atime);
cl_assert(before.st_mtime < after.st_mtime);
+
+ memcpy(&before, &after, sizeof(struct stat));
+
+ /* ensure that the pack file is not freshened again immediately */
+ cl_git_pass(git_odb_write(&id, odb, PACKED_STR,
+ CONST_STRLEN(PACKED_STR), GIT_OBJ_BLOB));
+ cl_assert_equal_oid(&expected_id, &id);
+ cl_must_pass(p_lstat("testrepo.git/objects/pack/" PACKED_FN, &after));
+
+ cl_assert(before.st_atime == after.st_atime);
+ cl_assert(before.st_atime_nsec == after.st_atime_nsec);
+ cl_assert(before.st_mtime == after.st_mtime);
+ cl_assert(before.st_mtime_nsec == after.st_mtime_nsec);
}