Rev 3177: Implement get_parent_map for RemoteRepository with caching, based on get_revision_graph. in http://people.ubuntu.com/~robertc/baz2.0/remote.graph

Robert Collins robertc at robertcollins.net
Fri Jan 11 05:56:55 GMT 2008


At http://people.ubuntu.com/~robertc/baz2.0/remote.graph

------------------------------------------------------------
revno: 3177
revision-id:robertc at robertcollins.net-20080111055647-tdn8bkdri6q5i39w
parent: robertc at robertcollins.net-20080111053222-dxepbgap3vobkvqd
committer: Robert Collins <robertc at robertcollins.net>
branch nick: remote.graph
timestamp: Fri 2008-01-11 16:56:47 +1100
message:
  Implement get_parent_map for RemoteRepository with caching, based on get_revision_graph.
modified:
  bzrlib/remote.py               remote.py-20060720103555-yeeg2x51vn0rbtdp-1
  bzrlib/tests/test_remote.py    test_remote.py-20060720103555-yeeg2x51vn0rbtdp-2
=== modified file 'bzrlib/remote.py'
--- a/bzrlib/remote.py	2008-01-11 05:32:22 +0000
+++ b/bzrlib/remote.py	2008-01-11 05:56:47 +0000
@@ -265,6 +265,8 @@
         self._lock_token = None
         self._lock_count = 0
         self._leave_lock = False
+        # A cache of looked up revision parent data; reset at unlock time.
+        self._parents_map = None
         # For tests:
         # These depend on the actual remote format, so force them off for
         # maximum compatibility. XXX: In future these should depend on the
@@ -468,6 +470,7 @@
         if not self._lock_mode:
             self._lock_mode = 'r'
             self._lock_count = 1
+            self._parents_map = {}
             if self._real_repository is not None:
                 self._real_repository.lock_read()
         else:
@@ -505,6 +508,7 @@
                 self._leave_lock = False
             self._lock_mode = 'w'
             self._lock_count = 1
+            self._parents_map = {}
         elif self._lock_mode == 'r':
             raise errors.ReadOnlyError(self)
         else:
@@ -564,6 +568,7 @@
         self._lock_count -= 1
         if self._lock_count > 0:
             return
+        self._parents_map = None
         old_mode = self._lock_mode
         self._lock_mode = None
         try:
@@ -732,6 +737,15 @@
         self._ensure_real()
         return self._real_repository.iter_files_bytes(desired_files)
 
+    def get_parent_map(self, keys):
+        """See bzrlib.Graph.get_parent_map()."""
+        # Hack to build up the caching logic.
+        ancestry = self._parents_map
+        missing_revisions = set(key for key in keys if key not in ancestry)
+        if missing_revisions:
+            self._parents_map.update(self.get_revision_graph())
+        return dict((k, ancestry[k]) for k in keys if k in ancestry)
+
     @needs_read_lock
     def get_signature_text(self, revision_id):
         self._ensure_real()
@@ -926,12 +940,6 @@
     def _make_parents_provider(self):
         return self
 
-    def get_parent_map(self, keys):
-        # Thunk across to real for now.
-        self._ensure_real()
-        return self._real_repository._make_parents_provider().get_parent_map(
-            keys)
-
 
 class RemoteBranchLockableFiles(LockableFiles):
     """A 'LockableFiles' implementation that talks to a smart server.

=== modified file 'bzrlib/tests/test_remote.py'
--- a/bzrlib/tests/test_remote.py	2008-01-11 05:32:22 +0000
+++ b/bzrlib/tests/test_remote.py	2008-01-11 05:56:47 +0000
@@ -484,18 +484,6 @@
         return repo, client
 
 
-class TestRepositoryGetGraph(TestRemoteRepository):
-
-    def test_get_graph(self):
-        # get_graph returns a graph with get_parent_map as the repository.
-        responses = []
-        transport_path = 'quack'
-        repo, client = self.setup_fake_client_and_repository(
-            responses, transport_path)
-        graph = repo.get_graph()
-        self.assertEqual(graph._parents_provider, repo)
-
-
 class TestRepositoryGatherStats(TestRemoteRepository):
 
     def test_revid_none(self):
@@ -556,6 +544,61 @@
                          result)
 
 
+class TestRepositoryGetGraph(TestRemoteRepository):
+
+    def test_get_graph(self):
+        # get_graph returns a graph with get_parent_map as the repository.
+        responses = []
+        transport_path = 'quack'
+        repo, client = self.setup_fake_client_and_repository(
+            responses, transport_path)
+        graph = repo.get_graph()
+        self.assertEqual(graph._parents_provider, repo)
+
+
+class TestRepositoryGetParentMap(TestRemoteRepository):
+
+    def test_get_parent_map_caching(self):
+        # get_parent_map returns from cache until unlock()
+        # setup a reponse with two revisions
+        r1 = u'\u0e33'.encode('utf8')
+        r2 = u'\u0dab'.encode('utf8')
+        lines = [' '.join([r2, r1]), r1]
+        encoded_body = '\n'.join(lines)
+        responses = [(('ok', ), encoded_body), (('ok', ), encoded_body)]
+
+        transport_path = 'quack'
+        repo, client = self.setup_fake_client_and_repository(
+            responses, transport_path)
+        repo.lock_read()
+        graph = repo.get_graph()
+        parents = graph.get_parent_map([r2])
+        self.assertEqual({r2: (r1,)}, parents)
+        # locking and unlocking deeper should not reset
+        repo.lock_read()
+        repo.unlock()
+        parents = graph.get_parent_map([r1])
+        self.assertEqual({r1: ()}, parents)
+        self.assertEqual(
+            [('call_expecting_body', 'Repository.get_revision_graph',
+             ('///quack/', ''))],
+            client._calls)
+        repo.unlock()
+        # now we call again, and it should use the second response.
+        repo.lock_read()
+        graph = repo.get_graph()
+        parents = graph.get_parent_map([r2])
+        self.assertEqual({r2: (r1,)}, parents)
+        self.assertEqual(
+            [('call_expecting_body', 'Repository.get_revision_graph',
+              ('///quack/', '')),
+             ('call_expecting_body', 'Repository.get_revision_graph',
+              ('///quack/', ''))
+            ],
+            client._calls)
+        repo.unlock()
+
+
 class TestRepositoryGetRevisionGraph(TestRemoteRepository):
     
     def test_null_revision(self):
@@ -719,7 +762,7 @@
             responses, transport_path)
 
         # The null revision is always there, so has_revision(None) == True.
-        self.assertEqual(True, repo.has_revision(None))
+        self.assertEqual(True, repo.has_revision(NULL_REVISION))
 
         # The remote repo shouldn't be accessed.
         self.assertEqual([], client._calls)



More information about the bazaar-commits mailing list