From a74b8e3dbfe9a0a52ea99fbd00c9ecb94fa30e49 Mon Sep 17 00:00:00 2001 From: "Kyle J. McKay" Date: Mon, 29 Jun 2020 21:54:59 -0700 Subject: [PATCH] jobd/gc.sh: check the "repack" "signature" before skipping gc Compute and check the third and final "signature" hash before actually allowing gc to be skipped (because there's nothing to do). This computation only takes place if the other two "signature" hashes match and the repository is not considered "dirty". Since it's not possible to perform the computation until after `make_repack_dir` has been run, the check to skip is now postponed. As a result, expiration of reflogs, worktrees and rerere information will now continue to take place on a regular basis even when full gc gets skipped (because there was nothing to do). This corrects a longstanding "feature" which only came into play if worktrees, reflogs and or rerere information was in use via a non-Girocco mechanism. For optimization reasons, all refs are also packed before performing the expiration tasks or checking to seek if gc can truly be skipped. As a result the `packed-refs` file in the repository will always get a fresh timestamp on it since Git is not smart enough to leave it alone when `git pack-refs --all` is run and there are no refs to pack regardless of whether or not gc ultimately gets skipped. Since the computation of the "repack" "signature" hash involves a bit more work than the other two "signature" hashes, optimize the initial check of the other two "signature" hashes to not actually do any computations and just disable skipping gc unless all three previous "signature" hash values exist and are non-empty. Signed-off-by: Kyle J. McKay --- jobd/gc.sh | 65 ++++++++++++++++++++++++++++++++++++++++++++++---------------- 1 file changed, 48 insertions(+), 17 deletions(-) diff --git a/jobd/gc.sh b/jobd/gc.sh index 568fa59..84f0c57 100755 --- a/jobd/gc.sh +++ b/jobd/gc.sh @@ -830,7 +830,19 @@ fi # Now check the simple signatures if $skipgc and decline to skip if they do not match if [ -n "$skipgc" ]; then - # check the packs signature first, no previous sig => must run gc + # while we cannot actually compute the current "repack" "signature" + # hash yet, if there is no previous sig => must run gc + rpksig="$(config_get girocco.gcsig.repack)" || : + [ -n "$rpksig" ] || skipgc= +fi +if [ -n "$skipgc" ]; then + # make sure the "refs" "signature" hash exists before doing anything expensive + # no previous "refs" "signagure" => must run gc + rhash="$(config_get girocco.gcsig.refs)" || : + [ -n "$rhash" ] || skipgc= +fi +if [ -n "$skipgc" ]; then + # check the "packs" "signature" first, no previous sig => must run gc phash="$(config_get girocco.gcsig.packs)" || : [ -n "$phash" ] || skipgc= if [ -n "$skipgc" ]; then @@ -841,14 +853,10 @@ if [ -n "$skipgc" ]; then fi fi if [ -n "$skipgc" ]; then - # check the refs signature second, no previous sig => must run gc - rhash="$(config_get girocco.gcsig.refs)" || : - [ -n "$rhash" ] || skipgc= - if [ -n "$skipgc" ]; then - # a previous refs sig is available, check for a match - v_compute_refs_sighash _chksig - [ "$rhash" = "$_chksig" ] || skipgc= - fi + # check the refs signature second + # a previous refs sig is available, check for a match + v_compute_refs_sighash _chksig + [ "$rhash" = "$_chksig" ] || skipgc= fi # Prevent any other simultaneous gc operations @@ -888,13 +896,11 @@ if [ -e .delaygc ]; then fi # Do not skip gc if the repo is dirty -if [ -n "$skipgc" ] && ! is_dirty; then - progress "= [$proj] garbage check nothing but crud removal to do ($(date))" - config_set lastgc "$gcstart" - rm -f "$lockf" - exit 0 -fi +[ -z "$skipgc" ] || ! is_dirty || skipgc= +# It's safe to do this part before checking the "repack" "signature" hash because +# this section won't do anything unless lastparentgc and/or lastreceive is unset +# in which case we never skip gc anyway bumptime= if [ -n "$isfork" ] && [ -z "$lastparentgcsecs" ]; then # set lastparentgc and then update gcstart to be at least 1 second later @@ -911,8 +917,6 @@ if [ -n "$bumptime" ]; then gcstart="$(date "$datefmt")" fi -progress "+ [$proj] garbage check ($(date))" - # ## Safe Pruning In Forks ## @@ -1231,6 +1235,33 @@ fi make_repack_dir +# if we are in a possible $skipgc situation, compute and check the final +# signature hash and possibly exit without actually doing anything more +# if we get this far and $skipgc is true then $rpksig must be non-empty +if [ -n "$skipgc" ]; then + # compute the current value of the "repack" "signature" hash + # it wasn't possible to do this until `make_repack_dir` was run + _chksig="$({ + cat repack/packed-refs + compute_extra_reachables + cat_all_ref_logs + } | sig_hash_stdin)" || : + [ "$rpksig" = "$_chksig" ] || skipgc= +fi + +if [ -n "$skipgc" ]; then + # remove no longer needed repack and objects/pack/repack dirs and contents + # while not free to create them, it's cheaper to create and destroy + # them than to do an actual full gc on anything but a tiny project + rm -rf repack objects/pack/repack + progress "= [$proj] garbage check nothing but crud removal to do ($(date))" + config_set lastgc "$gcstart" + rm -f "$lockf" + exit 0 +fi + +progress "+ [$proj] garbage check ($(date))" + newdeltas= [ -z "$alwaysredelta" ] || newdeltas="$noreusedeltaopt" if [ -z "$newdeltas" ] && [ -n "$gfi_mirror" ]; then -- 2.11.4.GIT