Merge lp:~jelmer/bzr/move-write-group into lp:bzr

Proposed by Jelmer Vernooij
Status: Rejected
Rejected by: Jelmer Vernooij
Proposed branch: lp:~jelmer/bzr/move-write-group
Merge into: lp:bzr
Diff against target: 593 lines (+200/-170)
7 files modified
bzrlib/repofmt/knitrepo.py (+1/-1)
bzrlib/repository.py (+3/-157)
bzrlib/tests/per_repository/__init__.py (+0/-2)
bzrlib/tests/per_repository_vf/__init__.py (+2/-0)
bzrlib/tests/per_repository_vf/test_refresh_data.py (+13/-4)
bzrlib/tests/per_repository_vf/test_write_group.py (+17/-6)
bzrlib/vf_repository.py (+164/-0)
To merge this branch: bzr merge lp:~jelmer/bzr/move-write-group
Reviewer Review Type Date Requested Status
bzr-core Pending
Review via email: mp+60807@code.launchpad.net

Description of the change

Move write group infrastructure to bzrlib.vf_repository..

To post a comment you must log in.
Revision history for this message
Jelmer Vernooij (jelmer) wrote :

The only semi-generic thing that still requires the use of write groups is Repository.sign_revision(). I've filed bug 781751 about this.

Revision history for this message
Jelmer Vernooij (jelmer) wrote :

Actually, I'm going to wait with this. There is too much code that still relies on start_write_group/abort_write_group/commit_write_group at the moment, and moving them right now doesn't gain us anything.

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'bzrlib/repofmt/knitrepo.py'
2--- bzrlib/repofmt/knitrepo.py 2011-05-08 11:34:17 +0000
3+++ bzrlib/repofmt/knitrepo.py 2011-05-12 15:41:07 +0000
4@@ -35,11 +35,11 @@
5 from bzrlib.decorators import needs_read_lock, needs_write_lock
6 from bzrlib.repository import (
7 InterRepository,
8- IsInWriteGroupError,
9 RepositoryFormat,
10 )
11 from bzrlib.vf_repository import (
12 InterSameDataRepository,
13+ IsInWriteGroupError,
14 MetaDirVersionedFileRepository,
15 MetaDirVersionedFileRepositoryFormat,
16 VersionedFileCommitBuilder,
17
18=== modified file 'bzrlib/repository.py'
19--- bzrlib/repository.py 2011-05-09 12:01:12 +0000
20+++ bzrlib/repository.py 2011-05-12 15:41:07 +0000
21@@ -46,21 +46,15 @@
22 from bzrlib.inter import InterObject
23 from bzrlib.lock import _RelockDebugMixin, LogicalLockResult
24 from bzrlib.trace import (
25- log_exception_quietly, note, mutter, mutter_callsite, warning)
26+ mutter_callsite,
27+ warning,
28+ )
29
30
31 # Old formats display a warning, but only once
32 _deprecation_warning_done = False
33
34
35-class IsInWriteGroupError(errors.InternalBzrError):
36-
37- _fmt = "May not refresh_data of repo %(repo)s while in a write group."
38-
39- def __init__(self, repo):
40- errors.InternalBzrError.__init__(self, repo=repo)
41-
42-
43 class CommitBuilder(object):
44 """Provides an interface to build up a commit.
45
46@@ -252,49 +246,6 @@
47 base class for most Bazaar repositories.
48 """
49
50- def abort_write_group(self, suppress_errors=False):
51- """Commit the contents accrued within the current write group.
52-
53- :param suppress_errors: if true, abort_write_group will catch and log
54- unexpected errors that happen during the abort, rather than
55- allowing them to propagate. Defaults to False.
56-
57- :seealso: start_write_group.
58- """
59- if self._write_group is not self.get_transaction():
60- # has an unlock or relock occured ?
61- if suppress_errors:
62- mutter(
63- '(suppressed) mismatched lock context and write group. %r, %r',
64- self._write_group, self.get_transaction())
65- return
66- raise errors.BzrError(
67- 'mismatched lock context and write group. %r, %r' %
68- (self._write_group, self.get_transaction()))
69- try:
70- self._abort_write_group()
71- except Exception, exc:
72- self._write_group = None
73- if not suppress_errors:
74- raise
75- mutter('abort_write_group failed')
76- log_exception_quietly()
77- note('bzr: ERROR (ignored): %s', exc)
78- self._write_group = None
79-
80- def _abort_write_group(self):
81- """Template method for per-repository write group cleanup.
82-
83- This is called during abort before the write group is considered to be
84- finished and should cleanup any internal state accrued during the write
85- group. There is no requirement that data handed to the repository be
86- *not* made available - this is not a rollback - but neither should any
87- attempt be made to ensure that data added is fully commited. Abort is
88- invoked when an error has occured so futher disk or network operations
89- may not be possible or may error and if possible should not be
90- attempted.
91- """
92-
93 def add_fallback_repository(self, repository):
94 """Add a repository to use for looking up data not held locally.
95
96@@ -373,8 +324,6 @@
97 self.control_files = control_files
98 self._transport = control_files._transport
99 self.base = self._transport.base
100- # for tests
101- self._write_group = None
102 # Additional places to query for data.
103 self._fallback_repositories = []
104
105@@ -418,13 +367,6 @@
106 return False
107 return (self._transport.base == other._transport.base)
108
109- def is_in_write_group(self):
110- """Return True if there is an open write group.
111-
112- :seealso: start_write_group.
113- """
114- return self._write_group is not None
115-
116 def is_locked(self):
117 return self.control_files.is_locked()
118
119@@ -624,59 +566,6 @@
120 """
121 return InterRepository.get(self, destination).copy_content(revision_id)
122
123- def commit_write_group(self):
124- """Commit the contents accrued within the current write group.
125-
126- :seealso: start_write_group.
127-
128- :return: it may return an opaque hint that can be passed to 'pack'.
129- """
130- if self._write_group is not self.get_transaction():
131- # has an unlock or relock occured ?
132- raise errors.BzrError('mismatched lock context %r and '
133- 'write group %r.' %
134- (self.get_transaction(), self._write_group))
135- result = self._commit_write_group()
136- self._write_group = None
137- return result
138-
139- def _commit_write_group(self):
140- """Template method for per-repository write group cleanup.
141-
142- This is called before the write group is considered to be
143- finished and should ensure that all data handed to the repository
144- for writing during the write group is safely committed (to the
145- extent possible considering file system caching etc).
146- """
147-
148- def suspend_write_group(self):
149- raise errors.UnsuspendableWriteGroup(self)
150-
151- def refresh_data(self):
152- """Re-read any data needed to synchronise with disk.
153-
154- This method is intended to be called after another repository instance
155- (such as one used by a smart server) has inserted data into the
156- repository. On all repositories this will work outside of write groups.
157- Some repository formats (pack and newer for bzrlib native formats)
158- support refresh_data inside write groups. If called inside a write
159- group on a repository that does not support refreshing in a write group
160- IsInWriteGroupError will be raised.
161- """
162- self._refresh_data()
163-
164- def resume_write_group(self, tokens):
165- if not self.is_write_locked():
166- raise errors.NotWriteLocked(self)
167- if self._write_group:
168- raise errors.BzrError('already in a write group')
169- self._resume_write_group(tokens)
170- # so we can detect unlock/relock - the write group is now entered.
171- self._write_group = self.get_transaction()
172-
173- def _resume_write_group(self, tokens):
174- raise errors.UnsuspendableWriteGroup(self)
175-
176 def fetch(self, source, revision_id=None, find_ghosts=False,
177 fetch_spec=None):
178 """Fetch the content required to construct revision_id from source.
179@@ -684,10 +573,6 @@
180 If revision_id is None and fetch_spec is None, then all content is
181 copied.
182
183- fetch() may not be used when the repository is in a write group -
184- either finish the current write group before using fetch, or use
185- fetch before starting the write group.
186-
187 :param find_ghosts: Find and copy revisions in the source that are
188 ghosts in the target (and not reachable directly by walking out to
189 the first-present revision in target from revision_id).
190@@ -702,9 +587,6 @@
191 if fetch_spec is not None and revision_id is not None:
192 raise AssertionError(
193 "fetch_spec and revision_id are mutually exclusive.")
194- if self.is_in_write_group():
195- raise errors.InternalBzrError(
196- "May not fetch while in a write group.")
197 # fast path same-url fetch operations
198 # TODO: lift out to somewhere common with RemoteRepository
199 # <https://bugs.launchpad.net/bzr/+bug/401646>
200@@ -744,13 +626,6 @@
201
202 @only_raises(errors.LockNotHeld, errors.LockBroken)
203 def unlock(self):
204- if (self.control_files._lock_count == 1 and
205- self.control_files._lock_mode == 'w'):
206- if self._write_group is not None:
207- self.abort_write_group()
208- self.control_files.unlock()
209- raise errors.BzrError(
210- 'Must end write groups before releasing write locks.')
211 self.control_files.unlock()
212 if self.control_files._lock_count == 0:
213 for repo in self._fallback_repositories:
214@@ -771,35 +646,6 @@
215 self.copy_content_into(dest_repo, revision_id)
216 return dest_repo
217
218- def start_write_group(self):
219- """Start a write group in the repository.
220-
221- Write groups are used by repositories which do not have a 1:1 mapping
222- between file ids and backend store to manage the insertion of data from
223- both fetch and commit operations.
224-
225- A write lock is required around the start_write_group/commit_write_group
226- for the support of lock-requiring repository formats.
227-
228- One can only insert data into a repository inside a write group.
229-
230- :return: None.
231- """
232- if not self.is_write_locked():
233- raise errors.NotWriteLocked(self)
234- if self._write_group:
235- raise errors.BzrError('already in a write group')
236- self._start_write_group()
237- # so we can detect unlock/relock - the write group is now entered.
238- self._write_group = self.get_transaction()
239-
240- def _start_write_group(self):
241- """Template method for per-repository write group startup.
242-
243- This is called before the write group is considered to be
244- entered.
245- """
246-
247 @needs_read_lock
248 def sprout(self, to_bzrdir, revision_id=None):
249 """Create a descendent repository for new development.
250
251=== modified file 'bzrlib/tests/per_repository/__init__.py'
252--- bzrlib/tests/per_repository/__init__.py 2011-05-09 17:52:54 +0000
253+++ bzrlib/tests/per_repository/__init__.py 2011-05-12 15:41:07 +0000
254@@ -124,11 +124,9 @@
255 'test_merge_directive',
256 'test_pack',
257 'test_reconcile',
258- 'test_refresh_data',
259 'test_repository',
260 'test_revision',
261 'test_statistics',
262- 'test_write_group',
263 ]
264 # Parameterize per_repository test modules by format.
265 submod_tests = loader.loadTestsFromModuleNames(
266
267=== modified file 'bzrlib/tests/per_repository_vf/__init__.py'
268--- bzrlib/tests/per_repository_vf/__init__.py 2011-05-09 17:52:54 +0000
269+++ bzrlib/tests/per_repository_vf/__init__.py 2011-05-12 15:41:07 +0000
270@@ -43,7 +43,9 @@
271 'test__generate_text_key_index',
272 'test_fetch',
273 'test_reconcile',
274+ 'test_refresh_data',
275 'test_repository',
276+ 'test_write_group',
277 ]
278 basic_tests.addTest(loader.loadTestsFromModuleNames(
279 ["%s.%s" % (__name__, tmn) for tmn in testmod_names]))
280
281=== renamed file 'bzrlib/tests/per_repository/test_refresh_data.py' => 'bzrlib/tests/per_repository_vf/test_refresh_data.py'
282--- bzrlib/tests/per_repository/test_refresh_data.py 2010-05-11 14:14:55 +0000
283+++ bzrlib/tests/per_repository_vf/test_refresh_data.py 2011-05-12 15:41:07 +0000
284@@ -18,14 +18,23 @@
285
286 from bzrlib import (
287 errors,
288- repository,
289+ vf_repository,
290 )
291+from bzrlib.tests.scenarios import load_tests_apply_scenarios
292 from bzrlib.tests import TestSkipped
293-from bzrlib.tests.per_repository import TestCaseWithRepository
294+from bzrlib.tests.per_repository_vf import (
295+ TestCaseWithRepository,
296+ all_repository_vf_format_scenarios,
297+ )
298+
299+
300+load_tests = load_tests_apply_scenarios
301
302
303 class TestRefreshData(TestCaseWithRepository):
304
305+ scenarios = all_repository_vf_format_scenarios()
306+
307 def test_refresh_data_unlocked(self):
308 # While not interesting, it should not error.
309 repo = self.make_repository('.')
310@@ -55,7 +64,7 @@
311 self.addCleanup(repo.abort_write_group)
312 try:
313 repo.refresh_data()
314- except repository.IsInWriteGroupError:
315+ except vf_repository.IsInWriteGroupError:
316 # This is ok.
317 pass
318 else:
319@@ -112,7 +121,7 @@
320 # succeeds and the new revisions are visible.
321 try:
322 repo.refresh_data()
323- except repository.IsInWriteGroupError:
324+ except vf_repository.IsInWriteGroupError:
325 pass
326 else:
327 self.assertEqual(
328
329=== renamed file 'bzrlib/tests/per_repository/test_write_group.py' => 'bzrlib/tests/per_repository_vf/test_write_group.py'
330--- bzrlib/tests/per_repository/test_write_group.py 2011-01-10 23:52:14 +0000
331+++ bzrlib/tests/per_repository_vf/test_write_group.py 2011-05-12 15:41:07 +0000
332@@ -22,21 +22,28 @@
333 branch,
334 bzrdir,
335 errors,
336- graph,
337 memorytree,
338- osutils,
339 remote,
340 tests,
341 versionedfile,
342 )
343 from bzrlib.tests import (
344- per_repository,
345 test_server,
346 )
347+from bzrlib.tests.per_repository_vf import (
348+ TestCaseWithRepository,
349+ all_repository_vf_format_scenarios,
350+ )
351+from bzrlib.tests.scenarios import load_tests_apply_scenarios
352 from bzrlib.transport import memory
353
354
355-class TestWriteGroup(per_repository.TestCaseWithRepository):
356+load_tests = load_tests_apply_scenarios
357+
358+
359+class TestWriteGroup(TestCaseWithRepository):
360+
361+ scenarios = all_repository_vf_format_scenarios()
362
363 def test_start_write_group_unlocked_needs_write_lock(self):
364 repo = self.make_repository('.')
365@@ -134,7 +141,9 @@
366 self.assertEqual(None, repo.abort_write_group(suppress_errors=True))
367
368
369-class TestGetMissingParentInventories(per_repository.TestCaseWithRepository):
370+class TestGetMissingParentInventories(TestCaseWithRepository):
371+
372+ scenarios = all_repository_vf_format_scenarios()
373
374 def test_empty_get_missing_parent_inventories(self):
375 """A new write group has no missing parent inventories."""
376@@ -393,7 +402,9 @@
377
378
379
380-class TestResumeableWriteGroup(per_repository.TestCaseWithRepository):
381+class TestResumeableWriteGroup(TestCaseWithRepository):
382+
383+ scenarios = all_repository_vf_format_scenarios()
384
385 def make_write_locked_repo(self, relpath='repo'):
386 repo = self.make_repository(relpath)
387
388=== modified file 'bzrlib/vf_repository.py'
389--- bzrlib/vf_repository.py 2011-05-08 13:06:20 +0000
390+++ bzrlib/vf_repository.py 2011-05-12 15:41:07 +0000
391@@ -66,10 +66,20 @@
392 )
393
394 from bzrlib.trace import (
395+ log_exception_quietly,
396 mutter,
397+ note,
398 )
399
400
401+class IsInWriteGroupError(errors.InternalBzrError):
402+
403+ _fmt = "May not refresh_data of repo %(repo)s while in a write group."
404+
405+ def __init__(self, repo):
406+ errors.InternalBzrError.__init__(self, repo=repo)
407+
408+
409 class VersionedFileRepositoryFormat(RepositoryFormat):
410 """Base class for all repository formats that are VersionedFiles-based."""
411
412@@ -899,6 +909,138 @@
413 # get_commit_builder.
414 _commit_builder_class = VersionedFileCommitBuilder
415
416+ def start_write_group(self):
417+ """Start a write group in the repository.
418+
419+ Write groups are used by repositories which do not have a 1:1 mapping
420+ between file ids and backend store to manage the insertion of data from
421+ both fetch and commit operations.
422+
423+ A write lock is required around the start_write_group/commit_write_group
424+ for the support of lock-requiring repository formats.
425+
426+ One can only insert data into a repository inside a write group.
427+
428+ :return: None.
429+ """
430+ if not self.is_write_locked():
431+ raise errors.NotWriteLocked(self)
432+ if self._write_group:
433+ raise errors.BzrError('already in a write group')
434+ self._start_write_group()
435+ # so we can detect unlock/relock - the write group is now entered.
436+ self._write_group = self.get_transaction()
437+
438+ def _start_write_group(self):
439+ """Template method for per-repository write group startup.
440+
441+ This is called before the write group is considered to be
442+ entered.
443+ """
444+
445+ def abort_write_group(self, suppress_errors=False):
446+ """Commit the contents accrued within the current write group.
447+
448+ :param suppress_errors: if true, abort_write_group will catch and log
449+ unexpected errors that happen during the abort, rather than
450+ allowing them to propagate. Defaults to False.
451+
452+ :seealso: start_write_group.
453+ """
454+ if self._write_group is not self.get_transaction():
455+ # has an unlock or relock occured ?
456+ if suppress_errors:
457+ mutter(
458+ '(suppressed) mismatched lock context and write group. %r, %r',
459+ self._write_group, self.get_transaction())
460+ return
461+ raise errors.BzrError(
462+ 'mismatched lock context and write group. %r, %r' %
463+ (self._write_group, self.get_transaction()))
464+ try:
465+ self._abort_write_group()
466+ except Exception, exc:
467+ self._write_group = None
468+ if not suppress_errors:
469+ raise
470+ mutter('abort_write_group failed')
471+ log_exception_quietly()
472+ note('bzr: ERROR (ignored): %s', exc)
473+ self._write_group = None
474+
475+ def _abort_write_group(self):
476+ """Template method for per-repository write group cleanup.
477+
478+ This is called during abort before the write group is considered to be
479+ finished and should cleanup any internal state accrued during the write
480+ group. There is no requirement that data handed to the repository be
481+ *not* made available - this is not a rollback - but neither should any
482+ attempt be made to ensure that data added is fully commited. Abort is
483+ invoked when an error has occured so futher disk or network operations
484+ may not be possible or may error and if possible should not be
485+ attempted.
486+ """
487+
488+ def is_in_write_group(self):
489+ """Return True if there is an open write group.
490+
491+ :seealso: start_write_group.
492+ """
493+ return self._write_group is not None
494+
495+ def commit_write_group(self):
496+ """Commit the contents accrued within the current write group.
497+
498+ :seealso: start_write_group.
499+
500+ :return: it may return an opaque hint that can be passed to 'pack'.
501+ """
502+ if self._write_group is not self.get_transaction():
503+ # has an unlock or relock occured ?
504+ raise errors.BzrError('mismatched lock context %r and '
505+ 'write group %r.' %
506+ (self.get_transaction(), self._write_group))
507+ result = self._commit_write_group()
508+ self._write_group = None
509+ return result
510+
511+ def _commit_write_group(self):
512+ """Template method for per-repository write group cleanup.
513+
514+ This is called before the write group is considered to be
515+ finished and should ensure that all data handed to the repository
516+ for writing during the write group is safely committed (to the
517+ extent possible considering file system caching etc).
518+ """
519+
520+ def suspend_write_group(self):
521+ raise errors.UnsuspendableWriteGroup(self)
522+
523+ def resume_write_group(self, tokens):
524+ if not self.is_write_locked():
525+ raise errors.NotWriteLocked(self)
526+ if self._write_group:
527+ raise errors.BzrError('already in a write group')
528+ self._resume_write_group(tokens)
529+ # so we can detect unlock/relock - the write group is now entered.
530+ self._write_group = self.get_transaction()
531+
532+ def _resume_write_group(self, tokens):
533+ raise errors.UnsuspendableWriteGroup(self)
534+
535+ def refresh_data(self):
536+ """Re-read any data needed to synchronise with disk.
537+
538+ This method is intended to be called after another repository instance
539+ (such as one used by a smart server) has inserted data into the
540+ repository. On all repositories this will work outside of write groups.
541+ Some repository formats (pack and newer for bzrlib native formats)
542+ support refresh_data inside write groups. If called inside a write
543+ group on a repository that does not support refreshing in a write group
544+ IsInWriteGroupError will be raised.
545+ """
546+ self._refresh_data()
547+
548 def add_fallback_repository(self, repository):
549 """Add a repository to use for looking up data not held locally.
550
551@@ -921,6 +1063,13 @@
552
553 @only_raises(errors.LockNotHeld, errors.LockBroken)
554 def unlock(self):
555+ if (self.control_files._lock_count == 1 and
556+ self.control_files._lock_mode == 'w'):
557+ if self._write_group is not None:
558+ self.abort_write_group()
559+ self.control_files.unlock()
560+ raise errors.BzrError(
561+ 'Must end write groups before releasing write locks.')
562 super(VersionedFileRepository, self).unlock()
563 if self.control_files._lock_count == 0:
564 self._inventory_entry_cache.clear()
565@@ -1189,6 +1338,7 @@
566 # Is it safe to return inventory entries directly from the entry cache,
567 # rather copying them?
568 self._safe_to_return_from_cache = False
569+ self._write_group = None
570
571 @needs_read_lock
572 def gather_stats(self, revid=None, committers=None):
573@@ -1204,6 +1354,20 @@
574 # result['size'] = t
575 return result
576
577+ def fetch(self, source, revision_id=None, find_ghosts=False,
578+ fetch_spec=None):
579+ """See Repository.fetch().
580+
581+ fetch() may not be used when the repository is in a write group -
582+ either finish the current write group before using fetch, or use
583+ fetch before starting the write group.
584+ """
585+ if self.is_in_write_group():
586+ raise errors.InternalBzrError(
587+ "May not fetch while in a write group.")
588+ return super(VersionedFileRepository, self).fetch(source,
589+ revision_id=revision_id, find_ghosts=find_ghosts,
590+ fetch_spec=fetch_spec)
591
592 def get_commit_builder(self, branch, parents, config, timestamp=None,
593 timezone=None, committer=None, revprops=None,