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/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/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) { 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/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/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/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/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/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/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/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/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/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/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/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/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/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/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/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/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 ``` diff --git a/docs/develop/java/awakeables.mdx b/docs/develop/java/awakeables.mdx index 722dc27d..0d37751f 100644 --- a/docs/develop/java/awakeables.mdx +++ b/docs/develop/java/awakeables.mdx @@ -13,118 +13,154 @@ 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 re-triggering the task 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 re-triggering the task 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/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/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. 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. 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 ``` - 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/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 ``` 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..560e040f 100644 --- a/docs/develop/ts/service-communication.mdx +++ b/docs/develop/ts/service-communication.mdx @@ -12,13 +12,10 @@ 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 +146,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/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 ``` 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/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/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/docs/use-cases/event-processing.mdx b/docs/use-cases/event-processing.mdx index 158e6877..2facbbb0 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 ``` --- @@ -60,10 +60,10 @@ 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 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 ``` 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.

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 ``` 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 { diff --git a/src/plugins/code-loader.js b/src/plugins/code-loader.js index fa440e04..18d67abb 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'); } 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