Rev 4194: Cache ghosts when we can get them from a RemoteRepository in get_parent_map. in http://people.ubuntu.com/~robertc/baz2.0/pending/Remote.negative_parents_cache

Robert Collins robertc at robertcollins.net
Tue Mar 24 07:09:18 GMT 2009


At http://people.ubuntu.com/~robertc/baz2.0/pending/Remote.negative_parents_cache

------------------------------------------------------------
revno: 4194
revision-id: robertc at robertcollins.net-20090324070911-w59etn9q3f9xj4fu
parent: robertc at robertcollins.net-20090324060735-nk6ti3wq06062lvz
committer: Robert Collins <robertc at robertcollins.net>
branch nick: Remote.negative_parents_cache
timestamp: Tue 2009-03-24 18:09:11 +1100
message:
  Cache ghosts when we can get them from a RemoteRepository in get_parent_map.
=== modified file 'bzrlib/graph.py'
--- a/bzrlib/graph.py	2009-03-24 05:11:56 +0000
+++ b/bzrlib/graph.py	2009-03-24 07:09:11 +0000
@@ -133,13 +133,13 @@
             raise AssertionError('Cache enabled when already enabled.')
         self._cache = {}
         self._cache_misses = cache_misses
-        self._missing_keys = set()
+        self.missing_keys = set()
 
     def disable_cache(self):
         """Disable and clear the cache."""
         self._cache = None
         self._cache_misses = None
-        self._missing_keys = set()
+        self.missing_keys = set()
 
     def get_cached_map(self):
         """Return any cached get_parent_map values."""
@@ -157,14 +157,14 @@
             # better.
             needed_revisions = set(key for key in keys if key not in cache)
             # Do not ask for negatively cached keys
-            needed_revisions.difference_update(self._missing_keys)
+            needed_revisions.difference_update(self.missing_keys)
             if needed_revisions:
                 parent_map = self._get_parent_map(needed_revisions)
                 cache.update(parent_map)
                 if self._cache_misses:
                     for key in needed_revisions:
                         if key not in parent_map:
-                            self._missing_keys.add(key)
+                            self.note_missing_key(key)
         result = {}
         for key in keys:
             value = cache.get(key)
@@ -172,6 +172,11 @@
                 result[key] = value
         return result
 
+    def note_missing_key(self, key):
+        """Note that key is a missing key."""
+        if self._cache_misses:
+            self.missing_keys.add(key)
+
 
 class Graph(object):
     """Provide incremental access to revision graphs.

=== modified file 'bzrlib/memorytree.py'
--- a/bzrlib/memorytree.py	2009-01-17 01:30:58 +0000
+++ b/bzrlib/memorytree.py	2009-03-24 07:09:11 +0000
@@ -212,8 +212,7 @@
 
     def _populate_from_branch(self):
         """Populate the in-tree state from the branch."""
-        self._basis_tree = self.branch.repository.revision_tree(
-            self._branch_revision_id)
+        self._set_basis()
         if self._branch_revision_id == _mod_revision.NULL_REVISION:
             self._parent_ids = []
         else:
@@ -280,13 +279,23 @@
             _mod_revision.check_not_reserved_id(revision_id)
         if len(revision_ids) == 0:
             self._parent_ids = []
-            self._basis_tree = self.branch.repository.revision_tree(
-                                    _mod_revision.NULL_REVISION)
+            self._branch_revision_id = _mod_revision.NULL_REVISION
         else:
             self._parent_ids = revision_ids
-            self._basis_tree = self.branch.repository.revision_tree(
-                                    revision_ids[0])
             self._branch_revision_id = revision_ids[0]
+        self._allow_leftmost_as_ghost = allow_leftmost_as_ghost
+        self._set_basis()
+    
+    def _set_basis(self):
+        try:
+            self._basis_tree = self.branch.repository.revision_tree(
+                self._branch_revision_id)
+        except errors.NoSuchRevision:
+            if self._allow_leftmost_as_ghost:
+                self._basis_tree = self.branch.repository.revision_tree(
+                    _mod_revision.NULL_REVISION)
+            else:
+                raise
 
     def set_parent_trees(self, parents_list, allow_leftmost_as_ghost=False):
         """See MutableTree.set_parent_trees()."""

=== modified file 'bzrlib/remote.py'
--- a/bzrlib/remote.py	2009-03-24 06:07:35 +0000
+++ b/bzrlib/remote.py	2009-03-24 07:09:11 +0000
@@ -1247,6 +1247,9 @@
         for parents in parents_map.itervalues():
             result_parents.update(parents)
         stop_keys = result_parents.difference(start_set)
+        # We don't need to send ghosts back to the server as a position to
+        # stop either.
+        stop_keys.difference_update(self._unstacked_provider.missing_keys)
         included_keys = start_set.intersection(result_parents)
         start_set.difference_update(included_keys)
         recipe = ('manual', start_set, stop_keys, len(parents_map))
@@ -1257,7 +1260,7 @@
                 raise ValueError(
                     "key %r not a plain string" % (key,))
         verb = 'Repository.get_parent_map'
-        args = (path,) + tuple(keys)
+        args = (path, 'include-missing:') + tuple(keys)
         try:
             response = self._call_with_body_bytes_expecting_body(
                 verb, args, body)
@@ -1291,8 +1294,14 @@
                 if len(d) > 1:
                     revision_graph[d[0]] = d[1:]
                 else:
-                    # No parents - so give the Graph result (NULL_REVISION,).
-                    revision_graph[d[0]] = (NULL_REVISION,)
+                    # No parents:
+                    if d[0].startswith('missing:'):
+                        revid = d[0][8:]
+                        self._unstacked_provider.note_missing_key(revid)
+                    else:
+                        # no parents - so give the Graph result
+                        # (NULL_REVISION,).
+                        revision_graph[d[0]] = (NULL_REVISION,)
             return revision_graph
 
     @needs_read_lock

=== modified file 'bzrlib/tests/test_graph.py'
--- a/bzrlib/tests/test_graph.py	2009-03-24 05:11:56 +0000
+++ b/bzrlib/tests/test_graph.py	2009-03-24 07:09:11 +0000
@@ -1432,6 +1432,13 @@
         # only present 1 time.
         self.assertEqual(['a', 'b'], sorted(self.inst_pp.calls))
 
+    def test_note_missing_key(self):
+        """After noting that a key is missing it is cached."""
+        self.caching_pp.note_missing_key('b')
+        self.assertEqual({}, self.caching_pp.get_parent_map(['b']))
+        self.assertEqual([], self.inst_pp.calls)
+        self.assertEqual(set(['b']), self.caching_pp.missing_keys)
+
 
 class TestCachingParentsProviderExtras(tests.TestCaseWithTransport):
     """Test the behaviour when parents are provided that were not requested."""

=== modified file 'bzrlib/tests/test_remote.py'
--- a/bzrlib/tests/test_remote.py	2009-03-24 05:11:56 +0000
+++ b/bzrlib/tests/test_remote.py	2009-03-24 07:09:11 +0000
@@ -36,6 +36,7 @@
     repository,
     smart,
     tests,
+    treebuilder,
     urlutils,
     )
 from bzrlib.branch import Branch
@@ -1648,6 +1649,32 @@
                 'more-missing']))
         self.assertLength(1, self.hpss_calls)
 
+    def test_get_parent_map_gets_ghosts_from_result(self):
+        # asking for a revision should negatively cache close ghosts in its
+        # ancestry.
+        self.setup_smart_server_with_call_log()
+        tree = self.make_branch_and_memory_tree('foo')
+        tree.lock_write()
+        try:
+            builder = treebuilder.TreeBuilder()
+            builder.start_tree(tree)
+            builder.build([])
+            builder.finish_tree()
+            tree.set_parent_ids(['non-existant'], allow_leftmost_as_ghost=True)
+            rev_id = tree.commit('')
+        finally:
+            tree.unlock()
+        tree.lock_read()
+        self.addCleanup(tree.unlock)
+        repo = tree.branch.repository
+        self.assertIsInstance(repo, RemoteRepository)
+        # ask for rev_id
+        repo.get_parent_map([rev_id])
+        self.reset_smart_call_log()
+        # Now asking for rev_id's ghost parent should not make calls
+        self.assertEqual({}, repo.get_parent_map(['non-existant']))
+        self.assertLength(0, self.hpss_calls)
+
 
 class TestGetParentMapAllowsNew(tests.TestCaseWithTransport):
 
@@ -2368,7 +2395,7 @@
         rev_ord, expected_revs = self.get_ordered_revs('knit', 'topological')
         self.assertEqual(expected_revs, rev_ord)
         # Getting topological sort requires VFS calls still
-        self.assertLength(14, self.hpss_calls)
+        self.assertLength(13, self.hpss_calls)
 
     def test_stacked_get_stream_groupcompress(self):
         # Repository._get_source.get_stream() from a stacked repository with




More information about the bazaar-commits mailing list