diff options
| author | Dave Borowitz <dborowitz@google.com> | 2012-10-23 15:00:54 -0700 | 
|---|---|---|
| committer | Dave Borowitz <dborowitz@google.com> | 2012-10-24 14:51:58 -0700 | 
| commit | 67700e9b90a38cc3719c818bc967153e8b7b429e (patch) | |
| tree | 57768c3936f66cb28b623c13e59c2bc1dba247ff /subcmds | |
| parent | 5c6eeac8f0350fd6b14cf226ffcff655f1dd9582 (diff) | |
| download | git-repo-67700e9b90a38cc3719c818bc967153e8b7b429e.tar.gz | |
sync: Order projects according to last fetch time
Some projects may consistently take longer to fetch than others, for
example a more active project may have many more Gerrit changes than a
less active project, which take longer to transfer. Use a simple
heuristic based on the last fetch time to fetch slower projects first,
so we do not tend to spend the end of the sync fetching a small number
of outliers.
This algorithm is probably not optimal, and due to inter-run latency
variance and Python thread scheduling, we may not even have good
estimates of a project sync time.
Change-Id: I9a463f214b3ed742e4d807c42925b62cb8b1745b
Diffstat (limited to 'subcmds')
| -rw-r--r-- | subcmds/sync.py | 61 | 
1 files changed, 60 insertions, 1 deletions
| diff --git a/subcmds/sync.py b/subcmds/sync.py index e68a025e..a8022d9d 100644 --- a/subcmds/sync.py +++ b/subcmds/sync.py | |||
| @@ -16,6 +16,7 @@ | |||
| 16 | import netrc | 16 | import netrc | 
| 17 | from optparse import SUPPRESS_HELP | 17 | from optparse import SUPPRESS_HELP | 
| 18 | import os | 18 | import os | 
| 19 | import pickle | ||
| 19 | import re | 20 | import re | 
| 20 | import shutil | 21 | import shutil | 
| 21 | import socket | 22 | import socket | 
| @@ -47,6 +48,8 @@ from error import RepoChangedException, GitError | |||
| 47 | from project import SyncBuffer | 48 | from project import SyncBuffer | 
| 48 | from progress import Progress | 49 | from progress import Progress | 
| 49 | 50 | ||
| 51 | _ONE_DAY_S = 24 * 60 * 60 | ||
| 52 | |||
| 50 | class _FetchError(Exception): | 53 | class _FetchError(Exception): | 
| 51 | """Internal error thrown in _FetchHelper() when we don't want stack trace.""" | 54 | """Internal error thrown in _FetchHelper() when we don't want stack trace.""" | 
| 52 | pass | 55 | pass | 
| @@ -212,10 +215,12 @@ later is required to fix a server side protocol bug. | |||
| 212 | # - We always make sure we unlock the lock if we locked it. | 215 | # - We always make sure we unlock the lock if we locked it. | 
| 213 | try: | 216 | try: | 
| 214 | try: | 217 | try: | 
| 218 | start = time.time() | ||
| 215 | success = project.Sync_NetworkHalf( | 219 | success = project.Sync_NetworkHalf( | 
| 216 | quiet=opt.quiet, | 220 | quiet=opt.quiet, | 
| 217 | current_branch_only=opt.current_branch_only, | 221 | current_branch_only=opt.current_branch_only, | 
| 218 | clone_bundle=not opt.no_clone_bundle) | 222 | clone_bundle=not opt.no_clone_bundle) | 
| 223 | self._fetch_times.Set(project, time.time() - start) | ||
| 219 | 224 | ||
| 220 | # Lock around all the rest of the code, since printing, updating a set | 225 | # Lock around all the rest of the code, since printing, updating a set | 
| 221 | # and Progress.update() are not thread safe. | 226 | # and Progress.update() are not thread safe. | 
| @@ -293,6 +298,7 @@ later is required to fix a server side protocol bug. | |||
| 293 | sys.exit(1) | 298 | sys.exit(1) | 
| 294 | 299 | ||
| 295 | pm.end() | 300 | pm.end() | 
| 301 | self._fetch_times.Save() | ||
| 296 | for project in projects: | 302 | for project in projects: | 
| 297 | project.bare_git.gc('--auto') | 303 | project.bare_git.gc('--auto') | 
| 298 | return fetched | 304 | return fetched | 
| @@ -496,12 +502,15 @@ uncommitted changes are present' % project.relpath | |||
| 496 | self.jobs = self.manifest.default.sync_j | 502 | self.jobs = self.manifest.default.sync_j | 
| 497 | all_projects = self.GetProjects(args, missing_ok=True) | 503 | all_projects = self.GetProjects(args, missing_ok=True) | 
| 498 | 504 | ||
| 505 | self._fetch_times = _FetchTimes(self.manifest) | ||
| 499 | if not opt.local_only: | 506 | if not opt.local_only: | 
| 500 | to_fetch = [] | 507 | to_fetch = [] | 
| 501 | now = time.time() | 508 | now = time.time() | 
| 502 | if (24 * 60 * 60) <= (now - rp.LastFetch): | 509 | if _ONE_DAY_S <= (now - rp.LastFetch): | 
| 503 | to_fetch.append(rp) | 510 | to_fetch.append(rp) | 
| 504 | to_fetch.extend(all_projects) | 511 | to_fetch.extend(all_projects) | 
| 512 | to_fetch.sort(key=self._fetch_times.Get, reverse=True) | ||
| 513 | self._fetch_times.Clear() | ||
| 505 | 514 | ||
| 506 | self._Fetch(to_fetch, opt) | 515 | self._Fetch(to_fetch, opt) | 
| 507 | _PostRepoFetch(rp, opt.no_repo_verify) | 516 | _PostRepoFetch(rp, opt.no_repo_verify) | 
| @@ -602,3 +611,53 @@ warning: Cannot automatically authenticate repo.""" | |||
| 602 | print >>sys.stderr | 611 | print >>sys.stderr | 
| 603 | return False | 612 | return False | 
| 604 | return True | 613 | return True | 
| 614 | |||
| 615 | class _FetchTimes(object): | ||
| 616 | def __init__(self, manifest): | ||
| 617 | self._path = os.path.join(manifest.repodir, '.repopickle_fetchtimes') | ||
| 618 | self._times = None | ||
| 619 | |||
| 620 | def Clear(self): | ||
| 621 | self._times = {} | ||
| 622 | |||
| 623 | def Get(self, project): | ||
| 624 | self._Load() | ||
| 625 | return self._times.get(project.name, _ONE_DAY_S) | ||
| 626 | |||
| 627 | def Set(self, project, t): | ||
| 628 | self._times[project.name] = t | ||
| 629 | |||
| 630 | def _Load(self): | ||
| 631 | if self._times is None: | ||
| 632 | try: | ||
| 633 | f = open(self._path) | ||
| 634 | except IOError: | ||
| 635 | self._times = {} | ||
| 636 | return self._times | ||
| 637 | try: | ||
| 638 | try: | ||
| 639 | self._times = pickle.load(f) | ||
| 640 | except: | ||
| 641 | try: | ||
| 642 | os.remove(self._path) | ||
| 643 | except OSError: | ||
| 644 | pass | ||
| 645 | self._times = {} | ||
| 646 | finally: | ||
| 647 | f.close() | ||
| 648 | return self._times | ||
| 649 | |||
| 650 | def Save(self): | ||
| 651 | if self._times is None: | ||
| 652 | return | ||
| 653 | try: | ||
| 654 | f = open(self._path, 'wb') | ||
| 655 | try: | ||
| 656 | pickle.dump(self._times, f) | ||
| 657 | except (IOError, OSError, pickle.PickleError): | ||
| 658 | try: | ||
| 659 | os.remove(self._path) | ||
| 660 | except OSError: | ||
| 661 | pass | ||
| 662 | finally: | ||
| 663 | f.close() | ||
