44
44
from bzrlib.revision import NULL_REVISION
45
from bzrlib.trace import mutter, note
45
from bzrlib.trace import mutter, note, warning
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
780
missing_revisions = set(key for key in keys if key not in ancestry)
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)
781
786
if missing_revisions:
782
787
parent_map = self._get_parent_map(missing_revisions)
783
788
if 'hpss' in debug.debug_flags:
784
789
mutter('retransmitted revisions: %d of %d',
785
len(set(self._parents_map).intersection(parent_map)),
790
len(set(ancestry).intersection(parent_map)),
787
self._parents_map.update(parent_map)
792
ancestry.update(parent_map)
788
793
present_keys = [k for k in keys if k in ancestry]
789
794
if 'hpss' in debug.debug_flags:
790
795
self._requested_parents.update(present_keys)
791
796
mutter('Current RemoteRepository graph hit rate: %d%%',
792
100.0 * len(self._requested_parents) / len(self._parents_map))
797
100.0 * len(self._requested_parents) / len(ancestry))
793
798
return dict((k, ancestry[k]) for k in present_keys)
795
800
def _response_is_unknown_method(self, response, verb):
813
818
def _get_parent_map(self, keys):
814
819
"""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()
816
828
if NULL_REVISION in keys:
817
829
keys.discard(NULL_REVISION)
831
843
# TODO: Manage this incrementally to avoid covering the same path
832
844
# repeatedly. (The server will have to on each request, but the less
833
845
# work done the better).
834
start_set = set(self._parents_map)
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)
835
851
result_parents = set()
836
for parents in self._parents_map.itervalues():
852
for parents in parents_map.itervalues():
837
853
result_parents.update(parents)
838
854
stop_keys = result_parents.difference(start_set)
839
855
included_keys = start_set.intersection(result_parents)
840
856
start_set.difference_update(included_keys)
841
recipe = (start_set, stop_keys, len(self._parents_map))
857
recipe = (start_set, stop_keys, len(parents_map))
842
858
body = self._serialise_search_recipe(recipe)
843
859
path = self.bzrdir._path_for_remote_call(self._client)
848
864
response = self._client.call_with_body_bytes_expecting_body(
849
865
verb, args, self._serialise_search_recipe(recipe))
850
866
if self._response_is_unknown_method(response, verb):
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])
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()
857
879
elif response[0][0] not in ['ok']:
858
880
reponse[1].cancel_read_body()
859
881
raise errors.UnexpectedSmartServerResponse(response[0])
1009
1031
return self._real_repository.has_signature_for_revision_id(revision_id)
1011
1033
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)
1012
1038
REQUEST_NAME = 'Repository.stream_revisions_chunked'
1013
1039
path = self.bzrdir._path_for_remote_call(self._client)
1014
1040
body = self._serialise_search_recipe(search.get_recipe())
1015
1041
response, protocol = self._client.call_with_body_bytes_expecting_body(
1016
1042
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)
1018
1059
if response == ('ok',):
1019
1060
return self._deserialise_stream(protocol)
1020
1061
if response == ('NoSuchRevision', ):
1021
1062
# We cannot easily identify the revision that is missing in this
1022
1063
# situation without doing much more network IO. For now, bail.
1023
1064
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)
1032
1066
raise errors.UnexpectedSmartServerResponse(response)