From 6b6b0e3aabfed211d689ae3b0ab5f88f0acbb481 Mon Sep 17 00:00:00 2001 From: Jason Madden Date: Thu, 6 Aug 2020 14:47:21 -0500 Subject: [PATCH] Let the MergingCounter be reset to 0. Fixes #6 --- CHANGES.rst | 4 ++++ src/nti/zodb/minmax.py | 10 ++++++++++ src/nti/zodb/tests/test_minmax.py | 9 ++++++--- 3 files changed, 20 insertions(+), 3 deletions(-) diff --git a/CHANGES.rst b/CHANGES.rst index 6628e63..fafba47 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -13,6 +13,10 @@ the ``increment`` method. See `issue 7 `_. +- The merging counter does the right thing when reset to zero by two + conflicting transactions. See `issue 6 + `_. + 1.1.0 (2020-07-15) ================== diff --git a/src/nti/zodb/minmax.py b/src/nti/zodb/minmax.py index 46b8b55..ac4db69 100644 --- a/src/nti/zodb/minmax.py +++ b/src/nti/zodb/minmax.py @@ -152,6 +152,13 @@ class MergingCounter(AbstractNumericValue): A :mod:`zope.minmax` item that resolves conflicts by merging the numeric value of the difference in magnitude of changes. Intented to be used for monotonically increasing counters. + + As a special case, if the counter is reset to zero by both transactions, + that becomes the new state. + + .. versionchanged:: 1.2.0 + Special case setting the counter to zero. + """ def increment(self, amount=1): @@ -160,6 +167,9 @@ def increment(self, amount=1): return self def _p_resolveConflict(self, oldState, savedState, newState): # pylint:disable=arguments-differ + if savedState == newState == 0: + return 0 + saveDiff = savedState - oldState newDiff = newState - oldState savedState = oldState + saveDiff + newDiff diff --git a/src/nti/zodb/tests/test_minmax.py b/src/nti/zodb/tests/test_minmax.py index 7b50870..718c5de 100644 --- a/src/nti/zodb/tests/test_minmax.py +++ b/src/nti/zodb/tests/test_minmax.py @@ -97,10 +97,13 @@ def test_numeric_counter_interface(self): validly_provides(interfaces.INumericCounter)) def test_merge_resolve(self): - assert_that(MergingCounter()._p_resolveConflict(0, 0, 1), is_(1)) + # (original state, currently committed, desired) + mc = self._makeOne() + assert_that(mc._p_resolveConflict(0, 0, 1), is_(1)) # simultaneous increment adds - assert_that(MergingCounter()._p_resolveConflict(0, 1, 1), is_(2)) - + assert_that(mc._p_resolveConflict(0, 1, 1), is_(2)) + # In the special case that both set it to zero, it becomes 0 + assert_that(mc._p_resolveConflict(10, 0, 0), is_(0)) def test_str(self):