Rev 2618: * New method on Repository - ``start_write_group``, ``end_write_group`` in http://people.ubuntu.com/~robertc/baz2.0/repo-write-group
Robert Collins
robertc at robertcollins.net
Mon Jul 16 12:01:39 BST 2007
At http://people.ubuntu.com/~robertc/baz2.0/repo-write-group
------------------------------------------------------------
revno: 2618
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-13 06:12:27 +0000
+++ b/NEWS 2007-07-16 11:01:20 +0000
@@ -47,6 +47,13 @@
the null revision, and consider using ``None`` for this purpose
deprecated. (Aaron Bentley)
+ * 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-12 13:35:54 +0000
+++ b/bzrlib/repository.py 2007-07-16 11:01:20 +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