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 server.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 server.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 utils.getCurrentUser() 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 server.tasks.CallableTaskBase and decorated with server.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 server.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.