It all depends on the size of the website. CherryPy is a streaming server, and as soon as each thread waits for a database, new requests will not be processed. There is also an aspect of the request queue, but overall it is.
Bad decision
If you know that you have little traffic, you can try a workaround. Increase response.timeout if necessary (300 seconds by default). Increase server.thread_pool (default is 10). If you use a backup proxy server, such as nginx, before the CherryPy application, also increase the proxy server time.
The following solutions will require you to redesign your site. In particular, to make it asynchronous, where the client code sends the task, and then uses pull or push to get its result. This will require changes on both sides of the wire.
Cherrypy backgroundtask
You can use cherrypy.process.plugins.BackgroundTask and some intermediate storage (like a new table in your database) on the server side. XmlHttpRequest for pull or WebSockets for the push client side. CherryPy can handle both.
Note that since CherryPy runs in the same Python process, the background task thread will also work in it. If you do some post-processing of the SQL result set, the GIL will affect you. Therefore, you can rewrite it instead of using processes, which is a bit more complicated.
Industrial solution
If your site works or is considered functioning on a scale, you better consider a distributed task queue such as Rq or Celery . This makes a difference on the server side. Client side is the same click or click.
Example
Here's the implementation of the toy for BackgroundTags with an XHR poll.
#!/usr/bin/env python # -*- coding: utf-8 -*- import time import uuid import cherrypy from cherrypy.process.plugins import BackgroundTask config = { 'global' : { 'server.socket_host' : '127.0.0.1', 'server.socket_port' : 8080, 'server.thread_pool' : 8, } } class App: _taskResultMap = None def __init__(self): self._taskResultMap = {} def _target(self, task, id, arg): time.sleep(10) # long one, right? try: self._taskResultMap[id] = 42 + arg finally: task.cancel() @cherrypy.expose @cherrypy.tools.json_out() def schedule(self, arg): id = str(uuid.uuid1()) self._taskResultMap[id] = None task = BackgroundTask( interval = 0, function = self._target, args = [id, int(arg)], bus = cherrypy.engine) task.args.insert(0, task) task.start() return str(id) @cherrypy.expose @cherrypy.tools.json_out() def poll(self, id): if self._taskResultMap[id] is None: return {'id': id, 'status': 'wait', 'result': None} else: return { 'id' : id, 'status' : 'ready', 'result' : self._taskResultMap.pop(id) } @cherrypy.expose def index(self): return '''<!DOCTYPE html> <html> <head> <title>CherryPy BackgroundTask demo</title> <script type='text/javascript' src='http://cdnjs.cloudflare.com/ajax/libs/qooxdoo/3.5.1/q.min.js'> </script> <script type='text/javascript'> // Do not structure you real JavaScript application this way. // This callback spaghetti is only for brevity. function sendSchedule(arg, callback) { var xhr = q.io.xhr('/schedule?arg=' + arg); xhr.on('loadend', function(xhr) { if(xhr.status == 200) { callback(JSON.parse(xhr.responseText)) } }); xhr.send(); }; function sendPoll(id, callback) { var xhr = q.io.xhr('/poll?id=' + id); xhr.on('loadend', function(xhr) { if(xhr.status == 200) { callback(JSON.parse(xhr.responseText)) } }); xhr.send(); } function start(event) { event.preventDefault(); // example argument to pass to the task var arg = Math.round(Math.random() * 100); sendSchedule(arg, function(id) { console.log('scheduled (', arg, ') as', id); q.create('<li/>') .setAttribute('id', id) .append('<span>' + id + ': 42 + ' + arg + ' = <img src="http://sstatic.net/Img/progress-dots.gif" />' + '</span>') .appendTo('#result-list'); var poll = function() { console.log('polling', id); sendPoll(id, function(response) { console.log('polled', id, '(', response, ')'); if(response.status == 'wait') { setTimeout(poll, 2500); } else if(response.status == 'ready') { q('#' + id) .empty() .append('<span>' + id + ': 42 + ' + arg + ' = ' + response.result + '</span>'); } }); }; setTimeout(poll, 2500); }); } q.ready(function() { q('#run').on('click', start); }); </script> </head> <body> <p> <a href='#' id='run'>Run a long task</a>, look in browser console. </p> <ul id='result-list'></ul> </body> </html> ''' if __name__ == '__main__': cherrypy.quickstart(App(), '/', config)