Note that there are some explanatory texts on larger screens.

plurals
  1. POBest practice for integrating CherryPy web-framework, SQLAlchemy sessions and lighttpd to serve a high-load webservice
    text
    copied!<p>I'm developing a CherryPy FastCGI server behind lighttpd with the following setup to enable using ORM SQLAlchemy sessions inside CherryPy controllers. However, when I run stress tests with 14 concurrent requests for about 500 loops, it starts to give errors like <code>AttributeError: '_ThreadData' object has no attribute 'scoped_session_class'</code> in <code>open_dbsession()</code> or <code>AttributeError: 'Request' object has no attribute 'scoped_session_class'</code> in <code>close_dbsession()</code> after a while. The error rate is around 50% in total.</p> <p>This happens only when I run the server behind lighttpd, not when it's run directly through <code>cherrypy.engine.start()</code>. It's confirmed that <code>connect()</code> isn't raising exceptions.</p> <p>I also tried assigning the return value of <code>scoped_session</code> to <code>GlobalSession</code> (like it does <a href="http://code.google.com/p/webpyte/source/browse/trunk/webpyte/sqlalchemy_tool.py" rel="noreferrer">here</a>), but then it gave out errors like <code>UnboundExceptionError</code> and other SA-level errors. (Concurrency: 10, loops: 1000, error rate: 16%. Occurs even when run directly.)</p> <p>There are some possible causes but I lack sufficient knowledge to pick one.<br> 1. Are <code>start_thread</code> subscriptions unreliable under FastCGI environment? It seems that <code>open_dbsession()</code> is called before <code>connect()</code><br> 2. Does <code>cherrypy.thread_data</code> get cleared for some reason?</p> <h3>server code</h3> <pre><code>import sqlalchemy as sa from sqlalchemy.orm import session_maker, scoped_session engine = sa.create_engine(dburi, strategy="threadlocal") GlobalSession = session_maker(bind=engine, transactional=False) def connect(thread_index): cherrypy.thread_data.scoped_session_class = scoped_session(GlobalSession) def open_dbsession(): cherrypy.request.scoped_session_class = cherrypy.thread_data.scoped_session_class def close_dbsession(): cherrypy.request.scoped_session_class.remove() cherrypy.tools.dbsession_open = cherrypy.Tool('on_start_resource', open_dbsession) cherrypy.tools.dbsession_close = cherrypy.Tool('on_end_resource', close_dbsession) cherrypy.engine.subscribe('start_thread', connect) </code></pre> <h3>lighttpd fastcgi config</h3> <pre><code>... var.server_name = "test" var.server_root = "/path/to/root" var.svc_env = "test" fastcgi.server = ( "/" =&gt; ( "cherry.fcgi" =&gt; ( "bin-path" =&gt; server_root + "/fcgi_" + server_name + ".fcgi", "bin-environment" =&gt; ( "SVC_ENV" =&gt; svc_env ), "bin-copy-environment" =&gt; ("PATH", "LC_CTYPE"), "socket" =&gt; "/tmp/cherry_" + server_name + "." + svc_env + ".sock", "check-local" =&gt; "disable", "disable-time" =&gt; 1, "min-procs" =&gt; 1, "max-procs" =&gt; 4, ), ), ) </code></pre> <h3>edits</h3> <ul> <li>Restored the missing <code>thread_index</code> argument in the code example from the original source code (thanks to the comment)</li> <li>Clarified that errors do not occur immediately</li> <li>Narrowed down the conditions to lighttpd</li> </ul>
 

Querying!

 
Guidance

SQuiL has stopped working due to an internal error.

If you are curious you may find further information in the browser console, which is accessible through the devtools (F12).

Reload