Worker Lambda & async tasks
WorkerFunction (actionstools-<env>-worker, handler lambda_handlers.worker_handler) is the
only place background work runs. It replaces Zappa’s @task decorator and the separate prod2
deployment.
@async_task
from actionstools_app.tools.async_task import async_task
@async_task
@handle_async_errors
def notify_on_slack(slack_context, email, action_name, name):
...
Calling a decorated function:
-
With
WORKER_FUNCTION_NAMEset (both deployed functions): serialises{"task_path", "args", "kwargs"}withsimplejsonand callslambda:InvokewithInvocationType="Event". The caller getsNoneback immediately. -
Without it (tests,
make run, CI): runs the function inline, synchronously.
The undecorated function stays reachable as fn.sync; the Worker calls that, so a task never
re-queues itself.
|
Keep @handle_async_errors inside @async_task: it initialises Sentry and logs the exception
before re-raising.
Events the Worker accepts
worker_handler pushes a Flask app context, then dispatches on the event shape:
| Event | Source | Runs |
|---|---|---|
|
|
The task’s |
|
|
|
|
|
|
Anything else raises ValueError("Unrecognised worker event").
{
"task_path": "actionstools_app.tasks.notifications.notify_on_slack",
"args": [
{"login_id": "agent@payability.com", "task_url": "https://internal.payability.com/#/tasks?taskUuid=fcee792a-9797-4857-a948-02e4df2ed171", "number": 170, "title": "New task", "body": "<p>TEST</p>"},
"assignee@payability.com",
"assigned",
"Assignee Name"
],
"kwargs": {}
}
Failure semantics
| Source | On error |
|---|---|
Async task |
|
Tasks stream |
Retried up to 3 times, splitting the batch in half on each failure ( |
Schedule |
Not retried; the next run is 4 days later. Re-run by hand (below). |
Running things by hand
# Re-run the counters cleanup
aws lambda invoke --function-name actionstools-prod-worker \
--cli-binary-format raw-in-base64-out \
--payload '{"action": "remove_counters"}' /dev/stdout
# Run any async task with explicit arguments
aws lambda invoke --function-name actionstools-stage-worker \
--cli-binary-format raw-in-base64-out \
--payload file://task.json /dev/stdout
Adding a task
-
Write a module-level function in
actionstools_app/…with JSON-friendly parameters. -
Decorate it
@async_task(outer) and@handle_async_errors(inner). -
If it touches a new AWS resource, add the permission to both functions in
template.yaml(the API may run it inline in tests, the Worker runs it in production). -
Add a test in
tests/test_lambda_handlers.pystyle: call it withWORKER_FUNCTION_NAMEunset to exercise the logic, and with a mocked Lambda client to check the payload.