~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/remote.py

  • Committer: Robert Collins
  • Date: 2008-02-03 22:55:08 UTC
  • mto: This revision was merged to the branch mainline in revision 3216.
  • Revision ID: robertc@robertcollins.net-20080203225508-0rogbg0ggonuqfhp
Change the smart server get_parents method to take a graph search to exclude already recieved parents from. This prevents history shortcuts causing huge numbers of duplicates.

Show diffs side-by-side

added added

removed removed

Lines of Context:
42
42
    )
43
43
from bzrlib.revision import NULL_REVISION
44
44
from bzrlib.trace import mutter, note
 
45
from bzrlib.tuned_gzip import GzipFile
45
46
 
46
47
# Note: RemoteBzrDirFormat is in bzrdir.py
47
48
 
268
269
        self._leave_lock = False
269
270
        # A cache of looked up revision parent data; reset at unlock time.
270
271
        self._parents_map = None
 
272
        if 'hpss' in debug.debug_flags:
 
273
            self._requested_parents = None
271
274
        # For tests:
272
275
        # These depend on the actual remote format, so force them off for
273
276
        # maximum compatibility. XXX: In future these should depend on the
472
475
            self._lock_mode = 'r'
473
476
            self._lock_count = 1
474
477
            self._parents_map = {}
 
478
            if 'hpss' in debug.debug_flags:
 
479
                self._requested_parents = set()
475
480
            if self._real_repository is not None:
476
481
                self._real_repository.lock_read()
477
482
        else:
510
515
            self._lock_mode = 'w'
511
516
            self._lock_count = 1
512
517
            self._parents_map = {}
 
518
            if 'hpss' in debug.debug_flags:
 
519
                self._requested_parents = set()
513
520
        elif self._lock_mode == 'r':
514
521
            raise errors.ReadOnlyError(self)
515
522
        else:
570
577
        if self._lock_count > 0:
571
578
            return
572
579
        self._parents_map = None
 
580
        if 'hpss' in debug.debug_flags:
 
581
            self._requested_parents = None
573
582
        old_mode = self._lock_mode
574
583
        self._lock_mode = None
575
584
        try:
772
781
                        len(set(self._parents_map).intersection(parent_map)),
773
782
                        len(parent_map))
774
783
            self._parents_map.update(parent_map)
775
 
        return dict((k, ancestry[k]) for k in keys if k in ancestry)
 
784
        present_keys = [k for k in keys if k in ancestry]
 
785
        if 'hpss' in debug.debug_flags:
 
786
            self._requested_parents.update(present_keys)
 
787
            mutter('Current RemoteRepository graph hit rate: %d%%',
 
788
                100.0 * len(self._requested_parents) / len(self._parents_map))
 
789
        return dict((k, ancestry[k]) for k in present_keys)
776
790
 
777
791
    def _response_is_unknown_method(self, response, verb):
778
792
        """Return True if response is an unknonwn method response to verb.
802
816
                return found_parents
803
817
        else:
804
818
            found_parents = {}
 
819
        # TODO(Needs analysis): We could assume that the keys being requested
 
820
        # from get_parent_map are in a breadth first search, so typically they
 
821
        # will all be depth N from some common parent, and we don't have to
 
822
        # have the server iterate from the root parent, but rather from the
 
823
        # keys we're searching; and just tell the server the keyspace we
 
824
        # already have; but this may be more traffic again.
 
825
 
 
826
        # Transform self._parents_map into a search request recipe.
 
827
        # TODO: Manage this incrementally to avoid covering the same path
 
828
        # repeatedly. (The server will have to on each request, but the less
 
829
        # work done the better).
 
830
        start_set = set(self._parents_map)
 
831
        result_parents = set()
 
832
        for parents in self._parents_map.itervalues():
 
833
            result_parents.update(parents)
 
834
        stop_keys = result_parents.difference(start_set)
 
835
        included_keys = start_set.intersection(result_parents)
 
836
        start_set.difference_update(included_keys)
 
837
        recipe = (start_set, stop_keys, len(self._parents_map))
 
838
        body = self._serialise_search_recipe(recipe)
805
839
        path = self.bzrdir._path_for_remote_call(self._client)
806
840
        for key in keys:
807
841
            assert type(key) is str
808
842
        verb = 'Repository.get_parent_map'
809
 
        response = self._client.call_expecting_body(
810
 
            verb, path, *keys)
 
843
        args = (path,) + tuple(keys)
 
844
        response = self._client.call_with_body_bytes_expecting_body(
 
845
            verb, args, self._serialise_search_recipe(recipe))
811
846
        if self._response_is_unknown_method(response, verb):
812
847
            # Server that does not support this method, get the whole graph.
813
848
            response = self._client.call_expecting_body(
819
854
            reponse[1].cancel_read_body()
820
855
            raise errors.UnexpectedSmartServerResponse(response[0])
821
856
        if response[0][0] == 'ok':
822
 
            coded = response[1].read_body_bytes()
 
857
            coded = GzipFile(mode='rb',
 
858
                fileobj=StringIO(response[1].read_body_bytes())).read()
823
859
            if coded == '':
824
860
                # no revisions found
825
861
                return {}
972
1008
    def get_data_stream_for_search(self, search):
973
1009
        REQUEST_NAME = 'Repository.stream_revisions_chunked'
974
1010
        path = self.bzrdir._path_for_remote_call(self._client)
975
 
        recipe = search.get_recipe()
976
 
        start_keys = ' '.join(recipe[0])
977
 
        stop_keys = ' '.join(recipe[1])
978
 
        count = str(recipe[2])
979
 
        body = '\n'.join((start_keys, stop_keys, count))
 
1011
        body = self._serialise_search_recipe(search.get_recipe())
980
1012
        response, protocol = self._client.call_with_body_bytes_expecting_body(
981
1013
            REQUEST_NAME, (path,), body)
982
1014
 
1037
1069
    def _make_parents_provider(self):
1038
1070
        return self
1039
1071
 
 
1072
    def _serialise_search_recipe(self, recipe):
 
1073
        """Serialise a graph search recipe.
 
1074
 
 
1075
        :param recipe: A search recipe (start, stop, count).
 
1076
        :return: Serialised bytes.
 
1077
        """
 
1078
        start_keys = ' '.join(recipe[0])
 
1079
        stop_keys = ' '.join(recipe[1])
 
1080
        count = str(recipe[2])
 
1081
        return '\n'.join((start_keys, stop_keys, count))
 
1082
 
1040
1083
 
1041
1084
class RemoteBranchLockableFiles(LockableFiles):
1042
1085
    """A 'LockableFiles' implementation that talks to a smart server.