Tasks¶
ViUR is shipped with a sophisticated interface for tasks, providing a standardized way of having a function called in a regular interval, providing an interface for tasks that can be called by the user on demand, and deferring functionality from the current request.
Time based¶
A common problem on the GAE is having certain functionality executed in a regular interval.
ViUR simplifies this down to a decorator. Just wrap a function with viur.core.tasks.PeriodicTask()
and
ViUR will call it in a regular interval.
The decorator takes one argument - the interval in minutes.
ViUR will not call your function faster than once in each interval-minutes.
from server.tasks import PeriodicTask
@PeriodicTask(24*60)
def mytask(): # Will be called roughly every 24 hours
logging.debug("Your deferred task was just called :)")
Note
This is the lower bound. There is no guarantee that it will be called each ‘interval’ minutes. The upper bound is defined in cron.yaml and currently defaults to each 4 hours.
Note
Time-based tasks can be a bound or unbound Function. If a function is bound (defined in a class) it will be called once for each module derived from this class. If there is no instance of its class, it won’t be called. If its unbound (defined at module-level) it will be called, regardless if any Class in its module is used or not (but the module itself needs to be imported).
Warning
There’s currently only a main loop which calls all function scheduled for execution. It has a time-limit of 10 minutes. If your task takes more than a few minutes to execute, you should defer that code. Otherwise your tasks (all tasks in total) might exceed these 10 minutes, which causes the request to be aborted despite not all tasks had been called yet.
Deferred¶
Sometimes its necessary to delay the execution of some specific code, so it won’t slow down the
response of the current request. ViUR provides the viur.core.tasks.callDefered()
Decorator for such cases.
A function decorated this way will never execute in the context of the current request. All calls to
such a function are catched, its parameters serialized, and a task is created to call this function later.
These calls are executed in a deferred task which can run up to 10 minutes. As these tasks run deferred, they run outside
of the current context where they had been created. ViUR however will preserve the following two values:
The currently logged in user (if any). If the task was created in the context of a known user, calls to current.user.get() will return the same values as it would have returned when the task had been created.
The language used for the request. Within the deferred task any calls to i18N functions provided by ViUR will yield results in the language of the original request.
Note
A deferred function cannot return a value! The return-value for the code calling such a function will always be None, and any return-value generated by the function (when its actually called) will be discarded.
Warning
This replaces the deferred-api provided by the GAE itself. Don’t use it - it will break assumptions made by ViUR!
On Demand¶
The third use-case for tasks is on demand: A task that’s run infrequently by the user.
One example is our rebuild searchindex task: If changes are made to a data-model (ie. include
the contents of a Bone in the fulltext search), and there is already data in the datastore
created by the old model, its necessary to update the searchindex, as it doesn’t contain
the contents of that bone yet.
It would be a waste of resources if we rebuild each index frequently.
So this task is only called on demand. If the developer has made changes to the datamodel,
he calls that task once for each affected kind.
Creating such a task is also easy, it’s a Class derived from viur.core.tasks.CallableTaskBase
and decorated with
viur.core.tasks.CallableTask()
. The derived subclass must override the following properties and functions.
Name |
Type |
Description |
---|---|---|
key |
Property (String) |
An unique identifier for this task. |
name |
Property (String) |
A short human-readable description |
descr |
Property (String) |
A longer explanation |
canCall |
Function |
Must return True if the current user (if any) is allowed to execute that task. Return False otherwise. |
dataSkel |
Function or Skeleton-class |
If your tasks need additional Input (ie: which searchindex?) from the user, query him by returning an skeleton. Return None if you don’t need any information. |
execute |
Function |
Does the actual work. If you returned a skeleton in dataSkel, the values of that Skeleton are passed as keyword arguments. |
On instance startup¶
The last hook you can use is the viur.core.tasks.StartupTask()
decorator. This way you can have code being executed
whenever a new instance starts up without slowing down the instance startup itself (The code will be called deferred
shortly after an instance gets ready).
Useful to ensure some database initialization or the like.
Warning
There’s absolutely no guarantee that the function will be called on the instance that started up. It can be called any of the currently running instances. So it’s possible that such a function is called never, once or multiple times on the same instance. Do not put any code here required to correctly setup your instances.