Commit bc0fdca4 authored by Barry Warsaw's avatar Barry Warsaw

Get rid of the differences between forking off the zeo server for NT

and Unix.  We actually use the Windows code as the basis for the new
approach so there's some hope that this will actually work on Windows
<wink>.  Tim will verify once the checkins are finished.

start_zeo_server(): Is now the blessed way of starting a ZEO server,
and it primarily takes a ZConfig storage section string as its
argument.  This it writes to a temp file and passes -C to the
zeoserver.py script, which parses that temp file and creates the
underlying storage.  zeoserver.py is responsible for deleting the temp
file.

zeoserver opens an `admin' port which is used to quit the server, but
the protocol has been expanded slightly.  The first time the admin
port is connected to, an ack character is sent from zeoserver.  This
is necessary to coordinate the tests so that they are assured the
server is running and responding before it tries to do more.
Otherwise, some tests were vulnerable to timing bugs where the
shutdown connect happened before the server was ready to accept them.

The second connect exits the server process just like before.

start_zeo_server() returns a 3-tuple.  The first element is the
(host,port) tuple of the ZEO server, the second is the (host,port)
tuple of the admin socket, and the third is the pid of the spawned
child process.

shutdown_zeo_server(): The blessed way to shut down a zeoserver.py
process.  Takes an admin (host,port) tuple, connects to that and
writes a log entry.
parent ea15fb62
......@@ -15,16 +15,17 @@
import os
import sys
import time
import errno
import types
import random
import socket
import asyncore
import tempfile
import traceback
from cStringIO import StringIO
import ZEO.ClientStorage
import ZConfig
from ZODB import StorageConfig
import zLOG
# Change value of PROFILE to enable server-side profiling
PROFILE = 0
......@@ -52,130 +53,56 @@ def get_port():
s.close()
raise RuntimeError, "Can't find port"
if os.name == "nt":
# XXX This is probably broken now
def start_zeo_server(conf, addr=None, ro_svr=0):
"""Start a ZEO server in a separate process.
def start_zeo_server(conf, addr=None, ro_svr=0):
"""Start a ZEO server in a separate process.
Returns the ZEO port, the test server port, and the pid.
"""
import ZEO.tests.winserver
if addr is None:
port = get_port()
else:
port = addr[1]
script = ZEO.tests.winserver.__file__
if script.endswith('.pyc'):
script = script[:-1]
if ro_svr:
prefix = (sys.executable, script, "-r")
else:
prefix = (sys.executable, script)
args = prefix + (str(port), storage_name) + args
d = os.environ.copy()
d['PYTHONPATH'] = os.pathsep.join(sys.path)
pid = os.spawnve(os.P_NOWAIT, sys.executable, args, d)
return ('localhost', port), ('localhost', port + 1), pid
else:
class ZEOServerExit(asyncore.file_dispatcher):
"""Used to exit ZEO.StorageServer when run is done"""
def writable(self):
return 0
def readable(self):
return 1
def handle_read(self):
buf = self.recv(4)
if buf:
assert buf == "done"
server.close_server()
asyncore.socket_map.clear()
def handle_close(self):
server.close_server()
asyncore.socket_map.clear()
class ZEOClientExit:
"""Used by client to cause server to exit"""
def __init__(self, pipe):
self.pipe = pipe
def close(self):
try:
os.write(self.pipe, "done")
os.close(self.pipe)
except os.error:
pass
def start_zeo_server(conf, addr, ro_svr=0):
rd, wr = os.pipe()
pid = os.fork()
if pid == 0:
asyncore.socket_map.clear() # Don't service the parent's sockets
import ZEO.zrpc.log
reload(ZEO.zrpc.log) # Don't share the logging file object
try:
if PROFILE:
p = hotshot.Profile("stats.s.%d" % os.getpid())
p.runctx(
"run_server(addr, rd, wr, conf, ro_svr)",
globals(), locals())
p.close()
else:
run_server(addr, rd, wr, conf, ro_svr)
except:
print "Exception in ZEO server process"
traceback.print_exc()
os._exit(0)
else:
os.close(rd)
return pid, ZEOClientExit(wr)
def load_storage(conf):
fp = StringIO(conf)
rootconf = ZConfig.loadfile(fp)
storageconf = rootconf.getSection('Storage')
return StorageConfig.createStorage(storageconf)
def run_server(addr, rd, wr, conf, ro_svr):
# in the child, run the storage server
global server
os.close(wr)
ZEOServerExit(rd)
import ZEO.StorageServer, ZEO.zrpc.server
storage = load_storage(conf)
server = ZEO.StorageServer.StorageServer(addr, {'1':storage}, ro_svr)
ZEO.zrpc.server.loop()
storage.close()
if isinstance(addr, types.StringType):
os.unlink(addr)
def start_zeo(conf, cache=None, cleanup=None,
domain="AF_INET", storage_id="1", cache_size=20000000):
"""Setup ZEO client-server for storage.
Returns a ClientStorage instance and a ZEOClientExit instance.
XXX Don't know if os.pipe() will work on Windows.
"""
if domain == "AF_INET":
addr = '', get_port()
elif domain == "AF_UNIX":
import tempfile
addr = tempfile.mktemp()
else:
raise ValueError, "bad domain: %s" % domain
pid, exit = start_zeo_server(conf, addr)
s = ZEO.ClientStorage.ClientStorage(addr, storage_id,
client=cache,
cache_size=cache_size,
min_disconnect_poll=0.5,
wait=1)
return s, exit, pid
Returns the ZEO port, the test server port, and the pid.
"""
# Store the config info in a temp file.
tmpfile = tempfile.mktemp()
fp = open(tmpfile, 'w')
fp.write(conf)
fp.close()
# Create the server
import ZEO.tests.zeoserver
if addr is None:
port = get_port()
else:
port = addr[1]
script = ZEO.tests.zeoserver.__file__
if script.endswith('.pyc'):
script = script[:-1]
# Create a list of arguments, which we'll tuplify below
args = [sys.executable, script, '-C', tmpfile]
if ro_svr:
args.append('-r')
args.append(str(port))
d = os.environ.copy()
d['PYTHONPATH'] = os.pathsep.join(sys.path)
pid = os.spawnve(os.P_NOWAIT, sys.executable, tuple(args), d)
adminaddr = ('localhost', port+1)
# We need to wait until the server starts, but not forever
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
for i in range(5):
try:
zLOG.LOG('forker', zLOG.DEBUG, 'connect %s' % i)
s.connect(adminaddr)
ack = s.recv(1)
zLOG.LOG('forker', zLOG.DEBUG, 'acked: %s' % ack)
break
except socket.error, e:
if e[0] <> errno.ECONNREFUSED: raise
time.sleep(1)
else:
zLOG.LOG('forker', zLOG.DEBUG, 'boo hoo')
raise
return ('localhost', port), adminaddr, pid
def shutdown_zeo_server(adminaddr):
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect(adminaddr)
ack = s.recv(1)
zLOG.LOG('shutdownServer', zLOG.DEBUG, 'acked: %s' % ack)
s.close()
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