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

Re: [PATCH v4 00/32] Lockfile correctness and refactoring

From
Michael Haggerty <mhagger@alum.mit.edu>
Date
Sep 12, 2014, 14:21 UTC
Message-ID
<54130155.90707@alum.mit.edu>
In-Reply-To
<20140910081358.GB16413@peff.net>
On 09/10/2014 10:13 AM, Jeff King wrote:
Show 26 quoted lines
> [...]
> I did coincidentally have an interesting experience with our lockfile
> code earlier today, which I'd like to relate.
> 
> I was running pack-refs on a repository with a very large number of
> loose refs (about 1.8 million). [...] Each call to lock_ref_sha1_basic
> allocates a "struct lock_file", which then gets added to the global
> lock_file list. Each one contains a fixed PATH_MAX buffer (4K on this
> machine). After we're done updating the ref, we leak the lock_file
> struct, since there's no way to remove it from the list.
> 
> As a result, git tried to allocate 7G of RAM and got OOM-killed (the
> machine had only 8G). In addition to thrashing the disk even harder,
> since there was no room left for disk cache while we touched millions of
> loose refs. :)
> 
> Your change in this series to use a strbuf would make this a lot better.
> But I still wonder how hard it would be to just remove lock_file structs
> from the global list when they are committed or rolled back. That would
> presumably also make the "avoid transitory valid states" patch from your
> series a bit easier, too (you could prepare the lockfile in peace, and
> then link it in fully formed, and do the opposite when removing it).
> 
> I think with your strbuf patch, this leak at least becomes reasonable.
> So maybe it's not worth going further. But I'd be interested to hear
> your thoughts since you've been touching the area recently.

I've thought about this too, but it didn't seem to be worth the effort. (Though your use case certainly adds a bit of justification.)

To make that change, we would have to remove entries from the list of lock_file objects in a way that the code can be interrupted at any time by a signal while leaving it in a state that is traversable by the signal handler.

I think that can be done pretty easily with a singly-linked list. But with a singly-linked list, we would have to iterate through the list to find the node that needs to be removed. This could get expensive if there are a lot of nodes in the list (see below).

So we would probably want to switch to using a doubly-linked list. I think this would also be fairly simple, given that the signal handler only needs to iterate through the list in a single direction. You'd just have to be careful about adjusting the pointers in the right order to let (say) a forwards traversal always work.

Then the callers who use heap-allocated lock_file objects would have to be changed to free them when they're done with them, probably using a special function that releases the strbufs, too. Callers using statically-allocated lock_file objects would probably not have to be changed.

But...

The ref-transaction code is, I think, moving in the direction of updating all references in a single transaction. This means that we would need to hold locks for all of the references at once anyway. So it might be all for naught.

Michael
-- 
Michael Haggerty
mhagger@alum.mit.edu
Previous: Michael HaggertyNext: Jeff King
Message 65 of 66 in “Lockfile correctness and refactoring”
  1. 00/32 Lockfile correctness and refactoringMichael Haggerty, Sep 6, 2014
  2. 01/32 unable_to_lock_die(): rename function from unable_to_lock_index_die()Michael Haggerty, Sep 6, 2014
  3. 02/32 api-lockfile: expand the documentationMichael Haggerty, Sep 6, 2014
  4. Junio C HamanoSep 9, 2014
  5. 03/32 rollback_lock_file(): do not clear filename redundantlyMichael Haggerty, Sep 6, 2014
  6. Ronnie SahlbergSep 11, 2014
  7. 04/32 rollback_lock_file(): exit early if lock is not activeMichael Haggerty, Sep 6, 2014
  8. Ronnie SahlbergSep 11, 2014
  9. 05/32 rollback_lock_file(): set fd to -1Michael Haggerty, Sep 6, 2014
  10. 06/32 lockfile: unlock file if lockfile permissions cannot be adjustedMichael Haggerty, Sep 6, 2014
  11. Junio C HamanoSep 9, 2014
  12. Michael HaggertySep 12, 2014
  13. 07/32 hold_lock_file_for_append(): release lock on errorsMichael Haggerty, Sep 6, 2014
  14. Junio C HamanoSep 9, 2014
  15. Michael HaggertySep 12, 2014
  16. 08/32 lock_file(): always add lock_file object to lock_file_listMichael Haggerty, Sep 6, 2014
  17. 09/32 lockfile.c: document the various states of lock_file objectsMichael Haggerty, Sep 6, 2014
  18. Ronnie SahlbergSep 11, 2014
  19. 10/32 cache.h: define constants LOCK_SUFFIX and LOCK_SUFFIX_LENMichael Haggerty, Sep 6, 2014
  20. Ronnie SahlbergSep 11, 2014
  21. Michael HaggertySep 12, 2014
  22. Ronnie SahlbergSep 11, 2014
  23. Michael HaggertySep 12, 2014
  24. Ronnie SahlbergSep 12, 2014
  25. 11/32 delete_ref_loose(): don't muck around in the lock_file's filenameMichael Haggerty, Sep 6, 2014
  26. Johannes SixtSep 13, 2014
  27. Michael HaggertySep 14, 2014
  28. Michael HaggertySep 14, 2014
  29. Johannes SixtSep 14, 2014
  30. 12/32 prepare_index(): declare return value to be (const char *)Michael Haggerty, Sep 6, 2014
  31. 13/32 write_packed_entry_fn(): convert cb_data into a (const int *)Michael Haggerty, Sep 6, 2014
  32. Ronnie SahlbergSep 11, 2014
  33. 14/32 lock_file(): exit early if lockfile cannot be openedMichael Haggerty, Sep 6, 2014
  34. Ronnie SahlbergSep 11, 2014
  35. 15/32 remove_lock_file(): call rollback_lock_file()Michael Haggerty, Sep 6, 2014
  36. 16/32 commit_lock_file(): inline temporary variableMichael Haggerty, Sep 6, 2014
  37. 17/32 commit_lock_file(): die() if called for unlocked lockfile objectMichael Haggerty, Sep 6, 2014
  38. 18/32 commit_lock_file(): if close fails, roll backMichael Haggerty, Sep 6, 2014
  39. 19/32 commit_lock_file(): rollback lock file on failure to renameMichael Haggerty, Sep 6, 2014
  40. Jeff KingSep 10, 2014
  41. Duy NguyenSep 10, 2014
  42. 20/32 api-lockfile: document edge casesMichael Haggerty, Sep 6, 2014
  43. 21/32 dump_marks(): remove a redundant call to rollback_lock_file()Michael Haggerty, Sep 6, 2014
  44. 22/32 git_config_set_multivar_in_file(): avoid call to rollback_lock_file()Michael Haggerty, Sep 6, 2014
  45. 23/32 lockfile: avoid transitory invalid statesMichael Haggerty, Sep 6, 2014
  46. 24/32 struct lock_file: declare some fields volatileMichael Haggerty, Sep 6, 2014
  47. 25/32 try_merge_strategy(): remove redundant lock_file allocationMichael Haggerty, Sep 6, 2014
  48. 26/32 try_merge_strategy(): use a statically-allocated lock_file objectMichael Haggerty, Sep 6, 2014
  49. 27/32 commit_lock_file(): use a strbuf to manage temporary spaceMichael Haggerty, Sep 6, 2014
  50. 28/32 Change lock_file::filename into a strbufMichael Haggerty, Sep 6, 2014
  51. 29/32 resolve_symlink(): use a strbuf for internal scratch spaceMichael Haggerty, Sep 6, 2014
  52. 30/32 resolve_symlink(): take a strbuf parameterMichael Haggerty, Sep 6, 2014
  53. 31/32 trim_last_path_elm(): replace last_path_elm()Michael Haggerty, Sep 6, 2014
  54. 32/32 Extract a function commit_lock_file_to()Michael Haggerty, Sep 6, 2014
  55. Torsten BögershausenSep 7, 2014
  56. Michael HaggertySep 12, 2014
  57. Junio C HamanoSep 8, 2014
  58. Jeff KingSep 10, 2014
  59. Duy NguyenSep 10, 2014
  60. Jeff KingSep 10, 2014
  61. Junio C HamanoSep 10, 2014
  62. Jeff KingSep 10, 2014
  63. Michael HaggertySep 12, 2014
  64. Michael HaggertySep 12, 2014
  65. Michael HaggertySep 12, 2014
  66. Jeff KingSep 13, 2014

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.