Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
Z
ZODB
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Analytics
Analytics
CI / CD
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
Kirill Smelkov
ZODB
Commits
d6b60a5b
Commit
d6b60a5b
authored
Sep 09, 2016
by
Julien Muchembled
Browse files
Options
Browse Files
Download
Plain Diff
Merge 4.x branch
parents
6fc21dc5
27f3a173
Changes
3
Hide whitespace changes
Inline
Side-by-side
Showing
3 changed files
with
32 additions
and
16 deletions
+32
-16
CHANGES.rst
CHANGES.rst
+3
-0
src/ZODB/ConflictResolution.py
src/ZODB/ConflictResolution.py
+0
-7
src/ZODB/tests/testconflictresolution.py
src/ZODB/tests/testconflictresolution.py
+29
-9
No files found.
CHANGES.rst
View file @
d6b60a5b
...
...
@@ -9,6 +9,9 @@
to store a blob into a temporary changes before reading a blob. See
`issue 103 <https://github.com/zopefoundation/ZODB/issues/103>`_.
- Call _p_resolveConflict() even if a conflicting change doesn't change the
state. This reverts to the behaviour of 3.10.3 and older.
5.0.0 (2016-09-06)
==================
...
...
src/ZODB/ConflictResolution.py
View file @
d6b60a5b
...
...
@@ -269,13 +269,6 @@ def tryToResolveConflict(self, oid, committedSerial, oldSerial, newpickle,
if
not
committedData
:
committedData
=
self
.
loadSerial
(
oid
,
committedSerial
)
if
newpickle
==
oldData
:
# old -> new diff is empty, so merge is trivial
return
committedData
if
committedData
==
oldData
:
# old -> committed diff is empty, so merge is trivial
return
newpickle
newstate
=
unpickler
.
load
()
old
=
state
(
self
,
oid
,
oldSerial
,
prfactory
,
oldData
)
committed
=
state
(
self
,
oid
,
committedSerial
,
prfactory
,
committedData
)
...
...
src/ZODB/tests/testconflictresolution.py
View file @
d6b60a5b
...
...
@@ -40,6 +40,15 @@ def tearDown(test):
class
ResolveableWhenStateDoesNotChange
(
persistent
.
Persistent
):
def
_p_resolveConflict
(
self
,
old
,
committed
,
new
):
if
new
==
old
:
# old -> new diff is empty, so merge is trivial
committed
[
'resolved'
]
=
'committed'
return
committed
elif
committed
==
old
:
# old -> committed diff is empty, so merge is trivial
new
[
'resolved'
]
=
'new'
return
new
# 3-way merge
raise
ZODB
.
POSException
.
ConflictError
class
Unresolvable
(
persistent
.
Persistent
):
...
...
@@ -47,8 +56,17 @@ class Unresolvable(persistent.Persistent):
def
succeed_with_resolution_when_state_is_unchanged
():
"""
If a conflicting change doesn't change the state, then don't even
bother calling _p_resolveConflict
If a conflicting change doesn't change the state, then we must still call
_p_resolveConflict, even if in most cases the result would be either
committed or new (as shown above in ResolveableWhenStateDoesNotChange).
One use case is to implement an "asynchronous" cache:
- Initially, a cache value is not filled (e.g. None is used to describe
this state).
- A transaction fills the cache (actually done by a background application)
(None -> "foo").
- A concurrent transaction invalidates the cache due to some user action
(None -> None), and pushes a new background task to fill the cache.
Then the expected resolved value is None, and not "foo".
>>> db = ZODB.DB('t.fs') # FileStorage!
>>> storage = db.storage
...
...
@@ -63,23 +81,23 @@ def succeed_with_resolution_when_state_is_unchanged():
>>> oid = conn.root.x._p_oid
So, let's try resolving when the old and committed states are the same
b
i
t the new state (pickle) is different:
b
u
t the new state (pickle) is different:
>>> p = storage.tryToResolveConflict(
... oid, serial1, serial1, storage.loadSerial(oid, serial2))
>>>
p == storage.loadSerial(oid, serial2)
True
>>>
conn._reader.getState(p)['resolved']
'new'
And when the old and new states are the same b
i
t the committed state
And when the old and new states are the same b
u
t the committed state
is different:
>>> p = storage.tryToResolveConflict(
... oid, serial2, serial1, storage.loadSerial(oid, serial1))
>>>
p == storage.loadSerial(oid, serial2)
True
>>>
conn._reader.getState(p)['resolved']
'committed'
But we still conflict if both the committed and new are different than
the original:
...
...
@@ -92,7 +110,9 @@ the original:
ConflictError: database conflict error (oid 0x01, ...
Of course, none of this applies if content doesn't support conflict resolution.
Of course, there's also no automatic trivial merge if content doesn't support
conflict resolution. Touching an object without change is a common locking
mechanism.
>>> conn.root.y = Unresolvable()
>>> conn.root.y.v = 1
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment