Commit 1844c7db authored by Kirill Smelkov's avatar Kirill Smelkov

zodbcommit: Don't forget to call tpc_abort on an error

Two-phase commit protocol assumes that after tpc_begin, it will be
either successful tpc_vote + tpc_finish, or tpc_abort. We were not
calling tpc_abort on an error, potentially leaving storage in "commit is
in progress" state on an error.
parent c59a54ca
# Copyright (C) 2018-2020 Nexedi SA and Contributors. # Copyright (C) 2018-2021 Nexedi SA and Contributors.
# Kirill Smelkov <kirr@nexedi.com> # Kirill Smelkov <kirr@nexedi.com>
# #
# This program is free software: you can Use, Study, Modify and Redistribute # This program is free software: you can Use, Study, Modify and Redistribute
...@@ -58,49 +58,56 @@ def zodbcommit(stor, at, txn): ...@@ -58,49 +58,56 @@ def zodbcommit(stor, at, txn):
before = p64(u64(at)+1) before = p64(u64(at)+1)
stor.tpc_begin(txn) stor.tpc_begin(txn)
for obj in txn.objv:
data = None # data do be committed - setup vvv
if isinstance(obj, zodbdump.ObjectCopy):
# NEO does not support restore, and even if stor supports restore,
# going that way requires to already know tid for transaction we are
# committing. -> we just imitate copy by actually copying data and
# letting the storage deduplicate it.
data, _, _ = stor.loadBefore(obj.oid, p64(u64(obj.copy_from)+1))
elif isinstance(obj, zodbdump.ObjectDelete): def _():
data = None for obj in txn.objv:
data = None # data do be committed - setup vvv
if isinstance(obj, zodbdump.ObjectCopy):
# NEO does not support restore, and even if stor supports restore,
# going that way requires to already know tid for transaction we are
# committing. -> we just imitate copy by actually copying data and
# letting the storage deduplicate it.
data, _, _ = stor.loadBefore(obj.oid, p64(u64(obj.copy_from)+1))
elif isinstance(obj, zodbdump.ObjectData): elif isinstance(obj, zodbdump.ObjectDelete):
data = None
if isinstance(obj.data, zodbdump.HashOnly): elif isinstance(obj, zodbdump.ObjectData):
raise ValueError('cannot commit transaction with hashonly object')
data = obj.data if isinstance(obj.data, zodbdump.HashOnly):
raise ValueError('cannot commit transaction with hashonly object')
else: data = obj.data
panic('invalid object record: %r' % (obj,))
else:
panic('invalid object record: %r' % (obj,))
# now we have the data. # now we have the data.
# find out what is oid's serial as of <before state # find out what is oid's serial as of <before state
try: try:
xdata = stor.loadBefore(obj.oid, before) xdata = stor.loadBefore(obj.oid, before)
except POSKeyError: except POSKeyError:
serial_prev = z64
else:
if xdata is None:
serial_prev = z64 serial_prev = z64
else: else:
_, serial_prev, _ = xdata if xdata is None:
serial_prev = z64
# store the object. else:
# if it will be ConflictError - we just fail and let the caller retry. _, serial_prev, _ = xdata
if data is None:
stor.deleteObject(obj.oid, serial_prev, txn) # store the object.
else: # if it will be ConflictError - we just fail and let the caller retry.
stor.store(obj.oid, serial_prev, data, '', txn) if data is None:
stor.deleteObject(obj.oid, serial_prev, txn)
else:
stor.store(obj.oid, serial_prev, data, '', txn)
stor.tpc_vote(txn) try:
_()
stor.tpc_vote(txn)
except:
stor.tpc_abort(txn)
raise
# in ZODB >= 5 tpc_finish returns tid directly, but on ZODB 4 it # in ZODB >= 5 tpc_finish returns tid directly, but on ZODB 4 it
# does not do so. Since we still need to support ZODB 4, utilize tpc_finish # does not do so. Since we still need to support ZODB 4, utilize tpc_finish
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment