Rev 4148: (andrew) Add RPC that can insert a stream into older (i.e. lockable) in file:///home/pqm/archives/thelove/bzr/%2Btrunk/
Canonical.com Patch Queue Manager
pqm at pqm.ubuntu.com
Mon Mar 16 06:51:40 GMT 2009
At file:///home/pqm/archives/thelove/bzr/%2Btrunk/
------------------------------------------------------------
revno: 4148
revision-id: pqm at pqm.ubuntu.com-20090316065136-1g3udxbvvlgtsbup
parent: pqm at pqm.ubuntu.com-20090316041621-taek91nogxt42bfy
parent: andrew.bennetts at canonical.com-20090316055842-6jmbqwy3q4apljtn
committer: Canonical.com Patch Queue Manager <pqm at pqm.ubuntu.com>
branch nick: +trunk
timestamp: Mon 2009-03-16 06:51:36 +0000
message:
(andrew) Add RPC that can insert a stream into older (i.e. lockable)
repository formats.
modified:
NEWS NEWS-20050323055033-4e00b5db738777ff
bzrlib/remote.py remote.py-20060720103555-yeeg2x51vn0rbtdp-1
bzrlib/smart/repository.py repository.py-20061128022038-vr5wy5bubyb8xttk-1
bzrlib/smart/request.py request.py-20061108095550-gunadhxmzkdjfeek-1
bzrlib/tests/test_remote.py test_remote.py-20060720103555-yeeg2x51vn0rbtdp-2
bzrlib/tests/test_smart.py test_smart.py-20061122024551-ol0l0o0oofsu9b3t-2
bzrlib/tests/test_smart_request.py test_smart_request.p-20090211070731-o38wayv3asm25d6a-1
------------------------------------------------------------
revno: 4144.3.5
revision-id: andrew.bennetts at canonical.com-20090316055842-6jmbqwy3q4apljtn
parent: andrew.bennetts at canonical.com-20090316055832-fn87u872p0kctjrt
parent: pqm at pqm.ubuntu.com-20090316041621-taek91nogxt42bfy
committer: Andrew Bennetts <andrew.bennetts at canonical.com>
branch nick: simplify-interrepo-stack
timestamp: Mon 2009-03-16 16:58:42 +1100
message:
Merge from bzr.dev
modified:
NEWS NEWS-20050323055033-4e00b5db738777ff
bzrlib/builtins.py builtins.py-20050830033751-fc01482b9ca23183
bzrlib/tests/__init__.py selftest.py-20050531073622-8d0e3c8845c97a64
bzrlib/tests/blackbox/test_merge.py test_merge.py-20060323225809-9bc0459c19917f41
bzrlib/tests/test_selftest.py test_selftest.py-20051202044319-c110a115d8c0456a
------------------------------------------------------------
revno: 4144.3.4
revision-id: andrew.bennetts at canonical.com-20090316055832-fn87u872p0kctjrt
parent: andrew.bennetts at canonical.com-20090316055542-8dpb2i3xvmairepn
committer: Andrew Bennetts <andrew.bennetts at canonical.com>
branch nick: simplify-interrepo-stack
timestamp: Mon 2009-03-16 16:58:32 +1100
message:
Add NEWS entry.
modified:
NEWS NEWS-20050323055033-4e00b5db738777ff
------------------------------------------------------------
revno: 4144.3.3
revision-id: andrew.bennetts at canonical.com-20090316055542-8dpb2i3xvmairepn
parent: andrew.bennetts at canonical.com-20090316054753-0mfo07r21tsjl5p0
committer: Andrew Bennetts <andrew.bennetts at canonical.com>
branch nick: simplify-interrepo-stack
timestamp: Mon 2009-03-16 16:55:42 +1100
message:
Tweaks based on review from Robert.
modified:
bzrlib/smart/repository.py repository.py-20061128022038-vr5wy5bubyb8xttk-1
bzrlib/tests/test_remote.py test_remote.py-20060720103555-yeeg2x51vn0rbtdp-2
------------------------------------------------------------
revno: 4144.3.2
revision-id: andrew.bennetts at canonical.com-20090316054753-0mfo07r21tsjl5p0
parent: andrew.bennetts at canonical.com-20090316044809-0bsw43pof8fq7byl
committer: Andrew Bennetts <andrew.bennetts at canonical.com>
branch nick: simplify-interrepo-stack
timestamp: Mon 2009-03-16 16:47:53 +1100
message:
Use Repository.insert_stream_locked if there is a lock_token for the remote repo.
modified:
bzrlib/remote.py remote.py-20060720103555-yeeg2x51vn0rbtdp-1
bzrlib/tests/test_remote.py test_remote.py-20060720103555-yeeg2x51vn0rbtdp-2
------------------------------------------------------------
revno: 4144.3.1
revision-id: andrew.bennetts at canonical.com-20090316044809-0bsw43pof8fq7byl
parent: andrew.bennetts at canonical.com-20090316022525-49vw2o0on4foz3u0
committer: Andrew Bennetts <andrew.bennetts at canonical.com>
branch nick: simplify-interrepo-stack
timestamp: Mon 2009-03-16 15:48:09 +1100
message:
Add Repository.insert_stream_locked server-side implementation, plus tests for server-side _translate_error.
modified:
bzrlib/smart/repository.py repository.py-20061128022038-vr5wy5bubyb8xttk-1
bzrlib/smart/request.py request.py-20061108095550-gunadhxmzkdjfeek-1
bzrlib/tests/test_smart.py test_smart.py-20061122024551-ol0l0o0oofsu9b3t-2
bzrlib/tests/test_smart_request.py test_smart_request.p-20090211070731-o38wayv3asm25d6a-1
=== modified file 'NEWS'
--- a/NEWS 2009-03-16 03:35:34 +0000
+++ b/NEWS 2009-03-16 05:58:42 +0000
@@ -32,6 +32,10 @@
* Progress bars now show the rate of network activity for
``bzr+ssh://`` and ``bzr://`` connections. (Andrew Bennetts)
+ * Streaming push can be done to older repository formats. This is
+ implemented using a new ``Repository.insert_stream_locked`` RPC.
+ (Andrew Bennetts, Robert Collins)
+
* Tildes are no longer escaped. No more %7Euser/project/branch!
(Jonathan Lange)
=== modified file 'bzrlib/remote.py'
--- a/bzrlib/remote.py 2009-03-12 08:50:04 +0000
+++ b/bzrlib/remote.py 2009-03-16 05:47:53 +0000
@@ -1522,13 +1522,21 @@
return result
def insert_stream(self, stream, src_format, resume_tokens):
- repo = self.target_repo
- client = repo._client
+ target = self.target_repo
+ if target._lock_token:
+ verb = 'Repository.insert_stream_locked'
+ extra_args = (target._lock_token or '',)
+ required_version = (1, 14)
+ else:
+ verb = 'Repository.insert_stream'
+ extra_args = ()
+ required_version = (1, 13)
+ client = target._client
medium = client._medium
- if medium._is_remote_before((1, 13)):
+ if medium._is_remote_before(required_version):
# No possible way this can work.
return self._insert_real(stream, src_format, resume_tokens)
- path = repo.bzrdir._path_for_remote_call(client)
+ path = target.bzrdir._path_for_remote_call(client)
if not resume_tokens:
# XXX: Ugly but important for correctness, *will* be fixed during
# 1.13 cycle. Pushing a stream that is interrupted results in a
@@ -1541,15 +1549,15 @@
byte_stream = smart_repo._stream_to_byte_stream([], src_format)
try:
response = client.call_with_body_stream(
- ('Repository.insert_stream', path, ''), byte_stream)
+ (verb, path, '') + extra_args, byte_stream)
except errors.UnknownSmartMethod:
- medium._remember_remote_is_before((1,13))
+ medium._remember_remote_is_before(required_version)
return self._insert_real(stream, src_format, resume_tokens)
byte_stream = smart_repo._stream_to_byte_stream(
stream, src_format)
resume_tokens = ' '.join(resume_tokens)
response = client.call_with_body_stream(
- ('Repository.insert_stream', path, resume_tokens), byte_stream)
+ (verb, path, resume_tokens) + extra_args, byte_stream)
if response[0][0] not in ('ok', 'missing-basis'):
raise errors.UnexpectedSmartServerResponse(response)
if response[0][0] == 'missing-basis':
=== modified file 'bzrlib/smart/repository.py'
--- a/bzrlib/smart/repository.py 2009-03-06 08:13:23 +0000
+++ b/bzrlib/smart/repository.py 2009-03-16 05:55:42 +0000
@@ -524,17 +524,22 @@
tarball.close()
-class SmartServerRepositoryInsertStream(SmartServerRepositoryRequest):
+class SmartServerRepositoryInsertStreamLocked(SmartServerRepositoryRequest):
"""Insert a record stream from a RemoteSink into a repository.
This gets bytes pushed to it by the network infrastructure and turns that
into a bytes iterator using a thread. That is then processed by
_byte_stream_to_stream.
+
+ New in 1.14.
"""
- def do_repository_request(self, repository, resume_tokens):
+ def do_repository_request(self, repository, resume_tokens, lock_token):
"""StreamSink.insert_stream for a remote repository."""
- repository.lock_write()
+ repository.lock_write(token=lock_token)
+ self.do_insert_stream_request(repository, resume_tokens)
+
+ def do_insert_stream_request(self, repository, resume_tokens):
tokens = [token for token in resume_tokens.split(' ') if token]
self.tokens = tokens
self.repository = repository
@@ -582,3 +587,21 @@
else:
self.repository.unlock()
return SuccessfulSmartServerResponse(('ok', ))
+
+
+class SmartServerRepositoryInsertStream(SmartServerRepositoryInsertStreamLocked):
+ """Insert a record stream from a RemoteSink into an unlocked repository.
+
+ This is the same as SmartServerRepositoryInsertStreamLocked, except it
+ takes no lock_tokens; i.e. it works with an unlocked (or lock-free, e.g.
+ like pack format) repository.
+
+ New in 1.13.
+ """
+
+ def do_repository_request(self, repository, resume_tokens):
+ """StreamSink.insert_stream for a remote repository."""
+ repository.lock_write()
+ self.do_insert_stream_request(repository, resume_tokens)
+
+
=== modified file 'bzrlib/smart/request.py'
--- a/bzrlib/smart/request.py 2009-03-06 03:55:27 +0000
+++ b/bzrlib/smart/request.py 2009-03-16 04:48:09 +0000
@@ -344,6 +344,10 @@
return ('ReadError', err.path)
elif isinstance(err, errors.PermissionDenied):
return ('PermissionDenied', err.path, err.extra)
+ elif isinstance(err, errors.TokenMismatch):
+ return ('TokenMismatch', err.given_token, err.lock_token)
+ elif isinstance(err, errors.LockContention):
+ return ('LockContention', err.lock, err.msg)
# Unserialisable error. Log it, and return a generic error
trace.log_exception_quietly()
return ('error', str(err))
@@ -482,6 +486,8 @@
request_handlers.register_lazy(
'Repository.insert_stream', 'bzrlib.smart.repository', 'SmartServerRepositoryInsertStream')
request_handlers.register_lazy(
+ 'Repository.insert_stream_locked', 'bzrlib.smart.repository', 'SmartServerRepositoryInsertStreamLocked')
+request_handlers.register_lazy(
'Repository.is_shared', 'bzrlib.smart.repository', 'SmartServerRepositoryIsShared')
request_handlers.register_lazy(
'Repository.lock_write', 'bzrlib.smart.repository', 'SmartServerRepositoryLockWrite')
=== modified file 'bzrlib/tests/test_remote.py'
--- a/bzrlib/tests/test_remote.py 2009-03-13 02:13:08 +0000
+++ b/bzrlib/tests/test_remote.py 2009-03-16 05:55:42 +0000
@@ -270,7 +270,11 @@
stream = list(stream)
self._check_call(args[0], args[1:])
self._calls.append(('call_with_body_stream', args[0], args[1:], stream))
- return self._get_next_response()[1]
+ result = self._get_next_response()
+ # The second value returned from call_with_body_stream is supposed to
+ # be a response_handler object, but so far no tests depend on that.
+ response_handler = None
+ return result[1], response_handler
class FakeMedium(medium.SmartClientMedium):
@@ -1825,6 +1829,65 @@
self.assertEqual([], client._calls)
+class TestRepositoryInsertStream(TestRemoteRepository):
+
+ def test_unlocked_repo(self):
+ transport_path = 'quack'
+ repo, client = self.setup_fake_client_and_repository(transport_path)
+ client.add_expected_call(
+ 'Repository.insert_stream', ('quack/', ''),
+ 'success', ('ok',))
+ client.add_expected_call(
+ 'Repository.insert_stream', ('quack/', ''),
+ 'success', ('ok',))
+ sink = repo._get_sink()
+ fmt = repository.RepositoryFormat.get_default_format()
+ resume_tokens, missing_keys = sink.insert_stream([], fmt, [])
+ self.assertEqual([], resume_tokens)
+ self.assertEqual(set(), missing_keys)
+ client.finished_test()
+
+ def test_locked_repo_with_no_lock_token(self):
+ transport_path = 'quack'
+ repo, client = self.setup_fake_client_and_repository(transport_path)
+ client.add_expected_call(
+ 'Repository.lock_write', ('quack/', ''),
+ 'success', ('ok', ''))
+ client.add_expected_call(
+ 'Repository.insert_stream', ('quack/', ''),
+ 'success', ('ok',))
+ client.add_expected_call(
+ 'Repository.insert_stream', ('quack/', ''),
+ 'success', ('ok',))
+ repo.lock_write()
+ sink = repo._get_sink()
+ fmt = repository.RepositoryFormat.get_default_format()
+ resume_tokens, missing_keys = sink.insert_stream([], fmt, [])
+ self.assertEqual([], resume_tokens)
+ self.assertEqual(set(), missing_keys)
+ client.finished_test()
+
+ def test_locked_repo_with_lock_token(self):
+ transport_path = 'quack'
+ repo, client = self.setup_fake_client_and_repository(transport_path)
+ client.add_expected_call(
+ 'Repository.lock_write', ('quack/', ''),
+ 'success', ('ok', 'a token'))
+ client.add_expected_call(
+ 'Repository.insert_stream_locked', ('quack/', '', 'a token'),
+ 'success', ('ok',))
+ client.add_expected_call(
+ 'Repository.insert_stream_locked', ('quack/', '', 'a token'),
+ 'success', ('ok',))
+ repo.lock_write()
+ sink = repo._get_sink()
+ fmt = repository.RepositoryFormat.get_default_format()
+ resume_tokens, missing_keys = sink.insert_stream([], fmt, [])
+ self.assertEqual([], resume_tokens)
+ self.assertEqual(set(), missing_keys)
+ client.finished_test()
+
+
class TestRepositoryTarball(TestRemoteRepository):
# This is a canned tarball reponse we can validate against
=== modified file 'bzrlib/tests/test_smart.py'
--- a/bzrlib/tests/test_smart.py 2009-03-09 08:45:56 +0000
+++ b/bzrlib/tests/test_smart.py 2009-03-16 04:48:09 +0000
@@ -1114,9 +1114,6 @@
class TestSmartServerRepositoryLockWrite(tests.TestCaseWithMemoryTransport):
- def setUp(self):
- tests.TestCaseWithMemoryTransport.setUp(self)
-
def test_lock_write_on_unlocked_repo(self):
backing = self.get_transport()
request = smart.repository.SmartServerRepositoryLockWrite(backing)
@@ -1149,6 +1146,54 @@
self.assertEqual('LockFailed', response.args[0])
+class TestInsertStreamBase(tests.TestCaseWithMemoryTransport):
+
+ def make_empty_byte_stream(self, repo):
+ byte_stream = smart.repository._stream_to_byte_stream([], repo._format)
+ return ''.join(byte_stream)
+
+
+class TestSmartServerRepositoryInsertStream(TestInsertStreamBase):
+
+ def test_insert_stream_empty(self):
+ backing = self.get_transport()
+ request = smart.repository.SmartServerRepositoryInsertStream(backing)
+ repository = self.make_repository('.')
+ response = request.execute('', '')
+ self.assertEqual(None, response)
+ response = request.do_chunk(self.make_empty_byte_stream(repository))
+ self.assertEqual(None, response)
+ response = request.do_end()
+ self.assertEqual(SmartServerResponse(('ok', )), response)
+
+
+class TestSmartServerRepositoryInsertStreamLocked(TestInsertStreamBase):
+
+ def test_insert_stream_empty(self):
+ backing = self.get_transport()
+ request = smart.repository.SmartServerRepositoryInsertStreamLocked(
+ backing)
+ repository = self.make_repository('.', format='knit')
+ lock_token = repository.lock_write()
+ response = request.execute('', '', lock_token)
+ self.assertEqual(None, response)
+ response = request.do_chunk(self.make_empty_byte_stream(repository))
+ self.assertEqual(None, response)
+ response = request.do_end()
+ self.assertEqual(SmartServerResponse(('ok', )), response)
+ repository.unlock()
+
+ def test_insert_stream_with_wrong_lock_token(self):
+ backing = self.get_transport()
+ request = smart.repository.SmartServerRepositoryInsertStreamLocked(
+ backing)
+ repository = self.make_repository('.', format='knit')
+ lock_token = repository.lock_write()
+ self.assertRaises(
+ errors.TokenMismatch, request.execute, '', '', 'wrong-token')
+ repository.unlock()
+
+
class TestSmartServerRepositoryUnlock(tests.TestCaseWithMemoryTransport):
def setUp(self):
@@ -1339,18 +1384,24 @@
'Repository.get_revision_graph'),
smart.repository.SmartServerRepositoryGetRevisionGraph)
self.assertEqual(
+ smart.request.request_handlers.get('Repository.get_stream'),
+ smart.repository.SmartServerRepositoryGetStream)
+ self.assertEqual(
smart.request.request_handlers.get('Repository.has_revision'),
smart.repository.SmartServerRequestHasRevision)
self.assertEqual(
+ smart.request.request_handlers.get('Repository.insert_stream'),
+ smart.repository.SmartServerRepositoryInsertStream)
+ self.assertEqual(
+ smart.request.request_handlers.get('Repository.insert_stream_locked'),
+ smart.repository.SmartServerRepositoryInsertStreamLocked)
+ self.assertEqual(
smart.request.request_handlers.get('Repository.is_shared'),
smart.repository.SmartServerRepositoryIsShared)
self.assertEqual(
smart.request.request_handlers.get('Repository.lock_write'),
smart.repository.SmartServerRepositoryLockWrite)
self.assertEqual(
- smart.request.request_handlers.get('Repository.get_stream'),
- smart.repository.SmartServerRepositoryGetStream)
- self.assertEqual(
smart.request.request_handlers.get('Repository.tarball'),
smart.repository.SmartServerRepositoryTarball)
self.assertEqual(
=== modified file 'bzrlib/tests/test_smart_request.py'
--- a/bzrlib/tests/test_smart_request.py 2009-03-02 05:38:55 +0000
+++ b/bzrlib/tests/test_smart_request.py 2009-03-16 04:48:09 +0000
@@ -28,6 +28,39 @@
return request.SuccessfulSmartServerResponse(('ok',))
+class DoErrorRequest(request.SmartServerRequest):
+ """A request that raises an error from self.do()."""
+
+ def do(self):
+ raise errors.NoSuchFile('xyzzy')
+
+
+class ChunkErrorRequest(request.SmartServerRequest):
+ """A request that raises an error from self.do_chunk()."""
+
+ def do(self):
+ """No-op."""
+ pass
+
+ def do_chunk(self, bytes):
+ raise errors.NoSuchFile('xyzzy')
+
+
+class EndErrorRequest(request.SmartServerRequest):
+ """A request that raises an error from self.do_end()."""
+
+ def do(self):
+ """No-op."""
+ pass
+
+ def do_chunk(self, bytes):
+ """No-op."""
+ pass
+
+ def do_end(self):
+ raise errors.NoSuchFile('xyzzy')
+
+
class TestSmartRequest(TestCase):
def test_request_class_without_do_body(self):
@@ -43,3 +76,61 @@
handler.end_received()
# Request done, no exception was raised.
+
+class TestSmartRequestHandlerErrorTranslation(TestCase):
+ """Tests that SmartServerRequestHandler will translate exceptions raised by
+ a SmartServerRequest into FailedSmartServerResponses.
+ """
+
+ def assertNoResponse(self, handler):
+ self.assertEqual(None, handler.response)
+
+ def assertResponseIsTranslatedError(self, handler):
+ expected_translation = ('NoSuchFile', 'xyzzy')
+ self.assertEqual(
+ request.FailedSmartServerResponse(expected_translation),
+ handler.response)
+
+ def test_error_translation_from_args_received(self):
+ handler = request.SmartServerRequestHandler(
+ None, {'foo': DoErrorRequest}, '/')
+ handler.args_received(('foo',))
+ self.assertResponseIsTranslatedError(handler)
+
+ def test_error_translation_from_chunk_received(self):
+ handler = request.SmartServerRequestHandler(
+ None, {'foo': ChunkErrorRequest}, '/')
+ handler.args_received(('foo',))
+ self.assertNoResponse(handler)
+ handler.accept_body('bytes')
+ self.assertResponseIsTranslatedError(handler)
+
+ def test_error_translation_from_end_received(self):
+ handler = request.SmartServerRequestHandler(
+ None, {'foo': EndErrorRequest}, '/')
+ handler.args_received(('foo',))
+ self.assertNoResponse(handler)
+ handler.end_received()
+ self.assertResponseIsTranslatedError(handler)
+
+
+class TestRequestHanderErrorTranslation(TestCase):
+ """Tests for bzrlib.smart.request._translate_error."""
+
+ def assertTranslationEqual(self, expected_tuple, error):
+ self.assertEqual(expected_tuple, request._translate_error(error))
+
+ def test_NoSuchFile(self):
+ self.assertTranslationEqual(
+ ('NoSuchFile', 'path'), errors.NoSuchFile('path'))
+
+ def test_LockContention(self):
+ self.assertTranslationEqual(
+ ('LockContention', 'lock', 'msg'),
+ errors.LockContention('lock', 'msg'))
+
+ def test_TokenMismatch(self):
+ self.assertTranslationEqual(
+ ('TokenMismatch', 'some-token', 'actual-token'),
+ errors.TokenMismatch('some-token', 'actual-token'))
+
More information about the bazaar-commits
mailing list