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
37 changes: 28 additions & 9 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -69,17 +69,22 @@ execution = client.durable.start(
)
print(execution.id, execution.status)

execution = client.durable.get(
owner_client = VolcanoClient(
api_url="https://api.volcano.dev",
anon_key="your-anon-key",
access_token="your-platform-user-token",
)
execution = owner_client.durable.get(
"00000000-0000-4000-8000-000000000001", "order-pipeline", execution.id
)
print(execution.is_terminal, execution.result)

executions = client.durable.list(
executions = owner_client.durable.list(
"00000000-0000-4000-8000-000000000001", "order-pipeline", status="running"
)
print(executions.total, executions.has_more)

client.durable.stop(
owner_client.durable.stop(
"00000000-0000-4000-8000-000000000001", "order-pipeline", execution.id
)

Expand Down Expand Up @@ -239,18 +244,18 @@ A function's own response, HTTP 403, or a network failure never triggers this re
Anonymous and service keys do not refresh.

`durable.start()` begins an execution of a deployed durable function and returns
a handle rather than a result: an execution can run for hours, so its result is
read back with `durable.get()`. It takes the credential `functions.invoke()`
a handle rather than a result: an execution can run for up to 366 days, so its
result is read back with `durable.get()`. It takes the credential `functions.invoke()`
takes, and is the only durable operation an application credential may perform.
An `execution_name` makes the start idempotent — starting again under the same
name returns the execution that already exists rather than beginning a second
one, and is charged once.

`durable.get()`, `durable.list()` and `durable.stop()` are owner-scoped and need
the project's own platform token, because an execution is addressed by its id
alone and an anonymous key is held by everyone who loads the page. Neither an
auth-user session from `sign_in()` nor a service key is accepted -- the routes
take a user token, and anything else is answered 401. Poll them from a backend.
the project owner's platform user token, because an execution is addressed by
its id alone and an anonymous key is held by everyone who loads the page.
Auth-user sessions from `sign_in()`, anonymous keys, service keys, and project
access tokens are not accepted. Poll them from a trusted backend.
`get()` carries `result` once the execution has succeeded and `error` when it
failed; `result_expired` separates a result the platform has discarded from a
function that returned nothing. `is_terminal` reports whether the execution has
Expand Down Expand Up @@ -331,6 +336,20 @@ nothing while it waits. Running the handler anywhere durable execution does not
exist raises `DurableRuntimeMissingError` rather than an import error from an
unfamiliar package.

Run the same handler through the local durable engine:

```bash
volcano start
volcano durable deploy --all
volcano durable start order-pipeline --input '{"order_id":"order-9"}'
```

Local waits resolve immediately by default while preserving checkpoint and replay
behavior. Set `LOCAL_DURABLE_REAL_TIME=true` before `volcano start` when wait
timing must match the deployed function. Local executions persist across
`volcano stop` and `volcano start`. Volcano does not expose externally completed
callbacks; use `ctx.wait_until` to poll application state instead.

`logs.search()` returns an immutable page of retained runtime or deployment log
events. Pass `next_cursor` back as `cursor` to continue a search. `logs.activity()`
returns immutable time buckets using the same resource selector and query syntax.
Expand Down
2 changes: 1 addition & 1 deletion docs/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,6 @@ See [Realtime](./realtime.md) for broadcasts, presence, database changes, and sh

See [Authentication](./authentication.md) for account, session, email, and OAuth workflows.

See [Functions](./functions.md) for invocation identity, response values, and error handling.
See [Functions](./functions.md) for standard invocation, durable execution, local durable development, and error handling.

See [Versions and compatibility](./versions.md) for runtime support, upgrades, and restoring a tested dependency set.
56 changes: 56 additions & 0 deletions docs/functions.md
Original file line number Diff line number Diff line change
Expand Up @@ -63,3 +63,59 @@ A function's own HTTP 404 does not trigger another invocation.
Network failures do not establish whether a function ran; do not blindly retry operations with side effects.

Invalid invocation arguments and malformed successful resolution responses can raise `ValueError` or `TypeError`.

## Start and follow a durable execution

A durable execution can run for up to 366 days. Starting one returns a handle instead of waiting for its result:

```python
handle = client.durable.start(
"charge-order",
{"order_id": "order-9"},
execution_name="order-9",
)

owner_client = VolcanoClient(
anon_key=os.environ["VOLCANO_ANON_KEY"],
api_url=os.environ.get("VOLCANO_API_URL", "https://api.volcano.dev"),
access_token=os.environ["VOLCANO_PLATFORM_TOKEN"],
)
execution = owner_client.durable.get(project_id, "charge-order", handle.id)
page = owner_client.durable.list(project_id, "charge-order", status="running")
owner_client.durable.stop(project_id, "charge-order", handle.id)
```

`start()` accepts the same active session, service key, or anonymous key as `functions.invoke()`. It is the only durable operation available to application credentials. An execution name makes a start idempotent.

`get()`, `list()`, and `stop()` are owner-scoped. Call them from a trusted backend with the project owner's platform user token. Auth-user sessions, anonymous keys, service keys, and project access tokens are not accepted. `stop()` returns after the stop request is accepted, so poll `get()` until `is_terminal` is true.

## Write a durable function

Use `volcano_sdk.durable_authoring` in a durable function running on `python3.13` or `python3.14`:

```python
from volcano_sdk.durable_authoring import durable


@durable
def handler(event, ctx):
charge = ctx.step("charge", lambda scope: charge_card(event["order_id"]))
ctx.wait("settle", "30s")
return {"charge_id": charge["id"]}
```

Volcano records each context operation. Resumed executions replay recorded results instead of repeating completed work. Keep changing decisions inside `ctx.step()`. Use `ctx.wait_until()` to poll application state. Volcano does not expose externally completed callbacks.

## Run durable functions locally

Deploy and start the same handler through the local durable engine:

```bash
volcano start
volcano durable deploy --all
volcano durable start charge-order --input '{"order_id":"order-9"}'
```

Local waits resolve immediately by default while preserving checkpoint and replay behavior. Set `LOCAL_DURABLE_REAL_TIME=true` before `volcano start` when wait timing must match the deployed function. Local executions persist across `volcano stop` and `volcano start`.

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