From 176cc1dbabbd752bf87cb735c782e68a4fb17c8a Mon Sep 17 00:00:00 2001 From: Till Rohrmann Date: Tue, 18 Jun 2024 12:56:50 +0200 Subject: [PATCH 01/18] Integrate sql introspection page generation This commit integrates the newly added automatic sql schema generation into the generate.sh script to generate the sql introspection page. This fixes #421. --- docs/references/sql-introspection.md | 129 +++++++++++++++++++++++ docs/references/sql-introspection.mdx | 116 -------------------- tools/generate.sh | 3 + tools/generate_sql_introspection_page.sh | 24 +++++ 4 files changed, 156 insertions(+), 116 deletions(-) create mode 100644 docs/references/sql-introspection.md delete mode 100644 docs/references/sql-introspection.mdx create mode 100755 tools/generate_sql_introspection_page.sh diff --git a/docs/references/sql-introspection.md b/docs/references/sql-introspection.md new file mode 100644 index 00000000..6a2dc5db --- /dev/null +++ b/docs/references/sql-introspection.md @@ -0,0 +1,129 @@ +--- +sidebar_position: 3 +description: "API reference for inspecting the invocation status and service state." +--- +# SQL Introspection API + +This page contains the reference of the introspection tables. +To learn how to access the instrospection interface, check out the [instrospection documentation](/operate/introspection). + +## Table: `state` + +| Column name | Type | Description | +|-------------|------|-------------| +| `partition_key` | `UInt64` | Internal column that is used for partitioning the services invocations. Can be ignored. | +| `service_name` | `Utf8` | The name of the invoked service. | +| `service_key` | `Utf8` | The key of the Virtual Object. | +| `key` | `Utf8` | The `utf8` state key. | +| `value_utf8` | `Utf8` | Only contains meaningful values when a service stores state as `utf8`. This is the case for services that serialize state using JSON (default for Typescript SDK, Java/Kotlin SDK if using JsonSerdes). | +| `value` | `Binary` | A binary, uninterpreted representation of the value. You can use the more specific column `value_utf8` if the value is a string. | + +## Table: `sys_journal` + +| Column name | Type | Description | +|-------------|------|-------------| +| `partition_key` | `UInt64` | Internal column that is used for partitioning the services invocations. Can be ignored. | +| `id` | `Utf8` | [Invocation ID](/operate/invocation#invocation-identifier). | +| `index` | `UInt32` | The index of this journal entry. | +| `entry_type` | `Utf8` | The entry type. You can check all the available entry types in [`entries.rs`](https://github.com/restatedev/restate/blob/main/crates/types/src/journal/entries.rs). | +| `name` | `Utf8` | The name of the entry supplied by the user, if any. | +| `completed` | `Boolean` | Indicates whether this journal entry has been completed; this is only valid for some entry types. | +| `invoked_id` | `Utf8` | If this entry represents an outbound invocation, indicates the ID of that invocation. | +| `invoked_target` | `Utf8` | If this entry represents an outbound invocation, indicates the invocation Target. Format for plain services: `ServiceName/HandlerName`, e.g. `Greeter/greet`. Format for virtual objects/workflows: `VirtualObjectName/Key/HandlerName`, e.g. `Greeter/Francesco/greet`. | +| `sleep_wakeup_at` | `Date64` | If this entry represents a sleep, indicates wakeup time. | +| `raw` | `Binary` | Raw binary representation of the entry. Check the [service protocol](https://github.com/restatedev/service-protocol) for more details to decode it. | + +## Table: `sys_keyed_service_status` + +| Column name | Type | Description | +|-------------|------|-------------| +| `partition_key` | `UInt64` | Internal column that is used for partitioning the services invocations. Can be ignored. | +| `service_name` | `Utf8` | The name of the invoked virtual object/workflow. | +| `service_key` | `Utf8` | The key of the virtual object/workflow. | +| `invocation_id` | `Utf8` | [Invocation ID](/operate/invocation#invocation-identifier). | + +## Table: `sys_inbox` + +| Column name | Type | Description | +|-------------|------|-------------| +| `partition_key` | `UInt64` | Internal column that is used for partitioning the services invocations. Can be ignored. | +| `service_name` | `Utf8` | The name of the invoked virtual object/workflow. | +| `service_key` | `Utf8` | The key of the virtual object/workflow. | +| `id` | `Utf8` | [Invocation ID](/operate/invocation#invocation-identifier). | +| `sequence_number` | `UInt64` | Sequence number in the inbox. | +| `created_at` | `Date64` | Timestamp indicating the start of this invocation. | + +## Table: `sys_idempotency` + +| Column name | Type | Description | +|-------------|------|-------------| +| `partition_key` | `UInt64` | Internal column that is used for partitioning the services invocations. Can be ignored. | +| `service_name` | `Utf8` | The name of the invoked service. | +| `service_key` | `Utf8` | The key of the virtual object or the workflow ID. Null for regular services. | +| `service_handler` | `Utf8` | The invoked handler. | +| `idempotency_key` | `Utf8` | The user provided idempotency key. | +| `invocation_id` | `Utf8` | [Invocation ID](/operate/invocation#invocation-identifier). | + +## Table: `sys_promise` + +| Column name | Type | Description | +|-------------|------|-------------| +| `partition_key` | `UInt64` | Internal column that is used for partitioning the services invocations. Can be ignored. | +| `service_name` | `Utf8` | The name of the workflow service. | +| `service_key` | `Utf8` | The workflow ID. | +| `key` | `Utf8` | The promise key. | +| `completed` | `Boolean` | True if the promise was completed. | +| `completion_success_value` | `Binary` | The completion success, if any. | +| `completion_success_value_utf8` | `Utf8` | The completion success as UTF-8 string, if any. | +| `completion_failure` | `Utf8` | The completion failure, if any. | + +## Table: `sys_service` + +| Column name | Type | Description | +|-------------|------|-------------| +| `name` | `Utf8` | The name of the registered user service. | +| `revision` | `UInt64` | The latest deployed revision. | +| `public` | `Boolean` | Whether the service is accessible through the ingress endpoint or not. | +| `ty` | `Utf8` | The service type. Either `service` or `virtual_object` or `workflow`. | +| `deployment_id` | `Utf8` | The ID of the latest deployment | + +## Table: `sys_deployment` + +| Column name | Type | Description | +|-------------|------|-------------| +| `id` | `Utf8` | The ID of the service deployment. | +| `ty` | `Utf8` | The type of the endpoint. Either `http` or `lambda`. | +| `endpoint` | `Utf8` | The address of the endpoint. Either HTTP URL or Lambda ARN. | +| `created_at` | `Date64` | Timestamp indicating the deployment registration time. | + +## Table: `sys_invocation` + +| Column name | Type | Description | +|-------------|------|-------------| +| `id` | `Utf8` | [Invocation ID](/operate/invocation#invocation-identifier). | +| `target` | `Utf8` | Invocation Target. Format for plain services: `ServiceName/HandlerName`, e.g. `Greeter/greet`. Format for virtual objects/workflows: `VirtualObjectName/Key/HandlerName`, e.g. `Greeter/Francesco/greet`. | +| `target_service_name` | `Utf8` | The name of the invoked service. | +| `target_service_key` | `Utf8` | The key of the virtual object or the workflow ID. Null for regular services. | +| `target_handler_name` | `Utf8` | The invoked handler. | +| `target_service_ty` | `Utf8` | The service type. Either `service` or `virtual_object` or `workflow`. | +| `invoked_by` | `Utf8` | Either `ingress` if the service was invoked externally or `service` if the service was invoked by another Restate service. | +| `invoked_by_service_name` | `Utf8` | The name of the invoking service. Or `null` if invoked externally. | +| `invoked_by_id` | `Utf8` | The caller [Invocation ID](/operate/invocation#invocation-identifier) if the service was invoked by another Restate service. Or `null` if invoked externally. | +| `invoked_by_target` | `Utf8` | The caller invocation target if the service was invoked by another Restate service. Or `null` if invoked externally. | +| `pinned_deployment_id` | `Utf8` | The ID of the service deployment that started processing this invocation, and will continue to do so (e.g. for retries). This gets set after the first journal entry has been stored for this invocation. | +| `trace_id` | `Utf8` | The ID of the trace that is assigned to this invocation. Only relevant when tracing is enabled. | +| `journal_size` | `UInt32` | The number of journal entries durably logged for this invocation. | +| `created_at` | `Date64` | Timestamp indicating the start of this invocation. | +| `modified_at` | `Date64` | Timestamp indicating the last invocation status transition. For example, last time the status changed from `invoked` to `suspended`. | +| `retry_count` | `UInt64` | The number of invocation attempts since the current leader started executing it. Increments on start, so a value greater than 1 means a failure occurred. Note: the value is not a global attempt counter across invocation suspensions and leadership changes. | +| `last_start_at` | `Date64` | Timestamp indicating the start of the most recent attempt of this invocation. | +| `next_retry_at` | `Date64` | Timestamp indicating the start of the next attempt of this invocation. | +| `last_attempt_deployment_id` | `Utf8` | The ID of the service deployment that executed the most recent attempt of this invocation; this is set before a journal entry is stored, but can change later. | +| `last_attempt_server` | `Utf8` | Server/SDK version, e.g. `restate-sdk-java/1.0.1` | +| `last_failure` | `Utf8` | An error message describing the most recent failed attempt of this invocation, if any. | +| `last_failure_error_code` | `Utf8` | The error code of the most recent failed attempt of this invocation, if any. | +| `last_failure_related_entry_index` | `UInt64` | The index of the journal entry that caused the failure, if any. It may be out-of-bound of the currently stored entries in `sys_journal`. | +| `last_failure_related_entry_name` | `Utf8` | The name of the journal entry that caused the failure, if any. | +| `last_failure_related_entry_type` | `Utf8` | The type of the journal entry that caused the failure, if any. You can check all the available entry types in [`entries.rs`](https://github.com/restatedev/restate/blob/main/crates/types/src/journal/entries.rs). | +| `status` | `Utf8` | Either `pending` or `ready` or `running` or `backing-off` or `suspended` or `completed`. | + diff --git a/docs/references/sql-introspection.mdx b/docs/references/sql-introspection.mdx deleted file mode 100644 index 2fb7860f..00000000 --- a/docs/references/sql-introspection.mdx +++ /dev/null @@ -1,116 +0,0 @@ ---- -sidebar_position: 3 -description: "API reference for inspecting the invocation status and service state." ---- - -# SQL Introspection API - -Restate exposes a set of tables that can be queried via [psql](https://www.postgresql.org/docs/current/app-psql.html). -The schema of these tables is described in the following. - -## Table: `state` - -| Column name | Type | Description | Example | -|-----------------|----------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------|-------------------------------------------------------------------------| -| `partition_key` | `UInt64` | Internal column that is used for partitioning the services invocations. Can be ignored. | | -| `service_name` | `Utf8` | The name of the invoked service. | `Greeter` | -| `service_key` | `Utf8` | The key of the Virtual Object. | `bob` | -| `key` | `Utf8` | The `utf8` state key. | A snippet like `ctx.set(“seen”, 1);` Will produce a value like: `seen` | -| `value` | `Binary` | A binary, uninterpreted representation of the value. You can use the more specific column `value_utf8` if the value is a string. | | -| `value_utf8` | `Utf8` | Only contains meaningful values when a service stores state as `utf8`. This is the case for TypeScript services since the TypeScript SDK serializes values as JSON. | `1` | - -## Table: `sys_invocation` - -| Column name | Type | Description | Example | -|------------------------------------|----------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------|----------------------------------------------| -| `partition_key` | `UInt64` | Internal column that is used for partitioning the services invocations. Can be ignored. | | -| `id` | `Utf8` | [Invocation ID](/operate/invocation#invocation-identifier). | | -| `target` | `Utf8` | Invocation Target. Format for plain services: `ServiceName/HandlerName`. Format for Virtual Objects: `VirtualObjectName/Key/HandlerName`. | `Greeter/greet` or `Greeter/Francesco/greet` | -| `target_service_name` | `Utf8` | The name for the invoked service. | `Greeter` | -| `target_handler_name` | `Utf8` | The invoked handler. | `greet` | -| `target_service_key` | `Utf8` | The key of the Virtual Object. Null for regular services. | `bob` | -| `status` | `Utf8` | Enum value: `pending` or `ready` or `running` or `backing-off` or `suspended` or `completed`. | | -| `invoked_by` | `Utf8` | Enum describing the invoker of this service: `ingress` = invoked externally / `service` = invoked by a service. | | -| `invoked_by_service_name` | `Utf8` | The name of the invoking service. Or `null` if invoked via the ingress. | `Greeter` | -| `invoked_by_id` | `Utf8` | The Invocation ID of the service that triggered this invocation. Or `null` if invoked externally. | | -| `pinned_deployment_id` | `Utf8` | The opaque service deployment ID that has been committed for this invocation; this is set after the first journal entry is stored for this invocation. | | -| `trace_id` | `Utf8` | The ID of the trace that is assigned to this invocation. Only relevant when tracing is enabled. | | -| `journal_size` | `UInt32` | The number of journal entries durably logged for this invocation. | | -| `created_at` | `Date64` | Timestamp indicating the start of this invocation. | | -| `modified_at` | `Date64` | Timestamp indicating the last state transition. For example, last time the status changed from `invoked` to `suspended`. | | -| `retry_count` | `UInt64` | The number of attempts since the last successful attempt of this invocation. Increments on start, so 2 or more means a failure occurred. | | -| `last_start_at` | `Date64` | Timestamp indicating the start of the most recent attempt of this invocation. | | -| `next_retry_at` | `Date64` | Timestamp indicating the start of the next attempt of this invocation. | | -| `last_attempt_deployment_id` | `Utf8` | The opaque service deployment ID that was used in the most recent attempt of this invocation; this will be set before a journal entry is stored, but can change later. | | -| `last_attempt_server` | `Utf8` | Server/SDK version. | `restate-sdk-java/0.8.0` | -| `last_failure` | `Utf8` | An error message describing the most recent failed attempt of this invocation, if any. | | -| `last_failure_error_code` | `Utf8` | The error code of the most recent failed attempt of this invocation, if any. | | -| `last_failure_related_entry_index` | `UInt64` | The index of the journal entry that caused the failure, if any. It may be out-of-bound of the currently stored entries in `sys_journal`. | `3` | -| `last_failure_related_entry_name` | `Utf8` | The name of the journal entry that caused the failure, if any. | `my-side-effect` | -| `last_failure_related_entry_type` | `Utf8` | The type of the journal entry that caused the failure, if any. | `SideEffect` | - -## Table: `sys_journal` - -| Column name | Type | Description | Example | -|-------------------|-----------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|----------------------------------------------| -| `partition_key` | `UInt64` | Internal column that is used for partitioning the services invocations. Can be ignored. | | -| `id` | `Utf8` | [Invocation ID](/operate/invocation#invocation-identifier) | | -| `index` | `UInt32` | The index of this journal entry, where `PollInputStream` will be index 0 | | -| `entry_type` | `Utf8` | Enum value: one of `PollInputStream`, `OutputStream`, `GetState`, `SetState`, `ClearState`, `Sleep`, `Invoke`, `BackgroundInvoke`, `Awakeable`, `CompleteAwakeable`, `Custom`. | | -| `completed` | `Boolean` | Indicates whether this journal entry has been completed; this is only valid for some entry types. | | -| `name` | `Utf8` | The name of the entry supplied by the user, if any. | `my-journaled-action` | -| `invoked_id` | `Utf8` | If this entry represents an outbound invocation, indicates the ID of that invocation. | | -| `invoked_target` | `Utf8` | If this entry represents an outbound invocation, indicates the invocation Target. Format for plain services: `ServiceName/HandlerName`. Format for Virtual Objects: `VirtualObjectName/Key/HandlerName`. | `Greeter/greet` or `Greeter/Francesco/greet` | -| `sleep_wakeup_at` | `Date64` | If this entry represents a sleep, indicates wakeup time. | | -| `raw` | `Binary` | Raw binary representation of the entry. Check the [service protocol](https://github.com/restatedev/service-protocol) for more details to decode it. | | - -## Table: `sys_keyed_service_status` - -| Column name | Type | Description | Example | -|-----------------|----------|-----------------------------------------------------------------------------------------|-----------| -| `partition_key` | `UInt64` | Internal column that is used for partitioning the services invocations. Can be ignored. | | -| `service_name` | `Utf8` | The name for the invoked service. | `Greeter` | -| `service_key` | `Utf8` | The key of the Virtual Object. | `bob` | -| `invocation_id` | `Utf8` | [Invocation ID](/operate/invocation#invocation-identifier) | | - -## Table: `sys_inbox` - -| Column name | Type | Description | Example | -|-------------------|----------|-----------------------------------------------------------------------------------------|-----------| -| `partition_key` | `UInt64` | Internal column that is used for partitioning the services invocations. Can be ignored. | | -| `service_name` | `Utf8` | The name for the invoked service. | `Greeter` | -| `service_key` | `Utf8` | The key of the Virtual Object. | `bob` | -| `id` | `Utf8` | [Invocation ID](/operate/invocation#invocation-identifier). | | -| `sequence_number` | `UInt64` | Internal identified. Can be ignored. | | -| `created_at` | `Date64` | Timestamp indicating the start of this invocation. | | - -## Table: `sys_idempotency` - -| Column name | Type | Description | Example | -|-------------------|----------|-----------------------------------------------------------------------------------------|---------------------| -| `partition_key` | `UInt64` | Internal column that is used for partitioning the services invocations. Can be ignored. | | -| `service_name` | `Utf8` | The name for the invoked service. | `Greeter` | -| `service_key` | `Utf8` | The key of the Virtual Object. Null for regular services. | `bob` | -| `service_handler` | `Utf8` | The invoked handler. | `greet` | -| `idempotency_key` | `Utf8` | The user provided idempotency key. | `my-idemptency-key` | -| `invocation_id` | `Utf8` | [Invocation ID](/operate/invocation#invocation-identifier) | | - - -## Table: `sys_service` - -| Column name | Type | Description | Example | -|-----------------|-----------|-----------------------------------------------------------------------|-----------| -| `name` | `Utf8` | The name of the registered user service | `Greeter` | -| `revision` | `UInt64` | The latest deployed revision | `5` | -| `public` | `Boolean` | Whether the service is accessible through the ingress endpoint or not | | -| `ty` | `Utf8` | Identifies if this is a `Service` or a `VirtualObject` | `Service` | -| `deployment_id` | `Utf8` | The ID of the latest deployment | | - -## Table: `sys_deployment` - -| Column name | Type | Description | Example | -|-----------------|-------------|-----------------------------------------------------------------------|------------------------| -| `id` | `Utf8` | The ID of the service deployment | | -| `ty` | `Utf8` | The type of the endpoint, this can be `http` or `lambda` | `http` | -| `endpoint` | `Utf8` | The address of the endpoint (http URL or Lambda ARN) | | -| `created_at` | `Date64` | Timestamp indicating the deployment registration time. | | diff --git a/tools/generate.sh b/tools/generate.sh index d9ab1ba6..70d3b9de 100755 --- a/tools/generate.sh +++ b/tools/generate.sh @@ -25,5 +25,8 @@ cargo xtask generate-config-schema > $DOCS_DIR/static/schemas/config_schema.json echo "Generate default config" cargo xtask generate-default-config > $DOCS_DIR/static/schemas/restate.toml +echo "Generate sql introspection page" +$SCRIPT_DIR/generate_sql_introspection_page.sh $RESTATE_PATH + popd diff --git a/tools/generate_sql_introspection_page.sh b/tools/generate_sql_introspection_page.sh new file mode 100755 index 00000000..d706529c --- /dev/null +++ b/tools/generate_sql_introspection_page.sh @@ -0,0 +1,24 @@ +#!/usr/bin/env bash +shopt -s nullglob + +if [ -z "$1" ] + then + echo "You must provide the restate project path" +fi + +RESTATE_PATH=$1 +SCRIPT_DIR=$( cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && pwd ) +OUT_FILE=$SCRIPT_DIR/../docs/references/sql-introspection.md + +cat > $OUT_FILE << EOF +--- +sidebar_position: 3 +description: "API reference for inspecting the invocation status and service state." +--- +EOF + +pushd $RESTATE_PATH + +cargo xtask generate-table-docs >> $OUT_FILE + +popd From d516772c3c43b36e18a1fffa703370842f25220f Mon Sep 17 00:00:00 2001 From: Giselle van Dongen Date: Mon, 17 Jun 2024 12:46:32 +0200 Subject: [PATCH 02/18] Fix code loader to allow #tag?number in the links. --- src/plugins/code-loader.js | 32 ++++++++++++++++++-------------- 1 file changed, 18 insertions(+), 14 deletions(-) diff --git a/src/plugins/code-loader.js b/src/plugins/code-loader.js index f0398f65..fa440e04 100644 --- a/src/plugins/code-loader.js +++ b/src/plugins/code-loader.js @@ -2,12 +2,12 @@ const fs = require('fs'); const fetch = require('node-fetch'); const plugin = (options) => { - const codeLoadRegex = /^CODE_LOAD::([^#?]+)(?:#([^#]+)-([^#]+))?(?:\?([^#]+))?$/g; + const codeLoadRegex = /^CODE_LOAD::([^#?]+)(?:#([^#]+))?(?:\?([^#]+))?$/g; const injectCode = async (str) => { let fileData = null; - str.replace(codeLoadRegex, (match, filePath, customStartTag, customEndTag, markNumber) => { - fileData = { filePath, customStartTag, customEndTag, markNumber }; + str.replace(codeLoadRegex, (match, filePath, customTag, markNumber) => { + fileData = { filePath, customTag, markNumber }; return match; }); @@ -16,7 +16,7 @@ const plugin = (options) => { } const fileContent = await readFileOrFetch(fileData.filePath); - const data = extractAndClean(fileContent, fileData.customStartTag, fileData.customEndTag, fileData.markNumber, fileData.filePath); + const data = extractAndClean(fileContent, fileData.customTag, fileData.markNumber, fileData.filePath); return str.replace(codeLoadRegex, () => data); }; @@ -32,18 +32,24 @@ const plugin = (options) => { } } - function extractAndClean(fileContent, customStartTag, customEndTag, markNumber, filePath) { - if (customStartTag && !fileContent.includes(customStartTag)) { - throw new Error(`Custom start tag "${customStartTag}" not found in file ${filePath}`); + function extractAndClean(fileContent, customTag, markNumber, filePath) { + const startTag = (customTag) ? `` : ""; + const endTag = (customTag) ? `` : ""; + if (customTag && !fileContent.includes(startTag)) { + throw new Error(`Custom start tag "${startTag}" not found in file ${filePath}`); } - if (customEndTag && !fileContent.includes(customEndTag)) { - throw new Error(`Custom end tag "${customEndTag}" not found in file ${filePath}`); + if (customTag && !fileContent.includes(endTag)) { + throw new Error(`Custom end tag "${endTag}" not found in file ${filePath}`); } - const startTag = customStartTag ?? ""; - const endTag = customEndTag ?? ""; + let lines; + // Only remove the tag lines if there are tags present + if(fileContent.includes(startTag) && fileContent.includes(endTag)){ + lines = fileContent.split(startTag).pop().split(endTag).shift().split('\n').slice(1,-1); + } else { + lines = fileContent.split('\n'); + } - let lines = fileContent.split(startTag).pop().split(endTag).shift().split('\n').slice(1,-1); let finalLines = []; if (markNumber) { @@ -72,8 +78,6 @@ const plugin = (options) => { finalLines.push(line); } }); - - console.log(finalLines) } else { finalLines = lines; } From 30ecf5d443b4c6b2205b7312003ebf69fab11d9f Mon Sep 17 00:00:00 2001 From: Giselle van Dongen Date: Mon, 17 Jun 2024 12:46:51 +0200 Subject: [PATCH 03/18] Fix all code snippets to use new code loader syntax --- docs/concepts/invocations.mdx | 24 +++---- docs/concepts/services.mdx | 8 +-- docs/develop/java/awakeables.mdx | 12 ++-- docs/develop/java/clients.mdx | 20 +++--- docs/develop/java/durable-timers.mdx | 4 +- docs/develop/java/journaling-results.mdx | 20 +++--- docs/develop/java/serialization.mdx | 12 ++-- docs/develop/java/service-communication.mdx | 22 +++---- docs/develop/java/state.mdx | 20 +++--- docs/develop/java/workflows.mdx | 12 ++-- docs/develop/ts/awakeables.mdx | 6 +- docs/develop/ts/clients.mdx | 10 +-- docs/develop/ts/journaling-results.mdx | 8 +-- docs/develop/ts/service-communication.mdx | 19 +++--- docs/develop/ts/serving.mdx | 6 +- docs/develop/ts/state.mdx | 13 ++-- docs/develop/ts/workflows.mdx | 6 +- docs/get_started/tour.mdx | 72 ++++++++++----------- docs/invoke/kafka.mdx | 4 +- 19 files changed, 150 insertions(+), 148 deletions(-) diff --git a/docs/concepts/invocations.mdx b/docs/concepts/invocations.mdx index 0040a752..9056ddb5 100644 --- a/docs/concepts/invocations.mdx +++ b/docs/concepts/invocations.mdx @@ -68,11 +68,11 @@ Invocations get a unique identifier. This identifier is used to track the progre ```typescript restate_service.ts - CODE_LOAD::ts/src/concepts/invocations/rpc.ts#- + CODE_LOAD::ts/src/concepts/invocations/rpc.ts#rpc_call ``` ```typescript plain_node_app.ts - CODE_LOAD::ts/src/concepts/invocations/ingress/rpc.ts#- + CODE_LOAD::ts/src/concepts/invocations/ingress/rpc.ts#rpc_call_node ``` @@ -85,11 +85,11 @@ Invocations get a unique identifier. This identifier is used to track the progre ```typescript restate_service.ts - CODE_LOAD::ts/src/concepts/invocations/one_way.ts#- + CODE_LOAD::ts/src/concepts/invocations/one_way.ts#one_way_call ``` ```typescript plain_node_app.ts - CODE_LOAD::ts/src/concepts/invocations/ingress/one_way.ts#- + CODE_LOAD::ts/src/concepts/invocations/ingress/one_way.ts#one_way_call_node ``` @@ -101,11 +101,11 @@ Invocations get a unique identifier. This identifier is used to track the progre ```typescript restate_service.ts - CODE_LOAD::ts/src/concepts/invocations/delayed.ts#- + CODE_LOAD::ts/src/concepts/invocations/delayed.ts#delayed_call ``` ```typescript plain_node_app.ts - CODE_LOAD::ts/src/concepts/invocations/ingress/delayed.ts#- + CODE_LOAD::ts/src/concepts/invocations/ingress/delayed.ts#delayed_call_node ``` @@ -121,11 +121,11 @@ Invocations get a unique identifier. This identifier is used to track the progre ```java RestateService.java - CODE_LOAD::java/src/main/java/concepts/invocations/RpcCalls.java#- + CODE_LOAD::java/src/main/java/concepts/invocations/RpcCalls.java#rpc ``` ```java PlainJavaService.java - CODE_LOAD::java/src/main/java/concepts/invocations/RpcCalls.java#- + CODE_LOAD::java/src/main/java/concepts/invocations/RpcCalls.java#rpc_java ``` @@ -138,11 +138,11 @@ Invocations get a unique identifier. This identifier is used to track the progre ```java RestateService.java - CODE_LOAD::java/src/main/java/concepts/invocations/OneWayCalls.java#- + CODE_LOAD::java/src/main/java/concepts/invocations/OneWayCalls.java#one_way_call ``` ```java PlainJavaService.java - CODE_LOAD::java/src/main/java/concepts/invocations/OneWayCalls.java#- + CODE_LOAD::java/src/main/java/concepts/invocations/OneWayCalls.java#one_way_call_java ``` @@ -155,11 +155,11 @@ Invocations get a unique identifier. This identifier is used to track the progre ```java RestateService.java - CODE_LOAD::java/src/main/java/concepts/invocations/DelayedCalls.java#- + CODE_LOAD::java/src/main/java/concepts/invocations/DelayedCalls.java#delayed_call ``` ```java PlainJavaService.java - CODE_LOAD::java/src/main/java/concepts/invocations/DelayedCalls.java#- + CODE_LOAD::java/src/main/java/concepts/invocations/DelayedCalls.java#delayed_call_java ``` diff --git a/docs/concepts/services.mdx b/docs/concepts/services.mdx index dd58502e..4dda09d2 100644 --- a/docs/concepts/services.mdx +++ b/docs/concepts/services.mdx @@ -44,7 +44,7 @@ Services expose a collection of handlers: Restate takes care of retries and recovers the handler to the point where it failed. ```typescript mark=10,16 - CODE_LOAD::ts/src/concepts/services.ts#- + CODE_LOAD::ts/src/concepts/services.ts#service ``` --- @@ -120,7 +120,7 @@ Virtual objects expose a set of handlers with access to K/V state stored in Rest A virtual object is **uniquely identified and accessed by its key**. ```typescript mark=4,6,7,11,12,14,20,21,22 - CODE_LOAD::ts/src/concepts/virtual_objects.ts#- + CODE_LOAD::ts/src/concepts/virtual_objects.ts#virtual_object ``` --- @@ -187,7 +187,7 @@ They have some added capabilities such as signaling, querying, additional invoca The `run` handler runs exactly once per workflow ID (object). ```typescript mark=4:25 - CODE_LOAD::ts/src/concepts/workflow.ts#- + CODE_LOAD::ts/src/concepts/workflow.ts#workflow ``` --- @@ -215,7 +215,7 @@ They have some added capabilities such as signaling, querying, additional invoca The `run` handler runs exactly once per workflow ID (object). ```java mark=8:28 - CODE_LOAD::java/src/main/java/concepts/services/Payment.java#- + CODE_LOAD::java/src/main/java/concepts/services/Payment.java#workflow ``` --- diff --git a/docs/develop/java/awakeables.mdx b/docs/develop/java/awakeables.mdx index 3079436d..722dc27d 100644 --- a/docs/develop/java/awakeables.mdx +++ b/docs/develop/java/awakeables.mdx @@ -18,7 +18,7 @@ This pattern is also known as the callback (task token) pattern. ```java MyHandler.java - CODE_LOAD::java/src/main/java/develop/Awakeables.java#- + CODE_LOAD::java/src/main/java/develop/Awakeables.java#create ``` @@ -43,11 +43,11 @@ This pattern is also known as the callback (task token) pattern. ``` ```java ExternalProcessResolve.java - CODE_LOAD::java/src/main/java/develop/Awakeables.java#- + CODE_LOAD::java/src/main/java/develop/Awakeables.java#resolve ``` ```java ExternalProcessReject.java - CODE_LOAD::java/src/main/java/develop/Awakeables.java#- + CODE_LOAD::java/src/main/java/develop/Awakeables.java#reject ``` - **Resolves** the awakeable @@ -66,7 +66,7 @@ This pattern is also known as the callback (task token) pattern. ```kotlin MyHandler.kt - CODE_LOAD::kotlin/src/main/kotlin/develop/Awakeables.kt#- + CODE_LOAD::kotlin/src/main/kotlin/develop/Awakeables.kt#create ``` @@ -92,11 +92,11 @@ This pattern is also known as the callback (task token) pattern. ``` ```kotlin ExternalProcessResolve.kt - CODE_LOAD::kotlin/src/main/kotlin/develop/Awakeables.kt#- + CODE_LOAD::kotlin/src/main/kotlin/develop/Awakeables.kt#resolve ``` ```kotlin ExternalProcessReject.kt - CODE_LOAD::kotlin/src/main/kotlin/develop/Awakeables.kt#- + CODE_LOAD::kotlin/src/main/kotlin/develop/Awakeables.kt#reject ``` - **Resolving** the awakeable diff --git a/docs/develop/java/clients.mdx b/docs/develop/java/clients.mdx index 9a71b74b..0043b6c5 100644 --- a/docs/develop/java/clients.mdx +++ b/docs/develop/java/clients.mdx @@ -35,7 +35,7 @@ Invoke a handler programmatically with the SDK clients as follows: **Request-response invocations** allow you to wait on a response from the handler. ```java - CODE_LOAD::java/src/main/java/develop/clients/Ingress.java#- + CODE_LOAD::java/src/main/java/develop/clients/Ingress.java#rpc_java ``` @@ -44,7 +44,7 @@ Invoke a handler programmatically with the SDK clients as follows: **One-way invocations** allow you to send a message without waiting for a response. ```java - CODE_LOAD::java/src/main/java/develop/clients/Ingress.java#- + CODE_LOAD::java/src/main/java/develop/clients/Ingress.java#one_way_call_java ``` @@ -53,7 +53,7 @@ Invoke a handler programmatically with the SDK clients as follows: **Delayed invocations** allow you to schedule an invocation for a later point in time. ```java - CODE_LOAD::java/src/main/java/develop/clients/Ingress.java#- + CODE_LOAD::java/src/main/java/develop/clients/Ingress.java#delayed_call_java ``` @@ -64,7 +64,7 @@ Invoke a handler programmatically with the SDK clients as follows: **Request-response invocations** allow you to wait on a response from the handler. ```kotlin - CODE_LOAD::kotlin/src/main/kotlin/develop/clients/Ingress.kt#- + CODE_LOAD::kotlin/src/main/kotlin/develop/clients/Ingress.kt#rpc_kotlin ``` @@ -73,7 +73,7 @@ Invoke a handler programmatically with the SDK clients as follows: **One-way invocations** allow you to send a message without waiting for a response. ```kotlin - CODE_LOAD::kotlin/src/main/kotlin/develop/clients/Ingress.kt#- + CODE_LOAD::kotlin/src/main/kotlin/develop/clients/Ingress.kt#one_way_call_kotlin ``` @@ -82,7 +82,7 @@ Invoke a handler programmatically with the SDK clients as follows: **Delayed invocations** allow you to schedule an invocation for a later point in time. ```kotlin - CODE_LOAD::kotlin/src/main/kotlin/develop/clients/Ingress.kt#- + CODE_LOAD::kotlin/src/main/kotlin/develop/clients/Ingress.kt#delayed_call_kotlin ``` @@ -102,12 +102,12 @@ Add the idempotency key [to the header](https://datatracker.ietf.org/doc/draft-i ```java - CODE_LOAD::java/src/main/java/develop/clients/Ingress.java#- + CODE_LOAD::java/src/main/java/develop/clients/Ingress.java#service_idempotent ``` ```kotlin - CODE_LOAD::kotlin/src/main/kotlin/develop/clients/Ingress.kt#- + CODE_LOAD::kotlin/src/main/kotlin/develop/clients/Ingress.kt#service_idempotent ``` @@ -151,12 +151,12 @@ You can use the invocation ID to attach or peek at a service/object invocation t ```java - CODE_LOAD::java/src/main/java/develop/clients/Ingress.java#- + CODE_LOAD::java/src/main/java/develop/clients/Ingress.java#service_attach ``` ```kotlin - CODE_LOAD::kotlin/src/main/kotlin/develop/clients/Ingress.kt#- + CODE_LOAD::kotlin/src/main/kotlin/develop/clients/Ingress.kt#service_attach ``` diff --git a/docs/develop/java/durable-timers.mdx b/docs/develop/java/durable-timers.mdx index e509d71a..bc397dab 100644 --- a/docs/develop/java/durable-timers.mdx +++ b/docs/develop/java/durable-timers.mdx @@ -26,14 +26,14 @@ To sleep in a Restate application for ten seconds, do the following: ```java -CODE_LOAD::java/src/main/java/develop/DurableTimers.java#- +CODE_LOAD::java/src/main/java/develop/DurableTimers.java#sleep ``` ```kotlin -CODE_LOAD::kotlin/src/main/kotlin/develop/DurableTimers.kt#- +CODE_LOAD::kotlin/src/main/kotlin/develop/DurableTimers.kt#sleep ``` diff --git a/docs/develop/java/journaling-results.mdx b/docs/develop/java/journaling-results.mdx index 0020d7b4..056364b0 100644 --- a/docs/develop/java/journaling-results.mdx +++ b/docs/develop/java/journaling-results.mdx @@ -28,7 +28,7 @@ Here is an example of a database request for which the string response is stored ```java -CODE_LOAD::java/src/main/java/develop/SideEffects.java#- +CODE_LOAD::java/src/main/java/develop/SideEffects.java#side_effect ``` You can use Restate's built-in `CoreSerdes` to serialize primitive types. @@ -38,7 +38,7 @@ Have a look at the [serialization docs](/develop/java/serialization) for other o ```kotlin -CODE_LOAD::kotlin/src/main/kotlin/develop/SideEffects.kt#- +CODE_LOAD::kotlin/src/main/kotlin/develop/SideEffects.kt#side_effect ``` By default, Kotlin serialization is used to serialize the result. @@ -67,7 +67,7 @@ Restate then logs the order in which they are resolved or rejected, to make them The semantics are similar to [`CompleteableFuture.allOf()`](https://docs.oracle.com/en/java/javase/17/docs/api/java.base/java/util/concurrent/CompletableFuture.html#allOf(java.util.concurrent.CompletableFuture...)), but the outcome is stored in the Restate journal to be deterministically replayable. ```java - CODE_LOAD::java/src/main/java/develop/SideEffects.java#- + CODE_LOAD::java/src/main/java/develop/SideEffects.java#combine_all ``` @@ -75,7 +75,7 @@ Restate then logs the order in which they are resolved or rejected, to make them The semantics are similar to [`CompleteableFuture.anyOf()`](https://docs.oracle.com/en/java/javase/17/docs/api/java.base/java/util/concurrent/CompletableFuture.html#anyOf(java.util.concurrent.CompletableFuture...)), but the outcome is stored in the Restate journal to be deterministically replayable. ```java - CODE_LOAD::java/src/main/java/develop/SideEffects.java#- + CODE_LOAD::java/src/main/java/develop/SideEffects.java#combine_any ``` @@ -88,7 +88,7 @@ Restate then logs the order in which they are resolved or rejected, to make them ```kotlin - CODE_LOAD::kotlin/src/main/kotlin/develop/SideEffects.kt#- + CODE_LOAD::kotlin/src/main/kotlin/develop/SideEffects.kt#combine_all ``` @@ -99,7 +99,7 @@ Restate then logs the order in which they are resolved or rejected, to make them The semantics are similar to [`CompleteableFuture.anyOf()`](https://docs.oracle.com/en/java/javase/17/docs/api/java.base/java/util/concurrent/CompletableFuture.html#anyOf(java.util.concurrent.CompletableFuture...)), but the outcome is stored in the Restate journal to be deterministically replayable. ```kotlin - CODE_LOAD::kotlin/src/main/kotlin/develop/SideEffects.kt#- + CODE_LOAD::kotlin/src/main/kotlin/develop/SideEffects.kt#combine_any ``` @@ -122,7 +122,7 @@ Restate seeds the random number generator with the invocation ID, so it always r Do not use this in cryptographic contexts. ```java - CODE_LOAD::java/src/main/java/develop/SideEffects.java#- + CODE_LOAD::java/src/main/java/develop/SideEffects.java#uuid ``` @@ -136,7 +136,7 @@ Restate seeds the random number generator with the invocation ID, so it always r You can use any of the methods of [`java.util.Random`](https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/util/Random.html) to generate random numbers: for example, `nextBoolean()`, `nextLong()`, `nextFloat()`, etc. ```java - CODE_LOAD::java/src/main/java/develop/SideEffects.java#- + CODE_LOAD::java/src/main/java/develop/SideEffects.java#random_nb ``` @@ -152,7 +152,7 @@ Restate seeds the random number generator with the invocation ID, so it always r Do not use this in cryptographic contexts. ```kotlin - CODE_LOAD::kotlin/src/main/kotlin/develop/SideEffects.kt#- + CODE_LOAD::kotlin/src/main/kotlin/develop/SideEffects.kt#uuid ``` @@ -166,7 +166,7 @@ Restate seeds the random number generator with the invocation ID, so it always r You can use any of the methods of [`java.util.Random`](https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/util/Random.html) to generate random numbers: for example, `nextBoolean()`, `nextLong()`, `nextFloat()`, etc. ```kotlin - CODE_LOAD::kotlin/src/main/kotlin/develop/SideEffects.kt#- + CODE_LOAD::kotlin/src/main/kotlin/develop/SideEffects.kt#random_nb ``` diff --git a/docs/develop/java/serialization.mdx b/docs/develop/java/serialization.mdx index f7dfbd51..e6d5f4e0 100644 --- a/docs/develop/java/serialization.mdx +++ b/docs/develop/java/serialization.mdx @@ -23,13 +23,13 @@ implementation("dev.restate:sdk-serde-jackson:VAR::JAVA_SDK_VERSION") And then use `JacksonSerdes`. For example, to create a Serde for the `Person` class, you can do: ```java -CODE_LOAD::java/src/main/java/develop/SerializationExample.java#- +CODE_LOAD::java/src/main/java/develop/SerializationExample.java#pojoserdes ``` Alternatively, you can also supply the TypeReference of a generic type, for example: ```java -CODE_LOAD::java/src/main/java/develop/SerializationExample.java#- +CODE_LOAD::java/src/main/java/develop/SerializationExample.java#typerefserdes ``` ## Kotlin serialization @@ -52,7 +52,7 @@ CODE_LOAD::java/src/main/java/develop/SerializationExample.java For example, to specify the serializer for state of type `Long`: ```java -CODE_LOAD::java/src/main/java/develop/SerializationExample.java#- +CODE_LOAD::java/src/main/java/develop/SerializationExample.java#statekey ``` ## Protobuf messages @@ -67,7 +67,7 @@ implementation("dev.restate:sdk-serde-protobuf:VAR::JAVA_SDK_VERSION") And then use `ProtobufParser`: ```java -CODE_LOAD::java/src/main/java/develop/SerializationExample.java#- +CODE_LOAD::java/src/main/java/develop/SerializationExample.java#protoserdes ``` ## Custom serialization @@ -75,13 +75,13 @@ CODE_LOAD::java/src/main/java/develop/SerializationExample.java#- +CODE_LOAD::java/src/main/java/develop/SerializationExample.java#customserdes ``` And then use it, for example, to create a state key for Person objects: ```java -CODE_LOAD::java/src/main/java/develop/SerializationExample.java#- +CODE_LOAD::java/src/main/java/develop/SerializationExample.java#person_state_key ``` diff --git a/docs/develop/java/service-communication.mdx b/docs/develop/java/service-communication.mdx index a2558429..7ff9b27c 100644 --- a/docs/develop/java/service-communication.mdx +++ b/docs/develop/java/service-communication.mdx @@ -38,21 +38,21 @@ Use the generated client's of the SDK to do the request-response call: To a Service: ```java - CODE_LOAD::java/src/main/java/develop/ServiceCommunication.java#- + CODE_LOAD::java/src/main/java/develop/ServiceCommunication.java#request_response_service ``` To a Virtual Object: ```java - CODE_LOAD::java/src/main/java/develop/ServiceCommunication.java#- + CODE_LOAD::java/src/main/java/develop/ServiceCommunication.java#request_response_virtual_object ``` To a Workflow: ```java - CODE_LOAD::java/src/main/java/develop/ServiceCommunication.java#- + CODE_LOAD::java/src/main/java/develop/ServiceCommunication.java#request_response_workflow ``` @@ -61,14 +61,14 @@ Use the generated client's of the SDK to do the request-response call: To a Service: ```kotlin - CODE_LOAD::kotlin/src/main/kotlin/develop/ServiceCommunication.kt#- + CODE_LOAD::kotlin/src/main/kotlin/develop/ServiceCommunication.kt#request_response_service ``` To a Virtual Object: ```kotlin - CODE_LOAD::kotlin/src/main/kotlin/develop/ServiceCommunication.kt#- + CODE_LOAD::kotlin/src/main/kotlin/develop/ServiceCommunication.kt#request_response_virtual_object ``` @@ -76,7 +76,7 @@ Use the generated client's of the SDK to do the request-response call: To a Workflow: ```kotlin CallWorkflow.kt - CODE_LOAD::kotlin/src/main/kotlin/develop/ServiceCommunication.kt#- + CODE_LOAD::kotlin/src/main/kotlin/develop/ServiceCommunication.kt#request_response_workflow ``` @@ -109,14 +109,14 @@ Use the client's `.send()` method to do this as follows: ```java -CODE_LOAD::java/src/main/java/develop/ServiceCommunication.java#- +CODE_LOAD::java/src/main/java/develop/ServiceCommunication.java#one_way ``` ```kotlin -CODE_LOAD::kotlin/src/main/kotlin/develop/ServiceCommunication.kt#- +CODE_LOAD::kotlin/src/main/kotlin/develop/ServiceCommunication.kt#one_way ``` @@ -136,14 +136,14 @@ Use Restate's generated clients `.send(Duration)` method to send a delayed reque ```java -CODE_LOAD::java/src/main/java/develop/ServiceCommunication.java#- +CODE_LOAD::java/src/main/java/develop/ServiceCommunication.java#delayed ``` ```kotlin -CODE_LOAD::kotlin/src/main/kotlin/develop/ServiceCommunication.kt#- +CODE_LOAD::kotlin/src/main/kotlin/develop/ServiceCommunication.kt#delayed ``` @@ -163,7 +163,7 @@ Restate will make sure the task gets executed at the desired time. ```java - CODE_LOAD::java/src/main/java/develop/ServiceCommunication.java#- + CODE_LOAD::java/src/main/java/develop/ServiceCommunication.java#ordering ``` diff --git a/docs/develop/java/state.mdx b/docs/develop/java/state.mdx index ac3d1baf..a478dbfe 100644 --- a/docs/develop/java/state.mdx +++ b/docs/develop/java/state.mdx @@ -36,14 +36,14 @@ For a single Virtual Object, you can list all the state keys that have entries i ```java -CODE_LOAD::java/src/main/java/develop/State.java#- +CODE_LOAD::java/src/main/java/develop/State.java#statekeys ``` ```kotlin -CODE_LOAD::kotlin/src/main/kotlin/develop/State.kt#- +CODE_LOAD::kotlin/src/main/kotlin/develop/State.kt#statekeys ``` @@ -56,7 +56,7 @@ CODE_LOAD::kotlin/src/main/kotlin/develop/State.kt#- ```java - CODE_LOAD::java/src/main/java/develop/State.java#- + CODE_LOAD::java/src/main/java/develop/State.java#get ``` 1. [Define the state key](focus://2,6) (key name and [(de)serializer](/develop/java/serialization)) at the top of the Virtual Object class. @@ -67,7 +67,7 @@ CODE_LOAD::kotlin/src/main/kotlin/develop/State.kt#- ```kotlin - CODE_LOAD::kotlin/src/main/kotlin/develop/State.kt#- + CODE_LOAD::kotlin/src/main/kotlin/develop/State.kt#get ``` 1. [Define the state key](focus://2,6) (key name and [(de)serializer](/develop/java/serialization)) at the top of the Virtual Object class. 2. [Use `ctx.get` to retrieve the state for a specific key.](focus://3,7) @@ -82,7 +82,7 @@ CODE_LOAD::kotlin/src/main/kotlin/develop/State.kt#- ```java -CODE_LOAD::java/src/main/java/develop/State.java#- +CODE_LOAD::java/src/main/java/develop/State.java#set ``` 1. [Define the state key](focus://1) (key name and [(de)serializer](/develop/java/serialization)) at the top of the Virtual Object class. 2. [Use `ctx.set` to set the state for a specific key.](focus://2) The type of value needs to line up with the type that was defined in the StateKey. @@ -92,7 +92,7 @@ CODE_LOAD::java/src/main/java/develop/State.java#- ```kotlin -CODE_LOAD::kotlin/src/main/kotlin/develop/State.kt#- +CODE_LOAD::kotlin/src/main/kotlin/develop/State.kt#set ``` 1. [Define the state key](focus://1) (key name and [(de)serializer](/develop/java/serialization)) at the top of the Virtual Object class. 2. [Use `ctx.set` to set the state for a specific key.](focus://2) The type of value needs to line up with the type that was defined in the StateKey. @@ -108,7 +108,7 @@ CODE_LOAD::kotlin/src/main/kotlin/develop/State.kt#- ```java -CODE_LOAD::java/src/main/java/develop/State.java#- +CODE_LOAD::java/src/main/java/develop/State.java#clear ``` 1. [Define the state key](focus://1) (key name and [(de)serializer](/develop/java/serialization)) at the top of the Virtual Object class. 2. [Use `ctx.clear` to delete the state for a specific key.](focus://2) @@ -118,7 +118,7 @@ CODE_LOAD::java/src/main/java/develop/State.java#- ```kotlin -CODE_LOAD::kotlin/src/main/kotlin/develop/State.kt#- +CODE_LOAD::kotlin/src/main/kotlin/develop/State.kt#clear ``` 1. [Define the state key](focus://1) (key name and [(de)serializer](/develop/java/serialization)) at the top of the Virtual Object class. 2. [Use `ctx.clear` to delete the state for a specific key.](focus://2) @@ -132,14 +132,14 @@ CODE_LOAD::kotlin/src/main/kotlin/develop/State.kt#- ```java -CODE_LOAD::java/src/main/java/develop/State.java#- +CODE_LOAD::java/src/main/java/develop/State.java#clear_all ``` ```kotlin -CODE_LOAD::kotlin/src/main/kotlin/develop/State.kt#- +CODE_LOAD::kotlin/src/main/kotlin/develop/State.kt#clear_all ``` diff --git a/docs/develop/java/workflows.mdx b/docs/develop/java/workflows.mdx index 58237c0d..3b58e3e3 100644 --- a/docs/develop/java/workflows.mdx +++ b/docs/develop/java/workflows.mdx @@ -145,7 +145,7 @@ Have a look at the code example to get a better understanding of how workflows a You can only submit once per workflow ID (here `"someone"`). ```java - CODE_LOAD::java/src/main/java/develop/workflows/WorkflowSubmitter.java#- + CODE_LOAD::java/src/main/java/develop/workflows/WorkflowSubmitter.java#submit ``` @@ -155,7 +155,7 @@ Have a look at the code example to get a better understanding of how workflows a Use `send()` for one-way calls. ```java - CODE_LOAD::java/src/main/java/develop/workflows/WorkflowSubmitter.java#- + CODE_LOAD::java/src/main/java/develop/workflows/WorkflowSubmitter.java#query ``` @@ -165,7 +165,7 @@ Have a look at the code example to get a better understanding of how workflows a This lets you attach to a workflow and wait for it to finish, or to peek whether the result is ready. ```java - CODE_LOAD::java/src/main/java/develop/workflows/WorkflowSubmitter.java#- + CODE_LOAD::java/src/main/java/develop/workflows/WorkflowSubmitter.java#interact ``` @@ -176,7 +176,7 @@ Have a look at the code example to get a better understanding of how workflows a You can only submit once per workflow ID (here `"someone"`). ```kotlin - CODE_LOAD::kotlin/src/main/kotlin/develop/workflows/WorkflowSubmitter.kt#- + CODE_LOAD::kotlin/src/main/kotlin/develop/workflows/WorkflowSubmitter.kt#submit ``` @@ -186,7 +186,7 @@ Have a look at the code example to get a better understanding of how workflows a Use `send()` for one-way calls. ```kotlin - CODE_LOAD::kotlin/src/main/kotlin/develop/workflows/WorkflowSubmitter.kt#- + CODE_LOAD::kotlin/src/main/kotlin/develop/workflows/WorkflowSubmitter.kt#query ``` @@ -196,7 +196,7 @@ Have a look at the code example to get a better understanding of how workflows a This lets you attach to a workflow and wait for it to finish, or to peek whether the result is ready. ```kotlin - CODE_LOAD::kotlin/src/main/kotlin/develop/workflows/WorkflowSubmitter.kt#- + CODE_LOAD::kotlin/src/main/kotlin/develop/workflows/WorkflowSubmitter.kt#interact ``` diff --git a/docs/develop/ts/awakeables.mdx b/docs/develop/ts/awakeables.mdx index cc926318..149152f5 100644 --- a/docs/develop/ts/awakeables.mdx +++ b/docs/develop/ts/awakeables.mdx @@ -15,7 +15,7 @@ This pattern is also known as the callback (task token) pattern. ```typescript my_handler.ts - CODE_LOAD::ts/src/develop/awakeable.ts#- + CODE_LOAD::ts/src/develop/awakeable.ts#create ``` @@ -40,11 +40,11 @@ This pattern is also known as the callback (task token) pattern. ``` ```typescript external_process_resolve.ts - CODE_LOAD::ts/src/develop/awakeable.ts#- + CODE_LOAD::ts/src/develop/awakeable.ts#resolve ``` ```typescript external_proces_reject.ts - CODE_LOAD::ts/src/develop/awakeable.ts#- + CODE_LOAD::ts/src/develop/awakeable.ts#reject ``` diff --git a/docs/develop/ts/clients.mdx b/docs/develop/ts/clients.mdx index 41b2071e..ff1bf0d4 100644 --- a/docs/develop/ts/clients.mdx +++ b/docs/develop/ts/clients.mdx @@ -31,7 +31,7 @@ Restate then attaches information about the invocation to the parent invocation. **Request-response invocations** allow you to wait on a response from the handler. ```typescript - CODE_LOAD::ts/src/develop/clients/ingress.ts#- + CODE_LOAD::ts/src/develop/clients/ingress.ts#rpc_call_node ``` @@ -40,7 +40,7 @@ Restate then attaches information about the invocation to the parent invocation. **One-way invocations** allow you to send a message without waiting for a response. ```typescript - CODE_LOAD::ts/src/develop/clients/ingress.ts#- + CODE_LOAD::ts/src/develop/clients/ingress.ts#one_way_call_node ``` @@ -49,7 +49,7 @@ Restate then attaches information about the invocation to the parent invocation. **Delayed invocations** allow you to schedule an invocation for a later point in time. ```typescript - CODE_LOAD::ts/src/develop/clients/ingress.ts#- + CODE_LOAD::ts/src/develop/clients/ingress.ts#delayed_call_node ``` @@ -64,7 +64,7 @@ To make a service call idempotent, you can use the idempotency key feature. Add the idempotency key [to the header](https://datatracker.ietf.org/doc/draft-ietf-httpapi-idempotency-key-header/) via: ```typescript -CODE_LOAD::ts/src/develop/clients/ingress.ts#- +CODE_LOAD::ts/src/develop/clients/ingress.ts#service_idempotent ``` After the invocation completes, Restate persists the response for a retention period of one day (24 hours). @@ -101,7 +101,7 @@ You can use the client library to retrieve the results of invocations **with an Attach to them by using the handle that is returned from the invocation: ```typescript -CODE_LOAD::ts/src/develop/clients/ingress.ts#- +CODE_LOAD::ts/src/develop/clients/ingress.ts#service_attach ``` diff --git a/docs/develop/ts/journaling-results.mdx b/docs/develop/ts/journaling-results.mdx index 3c9cc5f4..2cfa97d8 100644 --- a/docs/develop/ts/journaling-results.mdx +++ b/docs/develop/ts/journaling-results.mdx @@ -21,7 +21,7 @@ Restate replays the result instead of re-executing the operation on retries. Here is an example of a database request for which the string response is stored in Restate: ```typescript -CODE_LOAD::ts/src/develop/side_effects.ts#- +CODE_LOAD::ts/src/develop/side_effects.ts#side_effect ``` You cannot invoke any methods on the Restate context within a side effect. @@ -49,7 +49,7 @@ Restate then logs the order in which they are resolved or rejected, to make them - Rejects when any input promise is rejected. ```typescript focus=1:6 mark=5:6 - CODE_LOAD::ts/src/develop/side_effects.ts#- + CODE_LOAD::ts/src/develop/side_effects.ts#promises ``` --- @@ -97,7 +97,7 @@ The SDK provides helper functions for the deterministic generation of UUIDs and Do not use this in cryptographic contexts. ```typescript - CODE_LOAD::ts/src/develop/side_effects.ts#- + CODE_LOAD::ts/src/develop/side_effects.ts#uuid ``` @@ -110,6 +110,6 @@ The SDK provides helper functions for the deterministic generation of UUIDs and This is the equivalent of JS `Math.random()` but deterministically replayable. ```typescript - CODE_LOAD::ts/src/develop/side_effects.ts#- + CODE_LOAD::ts/src/develop/side_effects.ts#random_nb ``` diff --git a/docs/develop/ts/service-communication.mdx b/docs/develop/ts/service-communication.mdx index 98be3a06..220eb162 100644 --- a/docs/develop/ts/service-communication.mdx +++ b/docs/develop/ts/service-communication.mdx @@ -17,7 +17,6 @@ Make sure you export the service definition of the service you want to call, as ```typescript focus=12 CODE_LOAD::ts/src/develop/my_service.ts ``` - Import this service definition in the service handler that wants to call it. @@ -32,7 +31,7 @@ Request-response calls are requests where the client waits for the response. To a Service: ```ts - CODE_LOAD::ts/src/develop/service_communication.ts#- + CODE_LOAD::ts/src/develop/service_communication.ts#request_response_service ``` @@ -41,7 +40,7 @@ Request-response calls are requests where the client waits for the response. To a Virtual Object: ```ts - CODE_LOAD::ts/src/develop/service_communication.ts#- + CODE_LOAD::ts/src/develop/service_communication.ts#request_response_object ``` @@ -50,7 +49,7 @@ Request-response calls are requests where the client waits for the response. To a Workflow: ```ts - CODE_LOAD::ts/src/develop/service_communication.ts#- + CODE_LOAD::ts/src/develop/service_communication.ts#request_response_workflow ``` @@ -89,7 +88,7 @@ Handlers can send messages (a.k.a. one-way calls, or fire-and-forget calls), as To a Service: ```ts - CODE_LOAD::ts/src/develop/service_communication.ts#- + CODE_LOAD::ts/src/develop/service_communication.ts#one_way_service ``` @@ -99,7 +98,7 @@ Handlers can send messages (a.k.a. one-way calls, or fire-and-forget calls), as To a Virtual Object: ```ts - CODE_LOAD::ts/src/develop/service_communication.ts#- + CODE_LOAD::ts/src/develop/service_communication.ts#one_way_object ``` @@ -109,7 +108,7 @@ Handlers can send messages (a.k.a. one-way calls, or fire-and-forget calls), as To a Workflow: ```ts - CODE_LOAD::ts/src/develop/service_communication.ts#- + CODE_LOAD::ts/src/develop/service_communication.ts#one_way_workflow ``` @@ -130,7 +129,7 @@ To schedule a delayed call, send a message with a delay parameter, as follows: To a Service: ```ts - CODE_LOAD::ts/src/develop/service_communication.ts#- + CODE_LOAD::ts/src/develop/service_communication.ts#delayed_service ``` @@ -139,7 +138,7 @@ To schedule a delayed call, send a message with a delay parameter, as follows: To a Virtual Object: ```ts - CODE_LOAD::ts/src/develop/service_communication.ts#- + CODE_LOAD::ts/src/develop/service_communication.ts#delayed_object ``` @@ -157,7 +156,7 @@ To schedule a delayed call, send a message with a delay parameter, as follows: ```typescript - CODE_LOAD::ts/src/develop/service_communication.ts#- + CODE_LOAD::ts/src/develop/service_communication.ts#ordering ``` diff --git a/docs/develop/ts/serving.mdx b/docs/develop/ts/serving.mdx index 121aadf0..9b898fa3 100644 --- a/docs/develop/ts/serving.mdx +++ b/docs/develop/ts/serving.mdx @@ -12,7 +12,7 @@ Restate services can run in two ways: as an HTTP endpoint or as AWS Lambda funct ## Creating an HTTP endpoint ```typescript -CODE_LOAD::ts/src/develop/serving.ts#- +CODE_LOAD::ts/src/develop/serving.ts#endpoint ``` @@ -28,7 +28,7 @@ CODE_LOAD::ts/src/develop/serving.ts#- If you need to manually control or customize the HTTP2 server, you can call `http2Handler()` instead of `listen()`, and then use it to manually instantiate the HTTP server: ```ts - CODE_LOAD::ts/src/develop/serving.ts#- + CODE_LOAD::ts/src/develop/serving.ts#custom_endpoint ``` @@ -36,7 +36,7 @@ CODE_LOAD::ts/src/develop/serving.ts#- To register your service as a Lambda function, change the endpoint into a Lambda handler: ```typescript -CODE_LOAD::ts/src/develop/serving.ts#- +CODE_LOAD::ts/src/develop/serving.ts#lambda ``` Have a look at the [deployment section](/category/aws--aws-lambda) diff --git a/docs/develop/ts/state.mdx b/docs/develop/ts/state.mdx index 5491c8e0..c910ae2c 100644 --- a/docs/develop/ts/state.mdx +++ b/docs/develop/ts/state.mdx @@ -26,28 +26,31 @@ Restate makes sure the state is consistent with the processing of the code execu For a single Virtual Object, you can list all the state keys that have entries in the state store via: ```typescript -CODE_LOAD::ts/src/develop/state.ts#- +CODE_LOAD::ts/src/develop/state.ts#statekeys ``` ### Retrieving state Use `ctx.get` to retrieve the state for [a key](focus://1[40:53],2[40:53]): ```typescript -CODE_LOAD::ts/src/develop/state.ts#- +CODE_LOAD::ts/src/develop/state.ts#get ``` The return value is `null` if no value was stored. + ### Setting state Use `ctx.set` to set a new value for a key: + ```typescript -CODE_LOAD::ts/src/develop/state.ts#- +CODE_LOAD::ts/src/develop/state.ts#set ``` ### Clearing state Use `ctx.clear` to delete the value of a key: + ```typescript -CODE_LOAD::ts/src/develop/state.ts#- +CODE_LOAD::ts/src/develop/state.ts#clear ``` ### Clearing all state @@ -55,5 +58,5 @@ CODE_LOAD::ts/src/develop/state.ts#- Delete all the state stored in Restate for a Virtual Object via: ```typescript -CODE_LOAD::ts/src/develop/state.ts#- +CODE_LOAD::ts/src/develop/state.ts#clear_all ``` diff --git a/docs/develop/ts/workflows.mdx b/docs/develop/ts/workflows.mdx index 8d759ef6..233d673a 100644 --- a/docs/develop/ts/workflows.mdx +++ b/docs/develop/ts/workflows.mdx @@ -86,7 +86,7 @@ Have a look at the code example to get a better understanding of how workflows a You can only submit once per workflow ID (here `"someone"`). ```ts - CODE_LOAD::ts/src/develop/workflows/submit.ts#- + CODE_LOAD::ts/src/develop/workflows/submit.ts#submit ``` @@ -97,7 +97,7 @@ Have a look at the code example to get a better understanding of how workflows a For now, you can only do request-response calls. ```ts - CODE_LOAD::ts/src/develop/workflows/submit.ts#- + CODE_LOAD::ts/src/develop/workflows/submit.ts#query ``` @@ -107,7 +107,7 @@ Have a look at the code example to get a better understanding of how workflows a This lets you retrieve the result of a workflow or check if it's finished. ```ts - CODE_LOAD::ts/src/develop/workflows/submit.ts#- + CODE_LOAD::ts/src/develop/workflows/submit.ts#attach ``` diff --git a/docs/get_started/tour.mdx b/docs/get_started/tour.mdx index 559cfb4c..e349df8c 100644 --- a/docs/get_started/tour.mdx +++ b/docs/get_started/tour.mdx @@ -225,7 +225,7 @@ If you run Restate with Docker, replace `http://localhost:9080` by `http://host. In `src/app` you will find the skeletons of the various services to help you start implementing the app. For example: ```typescript checkout_service.ts - CODE_LOAD::https://raw.githubusercontent.com/restatedev/examples/main/tutorials/tour-of-restate-typescript/src/part1/checkout_service.ts#- + CODE_LOAD::https://raw.githubusercontent.com/restatedev/examples/main/tutorials/tour-of-restate-typescript/src/part1/checkout_service.ts#checkout ``` Restate handlers have the Restate Context supplied as the first argument. @@ -239,7 +239,7 @@ In `src/app` you will find the skeletons of the various services to help you sta In `src/main/java/dev/restate/tour/app` you will find the skeletons of the various services to help you start implementing the app. For example: ```java CheckoutService.java -CODE_LOAD::https://raw.githubusercontent.com/restatedev/examples/main/tutorials/tour-of-restate-java/src/main/java/dev/restate/tour/part1/CheckoutService.java#- +CODE_LOAD::https://raw.githubusercontent.com/restatedev/examples/main/tutorials/tour-of-restate-java/src/main/java/dev/restate/tour/part1/CheckoutService.java#service ``` Services are annotated by `@Service`. A service consists of a set of handlers, and each handler is annotated by `@Handler`. @@ -306,7 +306,7 @@ It does that by calling the `TicketObject/reserve` handler: ```typescript cart_object.ts - CODE_LOAD::https://raw.githubusercontent.com/restatedev/examples/main/tutorials/tour-of-restate-typescript/src/part1/cart_object.ts#- + CODE_LOAD::https://raw.githubusercontent.com/restatedev/examples/main/tutorials/tour-of-restate-typescript/src/part1/cart_object.ts#add_ticket ```
Service logs @@ -339,7 +339,7 @@ It does that by calling the `TicketObject/reserve` handler: ```java CartObject.java - CODE_LOAD::https://raw.githubusercontent.com/restatedev/examples/main/tutorials/tour-of-restate-java/src/main/java/dev/restate/tour/part1/CartObject.java#- + CODE_LOAD::https://raw.githubusercontent.com/restatedev/examples/main/tutorials/tour-of-restate-java/src/main/java/dev/restate/tour/part1/CartObject.java#add_ticket ```
@@ -390,7 +390,7 @@ Let the `expireTicket` handler call the `TicketObject/unreserve` handler. ```typescript cart_object.ts -CODE_LOAD::https://raw.githubusercontent.com/restatedev/examples/main/tutorials/tour-of-restate-typescript/src/part1/cart_object.ts#- +CODE_LOAD::https://raw.githubusercontent.com/restatedev/examples/main/tutorials/tour-of-restate-typescript/src/part1/cart_object.ts#expire_ticket ``` Specify that you want to call the `TicketObject` by supplying `ticketObjectApi` to the `send` function. @@ -421,7 +421,7 @@ curl localhost:8080/CartObject/Mary/expireTicket -H 'content-type: application/j ```java CartObject.java -CODE_LOAD::https://raw.githubusercontent.com/restatedev/examples/main/tutorials/tour-of-restate-java/src/main/java/dev/restate/tour/part1/CartObject.java#- +CODE_LOAD::https://raw.githubusercontent.com/restatedev/examples/main/tutorials/tour-of-restate-java/src/main/java/dev/restate/tour/part1/CartObject.java#expire_ticket ``` You now call `send()` on the generated client to send a message instead of doing a request-response call. You also don't need to await the response, as you don't expect one. @@ -498,7 +498,7 @@ You will fix this later on. Note that the `CheckoutService` is not a Virtual Obj ```typescript cart_object.ts - CODE_LOAD::https://raw.githubusercontent.com/restatedev/examples/main/tutorials/tour-of-restate-typescript/src/part1/cart_object.ts#- + CODE_LOAD::https://raw.githubusercontent.com/restatedev/examples/main/tutorials/tour-of-restate-typescript/src/part1/cart_object.ts#checkout ``` Call `CartObject/checkout` as you did [earlier](#request-response-calls-over-http) and have a look at the logs again to see what happened: @@ -519,7 +519,7 @@ You will fix this later on. Note that the `CheckoutService` is not a Virtual Obj ```java CartObject.java - CODE_LOAD::https://raw.githubusercontent.com/restatedev/examples/main/tutorials/tour-of-restate-java/src/main/java/dev/restate/tour/part1/CartObject.java#- + CODE_LOAD::https://raw.githubusercontent.com/restatedev/examples/main/tutorials/tour-of-restate-java/src/main/java/dev/restate/tour/part1/CartObject.java#checkout ``` Restart the service and call the `CartObject/checkout` handler as you did earlier and have a look at the logs again to see what happened. @@ -572,7 +572,7 @@ To see the recovery of partial progress in practice, let's make the `CartObject/ ```typescript cart_object.ts - CODE_LOAD::https://raw.githubusercontent.com/restatedev/examples/main/tutorials/tour-of-restate-typescript/src/part1/cart_object.ts#- + CODE_LOAD::https://raw.githubusercontent.com/restatedev/examples/main/tutorials/tour-of-restate-typescript/src/part1/cart_object.ts#add_ticket ``` @@ -615,7 +615,7 @@ To see the recovery of partial progress in practice, let's make the `CartObject/ ```java CartObject.java - CODE_LOAD::https://raw.githubusercontent.com/restatedev/examples/main/tutorials/tour-of-restate-java/src/main/java/dev/restate/tour/part1/CartObject.java#- + CODE_LOAD::https://raw.githubusercontent.com/restatedev/examples/main/tutorials/tour-of-restate-java/src/main/java/dev/restate/tour/part1/CartObject.java#add_ticket ``` @@ -800,14 +800,14 @@ Let the `CartObject/addTicket` handler call the `CartObject/expireTicket` handle ```typescript cart_object.ts -CODE_LOAD::https://raw.githubusercontent.com/restatedev/examples/main/tutorials/tour-of-restate-typescript/src/part2/cart_object.ts#- +CODE_LOAD::https://raw.githubusercontent.com/restatedev/examples/main/tutorials/tour-of-restate-typescript/src/part2/cart_object.ts#add_ticket ``` ```java CartObject.java -CODE_LOAD::https://raw.githubusercontent.com/restatedev/examples/main/tutorials/tour-of-restate-java/src/main/java/dev/restate/tour/part2/CartObject.java#- +CODE_LOAD::https://raw.githubusercontent.com/restatedev/examples/main/tutorials/tour-of-restate-java/src/main/java/dev/restate/tour/part2/CartObject.java#add_ticket ``` @@ -881,14 +881,14 @@ When running on function-as-a-service platforms, your function can suspend in th ```ts - CODE_LOAD::ts/src/get_started/tour.ts#- + CODE_LOAD::ts/src/get_started/tour.ts#sleep ``` ```java - CODE_LOAD::java/src/main/java/get_started/Tour.java#- + CODE_LOAD::java/src/main/java/get_started/Tour.java#sleep ``` @@ -943,14 +943,14 @@ To get this behaviour, we key the `TicketObject` on ticket ID. We now have a sin ```typescript - CODE_LOAD::ts/src/get_started/tour.ts#- + CODE_LOAD::ts/src/get_started/tour.ts#sleep_and_send ``` ```java - CODE_LOAD::java/src/main/java/get_started/Tour.java#- + CODE_LOAD::java/src/main/java/get_started/Tour.java#sleep_and_send ``` @@ -978,7 +978,7 @@ Have a look at the highlighted code: ```ts cart_object.ts -CODE_LOAD::https://raw.githubusercontent.com/restatedev/examples/main/tutorials/tour-of-restate-typescript/src/part3/cart_object.ts#- +CODE_LOAD::https://raw.githubusercontent.com/restatedev/examples/main/tutorials/tour-of-restate-typescript/src/part3/cart_object.ts#add_ticket ``` To retrieve the cart, you use `ctx.get`. @@ -988,7 +988,7 @@ This returns `null` if the value has never been set. ```java CartObject.java -CODE_LOAD::https://raw.githubusercontent.com/restatedev/examples/main/tutorials/tour-of-restate-java/src/main/java/dev/restate/tour/part3/CartObject.java#- +CODE_LOAD::https://raw.githubusercontent.com/restatedev/examples/main/tutorials/tour-of-restate-java/src/main/java/dev/restate/tour/part3/CartObject.java#add_ticket ``` To retrieve the cart, you use `ctx.get` with a state key that describes the name and the [(de)serializers](/develop/java/serialization) to be used. @@ -1056,14 +1056,14 @@ Also adapt the `CartObject/checkout` function, to use the tickets: ```typescript cart_object.ts - CODE_LOAD::https://raw.githubusercontent.com/restatedev/examples/main/tutorials/tour-of-restate-typescript/src/part3/cart_object.ts#- + CODE_LOAD::https://raw.githubusercontent.com/restatedev/examples/main/tutorials/tour-of-restate-typescript/src/part3/cart_object.ts#checkout ``` ```java CartObject.java - CODE_LOAD::https://raw.githubusercontent.com/restatedev/examples/main/tutorials/tour-of-restate-java/src/main/java/dev/restate/tour/part3/CartObject.java#- + CODE_LOAD::https://raw.githubusercontent.com/restatedev/examples/main/tutorials/tour-of-restate-java/src/main/java/dev/restate/tour/part3/CartObject.java#checkout ``` @@ -1127,14 +1127,14 @@ If this is the case, then you call `TicketObject/unreserve` and remove it from t ```ts cart_object.ts -CODE_LOAD::https://raw.githubusercontent.com/restatedev/examples/main/tutorials/tour-of-restate-typescript/src/part3/cart_object.ts#- +CODE_LOAD::https://raw.githubusercontent.com/restatedev/examples/main/tutorials/tour-of-restate-typescript/src/part3/cart_object.ts#expire_ticket ``` ```java CartObject.java -CODE_LOAD::https://raw.githubusercontent.com/restatedev/examples/main/tutorials/tour-of-restate-java/src/main/java/dev/restate/tour/part3/CartObject.java#- +CODE_LOAD::https://raw.githubusercontent.com/restatedev/examples/main/tutorials/tour-of-restate-java/src/main/java/dev/restate/tour/part3/CartObject.java#expire_ticket ``` @@ -1178,14 +1178,14 @@ return `true` (reservation successful). ```typescript ticket_object.ts -CODE_LOAD::https://raw.githubusercontent.com/restatedev/examples/main/tutorials/tour-of-restate-typescript/src/part3/ticket_object.ts#- +CODE_LOAD::https://raw.githubusercontent.com/restatedev/examples/main/tutorials/tour-of-restate-typescript/src/part3/ticket_object.ts#reserve ``` ```java TicketObject.java -CODE_LOAD::https://raw.githubusercontent.com/restatedev/examples/main/tutorials/tour-of-restate-java/src/main/java/dev/restate/tour/part3/TicketObject.java#- +CODE_LOAD::https://raw.githubusercontent.com/restatedev/examples/main/tutorials/tour-of-restate-java/src/main/java/dev/restate/tour/part3/TicketObject.java#reserve ``` @@ -1206,14 +1206,14 @@ Clear the `"status"`, if it's not equal to `TicketStatus.Sold`. ```ts ticket_object.ts -CODE_LOAD::https://raw.githubusercontent.com/restatedev/examples/main/tutorials/tour-of-restate-typescript/src/part3/ticket_object.ts#- +CODE_LOAD::https://raw.githubusercontent.com/restatedev/examples/main/tutorials/tour-of-restate-typescript/src/part3/ticket_object.ts#unreserve ``` ```java TicketObject.java -CODE_LOAD::https://raw.githubusercontent.com/restatedev/examples/main/tutorials/tour-of-restate-java/src/main/java/dev/restate/tour/part3/TicketObject.java#- +CODE_LOAD::https://raw.githubusercontent.com/restatedev/examples/main/tutorials/tour-of-restate-java/src/main/java/dev/restate/tour/part3/TicketObject.java#unreserve ``` @@ -1234,14 +1234,14 @@ Set the `"status"` to `TicketStatus.Sold` if it's reserved. ```ts ticket_object.ts -CODE_LOAD::https://raw.githubusercontent.com/restatedev/examples/main/tutorials/tour-of-restate-typescript/src/part3/ticket_object.ts#- +CODE_LOAD::https://raw.githubusercontent.com/restatedev/examples/main/tutorials/tour-of-restate-typescript/src/part3/ticket_object.ts#mark_as_sold ``` ```java TicketObject.java -CODE_LOAD::https://raw.githubusercontent.com/restatedev/examples/main/tutorials/tour-of-restate-java/src/main/java/dev/restate/tour/part3/TicketObject.java#- +CODE_LOAD::https://raw.githubusercontent.com/restatedev/examples/main/tutorials/tour-of-restate-java/src/main/java/dev/restate/tour/part3/TicketObject.java#mark_as_sold ``` @@ -1300,14 +1300,14 @@ Try it out by printing the idempotency key and then throwing an error: ```ts checkout_service.ts - CODE_LOAD::ts/src/get_started/tour.ts#- + CODE_LOAD::ts/src/get_started/tour.ts#uuid ``` ```java CheckoutService.java - CODE_LOAD::java/src/main/java/get_started/Tour.java#- + CODE_LOAD::java/src/main/java/get_started/Tour.java#uuid ``` @@ -1377,14 +1377,14 @@ We assume every ticket costs 40 dollars. ```typescript checkout_service.ts -CODE_LOAD::ts/src/get_started/tour.ts#- +CODE_LOAD::ts/src/get_started/tour.ts#checkout ``` ```java CheckoutService.java -CODE_LOAD::java/src/main/java/get_started/Tour.java#- +CODE_LOAD::java/src/main/java/get_started/Tour.java#checkout ``` @@ -1408,14 +1408,14 @@ After the `CheckoutService/handle` handler has handled the payment, you need to ```ts checkout_service.ts -CODE_LOAD::https://raw.githubusercontent.com/restatedev/examples/main/tutorials/tour-of-restate-typescript/src/part4/checkout_service.ts#- +CODE_LOAD::https://raw.githubusercontent.com/restatedev/examples/main/tutorials/tour-of-restate-typescript/src/part4/checkout_service.ts#checkout ``` ```java CheckoutService.java -CODE_LOAD::https://raw.githubusercontent.com/restatedev/examples/main/tutorials/tour-of-restate-java/src/main/java/dev/restate/tour/part4/CheckoutService.java#- +CODE_LOAD::https://raw.githubusercontent.com/restatedev/examples/main/tutorials/tour-of-restate-java/src/main/java/dev/restate/tour/part4/CheckoutService.java#checkout ``` @@ -1433,14 +1433,14 @@ Let the `CartObject/checkout` handler mark all tickets as sold by calling `Ticke ```typescript cart_object.ts - CODE_LOAD::https://raw.githubusercontent.com/restatedev/examples/main/tutorials/tour-of-restate-typescript/src/part4/cart_object.ts#- + CODE_LOAD::https://raw.githubusercontent.com/restatedev/examples/main/tutorials/tour-of-restate-typescript/src/part4/cart_object.ts#checkout ``` ```java CartObject.java - CODE_LOAD::https://raw.githubusercontent.com/restatedev/examples/main/tutorials/tour-of-restate-java/src/main/java/dev/restate/tour/part4/CartObject.java#- + CODE_LOAD::https://raw.githubusercontent.com/restatedev/examples/main/tutorials/tour-of-restate-java/src/main/java/dev/restate/tour/part4/CartObject.java#checkout ``` diff --git a/docs/invoke/kafka.mdx b/docs/invoke/kafka.mdx index 1bc7a11f..ac3a9d3d 100644 --- a/docs/invoke/kafka.mdx +++ b/docs/invoke/kafka.mdx @@ -76,7 +76,7 @@ You can invoke handlers via Kafka events, by doing the following:
Event metadata - Each event carries within the _`CODE_LOAD::ts/src/develop/kafka.ts#-`_ map the following entries: + Each event carries within the _`CODE_LOAD::ts/src/develop/kafka.ts#headers`_ map the following entries: * `restate.subscription.id`: The subscription identifier, as shown by the [Admin API](../../references/admin-api). * `kafka.offset`: The record offset. @@ -99,7 +99,7 @@ You can invoke handlers via Kafka events, by doing the following:
Event metadata - Each event carries within the _`CODE_LOAD::java/src/main/java/develop/MyKafkaVirtualObject.java#-`_ map the following entries: + Each event carries within the _`CODE_LOAD::java/src/main/java/develop/MyKafkaVirtualObject.java#headers`_ map the following entries: * `restate.subscription.id`: The subscription identifier, as shown by the [Admin API](../../references/admin-api). * `kafka.offset`: The record offset. From 5615c595a17a6549b77d6c8b029d330558cdcff3 Mon Sep 17 00:00:00 2001 From: Giselle van Dongen Date: Mon, 17 Jun 2024 12:50:20 +0200 Subject: [PATCH 04/18] Update readme on new tag syntax --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 09ec4fcb..39e28b35 100644 --- a/README.md +++ b/README.md @@ -48,9 +48,9 @@ greet: async (ctx: restate.Context, name: string) => { // }, ``` -You can also use custom tags. Please keep the tags a bit uniform and descriptive. For example, `// ` and `// `. This way it's easy to discriminate tags from just comments. +You can also use custom tags. Tags need to follow the format `// ` and `// `. -Refer to the code snippet from within the markdown documentation file as follows `CODE_LOAD::`. If you use custom tags, specify them as `CODE_LOAD::#-`. You need to put this inside a code block with the language specified. The path is relative from **within** the code_snippets folder. For example, `CODE_LOAD::java/src/main/java/Greeter.java#-`. If you use custom tags, specify them as `CODE_LOAD::#custom_tag`. You need to put this inside a code block with the language specified. The path is relative from **within** the code_snippets folder. For example, `CODE_LOAD::java/src/main/java/Greeter.java#greet_function` will look for the tags `// ` and `// ` in the code. ### Marking code If you want to mark a specific line in the code snippet, you can use the following tags : `// ` and `// `. From 960521b237cd1871d5c8605339fc3295c8ec7215 Mon Sep 17 00:00:00 2001 From: Giselle van Dongen Date: Mon, 17 Jun 2024 13:57:17 +0200 Subject: [PATCH 05/18] Fix code loader to accept both a # start/end tag and a ? mark tag --- src/plugins/code-loader.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/plugins/code-loader.js b/src/plugins/code-loader.js index fa440e04..d25fef29 100644 --- a/src/plugins/code-loader.js +++ b/src/plugins/code-loader.js @@ -2,7 +2,7 @@ const fs = require('fs'); const fetch = require('node-fetch'); const plugin = (options) => { - const codeLoadRegex = /^CODE_LOAD::([^#?]+)(?:#([^#]+))?(?:\?([^#]+))?$/g; + const codeLoadRegex = /^CODE_LOAD::([^#?]+)(?:#([^?]*))?(?:\?(.+))?$/g; const injectCode = async (str) => { let fileData = null; From 293476e22f6602c00ac51aee96aa77e84fc25b59 Mon Sep 17 00:00:00 2001 From: Giselle van Dongen Date: Mon, 17 Jun 2024 14:17:51 +0200 Subject: [PATCH 06/18] Fix scrollycoding of concept services pages --- .../main/java/concepts/services/Greeter.java | 22 +++++++ .../main/java/concepts/services/Payment.java | 23 ++++++- .../concepts/services/RoleUpdateService.java | 14 +++- code_snippets/ts/src/concepts/services.ts | 18 ++++- .../ts/src/concepts/virtual_objects.ts | 26 +++++++- code_snippets/ts/src/concepts/workflow.ts | 18 ++++- docs/concepts/services.mdx | 66 +++++++++++-------- 7 files changed, 152 insertions(+), 35 deletions(-) diff --git a/code_snippets/java/src/main/java/concepts/services/Greeter.java b/code_snippets/java/src/main/java/concepts/services/Greeter.java index fac3b38e..c826c2ad 100644 --- a/code_snippets/java/src/main/java/concepts/services/Greeter.java +++ b/code_snippets/java/src/main/java/concepts/services/Greeter.java @@ -14,28 +14,50 @@ */ // +// @VirtualObject public class Greeter { + // public final static StateKey COUNT = StateKey.of("count", JsonSerdes.INT); + // + // @Handler public String greet(ObjectContext ctx, String greeting) { + // + // + // Integer count = ctx.get(COUNT).orElse(0); count++; ctx.set(COUNT, count); + // + // return greeting + " " + ctx.key() + "for the " + count + "-th time"; + // } + // + // @Handler public String ungreet(ObjectContext ctx) { + // + // + // Integer count = ctx.get(COUNT).orElse(0); + // if (count > 0) { + // count--; + // } + // ctx.set(COUNT, count); + // + // return "Dear " + ctx.key() + ", taking one greeting back"; + // } public static void main(String[] args) { diff --git a/code_snippets/java/src/main/java/concepts/services/Payment.java b/code_snippets/java/src/main/java/concepts/services/Payment.java index 73fcf6f4..5cf280e2 100644 --- a/code_snippets/java/src/main/java/concepts/services/Payment.java +++ b/code_snippets/java/src/main/java/concepts/services/Payment.java @@ -12,10 +12,11 @@ import dev.restate.sdk.common.DurablePromiseKey; import dev.restate.sdk.common.StateKey; import dev.restate.sdk.common.TerminalException; +import dev.restate.sdk.http.vertx.RestateHttpEndpointBuilder; import dev.restate.sdk.serde.jackson.JacksonSerdes; -// +// @Workflow public class Payment { @@ -23,6 +24,7 @@ public class Payment { private static final DurablePromiseKey PAYMENT_SUCCESS = DurablePromiseKey.of("payment.success", JacksonSerdes.of(PaymentSuccess.class)); + // @Workflow public String run(WorkflowContext ctx, PaymentRequest payment) { @@ -33,26 +35,43 @@ public String run(WorkflowContext ctx, PaymentRequest payment) { ctx.run("make a payment", JsonSerdes.BOOLEAN, () -> PaymentClient.charge(ctx.key(), payment.getAccount(), payment.getAmount())); + // ctx.promise(PAYMENT_SUCCESS).awaitable().await(); + // + // ctx.set(STATUS, "Payment succeeded"); + // ctx.run("notify the user", JsonSerdes.BOOLEAN, () -> EmailClient.sendSuccessNotification(payment.getEmail())); + // ctx.set(STATUS, "User notified of payment success"); + // return "success"; } + // + // @Shared public void paymentWebhook(SharedWorkflowContext ctx, PaymentSuccess msg) { ctx.promiseHandle(PAYMENT_SUCCESS).resolve(msg); } + // + // @Shared public String getStatus(SharedWorkflowContext ctx) { return ctx.get(STATUS).orElse("unknown"); } + // + + public static void main(String[] args) { + RestateHttpEndpointBuilder.builder() + .bind(new Payment()) + .buildAndListen(); + } } -// +// diff --git a/code_snippets/java/src/main/java/concepts/services/RoleUpdateService.java b/code_snippets/java/src/main/java/concepts/services/RoleUpdateService.java index 53fe1861..6129d9b5 100644 --- a/code_snippets/java/src/main/java/concepts/services/RoleUpdateService.java +++ b/code_snippets/java/src/main/java/concepts/services/RoleUpdateService.java @@ -16,20 +16,32 @@ */ // +// @Service public class RoleUpdateService { +// + // @Handler public void applyRoleUpdate(Context ctx, UpdateRequest req) { + // + // boolean success = ctx.run(JsonSerdes.BOOLEAN, () -> SystemA.applyUserRole(req.getUserId(), req.getRole())); + // + // if (!success) { return; } + // + // for(String permission: req.getPermissions()) { - ctx.run(JsonSerdes.BOOLEAN, () -> + // + // + ctx.run(JsonSerdes.BOOLEAN, () -> SystemB.applyPermission(req.getUserId(), permission)); + // } } diff --git a/code_snippets/ts/src/concepts/services.ts b/code_snippets/ts/src/concepts/services.ts index f10479ac..4cd7ecf7 100644 --- a/code_snippets/ts/src/concepts/services.ts +++ b/code_snippets/ts/src/concepts/services.ts @@ -4,30 +4,44 @@ * Make sure you adapt the line numbers when adapting the code */ -// +// import * as restate from "@restatedev/restate-sdk"; import {Context} from "@restatedev/restate-sdk"; +// export const roleUpdateService = restate.service({ + // name: "roleUpdate", + // handlers: { applyRoleUpdate: async (ctx: Context, update: UpdateRequest) => { + // const { userId, role, permissions } = update; + // const success = await ctx.run(() => applyUserRole(userId, role)); + // + // if (!success) { return; } + // + // for (const permission of permissions) { + // + // await ctx.run(() => applyPermission(userId, permission)); + // + // } + // } } }); restate.endpoint().bind(roleUpdateService).listen(); -// +// export type UserRole = { roleKey: string; diff --git a/code_snippets/ts/src/concepts/virtual_objects.ts b/code_snippets/ts/src/concepts/virtual_objects.ts index 9907c241..e914a285 100644 --- a/code_snippets/ts/src/concepts/virtual_objects.ts +++ b/code_snippets/ts/src/concepts/virtual_objects.ts @@ -4,30 +4,52 @@ * Make sure you adapt the line numbers when adapting the code */ -// +// import * as restate from "@restatedev/restate-sdk"; import {ObjectContext} from "@restatedev/restate-sdk"; +// export const greeterObject = restate.object({ +// name: "greeter", handlers: { + // + // greet: async (ctx: ObjectContext, greeting: string ) => { + // + // + // let count = (await ctx.get("count")) ?? 0; count++; ctx.set("count", count); + // + // return `${greeting} ${ctx.key} for the ${count}-th time.`; + // }, + // + // ungreet: async (ctx: ObjectContext) => { + // + // + // let count = (await ctx.get("count")) ?? 0; + // if (count > 0) { + // count--; + // } + // ctx.set("count", count); + // + // return `Dear ${ctx.key}, taking one greeting back: ${count}.`; + // }, } }); restate.endpoint().bind(greeterObject).listen(); -// +// diff --git a/code_snippets/ts/src/concepts/workflow.ts b/code_snippets/ts/src/concepts/workflow.ts index 22e78c3a..7bd663c4 100644 --- a/code_snippets/ts/src/concepts/workflow.ts +++ b/code_snippets/ts/src/concepts/workflow.ts @@ -28,10 +28,11 @@ interface PaymentSuccess { account: string; } -// +// const payment = restate.workflow({ name: "payment", handlers: { + // run: async (ctx: restate.WorkflowContext, payment: PaymentRequest) => { // Validate payment. If not valid, end workflow right here without retries. @@ -43,23 +44,36 @@ const payment = restate.workflow({ await paymentClnt.charge(ctx.key, payment.account, payment.amount); }); + // await ctx.promise("payment.success"); + // + // ctx.set("status", "Payment succeeded"); + // await ctx.run("notify the user", async () => { await emailClnt.sendSuccessNotification(payment.email); }); + // ctx.set("status", "User notified of payment success"); + // return "success"; }, + // + // paymentWebhook: async (ctx: restate.WorkflowSharedContext, account: string) => { await ctx.promise("payment.success").resolve({ account }); }, + // + // status: (ctx: restate.WorkflowSharedContext) => ctx.get("status"), + // }, }); -// \ No newline at end of file + +restate.endpoint().bind(payment).listen(); +// \ No newline at end of file diff --git a/docs/concepts/services.mdx b/docs/concepts/services.mdx index 4dda09d2..ca99e556 100644 --- a/docs/concepts/services.mdx +++ b/docs/concepts/services.mdx @@ -43,29 +43,32 @@ Services expose a collection of handlers: Restate logs the **results of actions** in the system. Restate takes care of retries and recovers the handler to the point where it failed. - ```typescript mark=10,16 - CODE_LOAD::ts/src/concepts/services.ts#service + ```ts + CODE_LOAD::ts/src/concepts/services.ts?1 ``` --- The handlers of services are **independent** and can be **invoked concurrently**. - ```diff mark=4,6,7,18,19,20 + ```ts + CODE_LOAD::ts/src/concepts/services.ts?2 ``` --- Handlers use the regular code and control flow, no custom DSLs. - ```diff mark=11:13,15,17 + ```ts + CODE_LOAD::ts/src/concepts/services.ts?3 ``` --- Service handlers **don't have access to Restate's K/V store**. - ```diff + ```ts + CODE_LOAD::ts/src/concepts/services.ts ``` @@ -77,29 +80,32 @@ Services expose a collection of handlers: Restate logs the **results of actions** in the system. Restate takes care of retries and recovers the handler to the point where it failed. - ```java mark=6:7,13:14 - CODE_LOAD::java/src/main/java/concepts/services/RoleUpdateService.java + ```java + CODE_LOAD::java/src/main/java/concepts/services/RoleUpdateService.java?1 ``` --- The handlers of services are **independent** and can be **invoked concurrently**. - ```diff mark=1:5,16,19,20,23 + ```java + CODE_LOAD::java/src/main/java/concepts/services/RoleUpdateService.java?2 ``` --- Handlers use the regular code and control flow, no custom DSLs. - ```diff mark=8:10,12,14,15 + ```java + CODE_LOAD::java/src/main/java/concepts/services/RoleUpdateService.java?3 ``` --- Service handlers **don't have access to Restate's K/V store**. - ```diff + ```java + CODE_LOAD::java/src/main/java/concepts/services/RoleUpdateService.java ``` @@ -119,8 +125,8 @@ Virtual objects expose a set of handlers with access to K/V state stored in Rest A virtual object is **uniquely identified and accessed by its key**. - ```typescript mark=4,6,7,11,12,14,20,21,22 - CODE_LOAD::ts/src/concepts/virtual_objects.ts#virtual_object + ```ts + CODE_LOAD::ts/src/concepts/virtual_objects.ts?1 ``` --- @@ -130,7 +136,8 @@ Virtual objects expose a set of handlers with access to K/V state stored in Rest Restate delivers the state together with the request to the virtual object, so virtual objects have their state locally accessible without requiring any database connection or lookup. State is exclusive, and atomically committed with the method execution. - ```diff mark=8:10,15,17,19 + ```ts + CODE_LOAD::ts/src/concepts/virtual_objects.ts?2 ``` --- @@ -138,7 +145,8 @@ Virtual objects expose a set of handlers with access to K/V state stored in Rest To ensure consistent writes to the state, Restate provides **concurrency guarantees**: at most one handler can execute at a time for a given virtual object. This can also be used for example to implement a locking mechanism or to ensure single writer to a database row. - ```diff mark=4,6,7,9,12,14,17,21,22,23 + ```ts + CODE_LOAD::ts/src/concepts/virtual_objects.ts?3 ``` @@ -148,8 +156,8 @@ Virtual objects expose a set of handlers with access to K/V state stored in Rest A virtual object is **uniquely identified and accessed by its key**. - ```java mark=1,2,7,8,12,13,15,16,22,23 - CODE_LOAD::java/src/main/java/concepts/services/Greeter.java + ```java + CODE_LOAD::java/src/main/java/concepts/services/Greeter.java?1 ``` --- @@ -159,7 +167,8 @@ Virtual objects expose a set of handlers with access to K/V state stored in Rest Restate delivers the state together with the request to the virtual object, so virtual objects have their state locally accessible without requiring any database connection or lookup. State is exclusive, and atomically committed with the method execution. - ```diff mark=9:11,17,19,21 + ```java + CODE_LOAD::java/src/main/java/concepts/services/Greeter.java?2 ``` --- @@ -167,7 +176,8 @@ Virtual objects expose a set of handlers with access to K/V state stored in Rest **Concurrency guarantees**: to ensure consistent writes to the state, at most one handler can execute at a time for a given virtual object. This can also be used, for example, to implement a locking mechanism or to ensure single writer to a database row. - ```diff mark=1,2,7,8,13,15,16,23 + ```java + CODE_LOAD::java/src/main/java/concepts/services/Greeter.java?3 ``` @@ -186,8 +196,8 @@ They have some added capabilities such as signaling, querying, additional invoca A workflow has a **`run` handler** that implements the **workflow logic**. The `run` handler runs exactly once per workflow ID (object). - ```typescript mark=4:25 - CODE_LOAD::ts/src/concepts/workflow.ts#workflow + ```ts + CODE_LOAD::ts/src/concepts/workflow.ts?1 ``` --- @@ -195,7 +205,8 @@ They have some added capabilities such as signaling, querying, additional invoca You can **query the workflow** by defining other handlers in the same object. For example, you can store state in the workflow object, and query it from other handlers. - ```diff mark=16,22,31 + ```ts + CODE_LOAD::ts/src/concepts/workflow.ts?2 ``` --- @@ -203,7 +214,8 @@ They have some added capabilities such as signaling, querying, additional invoca You can **signal the workflow**, to send information to it, via [Durable Promises](/develop/ts/workflows#signaling-workflows). For example, the payment provider signals the workflow that the payment was successful by calling the `paymentWebhook`. - ```diff mark=15,27:29 + ```ts + CODE_LOAD::ts/src/concepts/workflow.ts?3 ``` @@ -214,8 +226,8 @@ They have some added capabilities such as signaling, querying, additional invoca A workflow has a **`run` handler** that implements the **workflow logic**. The `run` handler runs exactly once per workflow ID (object). - ```java mark=8:28 - CODE_LOAD::java/src/main/java/concepts/services/Payment.java#workflow + ```java + CODE_LOAD::java/src/main/java/concepts/services/Payment.java?1 ``` --- @@ -223,7 +235,8 @@ They have some added capabilities such as signaling, querying, additional invoca You can **query the workflow** by defining other handlers in the same object. For example, you can store state in the workflow object, and query it from other handlers. - ```diff mark=20,25,35:38 + ```java + CODE_LOAD::java/src/main/java/concepts/services/Payment.java?2 ``` --- @@ -231,7 +244,8 @@ They have some added capabilities such as signaling, querying, additional invoca You can **signal the workflow**, to send information to it, via [Durable Promises](/develop/ts/workflows#signaling-workflows). For example, the payment provider signals the workflow that the payment was successful by calling the `paymentWebhook`. - ```diff mark=18,30:33 + ```java + CODE_LOAD::java/src/main/java/concepts/services/Payment.java?3 ``` From 16aea24979dadfb35a658321f5ba1f981eea4278 Mon Sep 17 00:00:00 2001 From: Giselle van Dongen Date: Mon, 17 Jun 2024 14:39:59 +0200 Subject: [PATCH 07/18] Fix scrollycoding of workflow Java SDK page --- .../develop/workflows/SignupWorkflow.java | 10 +++++++ .../develop/workflows/SignupWorkflow.kt | 11 ++++++++ docs/develop/java/workflows.mdx | 27 +++++++++++-------- 3 files changed, 37 insertions(+), 11 deletions(-) diff --git a/code_snippets/java/src/main/java/develop/workflows/SignupWorkflow.java b/code_snippets/java/src/main/java/develop/workflows/SignupWorkflow.java index 597f573f..c4d03118 100644 --- a/code_snippets/java/src/main/java/develop/workflows/SignupWorkflow.java +++ b/code_snippets/java/src/main/java/develop/workflows/SignupWorkflow.java @@ -19,6 +19,7 @@ public class SignupWorkflow { private static final StateKey STATUS = StateKey.of("status", JsonSerdes.STRING); + // @Workflow public boolean run(WorkflowContext ctx, Email email) { String secret = ctx.random().nextUUID().toString(); @@ -28,28 +29,37 @@ public boolean run(WorkflowContext ctx, Email email) { () -> sendEmailWithLink(email, secret)); ctx.set(STATUS, "Sent email"); + // String clickSecret = ctx.promise(EMAIL_CLICKED) .awaitable() .await(); + // ctx.set(STATUS, "Clicked email"); return clickSecret.equals(secret); } + // @Shared public void click(SharedWorkflowContext ctx, String secret) { + // ctx.promiseHandle(EMAIL_CLICKED).resolve(secret); + // } + // @Shared public String getStatus(SharedWorkflowContext ctx) { return ctx.get(STATUS).orElse("Unknown"); } + // public static void main(String[] args) { + // RestateHttpEndpointBuilder.builder() .bind(new SignupWorkflow()) .buildAndListen(); + // } } // \ No newline at end of file diff --git a/code_snippets/kotlin/src/main/kotlin/develop/workflows/SignupWorkflow.kt b/code_snippets/kotlin/src/main/kotlin/develop/workflows/SignupWorkflow.kt index 439189b6..66851e47 100644 --- a/code_snippets/kotlin/src/main/kotlin/develop/workflows/SignupWorkflow.kt +++ b/code_snippets/kotlin/src/main/kotlin/develop/workflows/SignupWorkflow.kt @@ -15,6 +15,7 @@ class SignupWorkflow { private val STATUS = KtStateKey.json("status") } + // @Workflow suspend fun run(ctx: WorkflowContext, email: Email): Boolean { val secret = ctx.random().nextUUID().toString() @@ -24,30 +25,40 @@ class SignupWorkflow { sendEmailWithLink(email, secret) } + // val clickSecret = ctx.promise(EMAIL_CLICKED) .awaitable() .await() + // ctx.set(STATUS, "Clicked email") return clickSecret == secret } + // @Shared suspend fun click(ctx: SharedWorkflowContext, secret: String) { + // ctx.promiseHandle(EMAIL_CLICKED).resolve(secret) + // } + // @Shared suspend fun getStatus(ctx: SharedWorkflowContext): String? { return ctx.get(STATUS) } + // } + fun main() { + // RestateHttpEndpointBuilder .builder() .bind(SignupWorkflow()) .buildAndListen() + // } // diff --git a/docs/develop/java/workflows.mdx b/docs/develop/java/workflows.mdx index 3b58e3e3..0d88f487 100644 --- a/docs/develop/java/workflows.mdx +++ b/docs/develop/java/workflows.mdx @@ -36,8 +36,8 @@ Have a look at the code example to get a better understanding of how workflows a This handler has access to the same SDK features as Service and Virtual Object handlers. For example, use [`ctx.run`](/develop/java/journaling-results#journaled-actions) to log intermediate results in Restate and avoid re-execution on replay. - ```java SignupWorkflow.java mark=8:23 - CODE_LOAD::java/src/main/java/develop/workflows/SignupWorkflow.java + ```java + CODE_LOAD::java/src/main/java/develop/workflows/SignupWorkflow.java?1 ``` --- @@ -49,7 +49,8 @@ Have a look at the code example to get a better understanding of how workflows a Every workflow execution can be seen as a new object, so the state is isolated to a single workflow execution. The state can only be mutated by the `run` handler of the workflow. The other handlers can only read the state. - ```java SignupWorkflow.java mark=30:33 + ```java + CODE_LOAD::java/src/main/java/develop/workflows/SignupWorkflow.java?2 ``` --- @@ -63,7 +64,8 @@ Have a look at the code example to get a better understanding of how workflows a 1. Create a promise in your `run` handler that is durable and distributed 2. Resolve or reject the promise in any other handler in the workflow. This can be done at most one time. - ```java SignupWorkflow.java mark=17:19,25:28 + ```java + CODE_LOAD::java/src/main/java/develop/workflows/SignupWorkflow.java?3 ``` --- @@ -73,7 +75,8 @@ Have a look at the code example to get a better understanding of how workflows a You serve workflows in the same way as Services and Virtual Objects: by binding them to an [HTTP endpoint](/develop/java/serving#creating-an-http-endpoint) or [AWS Lambda handler](/develop/java/serving#creating-a-lambda-handler). Make sure you [register the endpoint or Lambda handler](/operate/registration) in Restate before invoking it. - ```java SignupWorkflow.java mark=35:39 + ```java + CODE_LOAD::java/src/main/java/develop/workflows/SignupWorkflow.java?4 ``` @@ -87,8 +90,8 @@ Have a look at the code example to get a better understanding of how workflows a This handler has access to the same SDK features as Service and Virtual Object handlers. For example, use [`ctx.runBlock`](/develop/java/journaling-results#journaled-actions) to log intermediate results in Restate and avoid re-execution on replay. - ```kotlin SignupWorkflow.kt mark=9:24 - CODE_LOAD::kotlin/src/main/kotlin/develop/workflows/SignupWorkflow.kt + ```kotlin + CODE_LOAD::kotlin/src/main/kotlin/develop/workflows/SignupWorkflow.kt?1 ``` --- @@ -100,7 +103,8 @@ Have a look at the code example to get a better understanding of how workflows a Every workflow execution can be seen as a new object, so the state is isolated to a single workflow execution. The state can only be mutated by the `run` handler of the workflow. The other handlers can only read the state. - ```kotlin SignupWorkflow.kt mark=31:34 + ```kotlin + CODE_LOAD::kotlin/src/main/kotlin/develop/workflows/SignupWorkflow.kt?2 ``` --- @@ -114,7 +118,8 @@ Have a look at the code example to get a better understanding of how workflows a 1. Create a promise in your `run` handler that is durable and distributed 2. Resolve or reject the promise in any other handler in the workflow. This can be done at most one time. - ```kotlin SignupWorkflow.kt mark=18:20,26:29 + ```kotlin + CODE_LOAD::kotlin/src/main/kotlin/develop/workflows/SignupWorkflow.kt?3 ``` --- @@ -124,11 +129,11 @@ Have a look at the code example to get a better understanding of how workflows a You serve workflows in the same way as Services and Virtual Objects: by binding them to an [HTTP endpoint](/develop/java/serving#creating-an-http-endpoint) or [AWS Lambda handler](/develop/java/serving#creating-a-lambda-handler). Make sure you [register the endpoint or Lambda handler](/operate/registration) in Restate before invoking it. - ```kotlin SignupWorkflow.kt mark=38:41 + ```kotlin + CODE_LOAD::kotlin/src/main/kotlin/develop/workflows/SignupWorkflow.kt?4 ``` - From 32d1a4d25a40b693b0a97ab8a36d0ba3173744a2 Mon Sep 17 00:00:00 2001 From: Giselle van Dongen Date: Mon, 17 Jun 2024 14:46:03 +0200 Subject: [PATCH 08/18] Fix journaling results TS SDK page --- ...{side_effects.ts => journaling_results.ts} | 13 ++++++++++-- docs/develop/ts/journaling-results.mdx | 20 ++++++++++--------- 2 files changed, 22 insertions(+), 11 deletions(-) rename code_snippets/ts/src/develop/{side_effects.ts => journaling_results.ts} (81%) diff --git a/code_snippets/ts/src/develop/side_effects.ts b/code_snippets/ts/src/develop/journaling_results.ts similarity index 81% rename from code_snippets/ts/src/develop/side_effects.ts rename to code_snippets/ts/src/develop/journaling_results.ts index fc215c18..7cdbe342 100644 --- a/code_snippets/ts/src/develop/side_effects.ts +++ b/code_snippets/ts/src/develop/journaling_results.ts @@ -14,20 +14,29 @@ const service = restate.service({ promiseCombinators: async (ctx: restate.Context, name: string) => { // const sleepPromise = ctx.sleep(100); - const callPromise = ctx.serviceClient(MyService) + const callPromise = ctx + .serviceClient(MyService) .myHandler("Hi"); + // const resultArray = await CombineablePromise .all([sleepPromise, callPromise]); + // + // const anyResult = await CombineablePromise .any([sleepPromise, callPromise]); + // + // const raceResult = await CombineablePromise .race([sleepPromise, callPromise]); + // - const allSettledResult = await CombineablePromise + // + const settledResult = await CombineablePromise .allSettled([sleepPromise, callPromise]); + // // // diff --git a/docs/develop/ts/journaling-results.mdx b/docs/develop/ts/journaling-results.mdx index 2cfa97d8..530124ce 100644 --- a/docs/develop/ts/journaling-results.mdx +++ b/docs/develop/ts/journaling-results.mdx @@ -21,7 +21,7 @@ Restate replays the result instead of re-executing the operation on retries. Here is an example of a database request for which the string response is stored in Restate: ```typescript -CODE_LOAD::ts/src/develop/side_effects.ts#side_effect +CODE_LOAD::ts/src/develop/journaling_results.ts#side_effect ``` You cannot invoke any methods on the Restate context within a side effect. @@ -48,8 +48,8 @@ Restate then logs the order in which they are resolved or rejected, to make them - Resolves with an array of results, once all input promises are resolved. - Rejects when any input promise is rejected. - ```typescript focus=1:6 mark=5:6 - CODE_LOAD::ts/src/develop/side_effects.ts#promises + ```ts + CODE_LOAD::ts/src/develop/journaling_results.ts#promises?1 ``` --- @@ -60,7 +60,8 @@ Restate then logs the order in which they are resolved or rejected, to make them - Rejects when all the input promises are rejected (including when an empty iterable is passed). This results in an `AggregateError` containing an array of the reasons for rejection. - ```typescript focus=1:3,8,9 mark=8:9 + ```ts + CODE_LOAD::ts/src/develop/journaling_results.ts#promises?2 ``` --- @@ -70,7 +71,8 @@ Restate then logs the order in which they are resolved or rejected, to make them - Resolves when any of the input promises are resolved. - Rejects when any of the input promises are rejected. - ```typescript focus=1:3,11,12 mark=11,12 + ```ts + CODE_LOAD::ts/src/develop/journaling_results.ts#promises?3 ``` --- @@ -80,8 +82,8 @@ Restate then logs the order in which they are resolved or rejected, to make them Similar to [`Promise.allSettled()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/allSettled), but the outcome is stored in the Restate journal to be deterministically replayable. - - ```typescript focus=1:3,14,15 mark=14,15 + ```ts + CODE_LOAD::ts/src/develop/journaling_results.ts#promises?4 ``` @@ -97,7 +99,7 @@ The SDK provides helper functions for the deterministic generation of UUIDs and Do not use this in cryptographic contexts. ```typescript - CODE_LOAD::ts/src/develop/side_effects.ts#uuid + CODE_LOAD::ts/src/develop/journaling_results.ts#uuid ``` @@ -110,6 +112,6 @@ The SDK provides helper functions for the deterministic generation of UUIDs and This is the equivalent of JS `Math.random()` but deterministically replayable. ```typescript - CODE_LOAD::ts/src/develop/side_effects.ts#random_nb + CODE_LOAD::ts/src/develop/journaling_results.ts#random_nb ``` From 10aceacb653a0309f2a01a298d1a9c628bd8a10e Mon Sep 17 00:00:00 2001 From: Giselle van Dongen Date: Mon, 17 Jun 2024 14:46:57 +0200 Subject: [PATCH 09/18] File rename --- .../java/develop/{SideEffects.java => JournalingResults.java} | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) rename code_snippets/java/src/main/java/develop/{SideEffects.java => JournalingResults.java} (98%) diff --git a/code_snippets/java/src/main/java/develop/SideEffects.java b/code_snippets/java/src/main/java/develop/JournalingResults.java similarity index 98% rename from code_snippets/java/src/main/java/develop/SideEffects.java rename to code_snippets/java/src/main/java/develop/JournalingResults.java index f7635bd1..bbc92e46 100644 --- a/code_snippets/java/src/main/java/develop/SideEffects.java +++ b/code_snippets/java/src/main/java/develop/JournalingResults.java @@ -8,7 +8,7 @@ import java.util.UUID; -class SideEffects { +class JournalingResults { void sideEffect(Context ctx) { From 765f8ed68668d19fec9e571f8d514f1ecbecfa27 Mon Sep 17 00:00:00 2001 From: Giselle van Dongen Date: Mon, 17 Jun 2024 14:51:19 +0200 Subject: [PATCH 10/18] Fix scrollycoding TS workflows --- code_snippets/ts/src/develop/workflows/signup.ts | 12 +++++++++++- docs/develop/ts/workflows.mdx | 13 ++++++++----- 2 files changed, 19 insertions(+), 6 deletions(-) diff --git a/code_snippets/ts/src/develop/workflows/signup.ts b/code_snippets/ts/src/develop/workflows/signup.ts index 5cf57929..07ca5d59 100644 --- a/code_snippets/ts/src/develop/workflows/signup.ts +++ b/code_snippets/ts/src/develop/workflows/signup.ts @@ -6,6 +6,7 @@ import {WorkflowContext} from "@restatedev/restate-sdk"; const signUpWorkflow = restate.workflow({ name: "signup", handlers: { + // run: async (ctx: WorkflowContext, req: { email: string }) => { const secret = ctx.rand.uuidv4(); ctx.set("status", "Generated secret"); @@ -14,24 +15,33 @@ const signUpWorkflow = restate.workflow({ sendEmailWithLink({ email: req.email, secret })); ctx.set("status", "Sent email"); + // const clickSecret = await ctx.promise("email.clicked"); + // ctx.set("status", "Clicked email"); return clickSecret == secret; }, + // + click: (ctx: restate.WorkflowSharedContext, secret: string) => + // ctx.promise("email.clicked").resolve(secret), + // + // getStatus: (ctx: restate.WorkflowSharedContext) => ctx.get("status"), + // }, }); export type SignUpWorkflow = typeof signUpWorkflow; +// restate.endpoint().bind(signUpWorkflow).listen(); -// or .lambdaHandler(); +// // function sendEmailWithLink(param: { email: string, secret: string}){ diff --git a/docs/develop/ts/workflows.mdx b/docs/develop/ts/workflows.mdx index 233d673a..25aac240 100644 --- a/docs/develop/ts/workflows.mdx +++ b/docs/develop/ts/workflows.mdx @@ -33,8 +33,8 @@ Have a look at the code example to get a better understanding of how workflows a This handler has access to the same SDK features as Service and Virtual Object handlers. For example, use [`ctx.run`](/develop/ts/journaling-results#journaled-actions) to log intermediate results in Restate and avoid re-execution on replay. - ```ts signup.ts mark=7:19 - CODE_LOAD::ts/src/develop/workflows/signup.ts + ```ts + CODE_LOAD::ts/src/develop/workflows/signup.ts?1 ``` --- @@ -46,7 +46,8 @@ Have a look at the code example to get a better understanding of how workflows a Every workflow execution can be seen as a new object, so the state is isolated to a single workflow execution. The state can only be mutated by the `run` handler of the workflow. The other handlers can only read the state. - ```ts signup.ts mark=24,25 + ```ts + CODE_LOAD::ts/src/develop/workflows/signup.ts?2 ``` --- @@ -60,7 +61,8 @@ Have a look at the code example to get a better understanding of how workflows a 1. Create a promise in your one handler that is durable and distributed. For example, here in the `run` handler. 2. Resolve or reject the promise in any other handler in the workflow. This can be done at most one time. - ```ts signup.ts mark=15,21,22 + ```ts + CODE_LOAD::ts/src/develop/workflows/signup.ts?3 ``` --- @@ -70,7 +72,8 @@ Have a look at the code example to get a better understanding of how workflows a You serve workflows in the same way as Services and Virtual Objects: by binding them to an [HTTP endpoint](/develop/ts/serving#creating-an-http-endpoint) or [AWS Lambda handler](/develop/ts/serving#creating-a-lambda-handler). Make sure you [register the endpoint or Lambda handler](/operate/registration) in Restate before invoking it. - ```ts signup.ts mark=31,32 + ```ts + CODE_LOAD::ts/src/develop/workflows/signup.ts?4 ``` From 15e317b5299e66ba3eecb6d3ce8fd8c1b3a803bd Mon Sep 17 00:00:00 2001 From: Giselle van Dongen Date: Mon, 17 Jun 2024 15:12:37 +0200 Subject: [PATCH 11/18] Fix async tasks --- .../{fan_out_fan_in.ts => fan_out_worker.ts} | 7 ++- .../simple_async_task/async_task_service.ts | 2 + .../simple_async_task/task_attach.ts | 39 ------------- .../simple_async_task/task_submitter.ts | 12 ++++ .../async_tasks/sync_to_async/client.ts | 15 +++-- .../sync_to_async/data_preparation_service.ts | 8 ++- docs/use-cases/async-tasks.mdx | 55 +++++++++++-------- src/css/custom.css | 2 +- 8 files changed, 70 insertions(+), 70 deletions(-) rename code_snippets/ts/src/use_cases/async_tasks/{fan_out_fan_in.ts => fan_out_worker.ts} (91%) delete mode 100644 code_snippets/ts/src/use_cases/async_tasks/simple_async_task/task_attach.ts diff --git a/code_snippets/ts/src/use_cases/async_tasks/fan_out_fan_in.ts b/code_snippets/ts/src/use_cases/async_tasks/fan_out_worker.ts similarity index 91% rename from code_snippets/ts/src/use_cases/async_tasks/fan_out_fan_in.ts rename to code_snippets/ts/src/use_cases/async_tasks/fan_out_worker.ts index 55299cad..b674cd10 100644 --- a/code_snippets/ts/src/use_cases/async_tasks/fan_out_fan_in.ts +++ b/code_snippets/ts/src/use_cases/async_tasks/fan_out_worker.ts @@ -11,13 +11,17 @@ const workerService = restate.service({ () => split(task)); const resultPromises = []; + // for (const subtask of subtasks) { const subResultPromise = ctx.serviceClient(workerService) .runSubtask(subtask); + // resultPromises.push(subResultPromise); } + // const results = await CombineablePromise.all(resultPromises); + // return aggregate(results); }, @@ -28,8 +32,9 @@ const workerService = restate.service({ } }); +// export const handler = restate.endpoint().bind(workerService).lambdaHandler(); - +// // // ----------------------- Stubs to please the compiler ----------------------- diff --git a/code_snippets/ts/src/use_cases/async_tasks/simple_async_task/async_task_service.ts b/code_snippets/ts/src/use_cases/async_tasks/simple_async_task/async_task_service.ts index 79165582..0db901a6 100644 --- a/code_snippets/ts/src/use_cases/async_tasks/simple_async_task/async_task_service.ts +++ b/code_snippets/ts/src/use_cases/async_tasks/simple_async_task/async_task_service.ts @@ -7,9 +7,11 @@ import {Context} from "@restatedev/restate-sdk"; const asyncTaskService = restate.service({ name: "taskWorker", handlers: { + // runTask: async (ctx: Context, params: TaskOpts) => { return someHeavyWork(params); } + // } }); diff --git a/code_snippets/ts/src/use_cases/async_tasks/simple_async_task/task_attach.ts b/code_snippets/ts/src/use_cases/async_tasks/simple_async_task/task_attach.ts deleted file mode 100644 index 788437fe..00000000 --- a/code_snippets/ts/src/use_cases/async_tasks/simple_async_task/task_attach.ts +++ /dev/null @@ -1,39 +0,0 @@ -import * as restate from "@restatedev/restate-sdk-clients"; -import { SendOpts } from "@restatedev/restate-sdk-clients"; -import {AsyncTaskService, TaskOpts} from "./async_task_service"; - -/* - * Restate is as a sophisticated task queue, with extra features like - * stateful tasks, queues per key, or workflows-as-code within tasks, - * and reliable timers. - * - * Every handler in Restate is executed asynchronously and can be treated - * as a reliable asynchronous task. Restate persists invocations, restarts - * upon failures, reliably queues them under backpressure. - */ - -// ------------------- submit and await tasks like RPC calls ------------------ - -const RESTATE_URL = process.env.RESTATE_URL ?? "http://localhost:8080"; - -// -async function submitAndAwaitTask(task: TaskOpts) { - const rs = restate.connect({ url: RESTATE_URL }); - - const taskHandle = await rs - .serviceSendClient({ name: "taskWorker" }) - .runTask( - task, - SendOpts.from({ idempotencyKey: "dQw4w9WgXcQ" }) - ); - - // await the handler's result - const result = await rs.result(taskHandle); -} - -async function attachToTask(taskHandle: string) { - const rs = restate.connect({ url: RESTATE_URL }); - const result2 = await rs.result(JSON.parse(taskHandle)); -} - -// \ No newline at end of file diff --git a/code_snippets/ts/src/use_cases/async_tasks/simple_async_task/task_submitter.ts b/code_snippets/ts/src/use_cases/async_tasks/simple_async_task/task_submitter.ts index 895e3f43..e9678643 100644 --- a/code_snippets/ts/src/use_cases/async_tasks/simple_async_task/task_submitter.ts +++ b/code_snippets/ts/src/use_cases/async_tasks/simple_async_task/task_submitter.ts @@ -20,15 +20,27 @@ const RESTATE_URL = process.env.RESTATE_URL ?? "http://localhost:8080"; async function submitAndAwaitTask(task: TaskOpts) { const rs = restate.connect({ url: RESTATE_URL }); + // const taskHandle = await rs .serviceSendClient({ name: "taskWorker" }) .runTask( task, + // SendOpts.from({ idempotencyKey: "dQw4w9WgXcQ" }) + // ); + // // await the handler's result + // const result = await rs.result(taskHandle); + // } +// +async function attachToTask(taskHandle: string) { + const rs = restate.connect({ url: RESTATE_URL }); + const result2 = await rs.result(JSON.parse(taskHandle)); +} +// // \ No newline at end of file diff --git a/code_snippets/ts/src/use_cases/async_tasks/sync_to_async/client.ts b/code_snippets/ts/src/use_cases/async_tasks/sync_to_async/client.ts index 6dc0497d..2690c858 100644 --- a/code_snippets/ts/src/use_cases/async_tasks/sync_to_async/client.ts +++ b/code_snippets/ts/src/use_cases/async_tasks/sync_to_async/client.ts @@ -14,20 +14,25 @@ const rs = restate.connect({ url: RESTATE_URL }); const dataPrepService: DataPrepService = { name: "dataPrep" }; async function downloadData(userId: string) { - const workflowId = userId; - - const dataPrep = rs.workflowClient(dataPrepService, workflowId); + // + const dataPrep = rs.workflowClient(dataPrepService, userId); + // + // await dataPrep.workflowSubmit({ userId }); + // + // const result = await withTimeout(dataPrep.workflowAttach(), 30_000); + // + // if (result === Timeout) { - const email = await readLine("This takes long... Just mail us the link later"); + const email = await readLine("This takes long... Mail us the link later"); await dataPrep.resultAsEmail({ email }); return; } - + // // ... process directly ... } // diff --git a/code_snippets/ts/src/use_cases/async_tasks/sync_to_async/data_preparation_service.ts b/code_snippets/ts/src/use_cases/async_tasks/sync_to_async/data_preparation_service.ts index dd70e9d3..9648ee4d 100644 --- a/code_snippets/ts/src/use_cases/async_tasks/sync_to_async/data_preparation_service.ts +++ b/code_snippets/ts/src/use_cases/async_tasks/sync_to_async/data_preparation_service.ts @@ -6,16 +6,22 @@ const dataPreparationService = restate.workflow({ name: "dataPrep", handlers: { run: async (ctx: WorkflowContext, args: { userId: string }) => { + // const url = await ctx.run("create S3 bucket", () => createS3Bucket()); await ctx.run("upload data", () => uploadData(url)); - ctx.promise("url").resolve(url); + // + await ctx.promise("url").resolve(url); + // return url; + // }, resultAsEmail: async (ctx: WorkflowSharedContext, req: { email: string }) => { + // const url = await ctx.promise("url"); + // await ctx.run("send email", () => sendEmail(url, req.email )); } } diff --git a/docs/use-cases/async-tasks.mdx b/docs/use-cases/async-tasks.mdx index 459508cc..86be5598 100644 --- a/docs/use-cases/async-tasks.mdx +++ b/docs/use-cases/async-tasks.mdx @@ -54,16 +54,13 @@ Schedule tasks for now or later with the Restate SDK. Restate handles retries and recovery upon failures. - - ```ts async_task_service.ts focus=1:8 mark=4:6 - CODE_LOAD::ts/src/use_cases/async_tasks/simple_async_task/async_task_service.ts - ``` - - ```ts task_submitter.ts CODE_LOAD::ts/src/use_cases/async_tasks/simple_async_task/task_submitter.ts ``` + ```ts async_task_service.ts active + CODE_LOAD::ts/src/use_cases/async_tasks/simple_async_task/async_task_service.ts?1 + ``` --- @@ -76,7 +73,8 @@ Schedule tasks for now or later with the Restate SDK. Handlers can be called asynchronously from anywhere. This returns a task handle once the call in enqueued. - ```ts task_submitter.ts focus=1:9,13 mark=4:9 + ```ts task_submitter.ts + CODE_LOAD::ts/src/use_cases/async_tasks/simple_async_task/task_submitter.ts?1 ``` --- @@ -87,7 +85,8 @@ Schedule tasks for now or later with the Restate SDK. Restate will deduplicate the request and return the previous response. - ```ts task_submitter.ts focus=1:9,13 mark=8 + ```ts task_submitter.ts + CODE_LOAD::ts/src/use_cases/async_tasks/simple_async_task/task_submitter.ts?2 ``` --- @@ -96,15 +95,16 @@ Schedule tasks for now or later with the Restate SDK. For requests with an idempotency key, you can use this task handle to latch on to the task later and retrieve the result, or wait for it to finish. - ```ts task_submitter.ts focus=1:13 mark=12 + ```ts task_submitter.ts + CODE_LOAD::ts/src/use_cases/async_tasks/simple_async_task/task_submitter.ts?3 ``` --- This works across processes, so you can have a separate process latch on to the task later. - ```ts task_submitter.ts focus=15:18 mark=17 - CODE_LOAD::ts/src/use_cases/async_tasks/simple_async_task/task_attach.ts + ```ts task_submitter.ts + CODE_LOAD::ts/src/use_cases/async_tasks/simple_async_task/task_submitter.ts?4 ``` @@ -121,6 +121,7 @@ Schedule tasks for now or later with the Restate SDK. }, ]}/> + ## Parallelizing work with Restate Write flexible scheduling logic via durable building blocks. @@ -133,7 +134,7 @@ Write flexible scheduling logic via durable building blocks. reliably exactly once. ```ts fan_out_worker.ts - CODE_LOAD::ts/src/use_cases/async_tasks/fan_out_fan_in.ts + CODE_LOAD::ts/src/use_cases/async_tasks/fan_out_worker.ts ``` --- @@ -145,7 +146,8 @@ Write flexible scheduling logic via durable building blocks. The subtasks might run in different processes, if this is deployed in a parallel setup. - ```ts fan_out_worker.ts focus=4:14,18:23 mark=10:12 + ```ts fan_out_worker.ts + CODE_LOAD::ts/src/use_cases/async_tasks/fan_out_worker.ts?1 ``` --- @@ -155,7 +157,8 @@ Write flexible scheduling logic via durable building blocks. Fan in by simply awaiting the combined promise. Invocation promises recover from failures, re-connect to running subtasks. - ```ts fan_out_worker.ts focus=5:17,18:23 mark=16:17 + ```ts fan_out_worker.ts + CODE_LOAD::ts/src/use_cases/async_tasks/fan_out_worker.ts?2 ``` --- @@ -164,7 +167,8 @@ Write flexible scheduling logic via durable building blocks. Deploy this service on an platform like Kubernetes or AWS Lambda to automatically get parallel scale out. - ```ts fan_out_worker.ts mark=27 + ```ts fan_out_worker.ts + CODE_LOAD::ts/src/use_cases/async_tasks/fan_out_worker.ts?3 ``` @@ -179,15 +183,15 @@ Write flexible scheduling logic via durable building blocks. ## Switch between async and sync with Restate - + Imagine a data preparation workflow that creates an S3 bucket, uploads a file to it, and then returns the URL. Let's now kick off this workflow from another process. - ```ts data_preparation_service.ts mark=5:10 - CODE_LOAD::ts/src/use_cases/async_tasks/sync_to_async/data_preparation_service.ts + ```ts data_preparation_service.ts + CODE_LOAD::ts/src/use_cases/async_tasks/sync_to_async/data_preparation_service.ts?1 ``` ```ts client.ts @@ -199,21 +203,24 @@ Write flexible scheduling logic via durable building blocks. 1. Connect to the Restate server and create a client for the data preparation workflow. - ```ts client.ts focus=1:7,18 mark=7 + ```ts client.ts + CODE_LOAD::ts/src/use_cases/async_tasks/sync_to_async/client.ts?1 ``` --- 2. Kick off a new data preparation workflow. This is idempotent per workflow ID. - ```ts client.ts focus=1:9,18 mark=9 + ```ts client.ts + CODE_LOAD::ts/src/use_cases/async_tasks/sync_to_async/client.ts?2 ``` --- 3. Wait for the result for 30 seconds. - ```ts client.ts focus=1:11,18 mark=11 + ```ts client.ts + CODE_LOAD::ts/src/use_cases/async_tasks/sync_to_async/client.ts?3 ``` --- @@ -221,7 +228,8 @@ Write flexible scheduling logic via durable building blocks. 4. If it takes longer, rewire the workflow to send an email instead. If returns within 30 seconds, process the URL directly. - ```ts client.ts focus=1:20 mark=13:19 + ```ts client.ts + CODE_LOAD::ts/src/use_cases/async_tasks/sync_to_async/client.ts?4 ``` --- @@ -229,7 +237,8 @@ Write flexible scheduling logic via durable building blocks. 5. This is implemented in the data preparation workflow by letting the workflow signal our handler when it's done. It does this by resolving a shared Durable Promise that we then retrieve in our handler to send the email. - ```ts data_preparation_client.ts focus=4:16 mark=8,13:16 + ```ts data_preparation_service.ts + CODE_LOAD::ts/src/use_cases/async_tasks/sync_to_async/data_preparation_service.ts?2 ``` diff --git a/src/css/custom.css b/src/css/custom.css index 7737d5de..572dcd75 100644 --- a/src/css/custom.css +++ b/src/css/custom.css @@ -353,7 +353,7 @@ video { } .workflows-scroller-large .ch-codegroup.not-prose { - min-height: 400px; + min-height: 450px; } .ch-scrollycoding-step-content { From c6c59425c91a83fd6d6ce4aef4178223572473cc Mon Sep 17 00:00:00 2001 From: Giselle van Dongen Date: Mon, 17 Jun 2024 15:29:12 +0200 Subject: [PATCH 12/18] Fix scrollycoding workflows --- .../ts/src/use_cases/event_processing.ts | 12 ++++++ .../ts/src/use_cases/events_state.ts | 16 ++++++++ docs/use-cases/event-processing.mdx | 41 +++++++++---------- 3 files changed, 48 insertions(+), 21 deletions(-) diff --git a/code_snippets/ts/src/use_cases/event_processing.ts b/code_snippets/ts/src/use_cases/event_processing.ts index 06876a49..5079ad16 100644 --- a/code_snippets/ts/src/use_cases/event_processing.ts +++ b/code_snippets/ts/src/use_cases/event_processing.ts @@ -4,17 +4,29 @@ import * as restate from "@restatedev/restate-sdk"; const userUpdates = restate.object({ name: "userUpdates", handlers: { + // updateUserEvent: async (ctx: restate.ObjectContext, event: UserUpdate) => { + // const { profile, permissions, resources } = verifyEvent(event); + // let userId = await ctx.run(() => updateProfile(profile)); + // + // while (userId === NOT_READY) { + // await ctx.sleep(5_000); + // + // userId = await ctx.run(() => updateProfile(profile)); + // } + // const roleId = await ctx.run(() => setPermissions(userId, permissions)); await ctx.run(() => provisionResources(userId, roleId, resources)); + // + // }, }, }); diff --git a/code_snippets/ts/src/use_cases/events_state.ts b/code_snippets/ts/src/use_cases/events_state.ts index c2f785b3..b26acf48 100644 --- a/code_snippets/ts/src/use_cases/events_state.ts +++ b/code_snippets/ts/src/use_cases/events_state.ts @@ -16,20 +16,36 @@ import {ObjectContext} from "@restatedev/restate-sdk"; const eventEnricher = restate.object({ name: "profile", handlers: { + // userEvent: async (ctx: ObjectContext, event: UserProfile) => { + // + // ctx.set("user", event); + // + // ctx.objectSendClient(EventEnricher, ctx.key,{ delay: 1000 }).emit(); + // }, + // featureEvent: async (ctx: ObjectContext, featureEvent: string) => { + // + // const userEvent = await ctx.get("user"); + // (userEvent!.features ??= []).push(featureEvent); + // ctx.set("user", userEvent) + // }, + // emit: async (ctx: ObjectContext) => { + // + // send(ctx.key, await ctx.get("user")); ctx.clearAll(); + // } } }) diff --git a/docs/use-cases/event-processing.mdx b/docs/use-cases/event-processing.mdx index 158e6877..c7e60dd9 100644 --- a/docs/use-cases/event-processing.mdx +++ b/docs/use-cases/event-processing.mdx @@ -50,8 +50,8 @@ Restate pushes the events to your function. Write functions that get a the Kafka event as an input. Functions execute with Durable Execution: their progress is tracked and they can be retried from the exact point before the crash, as if you are taking micro-checkpoints throughout the function execution. - ```ts user_updates.ts mark=4 - CODE_LOAD::ts/src/use_cases/event_processing.ts + ```ts user_updates.ts + CODE_LOAD::ts/src/use_cases/event_processing.ts?1 ``` --- @@ -62,8 +62,8 @@ Restate pushes the events to your function. - ```ts user_updates.ts mark=4 - CODE_LOAD::ts/src/use_cases/event_processing.ts + ```ts user_updates.ts + CODE_LOAD::ts/src/use_cases/event_processing.ts?1 ``` --- @@ -87,8 +87,8 @@ Restate pushes the events to your function. In the example, we process user updates in a queue per user. Slow updates for one user do not block updates for other users. - ```ts user_updates.ts mark=4 - CODE_LOAD::ts/src/use_cases/event_processing.ts + ```ts user_updates.ts + CODE_LOAD::ts/src/use_cases/event_processing.ts?1 ``` --- @@ -101,9 +101,8 @@ Restate pushes the events to your function. Here, we postpone processing for 5 seconds if the user profile is not ready yet. - - ```ts user_updates.ts mark=9 - CODE_LOAD::ts/src/use_cases/event_processing.ts + ```ts user_updates.ts + CODE_LOAD::ts/src/use_cases/event_processing.ts?2 ``` --- @@ -112,8 +111,8 @@ Restate pushes the events to your function. The results of interactions with external systems are tracked and recovered after failures. This simplifies writing flows that keep multiple systems in sync. - ```ts user_updates.ts mark=7,10,13:14 - CODE_LOAD::ts/src/use_cases/event_processing.ts + ```ts user_updates.ts + CODE_LOAD::ts/src/use_cases/event_processing.ts?3 ``` --- @@ -124,8 +123,8 @@ Restate pushes the events to your function. Each event craft its own path through the code and builds up its own recovery log. - ```ts user_updates.ts mark=8:14 - CODE_LOAD::ts/src/use_cases/event_processing.ts + ```ts user_updates.ts + CODE_LOAD::ts/src/use_cases/event_processing.ts?4 ``` @@ -154,8 +153,8 @@ Implement stateful event handlers with Restate. Restate guarantees that it is consistent and persistent. The state gets delivered together with the request, so you operate on local state. - ```ts profile_service.ts mark=5,10,12,16,17 - CODE_LOAD::ts/src/use_cases/events_state.ts + ```ts profile_service.ts + CODE_LOAD::ts/src/use_cases/events_state.ts?1 ``` --- @@ -164,8 +163,8 @@ Implement stateful event handlers with Restate. Enrich events with data from multiple sources by storing it in state and eventually exposing it via other functions. - ```ts profile_service.ts mark=4,9,15 - CODE_LOAD::ts/src/use_cases/events_state.ts + ```ts profile_service.ts + CODE_LOAD::ts/src/use_cases/events_state.ts?2 ``` --- @@ -176,8 +175,8 @@ Implement stateful event handlers with Restate. Here, we wait one second for other user features to arrive before sending the event to downstream processing. - ```ts profile_service.ts mark=6 - CODE_LOAD::ts/src/use_cases/events_state.ts + ```ts profile_service.ts + CODE_LOAD::ts/src/use_cases/events_state.ts?3 ``` --- @@ -187,8 +186,8 @@ Implement stateful event handlers with Restate. Functions can be called over RPC or Kafka without changing the code. In the example, the registration can come over Kafka, while the email gets called via HTTP. - ```ts profile_service.ts mark=4,9,15 - CODE_LOAD::ts/src/use_cases/events_state.ts + ```ts profile_service.ts + CODE_LOAD::ts/src/use_cases/events_state.ts?2 ``` From df2233e9de04f45d31040c62aac595a43f1346c4 Mon Sep 17 00:00:00 2001 From: Giselle van Dongen Date: Mon, 17 Jun 2024 15:35:32 +0200 Subject: [PATCH 13/18] Fix scrollycoding workflows use cases --- .../ts/src/use_cases/role_updater.ts | 14 +++++++++++++- docs/use-cases/microservice-orchestration.mdx | 19 ++++++++----------- 2 files changed, 21 insertions(+), 12 deletions(-) diff --git a/code_snippets/ts/src/use_cases/role_updater.ts b/code_snippets/ts/src/use_cases/role_updater.ts index f145cc84..5db68a51 100644 --- a/code_snippets/ts/src/use_cases/role_updater.ts +++ b/code_snippets/ts/src/use_cases/role_updater.ts @@ -13,20 +13,31 @@ import * as restate from "@restatedev/restate-sdk"; import {Context} from "@restatedev/restate-sdk"; // +// const roleUpdater = restate.object({ + // name: "roleUpdate", handlers: { - updateRole: async function applyRoleUpdate(ctx: Context, update: UpdateRequest) { + // + // + update: async function (ctx: Context, update: UpdateRequest) { + // + // const { userId, role, permissions: permissions } = update; + // const previousRole = await ctx.run(() => getCurrentRole(userId)); await ctx.run(() => applyUserRole(userId, role)); + // const previousPermissions: Permission[] = []; for (const permission of permissions) { + // try { + // const previous = await ctx.run(() => applyPermission(userId, permission)); + // previousPermissions.push(previous); } catch (err) { if (err instanceof restate.TerminalError) { @@ -34,6 +45,7 @@ const roleUpdater = restate.object({ } throw err; } + // } } }, diff --git a/docs/use-cases/microservice-orchestration.mdx b/docs/use-cases/microservice-orchestration.mdx index 41f206c4..9ace136b 100644 --- a/docs/use-cases/microservice-orchestration.mdx +++ b/docs/use-cases/microservice-orchestration.mdx @@ -47,9 +47,8 @@ Turn functions into durable handlers with the Restate SDK. Work with objects, functions, and promises as if failures don’t happen. Restate makes them distributed and durable out-of-the-box. - - ```ts role_updater.ts mark=1,4 - CODE_LOAD::ts/src/use_cases/role_updater.ts + ```ts role_updater.ts + CODE_LOAD::ts/src/use_cases/role_updater.ts?1 ``` --- @@ -63,8 +62,8 @@ Turn functions into durable handlers with the Restate SDK. Other updates to the same user are queued and processed in order. Updates either succeed or fail, but never leave the user in an inconsistent state. - ```ts role_updater.ts mark=4 - CODE_LOAD::ts/src/use_cases/role_updater.ts + ```ts role_updater.ts + CODE_LOAD::ts/src/use_cases/role_updater.ts?2 ``` --- @@ -74,26 +73,24 @@ Turn functions into durable handlers with the Restate SDK. Store results of interaction with external systems or non-deterministic actions. On retries, the action does not get re-executed but the previous result will be returned. - ```ts role_updater.ts mark=7,8,13:14 - CODE_LOAD::ts/src/use_cases/role_updater.ts + ```ts role_updater.ts + CODE_LOAD::ts/src/use_cases/role_updater.ts?3 ``` --- - ### Sagas and rollbacks Implement compensation logic with Restate’s durable building blocks and Restate takes care of running it till completion. Here, we update the user’s role in multiple systems. If one fails, we rollback the changes in the other systems. - ```ts role_updater.ts mark=12:16,18 - CODE_LOAD::ts/src/use_cases/role_updater.ts + ```ts role_updater.ts + CODE_LOAD::ts/src/use_cases/role_updater.ts?4 ``` -

Proxy RPC calls to other services via Restate and get durability and idempotency for free.

From 3dc2548532b28bf7c102d21a550a4fe56a592000 Mon Sep 17 00:00:00 2001 From: Giselle van Dongen Date: Mon, 17 Jun 2024 15:48:30 +0200 Subject: [PATCH 14/18] Fix scrollycoding workflows use cases page --- .../main/java/use_cases/SignupWorkflow.java | 7 ---- .../ts/src/use_cases/signup_workflow.ts | 32 +++++++++++++++---- docs/use-cases/workflows.mdx | 22 ++++++++----- 3 files changed, 40 insertions(+), 21 deletions(-) diff --git a/code_snippets/java/src/main/java/use_cases/SignupWorkflow.java b/code_snippets/java/src/main/java/use_cases/SignupWorkflow.java index 07ae31dd..14a6a373 100644 --- a/code_snippets/java/src/main/java/use_cases/SignupWorkflow.java +++ b/code_snippets/java/src/main/java/use_cases/SignupWorkflow.java @@ -77,13 +77,6 @@ public void rejectEmail(SharedWorkflowContext ctx){ ctx.promiseHandle(EMAIL_LINK).reject("Abort verification"); } // - - public static void main(String[] args) { - RestateHttpEndpointBuilder - .builder() - .bind(new SignupWorkflow()) - .buildAndListen(); - } } // diff --git a/code_snippets/ts/src/use_cases/signup_workflow.ts b/code_snippets/ts/src/use_cases/signup_workflow.ts index 4bf57138..1c6b8a96 100644 --- a/code_snippets/ts/src/use_cases/signup_workflow.ts +++ b/code_snippets/ts/src/use_cases/signup_workflow.ts @@ -2,37 +2,57 @@ import * as restate from "@restatedev/restate-sdk"; import {TerminalError, WorkflowContext, WorkflowSharedContext} from "@restatedev/restate-sdk"; // +// const signUpWorkflow = restate.workflow({ name: "sign-up-workflow", handlers: { run: async (ctx: WorkflowContext, user: User) => { + // const { id, name, email } = user; + // ctx.set("stage", "Creating User"); + // + // await ctx.run(() => createUserEntry({ id, name })); + // + // ctx.set("stage", "Email Verification"); + // + // const secret = ctx.rand.uuidv4(); await ctx.run(() => sendEmailWithLink({ email, secret })); + // + // const clickSecret = await ctx.promise("email-link"); + // + // if (clickSecret !== secret) { + // ctx.set("stage", `Verification failed`); + // throw new TerminalError("Wrong secret from email link"); } + // ctx.set("stage", "User verified"); + // return true; + // }, + // getStage: (ctx: WorkflowSharedContext) => ctx.get("stage"), + // - approveEmail: async (ctx: WorkflowSharedContext, secret: string) => { - await ctx.promise("email-link").resolve(secret); - }, + // + approveEmail: (ctx: WorkflowSharedContext, secret: string) => + ctx.promise("email-link").resolve(secret), - rejectEmail: async (ctx: WorkflowSharedContext) => { - await ctx.promise("email-link").reject("Abort verification"); - }, + rejectEmail: (ctx: WorkflowSharedContext) => + ctx.promise("email-link").reject("Abort verification"), + // } }); // diff --git a/docs/use-cases/workflows.mdx b/docs/use-cases/workflows.mdx index e5894480..727c66cf 100644 --- a/docs/use-cases/workflows.mdx +++ b/docs/use-cases/workflows.mdx @@ -54,8 +54,8 @@ Implement the `run` function of your workflow, using the Restate SDK. A workflow runs exactly one time. Restate makes sure that duplicate requests do not lead to duplicate execution. - ```ts user_sign_up_flow.ts mark=1:4 - CODE_LOAD::ts/src/use_cases/signup_workflow.ts + ```ts user_sign_up_flow.ts + CODE_LOAD::ts/src/use_cases/signup_workflow.ts?1 ``` --- @@ -66,7 +66,8 @@ Implement the `run` function of your workflow, using the Restate SDK. Restate makes sure that on retries, the code does not get re-executed and the previous result is returned. This lets you execute the steps of your workflows durably. - ```ts user_sign_up_flow.ts mark=8,11,12 + ```ts user_sign_up_flow.ts + CODE_LOAD::ts/src/use_cases/signup_workflow.ts?2 ``` --- @@ -76,7 +77,8 @@ Implement the `run` function of your workflow, using the Restate SDK. Use Restate’s built-in key-value store to store workflow state. Restate guarantees that it is consistent and persistent, since state updates are tracked together with the rest of the execution progress. - ```ts user_sign_up_flow.ts mark=7,10,16,19 + ```ts user_sign_up_flow.ts + CODE_LOAD::ts/src/use_cases/signup_workflow.ts?3 ``` --- @@ -85,7 +87,8 @@ Implement the `run` function of your workflow, using the Restate SDK. Retrieve the current state of the workflow from within other handlers and expose it to external clients. - ```ts user_sign_up_flow.ts mark=23 + ```ts user_sign_up_flow.ts + CODE_LOAD::ts/src/use_cases/signup_workflow.ts?4 ``` --- @@ -95,7 +98,8 @@ Implement the `run` function of your workflow, using the Restate SDK. Make Promises/Futures resilient by registering them in Restate. Share them and wait until other functions resolve them. - ```ts user_sign_up_flow.ts mark=14 + ```ts user_sign_up_flow.ts + CODE_LOAD::ts/src/use_cases/signup_workflow.ts?5 ``` --- @@ -105,7 +109,8 @@ Implement the `run` function of your workflow, using the Restate SDK. Notify the workflow of external signals, callbacks or Kafka events. Resolve or reject shared promises on which the workflow is waiting. The workflow handles the outcome. - ```ts user_sign_up_flow.ts mark=25:31 + ```ts user_sign_up_flow.ts + CODE_LOAD::ts/src/use_cases/signup_workflow.ts?6 ``` --- @@ -114,7 +119,8 @@ Implement the `run` function of your workflow, using the Restate SDK. Implement sagas and compensations in code, as per your requirements. - ```ts user_sign_up_flow.ts mark=15:20 + ```ts user_sign_up_flow.ts + CODE_LOAD::ts/src/use_cases/signup_workflow.ts?7 ``` From 378520a2aec6568943b99f2070ee226db6e58db9 Mon Sep 17 00:00:00 2001 From: Giselle van Dongen Date: Tue, 18 Jun 2024 14:11:31 +0200 Subject: [PATCH 15/18] Fix awakeables to not rely on line numbers --- .../src/main/java/develop/Awakeables.java | 46 ++-- .../src/main/kotlin/develop/Awakeables.kt | 10 +- .../{SideEffects.kt => JournalingResults.kt} | 1 - docs/develop/java/awakeables.mdx | 212 ++++++++++-------- docs/develop/java/journaling-results.mdx | 20 +- src/plugins/code-loader.js | 15 +- 6 files changed, 175 insertions(+), 129 deletions(-) rename code_snippets/kotlin/src/main/kotlin/develop/{SideEffects.kt => JournalingResults.kt} (97%) diff --git a/code_snippets/java/src/main/java/develop/Awakeables.java b/code_snippets/java/src/main/java/develop/Awakeables.java index d7074381..fbe66ebf 100644 --- a/code_snippets/java/src/main/java/develop/Awakeables.java +++ b/code_snippets/java/src/main/java/develop/Awakeables.java @@ -6,26 +6,32 @@ public class Awakeables { - public void awakeables(ObjectContext ctx) { - // - Awakeable awakeable = ctx.awakeable(JsonSerdes.STRING); - String awakeableId = awakeable.id(); - - ctx.run(() -> triggerTaskAndDeliverId(awakeableId)); - - String payload = awakeable.await(); - // - - // - ctx.awakeableHandle(awakeableId) - .resolve(JsonSerdes.STRING, "hello"); - // - - // - ctx.awakeableHandle(awakeableId) - .reject("my error reason"); - // - } +public void awakeables(ObjectContext ctx) { + // + // + Awakeable awakeable = ctx.awakeable(JsonSerdes.STRING); + String awakeableId = awakeable.id(); + // + + // + ctx.run(() -> triggerTaskAndDeliverId(awakeableId)); + // + + // + String payload = awakeable.await(); + // + // + + // + ctx.awakeableHandle(awakeableId) + .resolve(JsonSerdes.STRING, "hello"); + // + + // + ctx.awakeableHandle(awakeableId) + .reject("my error reason"); + // +} public void triggerTaskAndDeliverId(String awakeableId) { } diff --git a/code_snippets/kotlin/src/main/kotlin/develop/Awakeables.kt b/code_snippets/kotlin/src/main/kotlin/develop/Awakeables.kt index 9001df8c..f656d3c1 100644 --- a/code_snippets/kotlin/src/main/kotlin/develop/Awakeables.kt +++ b/code_snippets/kotlin/src/main/kotlin/develop/Awakeables.kt @@ -4,14 +4,20 @@ import dev.restate.sdk.kotlin.* class Awakeables { suspend fun awakeables(ctx: ObjectContext) { - // + // + // val awakeable = ctx.awakeable() val awakeableId: String = awakeable.id + // + // ctx.runBlock{ triggerTaskAndDeliverId(awakeableId) } + // + // val payload: String = awakeable.await() - // + // + // // diff --git a/code_snippets/kotlin/src/main/kotlin/develop/SideEffects.kt b/code_snippets/kotlin/src/main/kotlin/develop/JournalingResults.kt similarity index 97% rename from code_snippets/kotlin/src/main/kotlin/develop/SideEffects.kt rename to code_snippets/kotlin/src/main/kotlin/develop/JournalingResults.kt index 64c26fd8..85729db2 100644 --- a/code_snippets/kotlin/src/main/kotlin/develop/SideEffects.kt +++ b/code_snippets/kotlin/src/main/kotlin/develop/JournalingResults.kt @@ -3,7 +3,6 @@ package develop import dev.restate.sdk.common.TerminalException import dev.restate.sdk.kotlin.* import java.util.* -import kotlin.time.Duration.Companion.seconds internal class SideEffects { suspend fun sideEffect(ctx: Context) { diff --git a/docs/develop/java/awakeables.mdx b/docs/develop/java/awakeables.mdx index 722dc27d..798129d6 100644 --- a/docs/develop/java/awakeables.mdx +++ b/docs/develop/java/awakeables.mdx @@ -13,118 +13,152 @@ Awakeables pause an invocation while waiting for another process to complete a t You can use this pattern to let a handler execute a task somewhere else and retrieve the result. This pattern is also known as the callback (task token) pattern. +## Creating awakeables - - - - ```java MyHandler.java - CODE_LOAD::java/src/main/java/develop/Awakeables.java#create - ``` - + + - 1. [The handler **creates an awakeable**](focus://1,2). This contains a [String identifier](focus://2) and a Promise/Awaitable. - 2. [The handler **triggers a task/process** and attaches the awakeable ID](focus://4) (e.g. over Kafka, via HTTP,...). - For example, send an HTTP request to a service that executes the task, and attach the ID to the payload. - You use `ctx.run` to avoid retriggering the task this on retries. - 3. [The handler **waits** until the other process has executed the task.](focus://6) - - - 4. **The external process completes the awakeable** when the task is finished by: - - - ```shell resolve_with_curl.sh - curl localhost:8080/restate/awakeables/prom_1PePOqp/resolve -H 'content-type: application/json' - -d '{"hello": "world"}' - ``` - - ```shell reject_with_curl.sh - curl localhost:8080/restate/awakeables/prom_1PePOqp/reject -H 'content-type: text/plain' \ - -d 'Very bad error!' - ``` - - ```java ExternalProcessResolve.java - CODE_LOAD::java/src/main/java/develop/Awakeables.java#resolve - ``` - - ```java ExternalProcessReject.java - CODE_LOAD::java/src/main/java/develop/Awakeables.java#reject - ``` - - - **Resolves** the awakeable - - [Over HTTP](focus://resolve_with_curl.sh#1:2) with [its ID](focus://resolve_with_curl.sh#1[40:52]) and [an optional payload](focus://resolve_with_curl.sh#2) - - [Via the SDK with its ID and an optional payload](focus://ExternalProcessResolve.java#1:2) - - **Rejects** the awakeable with its ID and a reason: failure. This throws [a terminal error](/develop/java/error-handling) in the waiting handler. - - [Over HTTP](focus://reject_with_curl.sh#1:2) with [its ID](focus://reject_with_curl.sh#1[40:52]) and [an optional payload](focus://reject_with_curl.sh#2) - - [Via the SDK with its ID and an optional payload](focus://ExternalProcessReject.java#1:2) - - 5. Once the ID has been returned to the service, the **invocation resumes**. + 1. The handler **creates an awakeable**. This contains a String identifier and a Promise/Awaitable. + + ```java + CODE_LOAD::java/src/main/java/develop/Awakeables.java?1 + ``` + + --- + + 2. The handler **triggers a task/process** and attaches the awakeable ID (e.g. over Kafka, via HTTP,...). + For example, send an HTTP request to a service that executes the task, and attach the ID to the payload. + You use `ctx.run` to avoid retriggering the task this on retries. + + ```java + CODE_LOAD::java/src/main/java/develop/Awakeables.java?2 + ``` + + --- + 3. The handler **waits** until the other process has executed the task. + The handler **receives the payload and resumes**. + ```java + CODE_LOAD::java/src/main/java/develop/Awakeables.java?3 + ``` + + - - - ```kotlin MyHandler.kt - CODE_LOAD::kotlin/src/main/kotlin/develop/Awakeables.kt#create - ``` - - - 1. [The handler **creates an awakeable**](focus://1,2). This contains a [String identifier](focus://2) and a Promise/Awaitable. - 2. [The handler **triggers a task/process** and attaches the awakeable ID](focus://4) (e.g. over Kafka, via HTTP,...). - For example, send an HTTP request to a service that executes the task, and attach the ID to the payload. - You use `ctx.run` to avoid retriggering the task this on retries. - 3. [The handler **waits** until the other process has executed the task.](focus://6) - - - 4. **The external process completes the awakeable** when the task is finished by: - - - - ```shell resolve_curl.sh - curl localhost:8080/restate/awakeables/prom_1PePOqp/resolve -H 'content-type: application/json' - -d '{"hello": "world"}' - ``` - - ```shell reject_curl.sh - curl localhost:8080/restate/awakeables/prom_1PePOqp/reject -H 'content-type: text/plain' \ - -d 'Very bad error!' - ``` - - ```kotlin ExternalProcessResolve.kt - CODE_LOAD::kotlin/src/main/kotlin/develop/Awakeables.kt#resolve - ``` - - ```kotlin ExternalProcessReject.kt - CODE_LOAD::kotlin/src/main/kotlin/develop/Awakeables.kt#reject - ``` - - - **Resolving** the awakeable - - [Over HTTP](focus://resolve_curl.sh#1:2) with [its ID](focus://resolve_curl.sh#1[40:52]) and [an optional payload](focus://resolve_curl.sh#2) - - [Via the SDK with its ID and an optional payload](focus://ExternalProcessResolve.kt#1:2) - - **Rejecting** the awakeable. This throws [a terminal error](/develop/java/error-handling) in the waiting handler. - - [Over HTTP](focus://reject_curl.sh#1:2) with [its ID](focus://reject_curl.sh#1[40:52]) and [a failure reason](focus://reject_curl.sh#2) - - [Via the SDK with its ID and a failure reason](focus://ExternalProcessReject.kt#1:2) - - 5. The handler **receives the payload and resumes**. + + + 1. The handler **creates an awakeable**. This contains a String identifier and a Promise/Awaitable. + + ```kotlin + CODE_LOAD::kotlin/src/main/kotlin/develop/Awakeables.kt?1 + ``` + + --- + + 2. The handler **triggers a task/process** and attaches the awakeable ID (e.g. over Kafka, via HTTP,...). + For example, send an HTTP request to a service that executes the task, and attach the ID to the payload. + You use `ctx.runBlock` to avoid retriggering the task this on retries. + + ```kotlin + CODE_LOAD::kotlin/src/main/kotlin/develop/Awakeables.kt?2 + ``` + + --- + + 3. The handler **waits** until the other process has executed the task. + The handler **receives the payload and resumes**. + + ```kotlin + CODE_LOAD::kotlin/src/main/kotlin/develop/Awakeables.kt?3 + ``` + + +## Completing awakeables + +The external process completes the awakeable by either resolving it with an optional payload or by rejecting it +with its ID and a reason for the failure. This throws [a terminal error](/develop/java/error-handling) in the waiting handler. + +- Resolving over HTTP with its ID and an optional payload: + +```shell +curl localhost:8080/restate/awakeables/prom_1PePOqp/resolve + -H 'content-type: application/json' + -d '{"hello": "world"}' +``` + + +- Rejecting over HTTP with its ID and a reason: + +```shell +curl localhost:8080/restate/awakeables/prom_1PePOqp/reject + -H 'content-type: text/plain' \ + -d 'Very bad error!' +``` + + +- Resolving via the SDK with its ID and an optional payload: + +```java +CODE_LOAD::java/src/main/java/develop/Awakeables.java#resolve +``` + + +- Rejecting via the SDK with its ID and a reason: + +```java +CODE_LOAD::java/src/main/java/develop/Awakeables.java#reject +``` + For primitive types, you can use the Restate SDK's `CoreSerdes`. For other types, have a look at the [serialization docs](/develop/java/serialization). - + + - Resolving over HTTP with its ID and an optional payload: + + ```shell + curl localhost:8080/restate/awakeables/prom_1PePOqp/resolve + -H 'content-type: application/json' + -d '{"hello": "world"}' + ``` + + + - Rejecting over HTTP with its ID and a reason: + + ```shell + curl localhost:8080/restate/awakeables/prom_1PePOqp/reject + -H 'content-type: text/plain' \ + -d 'Very bad error!' + ``` + + + - Resolving via the SDK with its ID and an optional payload: + + ```kotlin + CODE_LOAD::kotlin/src/main/kotlin/develop/Awakeables.kt#resolve + ``` + + + - Rejecting via the SDK with its ID and a reason: + + ```kotlin + CODE_LOAD::kotlin/src/main/kotlin/develop/Awakeables.kt#reject + ``` + By default JSON is used to serialize payloads, using Kotlin serialization. For other types, have a look at the [serialization docs](/develop/java/serialization). - diff --git a/docs/develop/java/journaling-results.mdx b/docs/develop/java/journaling-results.mdx index 056364b0..7224466d 100644 --- a/docs/develop/java/journaling-results.mdx +++ b/docs/develop/java/journaling-results.mdx @@ -28,7 +28,7 @@ Here is an example of a database request for which the string response is stored ```java -CODE_LOAD::java/src/main/java/develop/SideEffects.java#side_effect +CODE_LOAD::java/src/main/java/develop/JournalingResults.java#side_effect ``` You can use Restate's built-in `CoreSerdes` to serialize primitive types. @@ -38,7 +38,7 @@ Have a look at the [serialization docs](/develop/java/serialization) for other o ```kotlin -CODE_LOAD::kotlin/src/main/kotlin/develop/SideEffects.kt#side_effect +CODE_LOAD::kotlin/src/main/kotlin/develop/JournalingResults.kt#side_effect ``` By default, Kotlin serialization is used to serialize the result. @@ -67,7 +67,7 @@ Restate then logs the order in which they are resolved or rejected, to make them The semantics are similar to [`CompleteableFuture.allOf()`](https://docs.oracle.com/en/java/javase/17/docs/api/java.base/java/util/concurrent/CompletableFuture.html#allOf(java.util.concurrent.CompletableFuture...)), but the outcome is stored in the Restate journal to be deterministically replayable. ```java - CODE_LOAD::java/src/main/java/develop/SideEffects.java#combine_all + CODE_LOAD::java/src/main/java/develop/JournalingResults.java#combine_all ``` @@ -75,7 +75,7 @@ Restate then logs the order in which they are resolved or rejected, to make them The semantics are similar to [`CompleteableFuture.anyOf()`](https://docs.oracle.com/en/java/javase/17/docs/api/java.base/java/util/concurrent/CompletableFuture.html#anyOf(java.util.concurrent.CompletableFuture...)), but the outcome is stored in the Restate journal to be deterministically replayable. ```java - CODE_LOAD::java/src/main/java/develop/SideEffects.java#combine_any + CODE_LOAD::java/src/main/java/develop/JournalingResults.java#combine_any ``` @@ -88,7 +88,7 @@ Restate then logs the order in which they are resolved or rejected, to make them ```kotlin - CODE_LOAD::kotlin/src/main/kotlin/develop/SideEffects.kt#combine_all + CODE_LOAD::kotlin/src/main/kotlin/develop/JournalingResults.kt#combine_all ``` @@ -99,7 +99,7 @@ Restate then logs the order in which they are resolved or rejected, to make them The semantics are similar to [`CompleteableFuture.anyOf()`](https://docs.oracle.com/en/java/javase/17/docs/api/java.base/java/util/concurrent/CompletableFuture.html#anyOf(java.util.concurrent.CompletableFuture...)), but the outcome is stored in the Restate journal to be deterministically replayable. ```kotlin - CODE_LOAD::kotlin/src/main/kotlin/develop/SideEffects.kt#combine_any + CODE_LOAD::kotlin/src/main/kotlin/develop/JournalingResults.kt#combine_any ``` @@ -122,7 +122,7 @@ Restate seeds the random number generator with the invocation ID, so it always r Do not use this in cryptographic contexts. ```java - CODE_LOAD::java/src/main/java/develop/SideEffects.java#uuid + CODE_LOAD::java/src/main/java/develop/JournalingResults.java#uuid ``` @@ -136,7 +136,7 @@ Restate seeds the random number generator with the invocation ID, so it always r You can use any of the methods of [`java.util.Random`](https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/util/Random.html) to generate random numbers: for example, `nextBoolean()`, `nextLong()`, `nextFloat()`, etc. ```java - CODE_LOAD::java/src/main/java/develop/SideEffects.java#random_nb + CODE_LOAD::java/src/main/java/develop/JournalingResults.java#random_nb ``` @@ -152,7 +152,7 @@ Restate seeds the random number generator with the invocation ID, so it always r Do not use this in cryptographic contexts. ```kotlin - CODE_LOAD::kotlin/src/main/kotlin/develop/SideEffects.kt#uuid + CODE_LOAD::kotlin/src/main/kotlin/develop/JournalingResults.kt#uuid ``` @@ -166,7 +166,7 @@ Restate seeds the random number generator with the invocation ID, so it always r You can use any of the methods of [`java.util.Random`](https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/util/Random.html) to generate random numbers: for example, `nextBoolean()`, `nextLong()`, `nextFloat()`, etc. ```kotlin - CODE_LOAD::kotlin/src/main/kotlin/develop/SideEffects.kt#random_nb + CODE_LOAD::kotlin/src/main/kotlin/develop/JournalingResults.kt#random_nb ``` diff --git a/src/plugins/code-loader.js b/src/plugins/code-loader.js index d25fef29..ee10ee24 100644 --- a/src/plugins/code-loader.js +++ b/src/plugins/code-loader.js @@ -51,7 +51,6 @@ const plugin = (options) => { } let finalLines = []; - if (markNumber) { const markStartTag = `// `; const markEndTag = `// `; @@ -82,13 +81,15 @@ const plugin = (options) => { finalLines = lines; } - const cleanedLines = finalLines.filter(line => { - return !line.includes(' { + return !line.includes(' line.replace(new RegExp(`^${leadingWhitespace}`), '')) .join('\n'); } From cd7243acbe650f82b52a1377078da3f6efb0550f Mon Sep 17 00:00:00 2001 From: Giselle van Dongen Date: Tue, 18 Jun 2024 14:18:46 +0200 Subject: [PATCH 16/18] Remove reliance on line numbers from service comm and serving Java SDK docs --- .../java/develop/ServiceCommunication.java | 4 +- docs/develop/java/service-communication.mdx | 14 +-- docs/develop/java/serving.mdx | 97 +++++++------------ 3 files changed, 43 insertions(+), 72 deletions(-) diff --git a/code_snippets/java/src/main/java/develop/ServiceCommunication.java b/code_snippets/java/src/main/java/develop/ServiceCommunication.java index ea0450b4..c771435b 100644 --- a/code_snippets/java/src/main/java/develop/ServiceCommunication.java +++ b/code_snippets/java/src/main/java/develop/ServiceCommunication.java @@ -68,8 +68,8 @@ private void delayedCall(Context ctx) { private void orderingGuarantees(Context ctx){ String objectKey = ""; // - MyVirtualObjectClient.fromContext(ctx, objectKey).send().myHandler("Hi!"); - MyVirtualObjectClient.fromContext(ctx, objectKey).send().myHandler("Hi again!"); + MyVirtualObjectClient.fromContext(ctx, objectKey).send().myHandler("I'm call A"); + MyVirtualObjectClient.fromContext(ctx, objectKey).send().myHandler("I'm call B"); // } } diff --git a/docs/develop/java/service-communication.mdx b/docs/develop/java/service-communication.mdx index 7ff9b27c..10870686 100644 --- a/docs/develop/java/service-communication.mdx +++ b/docs/develop/java/service-communication.mdx @@ -155,19 +155,15 @@ Restate will make sure the task gets executed at the desired time. - Invocations to a Virtual Object are executed serially. Invocations will execute in the same order in which they arrive at Restate. For example, assume the following code in `ServiceA`: - - ```java - CODE_LOAD::java/src/main/java/develop/ServiceCommunication.java#ordering - ``` - + ```java + CODE_LOAD::java/src/main/java/develop/ServiceCommunication.java#ordering + ``` - It is guaranteed that [the invocation on line `1`](focus://1) will execute before [the invocation on line `2`](focus://2). - It is not guaranteed though that [invocation `2`](focus://2) will be executed immediately after [invocation `1`](focus://1), as invocations coming from other handlers/sources, could interleave these two calls. - + It is guaranteed that call A will execute before call B. + It is not guaranteed though that call B will be executed immediately after call A, as invocations coming from other handlers/sources, could interleave these two calls. diff --git a/docs/develop/java/serving.mdx b/docs/develop/java/serving.mdx index 64a40fd6..adaa18fc 100644 --- a/docs/develop/java/serving.mdx +++ b/docs/develop/java/serving.mdx @@ -14,72 +14,47 @@ Restate services can run in two ways: as an HTTP endpoint or as AWS Lambda funct - - - - ```java - CODE_LOAD::java/src/main/java/develop/ServingHttp.java - ``` - - - 1. [Create a `RestateHttpEndpointBuilder`](focus://5) - 2. [Bind one or multiple services to it](focus://6:8) - 3. [Listen on the specified port (default `9080`) for connections and requests.](focus://9) - - - - - - - - - ```kotlin - CODE_LOAD::kotlin/src/main/kotlin/develop/ServingHttp.kt - ``` - - - 1. [Create a `RestateHttpEndpointBuilder`](focus://4) - 2. [Bind one or multiple services to it](focus://5:7) - 3. [Listen on the specified port (default `9080`) for connections and requests.](focus://8) - - - - - + 1. Create a `RestateHttpEndpointBuilder` + 2. Bind one or multiple services to it + 3. Listen on the specified port (default `9080`) for connections and requests. + + ```java + CODE_LOAD::java/src/main/java/develop/ServingHttp.java + ``` + + + 1. Create a `RestateHttpEndpointBuilder` + 2. Bind one or multiple services to it + 3. Listen on the specified port (default `9080`) for connections and requests. + + ```kotlin + CODE_LOAD::kotlin/src/main/kotlin/develop/ServingHttp.kt + ``` + - ## Creating a Lambda handler - - - ```java - CODE_LOAD::java/src/main/java/develop/ServingLambda.java - ``` - - - 1. Add the dependency `dev.restate:sdk-lambda:VAR::JAVA_SDK_VERSION`. - 2. [Extend the class `BaseRestateLambdaHandler`](focus://4) - 3. [Override the register method](focus://5:6,10) - 4. [Bind one or multiple services to the builder](focus://7:9) - - - - - - - ```kotlin - CODE_LOAD::kotlin/src/main/kotlin/develop/MyLambdaHandler.kt - ``` - - - 1. Add the dependency `dev.restate:sdk-lambda:VAR::JAVA_SDK_VERSION`. - 2. [Extend the class `BaseRestateLambdaHandler`](focus://4) - 3. [Override the register method](focus://5,9) - 4. [Bind one or multiple services to the builder](focus://6:8) - - + 1. Add the dependency `dev.restate:sdk-lambda:VAR::JAVA_SDK_VERSION`. + 2. Extend the class `BaseRestateLambdaHandler` + 3. Override the register method + 4. Bind one or multiple services to the builder + + ```java + CODE_LOAD::java/src/main/java/develop/ServingLambda.java + ``` + + + 1. Add the dependency `dev.restate:sdk-lambda:VAR::JAVA_SDK_VERSION`. + 2. Extend the class `BaseRestateLambdaHandler` + 3. Override the register method + 4. Bind one or multiple services to the builder + + ```kotlin + CODE_LOAD::kotlin/src/main/kotlin/develop/MyLambdaHandler.kt + ``` + Have a look at the [deployment section](/category/aws--aws-lambda) for guidance on how to deploy your services on AWS Lambda. From b8807f2ee683b5c254d654aae0b05e0493d97f05 Mon Sep 17 00:00:00 2001 From: Giselle van Dongen Date: Tue, 18 Jun 2024 14:35:39 +0200 Subject: [PATCH 17/18] Clean up Java SDK docs to not reference code line numbers --- .../java/src/main/java/develop/MyService.java | 1 - .../src/main/kotlin/develop/MyService.kt | 1 - docs/develop/java/overview.mdx | 116 +++++++----------- docs/develop/java/state.mdx | 80 +++++------- 4 files changed, 75 insertions(+), 123 deletions(-) diff --git a/code_snippets/java/src/main/java/develop/MyService.java b/code_snippets/java/src/main/java/develop/MyService.java index 9febdde3..9a04edf0 100644 --- a/code_snippets/java/src/main/java/develop/MyService.java +++ b/code_snippets/java/src/main/java/develop/MyService.java @@ -8,7 +8,6 @@ // @Service public class MyService { - @Handler public String myHandler(Context ctx, String input) { return "my-output"; diff --git a/code_snippets/kotlin/src/main/kotlin/develop/MyService.kt b/code_snippets/kotlin/src/main/kotlin/develop/MyService.kt index f84de0f5..891ea7fa 100644 --- a/code_snippets/kotlin/src/main/kotlin/develop/MyService.kt +++ b/code_snippets/kotlin/src/main/kotlin/develop/MyService.kt @@ -8,7 +8,6 @@ import dev.restate.sdk.kotlin.Context // @Service class MyService { - @Handler suspend fun myHandler(ctx: Context, input: String): String { return "my-output" diff --git a/docs/develop/java/overview.mdx b/docs/develop/java/overview.mdx index 625ffb16..24a2a656 100644 --- a/docs/develop/java/overview.mdx +++ b/docs/develop/java/overview.mdx @@ -24,44 +24,32 @@ Handlers can either be part of a [Service](/concepts/services#services-1), a [Vi - + ```java + CODE_LOAD::java/src/main/java/develop/MyService.java + ``` - - ```java MyService.java - CODE_LOAD::java/src/main/java/develop/MyService.java - ``` - -[//]: # (This code snippet is also used in the communication page, to show how to export the definition) - - - - [Use the `@Service` and `@Handler` annotations](focus://1,4) - - [Handlers have the `Context` parameter](focus://5[22:32]) ([JavaDocs](https://javadoc.io/doc/dev.restate/sdk-api/latest/dev/restate/sdk/Context.html)) as the first parameter. + - Use the `@Service` and `@Handler` annotations + - Handlers have the `Context` parameter ([JavaDocs](https://javadoc.io/doc/dev.restate/sdk-api/latest/dev/restate/sdk/Context.html)) as the first parameter. Within the handler, you use the `Context` to interact with Restate. The SDK stores the actions you do on the context in the Restate journal to make them durable. - The input parameter and return type are optional and can be of any type, as long as they are serializable/deserializable using [Jackson Databind](https://github.com/FasterXML/jackson) ([see serialization docs](/develop/java/serialization)). - The service will be reachable under the simple class name `MyService`. You can override it by using the annotation field `name`. - - [Create an endpoint](focus://10) and [bind the service(s)](focus://11) to the Restate endpoint. [Listen](focus://12) on the specified port (default `9080`) for connections and requests. - - + - Create an endpoint and bind the service(s) to the Restate endpoint. Listen on the specified port (default `9080`) for connections and requests. - - - ```kotlin MyService.kt - CODE_LOAD::kotlin/src/main/kotlin/develop/MyService.kt - ``` - + ```kotlin + CODE_LOAD::kotlin/src/main/kotlin/develop/MyService.kt + ``` - - [Use the `@Service` and `@Handler` annotations](focus://1,4) - - [Handlers have the `Context` parameter](focus://5[27:38]) ([JavaDocs](https://javadoc.io/doc/dev.restate/sdk-api/latest/dev/restate/sdk/Context.html)) as the first parameter. + - Use the `@Service` and `@Handler` annotations + - Handlers have the `Context` parameter ([JavaDocs](https://javadoc.io/doc/dev.restate/sdk-api/latest/dev/restate/sdk/Context.html)) as the first parameter. Within the handler, you use the `Context` to interact with Restate. The SDK stores the actions you do on the context in the Restate journal to make them durable. - The input parameter and return type are optional and can be of any type, as long as they are serializable/deserializable using [Kotlin serialization](https://kotlinlang.org/docs/serialization.html) ([see serialization docs](/develop/java/serialization)). - The service will be reachable under the simple class name `MyService`. You can override it by using the annotation field `name`. - - [Create an endpoint](focus://11,12) and [bind the service(s)](focus://13) to the Restate endpoint. [Listen](focus://14) on the specified port (default `9080`) for connections and requests. + - Create an endpoint and bind the service(s) to the Restate endpoint. Listen on the specified port (default `9080`) for connections and requests. - @@ -71,37 +59,28 @@ Handlers can either be part of a [Service](/concepts/services#services-1), a [Vi - - - ```java - CODE_LOAD::java/src/main/java/develop/MyVirtualObject.java - ``` - - - - [Use the `@VirtualObject` annotation.](focus://1) - - [The first argument of the handler must be the `ObjectContext` parameter](focus://5[29:45]) ([JavaDocs](https://javadoc.io/doc/dev.restate/sdk-api/latest/dev/restate/sdk/ObjectContext.html)). + ```java + CODE_LOAD::java/src/main/java/develop/MyVirtualObject.java + ``` + + - Use the `@VirtualObject` annotation. + - The first argument of the handler must be the `ObjectContext` parameter ([JavaDocs](https://javadoc.io/doc/dev.restate/sdk-api/latest/dev/restate/sdk/ObjectContext.html)). Handlers with the `ObjectContext` parameter can write to the K/V state store. Only one handler can be active at a time, to ensure consistency. - - [If you want to have a handler that executes concurrently to the others and doesn't have write access to the K/V state, use the `@Shared` annotation and the `SharedObjectContext`.](focus://9,10[39:61]) + - If you want to have a handler that executes concurrently to the others and doesn't have write access to the K/V state, use the `@Shared` annotation and the `SharedObjectContext`. For example, you can use these handlers to read K/V state, or interact with the blocking handler. - - - - - ```kotlin - CODE_LOAD::kotlin/src/main/kotlin/develop/MyVirtualObject.kt - ``` - - - - [Use the `@VirtualObject` annotation.](focus://1) - - [The first argument of the handler must be the `ObjectContext` parameter](focus://5[27:44]) ([JavaDocs](https://javadoc.io/doc/dev.restate/sdk-api/latest/dev/restate/sdk/ObjectContext.html)). + ```kotlin + CODE_LOAD::kotlin/src/main/kotlin/develop/MyVirtualObject.kt + ``` + + - Use the `@VirtualObject` annotation. + - The first argument of the handler must be the `ObjectContext` parameter ([JavaDocs](https://javadoc.io/doc/dev.restate/sdk-api/latest/dev/restate/sdk/ObjectContext.html)). Handlers with the `ObjectContext` parameter can write to the K/V state store. Only one handler can be active at a time, to ensure consistency. - - [If you want to have a handler that executes concurrently to the others and doesn't have write access to the K/V state, use the `@Shared` annotation and the `SharedObjectContext`.](focus://10,11[37:60]) + - If you want to have a handler that executes concurrently to the others and doesn't have write access to the K/V state, use the `@Shared` annotation and the `SharedObjectContext`. For example, you can use these handlers to read K/V state, or interact with the blocking handler. - @@ -111,40 +90,29 @@ Handlers can either be part of a [Service](/concepts/services#services-1), a [Vi - - - ```java - CODE_LOAD::java/src/main/java/develop/MyWorkflow.java - ``` - - - - [Create the workflow](focus://1,2) - - [Every workflow implementation needs to have a handler called `run` that implements the workflow logic. This handler uses the `WorkflowContext` to interact with the SDK. The `run` handler executes exactly one time per workflow execution/object.](focus://4:10) - - [The other handlers of the workflow are used to interact with the workflow: either query it, or signal it. They use the `SharedWorkflowContext` to interact with the SDK. These handlers can run concurrently with the `run` handler and can still be called after the `run` handler has finished.](focus://12:15) - - Have a look at the workflow docs to learn more. - - + ```java + CODE_LOAD::java/src/main/java/develop/MyWorkflow.java + ``` + + - Create the workflow + - Every workflow implementation needs to have a handler called `run` that implements the workflow logic. This handler uses the `WorkflowContext` to interact with the SDK. The `run` handler executes exactly one time per workflow execution/object. + - The other handlers of the workflow are used to interact with the workflow: either query it, or signal it. They use the `SharedWorkflowContext` to interact with the SDK. These handlers can run concurrently with the `run` handler and can still be called after the `run` handler has finished. + - [Have a look at the workflow docs to learn more.](/develop/java/workflows) - - - ```kotlin - CODE_LOAD::kotlin/src/main/kotlin/develop/MyWorkflow.kt - ``` - - - - [Create the workflow](focus://1,2) - - [Every workflow implementation needs to have a handler called `run` that implements the workflow logic. This handler uses the `WorkflowContext` to interact with the SDK. The `run` handler executes exactly one time per workflow execution/object.](focus://4:10) - - [The other handlers of the workflow are used to interact with the workflow: either query it, or signal it. They use the `SharedWorkflowContext` to interact with the SDK. These handlers can run concurrently with the `run` handler and can still be called after the `run` handler has finished.](focus://12:15) - - [Have a look at the workflow docs to learn more.](/develop/java/workflows) - - + ```kotlin + CODE_LOAD::kotlin/src/main/kotlin/develop/MyWorkflow.kt + ``` + + - Create the workflow + - Every workflow implementation needs to have a handler called `run` that implements the workflow logic. This handler uses the `WorkflowContext` to interact with the SDK. The `run` handler executes exactly one time per workflow execution/object. + - The other handlers of the workflow are used to interact with the workflow: either query it, or signal it. They use the `SharedWorkflowContext` to interact with the SDK. These handlers can run concurrently with the `run` handler and can still be called after the `run` handler has finished. + - [Have a look at the workflow docs to learn more.](/develop/java/workflows) Now that you have a high-level idea of what a Restate service might look like, let's have a look at what the Restate Context allows you to do. - The Java SDK generates code for service clients when you compile your project. Turn on [IntelliJ IDEA annotation processing support](https://www.jetbrains.com/help/idea/annotation-processors-support.html), to be able to re-run code generation by pressing `CTRL + F9`. diff --git a/docs/develop/java/state.mdx b/docs/develop/java/state.mdx index a478dbfe..521eccc2 100644 --- a/docs/develop/java/state.mdx +++ b/docs/develop/java/state.mdx @@ -30,6 +30,7 @@ You can do the following operations on the state: + ### Listing state keys For a single Virtual Object, you can list all the state keys that have entries in the state store via: @@ -50,28 +51,22 @@ CODE_LOAD::kotlin/src/main/kotlin/develop/State.kt#statekeys ### Retrieving state - - - - ```java - CODE_LOAD::java/src/main/java/develop/State.java#get - ``` - - 1. [Define the state key](focus://2,6) (key name and [(de)serializer](/develop/java/serialization)) at the top of the Virtual Object class. - 2. [Use `ctx.get` to retrieve the state for a specific key.](focus://3,7) - + ```java + CODE_LOAD::java/src/main/java/develop/State.java#get + ``` + + 1. Define the state key (key name and [(de)serializer](/develop/java/serialization)) at the top of the Virtual Object class. + 2. Use `ctx.get` to retrieve the state for a specific key. - - - ```kotlin - CODE_LOAD::kotlin/src/main/kotlin/develop/State.kt#get - ``` - 1. [Define the state key](focus://2,6) (key name and [(de)serializer](/develop/java/serialization)) at the top of the Virtual Object class. - 2. [Use `ctx.get` to retrieve the state for a specific key.](focus://3,7) - + ```kotlin + CODE_LOAD::kotlin/src/main/kotlin/develop/State.kt#get + ``` + + 1. Define the state key (key name and [(de)serializer](/develop/java/serialization)) at the top of the Virtual Object class. + 2. Use `ctx.get` to retrieve the state for a specific key. @@ -80,50 +75,41 @@ CODE_LOAD::kotlin/src/main/kotlin/develop/State.kt#statekeys - -```java -CODE_LOAD::java/src/main/java/develop/State.java#set -``` - 1. [Define the state key](focus://1) (key name and [(de)serializer](/develop/java/serialization)) at the top of the Virtual Object class. - 2. [Use `ctx.set` to set the state for a specific key.](focus://2) The type of value needs to line up with the type that was defined in the StateKey. + ```java + CODE_LOAD::java/src/main/java/develop/State.java#set + ``` - + 1. Define the state key (key name and [(de)serializer](/develop/java/serialization)) at the top of the Virtual Object class. + 2. Use `ctx.set` to set the state for a specific key. The type of value needs to line up with the type that was defined in the StateKey. - -```kotlin -CODE_LOAD::kotlin/src/main/kotlin/develop/State.kt#set -``` - 1. [Define the state key](focus://1) (key name and [(de)serializer](/develop/java/serialization)) at the top of the Virtual Object class. - 2. [Use `ctx.set` to set the state for a specific key.](focus://2) The type of value needs to line up with the type that was defined in the StateKey. + ```kotlin + CODE_LOAD::kotlin/src/main/kotlin/develop/State.kt#set + ``` - + 1. Define the state key (key name and [(de)serializer](/develop/java/serialization)) at the top of the Virtual Object class. + 2. Use `ctx.set` to set the state for a specific key. The type of value needs to line up with the type that was defined in the StateKey. - ### Clearing state - -```java -CODE_LOAD::java/src/main/java/develop/State.java#clear -``` - 1. [Define the state key](focus://1) (key name and [(de)serializer](/develop/java/serialization)) at the top of the Virtual Object class. - 2. [Use `ctx.clear` to delete the state for a specific key.](focus://2) + ```java + CODE_LOAD::java/src/main/java/develop/State.java#clear + ``` - + 1. Define the state key (key name and [(de)serializer](/develop/java/serialization)) at the top of the Virtual Object class. + 2. Use `ctx.clear` to delete the state for a specific key. - -```kotlin -CODE_LOAD::kotlin/src/main/kotlin/develop/State.kt#clear -``` - 1. [Define the state key](focus://1) (key name and [(de)serializer](/develop/java/serialization)) at the top of the Virtual Object class. - 2. [Use `ctx.clear` to delete the state for a specific key.](focus://2) + ```kotlin + CODE_LOAD::kotlin/src/main/kotlin/develop/State.kt#clear + ``` - + 1. Define the state key (key name and [(de)serializer](/develop/java/serialization)) at the top of the Virtual Object class. + 2. Use `ctx.clear` to delete the state for a specific key. From 1fe599926e169c864fc79fcf7f19d42d0e5aa814 Mon Sep 17 00:00:00 2001 From: Giselle van Dongen Date: Tue, 18 Jun 2024 15:03:48 +0200 Subject: [PATCH 18/18] Remove references to line numbers in TypeScript SDK docs --- code_snippets/ts/src/develop/awakeable.ts | 10 +- code_snippets/ts/src/develop/my_service.ts | 5 +- .../ts/src/develop/my_virtual_object.ts | 12 +- .../ts/src/develop/service_communication.ts | 4 +- code_snippets/ts/src/develop/workflow.ts | 5 +- docs/develop/java/awakeables.mdx | 5 +- docs/develop/ts/awakeables.mdx | 111 +++++++++++------- docs/develop/ts/overview.mdx | 56 ++++----- docs/develop/ts/service-communication.mdx | 25 ++-- docs/develop/ts/serving.mdx | 13 +- docs/develop/ts/state.mdx | 7 +- docs/invoke/http.mdx | 12 +- docs/use-cases/event-processing.mdx | 2 +- 13 files changed, 136 insertions(+), 131 deletions(-) diff --git a/code_snippets/ts/src/develop/awakeable.ts b/code_snippets/ts/src/develop/awakeable.ts index 444798ff..74e29a58 100644 --- a/code_snippets/ts/src/develop/awakeable.ts +++ b/code_snippets/ts/src/develop/awakeable.ts @@ -4,14 +4,20 @@ const service = restate.service({ name: "Awakeable", handlers: { greet: async (ctx: restate.Context, name: string) => { - // + // + // const awakeable = ctx.awakeable(); const awakeableId = awakeable.id + // + // await ctx.run(() => triggerTaskAndDeliverId(awakeableId)); + // + // const payload = await awakeable.promise; - // + // + // // ctx.resolveAwakeable(awakeableId, "hello"); diff --git a/code_snippets/ts/src/develop/my_service.ts b/code_snippets/ts/src/develop/my_service.ts index 57a7e768..79c85b03 100644 --- a/code_snippets/ts/src/develop/my_service.ts +++ b/code_snippets/ts/src/develop/my_service.ts @@ -1,15 +1,18 @@ import * as restate from "@restatedev/restate-sdk"; +import {Context} from "@restatedev/restate-sdk"; const myService = restate.service({ name: "MyService", handlers: { - myHandler: async (ctx: restate.Context, greeting: string) => { + myHandler: async (ctx: Context, greeting: string) => { return `${greeting}!`; }, } }) +// export const MyService: typeof myService = { name: "MyService" }; +// restate .endpoint() diff --git a/code_snippets/ts/src/develop/my_virtual_object.ts b/code_snippets/ts/src/develop/my_virtual_object.ts index 69a03ad5..ef9ca83b 100644 --- a/code_snippets/ts/src/develop/my_virtual_object.ts +++ b/code_snippets/ts/src/develop/my_virtual_object.ts @@ -1,15 +1,17 @@ import * as restate from "@restatedev/restate-sdk"; -import {handlers} from "@restatedev/restate-sdk"; +import {handlers, ObjectContext, ObjectSharedContext} from "@restatedev/restate-sdk"; const myVirtualObject = restate.object({ name: "MyVirtualObject", handlers: { - myHandler: async (ctx: restate.ObjectContext, greeting: string) => { + myHandler: async (ctx: ObjectContext, greeting: string) => { return `${greeting} ${ctx.key}!`; }, - myConcurrentHandler: handlers.object.shared(async (ctx: restate.ObjectSharedContext, greeting: string) => { - return `${greeting} ${ctx.key}!`; - }), + myConcurrentHandler: handlers.object.shared( + async (ctx: ObjectSharedContext, greeting: string) => { + return `${greeting} ${ctx.key}!`; + } + ), } }) diff --git a/code_snippets/ts/src/develop/service_communication.ts b/code_snippets/ts/src/develop/service_communication.ts index 05ffed62..5797f6c5 100644 --- a/code_snippets/ts/src/develop/service_communication.ts +++ b/code_snippets/ts/src/develop/service_communication.ts @@ -33,8 +33,8 @@ const service = restate.service({ // // - ctx.objectSendClient(MyVirtualObject, "Mary").myHandler("Hi!"); - ctx.objectSendClient(MyVirtualObject, "Mary").myHandler("Hi again!"); + ctx.objectSendClient(MyVirtualObject, "Mary").myHandler("I'm call A"); + ctx.objectSendClient(MyVirtualObject, "Mary").myHandler("I'm call B"); // }, callWorkflows: async (ctx: restate.Context, name: string) => { diff --git a/code_snippets/ts/src/develop/workflow.ts b/code_snippets/ts/src/develop/workflow.ts index 2d0bea32..d98598b6 100644 --- a/code_snippets/ts/src/develop/workflow.ts +++ b/code_snippets/ts/src/develop/workflow.ts @@ -11,18 +11,19 @@ // import * as restate from "@restatedev/restate-sdk"; +import {WorkflowContext, WorkflowSharedContext} from "@restatedev/restate-sdk"; const myWorkflow = restate.workflow({ name: "MyWorkflow", handlers: { - run: async (ctx: restate.WorkflowContext, req: string) => { + run: async (ctx: WorkflowContext, req: string) => { // implement workflow logic here return "success"; }, - interactWithWorkflow: async (ctx: restate.WorkflowSharedContext) => { + interactWithWorkflow: async (ctx: WorkflowSharedContext) => { // implement interaction logic here }, }, diff --git a/docs/develop/java/awakeables.mdx b/docs/develop/java/awakeables.mdx index 798129d6..400214a2 100644 --- a/docs/develop/java/awakeables.mdx +++ b/docs/develop/java/awakeables.mdx @@ -28,7 +28,7 @@ This pattern is also known as the callback (task token) pattern. 2. The handler **triggers a task/process** and attaches the awakeable ID (e.g. over Kafka, via HTTP,...). For example, send an HTTP request to a service that executes the task, and attach the ID to the payload. - You use `ctx.run` to avoid retriggering the task this on retries. + You use `ctx.run` to avoid re-triggering the task on retries. ```java CODE_LOAD::java/src/main/java/develop/Awakeables.java?2 @@ -59,7 +59,7 @@ This pattern is also known as the callback (task token) pattern. 2. The handler **triggers a task/process** and attaches the awakeable ID (e.g. over Kafka, via HTTP,...). For example, send an HTTP request to a service that executes the task, and attach the ID to the payload. - You use `ctx.runBlock` to avoid retriggering the task this on retries. + You use `ctx.runBlock` to avoid re-triggering the task on retries. ```kotlin CODE_LOAD::kotlin/src/main/kotlin/develop/Awakeables.kt?2 @@ -77,6 +77,7 @@ This pattern is also known as the callback (task token) pattern. + ## Completing awakeables The external process completes the awakeable by either resolving it with an optional payload or by rejecting it diff --git a/docs/develop/ts/awakeables.mdx b/docs/develop/ts/awakeables.mdx index 149152f5..3656c956 100644 --- a/docs/develop/ts/awakeables.mdx +++ b/docs/develop/ts/awakeables.mdx @@ -12,51 +12,74 @@ Awakeables pause an invocation while waiting for another process to complete a t You can use this pattern to let a handler execute a task somewhere else and retrieve the result. This pattern is also known as the callback (task token) pattern. - - - ```typescript my_handler.ts - CODE_LOAD::ts/src/develop/awakeable.ts#create - ``` - - - 1. [The handler **creates an awakeable**](focus://1,2). This contains a [String identifier](focus://2) and a Promise/Awaitable. - 2. [The handler **triggers a task/process** and attaches the awakeable ID](focus://4) (e.g. over Kafka, via HTTP,...). - For example, send an HTTP request to a service that executes the task, and attach the ID to the payload. - You use `ctx.run` to avoid retriggering the task this on retries. - 3. [The handler **waits** until the other process has executed the task.](focus://6) - - - 4. **The external process completes the awakeable** when the task is finished by: - - - ```shell resolve_curl.sh - curl localhost:8080/restate/awakeables/prom_1PePOqp/resolve -H 'content-type: application/json' - -d '{"hello": "world"}' - ``` - - ```shell reject_curl.sh - curl localhost:8080/restate/awakeables/prom_1PePOqp/reject -H 'content-type: text/plain' \ - -d 'Very bad error!' - ``` - - ```typescript external_process_resolve.ts - CODE_LOAD::ts/src/develop/awakeable.ts#resolve - ``` - - ```typescript external_proces_reject.ts - CODE_LOAD::ts/src/develop/awakeable.ts#reject - ``` - - - - **Resolving** the awakeable - - [Over HTTP](focus://resolve_curl.sh#1:2) with [its ID](focus://resolve_curl.sh#1[40:52]) and [an optional payload](focus://resolve_curl.sh#2) - - [Via the SDK with its ID and an optional payload](focus://external_process_resolve.ts#1) - - **Rejecting** the awakeable. This throws [a terminal error](/develop/java/error-handling) in the waiting handler. - - [Over HTTP](focus://reject_curl.sh#1:2) with [its ID](focus://reject_curl.sh#1[40:52]) and [a failure reason](focus://reject_curl.sh#2) - - [Via the SDK with its ID and a failure reason](focus://external_proces_reject.ts#1) - - 5. The handler **receives the payload and resumes**. +## Creating awakeables + + + + 1. The handler **creates an awakeable**. This contains a String identifier and a Promise. + ```ts + CODE_LOAD::ts/src/develop/awakeable.ts?1 + ``` + + --- + + 2. The handler **triggers a task/process** and attaches the awakeable ID (e.g. over Kafka, via HTTP,...). + For example, send an HTTP request to a service that executes the task, and attach the ID to the payload. + You use `ctx.run` to avoid re-triggering the task on retries. + + ```ts + CODE_LOAD::ts/src/develop/awakeable.ts?2 + ``` + + --- + + 3. The handler **waits** until the other process has executed the task. + The handler **receives the payload and resumes**. + + ```ts + CODE_LOAD::ts/src/develop/awakeable.ts?3 + ``` + + + +## Completing awakeables + +The external process completes the awakeable by either resolving it with an optional payload or by rejecting it +with its ID and a reason for the failure. This throws [a terminal error](/develop/java/error-handling) in the waiting handler. + + + - Resolving over HTTP with its ID and an optional payload: + + ```shell + curl localhost:8080/restate/awakeables/prom_1PePOqp/resolve + -H 'content-type: application/json' + -d '{"hello": "world"}' + ``` + + + - Rejecting over HTTP with its ID and a reason: + + ```shell + curl localhost:8080/restate/awakeables/prom_1PePOqp/reject + -H 'content-type: text/plain' \ + -d 'Very bad error!' + ``` + + + - Resolving via the SDK with its ID and an optional payload: + + ```ts + CODE_LOAD::ts/src/develop/awakeable.ts#resolve + ``` + + + - Rejecting via the SDK with its ID and a reason: + + ```ts + CODE_LOAD::ts/src/develop/awakeable.ts#reject + ``` + diff --git a/docs/develop/ts/overview.mdx b/docs/develop/ts/overview.mdx index 6038601c..e88845d7 100644 --- a/docs/develop/ts/overview.mdx +++ b/docs/develop/ts/overview.mdx @@ -21,69 +21,53 @@ Handlers can either be part of a [Service](/concepts/services#services-1), a [Vi ## Services [Services](/concepts/services#services-1) and their handlers are defined as follows: - - ```typescript CODE_LOAD::ts/src/develop/my_service.ts ``` - - -- [Create the service](focus://3,10) -- Specify [the service name.](focus://4) The service can then be called at `/MyService/myHandler`. -- [List the handlers.](focus://5:8) - [Each handler has a name (`myHandler`) and a function that implements the handler logic.](focus://6) +- Specify that you want to create a Service via _`restate.service`_. +- Specify the service name. The service can then be called at `/MyService/myHandler`. +- The service definition contains a list of handlers. + Each handler has a name (`myHandler`) and a function that implements the handler logic. The function has the Restate Context as its first argument. Within the handler, you use the `Context` to interact with Restate. The SDK stores the actions you do on the context in the Restate journal to make them durable. - The handler input parameters and return type are optional and can be of any type, as long as they can be serialized as a Buffer with _`Buffer.from(JSON.stringify(yourObject))`_ and deserialized with _`JSON.parse(result.toString()) as T`_. -- [Export the service definition](focus://12) so that it can be used by other handlers to call the service. (See [Service Communication docs](/develop/ts/service-communication).) -- [Create an endpoint](focus://14,15) and [bind the service(s)](focus://16) to the Restate endpoint. [Listen](focus://17) on the specified port (default `9080`) for connections and requests. - - +- Export the service definition `MyService` so that it can be used by other handlers to call the service. (See [Service Communication docs](/develop/ts/service-communication).) +- Finally, create an endpoint and bind the service(s) to the Restate endpoint. Listen on the specified port (default `9080`) for connections and requests. ## Virtual Objects [Virtual Objects](/concepts/services#virtual-objects) and their handlers are defined similarly to services, with the following differences: - - - ```typescript CODE_LOAD::ts/src/develop/my_virtual_object.ts ``` - - -- [Create the virtual object](focus://4) -- [The first argument of the handler must be the `ObjectContext` parameter](focus://7). +- Specify that you want to create a Virtual Object via _`restate.object`_. +- The first argument of each handler must be the `ObjectContext` parameter. Handlers with the `ObjectContext` parameter can write to the K/V state store. Only one handler can be active at a time, to ensure consistency. -- [If you want to have a handler that executes concurrently to the others and doesn't have write access to the K/V state, wrap the handler in `handlers.object.shared` and use the `ObjectSharedContext`.](focus://10) +- If you want to have a handler that executes concurrently to the others and doesn't have write access to the K/V state, wrap the handler in `handlers.object.shared` and use the `ObjectSharedContext`. For example, you can use these handlers to read K/V state, or interact with the blocking handler. - ## Workflows [Workflows](/concepts/services#workflows) are a special type of Virtual Objects, their definition is similar but with the following differences: - - - - ```typescript - CODE_LOAD::ts/src/develop/workflow.ts - ``` - - - - - [Create the workflow](focus://3,17) - - [Every workflow implementation needs to have a handler called `run` that implements the workflow logic. This handler uses the `WorkflowContext` to interact with the SDK. The `run` handler executes exactly one time per workflow execution/object.](focus://6:11) - - [The other handlers of the workflow are used to interact with the workflow: either query it, or signal it. They use the `WorkflowSharedContext` to interact with the SDK. These handlers can run concurrently with the `run` handler and can still be called after the `run` handler has finished.](focus://13:15) - - [Have a look at the workflow docs to learn more.](/develop/ts/workflows) - - +```typescript +CODE_LOAD::ts/src/develop/workflow.ts +``` +- Create the workflow with _`restate.workflow`_. +- Every workflow implementation needs to have a handler called `run` that implements the workflow logic. +This handler uses the `WorkflowContext` to interact with the SDK. +The `run` handler executes exactly one time per workflow execution/object. +- The other handlers of the workflow are used to interact with the workflow: either query it, or signal it. +They use the `WorkflowSharedContext` to interact with the SDK. +These handlers can run concurrently with the `run` handler and can still be called after the `run` handler has finished. +- [Have a look at the workflow docs to learn more.](/develop/ts/workflows) Now that you have a high-level idea of what a Restate service might look like, let's have a look at what the Restate Context allows you to do. diff --git a/docs/develop/ts/service-communication.mdx b/docs/develop/ts/service-communication.mdx index 220eb162..4d7973cb 100644 --- a/docs/develop/ts/service-communication.mdx +++ b/docs/develop/ts/service-communication.mdx @@ -12,13 +12,9 @@ A handler can call another handler and wait for the response (request-response), Make sure you export the service definition of the service you want to call, as we did in the [service example on the overview page](/develop/ts/overview#services): - - - ```typescript focus=12 - CODE_LOAD::ts/src/develop/my_service.ts - ``` - - +```typescript +CODE_LOAD::ts/src/develop/my_service.ts#api_export +``` Import this service definition in the service handler that wants to call it. @@ -149,19 +145,14 @@ To schedule a delayed call, send a message with a delay parameter, as follows: - Invocations to a Virtual Object are executed serially. Invocations will execute in the same order in which they arrive at Restate. For example, assume a handler calls the same Virtual Object twice: - - ```typescript - CODE_LOAD::ts/src/develop/service_communication.ts#ordering - ``` - - - It is guaranteed that [the invocation on line `1`](focus://1) will execute before [the invocation on line `2`](focus://2). - It is not guaranteed though that [invocation `2`](focus://2) will be executed immediately after [invocation `1`](focus://1), as invocations coming from other handlers/sources, could interleave these two calls. - + ```typescript + CODE_LOAD::ts/src/develop/service_communication.ts#ordering + ``` + It is guaranteed that call A will execute before call B. + It is not guaranteed though that call B will be executed immediately after call A, as invocations coming from other handlers/sources, could interleave these two calls. diff --git a/docs/develop/ts/serving.mdx b/docs/develop/ts/serving.mdx index 9b898fa3..9df6bad9 100644 --- a/docs/develop/ts/serving.mdx +++ b/docs/develop/ts/serving.mdx @@ -10,18 +10,14 @@ import Admonition from '@theme/Admonition'; Restate services can run in two ways: as an HTTP endpoint or as AWS Lambda functions. ## Creating an HTTP endpoint - +1. Create the endpoint +2. Bind one or multiple services to it. +3. Listen on the specified port (default `9080`) for connections and requests. + ```typescript CODE_LOAD::ts/src/develop/serving.ts#endpoint ``` - -1. [Create the endpoint](focus://1,2) -2. [Bind one or multiple services to it.](focus://3,4,5) -3. [Listen on the specified port (default `9080`) for connections and requests.](focus://6) - - -
Customizing the HTTP2 server @@ -42,7 +38,6 @@ CODE_LOAD::ts/src/develop/serving.ts#lambda Have a look at the [deployment section](/category/aws--aws-lambda) for guidance on how to deploy your services on AWS Lambda. - The implementation of your services and handlers remains the same for both deployment options. diff --git a/docs/develop/ts/state.mdx b/docs/develop/ts/state.mdx index c910ae2c..0e03a003 100644 --- a/docs/develop/ts/state.mdx +++ b/docs/develop/ts/state.mdx @@ -30,14 +30,13 @@ CODE_LOAD::ts/src/develop/state.ts#statekeys ``` ### Retrieving state - -Use `ctx.get` to retrieve the state for [a key](focus://1[40:53],2[40:53]): +Use `ctx.get` to retrieve the state for a key: + ```typescript CODE_LOAD::ts/src/develop/state.ts#get ``` -The return value is `null` if no value was stored. - +The return value is `null` if no value was stored. ### Setting state Use `ctx.set` to set a new value for a key: diff --git a/docs/invoke/http.mdx b/docs/invoke/http.mdx index e594b535..9747b319 100644 --- a/docs/invoke/http.mdx +++ b/docs/invoke/http.mdx @@ -91,15 +91,15 @@ You can **delay the message** by adding a delay request parameter in ISO8601 not ```shell humantime # focus[40:56] curl localhost:8080/MyService/myHandler/send?delay=10s \ - -H 'content-type: application/json' \ - -d '{"name": "Mary", "age": 25}' + -H 'content-type: application/json' \ + -d '{"name": "Mary", "age": 25}' ``` ```shell ISO8601 # focus[40:56] curl localhost:8080/MyService/myHandler/send?delay=PT10S \ - -H 'content-type: application/json' \ - -d '{"name": "Mary", "age": 25}' + -H 'content-type: application/json' \ + -d '{"name": "Mary", "age": 25}' ``` @@ -137,8 +137,8 @@ If you re-invoke the service with the same idempotency key within 24 hours, Rest You can tune the [retention time](focus://3) on a service-level by using the [Admin API](focus://1[15:28]) ([docs](/references/admin-api#tag/service/operation/modify_service)): ```shell curl -X PATCH localhost:9070/services/MyService \ - -H 'content-type: application/json' \ - -d '{"idempotency_retention": "2days"}' + -H 'content-type: application/json' \ + -d '{"idempotency_retention": "2days"}' ``` The [retention time](focus://3) is in [humantime format](https://docs.rs/humantime/latest/humantime/). diff --git a/docs/use-cases/event-processing.mdx b/docs/use-cases/event-processing.mdx index c7e60dd9..2facbbb0 100644 --- a/docs/use-cases/event-processing.mdx +++ b/docs/use-cases/event-processing.mdx @@ -60,7 +60,7 @@ Restate pushes the events to your function. Let Restate subscribe to a Kafka topic and specify to which function to push the events. Restate will take care of the event plumbing: polling for records, committing offsets, recovering... - + ```ts user_updates.ts CODE_LOAD::ts/src/use_cases/event_processing.ts?1