Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -388,6 +388,7 @@ the clock or a random value.
| `ctx.wait_until(check, options, name=None)` | Polls your own state until `options.until` holds, suspending between checks. `options.initial_state` is required. |
| `ctx.wait_for_approval(name, title=..., description=None, details=None, timeout=None)` | Suspends until a person approves or denies, and returns an `ApprovalDecision`. A timeout returns `status="expired"` rather than raising. |
| `ctx.wait_for_message(name, timeout=None)` | Suspends until a message with that name is sent to the execution, and returns a `ReceivedMessage`, or `None` once the timeout passes. |
| `ctx.wait_for_callback(name, submitter, timeout=None, heartbeat_timeout=None)` | Hands a token to another system and suspends until it succeeds or fails the callback. Returns the result, or `None` once a timeout passes; a failure raises `CallbackFailedError`. |
| `ctx.publish(name, data=None)` | Records a message for applications to list or receive live, and returns a `PublishedMessage` with `id` and `seq`. |
| `ctx.map(items, fn, name=None, options=None)` | Runs the same work over every item, each in its own child context. |
| `ctx.parallel(branches, name=None, options=None)` | Runs independent branches at the same time. |
Expand Down
51 changes: 51 additions & 0 deletions docs/functions.md
Original file line number Diff line number Diff line change
Expand Up @@ -209,6 +209,55 @@ def handler(event, ctx):

Names follow the approval rules: up to 237 printable ASCII characters, checked before anything is recorded, as is the timeout. Both need `VOLCANO_PLATFORM_API_URL`. A wait Volcano does not answer, cannot see yet, throttles, or fails on is retried for the same 30 seconds an approval gets, then raises `CallbackSubmitterError`. A wait Volcano refuses, for example with `too_many_message_waits` when 100 are already open, raises it at once. A refused publish fails its step without a retry, which fails the execution unless the handler catches it.

## Wait for another system with a callback token

`ctx.wait_for_callback()` hands a token to another system and pauses the execution until that system answers with it. Use it when the work happens somewhere you do not run code, such as a payment provider, a job queue, or a partner's API, and that system can call back once it is done:

```python
from volcano_sdk.durable_authoring import CallbackFailedError, durable


@durable
def handler(event, ctx):
try:
payment = ctx.wait_for_callback(
"payment",
lambda token: payments.start(
order_id=event["order_id"], callback_token=token
),
timeout="1h",
heartbeat_timeout="10m",
)
except CallbackFailedError as error:
return {"status": "declined", "reason": error.type, "message": error.message}

if payment is None:
return {"status": "abandoned"}
return ctx.step(
"fulfil", lambda scope: fulfil(event["order_id"], payment["charge_id"])
)
```

The submitter receives a `vcb_` token. The system holding it answers with the token alone and needs no Volcano credential:

```bash
curl -X POST "https://api.volcano.dev/durable-callbacks/succeed" \
-H 'Content-Type: application/json' \
-d '{"token":"vcb_...","result":{"charge_id":"ch_1"}}'

curl -X POST "https://api.volcano.dev/durable-callbacks/fail" \
-H 'Content-Type: application/json' \
-d '{"token":"vcb_...","error":{"type":"CardDeclined","message":"Insufficient funds"}}'

curl -X POST "https://api.volcano.dev/durable-callbacks/heartbeat" \
-H 'Content-Type: application/json' \
-d '{"token":"vcb_..."}'
```

`wait_for_callback(name, submitter, timeout=None, heartbeat_timeout=None)` returns the JSON value the token was succeeded with, or `None` once `timeout` passes, or `heartbeat_timeout` passes without a heartbeat. A result is never `None`, so the two cannot be confused. A failure raises `CallbackFailedError` with the `type` and `message` it was failed with. A token takes one answer: a second gets `409 callback_completed`, and one after the wait timed out or the execution ended gets `410`. It works only for this wait, so treat it as a secret.

The submitter runs inside a checkpointed step that is not retried, so a replay does not run it again. An attempt interrupted before the step is recorded can run it twice, with the same token both times, so make the hand-off idempotent; if the token was answered or the wait timed out in between, it is not run again. When it raises, `wait_for_callback()` raises the runtime's `CallbackSubmitterError`. It must be synchronous: an `async def` submitter raises `TypeError` before the callback opens, and one that returns an awaitable fails the wait instead of handing off. Getting the token needs `VOLCANO_PLATFORM_API_URL` and is retried for the same 30 seconds an approval gets. See [Durable callbacks](/platform/functions/durable-callbacks) for every request, response, and limit.

## Decide approvals from a backend

`client.durable.approvals` lists, reads, and decides a project's approvals:
Expand Down Expand Up @@ -322,4 +371,6 @@ Approvals work the same locally. Decide one with `volcano durable approvals appr

Messages work the same locally too. Send one with `volcano durable messages send`, or point the client at the local server. Message wait timeouts run in real time.

Callback tokens work locally as well: send the same requests to the local server. A callback's timeout and heartbeat timeout run in real time.

Running a decorated handler directly in a Python process still needs the optional test runtime: `python -m pip install 'volcano-sdk-python[durable]'`.
Loading
Loading