Commit 8bf8f23b authored by Kirill Smelkov's avatar Kirill Smelkov

X bigfile/_file_zodb: Fix logic around ZSync usage

The logic inside ZSync was correct, but it was incorrect to attach zsync
to zconn to stay alive and react when that zconn is garbage collected:
zsync._on_zconn_dealloc was not called because zsync itself was garbage
collected too.

This fixes many failures where wconn and associated pinner was not
released even though ZODB DB was correctly closed.
parent 3a6bd764
...@@ -58,6 +58,7 @@ from cpython cimport PyCapsule_New ...@@ -58,6 +58,7 @@ from cpython cimport PyCapsule_New
from ZODB.Connection import Connection as ZConnection from ZODB.Connection import Connection as ZConnection
from ZODB.utils import u64 from ZODB.utils import u64
from wendelin.lib.zodb import zconn_at from wendelin.lib.zodb import zconn_at
from golang import sync as pysync
import weakref import weakref
...@@ -147,7 +148,7 @@ cdef wcfs.PyConn pywconnOf(zconn): ...@@ -147,7 +148,7 @@ cdef wcfs.PyConn pywconnOf(zconn):
zconn._wcfs_wconn = wconn zconn._wcfs_wconn = wconn
# keep wconn view of the database in sync with zconn # keep wconn view of the database in sync with zconn
zconn._wcfs_wconn_zsync = ZSync(zconn, wconn) ZSync(zconn, wconn)
return wconn return wconn
...@@ -156,6 +157,10 @@ cdef wcfs.PyConn pywconnOf(zconn): ...@@ -156,6 +157,10 @@ cdef wcfs.PyConn pywconnOf(zconn):
# #
# wconn will be closed once zconn is destroyed (not closed, which returns it # wconn will be closed once zconn is destroyed (not closed, which returns it
# back into DB pool). # back into DB pool).
#
# ZSync cares itself to stay alive as long as zconn stays alive.
_zsyncRegMu = pysync.Mutex()
_zsyncReg = {} # id(zsync) -> zsync
class ZSync: class ZSync:
# .zconn_ref weakref[zodb.Connection] # .zconn_ref weakref[zodb.Connection]
# .wconn (py) wcfs.Connection # .wconn (py) wcfs.Connection
...@@ -172,9 +177,15 @@ class ZSync: ...@@ -172,9 +177,15 @@ class ZSync:
# tm.commit() # zconn.at updated again # tm.commit() # zconn.at updated again
zconn.onResyncCallback(zsync) zconn.onResyncCallback(zsync)
# .zconn dealloc -> wconn.close # keep zsync in _zsyncReg for zsync to stay alive independently of the caller.
with _zsyncRegMu:
_zsyncReg[id(zsync)] = zsync
# .zconn dealloc -> wconn.close; release zsync
def on_zconn_dealloc(zsync, _): def on_zconn_dealloc(zsync, _):
zsync.wconn.close() zsync.wconn.close()
with _zsyncRegMu:
del _zsyncReg[id(zsync)]
# DB resyncs .zconn onto new database view. # DB resyncs .zconn onto new database view.
# -> resync .wconn to updated database view of ZODB connection. # -> resync .wconn to updated database view of ZODB connection.
......
...@@ -56,8 +56,8 @@ def test_zsync(): ...@@ -56,8 +56,8 @@ def test_zsync():
defer(wc.close) defer(wc.close)
wconn = wc.connect(at0) wconn = wc.connect(at0)
assert wconn.at() == at0 assert wconn.at() == at0
# setup ZSync for wconn <-> zconn # setup ZSync for wconn <-> zconn; don't keep zsync explicitly referenced
zsync = ZSync(zconn, wconn) ZSync(zconn, wconn)
assert wconn.at() == at0 assert wconn.at() == at0
......
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