git/list[1] front-page[2] threads[3] people[4] search[5] about
 

Re: [PATCH v5 04/13] csum-file: add CSUM_KEEP_OPEN flag

From
Junio C Hamano <gitster@pobox.com>
Date
Mar 13, 2018, 21:42 UTC
Message-ID
<xmqqsh931wl1.fsf@gitster-ct.c.googlers.com>
In-Reply-To
<e1264d9a-9d6c-b294-6d56-46382a28b280@gmail.com>
Derrick Stolee <stolee@gmail.com> writes:
Show 12 quoted lines
> On 2/26/2018 9:32 PM, Derrick Stolee wrote:
>> This patch is new to the series due to the interactions with the lockfile API
>> and the hashfile API. I need to ensure the hashfile writes the hash value at
>> the end of the file, but keep the file descriptor open so the lock is valid.
>>
>> I welcome any susggestions to this patch or to the way I use it in the commit
>> that follows.
>>
>> -- >8 --
>
> I haven't gotten any feedback on this step of the patch. Could someone
> take a look and let me know what you think?
Let's follow the commit-graph writing codepath to see what happens:
	fd = hold_lock_file_for_update(&lk, graph_name, 0);
	...
	f = hashfd(lk.tempfile->fd, lk.tempfile->filename.buf);

The caller creates a lockfile, and then wraps its file descriptor in a hashfile.

	hashwrite_be32(f, GRAPH_SIGNATURE);
	...
Then it goes on writing to the hashfile, growing the lockfile.
        ...
	write_graph_chunk_large_edges(f, commits.list, commits.nr);
	close_commit_graph();

And after writing all data out (oh by the way, why aren't we passing commit_graph instance around and instead relying on a file-scope static global?)...

	hashclose(f, final_hash, CSUM_CLOSE | CSUM_FSYNC | CSUM_KEEP_OPEN);

We ask for the final hash value to be written to the file (and also returned to us---although you do not seem to use that at all). See a comment on this, though, at the end.

	commit_lock_file(&lk);

And then, we put the lockfile to its final place, while closing its file descriptor.

The overall API sounds sensible, from the above.
However.

The function whose name is hashclose() that takes a flag word whose possible bit value includes "Please close this thing" feels strange enough (does it mean the hashclose() function does not close it if CSUM_CLOSE is not given?), but adding another to the mix that lets us say "Please close this (with or without FSYNC), oh by the way please leave it open" feels a bit borderline to insanity.

I _think_ the word "close" in the name hashclose() is about closing the (virtual) stream for the hashing that is overlayed on top of the underlying file descriptor, and being able to choose between closing and not closing the underlying file descriptor when "closing" the hashing layer sort of makes sense. So I won't complain too much about hashclose() that takes optional CSUM_CLOSE flag.

But then what does it mean to give KEEP_OPEN and CLOSE together?

The new caller (which is the only one that wants the nominally nonsensical CLOSE|KEEP_OPEN combination, which is shown above) wants the final checksum of the data sent over the (virtual) stream computed and written, and the file descriptor fsync'ed, but the file descriptor kept open. As we _DO_ want to keep the verbs in flags CSUM_CLOSE and CSUM_FSYNC to be about the underlying file descriptor, I think your new code for KEEP_OPEN that is inside the if() block that is for CSUM_CLOSE is an ugly hack, and your asking for improvements is very much appreciated.

Let's step back and see what different behaviours the existing code wants to support before your patch:

    - hashclose() is always about finializing the hash computation
      over the data sent through the struct hashfile (i.e. the
      virtual stream opened by hashfd()).  The optional *result can
      be used to receive this hash value, even when the caller does
      not want to write that hash value to the output stream.
    - when CSUM_CLOSE is given, however, the hash value is written
      out as the trailing record to the output stream and the stream
      is closed.  CSUM_FSYNC can instead be used to ensure that the
      data hits the disk platter when the output stream is closed.
    - when CSUM_CLOSE nor CSUM_FSYNC is not given, hash value is not
      written to the output stream (the caller takes responsibility
      of using *result), and the output stream is left open.

I think the first mistake in the existing code is to associate "close the underlying stream" and "write the hash out to the underlying stream" more closely than it should. It should be possible to "close the underlying steam" without first writing the hash out to the underlying stream", and vice versa.

IOW, I think
        hashclose() {
                hashflush();
                the_hash_algo->final_fn();
                if (result)             
                        hashcpy(result, f->buffer);
        +       if (flags & CSUM_HASH_IN_STREAM)
        +               flush(f, f->buffer, the_hash_algo->rawsz);
        +       if (flags & CSUM_FSYNC)
        +               fsync_or_die();
                if (flags & (CSUM_CLOSE | CSUM_FSYNC)) {
        -               flush();                
        -               if (flags & CSUM_FSYNC)
        -                       fsync_or_die();
                        if (close(f->fd))
                                die_errno();
                        fd = 0;
                } else
                        fd = f->fd;
                if (0 <= f->check_fd) {
                        ...
                }
                free(f);
                return fd;
        }
with would be a good first "preliminary preparation" step.

Existing callers that pass CSUM_FSYNC or CSUM_CLOSE now need to also say "I want the resulting hash in the output stream", but that allows your later caller to omit CSUM_CLOSE and then ask for HASH_IN_STREAM alone.

Existing callers can expect that FSYNC alone means fsync and close, but your caller wants hashclose() to compute the hash, write the hash to the output stream, and fsync the output stream, and return without closing the output stream. For that, you'd make FSYNC not to imply CLOSE, and you'd need to vet all the existing callers that use FSYNC are OK with such a change. And then the above would become

        hashclose() {
                hashflush();
                the_hash_algo->final_fn();
                if (result)             
                        hashcpy(result, f->buffer);
                if (flags & CSUM_HASH_IN_STREAM)
                        flush(f, f->buffer, the_hash_algo->rawsz);
                if (flags & CSUM_FSYNC)
                        fsync_or_die();
                if (flags & CSUM_CLOSE) {
                        if (close(f->fd))
                                die_errno();
                        fd = 0;
                } else
                        fd = f->fd;
                if (0 <= f->check_fd) {
                        ...
                }
                free(f);
                return fd;
        }

Once we reach that state, the new caller in write_commit_graph() does not have to pass nonsensical CLOSE|KEEP_OPEN combination. Instead we can do

	hashclose(f, NULL, CSUM_HASH_IN_STREAM | CSUM_FSYNC);
or something like that, I would think, without having KEEP_OPEN.

I am actually wondering if it is worth making CSUM_FSYNC not imply CSUM_CLOSE. There aren't that many existing callers of hashclose() that uses FSYNC, so vetting all of them and replacing their FSYNC with (FSYNC|CLOSE) is not all that difficult, but if this new caller is an oddball then another strategy may be to do the fsync_or_die() on the caller side, something like:

                hashclose(f, NULL, CSUM_HASH_IN_STREAM);
        +       fsync_or_die(fd, get_lock_file_path(&lk));
                commit_lock_file(&lk);

And then we can keep the "FSYNC means fsync and then close" the current set of callers rely on. I dunno if that is a major issue, but I do think "close this, or no, keep it open" is far worse than "do we want the resulting hash in the stream?"

An alternative design of the above is without making CSUM_HASH_IN_STREAM a new flag bit. I highly suspect that the calling codepath _knows_ whether the resulting final hash will be written out at the end of the stream or not when it wraps an fd with a hashfile structure, so "struct hashfile" could gain a bit to tell hashclose() whether the resulting hash need to be written (or not). That would be a bit larger change than what I outlined above, and I do not know if it is worth doing, though.

Previous: Derrick StoleeNext: Derrick Stolee
Message 6 of 110 in “Serialized Git Commit Graph”
  1. 00/13 Serialized Git Commit GraphDerrick Stolee, Feb 27, 2018
  2. 01/13 commit-graph: add format documentDerrick Stolee, Feb 27, 2018
  3. 02/13 graph: add commit graph design documentDerrick Stolee, Feb 27, 2018
  4. 04/13 csum-file: add CSUM_KEEP_OPEN flagDerrick Stolee, Feb 27, 2018
  5. Derrick StoleeMar 12, 2018
  6. Junio C HamanoMar 13, 2018
  7. Derrick StoleeMar 14, 2018
  8. Junio C HamanoMar 14, 2018
  9. 06/13 commit-graph: implement 'git-commit-graph write'Derrick Stolee, Feb 27, 2018
  10. 05/13 commit-graph: implement write_commit_graph()Derrick Stolee, Feb 27, 2018
  11. 03/13 commit-graph: create git-commit-graph builtinDerrick Stolee, Feb 27, 2018
  12. 13/13 commit-graph: implement "--additive" optionDerrick Stolee, Feb 27, 2018
  13. 12/13 commit-graph: build graph from starting commitsDerrick Stolee, Feb 27, 2018
  14. 11/13 commit-graph: read only from specific pack-indexesDerrick Stolee, Feb 27, 2018
  15. Stefan BellerFeb 27, 2018
  16. 10/13 commit: integrate commit graph with commit parsingDerrick Stolee, Feb 27, 2018
  17. 07/13 commit-graph: implement git commit-graph readDerrick Stolee, Feb 27, 2018
  18. 08/13 commit-graph: add core.commitGraph settingDerrick Stolee, Feb 27, 2018
  19. 09/13 commit-graph: close under reachabilityDerrick Stolee, Feb 27, 2018
  20. Stefan BellerFeb 27, 2018
  21. 00/14 Serialized Git Commit GraphDerrick Stolee, Mar 14, 2018
  22. 02/14 csum-file: refactor finalize_hashfile() methodDerrick Stolee, Mar 14, 2018
  23. 03/14 commit-graph: add format documentDerrick Stolee, Mar 14, 2018
  24. 04/14 graph: add commit graph design documentDerrick Stolee, Mar 14, 2018
  25. 08/14 commit-graph: implement git commit-graph readDerrick Stolee, Mar 14, 2018
  26. 06/14 commit-graph: implement write_commit_graph()Derrick Stolee, Mar 14, 2018
  27. 10/14 commit-graph: close under reachabilityDerrick Stolee, Mar 14, 2018
  28. 12/14 commit-graph: read only from specific pack-indexesDerrick Stolee, Mar 14, 2018
  29. SZEDER GáborMar 15, 2018
  30. Derrick StoleeMar 19, 2018
  31. 09/14 commit-graph: add core.commitGraph settingDerrick Stolee, Mar 14, 2018
  32. 11/14 commit: integrate commit graph with commit parsingDerrick Stolee, Mar 14, 2018
  33. 13/14 commit-graph: build graph from starting commitsDerrick Stolee, Mar 14, 2018
  34. 14/14 commit-graph: implement "--additive" optionDerrick Stolee, Mar 14, 2018
  35. 05/14 commit-graph: create git-commit-graph builtinDerrick Stolee, Mar 14, 2018
  36. 07/14 commit-graph: implement 'git-commit-graph write'Derrick Stolee, Mar 14, 2018
  37. Ævar Arnfjörð BjarmasonMar 18, 2018
  38. Derrick StoleeMar 19, 2018
  39. Ævar Arnfjörð BjarmasonMar 19, 2018
  40. Derrick StoleeMar 19, 2018
  41. Ævar Arnfjörð BjarmasonMar 19, 2018
  42. 01/14 csum-file: rename hashclose() to finalize_hashfile()Derrick Stolee, Mar 14, 2018
  43. Ramsay JonesMar 14, 2018
  44. Junio C HamanoMar 14, 2018
  45. Johannes SchindelinMar 15, 2018
  46. Junio C HamanoMar 15, 2018
  47. Ramsay JonesMar 15, 2018
  48. Johannes SchindelinMar 16, 2018
  49. Junio C HamanoMar 16, 2018
  50. Johannes SchindelinMar 19, 2018
  51. Lars SchneiderMar 16, 2018
  52. Derrick StoleeMar 19, 2018
  53. Ævar Arnfjörð BjarmasonMar 16, 2018
  54. SZEDER GáborMar 16, 2018
  55. Junio C HamanoMar 16, 2018
  56. SZEDER GáborMar 16, 2018
  57. Jeff KingMar 16, 2018
  58. Jeff KingMar 16, 2018
  59. Derrick StoleeMar 19, 2018
  60. Derrick StoleeMar 20, 2018
  61. Jeff KingMar 16, 2018
  62. 00/14 Serialized Git Commit GraphDerrick Stolee, Apr 2, 2018
  63. 01/14 csum-file: rename hashclose() to finalize_hashfile()Derrick Stolee, Apr 2, 2018
  64. 02/14 csum-file: refactor finalize_hashfile() methodDerrick Stolee, Apr 2, 2018
  65. Jakub NarebskiApr 7, 2018
  66. 03/14 commit-graph: add format documentDerrick Stolee, Apr 2, 2018
  67. Jakub NarebskiApr 7, 2018
  68. 04/14 graph: add commit graph design documentDerrick Stolee, Apr 2, 2018
  69. Jakub NarebskiApr 8, 2018
  70. 06/14 commit-graph: implement write_commit_graph()Derrick Stolee, Apr 2, 2018
  71. 11/14 commit: integrate commit graph with commit parsingDerrick Stolee, Apr 2, 2018
  72. 13/14 commit-graph: build graph from starting commitsDerrick Stolee, Apr 2, 2018
  73. Jakub NarebskiApr 8, 2018
  74. 14/14 commit-graph: implement "--additive" optionDerrick Stolee, Apr 2, 2018
  75. SZEDER GáborApr 5, 2018
  76. 10/14 commit-graph: close under reachabilityDerrick Stolee, Apr 2, 2018
  77. 12/14 commit-graph: read only from specific pack-indexesDerrick Stolee, Apr 2, 2018
  78. 09/14 commit-graph: add core.commitGraph settingDerrick Stolee, Apr 2, 2018
  79. Jakub NarebskiApr 8, 2018
  80. 05/14 commit-graph: create git-commit-graph builtinDerrick Stolee, Apr 2, 2018
  81. 07/14 commit-graph: implement git-commit-graph writeDerrick Stolee, Apr 2, 2018
  82. Jakub NarebskiApr 8, 2018
  83. 08/14 commit-graph: implement git commit-graph readDerrick Stolee, Apr 2, 2018
  84. Junio C HamanoApr 2, 2018
  85. Derrick StoleeApr 3, 2018
  86. Jakub NarebskiApr 8, 2018
  87. 00/14 Serialized Git Commit GraphDerrick Stolee, Apr 10, 2018
  88. 01/14 csum-file: rename hashclose() to finalize_hashfile()Derrick Stolee, Apr 10, 2018
  89. 02/14 csum-file: refactor finalize_hashfile() methodDerrick Stolee, Apr 10, 2018
  90. 03/14 commit-graph: add format documentDerrick Stolee, Apr 10, 2018
  91. Stefan BellerApr 10, 2018
  92. Derrick StoleeApr 10, 2018
  93. Jakub NarebskiApr 11, 2018
  94. Derrick StoleeApr 12, 2018
  95. Jakub NarebskiApr 13, 2018
  96. 04/14 graph: add commit graph design documentDerrick Stolee, Apr 10, 2018
  97. Jakub NarebskiApr 15, 2018
  98. 05/14 commit-graph: create git-commit-graph builtinDerrick Stolee, Apr 10, 2018
  99. 07/14 commit-graph: implement git-commit-graph writeDerrick Stolee, Apr 10, 2018
  100. 08/14 commit-graph: implement git commit-graph readDerrick Stolee, Apr 10, 2018
  101. Jakub NarebskiApr 14, 2018
  102. Eric SunshineApr 15, 2018
  103. 09/14 commit-graph: add core.commitGraph settingDerrick Stolee, Apr 10, 2018
  104. Jakub NarebskiApr 14, 2018
  105. 06/14 commit-graph: implement write_commit_graph()Derrick Stolee, Apr 10, 2018
  106. 10/14 commit-graph: close under reachabilityDerrick Stolee, Apr 10, 2018
  107. 11/14 commit: integrate commit graph with commit parsingDerrick Stolee, Apr 10, 2018
  108. 12/14 commit-graph: read only from specific pack-indexesDerrick Stolee, Apr 10, 2018
  109. 13/14 commit-graph: build graph from starting commitsDerrick Stolee, Apr 10, 2018
  110. 14/14 commit-graph: implement "--append" optionDerrick Stolee, Apr 10, 2018

Read the whole thread, see it on lore, or plain text.

$ cat FOOTERMessages come from the public archive at lore.kernel.org/git, fetched every hour. The front page is chosen and written each morning by an AI editor and can be wrong; the threads themselves are the record. About and API. For agents: an MCP server at https://gitlist.dev/mcp, and any thread, story or person page as Markdown by adding .md to its URL (or sending Accept: text/markdown). Details in /llms.txt.