44
44
from bzrlib.revision import NULL_REVISION
45
from bzrlib.trace import mutter, note, warning
45
from bzrlib.trace import mutter, note
47
47
# Note: RemoteBzrDirFormat is in bzrdir.py
777
777
"""See bzrlib.Graph.get_parent_map()."""
778
778
# Hack to build up the caching logic.
779
779
ancestry = self._parents_map
781
# Repository is not locked, so there's no cache.
782
missing_revisions = set(keys)
785
missing_revisions = set(key for key in keys if key not in ancestry)
780
missing_revisions = set(key for key in keys if key not in ancestry)
786
781
if missing_revisions:
787
782
parent_map = self._get_parent_map(missing_revisions)
788
783
if 'hpss' in debug.debug_flags:
789
784
mutter('retransmitted revisions: %d of %d',
790
len(set(ancestry).intersection(parent_map)),
785
len(set(self._parents_map).intersection(parent_map)),
792
ancestry.update(parent_map)
787
self._parents_map.update(parent_map)
793
788
present_keys = [k for k in keys if k in ancestry]
794
789
if 'hpss' in debug.debug_flags:
795
790
self._requested_parents.update(present_keys)
796
791
mutter('Current RemoteRepository graph hit rate: %d%%',
797
100.0 * len(self._requested_parents) / len(ancestry))
792
100.0 * len(self._requested_parents) / len(self._parents_map))
798
793
return dict((k, ancestry[k]) for k in present_keys)
800
795
def _response_is_unknown_method(self, response, verb):
818
813
def _get_parent_map(self, keys):
819
814
"""Helper for get_parent_map that performs the RPC."""
820
medium = self._client.get_smart_medium()
821
if not medium._remote_is_at_least_1_2:
822
# We already found out that the server can't understand
823
# Repository.get_parent_map requests, so just fetch the whole
825
return self.get_revision_graph()
828
816
if NULL_REVISION in keys:
829
817
keys.discard(NULL_REVISION)
843
831
# TODO: Manage this incrementally to avoid covering the same path
844
832
# repeatedly. (The server will have to on each request, but the less
845
833
# work done the better).
846
parents_map = self._parents_map
847
if parents_map is None:
848
# Repository is not locked, so there's no cache.
850
start_set = set(parents_map)
834
start_set = set(self._parents_map)
851
835
result_parents = set()
852
for parents in parents_map.itervalues():
836
for parents in self._parents_map.itervalues():
853
837
result_parents.update(parents)
854
838
stop_keys = result_parents.difference(start_set)
855
839
included_keys = start_set.intersection(result_parents)
856
840
start_set.difference_update(included_keys)
857
recipe = (start_set, stop_keys, len(parents_map))
841
recipe = (start_set, stop_keys, len(self._parents_map))
858
842
body = self._serialise_search_recipe(recipe)
859
843
path = self.bzrdir._path_for_remote_call(self._client)
864
848
response = self._client.call_with_body_bytes_expecting_body(
865
849
verb, args, self._serialise_search_recipe(recipe))
866
850
if self._response_is_unknown_method(response, verb):
867
# Server does not support this method, so get the whole graph.
868
# Worse, we have to force a disconnection, because the server now
869
# doesn't realise it has a body on the wire to consume, so the
870
# only way to recover is to abandon the connection.
872
'Server is too old for fast get_parent_map, reconnecting. '
873
'(Upgrade the server to Bazaar 1.2 to avoid this)')
875
# To avoid having to disconnect repeatedly, we keep track of the
876
# fact the server doesn't understand remote methods added in 1.2.
877
medium._remote_is_at_least_1_2 = False
878
return self.get_revision_graph()
851
# Server that does not support this method, get the whole graph.
852
response = self._client.call_expecting_body(
853
'Repository.get_revision_graph', path, '')
854
if response[0][0] not in ['ok', 'nosuchrevision']:
855
reponse[1].cancel_read_body()
856
raise errors.UnexpectedSmartServerResponse(response[0])
879
857
elif response[0][0] not in ['ok']:
880
858
reponse[1].cancel_read_body()
881
859
raise errors.UnexpectedSmartServerResponse(response[0])
1031
1009
return self._real_repository.has_signature_for_revision_id(revision_id)
1033
1011
def get_data_stream_for_search(self, search):
1034
medium = self._client.get_smart_medium()
1035
if not medium._remote_is_at_least_1_2:
1037
return self._real_repository.get_data_stream_for_search(search)
1038
1012
REQUEST_NAME = 'Repository.stream_revisions_chunked'
1039
1013
path = self.bzrdir._path_for_remote_call(self._client)
1040
1014
body = self._serialise_search_recipe(search.get_recipe())
1041
1015
response, protocol = self._client.call_with_body_bytes_expecting_body(
1042
1016
REQUEST_NAME, (path,), body)
1044
if self._response_is_unknown_method((response, protocol), REQUEST_NAME):
1045
# Server does not support this method, so fall back to VFS.
1046
# Worse, we have to force a disconnection, because the server now
1047
# doesn't realise it has a body on the wire to consume, so the
1048
# only way to recover is to abandon the connection.
1050
'Server is too old for streaming pull, reconnecting. '
1051
'(Upgrade the server to Bazaar 1.2 to avoid this)')
1053
# To avoid having to disconnect repeatedly, we keep track of the
1054
# fact the server doesn't understand this remote method.
1055
medium._remote_is_at_least_1_2 = False
1057
return self._real_repository.get_data_stream_for_search(search)
1059
1018
if response == ('ok',):
1060
1019
return self._deserialise_stream(protocol)
1061
1020
if response == ('NoSuchRevision', ):
1062
1021
# We cannot easily identify the revision that is missing in this
1063
1022
# situation without doing much more network IO. For now, bail.
1064
1023
raise NoSuchRevision(self, "unknown")
1024
elif (response == ('error', "Generic bzr smart protocol error: "
1025
"bad request '%s'" % REQUEST_NAME) or
1026
response == ('error', "Generic bzr smart protocol error: "
1027
"bad request u'%s'" % REQUEST_NAME)):
1028
protocol.cancel_read_body()
1030
return self._real_repository.get_data_stream_for_search(search)
1066
1032
raise errors.UnexpectedSmartServerResponse(response)