Commit 6b22f8c4 authored by Kirill Smelkov's avatar Kirill Smelkov

X wcfs: Teach start to start successfully even after unclean wcfs shutdown

This patch does to start what b0ca031f did for join/serve.

Stop duplicating the code and factor logic to "attach to wcsrv, or
prepare to start" into _try_attach_wcsrv function.
parent 669d7a20
...@@ -208,19 +208,10 @@ def join(zurl, autostart=_default_autostart()): # -> WCFS ...@@ -208,19 +208,10 @@ def join(zurl, autostart=_default_autostart()): # -> WCFS
return wc return wc
# no. try opening .wcfs - if we succeed - wcfs is already running. # no. try opening .wcfs - if we succeed - wcfs is already running.
unclean = False fwcfs, trylockstartf = _try_attach_wcsrv(mntpt)
try: if fwcfs is not None:
f = open(mntpt + "/.wcfs/zurl")
except IOError as e:
if e.errno == ENOENT: # wcfs cleanly unmounted
pass
elif e.errno == ENOTCONN: # wcfs crashed/killed
unclean = True
else:
raise
else:
# already have it # already have it
wc = WCFS(mntpt, f, None) wc = WCFS(mntpt, fwcfs, None)
_wcregistry[mntpt] = wc _wcregistry[mntpt] = wc
return wc return wc
...@@ -228,10 +219,7 @@ def join(zurl, autostart=_default_autostart()): # -> WCFS ...@@ -228,10 +219,7 @@ def join(zurl, autostart=_default_autostart()): # -> WCFS
raise RuntimeError("wcfs: join %s: server not running" % zurl) raise RuntimeError("wcfs: join %s: server not running" % zurl)
# start wcfs with telling it to automatically exit when there is no client activity. # start wcfs with telling it to automatically exit when there is no client activity.
# XXX race window if external process starts after ^^^ check trylockstartf() # XXX retry access if another wcfs was started in the meantime
# TODO -> fs-level locking
if unclean:
_fuse_unmount(mntpt)
wcsrv, fwcfs = _start(zurl, "-autoexit") wcsrv, fwcfs = _start(zurl, "-autoexit")
wc = WCFS(mntpt, fwcfs, wcsrv) wc = WCFS(mntpt, fwcfs, wcsrv)
...@@ -242,11 +230,51 @@ def join(zurl, autostart=_default_autostart()): # -> WCFS ...@@ -242,11 +230,51 @@ def join(zurl, autostart=_default_autostart()): # -> WCFS
return wc return wc
# _try_attach_wcsrv tries to attach to running wcfs server.
#
# if successful, it returns fwcfs - opened file handle for /.wcfs/zurl
# if unsuccessful, it returns fwcfs=None, and trylockstartf function that can
# be used to prepare to start new WCFS server.
def _try_attach_wcsrv(mntpt): # -> (fwcfs, trylockstartf)
# try opening .wcfs - if we succeed - wcfs is already running.
unclean = False
try:
fwcfs = open(mntpt + "/.wcfs/zurl")
except IOError as e:
if e.errno == ENOENT: # wcfs cleanly unmounted
pass
elif e.errno == ENOTCONN: # wcfs crashed/killed
unclean = True
else:
raise
else:
return (fwcfs, None)
# the server is not running.
# return func to prepare start of another wcfs server
def trylockstartf():
# XXX race window if external process starts after ^^^ check
# TODO -> fs-level locking
if unclean:
_fuse_unmount(mntpt)
return (None, trylockstartf)
# start starts wcfs server for ZODB @ zurl. # start starts wcfs server for ZODB @ zurl.
# #
# optv can be optionally given to pass flags to wcfs. # optv can be optionally given to pass flags to wcfs.
def start(zurl, *optv): # -> Server def start(zurl, *optv): # -> Server
# XXX check already started? # verify that wcfs is not already running
mntpt = _mntpt_4zurl(zurl)
fwcfs, trylockstartf = _try_attach_wcsrv(mntpt)
if fwcfs is not None:
fwcfs.close()
raise RuntimeError("wcfs: start %s: already running" % zurl)
# seems to be ok to start
trylockstartf() # XXX -> "already running" if lock fails
wcsrv, fwcfs = _start(zurl, *optv) wcsrv, fwcfs = _start(zurl, *optv)
fwcfs.close() fwcfs.close()
return wcsrv return wcsrv
...@@ -618,26 +646,14 @@ def serve(zurl, optv, exec_=False, _tstartingq=None): ...@@ -618,26 +646,14 @@ def serve(zurl, optv, exec_=False, _tstartingq=None):
# XXX take $WENDELIN_CORE_WCFS_OPTIONS into account? # XXX take $WENDELIN_CORE_WCFS_OPTIONS into account?
# try opening .wcfs - it is an error if we can do it. # try opening .wcfs - it is an error if we can do it.
# XXX -> option to wcfs itself to verify wcfs/something is already mounted? fwcfs, trylockstartf = _try_attach_wcsrv(mntpt)
unclean = False if fwcfs is not None:
try: fwcfs.close()
f = open(mntpt + "/.wcfs/zurl") raise RuntimeError("wcfs: serve %s: already running" % zurl)
except IOError as e:
if e.errno == ENOENT: # wcfs cleanly unmounted
pass
elif e.errno == ENOTCONN: # wcfs crashed/killed
unclean = True
else:
raise
else:
f.close()
raise RuntimeError("wcfs: start %s: already running" % zurl)
# seems to be ok to start # seems to be ok to start
# XXX race window if external process starts after ^^^ check trylockstartf() # XXX -> "already running" if lock fails
# TODO -> fs-level locking
if unclean:
_fuse_unmount(mntpt)
if _tstartingq is not None: if _tstartingq is not None:
_tstartingq.close() _tstartingq.close()
argv = [_wcfs_exe()] + list(optv) + [zurl, mntpt] argv = [_wcfs_exe()] + list(optv) + [zurl, mntpt]
......
...@@ -198,6 +198,28 @@ def test_join_after_crash(): ...@@ -198,6 +198,28 @@ def test_join_after_crash():
procmounts_lookup_wcfs(zurl) procmounts_lookup_wcfs(zurl)
# verify that start successfuly starts server if previous wcfs exited uncleanly.
@func
def test_start_after_crash():
zurl = testzurl
mntpt = testmntpt
wc = start_and_crash_wcfs(zurl, mntpt)
wcsrv = wcfs.start(zurl)
defer(wcsrv.stop)
assert wcsrv.mountpoint == mntpt
assert readfile(mntpt + "/.wcfs/zurl") == zurl
# /proc/mounts should contain wcfs entry
assert procmounts_lookup_wcfs(zurl) == mntpt
# stop the server - /proc/mounts entry should be gone
wcsrv.stop()
with raises(KeyError):
procmounts_lookup_wcfs(zurl)
# verify that serve successfully starts if previous wcfs exited uncleanly. # verify that serve successfully starts if previous wcfs exited uncleanly.
@func @func
def test_serve_after_crash(): def test_serve_after_crash():
...@@ -226,8 +248,6 @@ def test_serve_after_crash(): ...@@ -226,8 +248,6 @@ def test_serve_after_crash():
assert procmounts_lookup_wcfs(zurl) == mntpt assert procmounts_lookup_wcfs(zurl) == mntpt
# XXX test_start_after_crash?
# start_and_crash_wcfs starts wcfs and then kills it. # start_and_crash_wcfs starts wcfs and then kills it.
# it returns closed WCFS connection that was connected to the killed WCFS server. # it returns closed WCFS connection that was connected to the killed WCFS server.
......
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