Extras are extensions available for certain methods in Rocket. Currently there is only the Futures draft specification available for the WSGI method.
** EXPERIMENTAL **
This proposes two new standard environment keys environ['wsgiorg.executor'] and environ['wsgiorg.futures']. wsgiorg.executor will provide a wrapped futures (PEP 3148) executor that a WSGI application may use to register a function to be run outside of the normal WSGI request/response processing. wsgiorg.futures provides a dictionary that an application can use to reference previously submitted jobs.
As web applications grow in complexity and power, there is a growing need to execute operations related to a request that don’t need to finish before the response is fully sent to the client. Some example use cases are:
- Send an email
- Build or clear a cache
- Vacuum a database
Other applications could run jobs that would span requests such as generating a report or building a custom download. Subsequent polling requests could check the if the method was finished finding the named future available in wsgiorg.futures
This specification defines two new keys that can go in the WSGI environment, wsgiorg.executor and environ['wsgiorg.futures']. wsgiorg.executor is a wrapped futures executor. There is only one method available to a WSGI application.
submit(func, \*args,\*\*kwargs)
submit func to be executed as func(\*args, \*\*kwargs) in a separate thread/process. A special future instance is returned.
The executor will also have additional properties to indicate the concurrency model. These values are not mutually-exclusive and may both be true if a server implements such an executor. The concurrency model of the executor is implementation specific. These properties are:
multithread
a boolean value indicating if functions may be run in a separate thread from which the function is submitted
multiprocess
a boolean value indicating if functions may be run outside of the the process in which the function is submitted.
The future instance returned by submit has all of the normal methods of a standard futures instance but also has additional methods:
remember(name, lifespan, duplicate_behavior="raise")
Saves the future in wsgiorg.futures as name for up to lifespan seconds after the function associated with this future has completed. The lifespan parameter may be defaulted as a server-specific configuration option. duplicate_behavior dictates how the future should respond if there is already a future of the same name listed
forget()
Remove thes future from wsgiorg.futures. This action does not change the state of the future. A “forgotten” is not necessarily a cancelled future.
The future instance also has an additional property:
timeout
a float value property in seconds indicating how long the future will wait in the queue to run. Just before a function is run, its timeout value is checked. If the timeout value is less than the time elapsed waiting in the queue, the function will be cancelled. The default value will be None which means that functions will wait indefinitely to run.
wsgiorg.futures is a mapping type of remembered futures. The key is the name parameter passed to future.remember(). The futures provided must only be previously saved with a call to remember(). WSGI application may only access futures contained in wsgiorg.futures. Applications must not attempt to delete or overwrite values in wsgiorg.futures. It is effectively read-only.
The nature of running futures brings the possibility of certain deadlock situations. To avoid deadlocks, submitted functions should never wait on other submitted functions.
Also functions and parameters passed to submit() must abide by the same picklability limitations as the multiprocessing module can handle.
This example is a WSGI application that uses all features of this specification.
from concurrent import futures
class WsgiOrgFuturesExecutor(futures.ThreadPoolExecutor):
class WsgiOrgFuture(futures.Future):
def __init__(self):
self.timeout = None
def remember(self, name, lifespan=60):
return self
def forget(self):
return self
Example using futures to generate a report:
# inside a WSGI application...
def buildReport(vars):
pass
rpt_fut = environ["wsgiorg.executor"].submit(buildReport, data)
rpt_fut.remember("client-123-report")
rpt_fut.timeout = 90