43
43
from bzrlib.revision import NULL_REVISION
44
44
from bzrlib.trace import mutter, note
45
from bzrlib.tuned_gzip import GzipFile
46
47
# Note: RemoteBzrDirFormat is in bzrdir.py
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
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
772
781
len(set(self._parents_map).intersection(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)
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
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.
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)
807
841
assert type(key) is str
808
842
verb = 'Repository.get_parent_map'
809
response = self._client.call_expecting_body(
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()
824
860
# no revisions found
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)
1037
1069
def _make_parents_provider(self):
1072
def _serialise_search_recipe(self, recipe):
1073
"""Serialise a graph search recipe.
1075
:param recipe: A search recipe (start, stop, count).
1076
:return: Serialised bytes.
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))
1041
1084
class RemoteBranchLockableFiles(LockableFiles):
1042
1085
"""A 'LockableFiles' implementation that talks to a smart server.