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

Re: [PATCH/RFC 0/3] using stat() to avoid re-scanning pack dir

From
Jeff King <peff@peff.net>
Date
May 24, 2015, 08:20 UTC
Message-ID
<20150524082016.GA8718@peff.net>
In-Reply-To
<CACsJy8BjFM_OecoVU9DV3GmJafatSR2yPt6Xb6dETEpYjc1ODA@mail.gmail.com>
On Sat, May 23, 2015 at 08:19:03AM +0700, Duy Nguyen wrote:
Show 13 quoted lines
> On Sat, May 23, 2015 at 6:51 AM, Jeff King <peff@peff.net> wrote:
> > The other problem is that I'm not sure stat data is enough to notice
> > when a directory changes. Certainly the mtime should change, but if you
> > have only one-second resolution on your mtimes, we can be fooled.
> 
> mtime may or may not change. I based my untracked-cache series
> entirely on this directory mtime and did some research about it. For
> UNIXy filesystems on UNIXy OSes, mtime should work as you expect. FAT
> on Windows does not (but FAT on Linux does..). NTFS works fine
> according to some MS document. No idea about CIFS. But people often
> just do open operation of a time and this racy is not an issue. It is
> only an issue on the busy server side, and you can make sure you run
> on the right fs+OS.
Even on Linux+ext4, I think there is some raciness.

For instance, the program at the end of this mail will loop forever, running "stat" on an open directory fd, then enumerating the entries in the directory. If we ever get the same stat data as the last iteration but different contents, then it complains. If you run it alongside something simple, like touching 10,000 files in the directory, it fails pretty quickly.

This is because we have no atomicity guarantees with directories. We can stat() them, and then while we are reading them, somebody else can be changing the entries. Whether we see the "before" or "after" state depends on the timing.

I'm not 100% sure this translates into real-world problems for packfiles. If you notice that an object is missing and re-scan the pack directory (stat-ing it during the re-scan), then the change that made the object go missing must have happened _before_ the stat, and would presumably be reflected in it (modulo mtime resolution issues). But I haven't thought that hard about it yet, and I have a nagging feeling that there will be problem cases.

It might be that you could get an "atomic" read of the directory by doing a stat before and after and making sure they're the same (and if not, repeating the readdir() calls). But I think that suffers from the same mtime-resolution problems.

Linux+ext4 _does_ have nanosecond mtimes, which perhaps is enough to assume that any change will be reflected.

I dunno. I guess the most interesting test would be something closer to the real world: one process repeatedly making sure the object pointed to by "master" exists, and another one constantly rewriting "master" and then repacking the object.

-- >8 -- #include "cache.h" #include "string-list.h"

static void get_data(const char *path, struct string_list *entries,
		     struct stat_validity *validity)
{
	DIR *dir = opendir(path);
	struct dirent *de;
	stat_validity_update(validity, dirfd(dir));
	while ((de = readdir(dir)))
		string_list_insert(entries, de->d_name);
	closedir(dir);
}
static int list_eq(const struct string_list *a,
		    const struct string_list *b)
{
	int i;
	if (a->nr != b->nr)
		return 0;
	for (i = 0; i < a->nr; i++)
		if (strcmp(a->items[i].string, b->items[i].string))
			return 0;
	return 1;
}
static void monitor(const char *path)
{
	struct string_list last_entries = STRING_LIST_INIT_DUP;
	struct stat_validity last_validity;
	get_data(path, &last_entries, &last_validity);
	while (1) {
		struct string_list cur_entries = STRING_LIST_INIT_DUP;
		struct stat_validity cur_validity;
		get_data(path, &cur_entries, &cur_validity);
		if (!memcmp(&last_validity, &cur_validity, sizeof(last_validity)) &&
		    !list_eq(&cur_entries, &last_entries))
			die("mismatch");
		string_list_clear(&last_entries, 0);
		memcpy(&last_entries, &cur_entries, sizeof(last_entries));
		stat_validity_clear(&last_validity);
		memcpy(&last_validity, &cur_validity, sizeof(last_validity));
	}
}
int main(int argc, const char **argv)
{
	monitor(*++argv);
	return 0;
}
Previous: Duy NguyenNext: Duy Nguyen
Message 20 of 32 in “Troubleshoot clone issue to NFS.”
  1. steve.norman@thomsonreuters.comMay 21, 2015
  2. Christian CouderMay 21, 2015
  3. Duy NguyenMay 21, 2015
  4. Duy NguyenMay 21, 2015
  5. steve.norman@thomsonreuters.comMay 21, 2015
  6. Duy NguyenMay 22, 2015
  7. Jeff KingMay 22, 2015
  8. steve.norman@thomsonreuters.comMay 22, 2015
  9. Duy NguyenMay 22, 2015
  10. Junio C HamanoMay 22, 2015
  11. steve.norman@thomsonreuters.comMay 22, 2015
  12. 0/3 using stat() to avoid re-scanning pack dirJeff King, May 22, 2015
  13. 1/3 stat_validity: handle non-regular filesJeff King, May 22, 2015
  14. Michael HaggertyMay 23, 2015
  15. Jeff KingMay 24, 2015
  16. 2/3 cache.h: move stat_validity definition upJeff King, May 22, 2015
  17. 3/3 prepare_packed_git: use stat_validity to avoid re-reading packsJeff King, May 22, 2015
  18. Duy NguyenMay 23, 2015
  19. Duy NguyenMay 23, 2015
  20. Jeff KingMay 24, 2015
  21. Duy NguyenMay 24, 2015
  22. steve.norman@thomsonreuters.comJun 5, 2015
  23. Jeff KingJun 5, 2015
  24. index-pack: avoid excessive re-reading of pack directoryJeff King, Jun 5, 2015
  25. Jeff KingJun 9, 2015
  26. Jeff KingJun 9, 2015
  27. Shawn PearceJun 10, 2015
  28. Jeff KingJun 10, 2015
  29. Duy NguyenJun 10, 2015
  30. Shawn PearceJun 10, 2015
  31. steve.norman@thomsonreuters.comJun 5, 2015
  32. Jeff KingJun 16, 2015

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.