diff mbox series

[v2,08/11] refs/files: add a comment about refs_reflog_exists() call

Message ID patch-08.11-1e25b7c59c5-20210716T140631Z-avarab@gmail.com (mailing list archive)
State Superseded
Headers show
Series fix "git reflog expire" race & get rid of EISDIR in refs API | expand

Commit Message

Ævar Arnfjörð Bjarmason July 16, 2021, 2:13 p.m. UTC
Add a comment about why it is that we need to check for the the
existence of a reflog we're deleting after we've successfully acquired
the lock in files_reflog_expire(). As noted in [1] the lock protocol
for reflogs is somewhat intuitive.

This early exit code the comment applies to dates all the way back to
4264dc15e19 (git reflog expire, 2006-12-19).

1. https://lore.kernel.org/git/54DCDA42.2060800@alum.mit.edu/

Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
---
 refs/files-backend.c | 11 +++++++++++
 1 file changed, 11 insertions(+)

Comments

Jeff King July 17, 2021, 2:08 a.m. UTC | #1
On Fri, Jul 16, 2021 at 04:13:04PM +0200, Ævar Arnfjörð Bjarmason wrote:

> Add a comment about why it is that we need to check for the the

s/the the//

> existence of a reflog we're deleting after we've successfully acquired
> the lock in files_reflog_expire(). As noted in [1] the lock protocol
> for reflogs is somewhat intuitive.

Did you mean unintuitive here?

> diff --git a/refs/files-backend.c b/refs/files-backend.c
> index ec9c70d79cc..f73637fa087 100644
> --- a/refs/files-backend.c
> +++ b/refs/files-backend.c
> @@ -3068,6 +3068,17 @@ static int files_reflog_expire(struct ref_store *ref_store,
>  		strbuf_release(&err);
>  		return -1;
>  	}
> +
> +	/*
> +	 * When refs are deleted their reflog is deleted before the
> +	 * ref itself deleted. This race happens because there's no
> +	 * such thing as a lock on the reflog, instead we always lock
> +	 * the "loose ref" (even if packet) above with
> +	 * lock_ref_oid_basic().
> +	 *
> +	 * If race happens we've got nothing more to do, we were asked
> +	 * to delete the reflog, and it's not there anymore. Great!
> +	 */
>  	if (!refs_reflog_exists(ref_store, refname)) {
>  		unlock_ref(lock);
>  		return 0;

I think everything is accurate here, though I wouldn't have made the
distinction with "locking the loose ref". There is no such thing as
locking a packed ref; we always take the loose lock.

s/packet/packed/, and maybe s/If race/If a race/.

-Peff
Junio C Hamano July 19, 2021, 4:43 p.m. UTC | #2
Jeff King <peff@peff.net> writes:

> On Fri, Jul 16, 2021 at 04:13:04PM +0200, Ævar Arnfjörð Bjarmason wrote:
>
>> Add a comment about why it is that we need to check for the the
>
> s/the the//
>
>> existence of a reflog we're deleting after we've successfully acquired
>> the lock in files_reflog_expire(). As noted in [1] the lock protocol
>> for reflogs is somewhat intuitive.
>
> Did you mean unintuitive here?
>
>> diff --git a/refs/files-backend.c b/refs/files-backend.c
>> index ec9c70d79cc..f73637fa087 100644
>> --- a/refs/files-backend.c
>> +++ b/refs/files-backend.c
>> @@ -3068,6 +3068,17 @@ static int files_reflog_expire(struct ref_store *ref_store,
>>  		strbuf_release(&err);
>>  		return -1;
>>  	}
>> +
>> +	/*
>> +	 * When refs are deleted their reflog is deleted before the
>> +	 * ref itself deleted. This race happens because there's no

"... before the ref itself gets deleted", or some verb there.  A
comma in "When refs are deleted, there reflog is ..." would help
making it more readable, too.

>> +	 * such thing as a lock on the reflog, instead we always lock
>> +	 * the "loose ref" (even if packet) above with
>> +	 * lock_ref_oid_basic().
>> +	 *
>> +	 * If race happens we've got nothing more to do, we were asked
>> +	 * to delete the reflog, and it's not there anymore. Great!
>> +	 */
>>  	if (!refs_reflog_exists(ref_store, refname)) {
>>  		unlock_ref(lock);
>>  		return 0;
>
> I think everything is accurate here, though I wouldn't have made the
> distinction with "locking the loose ref". There is no such thing as
> locking a packed ref; we always take the loose lock.
>
> s/packet/packed/, and maybe s/If race/If a race/.

Meaning, there is no such thing as locking a packed ref or a loose
ref, we just lock a "ref" and the way it is done in files backend is
by creating a lockfile as if we are creating/updating a loose one?

I do agree that the second sentence needs rewriting to make it less
confusing.  lock_ref_oid_basic() being the way to lock "a ref" is
not limited to cases where we want to do something to do to the
reflog.

Taking all together, perhaps:

	When refs are deleted, their reflog is deleted before the
	ref itself is deleted.  This is because there is no separate
	lock for reflog---instead we take a lock on the ref with
	lock_ref_oid_basic().

        If a race happens we've got nothing more to do...


>
> -Peff
Jeff King July 20, 2021, 7:22 a.m. UTC | #3
On Mon, Jul 19, 2021 at 09:43:14AM -0700, Junio C Hamano wrote:

> >> +	 * such thing as a lock on the reflog, instead we always lock
> >> +	 * the "loose ref" (even if packet) above with
> >> +	 * lock_ref_oid_basic().
> >> +	 *
> >> +	 * If race happens we've got nothing more to do, we were asked
> >> +	 * to delete the reflog, and it's not there anymore. Great!
> >> +	 */
> >>  	if (!refs_reflog_exists(ref_store, refname)) {
> >>  		unlock_ref(lock);
> >>  		return 0;
> >
> > I think everything is accurate here, though I wouldn't have made the
> > distinction with "locking the loose ref". There is no such thing as
> > locking a packed ref; we always take the loose lock.
> >
> > s/packet/packed/, and maybe s/If race/If a race/.
> 
> Meaning, there is no such thing as locking a packed ref or a loose
> ref, we just lock a "ref" and the way it is done in files backend is
> by creating a lockfile as if we are creating/updating a loose one?

Yes, exactly. We do take a lock on the packed-refs file sometimes, but I
generally think "lock the ref" always means to take refs/heads/foo.lock
in the filesystem. Probably not all that important, but if we need a
re-roll to clarify language anyway...:)

> I do agree that the second sentence needs rewriting to make it less
> confusing.  lock_ref_oid_basic() being the way to lock "a ref" is
> not limited to cases where we want to do something to do to the
> reflog.
> 
> Taking all together, perhaps:
> 
> 	When refs are deleted, their reflog is deleted before the
> 	ref itself is deleted.  This is because there is no separate
> 	lock for reflog---instead we take a lock on the ref with
> 	lock_ref_oid_basic().
> 
>         If a race happens we've got nothing more to do...

Yeah, that reads fine to me.

-Peff
diff mbox series

Patch

diff --git a/refs/files-backend.c b/refs/files-backend.c
index ec9c70d79cc..f73637fa087 100644
--- a/refs/files-backend.c
+++ b/refs/files-backend.c
@@ -3068,6 +3068,17 @@  static int files_reflog_expire(struct ref_store *ref_store,
 		strbuf_release(&err);
 		return -1;
 	}
+
+	/*
+	 * When refs are deleted their reflog is deleted before the
+	 * ref itself deleted. This race happens because there's no
+	 * such thing as a lock on the reflog, instead we always lock
+	 * the "loose ref" (even if packet) above with
+	 * lock_ref_oid_basic().
+	 *
+	 * If race happens we've got nothing more to do, we were asked
+	 * to delete the reflog, and it's not there anymore. Great!
+	 */
 	if (!refs_reflog_exists(ref_store, refname)) {
 		unlock_ref(lock);
 		return 0;