Merge lp:~jelmer/brz/hpss-get-missing-keys into lp:brz

Proposed by Jelmer Vernooij
Status: Merged
Approved by: Jelmer Vernooij
Approved revision: no longer in the source branch.
Merge reported by: The Breezy Bot
Merged at revision: not available
Proposed branch: lp:~jelmer/brz/hpss-get-missing-keys
Merge into: lp:brz
Diff against target: 274 lines (+150/-9)
7 files modified
breezy/bzr/remote.py (+42/-2)
breezy/bzr/smart/repository.py (+46/-1)
breezy/bzr/smart/request.py (+3/-0)
breezy/bzr/vf_repository.py (+12/-3)
breezy/tests/blackbox/test_branch.py (+19/-0)
breezy/tests/per_branch/test_create_clone.py (+2/-2)
breezy/tests/test_smart.py (+26/-1)
To merge this branch: bzr merge lp:~jelmer/brz/hpss-get-missing-keys
Reviewer Review Type Date Requested Status
Martin Packman Approve
Review via email: mp+348448@code.launchpad.net

Commit message

Add HPSS call for Repository.get_stream_for_missing_keys.

Description of the change

Add HPSS call for Repository.get_stream_for_missing_keys.

To post a comment you must log in.
Revision history for this message
Martin Packman (gz) wrote :

Some conflicts to resolve but logic all looked fine to me.

review: Approve
Revision history for this message
The Breezy Bot (the-breezy-bot) wrote :

Running landing tests failed
https://ci.breezy-vcs.org/job/land-brz/192/

Revision history for this message
The Breezy Bot (the-breezy-bot) wrote :

Running landing tests failed
https://ci.breezy-vcs.org/job/land-brz/198/

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'breezy/bzr/remote.py'
2--- breezy/bzr/remote.py 2018-06-29 23:46:33 +0000
3+++ breezy/bzr/remote.py 2018-06-30 14:39:04 +0000
4@@ -2883,6 +2883,17 @@
5 self.target_repo.autopack()
6 return result
7
8+ def insert_missing_keys(self, source, missing_keys):
9+ if (isinstance(source, RemoteStreamSource) and
10+ source.from_repository._client._medium == self.target_repo._client._medium):
11+ # Streaming from and to the same medium is tricky, since we don't support
12+ # more than one concurrent request. For now, just force VFS.
13+ stream = source._get_real_stream_for_missing_keys(missing_keys)
14+ else:
15+ stream = source.get_stream_for_missing_keys(missing_keys)
16+ return self.insert_stream_without_locking(stream,
17+ self.target_repo._format)
18+
19 def insert_stream(self, stream, src_format, resume_tokens):
20 target = self.target_repo
21 target._unstacked_provider.missing_keys.clear()
22@@ -3010,12 +3021,38 @@
23 sources.append(repo)
24 return self.missing_parents_chain(search, sources)
25
26- def get_stream_for_missing_keys(self, missing_keys):
27+ def _get_real_stream_for_missing_keys(self, missing_keys):
28 self.from_repository._ensure_real()
29 real_repo = self.from_repository._real_repository
30 real_source = real_repo._get_source(self.to_format)
31 return real_source.get_stream_for_missing_keys(missing_keys)
32
33+ def get_stream_for_missing_keys(self, missing_keys):
34+ if not isinstance(self.from_repository, RemoteRepository):
35+ return self._get_real_stream_for_missing_keys(missing_keys)
36+ client = self.from_repository._client
37+ medium = client._medium
38+ if medium._is_remote_before((3, 0)):
39+ return self._get_real_stream_for_missing_keys(missing_keys)
40+ path = self.from_repository.controldir._path_for_remote_call(client)
41+ args = (path, self.to_format.network_name())
42+ search_bytes = b'\n'.join([b'\t'.join(key) for key in missing_keys])
43+ try:
44+ response, handler = self.from_repository._call_with_body_bytes_expecting_body(
45+ b'Repository.get_stream_for_missing_keys', args, search_bytes)
46+ except (errors.UnknownSmartMethod, errors.UnknownFormatError):
47+ return self._get_real_stream_for_missing_keys(missing_keys)
48+ if response[0] != b'ok':
49+ raise errors.UnexpectedSmartServerResponse(response)
50+ byte_stream = handler.read_streamed_body()
51+ src_format, stream = smart_repo._byte_stream_to_stream(byte_stream,
52+ self._record_counter)
53+ if src_format.network_name() != self.from_repository._format.network_name():
54+ raise AssertionError(
55+ "Mismatched RemoteRepository and stream src %r, %r" % (
56+ src_format.network_name(), repo._format.network_name()))
57+ return stream
58+
59 def _real_stream(self, repo, search):
60 """Get a stream for search from repo.
61
62@@ -3716,7 +3753,7 @@
63 err_context = {'token': str((branch_token, repo_token))}
64 response = self._call(
65 b'Branch.unlock', self._remote_path(), branch_token,
66- repo_token or '', **err_context)
67+ repo_token or b'', **err_context)
68 if response == (b'ok',):
69 return
70 raise errors.UnexpectedSmartServerResponse(response)
71@@ -4330,6 +4367,9 @@
72 lambda err: errors.FileExists(err.error_args[0].decode('utf-8')))
73 no_context_error_translators.register(b'DirectoryNotEmpty',
74 lambda err: errors.DirectoryNotEmpty(err.error_args[0].decode('utf-8')))
75+no_context_error_translators.register(b'UnknownFormat',
76+ lambda err: errors.UnknownFormatError(
77+ err.error_args[0].decode('ascii'), err.error_args[0].decode('ascii')))
78
79 def _translate_short_readv_error(err):
80 args = err.error_args
81
82=== modified file 'breezy/bzr/smart/repository.py'
83--- breezy/bzr/smart/repository.py 2018-06-30 10:19:02 +0000
84+++ breezy/bzr/smart/repository.py 2018-06-30 14:39:04 +0000
85@@ -1276,12 +1276,57 @@
86 return None
87
88
89+class SmartServerRepositoryGetStreamForMissingKeys(SmartServerRepositoryRequest):
90+
91+ def do_repository_request(self, repository, to_network_name):
92+ """Get a stream for missing keys.
93+
94+ :param repository: The repository to stream from.
95+ :param to_network_name: The network name of the format of the target
96+ repository.
97+ """
98+ try:
99+ self._to_format = network_format_registry.get(to_network_name)
100+ except KeyError:
101+ return FailedSmartServerResponse(
102+ (b'UnknownFormat', b'repository', to_network_name))
103+ return None # Signal that we want a body.
104+
105+ def do_body(self, body_bytes):
106+ repository = self._repository
107+ repository.lock_read()
108+ try:
109+ source = repository._get_source(self._to_format)
110+ stream = source.get_stream_for_missing_keys(
111+ [tuple(k.split(b'\t')) for k in body_bytes.split(b'\n')])
112+ except Exception:
113+ try:
114+ # On non-error, unlocking is done by the body stream handler.
115+ repository.unlock()
116+ finally:
117+ raise
118+ return SuccessfulSmartServerResponse((b'ok',),
119+ body_stream=self.body_stream(stream, repository))
120+
121+ def body_stream(self, stream, repository):
122+ byte_stream = _stream_to_byte_stream(stream, repository._format)
123+ try:
124+ for bytes in byte_stream:
125+ yield bytes
126+ except errors.RevisionNotPresent as e:
127+ # This shouldn't be able to happen, but as we don't buffer
128+ # everything it can in theory happen.
129+ repository.unlock()
130+ yield FailedSmartServerResponse((b'NoSuchRevision', e.revision_id))
131+ else:
132+ repository.unlock()
133+
134+
135 class SmartServerRepositoryRevisionArchive(SmartServerRepositoryRequest):
136
137 def do_repository_request(self, repository, revision_id, format, name,
138 root, subdir=None, force_mtime=None):
139 """Stream an archive file for a specific revision.
140-
141 :param repository: The repository to stream from.
142 :param revision_id: Revision for which to export the tree
143 :param format: Format (tar, tgz, tbz2, etc)
144
145=== modified file 'breezy/bzr/smart/request.py'
146--- breezy/bzr/smart/request.py 2018-06-23 12:51:06 +0000
147+++ breezy/bzr/smart/request.py 2018-06-30 14:39:04 +0000
148@@ -759,6 +759,9 @@
149 b'Repository.get_stream_1.19', 'breezy.bzr.smart.repository',
150 'SmartServerRepositoryGetStream_1_19', info='read')
151 request_handlers.register_lazy(
152+ b'Repository.get_stream_for_missing_keys', 'breezy.bzr.smart.repository',
153+ 'SmartServerRepositoryGetStreamForMissingKeys', info='read')
154+request_handlers.register_lazy(
155 b'Repository.iter_revisions', 'breezy.bzr.smart.repository',
156 'SmartServerRepositoryIterRevisions', info='read')
157 request_handlers.register_lazy(
158
159=== modified file 'breezy/bzr/vf_repository.py'
160--- breezy/bzr/vf_repository.py 2018-06-30 10:19:02 +0000
161+++ breezy/bzr/vf_repository.py 2018-06-30 14:39:04 +0000
162@@ -166,9 +166,7 @@
163 fallback_repo = fallback_repos.pop()
164 source = fallback_repo._get_source(self.repository._format)
165 sink = self.repository._get_sink()
166- stream = source.get_stream_for_missing_keys(missing_keys)
167- missing_keys = sink.insert_stream_without_locking(stream,
168- self.repository._format)
169+ missing_keys = sink.insert_missing_keys(source, missing_keys)
170 if missing_keys:
171 raise errors.BzrError('Unable to fill in parent inventories for a'
172 ' stacked branch')
173@@ -1718,6 +1716,17 @@
174 def __init__(self, target_repo):
175 self.target_repo = target_repo
176
177+ def insert_missing_keys(self, source, missing_keys):
178+ """Insert missing keys from another source.
179+
180+ :param source: StreamSource to stream from
181+ :param missing_keys: Keys to insert
182+ :return: keys still missing
183+ """
184+ stream = source.get_stream_for_missing_keys(missing_keys)
185+ return self.insert_stream_without_locking(stream,
186+ self.target_repo._format)
187+
188 def insert_stream(self, stream, src_format, resume_tokens):
189 """Insert a stream's content into the target repository.
190
191
192=== modified file 'breezy/tests/blackbox/test_branch.py'
193--- breezy/tests/blackbox/test_branch.py 2018-06-17 11:15:04 +0000
194+++ breezy/tests/blackbox/test_branch.py 2018-06-30 14:39:04 +0000
195@@ -588,6 +588,25 @@
196 self.expectFailure("branching to stacked requires VFS access",
197 self.assertThat, self.hpss_calls, ContainsNoVfsCalls)
198
199+ def test_branch_from_branch_with_ghosts(self):
200+ self.setup_smart_server_with_call_log()
201+ t = self.make_branch_and_tree('from')
202+ for count in range(9):
203+ t.commit(message='commit %d' % count)
204+ t.set_parent_ids([t.last_revision(), b'ghost'])
205+ t.commit(message='add commit with parent')
206+ self.reset_smart_call_log()
207+ out, err = self.run_bzr(['branch', self.get_url('from'),
208+ 'local-target'])
209+ # This figure represent the amount of work to perform this use case. It
210+ # is entirely ok to reduce this number if a test fails due to rpc_count
211+ # being too low. If rpc_count increases, more network roundtrips have
212+ # become necessary for this use case. Please do not adjust this number
213+ # upwards without agreement from bzr's network support maintainers.
214+ self.assertThat(self.hpss_calls, ContainsNoVfsCalls)
215+ self.assertLength(11, self.hpss_calls)
216+ self.assertLength(1, self.hpss_connections)
217+
218
219 class TestRemoteBranch(TestCaseWithSFTPServer):
220
221
222=== modified file 'breezy/tests/per_branch/test_create_clone.py'
223--- breezy/tests/per_branch/test_create_clone.py 2018-03-27 00:51:45 +0000
224+++ breezy/tests/per_branch/test_create_clone.py 2018-06-30 14:39:04 +0000
225@@ -140,8 +140,8 @@
226 branch.Branch.hooks.install_named_hook(
227 'pre_change_branch_tip', self.assertBranchHookBranchIsStacked, None)
228 try:
229- result = tree.branch.create_clone_on_transport(target_transport,
230- stacked_on=trunk.base)
231+ result = tree.branch.create_clone_on_transport(
232+ target_transport, stacked_on=trunk.base)
233 except branch.UnstackableBranchFormat:
234 if not trunk.repository._format.supports_full_versioned_files:
235 raise tests.TestNotApplicable("can not stack on format")
236
237=== modified file 'breezy/tests/test_smart.py'
238--- breezy/tests/test_smart.py 2018-06-30 12:28:52 +0000
239+++ breezy/tests/test_smart.py 2018-06-30 14:39:04 +0000
240@@ -2719,8 +2719,33 @@
241 b"Bazaar pack format 1 (introduced in 0.18)\nB54\n\nBazaar repository format 2a (needs bzr 1.16 or later)\nE")
242
243
244+class TestSmartServerRepositoryGetStreamForMissingKeys(GetStreamTestBase):
245+
246+ def test_missing(self):
247+ """The search argument may be a 'ancestry-of' some heads'."""
248+ backing = self.get_transport()
249+ request = smart_repo.SmartServerRepositoryGetStreamForMissingKeys(
250+ backing)
251+ repo, r1, r2 = self.make_two_commit_repo()
252+ request.execute(b'', repo._format.network_name())
253+ lines = b'inventories\t' + r1
254+ response = request.do_body(lines)
255+ self.assertEqual((b'ok',), response.args)
256+ stream_bytes = b''.join(response.body_stream)
257+ self.assertStartsWith(stream_bytes, b'Bazaar pack format 1')
258+
259+ def test_unknown_format(self):
260+ """The format may not be known by the remote server."""
261+ backing = self.get_transport()
262+ request = smart_repo.SmartServerRepositoryGetStreamForMissingKeys(
263+ backing)
264+ repo, r1, r2 = self.make_two_commit_repo()
265+ request.execute(b'', b'yada yada yada')
266+ expected = smart_req.FailedSmartServerResponse(
267+ (b'UnknownFormat', b'repository', b'yada yada yada'))
268+
269+
270 class TestSmartServerRepositoryRevisionArchive(tests.TestCaseWithTransport):
271-
272 def test_get(self):
273 backing = self.get_transport()
274 request = smart_repo.SmartServerRepositoryRevisionArchive(backing)

Subscribers

People subscribed via source and target branches