WSGI Middleware for Managing ZODB Database Conections
Project description
WSGI Middleware for Managing ZODB Database Conections
The zc.zodbwsgi provides middleware for managing connections to a ZODB database. It combines several features into a single middleware component:
database configuration
database initialization
connection management
optional transaction management
optional request retry on conflict errors (using repoze.retry)
It is designed to work with paste deployment and provides a “filter_app_factory” entry point, named “main”.
A number of configuration options are provided. Option values are strings.
- configuration
A required ZConfig formatted ZODB database configuration
If multiple databases are defined, they will define a multi-database. Connections will be to the first defined database.
- initializer
An optional database initialization function of the form module:expression
- key
An optional name of a WSGI environment key for database connections
This defaults to “zodb.connection”.
- transaction_management
An optional flag (either “true” or “false”) indicating whether the middleware manages transactions
Transaction management is enabled by default.
- transaction_key
An optional name of a WSGI environment key for transaction managers
This defaults to “transaction.manager”. The key will only be present if transaction management is enabled.
- retry
An optional retry count
The default is “3”, indicating that requests will be retried up to 3 times. Use “0” to disable retries.
Note that when retry is not “0”, request bodies will be buffered.
Let’s look at some examples.
First we define an demonstration “application” that we can pass to our factory:
import transaction, ZODB.POSException from sys import stdout class demo_app: def __init__(self, default): pass def __call__(self, environ, start_response): start_response('200 OK', [('content-type', 'text/html')]) root = environ['zodb.connection'].root() path = environ['PATH_INFO'] if path == '/inc': root['x'] = root.get('x', 0) + 1 if 'transaction.manager' in environ: environ['transaction.manager'].get().note('path: %r' % path) else: transaction.commit() # We have to commit our own! elif path == '/conflict': print >>stdout, 'Conflict!' raise ZODB.POSException.ConflictError return [repr(root)]
Now, we’ll define our application factory using a paste deployment configuration:
[app:main] paste.app_factory = zc.zodbwsgi.tests:demo_app filter-with = zodb [filter:zodb] use = egg:zc.zodbwsgi configuration = <zodb> <demostorage> </demostorage> </zodb>
Here, for demonstration purposes, we used an in-memory demo storage.
Now, we’ll create an application with paste:
>>> import paste.deploy, os >>> app = paste.deploy.loadapp('config:'+os.path.abspath('paste.ini'))
The resulting applications has a database attribute (mainly for testing) with the created database. Being newly initialized, the database is empty:
>>> conn = app.database.open() >>> conn.root() {}
Let’s do an “increment” request.
>>> import webtest >>> testapp = webtest.TestApp(app) >>> testapp.get('/inc') <200 OK text/html body="{'x': 1}">
Now, if we look at the database, we see that there’s now data in the root object:
>>> conn.sync() >>> conn.root() {'x': 1}
We can supply a database initialization function using the initializer option. Let’s define an initialization function:
import transaction def initialize_demo_db(db): conn = db.open() conn.root()['x'] = 100 transaction.commit() conn.close()
and update our paste configuration to use it:
[app:main] paste.app_factory = zc.zodbwsgi.tests:demo_app filter-with = zodb [filter:zodb] use = egg:zc.zodbwsgi configuration = <zodb> <demostorage> </demostorage> </zodb> initializer = zc.zodbwsgi.tests:initialize_demo_db
Now, when we use the application, we see the impact of the initializer:
>>> app = paste.deploy.loadapp('config:'+os.path.abspath('paste.ini')) >>> testapp = webtest.TestApp(app) >>> testapp.get('/inc') <200 OK text/html body="{'x': 101}">
Sometimes, you may not want the middleware to control transactions. You might do this if your application used multiple databases, including non-ZODB databases [1]. You can suppress transaction management by supplying a value of “false” for the transaction_management option:
[app:main] paste.app_factory = zc.zodbwsgi.tests:demo_app filter-with = zodb [filter:zodb] use = egg:zc.zodbwsgi configuration = <zodb> <demostorage> </demostorage> </zodb> initializer = zc.zodbwsgi.tests:initialize_demo_db transaction_management = false
By default, zc.zodbwsgi adds repoze.retry middleware to retry requests when there are conflict errors:
>>> import ZODB.POSException >>> app = paste.deploy.loadapp('config:'+os.path.abspath('paste.ini')) >>> testapp = webtest.TestApp(app) >>> try: testapp.get('/conflict') ... except ZODB.POSException.ConflictError: pass ... else: print 'oops' Conflict! Conflict! Conflict! Conflict!
Here we can see that the request was retried 3 times.
We can suppress this by supplying a value of “0” for the retry option:
[app:main] paste.app_factory = zc.zodbwsgi.tests:demo_app filter-with = zodb [filter:zodb] use = egg:zc.zodbwsgi configuration = <zodb> <demostorage> </demostorage> </zodb> retry = 0
Now, if we run the app, the request won’t be retried:
>>> app = paste.deploy.loadapp('config:'+os.path.abspath('paste.ini')) >>> testapp = webtest.TestApp(app) >>> try: testapp.get('/conflict') ... except ZODB.POSException.ConflictError: pass ... else: print 'oops' Conflict!
Changes
0.1.0 (2010-02-16)
Initial release
Project details
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.