-
Notifications
You must be signed in to change notification settings - Fork 189
reftable: fix quadratic behavior when re-creating deleted refs #2166
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,44 @@ | ||
| #!/bin/sh | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Patrick Steinhardt wrote on the Git mailing list (how to reply to this email): On Mon, Jul 06, 2026 at 01:35:55PM +0000, Kristofer Karlsson via GitGitGadget wrote:
> diff --git a/t/perf/p1401-ref-store-tombstones.sh b/t/perf/p1401-ref-store-tombstones.sh
> new file mode 100755
> index 0000000000..e40a6dcbf4
> --- /dev/null
> +++ b/t/perf/p1401-ref-store-tombstones.sh
> @@ -0,0 +1,44 @@
> +#!/bin/sh
> +
> +test_description="Tests performance of ref operations with many tombstones"
> +
> +. ./perf-lib.sh
> +
> +test_expect_success "setup" '
> + git init --ref-format=reftable repo &&
> + blob=$(echo foo | git -C repo hash-object -w --stdin) &&
> + for i in $(test_seq 8000)
> + do
> + printf "create refs/tags/tag-%d %s\n" "$i" "$blob" ||
> + return 1
> + done >repo/input &&
> + git -C repo update-ref --stdin <repo/input &&
> + git -C repo for-each-ref --format="delete %(refname)" |
> + git -C repo update-ref --stdin
> +'
> +
> +test_perf "recreate refs after mass delete" '
> + git -C repo update-ref --stdin <repo/input &&
> + git -C repo for-each-ref --format="delete %(refname)" |
> + git -C repo update-ref --stdin
> +'
You're not only benchmarking the reference recreation, but also their
deletion. If I'm not misreading things, then you can queue cleanups via
`test_when_finished`, and these calls will not be measured.
> +test_expect_success "setup asymmetric" '
> + for i in $(test_seq 8000)
> + do
> + printf "create refs/tags/old-%d %s\n" "$i" "$blob" ||
> + return 1
> + done >repo/input-old &&
> + sed "s/old-/new-/" <repo/input-old >repo/input-new &&
> + git -C repo update-ref --stdin <repo/input-old &&
> + git -C repo for-each-ref --format="delete %(refname)" |
> + git -C repo update-ref --stdin
> +'
Would it make sense to use separate repositories? Otherwise, state from
the preceding benchmark(s) will impact subsequent ones.
> diff --git a/t/t0610-reftable-basics.sh b/t/t0610-reftable-basics.sh
> index e19e036898..4b7cfe38e4 100755
> --- a/t/t0610-reftable-basics.sh
> +++ b/t/t0610-reftable-basics.sh
> @@ -1163,4 +1163,26 @@ test_expect_success 'writes do not persist peeled value for invalid tags' '
> )
> '
>
> +test_expect_success 'delete and re-create refs with tombstones' '
> + test_when_finished "rm -rf repo" &&
> + git init repo &&
> + test_commit -C repo A &&
> + A=$(git -C repo rev-parse HEAD) &&
> + cat >input <<-EOF &&
> + create refs/tags/a $A
> + create refs/tags/b $A
> + create refs/tags/c $A
> + EOF
> + git -C repo update-ref --stdin <input &&
> +
> + # delete all tags, leaving tombstones
> + git -C repo for-each-ref --format="delete %(refname)" refs/tags/ |
> + git -C repo update-ref --stdin &&
> +
> + # re-create the same refs and verify they are visible
> + git -C repo update-ref --stdin <input &&
> + git -C repo tag -l >actual &&
> + test_line_count = 3 actual
> +'
I wonder whether this test really adds any value. We probably have lots
of tests already that test creation/deletion of references.
PatrickThere was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Kristofer Karlsson wrote on the Git mailing list (how to reply to this email): On Tue, 7 Jul 2026 at 17:24, Patrick Steinhardt <ps@pks.im> wrote:
>
> On Mon, Jul 06, 2026 at 01:35:55PM +0000, Kristofer Karlsson via GitGitGadget wrote:
> > diff --git a/t/perf/p1401-ref-store-tombstones.sh b/t/perf/p1401-ref-store-tombstones.sh
> > new file mode 100755
> > index 0000000000..e40a6dcbf4
> > --- /dev/null
> > +++ b/t/perf/p1401-ref-store-tombstones.sh
> > @@ -0,0 +1,44 @@
> > +#!/bin/sh
> > +
> > +test_description="Tests performance of ref operations with many tombstones"
> > +
> > +. ./perf-lib.sh
> > +
> > +test_expect_success "setup" '
> > + git init --ref-format=reftable repo &&
> > + blob=$(echo foo | git -C repo hash-object -w --stdin) &&
> > + for i in $(test_seq 8000)
> > + do
> > + printf "create refs/tags/tag-%d %s\n" "$i" "$blob" ||
> > + return 1
> > + done >repo/input &&
> > + git -C repo update-ref --stdin <repo/input &&
> > + git -C repo for-each-ref --format="delete %(refname)" |
> > + git -C repo update-ref --stdin
> > +'
> > +
> > +test_perf "recreate refs after mass delete" '
> > + git -C repo update-ref --stdin <repo/input &&
> > + git -C repo for-each-ref --format="delete %(refname)" |
> > + git -C repo update-ref --stdin
> > +'
>
> You're not only benchmarking the reference recreation, but also their
> deletion. If I'm not misreading things, then you can queue cleanups via
> `test_when_finished`, and these calls will not be measured.
I don't think measuring the full create+delete cycle is wrong per se,
but you are right that if we can benchmark something more isolated
is even more useful. I will try to split this up better.
> > +test_expect_success "setup asymmetric" '
> > + for i in $(test_seq 8000)
> > + do
> > + printf "create refs/tags/old-%d %s\n" "$i" "$blob" ||
> > + return 1
> > + done >repo/input-old &&
> > + sed "s/old-/new-/" <repo/input-old >repo/input-new &&
> > + git -C repo update-ref --stdin <repo/input-old &&
> > + git -C repo for-each-ref --format="delete %(refname)" |
> > + git -C repo update-ref --stdin
> > +'
>
> Would it make sense to use separate repositories? Otherwise, state from
> the preceding benchmark(s) will impact subsequent ones.
Agreed, I can use a fresh repo for each scenario.
>
> > diff --git a/t/t0610-reftable-basics.sh b/t/t0610-reftable-basics.sh
> > +test_expect_success 'delete and re-create refs with tombstones' '
>
> I wonder whether this test really adds any value. We probably have lots
> of tests already that test creation/deletion of references.
I could not find an existing test that covers the delete-then-recreate
flow (where tombstones are present when the new refs are created).
The existing tests cover creation and deletion separately but not the
interaction with tombstones.
(But perhaps such a test exists and I just can't find it.)
Thanks,
Kristofer |
||
|
|
||
| test_description="Tests performance of ref operations with many tombstones" | ||
|
|
||
| . ./perf-lib.sh | ||
|
|
||
| test_expect_success "setup" ' | ||
| git init --ref-format=reftable repo && | ||
| blob=$(echo foo | git -C repo hash-object -w --stdin) && | ||
| for i in $(test_seq 8000) | ||
| do | ||
| printf "create refs/tags/tag-%d %s\n" "$i" "$blob" || | ||
| return 1 | ||
| done >repo/input && | ||
| git -C repo update-ref --stdin <repo/input && | ||
| git -C repo for-each-ref --format="delete %(refname)" | | ||
| git -C repo update-ref --stdin | ||
| ' | ||
|
|
||
| test_perf "recreate refs after mass delete" ' | ||
| git -C repo update-ref --stdin <repo/input && | ||
| git -C repo for-each-ref --format="delete %(refname)" | | ||
| git -C repo update-ref --stdin | ||
| ' | ||
|
|
||
| test_expect_success "setup asymmetric" ' | ||
| for i in $(test_seq 8000) | ||
| do | ||
| printf "create refs/tags/old-%d %s\n" "$i" "$blob" || | ||
| return 1 | ||
| done >repo/input-old && | ||
| sed "s/old-/new-/" <repo/input-old >repo/input-new && | ||
| git -C repo update-ref --stdin <repo/input-old && | ||
| git -C repo for-each-ref --format="delete %(refname)" | | ||
| git -C repo update-ref --stdin | ||
| ' | ||
|
|
||
| test_perf "create new refs after deleting differently-named refs" ' | ||
| git -C repo update-ref --stdin <repo/input-new && | ||
| git -C repo for-each-ref --format="delete %(refname)" | | ||
| git -C repo update-ref --stdin | ||
| ' | ||
|
|
||
| test_done | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Patrick Steinhardt wrote on the Git mailing list (how to reply to this email):
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Kristofer Karlsson wrote on the Git mailing list (how to reply to this email):