16 ) |
16 ) |
17 from . import ( |
17 from . import ( |
18 constants, |
18 constants, |
19 shallowutil, |
19 shallowutil, |
20 ) |
20 ) |
|
21 |
21 |
22 |
22 class basestore(object): |
23 class basestore(object): |
23 def __init__(self, repo, path, reponame, shared=False): |
24 def __init__(self, repo, path, reponame, shared=False): |
24 """Creates a remotefilelog store object for the given repo name. |
25 """Creates a remotefilelog store object for the given repo name. |
25 |
26 |
35 self._path = path |
36 self._path = path |
36 self._reponame = reponame |
37 self._reponame = reponame |
37 self._shared = shared |
38 self._shared = shared |
38 self._uid = os.getuid() if not pycompat.iswindows else None |
39 self._uid = os.getuid() if not pycompat.iswindows else None |
39 |
40 |
40 self._validatecachelog = self.ui.config("remotefilelog", |
41 self._validatecachelog = self.ui.config( |
41 "validatecachelog") |
42 "remotefilelog", "validatecachelog" |
42 self._validatecache = self.ui.config("remotefilelog", "validatecache", |
43 ) |
43 'on') |
44 self._validatecache = self.ui.config( |
|
45 "remotefilelog", "validatecache", 'on' |
|
46 ) |
44 if self._validatecache not in ('on', 'strict', 'off'): |
47 if self._validatecache not in ('on', 'strict', 'off'): |
45 self._validatecache = 'on' |
48 self._validatecache = 'on' |
46 if self._validatecache == 'off': |
49 if self._validatecache == 'off': |
47 self._validatecache = False |
50 self._validatecache = False |
48 |
51 |
52 def getmissing(self, keys): |
55 def getmissing(self, keys): |
53 missing = [] |
56 missing = [] |
54 for name, node in keys: |
57 for name, node in keys: |
55 filepath = self._getfilepath(name, node) |
58 filepath = self._getfilepath(name, node) |
56 exists = os.path.exists(filepath) |
59 exists = os.path.exists(filepath) |
57 if (exists and self._validatecache == 'strict' and |
60 if ( |
58 not self._validatekey(filepath, 'contains')): |
61 exists |
|
62 and self._validatecache == 'strict' |
|
63 and not self._validatekey(filepath, 'contains') |
|
64 ): |
59 exists = False |
65 exists = False |
60 if not exists: |
66 if not exists: |
61 missing.append((name, node)) |
67 missing.append((name, node)) |
62 |
68 |
63 return missing |
69 return missing |
75 |
81 |
76 def cleanup(self, ledger): |
82 def cleanup(self, ledger): |
77 ui = self.ui |
83 ui = self.ui |
78 entries = ledger.sources.get(self, []) |
84 entries = ledger.sources.get(self, []) |
79 count = 0 |
85 count = 0 |
80 progress = ui.makeprogress(_("cleaning up"), unit="files", |
86 progress = ui.makeprogress( |
81 total=len(entries)) |
87 _("cleaning up"), unit="files", total=len(entries) |
|
88 ) |
82 for entry in entries: |
89 for entry in entries: |
83 if entry.gced or (entry.datarepacked and entry.historyrepacked): |
90 if entry.gced or (entry.datarepacked and entry.historyrepacked): |
84 progress.update(count) |
91 progress.update(count) |
85 path = self._getfilepath(entry.filename, entry.node) |
92 path = self._getfilepath(entry.filename, entry.node) |
86 util.tryunlink(path) |
93 util.tryunlink(path) |
176 missingfilename.discard(sha) |
183 missingfilename.discard(sha) |
177 |
184 |
178 return filenames |
185 return filenames |
179 |
186 |
180 def _getrepocachepath(self): |
187 def _getrepocachepath(self): |
181 return os.path.join( |
188 return ( |
182 self._path, self._reponame) if self._shared else self._path |
189 os.path.join(self._path, self._reponame) |
|
190 if self._shared |
|
191 else self._path |
|
192 ) |
183 |
193 |
184 def _listkeys(self): |
194 def _listkeys(self): |
185 """List all the remotefilelog keys that exist in the store. |
195 """List all the remotefilelog keys that exist in the store. |
186 |
196 |
187 Returns a iterator of (filename hash, filecontent hash) tuples. |
197 Returns a iterator of (filename hash, filecontent hash) tuples. |
217 with open(self._validatecachelog, 'a+') as f: |
227 with open(self._validatecachelog, 'a+') as f: |
218 f.write("corrupt %s during read\n" % filepath) |
228 f.write("corrupt %s during read\n" % filepath) |
219 os.rename(filepath, filepath + ".corrupt") |
229 os.rename(filepath, filepath + ".corrupt") |
220 raise KeyError("corrupt local cache file %s" % filepath) |
230 raise KeyError("corrupt local cache file %s" % filepath) |
221 except IOError: |
231 except IOError: |
222 raise KeyError("no file found at %s for %s:%s" % (filepath, name, |
232 raise KeyError( |
223 hex(node))) |
233 "no file found at %s for %s:%s" % (filepath, name, hex(node)) |
|
234 ) |
224 |
235 |
225 return data |
236 return data |
226 |
237 |
227 def addremotefilelognode(self, name, node, data): |
238 def addremotefilelognode(self, name, node, data): |
228 filepath = self._getfilepath(name, node) |
239 filepath = self._getfilepath(name, node) |
242 shallowutil.mkstickygroupdir(self.ui, os.path.dirname(filepath)) |
253 shallowutil.mkstickygroupdir(self.ui, os.path.dirname(filepath)) |
243 shallowutil.writefile(filepath, data, readonly=True) |
254 shallowutil.writefile(filepath, data, readonly=True) |
244 |
255 |
245 if self._validatecache: |
256 if self._validatecache: |
246 if not self._validatekey(filepath, 'write'): |
257 if not self._validatekey(filepath, 'write'): |
247 raise error.Abort(_("local cache write was corrupted %s") % |
258 raise error.Abort( |
248 filepath) |
259 _("local cache write was corrupted %s") % filepath |
|
260 ) |
249 finally: |
261 finally: |
250 os.umask(oldumask) |
262 os.umask(oldumask) |
251 |
263 |
252 def markrepo(self, path): |
264 def markrepo(self, path): |
253 """Call this to add the given repo path to the store's list of |
265 """Call this to add the given repo path to the store's list of |
286 # it is truncated |
298 # it is truncated |
287 return False |
299 return False |
288 |
300 |
289 # extract the node from the metadata |
301 # extract the node from the metadata |
290 offset += size |
302 offset += size |
291 datanode = data[offset:offset + 20] |
303 datanode = data[offset : offset + 20] |
292 |
304 |
293 # and compare against the path |
305 # and compare against the path |
294 if os.path.basename(path) == hex(datanode): |
306 if os.path.basename(path) == hex(datanode): |
295 # Content matches the intended path |
307 # Content matches the intended path |
296 return True |
308 return True |
312 removed = 0 |
324 removed = 0 |
313 |
325 |
314 # keep files newer than a day even if they aren't needed |
326 # keep files newer than a day even if they aren't needed |
315 limit = time.time() - (60 * 60 * 24) |
327 limit = time.time() - (60 * 60 * 24) |
316 |
328 |
317 progress = ui.makeprogress(_("removing unnecessary files"), |
329 progress = ui.makeprogress( |
318 unit="files") |
330 _("removing unnecessary files"), unit="files" |
|
331 ) |
319 progress.update(0) |
332 progress.update(0) |
320 for root, dirs, files in os.walk(cachepath): |
333 for root, dirs, files in os.walk(cachepath): |
321 for file in files: |
334 for file in files: |
322 if file == 'repos': |
335 if file == 'repos': |
323 continue |
336 continue |
350 shallowutil.unlinkfile(path) |
363 shallowutil.unlinkfile(path) |
351 except OSError as e: |
364 except OSError as e: |
352 # errno.ENOENT = no such file or directory |
365 # errno.ENOENT = no such file or directory |
353 if e.errno != errno.ENOENT: |
366 if e.errno != errno.ENOENT: |
354 raise |
367 raise |
355 msg = _("warning: file %s was removed by another " |
368 msg = _( |
356 "process\n") |
369 "warning: file %s was removed by another " |
|
370 "process\n" |
|
371 ) |
357 ui.warn(msg % path) |
372 ui.warn(msg % path) |
358 continue |
373 continue |
359 removed += 1 |
374 removed += 1 |
360 progress.complete() |
375 progress.complete() |
361 |
376 |
362 # remove oldest files until under limit |
377 # remove oldest files until under limit |
363 limit = ui.configbytes("remotefilelog", "cachelimit") |
378 limit = ui.configbytes("remotefilelog", "cachelimit") |
364 if size > limit: |
379 if size > limit: |
365 excess = size - limit |
380 excess = size - limit |
366 progress = ui.makeprogress(_("enforcing cache limit"), unit="bytes", |
381 progress = ui.makeprogress( |
367 total=excess) |
382 _("enforcing cache limit"), unit="bytes", total=excess |
|
383 ) |
368 removedexcess = 0 |
384 removedexcess = 0 |
369 while queue and size > limit and size > 0: |
385 while queue and size > limit and size > 0: |
370 progress.update(removedexcess) |
386 progress.update(removedexcess) |
371 atime, oldpath, oldpathstat = queue.get() |
387 atime, oldpath, oldpathstat = queue.get() |
372 try: |
388 try: |
380 size -= oldpathstat.st_size |
396 size -= oldpathstat.st_size |
381 removed += 1 |
397 removed += 1 |
382 removedexcess += oldpathstat.st_size |
398 removedexcess += oldpathstat.st_size |
383 progress.complete() |
399 progress.complete() |
384 |
400 |
385 ui.status(_("finished: removed %d of %d files (%0.2f GB to %0.2f GB)\n") |
401 ui.status( |
386 % (removed, count, |
402 _("finished: removed %d of %d files (%0.2f GB to %0.2f GB)\n") |
387 float(originalsize) / 1024.0 / 1024.0 / 1024.0, |
403 % ( |
388 float(size) / 1024.0 / 1024.0 / 1024.0)) |
404 removed, |
|
405 count, |
|
406 float(originalsize) / 1024.0 / 1024.0 / 1024.0, |
|
407 float(size) / 1024.0 / 1024.0 / 1024.0, |
|
408 ) |
|
409 ) |
|
410 |
389 |
411 |
390 class baseunionstore(object): |
412 class baseunionstore(object): |
391 def __init__(self, *args, **kwargs): |
413 def __init__(self, *args, **kwargs): |
392 # If one of the functions that iterates all of the stores is about to |
414 # If one of the functions that iterates all of the stores is about to |
393 # throw a KeyError, try this many times with a full refresh between |
415 # throw a KeyError, try this many times with a full refresh between |
419 try: |
442 try: |
420 return fn(self, *args, **kwargs) |
443 return fn(self, *args, **kwargs) |
421 except KeyError: |
444 except KeyError: |
422 if i == self.numattempts: |
445 if i == self.numattempts: |
423 # retries exhausted |
446 # retries exhausted |
424 retrylog('retries exhausted in %s, raising KeyError\n' % |
447 retrylog( |
425 pycompat.sysbytes(funcname)) |
448 'retries exhausted in %s, raising KeyError\n' |
|
449 % pycompat.sysbytes(funcname) |
|
450 ) |
426 raise |
451 raise |
|
452 |
427 return wrapped |
453 return wrapped |