Skip to content

Commit 01a6f43

Browse files
quark-zjufacebook-github-bot
authored andcommitted
fsmonitor: wait for watchman full crawl and show progress
Summary: If watchman is in full crawl, it probably won't respond in time, wait for it instead of falling back to a non-watchman crawl. As we're here, show a progress bar so users have some expectation about what's going on. Reviewed By: jordanwebster Differential Revision: D37473141 fbshipit-source-id: 979760d3bd9ab5d8a222ade5555acdcf66995b10
1 parent 36fffe9 commit 01a6f43

File tree

2 files changed

+98
-0
lines changed

2 files changed

+98
-0
lines changed

eden/scm/edenscm/hgext/extlib/watchmanclient/__init__.py

Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,9 +11,11 @@
1111
import getpass
1212
import os
1313
import sys
14+
import time
1415

1516
from bindings import tracing
1617
from edenscm.mercurial import blackbox, encoding, json, progress, pycompat, util
18+
from edenscm.mercurial.i18n import _
1719
from edenscm.mercurial.node import hex
1820

1921
from .. import pywatchman
@@ -101,6 +103,7 @@ def __init__(self, repo, timeout=1.0):
101103
self._resolved_root = getcanonicalpath(self._root)
102104
self._ui = repo.ui
103105
self._firsttime = True
106+
self._approx_total_file_count = len(repo.dirstate._map)
104107

105108
def settimeout(self, timeout):
106109
self._timeout = timeout
@@ -197,9 +200,97 @@ def _retrycommand(self, span, retry, *args):
197200
if needretry:
198201
return self._retrycommand(span, retry + 1, *args)
199202

203+
def debug_status(self):
204+
"""Return the RootDebugStatus, which might look like:
205+
206+
{
207+
"recrawl_info": {
208+
"count": 0,
209+
"should-recrawl": true,
210+
"warning": null,
211+
"reason": "startup",
212+
"completed": null,
213+
"started": -20162,
214+
"stats": 295541
215+
}
216+
"crawl-status": "crawling for ...",
217+
"enable_parallel_crawl": false,
218+
"cookie_list": [],
219+
"path": "...",
220+
"queries": [],
221+
"fstype": "btrfs",
222+
"cookie_prefix": ["..."],
223+
"watcher": "inotify",
224+
"uptime": ...,
225+
"done_initial": false,
226+
"cookie_dir": [".../.hg"],
227+
"case_sensitive": true,
228+
"cancelled": false,
229+
}
230+
231+
Return an empty dict if watchman does not support debug-root-status.
232+
"""
233+
try:
234+
# use _command to bypass progress and util.timefuntion
235+
root_status = self._command("debug-root-status")["root_status"]
236+
except (Unavailable, AttributeError):
237+
# watchman does not support this command
238+
root_status = {}
239+
return root_status
240+
241+
def recrawl_info(self):
242+
"""Return the RootRecrawlInfo in the RootDebugStatus.
243+
244+
Return an empty dict if watchman does not support getting the recrawl
245+
info.
246+
"""
247+
debug_status = self.debug_status()
248+
info = {}
249+
try:
250+
info = debug_status["recrawl_info"]
251+
except AttributeError:
252+
pass
253+
return info
254+
255+
def recrawl_stat_count(self):
256+
"""Return the count of files stat()-ed by watchman during a full crawl
257+
Return None if watchman does not provide the information, or watchman
258+
is not in a full crawl state.
259+
"""
260+
stats = None
261+
try:
262+
stats = self.recrawl_info()["stats"]
263+
except AttributeError:
264+
pass
265+
return stats
266+
267+
def wait_for_full_crawl(self):
268+
"""Wait for watchman to complete a full recrawl. Blocking.
269+
Show a progress bar.
270+
"""
271+
ui = self._ui
272+
if not ui.configbool("fsmonitor", "wait-full-crawl"):
273+
return
274+
275+
stats = self.recrawl_stat_count()
276+
if stats is None:
277+
# Not in full recrawl
278+
return
279+
280+
# Show progress bar.
281+
total = self._approx_total_file_count
282+
with progress.bar(ui, _("crawling"), _("files (approx)"), total) as prog:
283+
while stats is not None:
284+
prog.value = stats
285+
stats = self.recrawl_stat_count()
286+
if stats is not None:
287+
time.sleep(0.1)
288+
200289
@util.timefunction("watchmanquery", 0, "_ui")
201290
def command(self, *args, **kwargs):
202291
ignoreerrors = kwargs.get("ignoreerrors", False)
292+
if args and args[0] in {"clock", "query"}:
293+
self.wait_for_full_crawl()
203294
with progress.spinner(self._ui, "querying watchman"):
204295
try:
205296
try:

eden/scm/edenscm/hgext/fsmonitor/__init__.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -152,6 +152,12 @@
152152
153153
If set to true then take a lock when running watchman queries to avoid
154154
overloading watchman.
155+
156+
::
157+
[fsmonitor]
158+
wait-full-crawl = true
159+
160+
If set, wait for watchman to complete a full crawl before performing queries.
155161
"""
156162

157163
# Platforms Supported
@@ -243,6 +249,7 @@
243249
configitem("fsmonitor", "tcp-host", default="::1")
244250
configitem("fsmonitor", "tcp-port", default=12300)
245251
configitem("fsmonitor", "watchman-query-lock", default=False)
252+
configitem("fsmonitor", "wait-full-crawl", default=True)
246253

247254
# This extension is incompatible with the following incompatible extensions
248255
# and will disable itself when encountering one of these:

0 commit comments

Comments
 (0)