System- and user-level tasks need to happen on a recurring basis in Zope, such as database packing, session data garbage collection, and so on. There is no generic facility in Zope which allows us to to perform these tasks.
This proposal specifically does not deal with UI and policy issues related to automated task scheduling; it only proposes the implementation of the generic mechanisms which all automated task schedulers could make use, such as a clock and a load indicator.
Clock Service: this is a simple bit of code which contacts listeners every N seconds. There is one and only one clock service per Zope instance.
Clock Listener: this is an object which listens to announcements from the clock service.
Load Indicator: a metric which allows us to determine how "busy" Zope is at a particular time.
Typically, all activity in Zope takes place based on diversions from
a mainloop. The mainloop (which is based on Python's asyncore)
continually performs a
select on a set of file descriptors, each
of which is related to one of Zope's various servers, aka
If select determines that there is any activity on any file descriptor in the set it was provided, it returns the filedescriptors on which it detected the avtivity. The mainloop then performs actions against the dispatchers related to the "active" filedescriptors, reading or writing from them as necessary. Select "times out" if there is no read/write activity within a certain amount of time (typically 30 seconds). When select times out, it returns an empty set of "active" filedescriptors.
We can get a rough sense of how "busy" Zope is at any particular time by keeping a running average of the times between mainloop select returns. Each time select returns in the mainloop, compute the difference between the current time and the last time there was a select return. Over time, average the difference. A large number indicates a not-so-busy system, a small number indicates a busy system. If the select timeout value is used as a baseline, we could compute some other scaled number, perhaps a float between 0 and 1 where 0 indicates a completely unused system and 1 indicates a completely saturated system.
This code could be placed in the Lifetime.py module and an API could be provided in this module as a function which returns the Zope instance's load value.
The clock service is very simple. It has two duties:
This behavior could either be implemented in a separate thread that is started when Zope starts or could be caused to happen in the mainloop.
A clock listener is any piece of code which registers a callback with the clock service. For instance, this could be a generic recurring scheduling service, like Xron, or a system-specific scheduled task like garbage collection. Its minimal duty is to register a callback with the clock service.
A clock event is just a special type of event. It is tempting to include a generic event system to support this single use case, although not necessary.