ostree/prune: Calculate reachability under exclusive lock
authorJonathan Lebon <jonathan@jlebon.com>
Mon, 30 Jan 2023 18:45:50 +0000 (13:45 -0500)
committerJonathan Lebon <jonathan@jlebon.com>
Mon, 30 Jan 2023 20:08:27 +0000 (15:08 -0500)
When we calculate the reachability set in `ostree prune`, we do this
without any locking. This means that between the time we build the set
and when we call `ostree_repo_prune_from_reachable`, new content
might've been added. This then causes us to immediately prune that
content since it's not in the now outdated set.

Fix this by calculating the set under an exclusive lock.

I think this is what happened in
https://github.com/fedora-silverblue/issue-tracker/issues/405. While
the pruner was running, the `new-updates-sync` script[1] was importing
content into the repo. The newly imported commits were immediately
deleted by the many `ostree prune --commit-only` calls the pruner does,
breaking the refs.

[1] https://pagure.io/fedora-infra/ansible/blob/35b35127e444/f/roles/bodhi2/backend/files/new-updates-sync#_18

src/ostree/ot-builtin-prune.c
tests/test-prune.sh

index 73dfe5e5da24ef7bfbac3ffdf7b496e4402e8d9d..0b4e8698a69261b6f4bd296888fbc4405c448650 100644 (file)
@@ -208,6 +208,15 @@ ostree_builtin_prune (int argc, char **argv, OstreeCommandInvocation *invocation
     }
   else
     {
+      /* In this branch, we need to compute the reachability set manually.
+       * While we do this, we can't let new content in since it'll race with
+       * reachability calculations and we may immediately nuke it. So push an
+       * exclusive lock now. */
+      g_autoptr(OstreeRepoAutoLock) lock =
+        ostree_repo_auto_lock_push (repo, OSTREE_REPO_LOCK_EXCLUSIVE, cancellable, error);
+      if (!lock)
+        return FALSE;
+
       g_autoptr(GHashTable) all_refs = NULL;
       g_autoptr(GHashTable) reachable = ostree_repo_traverse_new_reachable ();
       g_autoptr(GHashTable) retain_branch_depth = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
index bbb77a23de8b5d69b3efead9321426054d925d4d..0e628405d59fca8ef1aa5b6bbb682bba66225575 100755 (executable)
@@ -363,4 +363,16 @@ ${CMD_PREFIX} ostree --repo=repo prune --commit-only --keep-younger-than="1 week
 assert_repo_has_n_commits repo 4
 assert_repo_has_n_non_commit_objects repo ${orig_obj_count}
 tap_ok --commit-only and --keep-younger-than
+
+reinitialize_commit_only_test_repo
+for i in {1..10}; do
+    ${CMD_PREFIX} ostree --repo=repo prune --commit-only --keep-younger-than="1 week ago" &
+    commit=$(${CMD_PREFIX} ostree --repo=repo commit --branch foobar tree)
+    wait $!
+    if ! ostree show --repo=repo ${commit}; then
+        assert_not_reached "commit ${commit} on branch foobar was pruned?"
+    fi
+done
+tap_ok commit and prune together
+
 tap_end