@@ -219,6 +219,43 @@ static void fsmonitor_refresh_callback(struct index_state *istate, char *name)
untracked_cache_invalidate_path(istate, name, 0);
}
+/*
+ * The number of pathnames that we need to receive from FSMonitor
+ * before we force the index to be updated.
+ *
+ * Note that any pathname within the set of received paths MAY cause
+ * cache-entry or istate flag bits to be updated and thus cause the
+ * index to be updated on disk.
+ *
+ * However, the response may contain many paths (such as ignored
+ * paths) that will not update any flag bits. And thus not force the
+ * index to be updated. (This is fine and normal.) It also means
+ * that the token will not be updated in the FSMonitor index
+ * extension. So the next Git command will find the same token in the
+ * index, make the same token-relative request, and receive the same
+ * response (plus any newly changed paths). If this response is large
+ * (and continues to grow), performance could be impacted.
+ *
+ * For example, if the user runs a build and it writes 100K object
+ * files but doesn't modify any source files, the index would not need
+ * to be updated. The FSMonitor response (after the build and
+ * relative to a pre-build token) might be 5MB. Each subsequent Git
+ * command will receive that same 100K/5MB response until something
+ * causes the index to be updated. And `refresh_fsmonitor()` will
+ * have to iterate over those 100K paths each time.
+ *
+ * Performance could be improved if we optionally force update the
+ * index after a very large response and get an updated token into
+ * the FSMonitor index extension. This should allow subsequent
+ * commands to get smaller and more current responses.
+ *
+ * The value chosen here does not need to be precise. The index
+ * will be updated automatically the first time the user touches
+ * a tracked file and causes a command like `git status` to
+ * update an mtime to be updated and/or set a flag bit.
+ */
+static int fsmonitor_force_update_threshold = 100;
+
void refresh_fsmonitor(struct index_state *istate)
{
struct strbuf query_result = STRBUF_INIT;
@@ -362,25 +399,39 @@ apply_results:
* information and that we should consider everything
* invalid. We call this a trivial response.
*/
+ trace2_region_enter("fsmonitor", "apply_results", istate->repo);
+
if (query_success && !is_trivial) {
/*
* Mark all pathnames returned by the monitor as dirty.
*
* This updates both the cache-entries and the untracked-cache.
*/
+ int count = 0;
+
buf = query_result.buf;
for (i = bol; i < query_result.len; i++) {
if (buf[i] != '\0')
continue;
fsmonitor_refresh_callback(istate, buf + bol);
bol = i + 1;
+ count++;
}
- if (bol < query_result.len)
+ if (bol < query_result.len) {
fsmonitor_refresh_callback(istate, buf + bol);
+ count++;
+ }
/* Now mark the untracked cache for fsmonitor usage */
if (istate->untracked)
istate->untracked->use_fsmonitor = 1;
+
+ if (count > fsmonitor_force_update_threshold)
+ istate->cache_changed |= FSMONITOR_CHANGED;
+
+ trace2_data_intmax("fsmonitor", istate->repo, "apply_count",
+ count);
+
} else {
/*
* We failed to get a response or received a trivial response,
@@ -409,6 +460,8 @@ apply_results:
if (istate->untracked)
istate->untracked->use_fsmonitor = 0;
}
+ trace2_region_leave("fsmonitor", "apply_results", istate->repo);
+
strbuf_release(&query_result);
/* Now that we've updated istate, save the last_update_token */