# spydurus provides a threadsafe, pooled Spyce interface to a durus database. # Author: Jonathan Ellis # If you're just trying to get a feel for Spyce, the main thing to # understand is that this provides (via a subclass) the get method you # see used (e.g., "data.get(id)") in .spy pages. More than that you don't # really need to know unless you need to write your own connection # pool module. CONNECTIONS = 3 # max connections to put in the pool import sys, os, os.path, threading, time import spyce, spyceUtil # try to start a server: # in a production environment, you'd keep it running with inittab or something, # so this step wouldn't be necessary. # The best approach is to fork and then use StorageServer.serve to start durus; # that way we don't have to worry about durus being on the path. However, # since win32 doesn't support fork, AND that's where we're most likely to have # PATH issues (durus on win32 doesn't modify the path on install), # in the interest of making this run out of the box for windows people we'll # take the StorageServer approach, but in a thread, not a new process. # Please use the inittab approach in production; Durus will be more performant # in its own process. from durus.storage_server import StorageServer from durus.file_storage import FileStorage, TempFileStorage _runningDurus = False def maybeStartDurus(db_path): if spyce.getServer().threaded(): _runningDurus = True def _maybeStartDurus(): st = db_path and FileStorage(db_path) or TempFileStorage() try: StorageServer(st).serve() except: # already running _runningDurus = False _t = threading.Thread(target=_maybeStartDurus) _t.start() def _cleanup(): if _runningDurus: from durus.run_durus import stop_durus, DEFAULT_HOST, DEFAULT_PORT stop_durus(DEFAULT_HOST, DEFAULT_PORT) _t.join() import atexit atexit.register(_cleanup) import Queue q = Queue.Queue() def initPool(connections=3, db_path=None): "if another process takes care of durus, you never need to pass db_path" from durus.connection import Connection if spyce.getServer().threaded(): from durus.client_storage import ClientStorage for i in range(connections * 3): if q.qsize() >= connections: break try: q.put(Connection(ClientStorage())) except: time.sleep(0.5) # sometimes takes a while for server to start else: # not a long-running process -- spyce running under [Fast]CGI or mod_python st = spyceUtil.tryForAwhile(lambda: FileStorage(db_path)) if not st: raise 'timeout while getting durus connection' q.put(Connection(st)) if not q.qsize(): raise 'no durus connections created' # the actual spyceModule is refreshingly short now that starting Durus is out of the way from spyceModule import spyceModule class spydurus(spyceModule): def start(self): self.conn = None try: self.conn = q.get(timeout=10) except Queue.Empty: raise 'timeout while getting durus connection' else: self.conn.abort() # syncs connection # spyce automatically calls finish methods at the end of each request. # this is an excellent way to make sure that our connection always # returns to the queue. def finish(self, err): q.put(self.conn) def root(self): return self.conn.get_root() def get(self, oid): """oid should be a formatted oid, not a raw _p_oid""" return self.conn.get(int(oid)) def commit(self): self.conn.commit() # you may wish to inherit from this instead of Persistent directly from durus.persistent import Persistent class PersistentWithId(Persistent): # _p_format_oid is rather cumbersome to type def id(self): return self._p_format_oid()