Rev 2663: Merge write_group support. in http://people.ubuntu.com/~robertc/baz2.0/repository

Robert Collins robertc at robertcollins.net
Mon Jul 16 12:03:56 BST 2007


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

------------------------------------------------------------
revno: 2663
revision-id: robertc at robertcollins.net-20070716110352-caegeodiwg9fyvah
parent: robertc at robertcollins.net-20070716072932-jg6juekv52scgwu3
parent: robertc at robertcollins.net-20070716110120-9we93ynxjza948vd
committer: Robert Collins <robertc at robertcollins.net>
branch nick: repository
timestamp: Mon 2007-07-16 21:03:52 +1000
message:
  Merge write_group support.
added:
  bzrlib/tests/repository_implementations/test_write_group.py test_write_group.py-20070716105516-89n34xtogq5frn0m-1
modified:
  NEWS                           NEWS-20050323055033-4e00b5db738777ff
  bzrlib/remote.py               remote.py-20060720103555-yeeg2x51vn0rbtdp-1
  bzrlib/repository.py           rev_storage.py-20051111201905-119e9401e46257e3
  bzrlib/tests/repository_implementations/__init__.py __init__.py-20060131092037-9564957a7d4a841b
    ------------------------------------------------------------
    revno: 2592.1.25.1.1
    revision-id: robertc at robertcollins.net-20070716110120-9we93ynxjza948vd
    parent: pqm at pqm.ubuntu.com-20070713074627-93zxs9uh528y0fki
    committer: Robert Collins <robertc at robertcollins.net>
    branch nick: repo-write-group
    timestamp: Mon 2007-07-16 21:01:20 +1000
    message:
      * New method on Repository - ``start_write_group``, ``end_write_group``
        and ``is_in_write_group`` - which provide a clean hook point for 
        transactional Repositories - ones where all the data for a fetch or
        commit needs to be made atomically available in one step. This allows
        the write lock to remain while making a series of data insertions.
        (e.g. data conversion). (Robert Collins)
    added:
      bzrlib/tests/repository_implementations/test_write_group.py test_write_group.py-20070716105516-89n34xtogq5frn0m-1
    modified:
      NEWS                           NEWS-20050323055033-4e00b5db738777ff
      bzrlib/remote.py               remote.py-20060720103555-yeeg2x51vn0rbtdp-1
      bzrlib/repository.py           rev_storage.py-20051111201905-119e9401e46257e3
      bzrlib/tests/repository_implementations/__init__.py __init__.py-20060131092037-9564957a7d4a841b
=== added file 'bzrlib/tests/repository_implementations/test_write_group.py'
--- a/bzrlib/tests/repository_implementations/test_write_group.py	1970-01-01 00:00:00 +0000
+++ b/bzrlib/tests/repository_implementations/test_write_group.py	2007-07-16 11:01:20 +0000
@@ -0,0 +1,84 @@
+# Copyright (C) 2007 Canonical Ltd
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+"""Tests for repository write groups."""
+
+from bzrlib import errors
+from bzrlib.tests.repository_implementations.test_repository import TestCaseWithRepository
+
+
+class TestWriteGroup(TestCaseWithRepository):
+
+    def test_start_write_group_unlocked_needs_write_lock(self):
+        repo = self.make_repository('.')
+        self.assertRaises(errors.NotWriteLocked, repo.start_write_group)
+
+    def test_start_write_group_read_locked_needs_write_lock(self):
+        repo = self.make_repository('.')
+        repo.lock_read()
+        try:
+            self.assertRaises(errors.NotWriteLocked, repo.start_write_group)
+        finally:
+            repo.unlock()
+
+    def test_start_write_group_write_locked_gets_None(self):
+        repo = self.make_repository('.')
+        repo.lock_write()
+        self.assertEqual(None, repo.start_write_group())
+        repo.end_write_group()
+        repo.unlock()
+
+    def test_start_write_group_twice_errors(self):
+        repo = self.make_repository('.')
+        repo.lock_write()
+        repo.start_write_group()
+        try:
+            # don't need a specific exception for now - this is 
+            # really to be sure it's used right, not for signalling
+            # semantic information.
+            self.assertRaises(errors.BzrError, repo.start_write_group)
+        finally:
+            repo.end_write_group()
+            repo.unlock()
+
+    def test_end_write_group_gets_None(self):
+        repo = self.make_repository('.')
+        repo.lock_write()
+        repo.start_write_group()
+        self.assertEqual(None, repo.end_write_group())
+        repo.unlock()
+
+    def test_unlock_after_start_errors(self):
+        repo = self.make_repository('.')
+        repo.lock_write()
+        repo.start_write_group()
+        # don't need a specific exception for now - this is 
+        # really to be sure it's used right, not for signalling
+        # semantic information.
+        self.assertRaises(errors.BzrError, repo.unlock)
+        self.assertTrue(repo.is_locked())
+        repo.end_write_group()
+        repo.unlock()
+
+    def test_is_in_write_group(self):
+        repo = self.make_repository('.')
+        self.assertFalse(repo.is_in_write_group())
+        repo.lock_write()
+        repo.start_write_group()
+        self.assertTrue(repo.is_in_write_group())
+        repo.end_write_group()
+        self.assertFalse(repo.is_in_write_group())
+        repo.unlock()

=== modified file 'NEWS'
--- a/NEWS	2007-07-14 10:39:27 +0000
+++ b/NEWS	2007-07-16 11:03:52 +0000
@@ -61,6 +61,13 @@
     * New ``file_collection.FileCollection`` support class which mananges names
       for unlistable transport situations. (Robert Collins)
 
+    * New method on Repository - ``start_write_group``, ``end_write_group``
+      and ``is_in_write_group`` - which provide a clean hook point for 
+      transactional Repositories - ones where all the data for a fetch or
+      commit needs to be made atomically available in one step. This allows
+      the write lock to remain while making a series of data insertions.
+      (e.g. data conversion). (Robert Collins)
+
   TESTING:
 
     * Remove selftest ``--clean-output``, ``--numbered-dirs`` and

=== modified file 'bzrlib/remote.py'
--- a/bzrlib/remote.py	2007-07-12 12:07:13 +0000
+++ b/bzrlib/remote.py	2007-07-16 11:01:20 +0000
@@ -249,6 +249,17 @@
         self._lock_count = 0
         self._leave_lock = False
 
+    def end_write_group(self):
+        """End a write group on the decorated repository.
+        
+        Smart methods peform operations in a single step so this api
+        is not really applicable except as a compatability thunk
+        for older plugins that don't use e.g. the CommitBuilder
+        facility.
+        """
+        self._ensure_real()
+        return self._real_repository.end_write_group()
+
     def _ensure_real(self):
         """Ensure that there is a _real_repository set.
 
@@ -337,6 +348,17 @@
         """See Repository.get_physical_lock_status()."""
         return False
 
+    def is_in_write_group(self):
+        """Return True if there is an open write group.
+
+        write groups are only applicable locally for the smart server..
+        """
+        if self._real_repository:
+            return self._real_repository.is_in_write_group()
+
+    def is_locked(self):
+        return self._lock_count >= 1
+
     def is_shared(self):
         """See Repository.is_shared()."""
         path = self.bzrdir._path_for_remote_call(self._client)
@@ -408,6 +430,17 @@
         elif self._lock_mode == 'r':
             self._real_repository.lock_read()
 
+    def start_write_group(self):
+        """Start a write group on the decorated repository.
+        
+        Smart methods peform operations in a single step so this api
+        is not really applicable except as a compatability thunk
+        for older plugins that don't use e.g. the CommitBuilder
+        facility.
+        """
+        self._ensure_real()
+        return self._real_repository.start_write_group()
+
     def _unlock(self, token):
         path = self.bzrdir._path_for_remote_call(self._client)
         response = self._client.call('Repository.unlock', path, token)
@@ -419,6 +452,11 @@
             raise errors.UnexpectedSmartServerResponse(response)
 
     def unlock(self):
+        if self._lock_count == 1 and self._lock_mode == 'w':
+            # don't unlock if inside a write group.
+            if self.is_in_write_group():
+                raise errors.BzrError(
+                    'Must end write groups before releasing write locks.')
         self._lock_count -= 1
         if not self._lock_count:
             mode = self._lock_mode

=== modified file 'bzrlib/repository.py'
--- a/bzrlib/repository.py	2007-07-15 04:10:46 +0000
+++ b/bzrlib/repository.py	2007-07-16 11:03:52 +0000
@@ -224,11 +224,19 @@
         # TODO: make sure to construct the right store classes, etc, depending
         # on whether escaping is required.
         self._warn_if_deprecated()
+        self._write_group = None
 
     def __repr__(self):
         return '%s(%r)' % (self.__class__.__name__, 
                            self.bzrdir.transport.base)
 
+    def is_in_write_group(self):
+        """Return True if there is an open write group.
+
+        :seealso: start_write_group.
+        """
+        return self._write_group is not None
+
     def is_locked(self):
         return self.control_files.is_locked()
 
@@ -353,6 +361,16 @@
         revision_id = osutils.safe_revision_id(revision_id)
         return InterRepository.get(self, destination).copy_content(revision_id)
 
+    def end_write_group(self):
+        """End a write group in the repository.
+
+        :seealso: start_write_group.
+        """
+        if self._write_group is not self.get_transaction():
+            # has an unlock or relock occured ?
+            raise errors.BzrError('mismatched lock context and write group.')
+        self._write_group = None
+
     def fetch(self, source, revision_id=None, pb=None):
         """Fetch the content required to construct revision_id from source.
 
@@ -384,6 +402,11 @@
                               committer, revprops, revision_id)
 
     def unlock(self):
+        if (self.control_files._lock_count == 1 and
+            self.control_files._lock_mode == 'w'):
+            if self._write_group is not None:
+                raise errors.BzrError(
+                    'Must end write groups before releasing write locks.')
         self.control_files.unlock()
 
     @needs_read_lock
@@ -401,6 +424,23 @@
         self.copy_content_into(dest_repo, revision_id)
         return dest_repo
 
+    def start_write_group(self):
+        """Start a write group in the repository.
+
+        Write groups are used by repositories which do not have a 1:1 mapping
+        between file ids and backend store to manage the insertion of data from
+        both fetch and commit operations.
+
+        A write lock is required around the start_write_group/end_write_group
+        for the support of lock-requiring repository formats.
+        """
+        if not self.is_locked() or self.control_files._lock_mode != 'w':
+            raise errors.NotWriteLocked(self)
+        if self._write_group:
+            raise errors.BzrError('already in a write group')
+        # so we can detect unlock/relock.
+        self._write_group = self.get_transaction()
+
     @needs_read_lock
     def sprout(self, to_bzrdir, revision_id=None):
         """Create a descendent repository for new development.

=== modified file 'bzrlib/tests/repository_implementations/__init__.py'
--- a/bzrlib/tests/repository_implementations/__init__.py	2007-07-12 12:07:13 +0000
+++ b/bzrlib/tests/repository_implementations/__init__.py	2007-07-16 11:01:20 +0000
@@ -107,6 +107,7 @@
         'bzrlib.tests.repository_implementations.test_repository',
         'bzrlib.tests.repository_implementations.test_revision',
         'bzrlib.tests.repository_implementations.test_statistics',
+        'bzrlib.tests.repository_implementations.test_write_group',
         ]
 
     from bzrlib.smart.server import (



More information about the bazaar-commits mailing list