From 8b9724ade5527ca3308cb797bf147196c2b17444 Mon Sep 17 00:00:00 2001 From: Maxim Fateev Date: Sat, 27 Dec 2025 14:59:13 -0800 Subject: [PATCH 01/83] Kotlin proposal stub --- kotlin/sdk-proposal.md | 862 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 862 insertions(+) create mode 100644 kotlin/sdk-proposal.md diff --git a/kotlin/sdk-proposal.md b/kotlin/sdk-proposal.md new file mode 100644 index 0000000..c119bbd --- /dev/null +++ b/kotlin/sdk-proposal.md @@ -0,0 +1,862 @@ +# Kotlin SDK Proposal + +The Kotlin SDK will be implemented in three loosely defined phases: + +* Phase 1 - Coroutine-based workflows, untyped activity stubs, KotlinWorkerFactory +* Phase 2 - Typed activity stubs with suspend functions, signals/queries, child workflows +* Phase 3 - Updates, Nexus, testing framework, advanced features + +This proposal covers the overall design. Individual phases may have separate detailed proposals. + +Notes for this proposal: + +* Intentionally loosely defined, everything subject to change +* Some decisions are justified at the end +* Code examples may not be valid Kotlin but use `/* ... */` for brevity + +## Overview + +The Kotlin SDK will extend the existing Java SDK, utilizing Kotlin coroutines for workflow execution instead of Java's thread-based approach. A custom `TemporalCoroutineDispatcher` ensures deterministic execution. + +The high-level goals for the Kotlin SDK are: + +* Provide idiomatic Kotlin experience with coroutines and suspend functions +* Maintain full interoperability with the Java SDK +* Support existing Java-defined workflows and activities from Kotlin code +* Enable gradual migration from Java SDK patterns +* Minimum Kotlin version: 1.8.x +* Coroutines library: kotlinx-coroutines-core 1.7.x+ + +## Relationship to Java SDK + +The Kotlin SDK builds on top of the Java SDK rather than replacing it: + +* **Shared infrastructure**: Uses the same gRPC client, data conversion, and service client +* **Interoperability**: Kotlin workflows can call Java activities and vice versa +* **Gradual adoption**: Teams can mix Java and Kotlin workers in the same application +* **Existing module**: Extends the existing `temporal-kotlin` module which already provides DSL extensions + +## Relationship to Existing Kotlin Classes + +The `temporal-kotlin` module already provides Kotlin extensions for the Java SDK. The Kotlin SDK will build on this foundation: + +### Existing Classes (Retained) + +| File | Purpose | Status | +|------|---------|--------| +| `TemporalDsl.kt` | `@DslMarker` for type-safe builders | **Keep as-is** | +| `*OptionsExt.kt` (15 files) | DSL builders for all Options classes | **Keep as-is** | +| `WorkflowClientExt.kt` | Reified `newWorkflowStub()`, DSL extensions | **Keep as-is** | +| `WorkflowStubExt.kt` | Reified `getResult()` | **Keep as-is** | +| `WorkerFactoryExt.kt` | `WorkerFactory()` constructor-like DSL | **Keep as-is** | +| `WorkerExt.kt` | Reified `registerWorkflowImplementationType()` | **Keep as-is** | +| `WorkflowMetadata.kt` | `workflowName()`, `workflowSignalName()` | **Keep as-is** | +| `ActivityMetadata.kt` | `activityName(Interface::method)` | **Keep as-is** | +| `KotlinObjectMapperFactory.kt` | Jackson ObjectMapper for Kotlin | **Keep as-is** | +| `KotlinMethodReferenceDisassemblyService.kt` | Kotlin method references in `Async` | **Keep as-is** | + +### New Classes (Kotlin SDK) + +| Class | Purpose | +|-------|---------| +| `KotlinWorkerFactory` | Worker factory for coroutine-based workflows | +| `KotlinWorkerFactoryOptions` | Options specific to Kotlin worker factory | +| `KotlinWorkflow` | Entry point for workflow APIs (like `Workflow` in Java) | +| `KotlinWorkflowContext` | Internal workflow execution context | +| `KotlinActivityStub` | Suspend-function activity stub interface | +| `TemporalCoroutineDispatcher` | Deterministic coroutine dispatcher | +| `KotlinWorkerInterceptor` | Interceptor interface with suspend functions | +| `KotlinWorkflowImplementationFactory` | Creates coroutine-based workflow instances | + +### Worker Factory Distinction + +The SDK will have **two worker factory types**: + +``` +┌─────────────────────────────────────┐ +│ BaseWorkerFactory │ ← Abstract base (in Java SDK) +│ - Common worker lifecycle │ +│ - Worker registration │ +│ - Start/shutdown management │ +└──────────────┬──────────────────────┘ + │ + ┌───────┴───────┐ + │ │ + ▼ ▼ +┌──────────────┐ ┌──────────────────────┐ +│ WorkerFactory│ │ KotlinWorkerFactory │ +│ (Java SDK) │ │ (Kotlin SDK) │ +│ │ │ │ +│ Thread-based │ │ Coroutine-based │ +│ workflows │ │ workflows │ +│ │ │ │ +│ Uses: │ │ Uses: │ +│ - DeterministicRunner │ │ - TemporalCoroutineDispatcher │ +│ - POJOWorkflowImplementationFactory│ │ - KotlinWorkflowImplementationFactory │ +└──────────────┘ └──────────────────────┘ +``` + +**When to use which:** + +| Use Case | Worker Factory | +|----------|----------------| +| Java workflows (blocking) | `WorkerFactory` | +| Kotlin workflows without coroutines (blocking) | `WorkerFactory` | +| Kotlin workflows with suspend functions | `KotlinWorkerFactory` | +| Mixed: Java workflows + Kotlin activities | `WorkerFactory` | +| Mixed: Kotlin coroutine workflows + Java activities | `KotlinWorkerFactory` | + +**Important:** A single application can use both factory types for different task queues if needed. + +## Java SDK Refactoring for Pluggability + +To support the Kotlin coroutine-based execution model, the Java SDK requires refactoring to make the workflow execution dispatcher pluggable. The [prototype PR #1792](https://github.com/temporalio/sdk-java/pull/1792) demonstrates these changes. + +### Required Changes to `temporal-sdk` + +#### 1. Extract `BaseWorkerFactory` Abstract Class + +**File:** `io.temporal.worker.BaseWorkerFactory` (new) + +Extract common functionality from `WorkerFactory` into an abstract base class: + +```java +public abstract class BaseWorkerFactory { + protected final Scope metricsScope; + private final WorkflowClient workflowClient; + private final WorkerFactoryOptions factoryOptions; + private final WorkflowExecutorCache cache; + private final Map workers = new HashMap<>(); + + // Common lifecycle: start(), shutdown(), awaitTermination() + // Common worker management: newWorker(), getWorker() + // Common registration with WorkflowClient + + // Abstract method for pluggable workflow factory + protected abstract ReplayWorkflowFactory newReplayWorkflowFactory( + WorkerOptions workerOptions, + WorkflowClientOptions clientOptions, + WorkflowExecutorCache cache + ); +} +``` + +#### 2. Modify `WorkerFactory` to Extend Base + +**File:** `io.temporal.worker.WorkerFactory` + +```java +public final class WorkerFactory extends BaseWorkerFactory { + private final ThreadPoolExecutor workflowThreadPool; + private final WorkflowThreadExecutor workflowThreadExecutor; + + @Override + protected ReplayWorkflowFactory newReplayWorkflowFactory( + WorkerOptions workerOptions, + WorkflowClientOptions clientOptions, + WorkflowExecutorCache cache + ) { + // Return the existing POJO-based factory + return new POJOWorkflowImplementationFactory( + clientOptions, + workerOptions, + workflowThreadExecutor, + cache, + // ... other dependencies + ); + } +} +``` + +#### 3. Extend `ReplayWorkflowFactory` Interface + +**File:** `io.temporal.internal.replay.ReplayWorkflowFactory` + +Add workflow registration methods to the interface: + +```java +public interface ReplayWorkflowFactory { + // Existing + ReplayWorkflow getWorkflow(WorkflowType workflowType, WorkflowExecution workflowExecution); + boolean isAnyTypeSupported(); + + // New: Allow registration through the factory + void registerWorkflowImplementationTypes( + WorkflowImplementationOptions options, + Class[] workflowImplementationTypes + ); + + void addWorkflowImplementationFactory( + WorkflowImplementationOptions options, + Class clazz, + Functions.Func factory + ); +} +``` + +#### 4. Update Client Registry to Accept Base Type + +**File:** `io.temporal.internal.client.WorkflowClientInternal` + +```java +public interface WorkflowClientInternal { + // Change from WorkerFactory to BaseWorkerFactory + void registerWorkerFactory(BaseWorkerFactory workerFactory); +} +``` + +**File:** `io.temporal.internal.client.WorkerFactoryRegistry` + +```java +public class WorkerFactoryRegistry { + // Change from WorkerFactory to BaseWorkerFactory + public synchronized void register(BaseWorkerFactory workerFactory) { ... } + public synchronized void deregister(BaseWorkerFactory workerFactory) { ... } +} +``` + +#### 5. Update `EagerWorkflowTaskDispatcher` + +**File:** `io.temporal.internal.client.EagerWorkflowTaskDispatcher` + +Update to work with `BaseWorkerFactory` instead of `WorkerFactory`. + +### Files Changed Summary + +| File | Change Type | Description | +|------|-------------|-------------| +| `BaseWorkerFactory.java` | **New** | Abstract base class extracted from WorkerFactory | +| `WorkerFactory.java` | **Modified** | Now extends BaseWorkerFactory | +| `ReplayWorkflowFactory.java` | **Modified** | Added registration methods | +| `POJOWorkflowImplementationFactory.java` | **Modified** | Implements new interface methods | +| `WorkflowClientInternal.java` | **Modified** | Accept BaseWorkerFactory | +| `WorkflowClientInternalImpl.java` | **Modified** | Accept BaseWorkerFactory | +| `WorkerFactoryRegistry.java` | **Modified** | Work with BaseWorkerFactory | +| `EagerWorkflowTaskDispatcher.java` | **Modified** | Work with BaseWorkerFactory | +| `Worker.java` | **Minor** | Adjust for base class changes | +| `SyncWorkflowWorker.java` | **Minor** | Adjust for interface changes | + +### Kotlin SDK Classes Using This Infrastructure + +Once the Java SDK refactoring is complete, the Kotlin SDK adds: + +```kotlin +// Extends the base factory +class KotlinWorkerFactory( + workflowClient: WorkflowClient, + factoryOptions: KotlinWorkerFactoryOptions? +) : BaseWorkerFactory(workflowClient, factoryOptions) { + + override fun newReplayWorkflowFactory( + workerOptions: WorkerOptions, + clientOptions: WorkflowClientOptions, + cache: WorkflowExecutorCache + ): ReplayWorkflowFactory { + // Return Kotlin-specific factory that uses coroutines + return KotlinWorkflowImplementationFactory( + clientOptions, + workerOptions, + cache + ) + } +} +``` + +```kotlin +// Kotlin-specific workflow factory +internal class KotlinWorkflowImplementationFactory( + private val clientOptions: WorkflowClientOptions, + private val workerOptions: WorkerOptions, + private val cache: WorkflowExecutorCache +) : ReplayWorkflowFactory { + + override fun getWorkflow( + workflowType: WorkflowType, + workflowExecution: WorkflowExecution + ): ReplayWorkflow { + // Returns KotlinWorkflow which uses TemporalCoroutineDispatcher + return KotlinWorkflow( + namespace, + workflowExecution, + workflowDefinition, + // ... uses coroutine-based execution + ) + } +} +``` + +### Backward Compatibility + +These changes maintain full backward compatibility: + +* `WorkerFactory` API remains unchanged for existing users +* Existing Java workflows continue to work without modification +* The refactoring is purely internal restructuring +* No breaking changes to public APIs + +## Repository/Package Strategy + +### Repository + +The Kotlin SDK will continue to live in the `temporal-kotlin` module within the `sdk-java` repository: + +* Pros: + * Shared build infrastructure + * Easier to maintain version compatibility + * Single release process +* Cons: + * Ties Kotlin SDK releases to Java SDK releases + +### Package Naming + +* Core workflow APIs: `io.temporal.kotlin.workflow` +* Worker APIs: `io.temporal.kotlin.worker` +* Interceptors: `io.temporal.kotlin.interceptors` +* Existing extensions remain in their current packages (e.g., `io.temporal.client`) + +### Maven Artifacts + +```xml + + io.temporal + temporal-kotlin + N.N.N + +``` + +Additional dependency on kotlinx-coroutines: +```xml + + org.jetbrains.kotlinx + kotlinx-coroutines-core + 1.7.x + +``` + +## Workflow Definition + +### Basic Example + +```kotlin +@WorkflowInterface +interface GreetingWorkflow { + @WorkflowMethod + suspend fun getGreeting(name: String): String +} + +class GreetingWorkflowImpl : GreetingWorkflow { + override suspend fun getGreeting(name: String): String = coroutineScope { + val activities = KotlinWorkflow.newUntypedActivityStub( + ActivityOptions { + setStartToCloseTimeout(Duration.ofSeconds(10)) + } + ) + + // Execute activities in parallel using coroutines + val hello = async { + activities.execute("greet", String::class.java, "Hello", name) + } + val goodbye = async { + activities.execute("greet", String::class.java, "Goodbye", name) + } + + "${hello.await()}\n${goodbye.await()}" + } +} +``` + +### Key Characteristics + +* Workflow methods are `suspend fun` instead of regular functions +* Use `coroutineScope`, `async`, `launch` for concurrent execution +* Use `delay()` instead of `Workflow.sleep()` for timers +* The `@WorkflowInterface` and `@WorkflowMethod` annotations are reused from Java SDK +* Data classes work naturally for workflow parameters and results + +### Signals and Queries (Phase 2) + +```kotlin +@WorkflowInterface +interface OrderWorkflow { + @WorkflowMethod + suspend fun processOrder(order: Order): OrderResult + + @SignalMethod + suspend fun cancelOrder(reason: String) + + @QueryMethod + fun getOrderStatus(): OrderStatus +} +``` + +Notes: +* Signal methods are `suspend fun` since they may trigger workflow logic +* Query methods remain non-suspend as they are read-only and synchronous + +### Child Workflows (Phase 2) + +```kotlin +override suspend fun parentWorkflow(): String = coroutineScope { + val child = KotlinWorkflow.newChildWorkflowStub( + ChildWorkflowOptions { + setWorkflowId("child-workflow-id") + } + ) + + // Start child workflow asynchronously + val result = async { child.doWork("input") } + + // Do other work... + + result.await() +} +``` + +## Activity Definition + +### Phase 1: Untyped Activity Stubs + +In Phase 1, activities are invoked through untyped stubs: + +```kotlin +val activities = KotlinWorkflow.newUntypedActivityStub( + ActivityOptions { + setStartToCloseTimeout(Duration.ofSeconds(30)) + setRetryOptions { + setInitialInterval(Duration.ofSeconds(1)) + setMaximumAttempts(3) + } + } +) + +val result = activities.execute("activityName", String::class.java, arg1, arg2) +``` + +### Phase 2: Typed Activity Stubs with Suspend Functions + +```kotlin +@ActivityInterface +interface GreetingActivities { + @ActivityMethod + suspend fun composeGreeting(greeting: String, name: String): String + + @ActivityMethod + suspend fun sendEmail(email: Email): SendResult +} + +// In workflow: +val activities = KotlinWorkflow.newActivityStub( + ActivityOptions { + setStartToCloseTimeout(Duration.ofSeconds(30)) + } +) + +val greeting = activities.composeGreeting("Hello", "World") +``` + +### Activity Implementation + +Activities can be implemented as regular Kotlin classes: + +```kotlin +class GreetingActivitiesImpl : GreetingActivities { + override suspend fun composeGreeting(greeting: String, name: String): String { + // Can use suspend functions for I/O + return "$greeting, $name!" + } + + override suspend fun sendEmail(email: Email): SendResult { + // Async I/O with coroutines + return emailService.send(email) + } +} +``` + +### Local Activities (Phase 2) + +```kotlin +val localActivities = KotlinWorkflow.newLocalActivityStub( + LocalActivityOptions { + setStartToCloseTimeout(Duration.ofSeconds(5)) + } +) +``` + +## Client API + +### Workflow Client Extensions + +Building on existing `temporal-kotlin` extensions: + +```kotlin +// Create client with DSL +val client = WorkflowClient(service) { + setNamespace("default") + setDataConverter(myConverter) +} + +// Start workflow with reified types +val workflow = client.newWorkflowStub { + setWorkflowId("greeting-123") + setTaskQueue("greeting-queue") +} + +// Execute workflow +val result = workflow.getGreeting("Temporal") + +// Or start async and get handle +val handle = WorkflowClient.start { workflow.getGreeting("Temporal") } +val result = handle.getResult() +``` + +### Signal and Query from Client + +```kotlin +// Get existing workflow +val workflow = client.newWorkflowStub("order-123") + +// Send signal +workflow.cancelOrder("Customer request") + +// Query +val status = workflow.getOrderStatus() +``` + +## Worker API + +### KotlinWorkerFactory + +A specialized worker factory for Kotlin coroutine-based workflows: + +```kotlin +val service = WorkflowServiceStubs.newLocalServiceStubs() +val client = WorkflowClient.newInstance(service) + +val factory = KotlinWorkerFactory(client) { + setMaxWorkflowThreadCount(800) +} + +val worker = factory.newWorker("task-queue") { + setMaxConcurrentActivityExecutionSize(100) +} + +// Register Kotlin workflow implementations +worker.registerWorkflowImplementationTypes( + GreetingWorkflowImpl::class.java, + OrderWorkflowImpl::class.java +) + +// Register activity implementations (can be Java or Kotlin) +worker.registerActivitiesImplementations( + GreetingActivitiesImpl(), + OrderActivitiesImpl() +) + +factory.start() +``` + +### TemporalCoroutineDispatcher + +The custom dispatcher ensures deterministic execution of coroutines: + +* Executes coroutines in a controlled, deterministic order +* Integrates with Temporal's replay mechanism +* Supports `delay()` by mapping to Temporal timers +* Handles cancellation scopes properly + +```kotlin +// Internal implementation - users don't interact with this directly +internal class TemporalCoroutineDispatcher( + private val workflowContext: KotlinWorkflowContext +) : CoroutineDispatcher() { + + override fun dispatch(context: CoroutineContext, block: Runnable) { + // Queue for deterministic execution + workflowContext.schedule(block) + } + + fun eventLoop(deadlockDetectionTimeout: Long) { + // Process queued coroutines deterministically + } +} +``` + +## Data Conversion + +### Kotlin Serialization Support + +Support for `kotlinx.serialization` as an alternative to Jackson: + +```kotlin +@Serializable +data class Order( + val id: String, + val items: List, + val status: OrderStatus +) + +@Serializable +data class OrderItem( + val productId: String, + val quantity: Int +) +``` + +### Jackson Kotlin Module + +The existing `KotlinObjectMapperFactory` continues to be supported: + +```kotlin +val converter = DefaultDataConverter.newDefaultInstance().withPayloadConverterOverrides( + JacksonJsonPayloadConverter( + KotlinObjectMapperFactory.new() + ) +) +``` + +### Data Classes Best Practices + +* Use `data class` for workflow parameters and results +* Ensure all properties have default values or are nullable for deserialization +* Consider using `@Serializable` annotation for kotlinx.serialization + +## Interceptors + +### Kotlin-Native Interceptor Interfaces + +```kotlin +interface KotlinWorkerInterceptor { + fun interceptWorkflow(next: WorkflowInboundCallsInterceptor): WorkflowInboundCallsInterceptor { + return next + } + + fun interceptActivity(next: ActivityInboundCallsInterceptor): ActivityInboundCallsInterceptor { + return next + } +} + +interface WorkflowInboundCallsInterceptor { + suspend fun init(outboundCalls: WorkflowOutboundCallsInterceptor) + + suspend fun execute(input: WorkflowInput): WorkflowOutput + + suspend fun handleSignal(input: SignalInput) + + fun handleQuery(input: QueryInput): QueryOutput +} + +interface WorkflowOutboundCallsInterceptor { + suspend fun executeActivity(input: ActivityInput): ActivityOutput + + suspend fun executeChildWorkflow(input: ChildWorkflowInput): ChildWorkflowOutput + + // ... other outbound calls +} +``` + +### Example: Logging Interceptor + +```kotlin +class LoggingInterceptor : KotlinWorkerInterceptor { + override fun interceptWorkflow( + next: WorkflowInboundCallsInterceptor + ): WorkflowInboundCallsInterceptor { + return object : WorkflowInboundCallsInterceptorBase(next) { + override suspend fun execute(input: WorkflowInput): WorkflowOutput { + log.info("Workflow started: ${input.workflowType}") + return try { + super.execute(input) + } finally { + log.info("Workflow completed: ${input.workflowType}") + } + } + } + } +} +``` + +## Testing + +### Workflow Testing with Coroutines (Phase 3) + +```kotlin +class GreetingWorkflowTest { + @Test + fun testGreetingWorkflow() = runTest { + val testEnv = TestWorkflowEnvironment.newInstance() + val worker = testEnv.newWorker("test-queue") + + worker.registerWorkflowImplementationTypes(GreetingWorkflowImpl::class.java) + worker.registerActivitiesImplementations(MockGreetingActivities()) + + testEnv.start() + + val client = testEnv.workflowClient + val workflow = client.newWorkflowStub { + setTaskQueue("test-queue") + } + + val result = workflow.getGreeting("Test") + + assertEquals("Hello, Test!", result) + + testEnv.close() + } +} +``` + +### Activity Testing + +```kotlin +class GreetingActivitiesTest { + @Test + fun testComposeGreeting() = runTest { + val context = TestActivityEnvironment.newInstance().activityContext + + val activities = GreetingActivitiesImpl() + + // Test with activity context + val result = context.run { + activities.composeGreeting("Hello", "World") + } + + assertEquals("Hello, World!", result) + } +} +``` + +### Mocking Activities in Workflows + +```kotlin +@Test +fun testWorkflowWithMockedActivities() = runTest { + val mockActivities = mockk() + coEvery { mockActivities.composeGreeting(any(), any()) } returns "Mocked greeting" + + // Configure test environment with mocked activities + worker.registerActivitiesImplementations(mockActivities) + + // ... run workflow +} +``` + +## Migration Guide + +### From Java SDK Workflows + +```java +// Java workflow +@WorkflowInterface +public interface GreetingWorkflow { + @WorkflowMethod + String getGreeting(String name); +} + +public class GreetingWorkflowImpl implements GreetingWorkflow { + @Override + public String getGreeting(String name) { + ActivityStub activities = Workflow.newUntypedActivityStub(...); + return activities.execute("greet", String.class, name); + } +} +``` + +```kotlin +// Kotlin equivalent +@WorkflowInterface +interface GreetingWorkflow { + @WorkflowMethod + suspend fun getGreeting(name: String): String +} + +class GreetingWorkflowImpl : GreetingWorkflow { + override suspend fun getGreeting(name: String): String { + val activities = KotlinWorkflow.newUntypedActivityStub(...) + return activities.execute("greet", String::class.java, name)!! + } +} +``` + +### Key Migration Differences + +| Java SDK | Kotlin SDK | +|----------|------------| +| `Workflow.sleep(duration)` | `delay(duration.toMillis())` | +| `Async.function(...)` | `async { ... }` | +| `Promise` | `Deferred` (from coroutines) | +| `Workflow.await(condition)` | Custom `awaitCondition { }` or channels | +| Thread-based execution | Coroutine-based execution | + +### Interoperability + +Kotlin workflows can call Java activities: + +```kotlin +// Kotlin workflow calling Java activity +override suspend fun processOrder(order: Order): String { + // JavaActivities is a Java @ActivityInterface + val javaActivities = KotlinWorkflow.newActivityStub(options) + return javaActivities.process(order) +} +``` + +Java workflows can be invoked from Kotlin clients: + +```kotlin +// Calling Java workflow from Kotlin client +val javaWorkflow = client.newWorkflowStub(options) +val result = javaWorkflow.execute(input) +``` + +## Decision Justifications + +### Why Coroutines Instead of Extending Java Thread Model? + +* **Idiomatic Kotlin**: Coroutines are the standard concurrency model in Kotlin +* **Structured concurrency**: `coroutineScope`, `async`, `launch` provide clear parent-child relationships +* **Cancellation**: Coroutine cancellation maps naturally to Temporal's cancellation scopes +* **Performance**: Lightweight compared to threads, though this is less relevant in workflow context +* **Ecosystem**: Integrates with Kotlin ecosystem (Flow, channels, etc.) + +### Why Suspend Functions for Workflows? + +* **Natural async**: Workflow operations (activities, timers) are inherently async +* **Sequential code**: Suspend functions allow sequential-looking code for async operations +* **Error handling**: Standard try-catch works with suspend functions +* **Testing**: Coroutine test utilities work well with suspend functions + +### Why Keep Compatibility with Java SDK? + +* **Gradual migration**: Teams can adopt Kotlin incrementally +* **Ecosystem leverage**: Benefits from Java SDK's stability and features +* **Reduced maintenance**: Shared infrastructure reduces duplication +* **Activity reuse**: Existing Java activities work without modification + +### Why Untyped Stubs First in Phase 1? + +* **Complexity management**: Typed stub generation requires more infrastructure +* **Proof of concept**: Validates coroutine-based execution model first +* **Faster iteration**: Can release usable SDK sooner +* **Java interop**: Untyped stubs work with any activity implementation + +### Why Custom CoroutineDispatcher? + +* **Determinism**: Standard dispatchers are non-deterministic +* **Replay support**: Must execute coroutines in same order during replay +* **Timer integration**: `delay()` must map to Temporal timers, not actual time +* **Deadlock detection**: Can implement workflow-aware deadlock detection + +## Open Questions + +1. **Workflow versioning**: How do `Workflow.getVersion()` semantics work with coroutines? +2. **CancellationScope mapping**: Best way to map Kotlin's structured concurrency to Temporal's cancellation scopes? +3. **Flow support**: Should we support Kotlin Flow for streaming results? +4. **Multiplatform**: Any consideration for Kotlin Multiplatform in the future? +5. **DSL vs Annotations**: Should we provide a pure DSL alternative to annotations? + +## References + +* [Kotlin Coroutines Prototype PR #1792](https://github.com/temporalio/sdk-java/pull/1792) +* [Existing temporal-kotlin Module](https://github.com/temporalio/sdk-java/tree/master/temporal-kotlin) +* [.NET SDK Proposal - Phase 1](../dotnet/sdk-phase-1.md) +* [.NET SDK Proposal - Phase 2](../dotnet/sdk-phase-2.md) From b3cb4e5ea0985c098dc8f6dc8b354b54061b2f20 Mon Sep 17 00:00:00 2001 From: Maxim Fateev Date: Sun, 28 Dec 2025 22:01:21 -0800 Subject: [PATCH 02/83] Kotlin SDK proposal: API and implementation docs API document (sdk-api.md): - Kotlin idioms: Duration, null safety, coroutines - Stub-less activity/workflow invocation using method references - String-based activity execution for cross-language interop - KWorkflowHandle and KTypedWorkflowHandle for typed operations - SignalWithStart and UpdateWithStart operations - KotlinPlugin for enabling coroutine support - K-prefix naming convention for public types - Complete migration guide from Java SDK Implementation document (sdk-implementation.md): - Unified worker architecture with pluggable WorkflowImplementationFactory - KotlinCoroutineDispatcher for deterministic execution - Reified generics for type-safe activity execution - Return type handling for DataConverter (typeOf()) - Detailed open questions with context and recommendations Proposal overview (sdk-proposal.md): - Updated to reflect coroutines as key idiom - Links to API and implementation docs --- kotlin/sdk-api.md | 1509 ++++++++++++++++++++++++++++++++++ kotlin/sdk-implementation.md | 1012 +++++++++++++++++++++++ kotlin/sdk-proposal.md | 882 +------------------- 3 files changed, 2554 insertions(+), 849 deletions(-) create mode 100644 kotlin/sdk-api.md create mode 100644 kotlin/sdk-implementation.md diff --git a/kotlin/sdk-api.md b/kotlin/sdk-api.md new file mode 100644 index 0000000..a436e9f --- /dev/null +++ b/kotlin/sdk-api.md @@ -0,0 +1,1509 @@ +# Kotlin SDK API Proposal + +This document describes the public API and developer experience for the Temporal Kotlin SDK. + +For implementation details, see [sdk-implementation.md](./sdk-implementation.md). + +## Overview + +The Kotlin SDK provides an idiomatic Kotlin experience for building Temporal workflows using coroutines and suspend functions. + +**Key Features:** + +* Coroutine-based workflows with `suspend fun` +* Full interoperability with Java SDK +* Kotlin Duration support (`30.seconds`) +* DSL builders for configuration +* Null safety (no `Optional`) + +**Requirements:** + +* Minimum Kotlin version: 1.8.x +* Coroutines library: kotlinx-coroutines-core 1.7.x+ + +## Phases + +* **Phase 1** - Coroutine-based workflows, untyped activity stubs, core Kotlin idioms +* **Phase 2** - Typed activity stubs, signals/queries/updates, child workflows +* **Phase 3** - Testing framework + +> **Note:** Nexus support is a separate project and will be addressed independently. + +## Kotlin Idioms + +The SDK leverages Kotlin-specific language features for an idiomatic experience. + +### Kotlin Duration + +Use `kotlin.time.Duration` for readable time expressions: + +```kotlin +import kotlin.time.Duration.Companion.seconds +import kotlin.time.Duration.Companion.minutes +import kotlin.time.Duration.Companion.hours + +// Activity with timeouts +val result = KWorkflow.executeActivity( + "ProcessOrder", + orderData, + ActivityOptions { + startToCloseTimeout = 30.seconds + scheduleToCloseTimeout = 5.minutes + heartbeatTimeout = 10.seconds + } +) + +// Timers +delay(1.hours) +``` + +### Null Safety + +Nullable types replace `Optional` throughout the API. The following Java SDK types have Kotlin equivalents with null-safe APIs: + +| Java SDK | Kotlin SDK | +|----------|------------| +| `io.temporal.workflow.Workflow` | `KWorkflow` object | +| `io.temporal.workflow.WorkflowInfo` | `KWorkflowInfo` | +| `io.temporal.workflow.Promise` | `KActivityHandle` / `Deferred` | +| `io.temporal.activity.Activity` | `KActivity` object | +| `io.temporal.activity.ActivityExecutionContext` | `KActivityContext` | +| `io.temporal.activity.ActivityInfo` | `KActivityInfo` | +| `io.temporal.client.WorkflowStub` | `KWorkflowHandle` / `KTypedWorkflowHandle` | + +```kotlin +// KWorkflowInfo - nullable instead of Optional +interface KWorkflowInfo { + val workflowId: String + val runId: String + val parentWorkflowId: String? // Optional in Java + val parentRunId: String? // Optional in Java + fun getMemo(key: String): String? // Optional in Java + fun getSearchAttribute(key: String): Any? + // ... other properties +} + +// KActivityContext - activity execution context +interface KActivityContext { + val info: KActivityInfo + suspend fun heartbeat(details: Any? = null) + fun doNotCompleteOnReturn() + // ... other methods +} + +// KActivityInfo - nullable instead of Optional +interface KActivityInfo { + val activityId: String + val activityType: String + val workflowId: String + val attempt: Int + fun getHeartbeatDetails(): Payloads? // Optional in Java + // ... other properties +} + +// Access via KWorkflow / KActivity +val info: KWorkflowInfo = KWorkflow.getInfo() +val parentId: String? = info.parentWorkflowId + +val context: KActivityContext = KActivity.getContext() +val activityInfo: KActivityInfo = context.info +context.heartbeat("progress") +``` + +This eliminates `.orElse(null)`, `.isPresent`, and other Optional ceremony. + +### Property Syntax for Queries + +Queries can be defined as Kotlin properties in the workflow interface: + +```kotlin +@WorkflowInterface +interface OrderWorkflow { + @QueryMethod + val status: OrderStatus // Property syntax + + @QueryMethod + fun getItemCount(): Int // Method syntax +} + +// Client usage via typed handle +val handle = client.getKWorkflowHandle("order-123") +val status = handle.query(OrderWorkflow::status) +val count = handle.query(OrderWorkflow::getItemCount) +``` + +## Workflow Definition + +There are two approaches for workflow definition, depending on whether you need Java interoperability. + +### Option A: Pure Kotlin (Recommended for Kotlin-only codebases) + +For pure Kotlin codebases, define interfaces with `suspend` methods: + +```kotlin +@WorkflowInterface +interface GreetingWorkflow { + @WorkflowMethod + suspend fun getGreeting(name: String): String +} + +class GreetingWorkflowImpl : GreetingWorkflow { + override suspend fun getGreeting(name: String): String { + return KWorkflow.executeActivity( + "composeGreeting", + "Hello", name, + ActivityOptions { startToCloseTimeout = 10.seconds } + ) + } +} + +// Client call - same pattern as activities, no stub needed +val result = client.executeWorkflow( + GreetingWorkflow::getGreeting, + "Temporal", + WorkflowOptions { + workflowId = "greeting-123" + taskQueue = "greetings" + } +) +``` + +### Option B: Java Interoperability (Parallel Interface Pattern) + +When you need to share workflow interfaces with Java code, use the parallel interface pattern: + +```kotlin +// Interface for client calls - non-suspend for Java compatibility +@WorkflowInterface +interface OrderWorkflow { + @WorkflowMethod + fun processOrder(order: Order): OrderResult + + @SignalMethod + fun cancelOrder(reason: String) + + @QueryMethod + val status: OrderStatus +} + +// Parallel suspend interface for Kotlin implementation +@KWorkflowImpl(workflow = OrderWorkflow::class) +interface OrderWorkflowSuspend { + suspend fun processOrder(order: Order): OrderResult + suspend fun cancelOrder(reason: String) + val status: OrderStatus // Queries are never suspend +} + +// Kotlin implementation uses the suspend interface +class OrderWorkflowImpl : OrderWorkflowSuspend { + override suspend fun processOrder(order: Order): OrderResult { + // Full coroutine support - delay(), async, etc. + delay(1.hours) + return OrderResult(success = true) + } + + override suspend fun cancelOrder(reason: String) { /* ... */ } + override val status: OrderStatus get() = OrderStatus.PENDING +} + +// Client call - same pattern, blocking for Option B +val result = client.executeWorkflow( + OrderWorkflow::processOrder, + order, + WorkflowOptions { + workflowId = "order-123" + taskQueue = "orders" + } +) +``` + +**When to use which:** + +| Scenario | Approach | +|----------|----------| +| Pure Kotlin codebase | Option A - suspend interfaces | +| Calling Java-defined workflows | Option A works (from coroutine context) | +| Kotlin workflows with Java-defined interface | Option B - parallel interface | +| Shared interface library with Java | Option B - parallel interface | + +### Key Characteristics + +* Use `coroutineScope`, `async`, `launch` for concurrent execution +* Use `delay()` for timers (maps to Temporal timers, not `Thread.sleep`) +* Reuses `@WorkflowInterface` and `@WorkflowMethod` annotations from Java SDK +* Data classes work naturally for parameters and results + +### Signals, Queries, and Updates + +Signals and updates follow the same suspend/non-suspend pattern as workflow methods. Queries are always synchronous (never suspend) and can be defined as properties: + +```kotlin +@WorkflowInterface +interface OrderWorkflow { + @WorkflowMethod + suspend fun processOrder(order: Order): OrderResult + + @SignalMethod + suspend fun cancelOrder(reason: String) + + @UpdateMethod + suspend fun addItem(item: OrderItem): Boolean + + @UpdateValidatorMethod(updateMethod = "addItem") + fun validateAddItem(item: OrderItem) + + // Queries - always synchronous, can use property syntax + @QueryMethod + val status: OrderStatus + + @QueryMethod + fun getItemCount(): Int +} +``` + +### Child Workflows + +Child workflows use the same stub-less pattern as activities: + +```kotlin +// Simple case - execute child workflow and wait for result +override suspend fun parentWorkflow(): String { + return KWorkflow.executeChildWorkflow( + ChildWorkflow::doWork, + "input", + ChildWorkflowOptions { + workflowId = "child-workflow-id" + } + ) +} + +// Parallel case - start child workflow and get handle +override suspend fun parentWorkflowParallel(): String = coroutineScope { + // Start child and activity in parallel + val childHandle = KWorkflow.startChildWorkflow( + ChildWorkflow::doWork, + "input", + ChildWorkflowOptions { + workflowId = "child-workflow-id" + } + ) + val activityHandle = KWorkflow.startActivity( + SomeActivities::doSomething, + ActivityOptions { startToCloseTimeout = 30.seconds } + ) + + // Wait for both + "${childHandle.await()} - ${activityHandle.await()}" +} +``` + +**ChildWorkflowOptions:** + +```kotlin +ChildWorkflowOptions { + workflowId = "child-workflow-id" + taskQueue = "child-queue" // Optional: defaults to parent's task queue + workflowExecutionTimeout = 1.hours + workflowRunTimeout = 30.minutes + retryOptions = RetryOptions { + initialInterval = 1.seconds + maximumAttempts = 3 + } + parentClosePolicy = ParentClosePolicy.TERMINATE + cancellationType = ChildWorkflowCancellationType.WAIT_CANCELLATION_COMPLETED +} +``` + +### Timers and Delays + +```kotlin +override suspend fun workflowWithTimer(): String { + // Simple delay using Kotlin Duration + delay(5.minutes) + + return "completed" +} +``` + +### Parallel Execution + +Use `coroutineScope` when you need `async` for parallel execution: + +```kotlin +val options = ActivityOptions { startToCloseTimeout = 30.seconds } + +override suspend fun parallelWorkflow(items: List): List = coroutineScope { + // Process all items in parallel + // coroutineScope is required because async needs a CoroutineScope + items.map { item -> + async { + KWorkflow.executeActivity("process", item, options) + } + }.awaitAll() +} + +// Another example: parallel activities with different results +override suspend fun getGreetings(name: String): String = coroutineScope { + val hello = async { KWorkflow.executeActivity("greet", "Hello", name, options) } + val goodbye = async { KWorkflow.executeActivity("greet", "Goodbye", name, options) } + + "${hello.await()}\n${goodbye.await()}" +} +``` + +Note: `coroutineScope` provides structured concurrency—if one `async` fails, all others are automatically cancelled. + +### Await Condition + +Wait for a condition to become true (equivalent to Java's `Workflow.await()`): + +```kotlin +override suspend fun workflowWithCondition(): String { + var approved = false + + // Signal handler sets approved = true + // ... + + // Wait until approved (blocks workflow until condition is true) + KWorkflow.condition { approved } + + return "Approved" +} + +// With timeout - returns false if timed out +override suspend fun workflowWithTimeout(): String { + var approved = false + + val wasApproved = KWorkflow.condition(timeout = 24.hours) { approved } + + return if (wasApproved) "Approved" else "Timed out" +} +``` + +> **Note:** Versioning (`KWorkflow.getVersion`), continue-as-new (`KWorkflow.continueAsNew`), side effects (`KWorkflow.sideEffect`), and search attributes (`KWorkflow.upsertSearchAttributes`) use the same patterns as the Java SDK. + +## Activity Definition + +### String-based Activity Execution (Phase 1) + +For calling activities by name (useful for cross-language interop or dynamic activity names): + +```kotlin +// Execute activity by string name +val result = KWorkflow.executeActivity( + "activityName", + arg1, arg2, + ActivityOptions { + startToCloseTimeout = 30.seconds + retryOptions = RetryOptions { + initialInterval = 1.seconds + maximumAttempts = 3 + } + } +) + +// Async execution by string name +val handle = KWorkflow.startActivity( + "activityName", + arg1, arg2, + ActivityOptions { startToCloseTimeout = 30.seconds } +) +val result = handle.await() +``` + +### Typed Activities (Phase 2) + +The typed activity API uses direct method references - no stub creation needed. This approach: +- Provides full compile-time type safety for arguments and return types +- Allows different options (timeouts, retry policies) per activity call +- Works with both Kotlin `suspend` and Java non-suspend activity interfaces +- Similar to TypeScript and Python SDK patterns + +```kotlin +// Define activity interface +@ActivityInterface +interface GreetingActivities { + @ActivityMethod + suspend fun composeGreeting(greeting: String, name: String): String + + @ActivityMethod + suspend fun sendEmail(email: Email): SendResult + + @ActivityMethod + suspend fun log(message: String) +} + +// In workflow - direct method reference, no stub needed +val greeting = KWorkflow.executeActivity( + GreetingActivities::composeGreeting, // Direct reference to interface method + "Hello", "World", + ActivityOptions { + startToCloseTimeout = 30.seconds + } +) + +// Different activity, different options +val result = KWorkflow.executeActivity( + GreetingActivities::sendEmail, + email, + ActivityOptions { + startToCloseTimeout = 2.minutes + retryOptions = RetryOptions { + maximumAttempts = 5 + } + } +) + +// Void activities work too +KWorkflow.executeActivity( + GreetingActivities::log, + "Processing started", + ActivityOptions { startToCloseTimeout = 5.seconds } +) +``` + +**Type Safety:** The API uses `KFunction` reflection to extract method metadata and provides compile-time type checking: + +```kotlin +// Compile error! Wrong argument types +KWorkflow.executeActivity( + GreetingActivities::composeGreeting, + 123, true, // ✗ Type mismatch: expected String, String + options +) +``` + +**Parallel Execution:** Use `startActivity` to get handles for concurrent execution: + +```kotlin +override suspend fun parallelGreetings(names: List): List = coroutineScope { + names.map { name -> + async { + KWorkflow.executeActivity( + GreetingActivities::composeGreeting, + "Hello", name, + ActivityOptions { startToCloseTimeout = 10.seconds } + ) + } + }.awaitAll() +} + +// Or with handles for more control +val handle1 = KWorkflow.startActivity(GreetingActivities::composeGreeting, "Hello", "Alice", options) +val handle2 = KWorkflow.startActivity(GreetingActivities::composeGreeting, "Hello", "Bob", options) +// Do other work... +val results = listOf(handle1.await(), handle2.await()) +``` + +**Java Activity Interoperability:** Method references work regardless of whether the activity is defined in Kotlin or Java: + +```kotlin +// Java activity interface works seamlessly +// public interface JavaPaymentActivities { +// PaymentResult processPayment(String orderId, BigDecimal amount); +// } + +val result: PaymentResult = KWorkflow.executeActivity( + JavaPaymentActivities::processPayment, + orderId, amount, + ActivityOptions { startToCloseTimeout = 2.minutes } +) +``` + +### Activity Execution API + +The `KWorkflow` object provides type-safe overloads using `KFunction` types. The interface method reference (`Interface::method`) is used to extract metadata (interface class, method name) via reflection - the function is never invoked directly. + +```kotlin +import kotlin.reflect.KFunction + +object KWorkflow { + // Execute activity - uses KFunction for metadata extraction + // T = activity interface, A1..An = arguments, R = return type + + // 1 argument + suspend fun executeActivity( + activity: KFunction2, // T::method with 1 arg + arg1: A1, + options: ActivityOptions + ): R + + // 2 arguments + suspend fun executeActivity( + activity: KFunction3, // T::method with 2 args + arg1: A1, arg2: A2, + options: ActivityOptions + ): R + + // ... up to 6 arguments + + // Start activity (returns handle for async) + fun startActivity( + activity: KFunction3, + arg1: A1, arg2: A2, + options: ActivityOptions + ): KActivityHandle + + // Local activity - same pattern + suspend fun executeLocalActivity( + activity: KFunction2, + arg1: A1, + options: LocalActivityOptions + ): R + + // --- String-based overloads (untyped) --- + + // Execute activity by name + suspend inline fun executeActivity( + activityName: String, + vararg args: Any?, + options: ActivityOptions + ): R + + // Start activity by name (async) + inline fun startActivity( + activityName: String, + vararg args: Any?, + options: ActivityOptions + ): KActivityHandle + + // Execute local activity by name + suspend inline fun executeLocalActivity( + activityName: String, + vararg args: Any?, + options: LocalActivityOptions + ): R +} + +// Activity handle for async execution +interface KActivityHandle { + suspend fun await(): R + fun cancel() + val isCompleted: Boolean +} +``` + +> **Implementation Note:** `KFunction` provides `.name` for the method name and `.parameters[0].type` for the declaring interface. This metadata is used to make Temporal activity calls by name. + +### Activity Implementation + +There are two approaches for activity implementation, depending on whether you need Java interoperability. + +#### Option A: Pure Kotlin (Recommended for Kotlin-only codebases) + +For pure Kotlin codebases, define interfaces with `suspend` methods directly: + +```kotlin +@ActivityInterface +interface GreetingActivities { + @ActivityMethod + suspend fun composeGreeting(greeting: String, name: String): String + + @ActivityMethod + suspend fun sendEmail(email: Email): SendResult +} + +class GreetingActivitiesImpl( + private val emailService: EmailService +) : GreetingActivities { + + override suspend fun composeGreeting(greeting: String, name: String): String { + // Full coroutine support - use withContext, async I/O, etc. + return withContext(Dispatchers.IO) { + "$greeting, $name!" + } + } + + override suspend fun sendEmail(email: Email): SendResult { + // Suspend functions for async I/O + return emailService.send(email) + } +} + +// In workflow - direct method reference, no stub needed +val greeting = KWorkflow.executeActivity( + GreetingActivities::composeGreeting, + "Hello", "World", + ActivityOptions { startToCloseTimeout = 30.seconds } +) +``` + +#### Option B: Java Interoperability (Parallel Interface Pattern) + +When you need to share activity interfaces with Java code (e.g., activities implemented in Java, or interfaces defined in a shared Java module), use the parallel interface pattern: + +```kotlin +// Interface for workflow calls - non-suspend for Java compatibility +// This interface can be defined in Java or Kotlin +@ActivityInterface +interface OrderActivities { + @ActivityMethod + fun validateOrder(order: Order): Boolean + + @ActivityMethod + fun chargePayment(order: Order): PaymentResult +} + +// Parallel suspend interface for Kotlin implementation +// Linked to the original interface via annotation +@KActivityImpl(activities = OrderActivities::class) +interface OrderActivitiesSuspend { + suspend fun validateOrder(order: Order): Boolean + suspend fun chargePayment(order: Order): PaymentResult +} + +// Kotlin implementation uses the suspend interface +class OrderActivitiesImpl( + private val paymentService: PaymentService +) : OrderActivitiesSuspend { + + override suspend fun validateOrder(order: Order): Boolean { + return order.items.isNotEmpty() && order.total > 0 + } + + override suspend fun chargePayment(order: Order): PaymentResult { + // Full suspend support for async operations + return paymentService.charge(order) + } +} + +// In workflow - use the non-suspend interface for method references +val isValid = KWorkflow.executeActivity( + OrderActivities::validateOrder, + order, + ActivityOptions { startToCloseTimeout = 10.seconds } +) +``` + +**When to use which:** + +| Scenario | Approach | +|----------|----------| +| Pure Kotlin codebase | Option A - suspend interfaces | +| Calling Java-defined activities | Option A works (executeActivity handles it) | +| Kotlin activities with Java-defined interface | Option B - parallel interface | +| Shared interface library with Java | Option B - parallel interface | + +#### Registering Activities + +```kotlin +// Option A: Register suspend implementation directly +worker.registerActivitiesImplementations(GreetingActivitiesImpl(emailService)) + +// Option B: Register implementation - binding inferred from @KActivityImpl annotation +worker.registerActivitiesImplementations(OrderActivitiesImpl(paymentService)) +``` + +### Local Activities + +Local activities use the same stub-less pattern: + +```kotlin +@ActivityInterface +interface ValidationActivities { + fun validate(input: String): Boolean + fun sanitize(input: String): String +} + +val isValid = KWorkflow.executeLocalActivity( + ValidationActivities::validate, + input, + LocalActivityOptions { + startToCloseTimeout = 5.seconds + } +) + +val sanitized = KWorkflow.executeLocalActivity( + ValidationActivities::sanitize, + input, + LocalActivityOptions { + startToCloseTimeout = 1.seconds + } +) +``` + +> **Note:** Activity context, heartbeating, and async activity completion use the same APIs as the Java SDK (`Activity.getExecutionContext()`, `ActivityCompletionClient`, etc.). + +## Client API + +### Creating a Client + +```kotlin +val service = WorkflowServiceStubs.newLocalServiceStubs() + +val client = WorkflowClient(service) { + namespace = "default" + dataConverter = myConverter +} +``` + +### Starting Workflows + +```kotlin +// Execute workflow and wait for result - no stub needed +val result = client.executeWorkflow( + GreetingWorkflow::getGreeting, + "Temporal", + WorkflowOptions { + workflowId = "greeting-123" + taskQueue = "greeting-queue" + workflowExecutionTimeout = 1.hours + } +) + +// Or start async and get handle +val handle = client.startWorkflow( + GreetingWorkflow::getGreeting, + "Temporal", + WorkflowOptions { + workflowId = "greeting-123" + taskQueue = "greeting-queue" + } +) +val result = handle.result() // Type inferred as String from method reference +``` + +### SignalWithStart + +Atomically start a workflow and send a signal. If the workflow already exists, only the signal is sent: + +```kotlin +// Returns KTypedWorkflowHandle - result type captured from method reference +val handle = client.signalWithStart( + // Workflow to start + workflow = OrderWorkflow::processOrder, + workflowArg = order, + // Signal to send + signal = OrderWorkflow::cancelOrder, + signalArg = "Price changed", + // Options + options = WorkflowOptions { + workflowId = "order-123" + taskQueue = "orders" + } +) + +// Can use typed handle for queries/signals +val status = handle.query(OrderWorkflow::status) +val result = handle.result() // Type inferred as OrderResult +``` + +### UpdateWithStart + +Atomically start a workflow and send an update. If the workflow already exists, only the update is sent: + +```kotlin +// Returns Pair, Boolean> +// - handle with result type captured from workflow method reference +// - updateResult typed by update method return type +val (handle, updateResult: Boolean) = client.updateWithStart( + // Workflow to start + workflow = OrderWorkflow::processOrder, + workflowArg = order, + // Update to send + update = OrderWorkflow::addItem, + updateArg = newItem, + // Options + options = WorkflowOptions { + workflowId = "order-123" + taskQueue = "orders" + } +) +println("Item added: $updateResult") + +// Can use typed handle for further operations +val result = handle.result() // Type inferred as OrderResult +``` + +### Workflow Handle + +For interacting with existing workflows (signals, queries, results, cancellation), use a typed or untyped handle: + +```kotlin +// Get typed handle for existing workflow by ID (like Python's get_workflow_handle_for) +val handle = client.getKWorkflowHandle("order-123") + +// Send signal - method reference provides type safety +handle.signal(OrderWorkflow::cancelOrder, "Customer request") + +// Query - method reference with compile-time type checking +val status = handle.query(OrderWorkflow::status) +val count = handle.query(OrderWorkflow::getItemCount) + +// Get result (suspends until workflow completes) +val result = handle.result() + +// Updates - execute and wait for result +val updateResult = handle.executeUpdate(OrderWorkflow::addItem, newItem) + +// Or start update async and get handle +val updateHandle = handle.startUpdate(OrderWorkflow::addItem, newItem) +val asyncResult = updateHandle.result() + +// Cancel or terminate +handle.cancel() +handle.terminate("No longer needed") + +// Workflow metadata +val info = handle.describe() +println("Workflow ID: ${handle.workflowId}, Run ID: ${handle.runId}") +``` + +**KWorkflowHandle API:** + +```kotlin +// Base handle - returned by getKWorkflowHandle(id) +// Result type is unknown, must specify when calling result() +interface KWorkflowHandle { + val workflowId: String + val runId: String? + + // Result - requires explicit type since we don't know it + suspend fun result(): R + + // Signals - type-safe method references + suspend fun signal(method: KFunction1) + suspend fun signal(method: KFunction2, arg: A1) + suspend fun signal(method: KFunction3, arg1: A1, arg2: A2) + + // Queries - type-safe method references + fun query(method: KFunction1): R + fun query(method: KFunction2, arg: A1): R + + // Updates - execute and wait for result + suspend fun executeUpdate(method: KFunction1): R + suspend fun executeUpdate(method: KFunction2, arg: A1): R + + // Updates - start and get handle for async result + suspend fun startUpdate(method: KFunction1): KUpdateHandle + suspend fun startUpdate(method: KFunction2, arg: A1): KUpdateHandle + + // Get handle for existing update by ID + fun getKUpdateHandle(updateId: String): KUpdateHandle + + // Lifecycle + suspend fun cancel() + suspend fun terminate(reason: String? = null) + fun describe(): WorkflowExecutionInfo +} + +// Extended handle - returned by startWorkflow() +// Result type R is captured from the workflow method reference +interface KTypedWorkflowHandle : KWorkflowHandle { + // Result type is known from method reference - no type parameter needed + suspend fun result(): R +} + +interface KUpdateHandle { + val updateId: String + suspend fun result(): R +} +``` + +**How result type is captured:** + +```kotlin +// startWorkflow captures result type from method reference +suspend fun startWorkflow( + workflow: KFunction2, // R is captured here + arg: A1, + options: WorkflowOptions +): KTypedWorkflowHandle // R is preserved in return type + +// Usage - result type is inferred +val handle = client.startWorkflow( + OrderWorkflow::processOrder, // KFunction2 + order, + options +) +val result: OrderResult = handle.result() // No type parameter needed! + +// getKWorkflowHandle doesn't know result type +val existingHandle = client.getKWorkflowHandle(workflowId) +val result = existingHandle.result() // Must specify type +``` + +This pattern matches Python SDK's `WorkflowHandle` with the same method names (`signal`, `query`, `result`, `cancel`, `terminate`, `execute_update`, `start_update`). + +**Untyped Handle (like Python's `get_workflow_handle`):** + +For cases where you don't know the workflow type at compile time: + +```kotlin +// Untyped handle - signal/query by string name +val untypedHandle = client.getWorkflowHandle("order-123") + +// Operations use string names instead of method references +untypedHandle.signal("cancelOrder", "Customer request") +val status = untypedHandle.query("status") +val result = untypedHandle.result() + +// Cancel/terminate work the same +untypedHandle.cancel() +``` + +```kotlin +interface WorkflowHandle { + val workflowId: String + val runId: String? + + suspend fun result(): R + suspend fun signal(signalName: String, vararg args: Any?) + fun query(queryName: String, vararg args: Any?): R + suspend fun executeUpdate(updateName: String, vararg args: Any?): Any? + suspend fun cancel() + suspend fun terminate(reason: String? = null) + fun describe(): WorkflowExecutionInfo +} +``` + +## Worker API + +### Enabling Kotlin Coroutine Support + +Kotlin coroutine support is enabled via a plugin passed to the client (similar to Python SDK's plugin system). The plugin provides custom workflow and activity executors that handle `suspend` functions. + +```kotlin +val service = WorkflowServiceStubs.newLocalServiceStubs() + +// Plugin is passed to the client and propagates to workers +val client = WorkflowClient(service) { + plugins = listOf(KotlinPlugin()) +} + +val factory = WorkerFactory(client) { + maxWorkflowThreadCount = 800 +} + +val worker = factory.newWorker("task-queue") { + maxConcurrentActivityExecutionSize = 100 +} + +// Register Kotlin coroutine workflows +worker.registerWorkflowImplementationTypes( + GreetingWorkflowImpl::class, + OrderWorkflowImpl::class +) + +// Register activities - plugin detects suspend functions automatically +worker.registerActivitiesImplementations( + GreetingActivitiesImpl(), // Kotlin suspend activities + JavaActivitiesImpl() // Java activities work too +) + +// Start the worker +factory.start() +``` + +> **Note:** The plugin API will be defined separately. It follows the same pattern as Python SDK's plugin system, allowing custom workflow runners and activity executors. + +### Mixed Java and Kotlin + +A single worker supports both Java and Kotlin workflows on the same task queue: + +```kotlin +// Java workflows (thread-based) +worker.registerWorkflowImplementationTypes( + OrderWorkflowJavaImpl::class.java +) + +// Kotlin workflows (coroutine-based) - same method, plugin handles execution +worker.registerWorkflowImplementationTypes( + GreetingWorkflowImpl::class +) + +// Both run on the same worker - execution model is per-workflow-instance +factory.start() +``` + +## Data Conversion + +The Kotlin SDK uses `kotlinx.serialization` by default for JSON serialization. It provides compile-time safety, no reflection overhead, and native Kotlin support. + +### kotlinx.serialization (Default) + +Annotate data classes with `@Serializable`: + +```kotlin +@Serializable +data class Order( + val id: String, + val items: List, + val status: OrderStatus +) + +@Serializable +data class OrderItem( + val productId: String, + val quantity: Int +) + +@Serializable +enum class OrderStatus { PENDING, PROCESSING, COMPLETED } +``` + +No additional configuration needed—the SDK automatically uses `kotlinx.serialization` for classes annotated with `@Serializable`. + +**Custom JSON configuration:** + +```kotlin +val client = WorkflowClient(service) { + dataConverter = KotlinxSerializationDataConverter { + ignoreUnknownKeys = true + prettyPrint = false // default + encodeDefaults = true + } +} +``` + +### Jackson (Optional, for Java Interop) + +For mixed Java/Kotlin codebases or when integrating with existing Jackson-based infrastructure: + +```kotlin +val converter = DefaultDataConverter.newDefaultInstance().withPayloadConverterOverrides( + JacksonJsonPayloadConverter( + KotlinObjectMapperFactory.new() + ) +) + +val client = WorkflowClient(service) { + dataConverter = converter +} +``` + +> **Note:** Jackson requires the `jackson-module-kotlin` dependency and uses runtime reflection. Prefer `kotlinx.serialization` for pure Kotlin projects. + +## Interceptors + +### Kotlin Interceptor Interface + +```kotlin +interface KWorkerInterceptor { + fun interceptWorkflow(next: KWorkflowInboundCallsInterceptor): KWorkflowInboundCallsInterceptor { + return next + } + + fun interceptActivity(next: KActivityInboundCallsInterceptor): KActivityInboundCallsInterceptor { + return next + } +} + +interface KWorkflowInboundCallsInterceptor { + suspend fun init(outboundCalls: KWorkflowOutboundCallsInterceptor) + suspend fun execute(input: WorkflowInput): WorkflowOutput + suspend fun handleSignal(input: SignalInput) + suspend fun handleUpdate(input: UpdateInput): UpdateOutput + fun handleQuery(input: QueryInput): QueryOutput +} + +interface KWorkflowOutboundCallsInterceptor { + suspend fun executeActivity(input: ActivityInput): ActivityOutput + fun startActivity(input: ActivityInput): KActivityHandle + suspend fun executeChildWorkflow(input: ChildWorkflowInput): ChildWorkflowOutput + fun startChildWorkflow(input: ChildWorkflowInput): ChildWorkflowHandle +} + +interface KActivityInboundCallsInterceptor { + suspend fun execute(input: ActivityInput): ActivityOutput +} +``` + +### Example: Logging Interceptor + +```kotlin +class LoggingInterceptor : KWorkerInterceptor { + override fun interceptWorkflow( + next: KWorkflowInboundCallsInterceptor + ): KWorkflowInboundCallsInterceptor { + return object : KWorkflowInboundCallsInterceptorBase(next) { + override suspend fun execute(input: WorkflowInput): WorkflowOutput { + log.info("Workflow started: ${input.workflowType}") + return try { + super.execute(input) + } finally { + log.info("Workflow completed: ${input.workflowType}") + } + } + } + } +} +``` + +### Registering Interceptors + +Kotlin interceptors are registered via the `KotlinPlugin`, which propagates them to all workers: + +```kotlin +val client = WorkflowClient(service) { + plugins = listOf( + KotlinPlugin { + workerInterceptors = listOf( + LoggingInterceptor(), + MetricsInterceptor() + ) + } + ) +} +``` + +## Migration from Java SDK + +### API Mapping + +| Java SDK | Kotlin SDK | +|----------|------------| +| **Activities** | | +| `Workflow.newUntypedActivityStub(opts)` | *(not needed - options passed per call)* | +| `activities.execute("name", Cls, arg)` | `KWorkflow.executeActivity("name", arg, options)` | +| `stub.method(arg)` (typed activity) | `KWorkflow.executeActivity(Interface::method, arg, options)` | +| `Async.function(stub::method, arg)` | `KWorkflow.startActivity(Interface::method, arg, options)` | +| **Workflows** | | +| `Workflow.newChildWorkflowStub(...)` | `KWorkflow.executeChildWorkflow(Interface::method, ...)` | +| `client.newWorkflowStub(...)` | `client.startWorkflow(Interface::method, ...)` → `KTypedWorkflowHandle` | +| `client.newWorkflowStub(Cls, id)` | `client.getKWorkflowHandle(id)` → `KWorkflowHandle` | +| `stub.signal(arg)` | `handle.signal(T::method, arg)` | +| `stub.query()` | `handle.query(T::method)` | +| `handle.getResult()` | `handle.result()` (type inferred) or `handle.result()` | +| **Primitives** | | +| `Workflow.sleep(duration)` | `delay(duration)` | +| `Workflow.await(() -> cond)` | `KWorkflow.condition { cond }` | +| `Promise` | `Deferred` / `KActivityHandle` | +| `Optional` | `T?` | +| `Duration.ofSeconds(30)` | `30.seconds` | + +### Before (Java) + +```java +@WorkflowInterface +public interface GreetingWorkflow { + @WorkflowMethod + String getGreeting(String name); +} + +public class GreetingWorkflowImpl implements GreetingWorkflow { + @Override + public String getGreeting(String name) { + ActivityStub activities = Workflow.newUntypedActivityStub( + ActivityOptions.newBuilder() + .setStartToCloseTimeout(Duration.ofSeconds(30)) + .build() + ); + return activities.execute("greet", String.class, name); + } +} +``` + +### After (Kotlin) + +```kotlin +@WorkflowInterface +interface GreetingWorkflow { + @WorkflowMethod + suspend fun getGreeting(name: String): String +} + +class GreetingWorkflowImpl : GreetingWorkflow { + override suspend fun getGreeting(name: String): String { + return KWorkflow.executeActivity( + "greet", + name, + ActivityOptions { startToCloseTimeout = 30.seconds } + ) + } +} +``` + +### Interoperability + +Kotlin workflows can call Java activities using direct method references: + +```kotlin +override suspend fun processOrder(order: Order): String { + // JavaActivities is a Java @ActivityInterface - no stub needed + return KWorkflow.executeActivity( + JavaActivities::process, + order, + ActivityOptions { startToCloseTimeout = 30.seconds } + ) +} +``` + +Java workflows can be invoked from Kotlin clients: + +```kotlin +val result = client.executeWorkflow( + JavaWorkflowInterface::execute, + input, + WorkflowOptions { taskQueue = "java-queue" } +) +``` + +## Complete Example + +This example uses Option A (pure Kotlin with suspend interfaces). + +```kotlin +// === Domain Types === + +@Serializable +data class Order( + val id: String, + val customerId: String, + val items: List, + val priority: Priority = Priority.NORMAL +) { + val total: BigDecimal get() = items.sumOf { it.price * it.quantity.toBigDecimal() } +} + +@Serializable +data class OrderItem( + val productId: String, + val quantity: Int, + val price: BigDecimal +) + +@Serializable +data class OrderResult(val success: Boolean, val trackingNumber: String?) + +enum class OrderStatus { PENDING, PROCESSING, SHIPPED, CANCELED } +enum class Priority { LOW, NORMAL, HIGH } + +// === Workflow Interface === + +@WorkflowInterface +interface OrderWorkflow { + @WorkflowMethod + suspend fun processOrder(order: Order): OrderResult + + @SignalMethod + suspend fun cancelOrder(reason: String) + + @UpdateMethod + suspend fun addItem(item: OrderItem): Boolean + + @UpdateValidatorMethod(updateMethod = "addItem") + fun validateAddItem(item: OrderItem) + + @QueryMethod + val status: OrderStatus + + @QueryMethod + val progress: Int +} + +// === Activity Interface === + +@ActivityInterface +interface OrderActivities { + @ActivityMethod + suspend fun validateOrder(order: Order): Boolean + + @ActivityMethod + suspend fun reserveInventory(item: OrderItem): Boolean + + @ActivityMethod + suspend fun chargePayment(order: Order): Boolean + + @ActivityMethod + suspend fun shipOrder(order: Order): String +} + +// === Activity Implementation === + +class OrderActivitiesImpl( + private val inventoryService: InventoryService, + private val paymentService: PaymentService, + private val shippingService: ShippingService +) : OrderActivities { + + override suspend fun validateOrder(order: Order): Boolean { + return order.items.isNotEmpty() && order.items.all { it.quantity > 0 } + } + + override suspend fun reserveInventory(item: OrderItem): Boolean { + return inventoryService.reserve(item.productId, item.quantity) + } + + override suspend fun chargePayment(order: Order): Boolean { + return paymentService.charge(order.customerId, order.total) + } + + override suspend fun shipOrder(order: Order): String { + return shippingService.createShipment(order) + } +} + +// === Workflow Implementation === + +class OrderWorkflowImpl : OrderWorkflow { + private var _status = OrderStatus.PENDING + private var _progress = 0 + private var canceled = false + + override val status get() = _status + override val progress get() = _progress + + // Reusable options for common cases + private val defaultOptions = ActivityOptions { + startToCloseTimeout = 30.seconds + retryOptions = RetryOptions { + initialInterval = 1.seconds + maximumAttempts = 3 + } + } + + override suspend fun processOrder(order: Order): OrderResult = coroutineScope { + _status = OrderStatus.PROCESSING + + // Validate order - direct method reference, no stub needed + _progress = 10 + val isValid = KWorkflow.executeActivity( + OrderActivities::validateOrder, + order, + ActivityOptions { startToCloseTimeout = 10.seconds } + ) + if (!isValid) { + return@coroutineScope OrderResult(success = false, trackingNumber = null) + } + + // Check for cancellation + if (canceled) { + _status = OrderStatus.CANCELED + return@coroutineScope OrderResult(success = false, trackingNumber = null) + } + + // Process items in parallel + _progress = 30 + order.items.map { item -> + async { + KWorkflow.executeActivity( + OrderActivities::reserveInventory, + item, + defaultOptions + ) + } + }.awaitAll() + + _progress = 60 + + // Charge payment - longer timeout for payment processing + val charged = KWorkflow.executeActivity( + OrderActivities::chargePayment, + order, + ActivityOptions { + startToCloseTimeout = 2.minutes + retryOptions = RetryOptions { + initialInterval = 5.seconds + maximumAttempts = 5 + } + } + ) + if (!charged) { + return@coroutineScope OrderResult(success = false, trackingNumber = null) + } + + _progress = 80 + + // Ship order + val trackingNumber = KWorkflow.executeActivity( + OrderActivities::shipOrder, + order, + defaultOptions + ) + + _status = OrderStatus.SHIPPED + _progress = 100 + + OrderResult(success = true, trackingNumber = trackingNumber) + } + + override suspend fun cancelOrder(reason: String) { + canceled = true + _status = OrderStatus.CANCELED + } + + override fun validateAddItem(item: OrderItem) { + require(item.quantity > 0) { "Quantity must be positive" } + require(item.price >= BigDecimal.ZERO) { "Price cannot be negative" } + require(_status == OrderStatus.PENDING) { "Cannot add items after processing started" } + } + + override suspend fun addItem(item: OrderItem): Boolean { + // Validator already checked status, so this is safe + // Update would modify order items here + return true + } +} + +// === Worker Setup === + +fun main() = runBlocking { + // Initialize services (could use DI framework) + val inventoryService = InventoryServiceImpl() + val paymentService = PaymentServiceImpl() + val shippingService = ShippingServiceImpl() + + val service = WorkflowServiceStubs.newLocalServiceStubs() + + // Enable Kotlin coroutine support via plugin + val client = WorkflowClient(service) { + plugins = listOf(KotlinPlugin()) + } + + val factory = WorkerFactory(client) + val worker = factory.newWorker("orders") + + // Plugin handles suspend functions automatically + worker.registerWorkflowImplementationTypes(OrderWorkflowImpl::class) + worker.registerActivitiesImplementations( + OrderActivitiesImpl(inventoryService, paymentService, shippingService) + ) + + factory.start() + + // === Client Usage === + + val order = Order( + id = "12345", + customerId = "cust-789", + items = listOf( + OrderItem("prod-1", 2, 29.99.toBigDecimal()), + OrderItem("prod-2", 1, 49.99.toBigDecimal()) + ) + ) + + val workflowId = "order-${UUID.randomUUID()}" + + // Start workflow and get handle + val handle = client.startWorkflow( + OrderWorkflow::processOrder, + order, + WorkflowOptions { + workflowId = workflowId + taskQueue = "orders" + } + ) + println("Started workflow: ${handle.workflowId}") + + // Query workflow state using typed handle + val status = handle.query(OrderWorkflow::status) + val progress = handle.query(OrderWorkflow::progress) + println("Status: $status, Progress: $progress%") + + // Or get handle for existing workflow by ID + val existingHandle = client.getKWorkflowHandle(workflowId) + + // Send update and wait for result + val newItem = OrderItem("prod-3", 1, 19.99.toBigDecimal()) + val added = existingHandle.executeUpdate(OrderWorkflow::addItem, newItem) + println("Item added: $added") + + // Send signal if needed + // existingHandle.signal(OrderWorkflow::cancelOrder, "Customer request") + + // Wait for result - type inferred from startWorkflow method reference + val result = handle.result() + println("Order result: $result") +} +``` diff --git a/kotlin/sdk-implementation.md b/kotlin/sdk-implementation.md new file mode 100644 index 0000000..6889718 --- /dev/null +++ b/kotlin/sdk-implementation.md @@ -0,0 +1,1012 @@ +# Kotlin SDK Implementation Details + +This document describes the internal architecture and implementation details for the Temporal Kotlin SDK. + +For public API and developer experience, see [sdk-api.md](./sdk-api.md). + +## Phases + +* **Phase 1** - Coroutine-based workflows, untyped activity stubs, pluggable WorkflowImplementationFactory, core Kotlin idioms (Duration, null safety) +* **Phase 2** - Typed activity stubs with suspend functions, signals/queries/updates, child workflows, property queries +* **Phase 3** - Testing framework + +> **Note:** Nexus support is a separate project and will be addressed independently. + +## Relationship to Java SDK + +The Kotlin SDK builds on top of the Java SDK rather than replacing it: + +* **Shared infrastructure**: Uses the same gRPC client, data conversion, and service client +* **Interoperability**: Kotlin workflows can call Java activities and vice versa +* **Gradual adoption**: Teams can mix Java and Kotlin workflows in the same worker +* **Existing module**: Extends the existing `temporal-kotlin` module which already provides DSL extensions + +## Repository/Package Strategy + +### Repository + +The Kotlin SDK will continue to live in the `temporal-kotlin` module within the `sdk-java` repository: + +* Pros: + * Shared build infrastructure + * Easier to maintain version compatibility + * Single release process +* Cons: + * Ties Kotlin SDK releases to Java SDK releases + +### Package Naming + +* Core workflow APIs: `io.temporal.kotlin.workflow` +* Worker APIs: `io.temporal.kotlin.worker` +* Interceptors: `io.temporal.kotlin.interceptors` +* Existing extensions remain in their current packages (e.g., `io.temporal.client`) + +### Maven Artifacts + +```xml + + io.temporal + temporal-kotlin + N.N.N + +``` + +Additional dependency on kotlinx-coroutines: +```xml + + org.jetbrains.kotlinx + kotlinx-coroutines-core + 1.7.x + +``` + +## Existing Kotlin Classes (Retained) + +The `temporal-kotlin` module already provides Kotlin extensions for the Java SDK: + +| File | Purpose | Status | +|------|---------|--------| +| `TemporalDsl.kt` | `@DslMarker` for type-safe builders | **Keep as-is** | +| `*OptionsExt.kt` (15 files) | DSL builders for all Options classes | **Keep as-is** | +| `WorkflowClientExt.kt` | Reified `newWorkflowStub()`, DSL extensions | **Keep as-is** | +| `WorkflowStubExt.kt` | Reified `getResult()` | **Keep as-is** | +| `WorkerFactoryExt.kt` | `WorkerFactory()` constructor-like DSL | **Keep as-is** | +| `WorkerExt.kt` | Reified `registerWorkflowImplementationType()` | **Keep as-is** | +| `WorkflowMetadata.kt` | `workflowName()`, `workflowSignalName()` | **Keep as-is** | +| `ActivityMetadata.kt` | `activityName(Interface::method)` | **Keep as-is** | +| `KotlinObjectMapperFactory.kt` | Jackson ObjectMapper for Kotlin | **Keep as-is** | +| `KotlinMethodReferenceDisassemblyService.kt` | Kotlin method references in `Async` | **Keep as-is** | + +## New Classes (Kotlin SDK) + +| Class | Purpose | +|-------|---------| +| **Core Workflow** | | +| `KWorkflow` | Entry point for workflow APIs (like `Workflow` in Java) | +| `KotlinWorkflowContext` | Internal workflow execution context | +| `KotlinCoroutineDispatcher` | Deterministic coroutine dispatcher | +| `KWorkerInterceptor` | Interceptor interface with suspend functions | +| **Factory/Registration** | | +| `KotlinPlugin` | Plugin for enabling coroutine support and registering interceptors | +| `KotlinWorkflowImplementationFactory` | Implements `WorkflowImplementationFactory` for coroutine workflows | +| `KotlinWorkflowDefinition` | Metadata about a Kotlin workflow type | +| `KotlinReplayWorkflow` | Implements `ReplayWorkflow` using coroutines | +| `WorkerExt.kt` (additions) | Extension `registerKotlinWorkflowImplementationTypes()` | +| **Kotlin Wrappers** | | +| `KWorkflowInfo` | Kotlin wrapper for WorkflowInfo with nullable types | +| `KActivityInfo` | Kotlin wrapper for ActivityInfo with nullable types | +| `KActivityContext` | Kotlin wrapper for ActivityExecutionContext | +| `WorkflowHandle` | Untyped workflow handle (string-based signals/queries) | +| `KWorkflowHandle` | Typed workflow handle for signals/queries/updates | +| `KTypedWorkflowHandle` | Extends KWorkflowHandle with typed result (returned by startWorkflow) | +| `KActivityHandle` | Handle for async activity execution | +| `KUpdateHandle` | Handle for async update execution | +| **Extensions** | | +| `DurationExt.kt` | Conversions between `kotlin.time.Duration` and `java.time.Duration` | + +## Unified Worker Architecture + +The SDK uses a **single unified `WorkerFactory`** that supports both Java and Kotlin workflows. The execution model (thread-based vs coroutine-based) is determined **per workflow instance**, not per worker. This is achieved through a pluggable `WorkflowImplementationFactory` interface. + +``` +┌─────────────────────────────────────────────────────────┐ +│ WorkerFactory │ +│ │ +│ registerWorkflowImplementationTypes(...) ─────────┐ │ +│ registerWorkflowImplementationFactory(...) │ │ +│ ▼ │ +│ ┌─────────────────────────────────────────────────┐ │ +│ │ WorkflowImplementationFactory Registry │ │ +│ │ │ │ +│ │ ┌─────────────────┐ ┌──────────────────────┐ │ │ +│ │ │ POJOWorkflow │ │ KotlinWorkflow │ │ │ +│ │ │ Implementation │ │ Implementation │ │ │ +│ │ │ Factory │ │ Factory │ │ │ +│ │ │ (Java SDK) │ │ (temporal-kotlin) │ │ │ +│ │ └────────┬────────┘ └──────────┬───────────┘ │ │ +│ └───────────┼──────────────────────┼─────────────┘ │ +└──────────────┼──────────────────────┼─────────────────┘ + │ │ + ▼ ▼ +┌──────────────────────┐ ┌──────────────────────────────┐ +│ Java workflow task │ │ Kotlin workflow task │ +│ │ │ │ +│ → DeterministicRunner│ │ → KotlinCoroutineDispatcher│ +│ → Thread-based exec │ │ → Coroutine-based exec │ +└──────────────────────┘ └──────────────────────────────┘ +``` + +**Key benefits:** + +| Benefit | Description | +|---------|-------------| +| Single worker type | No need for separate `KotlinWorkerFactory` | +| Mixed workflows | Java and Kotlin workflows on the same task queue | +| Gradual migration | Convert workflows one at a time | +| No Kotlin in Java SDK | All coroutine code stays in `temporal-kotlin` | +| Per-instance execution | Dispatcher is per workflow instance, not per worker | + +## Java SDK Refactoring for Pluggability + +To support the Kotlin coroutine-based execution model, the Java SDK requires refactoring to make workflow execution pluggable. The key principle is that the Java SDK remains **completely Kotlin-agnostic**—it only provides extension points that the `temporal-kotlin` module can use. + +### Required Changes to `temporal-sdk` + +#### 1. New `WorkflowImplementationFactory` Interface + +**File:** `io.temporal.worker.WorkflowImplementationFactory` (new) + +A pluggable interface for creating workflow instances with different execution models: + +```java +/** + * Factory for creating workflow implementations. Different implementations + * can provide different execution models (e.g., thread-based, coroutine-based). + */ +public interface WorkflowImplementationFactory { + /** + * Returns true if this factory handles the given workflow implementation type. + * Used to route workflow registration to the appropriate factory. + */ + boolean supportsType(Class workflowImplementationType); + + /** + * Register workflow implementation types with this factory. + */ + void registerWorkflowImplementationTypes( + WorkflowImplementationOptions options, + Class... workflowImplementationTypes + ); + + /** + * Register a workflow implementation factory function. + */ + void addWorkflowImplementationFactory( + WorkflowImplementationOptions options, + Class workflowInterface, + Functions.Func factory + ); + + /** + * Create a ReplayWorkflow instance for the given workflow type. + * Returns null if this factory doesn't handle the given type. + */ + @Nullable + ReplayWorkflow createWorkflow( + WorkflowType workflowType, + WorkflowExecution workflowExecution + ); + + /** + * Returns the set of workflow types registered with this factory. + */ + Set getRegisteredWorkflowTypes(); +} +``` + +#### 2. Update `Worker` to Support Multiple Factories + +**File:** `io.temporal.worker.Worker` + +```java +public final class Worker { + // Default factory for Java workflows (existing behavior) + private final POJOWorkflowImplementationFactory defaultFactory; + + // Additional factories (e.g., for Kotlin coroutine workflows) + private final List additionalFactories = new ArrayList<>(); + + /** + * Register a custom workflow implementation factory. + * The factory will be consulted for workflow types it supports. + */ + public void registerWorkflowImplementationFactory(WorkflowImplementationFactory factory) { + additionalFactories.add(factory); + } + + // Existing method unchanged - uses default POJO factory + public void registerWorkflowImplementationTypes(Class... workflowImplementationTypes) { + defaultFactory.registerWorkflowImplementationTypes(options, workflowImplementationTypes); + } +} +``` + +#### 3. Update `SyncWorkflowWorker` to Use Factory Registry + +**File:** `io.temporal.internal.worker.SyncWorkflowWorker` + +Modify to consult all registered factories when creating workflow instances: + +```java +class CompositeReplayWorkflowFactory implements ReplayWorkflowFactory { + private final POJOWorkflowImplementationFactory defaultFactory; + private final List additionalFactories; + + @Override + public ReplayWorkflow getWorkflow(WorkflowType workflowType, WorkflowExecution execution) { + // First, check additional factories + for (WorkflowImplementationFactory factory : additionalFactories) { + ReplayWorkflow workflow = factory.createWorkflow(workflowType, execution); + if (workflow != null) { + return workflow; + } + } + // Fall back to default POJO factory + return defaultFactory.getWorkflow(workflowType, execution); + } + + @Override + public boolean isAnyTypeSupported() { + return defaultFactory.isAnyTypeSupported() || + additionalFactories.stream().anyMatch(f -> !f.getRegisteredWorkflowTypes().isEmpty()); + } +} +``` + +#### 4. Expose Required Internal Classes + +Some internal classes need to be accessible for custom `WorkflowImplementationFactory` implementations: + +**File:** `io.temporal.internal.replay.ReplayWorkflow` → Move to SPI package or make accessible + +```java +// Either move to io.temporal.worker.spi or document as semi-public API +public interface ReplayWorkflow { + void start(HistoryEvent event, ReplayWorkflowContext context); + boolean eventLoop() throws Throwable; + WorkflowExecutionResult getOutput(); + void cancel(String reason); + void close(); + // ... other methods +} +``` + +**File:** `io.temporal.internal.replay.ReplayWorkflowContext` → Similar treatment + +The exact approach (SPI package vs `@InternalApi` annotation) is discussed in [Open Question #6: SPI Stability](#6-spi-stability). + +#### 5. Add Async Version of `Workflow.await()` + +**File:** `io.temporal.workflow.Async` + +The existing `Workflow.await()` is blocking with no `Promise`-based equivalent. To support Kotlin coroutines, we need an async version in the `Async` class (consistent with `Async.function()`, `Async.procedure()`, etc.): + +```java +public final class Async { + // Existing methods + public static Promise function(Functions.Func func); + public static Promise procedure(Functions.Proc proc); + // ... + + // New async await methods for coroutine support + public static Promise await(Supplier condition); + public static Promise await(Duration timeout, Supplier condition); +} +``` + +This allows Kotlin to wrap it as a suspend function in `KWorkflow`: + +```kotlin +/** + * Suspends until the condition becomes true. + * + * This is the Kotlin equivalent of Java's [Workflow.await]. + * The condition is re-evaluated after each workflow event. + */ +suspend fun condition(condition: () -> Boolean) { + Async.await { condition() }.await() // Promise.await() suspends +} + +/** + * Suspends until the condition becomes true or timeout expires. + * + * This is the Kotlin equivalent of Java's [Workflow.await] with timeout. + * + * @return true if condition became true, false if timed out + */ +suspend fun condition(timeout: Duration, condition: () -> Boolean): Boolean { + return Async.await(timeout.toJava()) { condition() }.await() +} +``` + +**Implementation notes:** +- **Prerequisite:** `Async.await()` does not currently exist in the Java SDK and must be added before `KWorkflow.condition()` can be implemented. +- Under the hood, this uses `Async.await()` which returns a `Promise` that completes when the condition becomes true. The condition is re-evaluated after each workflow event (signals, activity completions, timers, etc.). +- The async version should integrate with the same condition-tracking mechanism used by the blocking `Workflow.await()`, completing the Promise when the condition becomes true or timeout expires. + +### Files Changed Summary + +| File | Change Type | Description | +|------|-------------|-------------| +| `WorkflowImplementationFactory.java` | **New** | Pluggable factory interface | +| `Worker.java` | **Modified** | Add `registerWorkflowImplementationFactory()` method | +| `SyncWorkflowWorker.java` | **Modified** | Use composite factory for workflow creation | +| `ReplayWorkflow.java` | **Modified** | Make accessible for SPI implementations | +| `ReplayWorkflowContext.java` | **Modified** | Make accessible for SPI implementations | +| `POJOWorkflowImplementationFactory.java` | **Modified** | Implement new interface | +| `Async.java` | **Modified** | Add `await()` methods returning `Promise` | + +### Backward Compatibility + +These changes maintain full backward compatibility: + +* `Worker.registerWorkflowImplementationTypes()` unchanged for existing users +* Existing Java workflows continue to work without modification +* New `registerWorkflowImplementationFactory()` is purely additive +* No Kotlin dependencies in `temporal-sdk` module +* No breaking changes to public APIs + +## Kotlin Module Implementation + +The `temporal-kotlin` module provides the coroutine-based implementation: + +```kotlin +// In temporal-kotlin module +class KotlinWorkflowImplementationFactory( + private val clientOptions: WorkflowClientOptions, + private val workerOptions: WorkerOptions, + private val cache: WorkflowExecutorCache +) : WorkflowImplementationFactory { + + private val registeredTypes = mutableMapOf() + + override fun supportsType(type: Class<*>): Boolean { + // Check if workflow methods are suspend functions (have Continuation parameter) + return type.methods.any { method -> + method.isAnnotationPresent(WorkflowMethod::class.java) && + method.parameterTypes.any { it.name == "kotlin.coroutines.Continuation" } + } + } + + override fun registerWorkflowImplementationTypes( + options: WorkflowImplementationOptions, + vararg types: Class<*> + ) { + for (type in types) { + require(supportsType(type)) { + "Class ${type.name} does not have suspend workflow methods" + } + val definition = KotlinWorkflowDefinition(type, options) + registeredTypes[definition.workflowType] = definition + } + } + + override fun createWorkflow( + workflowType: WorkflowType, + workflowExecution: WorkflowExecution + ): ReplayWorkflow? { + val definition = registeredTypes[workflowType.name] ?: return null + return KotlinReplayWorkflow( + definition, + workflowExecution, + KotlinCoroutineDispatcher() + ) + } + + override fun getRegisteredWorkflowTypes(): Set = registeredTypes.keys +} +``` + +```kotlin +// Kotlin extension for ergonomic registration +fun Worker.registerKotlinWorkflowImplementationTypes(vararg types: KClass<*>) { + val factory = KotlinWorkflowImplementationFactory(/* obtain from worker */) + for (type in types) { + factory.registerWorkflowImplementationTypes( + WorkflowImplementationOptions.getDefaultInstance(), + type.java + ) + } + registerWorkflowImplementationFactory(factory) +} +``` + +## KotlinCoroutineDispatcher + +The custom dispatcher ensures deterministic execution of coroutines: + +* Executes coroutines in a controlled, deterministic order +* Integrates with Temporal's replay mechanism +* Supports `delay()` by mapping to Temporal timers +* Handles cancellation scopes properly + +```kotlin +internal class KotlinCoroutineDispatcher( + private val workflowContext: KotlinWorkflowContext +) : CoroutineDispatcher() { + + private val readyQueue = ArrayDeque() + private var inEventLoop = false + + override fun dispatch(context: CoroutineContext, block: Runnable) { + // Queue for deterministic execution + readyQueue.addLast(block) + + // If we're already in the event loop, the block will be picked up + // Otherwise, we need to signal the workflow to continue + if (!inEventLoop) { + workflowContext.signalReady() + } + } + + /** + * Process queued coroutines deterministically. + * Called by the replay machinery during workflow task processing. + */ + fun eventLoop(deadlockDetectionTimeout: Long): Boolean { + inEventLoop = true + try { + val deadline = System.currentTimeMillis() + deadlockDetectionTimeout + + while (readyQueue.isNotEmpty()) { + if (System.currentTimeMillis() > deadline) { + throw PotentialDeadlockException("Workflow thread stuck") + } + + val runnable = readyQueue.removeFirst() + runnable.run() + } + + return workflowContext.hasMoreWork() + } finally { + inEventLoop = false + } + } +} +``` + +### Delay Implementation + +The `delay()` function is intercepted to create Temporal timers: + +```kotlin +internal class KotlinDelay : Delay { + override fun scheduleResumeAfterDelay( + timeMillis: Long, + continuation: CancellableContinuation + ) { + val timer = workflowContext.createTimer(Duration.ofMillis(timeMillis)) + + timer.thenAccept { + continuation.resume(Unit) + } + + continuation.invokeOnCancellation { + timer.cancel() + } + } +} +``` + +## KotlinReplayWorkflow + +Implements the `ReplayWorkflow` interface using coroutines: + +```kotlin +internal class KotlinReplayWorkflow( + private val definition: KotlinWorkflowDefinition, + private val workflowExecution: WorkflowExecution, + private val dispatcher: KotlinCoroutineDispatcher +) : ReplayWorkflow { + + private var workflowJob: Job? = null + private var result: WorkflowExecutionResult? = null + private lateinit var context: KotlinWorkflowContext + + override fun start(event: HistoryEvent, replayContext: ReplayWorkflowContext) { + context = KotlinWorkflowContext(replayContext) + + // Create coroutine scope with our deterministic dispatcher + val scope = CoroutineScope( + dispatcher + + KotlinDelay(context) + + SupervisorJob() + ) + + // Start the workflow coroutine + workflowJob = scope.launch { + try { + val instance = definition.createInstance() + val output = definition.invokeWorkflowMethod(instance, event.arguments) + result = WorkflowExecutionResult.completed(output) + } catch (e: CancellationException) { + result = WorkflowExecutionResult.canceled(e.message) + } catch (e: Throwable) { + result = WorkflowExecutionResult.failed(e) + } + } + } + + override fun eventLoop(): Boolean { + return dispatcher.eventLoop(deadlockDetectionTimeout) + } + + override fun getOutput(): WorkflowExecutionResult? = result + + override fun cancel(reason: String) { + workflowJob?.cancel(CancellationException(reason)) + } + + override fun close() { + workflowJob?.cancel() + } +} +``` + +## KotlinWorkflowContext + +Provides workflow APIs to the coroutine-based workflow: + +```kotlin +internal class KotlinWorkflowContext( + private val replayContext: ReplayWorkflowContext +) { + fun createTimer(duration: Duration): CompletableFuture { + return replayContext.createTimer(duration) + } + + fun executeActivity( + name: String, + options: ActivityOptions, + args: Array + ): CompletableFuture { + return replayContext.executeActivity(name, options, args) + } + + fun executeChildWorkflow( + type: String, + options: ChildWorkflowOptions, + args: Array + ): CompletableFuture { + return replayContext.executeChildWorkflow(type, options, args) + } + + fun sideEffect(func: () -> Any?): Any? { + return replayContext.sideEffect(func) + } + + fun getVersion(changeId: String, minSupported: Int, maxSupported: Int): Int { + return replayContext.getVersion(changeId, minSupported, maxSupported) + } + + fun currentTime(): Instant = replayContext.currentTimeMillis().let { + Instant.ofEpochMilli(it) + } + + // ... other workflow APIs +} +``` + +## Activity Execution Implementation + +### String-based Activity Execution + +String-based activity execution is implemented directly in `KWorkflow` using **reified generics** to provide a clean API without requiring explicit `Class<*>` parameters or casts: + +```kotlin +object KWorkflow { + /** + * Execute an activity by name with reified return type. + * + * Usage: KWorkflow.executeActivity("activityName", arg1, arg2, options) + * + * The `inline` + `reified` combination allows access to R::class.java at runtime. + * At each call site, the compiler substitutes the actual type. + */ + inline suspend fun executeActivity( + activityName: String, + vararg args: Any?, + options: ActivityOptions + ): R { + return executeActivityInternal(activityName, R::class.java, args, options) as R + } + + /** + * Start an activity by name (async). + */ + inline fun startActivity( + activityName: String, + vararg args: Any?, + options: ActivityOptions + ): KActivityHandle { + return startActivityInternal(activityName, R::class.java, args, options) + } + + /** + * Internal implementation that receives the actual Class for DataConverter. + */ + @PublishedApi + internal suspend fun executeActivityInternal( + activityName: String, + resultType: Class<*>, + args: Array, + options: ActivityOptions + ): Any? { + val context = currentContext() + val future = context.executeActivity(activityName, resultType, options, args) + return future.await() // Suspends until activity completes + } + + @PublishedApi + internal fun startActivityInternal( + activityName: String, + resultType: Class<*>, + args: Array, + options: ActivityOptions + ): KActivityHandle { + val context = currentContext() + val future = context.executeActivity(activityName, resultType, options, args) + return KActivityHandleImpl(future) + } +} +``` + +**Why `inline` + `reified`?** + +Kotlin generics are erased at runtime (like Java). Without `reified`, we cannot access `R::class.java`: + +```kotlin +// Does NOT work - R is erased at runtime +suspend fun executeActivity(activityName: String, vararg args: Any?, options: ActivityOptions): R { + val resultType = R::class.java // Compile error: Cannot use 'R' as reified type parameter +} + +// WORKS - inline + reified captures type at compile time +inline suspend fun executeActivity(activityName: String, vararg args: Any?, options: ActivityOptions): R { + val resultType = R::class.java // OK: compiler substitutes actual type at call site +} +``` + +**How it works at the call site:** + +```kotlin +// User writes: +val result = KWorkflow.executeActivity("greet", name, options) + +// Compiler inlines to (conceptually): +val result = KWorkflow.executeActivityInternal("greet", String::class.java, arrayOf(name), options) as String +``` + +**Notes:** +- `@PublishedApi` is required for `internal` functions called from `inline` functions +- `inline suspend fun` is supported since Kotlin 1.3.70 +- Cannot be overridden (inline functions are not virtual) +- Cannot be called from Java (inlined at Kotlin call sites only) + +### Return Type for DataConverter + +The DataConverter needs the return type to deserialize the activity result. With `reified` generics, we can capture the type at compile time: + +```kotlin +inline suspend fun executeActivity(...): R { + // Simple approach - works for non-generic types + val resultType = R::class.java // String::class.java, Int::class.java, etc. + + // Full generic type info using typeOf (Kotlin 1.6+) + val kType: KType = typeOf() // Preserves List, Map, etc. +} +``` + +**Limitation with `R::class.java`:** + +```kotlin +// Works fine for simple types +executeActivity("greet", name, options) // R::class.java = String::class.java + +// Loses type parameter for generic types +executeActivity>("getNames", options) // R::class.java = List::class.java (loses String) +``` + +**Solution using `typeOf()`:** + +Kotlin's `typeOf()` (introduced in Kotlin 1.6) preserves full generic type information: + +```kotlin +inline suspend fun executeActivity( + activityName: String, + vararg args: Any?, + options: ActivityOptions +): R { + val kType: KType = typeOf() // Preserves full generic info + val javaType = kType.javaType // Converts to java.lang.reflect.Type + + return executeActivityInternal(activityName, javaType, args, options) as R +} + +// Internal method accepts java.lang.reflect.Type (handles ParameterizedType) +@PublishedApi +internal suspend fun executeActivityInternal( + activityName: String, + resultType: java.lang.reflect.Type, // Can be Class or ParameterizedType + args: Array, + options: ActivityOptions +): Any? { + val context = currentContext() + val future = context.executeActivity(activityName, resultType, options, args) + return future.await() +} +``` + +This allows the DataConverter to properly deserialize generic types: + +```kotlin +// Now works correctly +val names: List = KWorkflow.executeActivity>("getNames", options) +val mapping: Map = KWorkflow.executeActivity>("getMapping", options) +``` + +### CompletableFuture to Suspend Extension + +```kotlin +// Extension to convert CompletableFuture to suspend +private suspend fun CompletableFuture.await(): T { + return suspendCancellableCoroutine { cont -> + this.whenComplete { result, error -> + if (error != null) { + cont.resumeWithException(error) + } else { + cont.resume(result) + } + } + cont.invokeOnCancellation { + this.cancel(true) + } + } +} +``` + +### Method Reference-based Activity Execution (Phase 2) + +The typed activity API uses `KFunction` references to extract metadata (interface class, method name) without requiring proxy creation: + +```kotlin +object KWorkflow { + /** + * Execute activity using method reference. + * Uses KFunction to extract activity name and return type. + */ + suspend fun executeActivity( + activity: KFunction2, + arg1: A1, + options: ActivityOptions + ): R { + val activityName = extractActivityName(activity) + val resultType = extractReturnType(activity) + val context = currentContext() + val future = context.executeActivity(activityName, resultType, options, arrayOf(arg1)) + return future.await() as R + } + + /** + * Extract activity name from KFunction. + * Uses @ActivityMethod annotation name if present, otherwise method name. + */ + private fun extractActivityName(function: KFunction<*>): String { + val method = function.javaMethod + ?: throw IllegalArgumentException("Cannot get Java method from function") + + val annotation = method.getAnnotation(ActivityMethod::class.java) + return if (annotation != null && annotation.name.isNotEmpty()) { + annotation.name + } else { + method.name + } + } + + /** + * Extract return type from KFunction for DataConverter. + */ + private fun extractReturnType(function: KFunction<*>): Class<*> { + val method = function.javaMethod + ?: throw IllegalArgumentException("Cannot get Java method from function") + return method.returnType + } +} +``` + +**How `KFunction` works:** + +```kotlin +// User writes: +val result = KWorkflow.executeActivity( + GreetingActivities::composeGreeting, // KFunction2 + name, + options +) + +// At runtime: +// - activity.javaMethod gives us the Method object +// - Method.name gives us "composeGreeting" +// - Method.declaringClass gives us GreetingActivities +// - Method.returnType gives us String.class +``` + +## Duration Extensions + +```kotlin +// DurationExt.kt +package io.temporal.kotlin + +import java.time.Duration as JavaDuration +import kotlin.time.Duration as KotlinDuration +import kotlin.time.toJavaDuration +import kotlin.time.toKotlinDuration + +/** + * Convert Kotlin Duration to Java Duration for interop with Java SDK. + */ +fun KotlinDuration.toJava(): JavaDuration = this.toJavaDuration() + +/** + * Convert Java Duration to Kotlin Duration. + */ +fun JavaDuration.toKotlin(): KotlinDuration = this.toKotlinDuration() + +/** + * DSL property that accepts Kotlin Duration and converts to Java Duration. + */ +var ActivityOptions.Builder.startToCloseTimeout: KotlinDuration + @Deprecated("Write-only property", level = DeprecationLevel.HIDDEN) + get() = throw UnsupportedOperationException() + set(value) { setStartToCloseTimeout(value.toJava()) } + +// Similar extensions for other timeout properties... +``` + +## Decision Justifications + +### Why Coroutines Instead of Extending Java Thread Model? + +* **Idiomatic Kotlin**: Coroutines are the standard concurrency model in Kotlin +* **Structured concurrency**: `coroutineScope`, `async`, `launch` provide clear parent-child relationships +* **Cancellation**: Coroutine cancellation maps naturally to Temporal's cancellation scopes +* **Performance**: Lightweight compared to threads, though this is less relevant in workflow context +* **Ecosystem**: Integrates with Kotlin ecosystem (Flow, channels, etc.) + +### Why Suspend Functions for Workflows? + +* **Natural async**: Workflow operations (activities, timers) are inherently async +* **Sequential code**: Suspend functions allow sequential-looking code for async operations +* **Error handling**: Standard try-catch works with suspend functions +* **Testing**: Coroutine test utilities work well with suspend functions + +### Why Keep Compatibility with Java SDK? + +* **Gradual migration**: Teams can adopt Kotlin incrementally +* **Ecosystem leverage**: Benefits from Java SDK's stability and features +* **Reduced maintenance**: Shared infrastructure reduces duplication +* **Activity reuse**: Existing Java activities work without modification + +### Why Untyped Stubs First in Phase 1? + +* **Complexity management**: Typed stub generation requires more infrastructure +* **Proof of concept**: Validates coroutine-based execution model first +* **Faster iteration**: Can release usable SDK sooner +* **Java interop**: Untyped stubs work with any activity implementation + +### Why Custom CoroutineDispatcher? + +* **Determinism**: Standard dispatchers are non-deterministic +* **Replay support**: Must execute coroutines in same order during replay +* **Timer integration**: `delay()` must map to Temporal timers, not actual time +* **Deadlock detection**: Can implement workflow-aware deadlock detection + +### Why Unified Worker Instead of Separate KotlinWorkerFactory? + +* **Per-instance execution**: The dispatcher is per workflow instance, not per worker—there's no technical reason to separate workers +* **Simpler API**: One worker type is easier to understand and use +* **Mixed task queues**: Java and Kotlin workflows can share the same task queue +* **Easier migration**: Convert workflows one at a time without changing infrastructure +* **No Kotlin in Java SDK**: The pluggable `WorkflowImplementationFactory` keeps all Kotlin code in `temporal-kotlin` + +### Why Invest in Kotlin Idioms? + +* **Developer expectations**: Kotlin developers expect null safety and kotlin.time.Duration +* **Null safety**: Nullable types replace `Optional` throughout the API +* **Readability**: `30.seconds` is more readable than `Duration.ofSeconds(30)` +* **Coroutines**: `suspend fun` for natural async workflows and activities +* **IDE support**: Kotlin idioms provide better autocomplete and error detection +* **Differentiation**: Makes the Kotlin SDK feel native, not just a wrapper around Java + +## Open Questions + +### 1. Workflow Versioning with Coroutines + +How do `Workflow.getVersion()` semantics work with coroutines? + +- **Version marker ordering**: Version checks record markers in the event history. With coroutines suspending and resuming, need to ensure markers are recorded in deterministic order during replay. +- **Suspension points**: If `getVersion()` is called before a suspension point, does the version marker position remain stable across code changes? +- **Recommendation**: Likely works the same as Java since version markers are recorded synchronously before any suspension. Needs validation during implementation. + +### 2. CancellationScope Mapping + +Best way to map Kotlin's structured concurrency to Temporal's cancellation scopes? + +- **Semantic mismatch**: Kotlin's `coroutineScope {}` fails if any child fails, while Temporal's `CancellationScope` allows explicit cancellation handling. +- **SupervisorJob patterns**: Kotlin's `supervisorScope {}` isolates child failures. How does this map to Temporal's cancellation model? +- **Options**: + - A) `coroutineScope {}` creates implicit Temporal CancellationScope + - B) Require explicit `KWorkflow.cancellationScope {}` for Temporal semantics + - C) Hybrid approach with configuration +- **Recommendation**: Start with Option B (explicit) to avoid surprising behavior, consider Option A for future phases. + +### 3. Kotlin Flow Support + +Should we support Kotlin Flow for streaming results? + +- **Use cases**: Streaming activity progress, signal batching, continuous query results +- **Complexity**: Flow integration requires careful handling of backpressure, cancellation, and replay determinism +- **Alternatives**: Existing patterns (heartbeat details, signals) cover most use cases +- **Recommendation**: Defer to post-Phase 3. Evaluate demand and complexity once core SDK is stable. + +### 4. Kotlin Multiplatform + +Any consideration for Kotlin Multiplatform in the future? + +- **Current state**: SDK is JVM-only due to Java SDK dependency +- **Potential targets**: Kotlin/Native (server-side), Kotlin/JS (less relevant for workflows) +- **Blockers**: Core SDK (Rust) would need different bindings; significant effort +- **Recommendation**: Not a near-term priority. Document as potential future direction. May revisit if sdk-core provides C bindings that Kotlin/Native could use. + +### 5. DSL vs Annotations + +Should we provide a pure DSL alternative to annotations for workflow/activity definitions? + +- **Current approach**: Annotations (`@WorkflowMethod`, `@SignalMethod`) following Java SDK pattern +- **DSL alternative**: Builder-style registration without annotations + ```kotlin + worker.registerWorkflow { + workflowMethod(OrderWorkflow::processOrder) + signalMethod(OrderWorkflow::cancelOrder) + queryMethod(OrderWorkflow::status) + } + ``` +- **Trade-offs**: + - Annotations: Familiar to Java developers, IDE support, compile-time validation + - DSL: More flexible, no annotation processing, but less discoverable +- **Recommendation**: Keep annotations as primary approach for consistency with Java SDK. Consider DSL as optional alternative in future phase if there's demand. + +### 6. SPI Stability + +What's the right level of API stability for `WorkflowImplementationFactory` and related interfaces? + +- **Affected interfaces**: + - `WorkflowImplementationFactory` + - `ReplayWorkflow` + - `ReplayWorkflowContext` +- **Options**: + - A) **SPI package** (`io.temporal.worker.spi`): Explicit extension point, semver guarantees + - B) **@InternalApi annotation**: Accessible but no stability guarantees + - C) **Hybrid**: SPI for stable interfaces, @InternalApi for evolving ones +- **Considerations**: + - Third parties may want to build alternative execution models + - Premature stability commitments limit evolution + - Java SDK precedent: Most internal APIs are not stable +- **Recommendation**: Start with Option B (@InternalApi) for Phase 1. Promote to SPI package once interfaces stabilize after real-world usage (likely post-Phase 3). + +## References + +* [Kotlin Coroutines Prototype PR #1792](https://github.com/temporalio/sdk-java/pull/1792) - Early prototype; this proposal uses a different architecture (pluggable factory vs separate worker factory) +* [Existing temporal-kotlin Module](https://github.com/temporalio/sdk-java/tree/master/temporal-kotlin) +* [.NET SDK Proposal - Phase 1](../dotnet/sdk-phase-1.md) +* [.NET SDK Proposal - Phase 2](../dotnet/sdk-phase-2.md) diff --git a/kotlin/sdk-proposal.md b/kotlin/sdk-proposal.md index c119bbd..56aee02 100644 --- a/kotlin/sdk-proposal.md +++ b/kotlin/sdk-proposal.md @@ -1,862 +1,46 @@ # Kotlin SDK Proposal -The Kotlin SDK will be implemented in three loosely defined phases: +The Kotlin SDK proposal is split into two documents: -* Phase 1 - Coroutine-based workflows, untyped activity stubs, KotlinWorkerFactory -* Phase 2 - Typed activity stubs with suspend functions, signals/queries, child workflows -* Phase 3 - Updates, Nexus, testing framework, advanced features +## [SDK API](./sdk-api.md) -This proposal covers the overall design. Individual phases may have separate detailed proposals. +Public API and developer experience documentation including: -Notes for this proposal: +- Kotlin idioms (Duration, null safety, coroutines) +- Workflow definition with suspend functions +- Activity definition and stubs +- Client API +- Worker API +- Data conversion +- Interceptors +- Testing +- Migration guide from Java SDK +- Complete examples -* Intentionally loosely defined, everything subject to change -* Some decisions are justified at the end -* Code examples may not be valid Kotlin but use `/* ... */` for brevity +## [SDK Implementation](./sdk-implementation.md) -## Overview +Internal architecture and implementation details including: -The Kotlin SDK will extend the existing Java SDK, utilizing Kotlin coroutines for workflow execution instead of Java's thread-based approach. A custom `TemporalCoroutineDispatcher` ensures deterministic execution. +- Relationship to Java SDK +- Repository and package strategy +- Existing and new classes +- Unified worker architecture +- Java SDK refactoring for pluggability +- `WorkflowImplementationFactory` interface +- `KotlinCoroutineDispatcher` implementation +- `KotlinReplayWorkflow` implementation +- Decision justifications +- Open questions -The high-level goals for the Kotlin SDK are: +## Phases -* Provide idiomatic Kotlin experience with coroutines and suspend functions -* Maintain full interoperability with the Java SDK -* Support existing Java-defined workflows and activities from Kotlin code -* Enable gradual migration from Java SDK patterns -* Minimum Kotlin version: 1.8.x -* Coroutines library: kotlinx-coroutines-core 1.7.x+ +* **Phase 1** - Coroutine-based workflows, untyped activity stubs, pluggable WorkflowImplementationFactory, core Kotlin idioms (Duration, null safety) +* **Phase 2** - Typed activity stubs with suspend functions, signals/queries/updates, child workflows, property queries +* **Phase 3** - Testing framework -## Relationship to Java SDK +> **Note:** Nexus support is a separate project and will be addressed independently. -The Kotlin SDK builds on top of the Java SDK rather than replacing it: +## Quick Links -* **Shared infrastructure**: Uses the same gRPC client, data conversion, and service client -* **Interoperability**: Kotlin workflows can call Java activities and vice versa -* **Gradual adoption**: Teams can mix Java and Kotlin workers in the same application -* **Existing module**: Extends the existing `temporal-kotlin` module which already provides DSL extensions - -## Relationship to Existing Kotlin Classes - -The `temporal-kotlin` module already provides Kotlin extensions for the Java SDK. The Kotlin SDK will build on this foundation: - -### Existing Classes (Retained) - -| File | Purpose | Status | -|------|---------|--------| -| `TemporalDsl.kt` | `@DslMarker` for type-safe builders | **Keep as-is** | -| `*OptionsExt.kt` (15 files) | DSL builders for all Options classes | **Keep as-is** | -| `WorkflowClientExt.kt` | Reified `newWorkflowStub()`, DSL extensions | **Keep as-is** | -| `WorkflowStubExt.kt` | Reified `getResult()` | **Keep as-is** | -| `WorkerFactoryExt.kt` | `WorkerFactory()` constructor-like DSL | **Keep as-is** | -| `WorkerExt.kt` | Reified `registerWorkflowImplementationType()` | **Keep as-is** | -| `WorkflowMetadata.kt` | `workflowName()`, `workflowSignalName()` | **Keep as-is** | -| `ActivityMetadata.kt` | `activityName(Interface::method)` | **Keep as-is** | -| `KotlinObjectMapperFactory.kt` | Jackson ObjectMapper for Kotlin | **Keep as-is** | -| `KotlinMethodReferenceDisassemblyService.kt` | Kotlin method references in `Async` | **Keep as-is** | - -### New Classes (Kotlin SDK) - -| Class | Purpose | -|-------|---------| -| `KotlinWorkerFactory` | Worker factory for coroutine-based workflows | -| `KotlinWorkerFactoryOptions` | Options specific to Kotlin worker factory | -| `KotlinWorkflow` | Entry point for workflow APIs (like `Workflow` in Java) | -| `KotlinWorkflowContext` | Internal workflow execution context | -| `KotlinActivityStub` | Suspend-function activity stub interface | -| `TemporalCoroutineDispatcher` | Deterministic coroutine dispatcher | -| `KotlinWorkerInterceptor` | Interceptor interface with suspend functions | -| `KotlinWorkflowImplementationFactory` | Creates coroutine-based workflow instances | - -### Worker Factory Distinction - -The SDK will have **two worker factory types**: - -``` -┌─────────────────────────────────────┐ -│ BaseWorkerFactory │ ← Abstract base (in Java SDK) -│ - Common worker lifecycle │ -│ - Worker registration │ -│ - Start/shutdown management │ -└──────────────┬──────────────────────┘ - │ - ┌───────┴───────┐ - │ │ - ▼ ▼ -┌──────────────┐ ┌──────────────────────┐ -│ WorkerFactory│ │ KotlinWorkerFactory │ -│ (Java SDK) │ │ (Kotlin SDK) │ -│ │ │ │ -│ Thread-based │ │ Coroutine-based │ -│ workflows │ │ workflows │ -│ │ │ │ -│ Uses: │ │ Uses: │ -│ - DeterministicRunner │ │ - TemporalCoroutineDispatcher │ -│ - POJOWorkflowImplementationFactory│ │ - KotlinWorkflowImplementationFactory │ -└──────────────┘ └──────────────────────┘ -``` - -**When to use which:** - -| Use Case | Worker Factory | -|----------|----------------| -| Java workflows (blocking) | `WorkerFactory` | -| Kotlin workflows without coroutines (blocking) | `WorkerFactory` | -| Kotlin workflows with suspend functions | `KotlinWorkerFactory` | -| Mixed: Java workflows + Kotlin activities | `WorkerFactory` | -| Mixed: Kotlin coroutine workflows + Java activities | `KotlinWorkerFactory` | - -**Important:** A single application can use both factory types for different task queues if needed. - -## Java SDK Refactoring for Pluggability - -To support the Kotlin coroutine-based execution model, the Java SDK requires refactoring to make the workflow execution dispatcher pluggable. The [prototype PR #1792](https://github.com/temporalio/sdk-java/pull/1792) demonstrates these changes. - -### Required Changes to `temporal-sdk` - -#### 1. Extract `BaseWorkerFactory` Abstract Class - -**File:** `io.temporal.worker.BaseWorkerFactory` (new) - -Extract common functionality from `WorkerFactory` into an abstract base class: - -```java -public abstract class BaseWorkerFactory { - protected final Scope metricsScope; - private final WorkflowClient workflowClient; - private final WorkerFactoryOptions factoryOptions; - private final WorkflowExecutorCache cache; - private final Map workers = new HashMap<>(); - - // Common lifecycle: start(), shutdown(), awaitTermination() - // Common worker management: newWorker(), getWorker() - // Common registration with WorkflowClient - - // Abstract method for pluggable workflow factory - protected abstract ReplayWorkflowFactory newReplayWorkflowFactory( - WorkerOptions workerOptions, - WorkflowClientOptions clientOptions, - WorkflowExecutorCache cache - ); -} -``` - -#### 2. Modify `WorkerFactory` to Extend Base - -**File:** `io.temporal.worker.WorkerFactory` - -```java -public final class WorkerFactory extends BaseWorkerFactory { - private final ThreadPoolExecutor workflowThreadPool; - private final WorkflowThreadExecutor workflowThreadExecutor; - - @Override - protected ReplayWorkflowFactory newReplayWorkflowFactory( - WorkerOptions workerOptions, - WorkflowClientOptions clientOptions, - WorkflowExecutorCache cache - ) { - // Return the existing POJO-based factory - return new POJOWorkflowImplementationFactory( - clientOptions, - workerOptions, - workflowThreadExecutor, - cache, - // ... other dependencies - ); - } -} -``` - -#### 3. Extend `ReplayWorkflowFactory` Interface - -**File:** `io.temporal.internal.replay.ReplayWorkflowFactory` - -Add workflow registration methods to the interface: - -```java -public interface ReplayWorkflowFactory { - // Existing - ReplayWorkflow getWorkflow(WorkflowType workflowType, WorkflowExecution workflowExecution); - boolean isAnyTypeSupported(); - - // New: Allow registration through the factory - void registerWorkflowImplementationTypes( - WorkflowImplementationOptions options, - Class[] workflowImplementationTypes - ); - - void addWorkflowImplementationFactory( - WorkflowImplementationOptions options, - Class clazz, - Functions.Func factory - ); -} -``` - -#### 4. Update Client Registry to Accept Base Type - -**File:** `io.temporal.internal.client.WorkflowClientInternal` - -```java -public interface WorkflowClientInternal { - // Change from WorkerFactory to BaseWorkerFactory - void registerWorkerFactory(BaseWorkerFactory workerFactory); -} -``` - -**File:** `io.temporal.internal.client.WorkerFactoryRegistry` - -```java -public class WorkerFactoryRegistry { - // Change from WorkerFactory to BaseWorkerFactory - public synchronized void register(BaseWorkerFactory workerFactory) { ... } - public synchronized void deregister(BaseWorkerFactory workerFactory) { ... } -} -``` - -#### 5. Update `EagerWorkflowTaskDispatcher` - -**File:** `io.temporal.internal.client.EagerWorkflowTaskDispatcher` - -Update to work with `BaseWorkerFactory` instead of `WorkerFactory`. - -### Files Changed Summary - -| File | Change Type | Description | -|------|-------------|-------------| -| `BaseWorkerFactory.java` | **New** | Abstract base class extracted from WorkerFactory | -| `WorkerFactory.java` | **Modified** | Now extends BaseWorkerFactory | -| `ReplayWorkflowFactory.java` | **Modified** | Added registration methods | -| `POJOWorkflowImplementationFactory.java` | **Modified** | Implements new interface methods | -| `WorkflowClientInternal.java` | **Modified** | Accept BaseWorkerFactory | -| `WorkflowClientInternalImpl.java` | **Modified** | Accept BaseWorkerFactory | -| `WorkerFactoryRegistry.java` | **Modified** | Work with BaseWorkerFactory | -| `EagerWorkflowTaskDispatcher.java` | **Modified** | Work with BaseWorkerFactory | -| `Worker.java` | **Minor** | Adjust for base class changes | -| `SyncWorkflowWorker.java` | **Minor** | Adjust for interface changes | - -### Kotlin SDK Classes Using This Infrastructure - -Once the Java SDK refactoring is complete, the Kotlin SDK adds: - -```kotlin -// Extends the base factory -class KotlinWorkerFactory( - workflowClient: WorkflowClient, - factoryOptions: KotlinWorkerFactoryOptions? -) : BaseWorkerFactory(workflowClient, factoryOptions) { - - override fun newReplayWorkflowFactory( - workerOptions: WorkerOptions, - clientOptions: WorkflowClientOptions, - cache: WorkflowExecutorCache - ): ReplayWorkflowFactory { - // Return Kotlin-specific factory that uses coroutines - return KotlinWorkflowImplementationFactory( - clientOptions, - workerOptions, - cache - ) - } -} -``` - -```kotlin -// Kotlin-specific workflow factory -internal class KotlinWorkflowImplementationFactory( - private val clientOptions: WorkflowClientOptions, - private val workerOptions: WorkerOptions, - private val cache: WorkflowExecutorCache -) : ReplayWorkflowFactory { - - override fun getWorkflow( - workflowType: WorkflowType, - workflowExecution: WorkflowExecution - ): ReplayWorkflow { - // Returns KotlinWorkflow which uses TemporalCoroutineDispatcher - return KotlinWorkflow( - namespace, - workflowExecution, - workflowDefinition, - // ... uses coroutine-based execution - ) - } -} -``` - -### Backward Compatibility - -These changes maintain full backward compatibility: - -* `WorkerFactory` API remains unchanged for existing users -* Existing Java workflows continue to work without modification -* The refactoring is purely internal restructuring -* No breaking changes to public APIs - -## Repository/Package Strategy - -### Repository - -The Kotlin SDK will continue to live in the `temporal-kotlin` module within the `sdk-java` repository: - -* Pros: - * Shared build infrastructure - * Easier to maintain version compatibility - * Single release process -* Cons: - * Ties Kotlin SDK releases to Java SDK releases - -### Package Naming - -* Core workflow APIs: `io.temporal.kotlin.workflow` -* Worker APIs: `io.temporal.kotlin.worker` -* Interceptors: `io.temporal.kotlin.interceptors` -* Existing extensions remain in their current packages (e.g., `io.temporal.client`) - -### Maven Artifacts - -```xml - - io.temporal - temporal-kotlin - N.N.N - -``` - -Additional dependency on kotlinx-coroutines: -```xml - - org.jetbrains.kotlinx - kotlinx-coroutines-core - 1.7.x - -``` - -## Workflow Definition - -### Basic Example - -```kotlin -@WorkflowInterface -interface GreetingWorkflow { - @WorkflowMethod - suspend fun getGreeting(name: String): String -} - -class GreetingWorkflowImpl : GreetingWorkflow { - override suspend fun getGreeting(name: String): String = coroutineScope { - val activities = KotlinWorkflow.newUntypedActivityStub( - ActivityOptions { - setStartToCloseTimeout(Duration.ofSeconds(10)) - } - ) - - // Execute activities in parallel using coroutines - val hello = async { - activities.execute("greet", String::class.java, "Hello", name) - } - val goodbye = async { - activities.execute("greet", String::class.java, "Goodbye", name) - } - - "${hello.await()}\n${goodbye.await()}" - } -} -``` - -### Key Characteristics - -* Workflow methods are `suspend fun` instead of regular functions -* Use `coroutineScope`, `async`, `launch` for concurrent execution -* Use `delay()` instead of `Workflow.sleep()` for timers -* The `@WorkflowInterface` and `@WorkflowMethod` annotations are reused from Java SDK -* Data classes work naturally for workflow parameters and results - -### Signals and Queries (Phase 2) - -```kotlin -@WorkflowInterface -interface OrderWorkflow { - @WorkflowMethod - suspend fun processOrder(order: Order): OrderResult - - @SignalMethod - suspend fun cancelOrder(reason: String) - - @QueryMethod - fun getOrderStatus(): OrderStatus -} -``` - -Notes: -* Signal methods are `suspend fun` since they may trigger workflow logic -* Query methods remain non-suspend as they are read-only and synchronous - -### Child Workflows (Phase 2) - -```kotlin -override suspend fun parentWorkflow(): String = coroutineScope { - val child = KotlinWorkflow.newChildWorkflowStub( - ChildWorkflowOptions { - setWorkflowId("child-workflow-id") - } - ) - - // Start child workflow asynchronously - val result = async { child.doWork("input") } - - // Do other work... - - result.await() -} -``` - -## Activity Definition - -### Phase 1: Untyped Activity Stubs - -In Phase 1, activities are invoked through untyped stubs: - -```kotlin -val activities = KotlinWorkflow.newUntypedActivityStub( - ActivityOptions { - setStartToCloseTimeout(Duration.ofSeconds(30)) - setRetryOptions { - setInitialInterval(Duration.ofSeconds(1)) - setMaximumAttempts(3) - } - } -) - -val result = activities.execute("activityName", String::class.java, arg1, arg2) -``` - -### Phase 2: Typed Activity Stubs with Suspend Functions - -```kotlin -@ActivityInterface -interface GreetingActivities { - @ActivityMethod - suspend fun composeGreeting(greeting: String, name: String): String - - @ActivityMethod - suspend fun sendEmail(email: Email): SendResult -} - -// In workflow: -val activities = KotlinWorkflow.newActivityStub( - ActivityOptions { - setStartToCloseTimeout(Duration.ofSeconds(30)) - } -) - -val greeting = activities.composeGreeting("Hello", "World") -``` - -### Activity Implementation - -Activities can be implemented as regular Kotlin classes: - -```kotlin -class GreetingActivitiesImpl : GreetingActivities { - override suspend fun composeGreeting(greeting: String, name: String): String { - // Can use suspend functions for I/O - return "$greeting, $name!" - } - - override suspend fun sendEmail(email: Email): SendResult { - // Async I/O with coroutines - return emailService.send(email) - } -} -``` - -### Local Activities (Phase 2) - -```kotlin -val localActivities = KotlinWorkflow.newLocalActivityStub( - LocalActivityOptions { - setStartToCloseTimeout(Duration.ofSeconds(5)) - } -) -``` - -## Client API - -### Workflow Client Extensions - -Building on existing `temporal-kotlin` extensions: - -```kotlin -// Create client with DSL -val client = WorkflowClient(service) { - setNamespace("default") - setDataConverter(myConverter) -} - -// Start workflow with reified types -val workflow = client.newWorkflowStub { - setWorkflowId("greeting-123") - setTaskQueue("greeting-queue") -} - -// Execute workflow -val result = workflow.getGreeting("Temporal") - -// Or start async and get handle -val handle = WorkflowClient.start { workflow.getGreeting("Temporal") } -val result = handle.getResult() -``` - -### Signal and Query from Client - -```kotlin -// Get existing workflow -val workflow = client.newWorkflowStub("order-123") - -// Send signal -workflow.cancelOrder("Customer request") - -// Query -val status = workflow.getOrderStatus() -``` - -## Worker API - -### KotlinWorkerFactory - -A specialized worker factory for Kotlin coroutine-based workflows: - -```kotlin -val service = WorkflowServiceStubs.newLocalServiceStubs() -val client = WorkflowClient.newInstance(service) - -val factory = KotlinWorkerFactory(client) { - setMaxWorkflowThreadCount(800) -} - -val worker = factory.newWorker("task-queue") { - setMaxConcurrentActivityExecutionSize(100) -} - -// Register Kotlin workflow implementations -worker.registerWorkflowImplementationTypes( - GreetingWorkflowImpl::class.java, - OrderWorkflowImpl::class.java -) - -// Register activity implementations (can be Java or Kotlin) -worker.registerActivitiesImplementations( - GreetingActivitiesImpl(), - OrderActivitiesImpl() -) - -factory.start() -``` - -### TemporalCoroutineDispatcher - -The custom dispatcher ensures deterministic execution of coroutines: - -* Executes coroutines in a controlled, deterministic order -* Integrates with Temporal's replay mechanism -* Supports `delay()` by mapping to Temporal timers -* Handles cancellation scopes properly - -```kotlin -// Internal implementation - users don't interact with this directly -internal class TemporalCoroutineDispatcher( - private val workflowContext: KotlinWorkflowContext -) : CoroutineDispatcher() { - - override fun dispatch(context: CoroutineContext, block: Runnable) { - // Queue for deterministic execution - workflowContext.schedule(block) - } - - fun eventLoop(deadlockDetectionTimeout: Long) { - // Process queued coroutines deterministically - } -} -``` - -## Data Conversion - -### Kotlin Serialization Support - -Support for `kotlinx.serialization` as an alternative to Jackson: - -```kotlin -@Serializable -data class Order( - val id: String, - val items: List, - val status: OrderStatus -) - -@Serializable -data class OrderItem( - val productId: String, - val quantity: Int -) -``` - -### Jackson Kotlin Module - -The existing `KotlinObjectMapperFactory` continues to be supported: - -```kotlin -val converter = DefaultDataConverter.newDefaultInstance().withPayloadConverterOverrides( - JacksonJsonPayloadConverter( - KotlinObjectMapperFactory.new() - ) -) -``` - -### Data Classes Best Practices - -* Use `data class` for workflow parameters and results -* Ensure all properties have default values or are nullable for deserialization -* Consider using `@Serializable` annotation for kotlinx.serialization - -## Interceptors - -### Kotlin-Native Interceptor Interfaces - -```kotlin -interface KotlinWorkerInterceptor { - fun interceptWorkflow(next: WorkflowInboundCallsInterceptor): WorkflowInboundCallsInterceptor { - return next - } - - fun interceptActivity(next: ActivityInboundCallsInterceptor): ActivityInboundCallsInterceptor { - return next - } -} - -interface WorkflowInboundCallsInterceptor { - suspend fun init(outboundCalls: WorkflowOutboundCallsInterceptor) - - suspend fun execute(input: WorkflowInput): WorkflowOutput - - suspend fun handleSignal(input: SignalInput) - - fun handleQuery(input: QueryInput): QueryOutput -} - -interface WorkflowOutboundCallsInterceptor { - suspend fun executeActivity(input: ActivityInput): ActivityOutput - - suspend fun executeChildWorkflow(input: ChildWorkflowInput): ChildWorkflowOutput - - // ... other outbound calls -} -``` - -### Example: Logging Interceptor - -```kotlin -class LoggingInterceptor : KotlinWorkerInterceptor { - override fun interceptWorkflow( - next: WorkflowInboundCallsInterceptor - ): WorkflowInboundCallsInterceptor { - return object : WorkflowInboundCallsInterceptorBase(next) { - override suspend fun execute(input: WorkflowInput): WorkflowOutput { - log.info("Workflow started: ${input.workflowType}") - return try { - super.execute(input) - } finally { - log.info("Workflow completed: ${input.workflowType}") - } - } - } - } -} -``` - -## Testing - -### Workflow Testing with Coroutines (Phase 3) - -```kotlin -class GreetingWorkflowTest { - @Test - fun testGreetingWorkflow() = runTest { - val testEnv = TestWorkflowEnvironment.newInstance() - val worker = testEnv.newWorker("test-queue") - - worker.registerWorkflowImplementationTypes(GreetingWorkflowImpl::class.java) - worker.registerActivitiesImplementations(MockGreetingActivities()) - - testEnv.start() - - val client = testEnv.workflowClient - val workflow = client.newWorkflowStub { - setTaskQueue("test-queue") - } - - val result = workflow.getGreeting("Test") - - assertEquals("Hello, Test!", result) - - testEnv.close() - } -} -``` - -### Activity Testing - -```kotlin -class GreetingActivitiesTest { - @Test - fun testComposeGreeting() = runTest { - val context = TestActivityEnvironment.newInstance().activityContext - - val activities = GreetingActivitiesImpl() - - // Test with activity context - val result = context.run { - activities.composeGreeting("Hello", "World") - } - - assertEquals("Hello, World!", result) - } -} -``` - -### Mocking Activities in Workflows - -```kotlin -@Test -fun testWorkflowWithMockedActivities() = runTest { - val mockActivities = mockk() - coEvery { mockActivities.composeGreeting(any(), any()) } returns "Mocked greeting" - - // Configure test environment with mocked activities - worker.registerActivitiesImplementations(mockActivities) - - // ... run workflow -} -``` - -## Migration Guide - -### From Java SDK Workflows - -```java -// Java workflow -@WorkflowInterface -public interface GreetingWorkflow { - @WorkflowMethod - String getGreeting(String name); -} - -public class GreetingWorkflowImpl implements GreetingWorkflow { - @Override - public String getGreeting(String name) { - ActivityStub activities = Workflow.newUntypedActivityStub(...); - return activities.execute("greet", String.class, name); - } -} -``` - -```kotlin -// Kotlin equivalent -@WorkflowInterface -interface GreetingWorkflow { - @WorkflowMethod - suspend fun getGreeting(name: String): String -} - -class GreetingWorkflowImpl : GreetingWorkflow { - override suspend fun getGreeting(name: String): String { - val activities = KotlinWorkflow.newUntypedActivityStub(...) - return activities.execute("greet", String::class.java, name)!! - } -} -``` - -### Key Migration Differences - -| Java SDK | Kotlin SDK | -|----------|------------| -| `Workflow.sleep(duration)` | `delay(duration.toMillis())` | -| `Async.function(...)` | `async { ... }` | -| `Promise` | `Deferred` (from coroutines) | -| `Workflow.await(condition)` | Custom `awaitCondition { }` or channels | -| Thread-based execution | Coroutine-based execution | - -### Interoperability - -Kotlin workflows can call Java activities: - -```kotlin -// Kotlin workflow calling Java activity -override suspend fun processOrder(order: Order): String { - // JavaActivities is a Java @ActivityInterface - val javaActivities = KotlinWorkflow.newActivityStub(options) - return javaActivities.process(order) -} -``` - -Java workflows can be invoked from Kotlin clients: - -```kotlin -// Calling Java workflow from Kotlin client -val javaWorkflow = client.newWorkflowStub(options) -val result = javaWorkflow.execute(input) -``` - -## Decision Justifications - -### Why Coroutines Instead of Extending Java Thread Model? - -* **Idiomatic Kotlin**: Coroutines are the standard concurrency model in Kotlin -* **Structured concurrency**: `coroutineScope`, `async`, `launch` provide clear parent-child relationships -* **Cancellation**: Coroutine cancellation maps naturally to Temporal's cancellation scopes -* **Performance**: Lightweight compared to threads, though this is less relevant in workflow context -* **Ecosystem**: Integrates with Kotlin ecosystem (Flow, channels, etc.) - -### Why Suspend Functions for Workflows? - -* **Natural async**: Workflow operations (activities, timers) are inherently async -* **Sequential code**: Suspend functions allow sequential-looking code for async operations -* **Error handling**: Standard try-catch works with suspend functions -* **Testing**: Coroutine test utilities work well with suspend functions - -### Why Keep Compatibility with Java SDK? - -* **Gradual migration**: Teams can adopt Kotlin incrementally -* **Ecosystem leverage**: Benefits from Java SDK's stability and features -* **Reduced maintenance**: Shared infrastructure reduces duplication -* **Activity reuse**: Existing Java activities work without modification - -### Why Untyped Stubs First in Phase 1? - -* **Complexity management**: Typed stub generation requires more infrastructure -* **Proof of concept**: Validates coroutine-based execution model first -* **Faster iteration**: Can release usable SDK sooner -* **Java interop**: Untyped stubs work with any activity implementation - -### Why Custom CoroutineDispatcher? - -* **Determinism**: Standard dispatchers are non-deterministic -* **Replay support**: Must execute coroutines in same order during replay -* **Timer integration**: `delay()` must map to Temporal timers, not actual time -* **Deadlock detection**: Can implement workflow-aware deadlock detection - -## Open Questions - -1. **Workflow versioning**: How do `Workflow.getVersion()` semantics work with coroutines? -2. **CancellationScope mapping**: Best way to map Kotlin's structured concurrency to Temporal's cancellation scopes? -3. **Flow support**: Should we support Kotlin Flow for streaming results? -4. **Multiplatform**: Any consideration for Kotlin Multiplatform in the future? -5. **DSL vs Annotations**: Should we provide a pure DSL alternative to annotations? - -## References - -* [Kotlin Coroutines Prototype PR #1792](https://github.com/temporalio/sdk-java/pull/1792) -* [Existing temporal-kotlin Module](https://github.com/temporalio/sdk-java/tree/master/temporal-kotlin) -* [.NET SDK Proposal - Phase 1](../dotnet/sdk-phase-1.md) -* [.NET SDK Proposal - Phase 2](../dotnet/sdk-phase-2.md) +- [Kotlin Coroutines Prototype PR #1792](https://github.com/temporalio/sdk-java/pull/1792) +- [Existing temporal-kotlin Module](https://github.com/temporalio/sdk-java/tree/master/temporal-kotlin) From 57152b1758aa661c1bb019b2a94f57ca693bced5 Mon Sep 17 00:00:00 2001 From: Maxim Fateev Date: Mon, 29 Dec 2025 12:51:11 -0800 Subject: [PATCH 03/83] Kotlin SDK: Add cancellation documentation - Add Cancellation section to API doc covering cooperative cancellation, parallel execution, detached scopes with NonCancellable, and withTimeout - Resolve Open Question #2: Kotlin built-in cancellation replaces CancellationScope - Update complete example with cleanup activities and cancellation handling --- kotlin/sdk-api.md | 251 ++++++++++++++++++++++++++++++----- kotlin/sdk-implementation.md | 109 ++++++--------- 2 files changed, 259 insertions(+), 101 deletions(-) diff --git a/kotlin/sdk-api.md b/kotlin/sdk-api.md index a436e9f..575cb6b 100644 --- a/kotlin/sdk-api.md +++ b/kotlin/sdk-api.md @@ -382,6 +382,100 @@ override suspend fun workflowWithTimeout(): String { > **Note:** Versioning (`KWorkflow.getVersion`), continue-as-new (`KWorkflow.continueAsNew`), side effects (`KWorkflow.sideEffect`), and search attributes (`KWorkflow.upsertSearchAttributes`) use the same patterns as the Java SDK. +## Cancellation + +Kotlin's built-in coroutine cancellation replaces Java SDK's `CancellationScope`. This provides a more idiomatic experience while maintaining full Temporal semantics. + +### How Cancellation Works + +When a workflow is cancelled (from the server or programmatically), the Kotlin SDK translates this into coroutine cancellation. Cancellation is **cooperative**—it's checked at suspension points (`delay`, activity execution, child workflow execution, etc.): + +```kotlin +override suspend fun processOrder(order: Order): OrderResult = coroutineScope { + // Cancellation is checked at each suspension point + val validated = KWorkflow.executeActivity(...) // ← cancellation checked here + delay(5.minutes) // ← cancellation checked here + val result = KWorkflow.executeActivity(...) // ← cancellation checked here + result +} +``` + +### Explicit Cancellation Checks + +In rare cases where workflow code doesn't have suspension points (e.g., tight loops), you can check cancellation explicitly using `isActive` or `ensureActive()`. However, CPU-bound work should typically be delegated to activities. + +### Parallel Execution and Cancellation + +`coroutineScope` provides structured concurrency—if one child fails or the scope is cancelled, all children are automatically cancelled: + +```kotlin +override suspend fun parallelWorkflow(): String = coroutineScope { + val a = async { KWorkflow.executeActivity(...) } + val b = async { KWorkflow.executeActivity(...) } + + // If either activity fails, the other is cancelled + // If workflow is cancelled, both activities are cancelled + "${a.await()} - ${b.await()}" +} +``` + +### Detached Scopes (Cleanup Logic) + +Use `withContext(NonCancellable)` for cleanup code that must run even when the workflow is cancelled: + +```kotlin +override suspend fun processOrder(order: Order): OrderResult { + try { + return doProcessOrder(order) + } catch (e: CancellationException) { + // Cleanup runs even though workflow is cancelled + withContext(NonCancellable) { + KWorkflow.executeActivity( + OrderActivities::releaseReservation, + order, + ActivityOptions { startToCloseTimeout = 30.seconds } + ) + } + throw e // Re-throw to propagate cancellation + } +} +``` + +This is equivalent to Java's `Workflow.newDetachedCancellationScope()`. + +### Cancellation with Timeout + +Use `withTimeout` to cancel a block after a duration: + +```kotlin +override suspend fun processWithDeadline(order: Order): OrderResult { + return withTimeout(1.hours) { + // Everything in this block is cancelled if it takes > 1 hour + val validated = KWorkflow.executeActivity(...) + val charged = KWorkflow.executeActivity(...) + OrderResult(success = true) + } +} + +// Or use withTimeoutOrNull to get null instead of exception +override suspend fun tryProcess(order: Order): OrderResult? { + return withTimeoutOrNull(30.minutes) { + KWorkflow.executeActivity(...) + } +} +``` + +### Comparison with Java SDK + +| Java SDK | Kotlin SDK | +|----------|------------| +| `Workflow.newCancellationScope(() -> { ... })` | `coroutineScope { ... }` | +| `Workflow.newDetachedCancellationScope(() -> { ... })` | `withContext(NonCancellable) { ... }` | +| `CancellationScope.cancel()` | `job.cancel()` | +| `CancellationScope.isCancelRequested()` | `!isActive` | +| `CancellationScope.throwCanceled()` | `ensureActive()` | +| `scope.run()` with timeout | `withTimeout(duration) { ... }` | + ## Activity Definition ### String-based Activity Execution (Phase 1) @@ -1276,9 +1370,6 @@ interface OrderWorkflow { @WorkflowMethod suspend fun processOrder(order: Order): OrderResult - @SignalMethod - suspend fun cancelOrder(reason: String) - @UpdateMethod suspend fun addItem(item: OrderItem): Boolean @@ -1302,11 +1393,20 @@ interface OrderActivities { @ActivityMethod suspend fun reserveInventory(item: OrderItem): Boolean + @ActivityMethod + suspend fun releaseInventory(item: OrderItem): Boolean + @ActivityMethod suspend fun chargePayment(order: Order): Boolean + @ActivityMethod + suspend fun refundPayment(order: Order): Boolean + @ActivityMethod suspend fun shipOrder(order: Order): String + + @ActivityMethod + suspend fun notifyCustomer(customerId: String, message: String) } // === Activity Implementation === @@ -1314,7 +1414,8 @@ interface OrderActivities { class OrderActivitiesImpl( private val inventoryService: InventoryService, private val paymentService: PaymentService, - private val shippingService: ShippingService + private val shippingService: ShippingService, + private val notificationService: NotificationService ) : OrderActivities { override suspend fun validateOrder(order: Order): Boolean { @@ -1325,13 +1426,25 @@ class OrderActivitiesImpl( return inventoryService.reserve(item.productId, item.quantity) } + override suspend fun releaseInventory(item: OrderItem): Boolean { + return inventoryService.release(item.productId, item.quantity) + } + override suspend fun chargePayment(order: Order): Boolean { return paymentService.charge(order.customerId, order.total) } + override suspend fun refundPayment(order: Order): Boolean { + return paymentService.refund(order.customerId, order.total) + } + override suspend fun shipOrder(order: Order): String { return shippingService.createShipment(order) } + + override suspend fun notifyCustomer(customerId: String, message: String) { + notificationService.send(customerId, message) + } } // === Workflow Implementation === @@ -1339,7 +1452,9 @@ class OrderActivitiesImpl( class OrderWorkflowImpl : OrderWorkflow { private var _status = OrderStatus.PENDING private var _progress = 0 - private var canceled = false + private var _order: Order? = null + private var paymentCharged = false + private var reservedItems = mutableListOf() override val status get() = _status override val progress get() = _progress @@ -1353,9 +1468,24 @@ class OrderWorkflowImpl : OrderWorkflow { } } - override suspend fun processOrder(order: Order): OrderResult = coroutineScope { + override suspend fun processOrder(order: Order): OrderResult { + _order = order _status = OrderStatus.PROCESSING + return try { + doProcessOrder(order) + } catch (e: CancellationException) { + // Workflow was cancelled - run cleanup in detached scope + // This code runs even though the workflow is cancelled + withContext(NonCancellable) { + cleanup(order) + } + _status = OrderStatus.CANCELED + throw e // Re-throw to complete workflow as cancelled + } + } + + private suspend fun doProcessOrder(order: Order): OrderResult = coroutineScope { // Validate order - direct method reference, no stub needed _progress = 10 val isValid = KWorkflow.executeActivity( @@ -1367,41 +1497,43 @@ class OrderWorkflowImpl : OrderWorkflow { return@coroutineScope OrderResult(success = false, trackingNumber = null) } - // Check for cancellation - if (canceled) { - _status = OrderStatus.CANCELED - return@coroutineScope OrderResult(success = false, trackingNumber = null) - } - - // Process items in parallel + // Reserve inventory for all items in parallel + // If workflow is cancelled here, all parallel activities are cancelled _progress = 30 order.items.map { item -> async { - KWorkflow.executeActivity( + val reserved = KWorkflow.executeActivity( OrderActivities::reserveInventory, item, defaultOptions ) + if (reserved) { + reservedItems.add(item) // Track for cleanup + } + reserved } }.awaitAll() _progress = 60 - // Charge payment - longer timeout for payment processing - val charged = KWorkflow.executeActivity( - OrderActivities::chargePayment, - order, - ActivityOptions { - startToCloseTimeout = 2.minutes - retryOptions = RetryOptions { - initialInterval = 5.seconds - maximumAttempts = 5 + // Charge payment with timeout - auto-cancels if takes too long + val charged = withTimeout(2.minutes) { + KWorkflow.executeActivity( + OrderActivities::chargePayment, + order, + ActivityOptions { + startToCloseTimeout = 2.minutes + retryOptions = RetryOptions { + initialInterval = 5.seconds + maximumAttempts = 5 + } } - } - ) + ) + } if (!charged) { return@coroutineScope OrderResult(success = false, trackingNumber = null) } + paymentCharged = true _progress = 80 @@ -1418,9 +1550,47 @@ class OrderWorkflowImpl : OrderWorkflow { OrderResult(success = true, trackingNumber = trackingNumber) } - override suspend fun cancelOrder(reason: String) { - canceled = true - _status = OrderStatus.CANCELED + /** + * Cleanup logic that runs even when workflow is cancelled. + * Called from within withContext(NonCancellable) block. + */ + private suspend fun cleanup(order: Order) { + // Release any reserved inventory + for (item in reservedItems) { + try { + KWorkflow.executeActivity( + OrderActivities::releaseInventory, + item, + defaultOptions + ) + } catch (e: Exception) { + // Log but continue cleanup + } + } + + // Refund payment if it was charged + if (paymentCharged) { + try { + KWorkflow.executeActivity( + OrderActivities::refundPayment, + order, + defaultOptions + ) + } catch (e: Exception) { + // Log but continue cleanup + } + } + + // Notify customer of cancellation + try { + KWorkflow.executeActivity( + OrderActivities::notifyCustomer, + order.customerId, "Your order has been cancelled", + defaultOptions + ) + } catch (e: Exception) { + // Best effort notification + } } override fun validateAddItem(item: OrderItem) { @@ -1443,6 +1613,7 @@ fun main() = runBlocking { val inventoryService = InventoryServiceImpl() val paymentService = PaymentServiceImpl() val shippingService = ShippingServiceImpl() + val notificationService = NotificationServiceImpl() val service = WorkflowServiceStubs.newLocalServiceStubs() @@ -1457,7 +1628,7 @@ fun main() = runBlocking { // Plugin handles suspend functions automatically worker.registerWorkflowImplementationTypes(OrderWorkflowImpl::class) worker.registerActivitiesImplementations( - OrderActivitiesImpl(inventoryService, paymentService, shippingService) + OrderActivitiesImpl(inventoryService, paymentService, shippingService, notificationService) ) factory.start() @@ -1499,11 +1670,25 @@ fun main() = runBlocking { val added = existingHandle.executeUpdate(OrderWorkflow::addItem, newItem) println("Item added: $added") - // Send signal if needed - // existingHandle.signal(OrderWorkflow::cancelOrder, "Customer request") + // === Cancellation === + + // Cancel workflow - triggers CancellationException, cleanup block runs + // existingHandle.cancel() + + // Or terminate immediately - no cleanup runs + // existingHandle.terminate("Emergency shutdown") // Wait for result - type inferred from startWorkflow method reference - val result = handle.result() - println("Order result: $result") + // If workflow was cancelled, this throws WorkflowFailedException + try { + val result = handle.result() + println("Order result: $result") + } catch (e: WorkflowFailedException) { + if (e.cause is CanceledFailure) { + println("Order was cancelled - cleanup activities were executed") + } else { + throw e + } + } } ``` diff --git a/kotlin/sdk-implementation.md b/kotlin/sdk-implementation.md index 6889718..90a2895 100644 --- a/kotlin/sdk-implementation.md +++ b/kotlin/sdk-implementation.md @@ -930,79 +930,52 @@ var ActivityOptions.Builder.startToCloseTimeout: KotlinDuration ## Open Questions -### 1. Workflow Versioning with Coroutines +### Workflow Versioning with Coroutines How do `Workflow.getVersion()` semantics work with coroutines? - **Version marker ordering**: Version checks record markers in the event history. With coroutines suspending and resuming, need to ensure markers are recorded in deterministic order during replay. - **Suspension points**: If `getVersion()` is called before a suspension point, does the version marker position remain stable across code changes? -- **Recommendation**: Likely works the same as Java since version markers are recorded synchronously before any suspension. Needs validation during implementation. - -### 2. CancellationScope Mapping - -Best way to map Kotlin's structured concurrency to Temporal's cancellation scopes? - -- **Semantic mismatch**: Kotlin's `coroutineScope {}` fails if any child fails, while Temporal's `CancellationScope` allows explicit cancellation handling. -- **SupervisorJob patterns**: Kotlin's `supervisorScope {}` isolates child failures. How does this map to Temporal's cancellation model? -- **Options**: - - A) `coroutineScope {}` creates implicit Temporal CancellationScope - - B) Require explicit `KWorkflow.cancellationScope {}` for Temporal semantics - - C) Hybrid approach with configuration -- **Recommendation**: Start with Option B (explicit) to avoid surprising behavior, consider Option A for future phases. - -### 3. Kotlin Flow Support - -Should we support Kotlin Flow for streaming results? - -- **Use cases**: Streaming activity progress, signal batching, continuous query results -- **Complexity**: Flow integration requires careful handling of backpressure, cancellation, and replay determinism -- **Alternatives**: Existing patterns (heartbeat details, signals) cover most use cases -- **Recommendation**: Defer to post-Phase 3. Evaluate demand and complexity once core SDK is stable. - -### 4. Kotlin Multiplatform - -Any consideration for Kotlin Multiplatform in the future? - -- **Current state**: SDK is JVM-only due to Java SDK dependency -- **Potential targets**: Kotlin/Native (server-side), Kotlin/JS (less relevant for workflows) -- **Blockers**: Core SDK (Rust) would need different bindings; significant effort -- **Recommendation**: Not a near-term priority. Document as potential future direction. May revisit if sdk-core provides C bindings that Kotlin/Native could use. - -### 5. DSL vs Annotations - -Should we provide a pure DSL alternative to annotations for workflow/activity definitions? - -- **Current approach**: Annotations (`@WorkflowMethod`, `@SignalMethod`) following Java SDK pattern -- **DSL alternative**: Builder-style registration without annotations - ```kotlin - worker.registerWorkflow { - workflowMethod(OrderWorkflow::processOrder) - signalMethod(OrderWorkflow::cancelOrder) - queryMethod(OrderWorkflow::status) - } - ``` -- **Trade-offs**: - - Annotations: Familiar to Java developers, IDE support, compile-time validation - - DSL: More flexible, no annotation processing, but less discoverable -- **Recommendation**: Keep annotations as primary approach for consistency with Java SDK. Consider DSL as optional alternative in future phase if there's demand. - -### 6. SPI Stability - -What's the right level of API stability for `WorkflowImplementationFactory` and related interfaces? - -- **Affected interfaces**: - - `WorkflowImplementationFactory` - - `ReplayWorkflow` - - `ReplayWorkflowContext` -- **Options**: - - A) **SPI package** (`io.temporal.worker.spi`): Explicit extension point, semver guarantees - - B) **@InternalApi annotation**: Accessible but no stability guarantees - - C) **Hybrid**: SPI for stable interfaces, @InternalApi for evolving ones -- **Considerations**: - - Third parties may want to build alternative execution models - - Premature stability commitments limit evolution - - Java SDK precedent: Most internal APIs are not stable -- **Recommendation**: Start with Option B (@InternalApi) for Phase 1. Promote to SPI package once interfaces stabilize after real-world usage (likely post-Phase 3). +- **Expected behavior**: Likely works the same as Java since version markers are recorded synchronously before any suspension. Needs validation during implementation. + +## Resolved Decisions + +### CancellationScope Mapping + +Kotlin's built-in coroutine cancellation replaces Java SDK's `CancellationScope`. The `KotlinCoroutineDispatcher` already provides deterministic execution, so standard Kotlin cancellation patterns work correctly with Temporal's replay mechanism. + +| Java SDK | Kotlin SDK | Notes | +|----------|------------|-------| +| `Workflow.newCancellationScope(() -> {...})` | `coroutineScope { ... }` | Parent cancellation propagates to children | +| `Workflow.newDetachedCancellationScope(() -> {...})` | `withContext(NonCancellable) { ... }` | For cleanup code that must run | +| `CancellationScope.cancel()` | `job.cancel()` | Explicit cancellation | +| `CancellationScope.isCancelRequested()` | `!isActive` | Check without throwing | +| `CancellationScope.throwCanceled()` | `ensureActive()` | Throws if cancelled | +| `scope.run()` with timeout | `withTimeout(duration) { ... }` | Built-in timeout support | + +**Why this works:** +- Server cancellation triggers coroutine cancellation via root `Job` +- Cancellation state is recorded in workflow history for deterministic replay +- `withContext(NonCancellable)` maps directly to detached scope semantics +- No custom API needed—Kotlin's structured concurrency matches Temporal's model + +### DSL vs Annotations + +**Decision**: Keep annotations (`@WorkflowMethod`, `@SignalMethod`) as the primary approach for consistency with Java SDK. Consider DSL as optional alternative in future phases if there's demand. + +### SPI Stability + +**Decision**: Start with `@InternalApi` annotation for Phase 1. The affected interfaces (`WorkflowImplementationFactory`, `ReplayWorkflow`, `ReplayWorkflowContext`) will be promoted to a stable SPI package once they stabilize after real-world usage (likely post-Phase 3). + +## Future Considerations + +### Kotlin Flow Support + +Streaming results via Kotlin Flow is deferred to post-Phase 3. Existing patterns (heartbeat details, signals) cover most use cases. Flow integration requires careful handling of backpressure, cancellation, and replay determinism. + +### Kotlin Multiplatform + +Not a near-term priority. The SDK is JVM-only due to Java SDK dependency. May revisit if sdk-core provides C bindings that Kotlin/Native could use. ## References From 2f0887115a6026e5f9d58741e5efe4c65bb61209 Mon Sep 17 00:00:00 2001 From: Maxim Fateev Date: Tue, 30 Dec 2025 22:34:20 -0800 Subject: [PATCH 04/83] Kotlin SDK: Fix parameter order and rename condition to awaitCondition - Rename KWorkflow.condition to KWorkflow.awaitCondition for clarity - Fix parameter order to be consistent: (method/name, options, args...) - Add implementation plan and detailed phase documents --- kotlin/implementation-plan.md | 79 +++ kotlin/phases/phase-1.1-detailed.md | 119 ++++ kotlin/phases/phase-1.2-detailed.md | 140 ++++ kotlin/phases/phase-1.3-detailed.md | 164 +++++ kotlin/phases/phase-1.4-detailed.md | 118 ++++ .../phases/phase-1.5-plugin-architecture.md | 618 ++++++++++++++++++ kotlin/phases/phase-2.1-detailed.md | 137 ++++ kotlin/phases/phase-2.2-detailed.md | 156 +++++ kotlin/phases/phase-2.3-detailed.md | 154 +++++ kotlin/phases/phase-2.4-detailed.md | 191 ++++++ kotlin/phases/phase-2.5-detailed.md | 138 ++++ kotlin/phases/phase-3.1-detailed.md | 136 ++++ kotlin/phases/phase-3.2-detailed.md | 170 +++++ kotlin/sdk-api.md | 305 +++++---- kotlin/sdk-implementation.md | 133 ++-- kotlin/sdk-proposal.md | 29 +- 16 files changed, 2579 insertions(+), 208 deletions(-) create mode 100644 kotlin/implementation-plan.md create mode 100644 kotlin/phases/phase-1.1-detailed.md create mode 100644 kotlin/phases/phase-1.2-detailed.md create mode 100644 kotlin/phases/phase-1.3-detailed.md create mode 100644 kotlin/phases/phase-1.4-detailed.md create mode 100644 kotlin/phases/phase-1.5-plugin-architecture.md create mode 100644 kotlin/phases/phase-2.1-detailed.md create mode 100644 kotlin/phases/phase-2.2-detailed.md create mode 100644 kotlin/phases/phase-2.3-detailed.md create mode 100644 kotlin/phases/phase-2.4-detailed.md create mode 100644 kotlin/phases/phase-2.5-detailed.md create mode 100644 kotlin/phases/phase-3.1-detailed.md create mode 100644 kotlin/phases/phase-3.2-detailed.md diff --git a/kotlin/implementation-plan.md b/kotlin/implementation-plan.md new file mode 100644 index 0000000..7c63c0e --- /dev/null +++ b/kotlin/implementation-plan.md @@ -0,0 +1,79 @@ +# Kotlin SDK Implementation Plan + +## Phase 1: Core Coroutine Infrastructure + +### 1.1 Java SDK Refactoring +- Add `WorkflowImplementationFactory` interface +- Update `Worker` to support multiple factories +- Update `SyncWorkflowWorker` with composite factory +- Add `Async.await()` methods returning `Promise` +- Expose `ReplayWorkflow` and `ReplayWorkflowContext` for SPI + +### 1.2 Kotlin Coroutine Runtime +- Implement `KotlinCoroutineDispatcher` (deterministic execution) +- Implement `KotlinDelay` (timer integration) +- Implement `KotlinReplayWorkflow` +- Implement `KotlinWorkflowContext` +- Implement `KotlinWorkflowImplementationFactory` + +### 1.3 Core Kotlin APIs +- `KWorkflow` object with string-based activity execution +- `KWorkflowInfo` wrapper with null safety +- `KActivityHandle` for async activity execution +- Duration extensions (`kotlin.time.Duration` ↔ `java.time.Duration`) +- `KWorkflow.awaitCondition()` suspend function + +### 1.4 Worker Integration +- `KotlinPlugin` for enabling coroutine support +- Worker extension for registering Kotlin workflows +- Suspend function detection in registration + +--- + +## Phase 2: Typed APIs & Full Feature Set + +### 2.1 Typed Activity Stubs +- `KFunction`-based `executeActivity()` overloads +- Type extraction from method references +- Local activity support + +### 2.2 Signals, Queries, Updates +- Signal handler registration with suspend support +- Query methods (including property syntax) +- Update methods with validators + +### 2.3 Child Workflows +- `executeChildWorkflow()` with method references +- `startChildWorkflow()` returning handle +- `ChildWorkflowOptions` DSL + +### 2.4 Client API +- `KWorkflowHandle` and `KTypedWorkflowHandle` +- `WorkflowClient` extensions for Kotlin +- `signalWithStart` and `updateWithStart` + +### 2.5 Kotlin Wrappers +- `KActivity` object and `KActivityContext` +- `KActivityInfo` with null safety +- `KUpdateHandle` + +--- + +## Phase 3: Testing Framework + +### 3.1 Test Environment +- Kotlin-friendly test workflow environment +- Coroutine test utilities integration + +### 3.2 Mocking Support +- Activity mocking with suspend functions +- Time skipping utilities + +--- + +## Cross-Cutting Concerns (All Phases) + +- Documentation and examples +- Migration guide from Java SDK +- Integration tests +- Compatibility testing (Java ↔ Kotlin interop) diff --git a/kotlin/phases/phase-1.1-detailed.md b/kotlin/phases/phase-1.1-detailed.md new file mode 100644 index 0000000..3e41c56 --- /dev/null +++ b/kotlin/phases/phase-1.1-detailed.md @@ -0,0 +1,119 @@ +# Phase 1.1: Java SDK Refactoring - Detailed Plan + +## Overview + +Refactor the Java SDK to support pluggable workflow execution models. This enables the Kotlin module to provide coroutine-based workflow execution without modifying Java SDK internals. + +This phase must ensure full backwards compatibility for existing applications using Java SDK. + +## Prerequisites + +- None (this is the foundational phase) + +## Status + +- [x] Change 1: Add WorkflowImplementationFactory Interface +- [x] Change 2: Extract POJOWorkflowImplementationFactory +- [x] Change 3: Add Factory Registry to Worker +- [x] Change 4: Implement CompositeReplayWorkflowFactory +- [N/A] Change 5: ~~Expose ReplayWorkflow Interface for SPI~~ (Kotlin module accesses internal classes directly) +- [N/A] Change 6: ~~Expose ReplayWorkflowContext for SPI~~ (Kotlin module accesses internal classes directly) +- [Skip] Change 7: Add Async.await() Methods (implemented elsewhere) + +## Changes + +### Change 1: Add WorkflowImplementationFactory Interface ✓ + +**Summary:** Define the pluggable factory interface for creating workflow implementations + +**Scope:** +- Add `io.temporal.internal.worker.WorkflowImplementationFactory` interface (internal package - Kotlin module accesses directly) + +**Tests:** +- Compilation test for interface +- Javadoc validation + +**Dependencies:** None + +--- + +### Change 2: Extract POJOWorkflowImplementationFactory ✓ + +**Summary:** Refactor existing POJO workflow creation to implement the new interface + +**Scope:** +- Modify `POJOWorkflowImplementationFactory` to implement `WorkflowImplementationFactory` +- Extract workflow type registration logic +- No public API changes + +**Tests:** +- All existing workflow tests must pass +- Unit tests for factory method contracts + +**Dependencies:** Change 1 + +--- + +### Change 3: Add Factory Registry to Worker ✓ + +**Summary:** Update Worker to maintain a registry of workflow implementation factories + +**Scope:** +- Add `Worker.registerWorkflowImplementationFactory()` method +- Add internal factory registry +- Default factory remains `POJOWorkflowImplementationFactory` + +**Tests:** +- Test registering custom factory +- Test factory ordering/priority +- Existing tests unchanged + +**Dependencies:** Change 2 + +--- + +### Change 4: Implement CompositeReplayWorkflowFactory ✓ + +**Summary:** Create composite factory that delegates to registered factories + +**Scope:** +- Add `CompositeReplayWorkflowFactory` internal class +- Modify `SyncWorkflowWorker` to use composite factory +- Consult factories in registration order + +**Tests:** +- Test with multiple factories +- Test fallback to default factory +- Test workflow type routing + +**Dependencies:** Change 3 + +--- + +### Change 5: ~~Expose ReplayWorkflow Interface for SPI~~ [N/A] + +**Status:** Not applicable - Kotlin module accesses internal classes directly. No SPI package needed. + +--- + +### Change 6: ~~Expose ReplayWorkflowContext for SPI~~ [N/A] + +**Status:** Not applicable - Kotlin module accesses internal classes directly. No SPI package needed. + +--- + +### Change 7: Add Async.await() Methods + +**Summary:** Add Promise-returning await methods to Async class for coroutine support + +**Scope:** +- Add `Async.await(Supplier condition): Promise` +- Add `Async.await(Duration timeout, Supplier condition): Promise` +- Integrate with existing condition tracking mechanism + +**Tests:** +- Unit tests for Promise completion on condition +- Unit tests for timeout behavior +- Integration test with workflow execution + +**Dependencies:** Change 6 diff --git a/kotlin/phases/phase-1.2-detailed.md b/kotlin/phases/phase-1.2-detailed.md new file mode 100644 index 0000000..6a7e6e2 --- /dev/null +++ b/kotlin/phases/phase-1.2-detailed.md @@ -0,0 +1,140 @@ +# Phase 1.2: Kotlin Coroutine Runtime - Detailed Plan + +## Overview + +Implement the core coroutine-based workflow execution runtime in the temporal-kotlin module. This provides deterministic coroutine execution that integrates with Temporal's replay mechanism. + +## Prerequisites + +- Phase 1.1 complete (WorkflowImplementationFactory interface available) + +## Changes + +### Change 1: Add Coroutines Dependency and Base Infrastructure + +**Summary:** Add kotlinx-coroutines dependency and base internal classes + +**Scope:** +- Add `kotlinx-coroutines-core` dependency to temporal-kotlin +- Create `io.temporal.kotlin.internal` package +- Add internal marker annotations + +**Tests:** +- Verify dependency resolution +- Basic coroutine compilation test + +**Dependencies:** None + +--- + +### Change 2: Implement KotlinWorkflowContext + +**Summary:** Create internal context that wraps ReplayWorkflowContext for Kotlin workflows + +**Scope:** +- Add `KotlinWorkflowContext` internal class +- Wrap timer creation, activity execution, side effects +- Expose workflow info and current time + +**Tests:** +- Unit tests for context method delegation +- Test timer creation +- Test activity execution delegation + +**Dependencies:** Change 1 + +--- + +### Change 3: Implement KotlinCoroutineDispatcher + +**Summary:** Create deterministic coroutine dispatcher for workflow execution + +**Scope:** +- Add `KotlinCoroutineDispatcher` implementing `CoroutineDispatcher` +- Implement ready queue for deterministic execution +- Implement event loop processing +- Add deadlock detection + +**Tests:** +- Test deterministic execution order +- Test multiple coroutines queuing +- Test deadlock detection timeout + +**Dependencies:** Change 2 + +--- + +### Change 4: Implement KotlinDelay + +**Summary:** Create Delay implementation that maps to Temporal timers + +**Scope:** +- Add `KotlinDelay` implementing `kotlinx.coroutines.Delay` +- Map `delay()` calls to `KotlinWorkflowContext.createTimer()` +- Handle cancellation of timers + +**Tests:** +- Test delay creates Temporal timer +- Test delay cancellation +- Test delay completion resumes coroutine + +**Dependencies:** Change 3 + +--- + +### Change 5: Implement KotlinWorkflowDefinition + +**Summary:** Create metadata class for Kotlin workflow types + +**Scope:** +- Add `KotlinWorkflowDefinition` class +- Extract workflow type name from annotations +- Extract workflow method and parameter types +- Handle suspend function detection + +**Tests:** +- Test workflow type extraction +- Test suspend function detection +- Test parameter type extraction + +**Dependencies:** Change 4 + +--- + +### Change 6: Implement KotlinReplayWorkflow + +**Summary:** Create ReplayWorkflow implementation using coroutines + +**Scope:** +- Add `KotlinReplayWorkflow` implementing `ReplayWorkflow` +- Create coroutine scope with custom dispatcher +- Implement start(), eventLoop(), getOutput(), cancel(), close() +- Handle workflow completion and failure + +**Tests:** +- Test workflow start and completion +- Test workflow cancellation +- Test workflow failure handling +- Test event loop execution + +**Dependencies:** Change 5 + +--- + +### Change 7: Implement KotlinWorkflowImplementationFactory + +**Summary:** Create WorkflowImplementationFactory for Kotlin coroutine workflows + +**Scope:** +- Add `KotlinWorkflowImplementationFactory` implementing `WorkflowImplementationFactory` +- Implement `supportsType()` for suspend function detection +- Implement workflow registration and creation +- Wire up KotlinReplayWorkflow creation + +**Tests:** +- Test suspend function detection +- Test workflow registration +- Test workflow instance creation +- Integration test: full workflow execution + +**Dependencies:** Change 6 diff --git a/kotlin/phases/phase-1.3-detailed.md b/kotlin/phases/phase-1.3-detailed.md new file mode 100644 index 0000000..e7e16ab --- /dev/null +++ b/kotlin/phases/phase-1.3-detailed.md @@ -0,0 +1,164 @@ +# Phase 1.3: Core Kotlin APIs - Detailed Plan + +## Overview + +Implement the public Kotlin APIs for workflow development including the KWorkflow object, info wrappers, activity handles, and duration extensions. + +## Prerequisites + +- Phase 1.2 complete (Kotlin coroutine runtime available) + +## Changes + +### Change 1: Add Duration Extensions + +**Summary:** Add extension functions for Kotlin/Java Duration conversion + +**Scope:** +- Add `DurationExt.kt` in `io.temporal.kotlin` package +- Add `KotlinDuration.toJava()` extension +- Add `JavaDuration.toKotlin()` extension +- Add DSL property extensions for Options builders + +**Tests:** +- Test conversions in both directions +- Test edge cases (zero, max values) +- Test DSL usage in builders + +**Dependencies:** None + +--- + +### Change 2: Implement KWorkflowInfo + +**Summary:** Create Kotlin wrapper for WorkflowInfo with null safety + +**Scope:** +- Add `KWorkflowInfo` interface in `io.temporal.kotlin.workflow` +- Replace `Optional` with nullable types +- Add properties: workflowId, runId, parentWorkflowId?, parentRunId?, etc. +- Add internal implementation wrapping Java WorkflowInfo + +**Tests:** +- Test all property accessors +- Test nullable properties with and without values +- Test memo and search attribute access + +**Dependencies:** Change 1 + +--- + +### Change 3: Implement KActivityHandle + +**Summary:** Create handle type for async activity execution + +**Scope:** +- Add `KActivityHandle` interface +- Add `await(): R` suspend function +- Add `cancel()` method +- Add `isCompleted` property +- Add internal implementation wrapping CompletableFuture + +**Tests:** +- Test await completion +- Test cancellation +- Test isCompleted state transitions + +**Dependencies:** Change 2 + +--- + +### Change 4: Implement KWorkflow Object - Core Methods + +**Summary:** Create KWorkflow object with basic workflow utilities + +**Scope:** +- Add `KWorkflow` object in `io.temporal.kotlin.workflow` +- Add `getInfo(): KWorkflowInfo` +- Add `currentTime(): Instant` +- Add `randomUUID(): UUID` +- Add `getVersion()`, `sideEffect()`, `upsertSearchAttributes()` + +**Tests:** +- Test getInfo() returns correct values +- Test currentTime() during replay +- Test sideEffect determinism +- Test getVersion() marker recording + +**Dependencies:** Change 3 + +--- + +### Change 5: Implement KWorkflow.executeActivity (String-based) + +**Summary:** Add string-based activity execution to KWorkflow + +**Scope:** +- Add `executeActivity(name, options, vararg args): R` suspend function +- Add reified generic for return type extraction +- Wire to KotlinWorkflowContext.executeActivity() +- Add CompletableFuture.await() extension + +**Tests:** +- Test activity execution and result +- Test activity failure propagation +- Test activity timeout +- Test generic type handling + +**Dependencies:** Change 4 + +--- + +### Change 6: Implement KWorkflow.startActivity (String-based) + +**Summary:** Add async activity execution returning handle + +**Scope:** +- Add `startActivity(name, options, vararg args): KActivityHandle` +- Return handle immediately, execution in background +- Support parallel activity execution + +**Tests:** +- Test handle returned immediately +- Test parallel execution with multiple handles +- Test await on handle +- Test cancellation via handle + +**Dependencies:** Change 5 + +--- + +### Change 7: Implement KWorkflow.awaitCondition + +**Summary:** Add awaitCondition suspend function + +**Scope:** +- Add `awaitCondition(condition: () -> Boolean)` suspend function +- Add `awaitCondition(timeout: Duration, condition: () -> Boolean): Boolean` +- Wire to `Async.await()` Promise and suspend + +**Tests:** +- Test condition completion when true +- Test condition with timeout - success case +- Test condition with timeout - timeout case +- Test condition re-evaluation after events + +**Dependencies:** Change 6 + +--- + +### Change 8: Implement Local Activity Execution + +**Summary:** Add local activity execution methods + +**Scope:** +- Add `executeLocalActivity(name, options, vararg args): R` +- Add `startLocalActivity(name, options, vararg args): KActivityHandle` +- Wire to local activity execution in context + +**Tests:** +- Test local activity execution +- Test local activity options (timeout, retry) +- Test local activity failure + +**Dependencies:** Change 7 diff --git a/kotlin/phases/phase-1.4-detailed.md b/kotlin/phases/phase-1.4-detailed.md new file mode 100644 index 0000000..a326256 --- /dev/null +++ b/kotlin/phases/phase-1.4-detailed.md @@ -0,0 +1,118 @@ +# Phase 1.4: Worker Integration - Detailed Plan + +## Overview + +Implement the worker integration layer that enables registration and execution of Kotlin coroutine workflows alongside Java workflows. + +## Prerequisites + +- Phase 1.3 complete (Core Kotlin APIs available) + +## Changes + +### Change 1: Implement KotlinPlugin Interface + +**Summary:** Define plugin interface for Kotlin coroutine support + +**Scope:** +- Add `KotlinPlugin` class in `io.temporal.kotlin.worker` +- Define configuration options (interceptors placeholder) +- Create factory for `KotlinWorkflowImplementationFactory` + +**Tests:** +- Test plugin instantiation +- Test configuration options +- Test factory creation + +**Dependencies:** None + +--- + +### Change 2: Add Plugin Support to WorkflowClient Extensions + +**Summary:** Extend WorkflowClient DSL to accept plugins + +**Scope:** +- Modify `WorkflowClientExt.kt` to support plugins parameter +- Store plugin reference for worker factory access +- Pass plugin configuration through client options + +**Tests:** +- Test client creation with plugin +- Test plugin accessible from client + +**Dependencies:** Change 1 + +--- + +### Change 3: Implement Suspend Function Detection + +**Summary:** Create utility for detecting suspend workflow methods + +**Scope:** +- Add `SuspendFunctionDetector` internal utility +- Check for `kotlin.coroutines.Continuation` parameter +- Validate workflow interface annotations + +**Tests:** +- Test detection of suspend functions +- Test detection of non-suspend functions +- Test mixed interface handling + +**Dependencies:** Change 2 + +--- + +### Change 4: Add Worker Extension for Kotlin Workflows + +**Summary:** Add extension function for registering Kotlin workflow types + +**Scope:** +- Add `Worker.registerWorkflowImplementationTypes(vararg KClass<*>)` extension +- Auto-detect suspend functions and route to Kotlin factory +- Fall back to Java factory for non-suspend workflows + +**Tests:** +- Test Kotlin workflow registration +- Test Java workflow registration unchanged +- Test mixed registration + +**Dependencies:** Change 3 + +--- + +### Change 5: Implement Automatic Factory Registration + +**Summary:** Automatically register KotlinWorkflowImplementationFactory when plugin is active + +**Scope:** +- Detect KotlinPlugin in WorkerFactory +- Auto-register `KotlinWorkflowImplementationFactory` with workers +- Ensure factory is registered before workflow types + +**Tests:** +- Test automatic factory registration +- Test factory precedence +- Test without plugin (Java-only behavior) + +**Dependencies:** Change 4 + +--- + +### Change 6: End-to-End Integration Test + +**Summary:** Create comprehensive integration test for Kotlin workflow execution + +**Scope:** +- Add integration test module/package +- Test full workflow: client → worker → workflow → activity → result +- Test workflow with delays, conditions, parallel activities +- Test alongside Java workflows on same worker + +**Tests:** +- E2E test: simple workflow +- E2E test: workflow with timer +- E2E test: workflow with parallel activities +- E2E test: mixed Java/Kotlin worker + +**Dependencies:** Change 5 diff --git a/kotlin/phases/phase-1.5-plugin-architecture.md b/kotlin/phases/phase-1.5-plugin-architecture.md new file mode 100644 index 0000000..cb3c9d6 --- /dev/null +++ b/kotlin/phases/phase-1.5-plugin-architecture.md @@ -0,0 +1,618 @@ +# Phase 1.5: Plugin Architecture - Detailed Plan + +## Overview + +Implement a plugin architecture for the Java SDK that enables language-specific extensions like Kotlin coroutine support. This design intercepts workflow type registration and allows plugins to provide custom workflow implementation factories. + +## Motivation + +The current Phase 1.4 implementation provides worker-side Kotlin integration through extension functions: + +```kotlin +val plugin = KotlinPlugin { deadlockDetectionTimeout = 1500L } +worker.registerKotlinWorkflowImplementationTypes(plugin, MyWorkflowImpl::class) +``` + +This works but has limitations: +1. **Separate registration API** - Users must use Kotlin-specific extension functions +2. **No DataConverter propagation** - Plugin creates its own default DataConverter +3. **Not extensible** - Pattern doesn't generalize to other language integrations +4. **Mixed workflows require manual partitioning** - Users must know which workflows are suspend + +## Goals + +1. Create a plugin interface that generalizes to any language/framework integration +2. Enable automatic workflow type detection during standard `registerWorkflowImplementationTypes` calls +3. Propagate configuration (DataConverter) from client through to plugins +4. Maintain backward compatibility with existing extension function API + +## Non-Goals (Phase 1.5) + +1. Client-side plugins - Kotlin suspend workflows work with existing untyped client stubs +2. Activity plugins - Activity registration doesn't need special Kotlin handling +3. Complex plugin chaining/ordering - Keep initial implementation simple + +## Proposed Design + +### Core Concept: Registration Interception + +The key insight is that plugins should intercept workflow type registration rather than requiring a separate registration API: + +```kotlin +// User registers workflows with standard API +worker.registerWorkflowImplementationTypes( + MySuspendWorkflowImpl::class.java, // Plugin handles this + MyJavaWorkflowImpl::class.java // Default POJO factory handles this +) +``` + +When `registerWorkflowImplementationTypes` is called: +1. For each workflow class, ask plugins if they handle this type +2. First plugin to return a factory wins +3. If no plugin handles it, use the default POJO factory + +### Component 1: WorkerPlugin Interface (Java SDK) + +**Location:** `temporal-sdk/src/main/java/io/temporal/plugin/WorkerPlugin.java` + +```java +package io.temporal.plugin; + +import io.temporal.common.converter.DataConverter; +import io.temporal.internal.worker.WorkflowImplementationFactory; +import io.temporal.worker.Worker; +import io.temporal.worker.WorkerOptions; +import javax.annotation.Nullable; + +/** + * Plugin interface for extending Worker functionality. + * + * Plugins enable language-specific or framework-specific extensions to Temporal workers. + * They can: + * - Modify WorkerOptions before worker creation + * - Intercept workflow type registration and provide custom factories + * - Register activity implementations after worker creation + * + * Plugins are registered at the WorkerFactory level and are applied to all workers + * created by that factory. + * + * Example implementation: + *
{@code
+ * public class MyLanguagePlugin implements WorkerPlugin {
+ *     private MyWorkflowFactory factory;
+ *
+ *     @Override
+ *     public WorkerOptions.Builder configureWorker(WorkerOptions.Builder builder) {
+ *         return builder.setDefaultDeadlockDetectionTimeout(1500);
+ *     }
+ *
+ *     @Override
+ *     public WorkflowImplementationFactory getFactoryForType(
+ *             Class clazz, DataConverter dataConverter) {
+ *         if (!isMyLanguageWorkflow(clazz)) {
+ *             return null; // Let default factory handle it
+ *         }
+ *         if (factory == null) {
+ *             factory = new MyWorkflowFactory(dataConverter);
+ *         }
+ *         factory.registerWorkflowImplementationType(clazz);
+ *         return factory;
+ *     }
+ * }
+ * }
+ * + * Usage with WorkerFactory: + *
{@code
+ * WorkerFactory factory = WorkerFactory.newInstance(
+ *     client,
+ *     WorkerFactoryOptions.newBuilder()
+ *         .addPlugin(new MyLanguagePlugin())
+ *         .build()
+ * );
+ * Worker worker = factory.newWorker("task-queue");
+ * // Plugin automatically handles workflow types during registration
+ * worker.registerWorkflowImplementationTypes(
+ *     MyLanguageWorkflow.class,  // Handled by plugin
+ *     StandardJavaWorkflow.class  // Handled by default factory
+ * );
+ * }
+ */ +public interface WorkerPlugin { + + /** + * Called before Worker is created to allow modification of worker options. + * + * @param builder the WorkerOptions builder to modify + * @return the modified builder (may be same instance or new) + */ + default WorkerOptions.Builder configureWorker(WorkerOptions.Builder builder) { + return builder; + } + + /** + * Called for each workflow implementation type being registered. + * + * If this plugin handles the workflow type, it should: + * 1. Register the type with its internal factory + * 2. Return the factory instance + * + * If this plugin does not handle the type, return null to let the next + * plugin or the default POJO factory handle it. + * + * @param workflowImplementationType the workflow implementation class being registered + * @param dataConverter the DataConverter from WorkflowClient options + * @return a factory that handles this workflow type, or null to delegate + */ + @Nullable + default WorkflowImplementationFactory getFactoryForType( + Class workflowImplementationType, DataConverter dataConverter) { + return null; + } + + /** + * Called after Worker is created to allow registration of activity implementations + * or other setup. + * + * Note: For workflow registration, prefer using getFactoryForType which integrates + * with the standard registerWorkflowImplementationTypes API. + * + * @param worker the created worker + * @param dataConverter the DataConverter from WorkflowClient options + */ + default void onWorkerCreated(Worker worker, DataConverter dataConverter) { + // Default no-op + } +} +``` + +**Key Design Points:** +- `getFactoryForType` is called for each workflow class during registration +- Plugins can lazily create and reuse a single factory instance +- The same factory can handle multiple workflow types +- Returning `null` means "I don't handle this type, try the next plugin" + +### Component 2: WorkerFactoryOptions Plugin Support + +**Location:** `temporal-sdk/src/main/java/io/temporal/worker/WorkerFactoryOptions.java` + +```java +// Add to existing WorkerFactoryOptions class: + +public final class WorkerFactoryOptions { + // ... existing fields ... + private final List plugins; + + // In Builder: + public static final class Builder { + private List plugins = new ArrayList<>(); + + /** + * Adds a plugin that will be applied to all workers created by this factory. + */ + public Builder addPlugin(WorkerPlugin plugin) { + this.plugins.add(Objects.requireNonNull(plugin)); + return this; + } + + /** + * Sets all plugins, replacing any previously added. + */ + public Builder setPlugins(List plugins) { + this.plugins = new ArrayList<>(plugins); + return this; + } + } + + public List getPlugins() { + return Collections.unmodifiableList(plugins); + } +} +``` + +### Component 3: Worker Registration Interception + +**Location:** `temporal-sdk/src/main/java/io/temporal/worker/Worker.java` + +The Worker class is modified to: +1. Store plugins and dataConverter passed from WorkerFactory +2. Intercept `registerWorkflowImplementationTypes` to call plugins + +```java +public final class Worker implements Suspendable { + // ... existing fields ... + private final List plugins; + private final DataConverter dataConverter; + private final List workflowImplementationFactories = new ArrayList<>(); + + // Updated constructor receives plugins and dataConverter + Worker( + WorkflowClient client, + String taskQueue, + WorkerFactoryOptions factoryOptions, + WorkerOptions options, + // ... other params ... + List plugins, + DataConverter dataConverter + ) { + // ... existing initialization ... + this.plugins = plugins; + this.dataConverter = dataConverter; + } + + /** + * Registers workflow implementation types. + * Plugins are consulted first for each type. + */ + public void registerWorkflowImplementationTypes( + WorkflowImplementationOptions options, + Class... workflowImplementationClasses) { + checkNotStarted(); + + for (Class clazz : workflowImplementationClasses) { + WorkflowImplementationFactory factory = null; + + // Ask plugins if they handle this type + for (WorkerPlugin plugin : plugins) { + factory = plugin.getFactoryForType(clazz, dataConverter); + if (factory != null) { + break; // First plugin to return a factory wins + } + } + + if (factory != null) { + // Plugin handles this type - track the factory + if (!workflowImplementationFactories.contains(factory)) { + workflowImplementationFactories.add(factory); + } + } else { + // No plugin handles it - use default POJO factory + workflowWorker.registerWorkflowImplementationTypes(options, clazz); + } + } + } + + // During worker start, register all plugin factories with the workflow worker + public void start() { + // ... existing code ... + for (WorkflowImplementationFactory factory : workflowImplementationFactories) { + workflowWorker.registerWorkflowImplementationFactory(factory); + } + // ... existing code ... + } +} +``` + +### Component 4: WorkerFactory Plugin Integration + +**Location:** `temporal-sdk/src/main/java/io/temporal/worker/WorkerFactory.java` + +```java +public synchronized Worker newWorker(String taskQueue, WorkerOptions options) { + // ... existing validation ... + + // Let plugins configure worker options + WorkerOptions.Builder optionsBuilder = WorkerOptions.newBuilder(options); + for (WorkerPlugin plugin : factoryOptions.getPlugins()) { + optionsBuilder = plugin.configureWorker(optionsBuilder); + } + WorkerOptions configuredOptions = optionsBuilder.build(); + + DataConverter dataConverter = workflowClient.getOptions().getDataConverter(); + + Worker worker = new Worker( + workflowClient, + taskQueue, + factoryOptions, + configuredOptions, + // ... other params ... + factoryOptions.getPlugins(), // Pass plugins to worker + dataConverter // Pass dataConverter to worker + ); + + workers.put(taskQueue, worker); + + // Notify plugins of worker creation (for activity registration etc.) + for (WorkerPlugin plugin : factoryOptions.getPlugins()) { + plugin.onWorkerCreated(worker, dataConverter); + } + + return worker; +} +``` + +### Component 5: KotlinPlugin Implementation + +**Location:** `temporal-kotlin/src/main/kotlin/io/temporal/kotlin/worker/KotlinPlugin.kt` + +```kotlin +package io.temporal.kotlin.worker + +import io.temporal.common.converter.DataConverter +import io.temporal.internal.worker.WorkflowImplementationFactory +import io.temporal.kotlin.TemporalDsl +import io.temporal.kotlin.internal.KotlinWorkflowDefinition +import io.temporal.kotlin.internal.KotlinWorkflowImplementationFactory +import io.temporal.plugin.WorkerPlugin +import io.temporal.worker.WorkerOptions + +/** + * Plugin for enabling Kotlin coroutine support in Temporal workflows. + * + * When registered with a WorkerFactory, this plugin automatically detects Kotlin + * suspend workflows during registration and routes them to the Kotlin coroutine + * execution runtime. Non-suspend workflows are handled by the default Java factory. + * + * Usage: + * ```kotlin + * val factory = WorkerFactory.newInstance( + * client, + * WorkerFactoryOptions.newBuilder() + * .addPlugin(KotlinPlugin { deadlockDetectionTimeout = 1500L }) + * .build() + * ) + * val worker = factory.newWorker("task-queue") + * + * // Suspend workflows auto-detected and routed to Kotlin factory + * // Non-suspend workflows use default Java factory + * worker.registerWorkflowImplementationTypes( + * MySuspendWorkflowImpl::class.java, + * MyJavaWorkflowImpl::class.java + * ) + * ``` + */ +public class KotlinPlugin private constructor( + private val options: KotlinPluginOptions +) : WorkerPlugin { + + /** Lazily created factory - shared across all workflow types handled by this plugin. */ + private var factory: KotlinWorkflowImplementationFactory? = null + + /** + * Configures worker options before worker creation. + * Sets deadlock detection timeout if configured. + */ + override fun configureWorker(builder: WorkerOptions.Builder): WorkerOptions.Builder { + if (options.configureDeadlockDetection) { + builder.setDefaultDeadlockDetectionTimeout(options.deadlockDetectionTimeout) + } + return builder + } + + /** + * Called for each workflow type during registration. + * + * If the workflow uses Kotlin suspend functions, this plugin handles it by: + * 1. Creating/reusing a KotlinWorkflowImplementationFactory + * 2. Registering the type with that factory + * 3. Returning the factory + * + * For non-suspend workflows, returns null to let the default POJO factory handle them. + */ + override fun getFactoryForType( + workflowImplementationType: Class<*>, + dataConverter: DataConverter + ): WorkflowImplementationFactory? { + // Check if this is a Kotlin suspend workflow + if (!KotlinWorkflowDefinition.isSuspendWorkflow(workflowImplementationType)) { + return null // Not a suspend workflow, let default factory handle it + } + + // Lazily create the factory + if (factory == null) { + factory = KotlinWorkflowImplementationFactory( + dataConverter = dataConverter, + deadlockDetectionTimeoutMs = options.deadlockDetectionTimeout + ) + } + + // Register the workflow type with our factory + factory!!.registerWorkflowImplementationType(workflowImplementationType) + return factory + } + + public val deadlockDetectionTimeout: Long + get() = options.deadlockDetectionTimeout + + public companion object { + @JvmStatic + public fun create(): KotlinPlugin = KotlinPlugin(KotlinPluginOptions()) + + @JvmStatic + public fun create(options: KotlinPluginOptions): KotlinPlugin = KotlinPlugin(options) + + @JvmStatic + public fun create(block: KotlinPluginOptions.Builder.() -> Unit): KotlinPlugin { + return KotlinPlugin(KotlinPluginOptions.Builder().apply(block).build()) + } + } +} + +/** + * Configuration options for the Kotlin plugin. + */ +public class KotlinPluginOptions( + public val deadlockDetectionTimeout: Long = DEFAULT_DEADLOCK_DETECTION_TIMEOUT, + public val configureDeadlockDetection: Boolean = true +) { + public companion object { + public const val DEFAULT_DEADLOCK_DETECTION_TIMEOUT: Long = 1000L + } + + @TemporalDsl + public class Builder { + public var deadlockDetectionTimeout: Long = DEFAULT_DEADLOCK_DETECTION_TIMEOUT + public var configureDeadlockDetection: Boolean = true + + public fun build(): KotlinPluginOptions = KotlinPluginOptions( + deadlockDetectionTimeout = deadlockDetectionTimeout, + configureDeadlockDetection = configureDeadlockDetection + ) + } +} + +// DSL functions for Kotlin-friendly construction +public fun KotlinPlugin( + options: @TemporalDsl KotlinPluginOptions.Builder.() -> Unit +): KotlinPlugin = KotlinPlugin.create(options) + +public fun KotlinPlugin(): KotlinPlugin = KotlinPlugin.create() +``` + +## Changes Summary + +### Change 1: Create WorkerPlugin Interface + +**Summary:** Define plugin interface with `getFactoryForType` method + +**Scope:** +- Create `io.temporal.plugin` package +- Add `WorkerPlugin` interface with `configureWorker`, `getFactoryForType`, and `onWorkerCreated` methods +- `getFactoryForType` returns a factory for workflow types the plugin handles + +**Tests:** +- Unit test for default method behavior +- Test plugin returning factory for specific types +- Test plugin returning null for unhandled types + +--- + +### Change 2: Add Plugin Support to WorkerFactoryOptions + +**Summary:** Enable plugin registration at factory level + +**Scope:** +- Add `plugins` field to `WorkerFactoryOptions` +- Add `addPlugin()` and `setPlugins()` builder methods +- Add `getPlugins()` accessor + +**Tests:** +- Test adding single plugin +- Test adding multiple plugins +- Test plugin list immutability + +--- + +### Change 3: Integrate Plugins in Worker Registration + +**Summary:** Intercept workflow registration to call plugins + +**Scope:** +- Add `plugins` and `dataConverter` fields to Worker +- Modify `registerWorkflowImplementationTypes` to call `plugin.getFactoryForType` for each class +- Track plugin-provided factories and register them at worker start +- Classes not handled by plugins use default POJO factory + +**Tests:** +- Test plugin factory is used for handled types +- Test default factory is used for unhandled types +- Test mixed workflow registration (some handled, some not) +- Test DataConverter is passed correctly + +--- + +### Change 4: Update WorkerFactory to Pass Plugins + +**Summary:** Wire plugins and DataConverter through to Worker + +**Scope:** +- Pass `factoryOptions.getPlugins()` to Worker constructor +- Pass `dataConverter` from client to Worker constructor +- Call `plugin.configureWorker()` before worker creation +- Call `plugin.onWorkerCreated()` after worker creation + +--- + +### Change 5: Simplify KotlinPlugin + +**Summary:** Implement `getFactoryForType` for automatic suspend workflow detection + +**Scope:** +- Implement `WorkerPlugin.getFactoryForType` +- Detect suspend workflows via `KotlinWorkflowDefinition.isSuspendWorkflow()` +- Create factory lazily, reuse for all suspend workflow types +- Return `null` for non-suspend workflows + +**Tests:** +- Test suspend workflow detection and routing +- Test non-suspend workflow delegation to default factory +- Test factory reuse across multiple workflow types +- Test DataConverter propagation to factory + +## Usage Examples + +### Standard Registration with Plugin (Recommended) + +```kotlin +// 1. Create factory with Kotlin plugin +val factory = WorkerFactory.newInstance( + client, + WorkerFactoryOptions.newBuilder() + .addPlugin(KotlinPlugin { deadlockDetectionTimeout = 1500L }) + .build() +) + +// 2. Create worker +val worker = factory.newWorker("my-task-queue") + +// 3. Register workflows - suspend workflows auto-detected +worker.registerWorkflowImplementationTypes( + MySuspendWorkflowImpl::class.java, // Plugin handles this + MyJavaWorkflowImpl::class.java // Default factory handles this +) + +// 4. Register activities +worker.registerActivitiesImplementations(MyActivityImpl()) + +// 5. Start factory +factory.start() +``` + +### With Custom DataConverter + +```kotlin +// DataConverter flows from client through to plugin automatically +val clientOptions = WorkflowClientOptions.newBuilder() + .setDataConverter(JacksonJsonDataConverter.newDefaultInstance()) + .build() +val client = WorkflowClient.newInstance(service, clientOptions) + +// Plugin receives the client's DataConverter in getFactoryForType +val factory = WorkerFactory.newInstance( + client, + WorkerFactoryOptions.newBuilder() + .addPlugin(KotlinPlugin()) + .build() +) +``` + +### Legacy Extension Functions (Still Supported) + +```kotlin +// These extension functions are deprecated but still work +val plugin = KotlinPlugin() +worker.registerKotlinWorkflowImplementationTypes( + plugin, + MyWorkflowImpl::class +) +``` + +## Backward Compatibility + +- Existing `Worker.registerKotlinWorkflowImplementationTypes()` extension functions are deprecated but continue to work +- Existing code without plugins works unchanged - default POJO factory handles all workflows +- Plugin-based approach is opt-in via `WorkerFactoryOptions.addPlugin()` + +## Advantages of This Design + +1. **Single registration API** - Users use standard `registerWorkflowImplementationTypes` for all workflows +2. **Automatic detection** - Suspend workflows are automatically routed to the Kotlin factory +3. **DataConverter propagation** - Plugins receive the client's DataConverter automatically +4. **Extensible** - Pattern generalizes to other language/framework integrations +5. **Composable** - Multiple plugins can coexist, each handling different workflow types + +## Future Considerations + +1. **Activity plugins** - Could extend `getFactoryForType` pattern to activities +2. **Plugin ordering/priority** - Could add explicit ordering for plugin consultation +3. **Plugin lifecycle** - Could add `onWorkerShutdown` hook +4. **Client-side plugins** - Could add interceptors for workflow stubs diff --git a/kotlin/phases/phase-2.1-detailed.md b/kotlin/phases/phase-2.1-detailed.md new file mode 100644 index 0000000..8950983 --- /dev/null +++ b/kotlin/phases/phase-2.1-detailed.md @@ -0,0 +1,137 @@ +# Phase 2.1: Typed Activity Stubs - Detailed Plan + +## Overview + +Implement type-safe activity execution using Kotlin method references. This provides compile-time type checking for activity arguments and return types without requiring stub creation. + +## Prerequisites + +- Phase 1 complete (Core infrastructure and string-based activities working) + +## Changes + +### Change 1: Add KFunction Metadata Extraction Utilities + +**Summary:** Create utilities for extracting activity metadata from KFunction references + +**Scope:** +- Add `ActivityMetadataExtractor` internal class +- Extract activity name from `@ActivityMethod` annotation or method name +- Extract return type (handling generic types with `typeOf`) +- Extract declaring interface class + +**Tests:** +- Test name extraction with annotation +- Test name extraction without annotation +- Test return type extraction (simple types) +- Test return type extraction (generic types) + +**Dependencies:** None + +--- + +### Change 2: Implement KWorkflow.executeActivity with KFunction1 + +**Summary:** Add typed activity execution for 0-argument activities + +**Scope:** +- Add `executeActivity(KFunction1, options): R` overload +- Extract metadata using utilities from Change 1 +- Delegate to string-based execution + +**Tests:** +- Test 0-arg activity execution +- Test type inference for return type +- Test activity not found error + +**Dependencies:** Change 1 + +--- + +### Change 3: Implement KWorkflow.executeActivity with KFunction2-4 + +**Summary:** Add typed activity execution for 1-3 argument activities + +**Scope:** +- Add `executeActivity(KFunction2, options, A1): R` +- Add `executeActivity(KFunction3, options, A1, A2): R` +- Add `executeActivity(KFunction4, options, A1, A2, A3): R` + +**Tests:** +- Test 1-arg activity with correct types +- Test 2-arg activity with correct types +- Test 3-arg activity with correct types +- Test compile-time type errors (manual verification) + +**Dependencies:** Change 2 + +--- + +### Change 4: Implement KWorkflow.executeActivity with KFunction5-7 + +**Summary:** Add typed activity execution for 4-6 argument activities + +**Scope:** +- Add overloads for KFunction5, KFunction6, KFunction7 +- Follow same pattern as Change 3 + +**Tests:** +- Test 4-arg activity +- Test 5-arg activity +- Test 6-arg activity + +**Dependencies:** Change 3 + +--- + +### Change 5: Implement KWorkflow.startActivity with KFunction Overloads + +**Summary:** Add async typed activity execution returning handles + +**Scope:** +- Add `startActivity(KFunction2, options, A1): KActivityHandle` +- Add overloads for KFunction1-7 +- Return handle for async await + +**Tests:** +- Test handle returned with correct type +- Test parallel typed activities +- Test await on typed handle + +**Dependencies:** Change 4 + +--- + +### Change 6: Implement Typed Local Activity Execution + +**Summary:** Add KFunction-based local activity execution + +**Scope:** +- Add `executeLocalActivity` overloads with KFunction1-7 +- Add `startLocalActivity` overloads with KFunction1-7 +- Wire to local activity execution + +**Tests:** +- Test typed local activity execution +- Test local activity options respected +- Test local activity return types + +**Dependencies:** Change 5 + +--- + +### Change 7: Add Java Activity Interface Support + +**Summary:** Ensure typed execution works with Java-defined activity interfaces + +**Scope:** +- Test and fix any issues with Java interface method references +- Handle Java Optional return types if present +- Document interop behavior + +**Tests:** +- Test calling Java activity interface from Kotlin workflow +- Test Java activity with various return types +- Integration test with Java activity implementation + +**Dependencies:** Change 6 diff --git a/kotlin/phases/phase-2.2-detailed.md b/kotlin/phases/phase-2.2-detailed.md new file mode 100644 index 0000000..cff5882 --- /dev/null +++ b/kotlin/phases/phase-2.2-detailed.md @@ -0,0 +1,156 @@ +# Phase 2.2: Signals, Queries, Updates - Detailed Plan + +## Overview + +Implement support for workflow signals, queries, and updates with suspend function support for signal and update handlers. + +## Prerequisites + +- Phase 2.1 complete (Typed activity stubs available) + +## Changes + +### Change 1: Implement Signal Handler Registration + +**Summary:** Support @SignalMethod with suspend functions in Kotlin workflows + +**Scope:** +- Extend `KotlinWorkflowDefinition` to extract signal methods +- Support both suspend and non-suspend signal handlers +- Register handlers with `ReplayWorkflowContext` + +**Tests:** +- Test signal handler registration +- Test suspend signal handler +- Test non-suspend signal handler +- Test signal with arguments + +**Dependencies:** None + +--- + +### Change 2: Implement Signal Dispatch in KotlinReplayWorkflow + +**Summary:** Handle incoming signals in coroutine context + +**Scope:** +- Add signal dispatch to `KotlinReplayWorkflow` +- Launch signal handlers as child coroutines +- Handle signal handler exceptions + +**Tests:** +- Test signal delivery to handler +- Test multiple signals queued +- Test signal handler exception handling + +**Dependencies:** Change 1 + +--- + +### Change 3: Implement Query Handler Registration + +**Summary:** Support @QueryMethod including property syntax + +**Scope:** +- Extend `KotlinWorkflowDefinition` to extract query methods +- Support Kotlin property getters as queries +- Queries are always synchronous (never suspend) + +**Tests:** +- Test query method registration +- Test query property registration +- Test query with arguments +- Test query return value + +**Dependencies:** Change 2 + +--- + +### Change 4: Implement Query Dispatch + +**Summary:** Handle incoming queries in workflow + +**Scope:** +- Add query dispatch to `KotlinReplayWorkflow` +- Execute query synchronously (not in coroutine) +- Return serialized result + +**Tests:** +- Test query execution +- Test query during workflow execution +- Test query exception handling + +**Dependencies:** Change 3 + +--- + +### Change 5: Implement Update Handler Registration + +**Summary:** Support @UpdateMethod with suspend functions + +**Scope:** +- Extend `KotlinWorkflowDefinition` to extract update methods +- Support `@UpdateValidatorMethod` for validation +- Register handlers with context + +**Tests:** +- Test update handler registration +- Test update validator registration +- Test update with arguments + +**Dependencies:** Change 4 + +--- + +### Change 6: Implement Update Validation + +**Summary:** Execute update validators before update handlers + +**Scope:** +- Call validator synchronously before accepting update +- Validators are never suspend +- Reject update if validator throws + +**Tests:** +- Test validator called before handler +- Test validator rejection +- Test validator with arguments + +**Dependencies:** Change 5 + +--- + +### Change 7: Implement Update Dispatch + +**Summary:** Handle incoming updates in coroutine context + +**Scope:** +- Add update dispatch to `KotlinReplayWorkflow` +- Launch update handlers as coroutines +- Return update result to caller + +**Tests:** +- Test update execution +- Test update result returned +- Test update exception handling +- Test concurrent updates + +**Dependencies:** Change 6 + +--- + +### Change 8: Integration Tests for Signals/Queries/Updates + +**Summary:** End-to-end tests for all handler types + +**Scope:** +- Integration test combining signals, queries, updates +- Test interaction between handlers and main workflow +- Test state visibility in queries during updates + +**Tests:** +- E2E: signal triggers workflow progress +- E2E: query returns current state +- E2E: update modifies and returns state + +**Dependencies:** Change 7 diff --git a/kotlin/phases/phase-2.3-detailed.md b/kotlin/phases/phase-2.3-detailed.md new file mode 100644 index 0000000..3090fc1 --- /dev/null +++ b/kotlin/phases/phase-2.3-detailed.md @@ -0,0 +1,154 @@ +# Phase 2.3: Child Workflows - Detailed Plan + +## Overview + +Implement child workflow execution with both typed (method reference) and untyped (string-based) APIs. + +## Prerequisites + +- Phase 2.2 complete (Signals/Queries/Updates available) + +## Changes + +### Change 1: Add ChildWorkflowOptions DSL + +**Summary:** Create Kotlin DSL builder for ChildWorkflowOptions + +**Scope:** +- Add `ChildWorkflowOptions` DSL builder function +- Support Kotlin Duration for timeouts +- Include all options: workflowId, taskQueue, retryOptions, parentClosePolicy, etc. + +**Tests:** +- Test DSL builds correct options +- Test Duration conversion +- Test all option properties + +**Dependencies:** None + +--- + +### Change 2: Implement KChildWorkflowHandle + +**Summary:** Create handle type for child workflow execution + +**Scope:** +- Add `KChildWorkflowHandle` interface +- Add `await(): R` suspend function for result +- Add `signal()` methods for signaling child +- Add `workflowId` and `runId` properties + +**Tests:** +- Test await completion +- Test signal delivery +- Test handle properties + +**Dependencies:** Change 1 + +--- + +### Change 3: Implement KWorkflow.executeChildWorkflow (String-based) + +**Summary:** Add string-based child workflow execution + +**Scope:** +- Add `executeChildWorkflow(workflowType, options, vararg args): R` +- Wire to `KotlinWorkflowContext.executeChildWorkflow()` +- Handle result deserialization + +**Tests:** +- Test child workflow execution +- Test child workflow failure propagation +- Test child workflow cancellation + +**Dependencies:** Change 2 + +--- + +### Change 4: Implement KWorkflow.startChildWorkflow (String-based) + +**Summary:** Add async child workflow execution returning handle + +**Scope:** +- Add `startChildWorkflow(workflowType, options, vararg args): KChildWorkflowHandle` +- Return handle after child workflow started +- Support parallel child workflows + +**Tests:** +- Test handle returned after start +- Test parallel child workflows +- Test await on handle + +**Dependencies:** Change 3 + +--- + +### Change 5: Add Workflow Metadata Extraction Utilities + +**Summary:** Create utilities for extracting workflow metadata from KFunction + +**Scope:** +- Add `WorkflowMetadataExtractor` internal class +- Extract workflow type from `@WorkflowMethod` or method name +- Extract return type and parameter types + +**Tests:** +- Test type extraction with annotation +- Test type extraction without annotation +- Test return type handling + +**Dependencies:** Change 4 + +--- + +### Change 6: Implement KWorkflow.executeChildWorkflow (Typed) + +**Summary:** Add KFunction-based child workflow execution + +**Scope:** +- Add `executeChildWorkflow(KFunction2, options, A1): R` +- Add overloads for KFunction1-7 +- Extract metadata and delegate to string-based + +**Tests:** +- Test typed child workflow execution +- Test type inference +- Test with various argument counts + +**Dependencies:** Change 5 + +--- + +### Change 7: Implement KWorkflow.startChildWorkflow (Typed) + +**Summary:** Add KFunction-based async child workflow execution + +**Scope:** +- Add `startChildWorkflow(KFunction2, options, A1): KChildWorkflowHandle` +- Add overloads for KFunction1-7 +- Return typed handle + +**Tests:** +- Test typed handle +- Test parallel typed child workflows +- Test signal on typed handle + +**Dependencies:** Change 6 + +--- + +### Change 8: Implement Continue-As-New + +**Summary:** Add continue-as-new support for Kotlin workflows + +**Scope:** +- Add `KWorkflow.continueAsNew(vararg args)` function +- Add typed `KWorkflow.continueAsNew(KFunction, args)` overloads +- Throw `ContinueAsNewException` equivalent + +**Tests:** +- Test continue-as-new execution +- Test with different arguments +- Test typed continue-as-new + +**Dependencies:** Change 7 diff --git a/kotlin/phases/phase-2.4-detailed.md b/kotlin/phases/phase-2.4-detailed.md new file mode 100644 index 0000000..ebf7b91 --- /dev/null +++ b/kotlin/phases/phase-2.4-detailed.md @@ -0,0 +1,191 @@ +# Phase 2.4: Client API - Detailed Plan + +## Overview + +Implement the Kotlin client API for starting workflows, getting handles, and interacting with running workflows. + +## Prerequisites + +- Phase 2.3 complete (Child workflows available) + +## Changes + +### Change 1: Implement WorkflowHandle (Untyped) + +**Summary:** Create untyped workflow handle for string-based operations + +**Scope:** +- Add `WorkflowHandle` interface in `io.temporal.kotlin.client` +- Add `result()` suspend function +- Add `signal(name, vararg args)` method +- Add `query(name, vararg args)` method +- Add `cancel()`, `terminate()` methods + +**Tests:** +- Test result retrieval +- Test signal by name +- Test query by name +- Test cancel/terminate + +**Dependencies:** None + +--- + +### Change 2: Implement KWorkflowHandle (Typed) + +**Summary:** Create typed workflow handle for method reference operations + +**Scope:** +- Add `KWorkflowHandle` interface extending base operations +- Add `signal(KFunction, args)` overloads +- Add `query(KFunction): R` overloads +- Add `result()` requiring explicit type + +**Tests:** +- Test typed signal +- Test typed query +- Test property query syntax + +**Dependencies:** Change 1 + +--- + +### Change 3: Implement KTypedWorkflowHandle + +**Summary:** Create handle with captured result type from startWorkflow + +**Scope:** +- Add `KTypedWorkflowHandle` extending `KWorkflowHandle` +- Add `result(): R` with inferred type +- Returned by `startWorkflow` with method reference + +**Tests:** +- Test result type inference +- Test no explicit type needed + +**Dependencies:** Change 2 + +--- + +### Change 4: Implement KUpdateHandle + +**Summary:** Create handle for async update operations + +**Scope:** +- Add `KUpdateHandle` interface +- Add `updateId` property +- Add `result(): R` suspend function + +**Tests:** +- Test update result retrieval +- Test update ID access + +**Dependencies:** Change 3 + +--- + +### Change 5: Add Update Methods to KWorkflowHandle + +**Summary:** Add update execution methods to workflow handles + +**Scope:** +- Add `executeUpdate(KFunction, args): R` to `KWorkflowHandle` +- Add `startUpdate(KFunction, args): KUpdateHandle` +- Add `getKUpdateHandle(updateId)` for existing updates + +**Tests:** +- Test execute update and wait +- Test start update async +- Test get existing update handle + +**Dependencies:** Change 4 + +--- + +### Change 6: Implement WorkflowClient.startWorkflow + +**Summary:** Add Kotlin extension for starting workflows with method references + +**Scope:** +- Add `WorkflowClient.startWorkflow(KFunction, args, options): KTypedWorkflowHandle` +- Capture result type from method reference +- Support KFunction1-7 overloads + +**Tests:** +- Test workflow start +- Test handle type inference +- Test various argument counts + +**Dependencies:** Change 5 + +--- + +### Change 7: Implement WorkflowClient.executeWorkflow + +**Summary:** Add extension for starting and waiting for workflow result + +**Scope:** +- Add `WorkflowClient.executeWorkflow(KFunction, args, options): R` +- Start workflow and immediately await result +- Support KFunction1-7 overloads + +**Tests:** +- Test execute and wait +- Test result type inference +- Test failure propagation + +**Dependencies:** Change 6 + +--- + +### Change 8: Implement WorkflowClient.getKWorkflowHandle + +**Summary:** Add extension for getting typed handle to existing workflow + +**Scope:** +- Add `WorkflowClient.getKWorkflowHandle(workflowId): KWorkflowHandle` +- Add optional runId parameter +- Return typed handle for signal/query operations + +**Tests:** +- Test get handle by ID +- Test typed operations on handle +- Test with specific runId + +**Dependencies:** Change 7 + +--- + +### Change 9: Implement signalWithStart + +**Summary:** Add atomic signal-with-start operation + +**Scope:** +- Add `WorkflowClient.signalWithStart(workflow, workflowArg, signal, signalArg, options): KTypedWorkflowHandle` +- Atomically start or signal existing workflow +- Return typed handle + +**Tests:** +- Test start new workflow with signal +- Test signal existing workflow +- Test handle operations after + +**Dependencies:** Change 8 + +--- + +### Change 10: Implement updateWithStart + +**Summary:** Add atomic update-with-start operation + +**Scope:** +- Add `WorkflowClient.updateWithStart(workflow, workflowArg, update, updateArg, options): Pair` +- Atomically start or update existing workflow +- Return handle and update result + +**Tests:** +- Test start new workflow with update +- Test update existing workflow +- Test both return values + +**Dependencies:** Change 9 diff --git a/kotlin/phases/phase-2.5-detailed.md b/kotlin/phases/phase-2.5-detailed.md new file mode 100644 index 0000000..e0ab09f --- /dev/null +++ b/kotlin/phases/phase-2.5-detailed.md @@ -0,0 +1,138 @@ +# Phase 2.5: Kotlin Wrappers - Detailed Plan + +## Overview + +Implement Kotlin wrapper types for activity context and info, providing null-safe APIs and idiomatic Kotlin access patterns. + +## Prerequisites + +- Phase 2.4 complete (Client API available) + +## Changes + +### Change 1: Implement KActivityInfo + +**Summary:** Create Kotlin wrapper for ActivityInfo with null safety + +**Scope:** +- Add `KActivityInfo` interface in `io.temporal.kotlin.activity` +- Replace `Optional` with nullable types +- Add properties: activityId, activityType, workflowId, attempt, etc. +- Add `getHeartbeatDetails(): Payloads?` + +**Tests:** +- Test all property accessors +- Test nullable properties +- Test heartbeat details retrieval + +**Dependencies:** None + +--- + +### Change 2: Implement KActivityContext + +**Summary:** Create Kotlin wrapper for ActivityExecutionContext + +**Scope:** +- Add `KActivityContext` interface +- Add `info: KActivityInfo` property +- Add `heartbeat(details: Any?)` suspend function +- Add `doNotCompleteOnReturn()` method + +**Tests:** +- Test info access +- Test heartbeat call +- Test do not complete flag + +**Dependencies:** Change 1 + +--- + +### Change 3: Implement KActivity Object + +**Summary:** Create KActivity object as entry point for activity APIs + +**Scope:** +- Add `KActivity` object in `io.temporal.kotlin.activity` +- Add `getContext(): KActivityContext` function +- Add `getInfo(): KActivityInfo` shortcut +- Add internal context storage (thread-local or coroutine context) + +**Tests:** +- Test context retrieval in activity +- Test info shortcut +- Test context not available outside activity + +**Dependencies:** Change 2 + +--- + +### Change 4: Implement Suspend Activity Support + +**Summary:** Support suspend functions in activity implementations + +**Scope:** +- Detect suspend activity methods in registration +- Execute suspend activities with coroutine dispatcher +- Integrate with activity heartbeat + +**Tests:** +- Test suspend activity execution +- Test suspend activity with I/O +- Test heartbeat from suspend activity + +**Dependencies:** Change 3 + +--- + +### Change 5: Implement @KActivityImpl Annotation + +**Summary:** Add annotation for parallel activity interface pattern + +**Scope:** +- Add `@KActivityImpl(activities = KClass)` annotation +- Link suspend interface to non-suspend interface +- Support registration of implementations + +**Tests:** +- Test annotation processing +- Test interface linking +- Test registration with annotation + +**Dependencies:** Change 4 + +--- + +### Change 6: Implement Activity Registration Extensions + +**Summary:** Add worker extensions for registering Kotlin activity implementations + +**Scope:** +- Add `Worker.registerActivitiesImplementations()` extension +- Auto-detect suspend activities +- Support both direct and @KActivityImpl patterns + +**Tests:** +- Test direct suspend activity registration +- Test @KActivityImpl registration +- Test mixed registration + +**Dependencies:** Change 5 + +--- + +### Change 7: Documentation and Examples + +**Summary:** Add comprehensive documentation for activity patterns + +**Scope:** +- Document Option A (pure Kotlin) pattern +- Document Option B (Java interop) pattern +- Add example activity implementations +- Add migration guide section + +**Tests:** +- Example code compiles +- Examples pass execution tests + +**Dependencies:** Change 6 diff --git a/kotlin/phases/phase-3.1-detailed.md b/kotlin/phases/phase-3.1-detailed.md new file mode 100644 index 0000000..d5add59 --- /dev/null +++ b/kotlin/phases/phase-3.1-detailed.md @@ -0,0 +1,136 @@ +# Phase 3.1: Test Environment - Detailed Plan + +## Overview + +Implement a Kotlin-friendly test environment for workflow testing, integrating with kotlinx-coroutines-test utilities. + +## Prerequisites + +- Phase 2 complete (Full Kotlin SDK API available) + +## Changes + +### Change 1: Add Test Dependencies + +**Summary:** Add kotlinx-coroutines-test and test infrastructure + +**Scope:** +- Add `kotlinx-coroutines-test` dependency +- Add test utility package `io.temporal.kotlin.testing` +- Set up test module structure + +**Tests:** +- Verify dependency resolution +- Basic compilation test + +**Dependencies:** None + +--- + +### Change 2: Implement KTestWorkflowEnvironment + +**Summary:** Create Kotlin wrapper for TestWorkflowEnvironment + +**Scope:** +- Add `KTestWorkflowEnvironment` class +- Wrap Java `TestWorkflowEnvironment` +- Add Kotlin DSL for configuration +- Add `client` property returning Kotlin client + +**Tests:** +- Test environment creation +- Test configuration options +- Test client access + +**Dependencies:** Change 1 + +--- + +### Change 3: Implement Test Worker Registration + +**Summary:** Add test worker with Kotlin workflow support + +**Scope:** +- Add `KTestWorkflowEnvironment.newWorker(taskQueue)` returning configured worker +- Auto-register Kotlin plugin for test workers +- Support workflow and activity registration + +**Tests:** +- Test worker creation +- Test workflow registration +- Test activity registration + +**Dependencies:** Change 2 + +--- + +### Change 4: Implement runWorkflowTest DSL + +**Summary:** Create DSL for writing workflow tests + +**Scope:** +- Add `runWorkflowTest { }` top-level function +- Auto-create environment and clean up +- Provide `environment`, `client`, `worker` in scope +- Integrate with coroutine test dispatcher + +**Tests:** +- Test DSL usage +- Test auto-cleanup +- Test coroutine integration + +**Dependencies:** Change 3 + +--- + +### Change 5: Implement Test Workflow Execution Helpers + +**Summary:** Add helper methods for common test patterns + +**Scope:** +- Add `KTestWorkflowEnvironment.executeWorkflow()` with timeout +- Add `KTestWorkflowEnvironment.startWorkflow()` returning test handle +- Add assertion helpers for workflow state + +**Tests:** +- Test execute with result +- Test start and interact +- Test timeout handling + +**Dependencies:** Change 4 + +--- + +### Change 6: Implement Test Activity Environment + +**Summary:** Add support for testing activities in isolation + +**Scope:** +- Add `KTestActivityEnvironment` class +- Support testing suspend activities +- Provide mock heartbeat recording + +**Tests:** +- Test activity execution +- Test heartbeat recording +- Test activity failure + +**Dependencies:** Change 5 + +--- + +### Change 7: Integration with JUnit 5 + +**Summary:** Add JUnit 5 extension for Kotlin workflow tests + +**Scope:** +- Add `@KTemporalTest` annotation +- Add `KTemporalTestExtension` implementing JUnit extension +- Auto-inject test environment into test class + +**Tests:** +- Test annotation processing +- Test environment injection +- Test lifecycle management + +**Dependencies:** Change 6 diff --git a/kotlin/phases/phase-3.2-detailed.md b/kotlin/phases/phase-3.2-detailed.md new file mode 100644 index 0000000..d212f88 --- /dev/null +++ b/kotlin/phases/phase-3.2-detailed.md @@ -0,0 +1,170 @@ +# Phase 3.2: Mocking Support - Detailed Plan + +## Overview + +Implement activity mocking and time manipulation utilities for comprehensive workflow testing. + +## Prerequisites + +- Phase 3.1 complete (Test environment available) + +## Changes + +### Change 1: Implement Activity Mocking Interface + +**Summary:** Create interface for mocking activity behavior + +**Scope:** +- Add `KActivityMock` interface +- Support mocking by activity interface type +- Support mocking by activity name (string) + +**Tests:** +- Test mock interface creation +- Test type-safe mock setup + +**Dependencies:** None + +--- + +### Change 2: Implement Activity Mock Registration + +**Summary:** Add mock registration to test worker + +**Scope:** +- Add `Worker.registerActivityMock(mock)` extension +- Add `Worker.registerActivityMock(name, handler)` for untyped +- Mocks take precedence over real implementations + +**Tests:** +- Test mock registration +- Test mock precedence +- Test mixed mock and real + +**Dependencies:** Change 1 + +--- + +### Change 3: Implement Mock Response Builders + +**Summary:** Create DSL for configuring mock responses + +**Scope:** +- Add `KActivityMock.returns(value)` for success +- Add `KActivityMock.throws(exception)` for failure +- Add `KActivityMock.answers { args -> result }` for dynamic + +**Tests:** +- Test static return value +- Test exception throwing +- Test dynamic answers + +**Dependencies:** Change 2 + +--- + +### Change 4: Implement Suspend Activity Mocking + +**Summary:** Support mocking suspend activity functions + +**Scope:** +- Add `KActivityMock.coAnswers { args -> result }` for suspend +- Support delay simulation in mocks +- Handle coroutine context in mocks + +**Tests:** +- Test suspend mock +- Test mock with delay +- Test mock cancellation + +**Dependencies:** Change 3 + +--- + +### Change 5: Implement Mock Verification + +**Summary:** Add verification capabilities for mock calls + +**Scope:** +- Add `KActivityMock.verify(times)` for call count +- Add `KActivityMock.verifyArgs(matcher)` for argument verification +- Add `KActivityMock.verifyNever()` shortcut + +**Tests:** +- Test call count verification +- Test argument verification +- Test verify never called + +**Dependencies:** Change 4 + +--- + +### Change 6: Implement Time Skipping + +**Summary:** Add time manipulation for test environment + +**Scope:** +- Add `KTestWorkflowEnvironment.skipTime(duration)` function +- Add `KTestWorkflowEnvironment.skipUntil(instant)` function +- Advance workflow timers without real delay + +**Tests:** +- Test skip by duration +- Test skip to instant +- Test timer advancement + +**Dependencies:** Change 5 + +--- + +### Change 7: Implement Auto Time Skipping + +**Summary:** Add automatic time advancement mode + +**Scope:** +- Add `KTestWorkflowEnvironment.autoTimeSkipping` property +- Automatically advance time when workflow blocked on timer +- Configurable skip increment + +**Tests:** +- Test auto skip enabled +- Test skip increment configuration +- Test disable auto skip + +**Dependencies:** Change 6 + +--- + +### Change 8: Implement Test Clock Access + +**Summary:** Expose test clock for assertions + +**Scope:** +- Add `KTestWorkflowEnvironment.currentTime` property +- Add `KTestWorkflowEnvironment.setCurrentTime(instant)` for setup +- Coordinate with workflow's `KWorkflow.currentTime()` + +**Tests:** +- Test read current time +- Test set initial time +- Test time consistency in workflow + +**Dependencies:** Change 7 + +--- + +### Change 9: Documentation and Examples + +**Summary:** Add comprehensive testing documentation + +**Scope:** +- Document activity mocking patterns +- Document time skipping strategies +- Add complete test examples +- Add troubleshooting guide + +**Tests:** +- Example code compiles +- Examples pass execution + +**Dependencies:** Change 8 diff --git a/kotlin/sdk-api.md b/kotlin/sdk-api.md index 575cb6b..7335bad 100644 --- a/kotlin/sdk-api.md +++ b/kotlin/sdk-api.md @@ -29,6 +29,25 @@ The Kotlin SDK provides an idiomatic Kotlin experience for building Temporal wor > **Note:** Nexus support is a separate project and will be addressed independently. +## Design Principle + +**Use idiomatic Kotlin language patterns wherever possible instead of custom APIs.** + +The Kotlin SDK should feel natural to Kotlin developers by leveraging standard `kotlinx.coroutines` primitives. Custom APIs should only be introduced when Temporal-specific semantics cannot be achieved through standard patterns. + +| Pattern | Standard Kotlin | Temporal Integration | +|---------|-----------------|----------------------| +| Parallel execution | `coroutineScope { async { ... } }` | Works via deterministic dispatcher | +| Await multiple | `awaitAll(d1, d2)` | Standard kotlinx.coroutines | +| Sleep/delay | `delay(duration)` | Intercepted via `Delay` interface | +| Deferred results | `Deferred` | Standard + `Promise.toDeferred()` | + +This approach provides: +- **Familiar patterns**: Kotlin developers use patterns they already know +- **IDE support**: Full autocomplete and documentation for standard APIs +- **Ecosystem compatibility**: Works with existing coroutine libraries and utilities +- **Smaller API surface**: Less custom code to learn and maintain + ## Kotlin Idioms The SDK leverages Kotlin-specific language features for an idiomatic experience. @@ -42,21 +61,23 @@ import kotlin.time.Duration.Companion.seconds import kotlin.time.Duration.Companion.minutes import kotlin.time.Duration.Companion.hours -// Activity with timeouts +// Activity with timeouts using existing DSL builders val result = KWorkflow.executeActivity( "ProcessOrder", - orderData, ActivityOptions { startToCloseTimeout = 30.seconds scheduleToCloseTimeout = 5.minutes heartbeatTimeout = 10.seconds - } + }, + orderData ) -// Timers +// Timers - standard kotlinx.coroutines delay (intercepted for determinism) delay(1.hours) ``` +> **Note:** The `ActivityOptions { }` DSL already exists in the `temporal-kotlin` module. The `Delay` interface intercepts standard `delay()` calls and routes them through Temporal's deterministic timer. + ### Null Safety Nullable types replace `Optional` throughout the API. The following Java SDK types have Kotlin equivalents with null-safe APIs: @@ -65,7 +86,7 @@ Nullable types replace `Optional` throughout the API. The following Java SDK |----------|------------| | `io.temporal.workflow.Workflow` | `KWorkflow` object | | `io.temporal.workflow.WorkflowInfo` | `KWorkflowInfo` | -| `io.temporal.workflow.Promise` | `KActivityHandle` / `Deferred` | +| `io.temporal.workflow.Promise` | Standard `Deferred` via `Promise.toDeferred()` | | `io.temporal.activity.Activity` | `KActivity` object | | `io.temporal.activity.ActivityExecutionContext` | `KActivityContext` | | `io.temporal.activity.ActivityInfo` | `KActivityInfo` | @@ -151,8 +172,8 @@ class GreetingWorkflowImpl : GreetingWorkflow { override suspend fun getGreeting(name: String): String { return KWorkflow.executeActivity( "composeGreeting", - "Hello", name, - ActivityOptions { startToCloseTimeout = 10.seconds } + ActivityOptions { startToCloseTimeout = 10.seconds }, + "Hello", name ) } } @@ -187,7 +208,7 @@ interface OrderWorkflow { } // Parallel suspend interface for Kotlin implementation -@KWorkflowImpl(workflow = OrderWorkflow::class) +@KWorkflowImpl(workflowInterface = OrderWorkflow::class) interface OrderWorkflowSuspend { suspend fun processOrder(order: Order): OrderResult suspend fun cancelOrder(reason: String) @@ -270,33 +291,38 @@ Child workflows use the same stub-less pattern as activities: override suspend fun parentWorkflow(): String { return KWorkflow.executeChildWorkflow( ChildWorkflow::doWork, - "input", ChildWorkflowOptions { workflowId = "child-workflow-id" - } + }, + "input" ) } -// Parallel case - start child workflow and get handle +// Parallel case - use standard coroutineScope { async {} } override suspend fun parentWorkflowParallel(): String = coroutineScope { - // Start child and activity in parallel - val childHandle = KWorkflow.startChildWorkflow( - ChildWorkflow::doWork, - "input", - ChildWorkflowOptions { - workflowId = "child-workflow-id" - } - ) - val activityHandle = KWorkflow.startActivity( - SomeActivities::doSomething, - ActivityOptions { startToCloseTimeout = 30.seconds } - ) + // Start child and activity in parallel using standard Kotlin async + val childDeferred = async { + KWorkflow.executeChildWorkflow( + ChildWorkflow::doWork, + ChildWorkflowOptions { workflowId = "child-workflow-id" }, + "input" + ) + } + val activityDeferred = async { + KWorkflow.executeActivity( + SomeActivities::doSomething, + ActivityOptions { startToCloseTimeout = 30.seconds } + ) + } - // Wait for both - "${childHandle.await()} - ${activityHandle.await()}" + // Wait for both using standard awaitAll + val (childResult, activityResult) = awaitAll(childDeferred, activityDeferred) + "$childResult - $activityResult" } ``` +> **Note:** We use standard `coroutineScope { async { } }` instead of custom `startChildWorkflow` or `startActivity` methods. The deterministic dispatcher ensures these execute correctly during replay. + **ChildWorkflowOptions:** ```kotlin @@ -327,31 +353,37 @@ override suspend fun workflowWithTimer(): String { ### Parallel Execution -Use `coroutineScope` when you need `async` for parallel execution: +Use standard `coroutineScope { async { } }` for parallel execution: ```kotlin val options = ActivityOptions { startToCloseTimeout = 30.seconds } override suspend fun parallelWorkflow(items: List): List = coroutineScope { - // Process all items in parallel - // coroutineScope is required because async needs a CoroutineScope + // Process all items in parallel using standard Kotlin patterns items.map { item -> async { - KWorkflow.executeActivity("process", item, options) + KWorkflow.executeActivity("process", options, item) } - }.awaitAll() + }.awaitAll() // Standard kotlinx.coroutines.awaitAll } // Another example: parallel activities with different results override suspend fun getGreetings(name: String): String = coroutineScope { - val hello = async { KWorkflow.executeActivity("greet", "Hello", name, options) } - val goodbye = async { KWorkflow.executeActivity("greet", "Goodbye", name, options) } + val hello = async { KWorkflow.executeActivity("greet", options, "Hello", name) } + val goodbye = async { KWorkflow.executeActivity("greet", options, "Goodbye", name) } - "${hello.await()}\n${goodbye.await()}" + // Standard awaitAll works with any Deferred + val (helloResult, goodbyeResult) = awaitAll(hello, goodbye) + "$helloResult\n$goodbyeResult" } ``` -Note: `coroutineScope` provides structured concurrency—if one `async` fails, all others are automatically cancelled. +**Why this works deterministically:** +- All coroutines inherit the workflow's deterministic dispatcher +- The dispatcher executes tasks in a FIFO queue ensuring consistent ordering +- Same execution order during replay + +> **Note:** `coroutineScope` provides structured concurrency—if one `async` fails, all others are automatically cancelled. This maps naturally to Temporal's cancellation semantics. ### Await Condition @@ -365,7 +397,7 @@ override suspend fun workflowWithCondition(): String { // ... // Wait until approved (blocks workflow until condition is true) - KWorkflow.condition { approved } + KWorkflow.awaitCondition { approved } return "Approved" } @@ -374,7 +406,7 @@ override suspend fun workflowWithCondition(): String { override suspend fun workflowWithTimeout(): String { var approved = false - val wasApproved = KWorkflow.condition(timeout = 24.hours) { approved } + val wasApproved = KWorkflow.awaitCondition(timeout = 24.hours) { approved } return if (wasApproved) "Approved" else "Timed out" } @@ -432,8 +464,8 @@ override suspend fun processOrder(order: Order): OrderResult { withContext(NonCancellable) { KWorkflow.executeActivity( OrderActivities::releaseReservation, - order, - ActivityOptions { startToCloseTimeout = 30.seconds } + ActivityOptions { startToCloseTimeout = 30.seconds }, + order ) } throw e // Re-throw to propagate cancellation @@ -483,28 +515,29 @@ override suspend fun tryProcess(order: Order): OrderResult? { For calling activities by name (useful for cross-language interop or dynamic activity names): ```kotlin -// Execute activity by string name +// Execute activity by string name - suspend function, awaits result val result = KWorkflow.executeActivity( "activityName", - arg1, arg2, ActivityOptions { startToCloseTimeout = 30.seconds - retryOptions = RetryOptions { + retryOptions { initialInterval = 1.seconds maximumAttempts = 3 } - } + }, + arg1, arg2 ) -// Async execution by string name -val handle = KWorkflow.startActivity( - "activityName", - arg1, arg2, - ActivityOptions { startToCloseTimeout = 30.seconds } -) -val result = handle.await() +// Parallel execution - use standard coroutineScope { async {} } +val results = coroutineScope { + val d1 = async { KWorkflow.executeActivity("activity1", options, arg1) } + val d2 = async { KWorkflow.executeActivity("activity2", options, arg2) } + awaitAll(d1, d2) // Returns List +} ``` +> **Note:** The `ActivityOptions { }` DSL already exists in `temporal-kotlin`. The nested `retryOptions { }` block is also supported. + ### Typed Activities (Phase 2) The typed activity API uses direct method references - no stub creation needed. This approach: @@ -530,29 +563,29 @@ interface GreetingActivities { // In workflow - direct method reference, no stub needed val greeting = KWorkflow.executeActivity( GreetingActivities::composeGreeting, // Direct reference to interface method - "Hello", "World", ActivityOptions { startToCloseTimeout = 30.seconds - } + }, + "Hello", "World" ) // Different activity, different options val result = KWorkflow.executeActivity( GreetingActivities::sendEmail, - email, ActivityOptions { startToCloseTimeout = 2.minutes retryOptions = RetryOptions { maximumAttempts = 5 } - } + }, + email ) // Void activities work too KWorkflow.executeActivity( GreetingActivities::log, - "Processing started", - ActivityOptions { startToCloseTimeout = 5.seconds } + ActivityOptions { startToCloseTimeout = 5.seconds }, + "Processing started" ) ``` @@ -562,12 +595,12 @@ KWorkflow.executeActivity( // Compile error! Wrong argument types KWorkflow.executeActivity( GreetingActivities::composeGreeting, - 123, true, // ✗ Type mismatch: expected String, String - options + options, + 123, true // ✗ Type mismatch: expected String, String ) ``` -**Parallel Execution:** Use `startActivity` to get handles for concurrent execution: +**Parallel Execution:** Use standard `coroutineScope { async { } }` for concurrent execution: ```kotlin override suspend fun parallelGreetings(names: List): List = coroutineScope { @@ -575,20 +608,23 @@ override suspend fun parallelGreetings(names: List): List = coro async { KWorkflow.executeActivity( GreetingActivities::composeGreeting, - "Hello", name, - ActivityOptions { startToCloseTimeout = 10.seconds } + ActivityOptions { startToCloseTimeout = 10.seconds }, + "Hello", name ) } - }.awaitAll() + }.awaitAll() // Standard kotlinx.coroutines.awaitAll } -// Or with handles for more control -val handle1 = KWorkflow.startActivity(GreetingActivities::composeGreeting, "Hello", "Alice", options) -val handle2 = KWorkflow.startActivity(GreetingActivities::composeGreeting, "Hello", "Bob", options) -// Do other work... -val results = listOf(handle1.await(), handle2.await()) +// Multiple different activities in parallel +val (result1, result2) = coroutineScope { + val d1 = async { KWorkflow.executeActivity(Activities::operation1, options, arg1) } + val d2 = async { KWorkflow.executeActivity(Activities::operation2, options, arg2) } + awaitAll(d1, d2) +} ``` +> **Note:** We use standard Kotlin `async` instead of a custom `startActivity` method. The workflow's deterministic dispatcher ensures correct replay behavior. + **Java Activity Interoperability:** Method references work regardless of whether the activity is defined in Kotlin or Java: ```kotlin @@ -599,8 +635,8 @@ val results = listOf(handle1.await(), handle2.await()) val result: PaymentResult = KWorkflow.executeActivity( JavaPaymentActivities::processPayment, - orderId, amount, - ActivityOptions { startToCloseTimeout = 2.minutes } + ActivityOptions { startToCloseTimeout = 2.minutes }, + orderId, amount ) ``` @@ -618,31 +654,24 @@ object KWorkflow { // 1 argument suspend fun executeActivity( activity: KFunction2, // T::method with 1 arg - arg1: A1, - options: ActivityOptions + options: ActivityOptions, + arg1: A1 ): R // 2 arguments suspend fun executeActivity( activity: KFunction3, // T::method with 2 args - arg1: A1, arg2: A2, - options: ActivityOptions + options: ActivityOptions, + arg1: A1, arg2: A2 ): R // ... up to 6 arguments - // Start activity (returns handle for async) - fun startActivity( - activity: KFunction3, - arg1: A1, arg2: A2, - options: ActivityOptions - ): KActivityHandle - // Local activity - same pattern suspend fun executeLocalActivity( activity: KFunction2, - arg1: A1, - options: LocalActivityOptions + options: LocalActivityOptions, + arg1: A1 ): R // --- String-based overloads (untyped) --- @@ -650,30 +679,27 @@ object KWorkflow { // Execute activity by name suspend inline fun executeActivity( activityName: String, - vararg args: Any?, - options: ActivityOptions + options: ActivityOptions, + vararg args: Any? ): R - // Start activity by name (async) - inline fun startActivity( - activityName: String, - vararg args: Any?, - options: ActivityOptions - ): KActivityHandle - // Execute local activity by name suspend inline fun executeLocalActivity( activityName: String, - vararg args: Any?, - options: LocalActivityOptions + options: LocalActivityOptions, + vararg args: Any? ): R } +``` -// Activity handle for async execution -interface KActivityHandle { - suspend fun await(): R - fun cancel() - val isCompleted: Boolean +**Parallel Execution:** Use standard `coroutineScope { async { } }` instead of custom handle types: + +```kotlin +// Standard Kotlin pattern for parallel execution +val results = coroutineScope { + val d1 = async { KWorkflow.executeActivity("add", options, 1, 2) } + val d2 = async { KWorkflow.executeActivity("add", options, 3, 4) } + awaitAll(d1, d2) // Returns standard Deferred instances } ``` @@ -717,8 +743,8 @@ class GreetingActivitiesImpl( // In workflow - direct method reference, no stub needed val greeting = KWorkflow.executeActivity( GreetingActivities::composeGreeting, - "Hello", "World", - ActivityOptions { startToCloseTimeout = 30.seconds } + ActivityOptions { startToCloseTimeout = 30.seconds }, + "Hello", "World" ) ``` @@ -764,8 +790,8 @@ class OrderActivitiesImpl( // In workflow - use the non-suspend interface for method references val isValid = KWorkflow.executeActivity( OrderActivities::validateOrder, - order, - ActivityOptions { startToCloseTimeout = 10.seconds } + ActivityOptions { startToCloseTimeout = 10.seconds }, + order ) ``` @@ -801,18 +827,18 @@ interface ValidationActivities { val isValid = KWorkflow.executeLocalActivity( ValidationActivities::validate, - input, LocalActivityOptions { startToCloseTimeout = 5.seconds - } + }, + input ) val sanitized = KWorkflow.executeLocalActivity( ValidationActivities::sanitize, - input, LocalActivityOptions { startToCloseTimeout = 1.seconds - } + }, + input ) ``` @@ -820,14 +846,21 @@ val sanitized = KWorkflow.executeLocalActivity( ## Client API +> **Note:** Many client extensions already exist in the `temporal-kotlin` module, including: +> - `WorkflowClient { }` DSL constructor +> - `WorkflowClient.newWorkflowStub { }` with reified generics +> - `WorkflowStub.getResult()` with reified generics +> - `WorkflowOptions { }`, `WorkflowClientOptions { }` DSL builders + ### Creating a Client ```kotlin val service = WorkflowServiceStubs.newLocalServiceStubs() +// Uses existing WorkflowClient DSL extension val client = WorkflowClient(service) { - namespace = "default" - dataConverter = myConverter + setNamespace("default") + setDataConverter(myConverter) } ``` @@ -1249,22 +1282,24 @@ val client = WorkflowClient(service) { |----------|------------| | **Activities** | | | `Workflow.newUntypedActivityStub(opts)` | *(not needed - options passed per call)* | -| `activities.execute("name", Cls, arg)` | `KWorkflow.executeActivity("name", arg, options)` | -| `stub.method(arg)` (typed activity) | `KWorkflow.executeActivity(Interface::method, arg, options)` | -| `Async.function(stub::method, arg)` | `KWorkflow.startActivity(Interface::method, arg, options)` | +| `activities.execute("name", Cls, arg)` | `KWorkflow.executeActivity("name", options, arg)` | +| `stub.method(arg)` (typed activity) | `KWorkflow.executeActivity(Interface::method, options, arg)` | +| `Async.function(stub::method, arg)` | `coroutineScope { async { KWorkflow.executeActivity(...) } }` | | **Workflows** | | -| `Workflow.newChildWorkflowStub(...)` | `KWorkflow.executeChildWorkflow(Interface::method, ...)` | +| `Workflow.newChildWorkflowStub(...)` | `KWorkflow.executeChildWorkflow(Interface::method, options, ...)` | | `client.newWorkflowStub(...)` | `client.startWorkflow(Interface::method, ...)` → `KTypedWorkflowHandle` | | `client.newWorkflowStub(Cls, id)` | `client.getKWorkflowHandle(id)` → `KWorkflowHandle` | | `stub.signal(arg)` | `handle.signal(T::method, arg)` | | `stub.query()` | `handle.query(T::method)` | | `handle.getResult()` | `handle.result()` (type inferred) or `handle.result()` | | **Primitives** | | -| `Workflow.sleep(duration)` | `delay(duration)` | -| `Workflow.await(() -> cond)` | `KWorkflow.condition { cond }` | -| `Promise` | `Deferred` / `KActivityHandle` | +| `Workflow.sleep(duration)` | `delay(duration)` - standard kotlinx.coroutines | +| `Workflow.await(() -> cond)` | `KWorkflow.awaitCondition { cond }` | +| `Promise` | Standard `Deferred` via `Promise.toDeferred()` | | `Optional` | `T?` | | `Duration.ofSeconds(30)` | `30.seconds` | +| **Parallel Execution** | | +| `Async.function(...)` + `Promise.get()` | `coroutineScope { async { ... } }` + `awaitAll()` | ### Before (Java) @@ -1301,8 +1336,8 @@ class GreetingWorkflowImpl : GreetingWorkflow { override suspend fun getGreeting(name: String): String { return KWorkflow.executeActivity( "greet", - name, - ActivityOptions { startToCloseTimeout = 30.seconds } + ActivityOptions { startToCloseTimeout = 30.seconds }, + name ) } } @@ -1317,8 +1352,8 @@ override suspend fun processOrder(order: Order): String { // JavaActivities is a Java @ActivityInterface - no stub needed return KWorkflow.executeActivity( JavaActivities::process, - order, - ActivityOptions { startToCloseTimeout = 30.seconds } + ActivityOptions { startToCloseTimeout = 30.seconds }, + order ) } ``` @@ -1490,29 +1525,29 @@ class OrderWorkflowImpl : OrderWorkflow { _progress = 10 val isValid = KWorkflow.executeActivity( OrderActivities::validateOrder, - order, - ActivityOptions { startToCloseTimeout = 10.seconds } + ActivityOptions { startToCloseTimeout = 10.seconds }, + order ) if (!isValid) { return@coroutineScope OrderResult(success = false, trackingNumber = null) } - // Reserve inventory for all items in parallel + // Reserve inventory for all items in parallel using standard async // If workflow is cancelled here, all parallel activities are cancelled _progress = 30 order.items.map { item -> async { val reserved = KWorkflow.executeActivity( OrderActivities::reserveInventory, - item, - defaultOptions + defaultOptions, + item ) if (reserved) { reservedItems.add(item) // Track for cleanup } reserved } - }.awaitAll() + }.awaitAll() // Standard kotlinx.coroutines.awaitAll _progress = 60 @@ -1520,14 +1555,14 @@ class OrderWorkflowImpl : OrderWorkflow { val charged = withTimeout(2.minutes) { KWorkflow.executeActivity( OrderActivities::chargePayment, - order, ActivityOptions { startToCloseTimeout = 2.minutes - retryOptions = RetryOptions { + retryOptions { initialInterval = 5.seconds maximumAttempts = 5 } - } + }, + order ) } if (!charged) { @@ -1540,8 +1575,8 @@ class OrderWorkflowImpl : OrderWorkflow { // Ship order val trackingNumber = KWorkflow.executeActivity( OrderActivities::shipOrder, - order, - defaultOptions + defaultOptions, + order ) _status = OrderStatus.SHIPPED @@ -1560,8 +1595,8 @@ class OrderWorkflowImpl : OrderWorkflow { try { KWorkflow.executeActivity( OrderActivities::releaseInventory, - item, - defaultOptions + defaultOptions, + item ) } catch (e: Exception) { // Log but continue cleanup @@ -1573,8 +1608,8 @@ class OrderWorkflowImpl : OrderWorkflow { try { KWorkflow.executeActivity( OrderActivities::refundPayment, - order, - defaultOptions + defaultOptions, + order ) } catch (e: Exception) { // Log but continue cleanup @@ -1585,8 +1620,8 @@ class OrderWorkflowImpl : OrderWorkflow { try { KWorkflow.executeActivity( OrderActivities::notifyCustomer, - order.customerId, "Your order has been cancelled", - defaultOptions + defaultOptions, + order.customerId, "Your order has been cancelled" ) } catch (e: Exception) { // Best effort notification diff --git a/kotlin/sdk-implementation.md b/kotlin/sdk-implementation.md index 90a2895..0692b89 100644 --- a/kotlin/sdk-implementation.md +++ b/kotlin/sdk-implementation.md @@ -84,7 +84,7 @@ The `temporal-kotlin` module already provides Kotlin extensions for the Java SDK | **Core Workflow** | | | `KWorkflow` | Entry point for workflow APIs (like `Workflow` in Java) | | `KotlinWorkflowContext` | Internal workflow execution context | -| `KotlinCoroutineDispatcher` | Deterministic coroutine dispatcher | +| `KotlinCoroutineDispatcher` | Deterministic coroutine dispatcher with `Delay` implementation | | `KWorkerInterceptor` | Interceptor interface with suspend functions | | **Factory/Registration** | | | `KotlinPlugin` | Plugin for enabling coroutine support and registering interceptors | @@ -99,10 +99,12 @@ The `temporal-kotlin` module already provides Kotlin extensions for the Java SDK | `WorkflowHandle` | Untyped workflow handle (string-based signals/queries) | | `KWorkflowHandle` | Typed workflow handle for signals/queries/updates | | `KTypedWorkflowHandle` | Extends KWorkflowHandle with typed result (returned by startWorkflow) | -| `KActivityHandle` | Handle for async activity execution | | `KUpdateHandle` | Handle for async update execution | | **Extensions** | | | `DurationExt.kt` | Conversions between `kotlin.time.Duration` and `java.time.Duration` | +| `PromiseExt.kt` | `Promise.toDeferred()` to bridge Java SDK to standard coroutines | + +> **Note:** We deliberately **do not** have `KActivityHandle` or `KChildWorkflowHandle`. Instead, users use standard `coroutineScope { async { } }` with `Deferred` for parallel execution. This follows the design principle of using idiomatic Kotlin patterns instead of custom APIs. ## Unified Worker Architecture @@ -324,13 +326,13 @@ suspend fun condition(condition: () -> Boolean) { * * @return true if condition became true, false if timed out */ -suspend fun condition(timeout: Duration, condition: () -> Boolean): Boolean { +suspend fun awaitCondition(timeout: Duration, condition: () -> Boolean): Boolean { return Async.await(timeout.toJava()) { condition() }.await() } ``` **Implementation notes:** -- **Prerequisite:** `Async.await()` does not currently exist in the Java SDK and must be added before `KWorkflow.condition()` can be implemented. +- **Prerequisite:** `Async.await()` does not currently exist in the Java SDK and must be added before `KWorkflow.awaitCondition()` can be implemented. - Under the hood, this uses `Async.await()` which returns a `Promise` that completes when the condition becomes true. The condition is re-evaluated after each workflow event (signals, activity completions, timers, etc.). - The async version should integrate with the same condition-tracking mechanism used by the blocking `Workflow.await()`, completing the Promise when the condition becomes true or timeout expires. @@ -423,17 +425,18 @@ fun Worker.registerKotlinWorkflowImplementationTypes(vararg types: KClass<*>) { ## KotlinCoroutineDispatcher -The custom dispatcher ensures deterministic execution of coroutines: +The custom dispatcher ensures deterministic execution of coroutines and implements the `Delay` interface to intercept standard `kotlinx.coroutines.delay()` calls: -* Executes coroutines in a controlled, deterministic order +* Executes coroutines in a controlled, deterministic order (FIFO queue) * Integrates with Temporal's replay mechanism -* Supports `delay()` by mapping to Temporal timers +* **Implements `Delay` interface** - standard `delay()` is automatically routed through Temporal timers * Handles cancellation scopes properly +* All coroutines launched with this dispatcher inherit deterministic behavior ```kotlin internal class KotlinCoroutineDispatcher( private val workflowContext: KotlinWorkflowContext -) : CoroutineDispatcher() { +) : CoroutineDispatcher(), Delay { private val readyQueue = ArrayDeque() private var inEventLoop = false @@ -449,6 +452,19 @@ internal class KotlinCoroutineDispatcher( } } + /** + * Intercept standard delay() calls and route through Temporal timer. + * This allows users to write `delay(5.seconds)` and have it work deterministically. + */ + override fun scheduleResumeAfterDelay( + timeMillis: Long, + continuation: CancellableContinuation + ) { + workflowContext.scheduleTimer(timeMillis) { + continuation.resume(Unit) + } + } + /** * Process queued coroutines deterministically. * Called by the replay machinery during workflow task processing. @@ -477,27 +493,22 @@ internal class KotlinCoroutineDispatcher( ### Delay Implementation -The `delay()` function is intercepted to create Temporal timers: +The `Delay` interface implementation is integrated directly into `KotlinCoroutineDispatcher` (shown above). This allows standard `kotlinx.coroutines.delay()` to work deterministically: ```kotlin -internal class KotlinDelay : Delay { - override fun scheduleResumeAfterDelay( - timeMillis: Long, - continuation: CancellableContinuation - ) { - val timer = workflowContext.createTimer(Duration.ofMillis(timeMillis)) - - timer.thenAccept { - continuation.resume(Unit) - } +// Users write standard Kotlin: +delay(5.seconds) - continuation.invokeOnCancellation { - timer.cancel() - } - } -} +// The dispatcher's scheduleResumeAfterDelay is called automatically, +// which routes through Temporal's deterministic timer mechanism ``` +**Why this works:** +- When a coroutine calls `delay()`, kotlinx.coroutines checks if the dispatcher implements `Delay` +- If it does, `scheduleResumeAfterDelay` is called instead of blocking +- Our implementation schedules a Temporal timer that resumes the continuation when fired +- This is completely transparent to user code - no custom `KWorkflow.delay()` needed + ## KotlinReplayWorkflow Implements the `ReplayWorkflow` interface using coroutines: @@ -608,28 +619,17 @@ object KWorkflow { /** * Execute an activity by name with reified return type. * - * Usage: KWorkflow.executeActivity("activityName", arg1, arg2, options) + * Usage: KWorkflow.executeActivity("activityName", options, arg1, arg2) * * The `inline` + `reified` combination allows access to R::class.java at runtime. * At each call site, the compiler substitutes the actual type. */ inline suspend fun executeActivity( activityName: String, - vararg args: Any?, - options: ActivityOptions + options: ActivityOptions, + vararg args: Any? ): R { - return executeActivityInternal(activityName, R::class.java, args, options) as R - } - - /** - * Start an activity by name (async). - */ - inline fun startActivity( - activityName: String, - vararg args: Any?, - options: ActivityOptions - ): KActivityHandle { - return startActivityInternal(activityName, R::class.java, args, options) + return executeActivityInternal(activityName, R::class.java, options, args) as R } /** @@ -639,40 +639,41 @@ object KWorkflow { internal suspend fun executeActivityInternal( activityName: String, resultType: Class<*>, - args: Array, - options: ActivityOptions + options: ActivityOptions, + args: Array ): Any? { val context = currentContext() val future = context.executeActivity(activityName, resultType, options, args) return future.await() // Suspends until activity completes } +} +``` - @PublishedApi - internal fun startActivityInternal( - activityName: String, - resultType: Class<*>, - args: Array, - options: ActivityOptions - ): KActivityHandle { - val context = currentContext() - val future = context.executeActivity(activityName, resultType, options, args) - return KActivityHandleImpl(future) - } +**Parallel Execution:** Instead of a custom `startActivity` method returning a handle, users use standard Kotlin patterns: + +```kotlin +// Parallel activities using standard coroutineScope { async { } } +val results = coroutineScope { + val d1 = async { KWorkflow.executeActivity("add", options, 1, 2) } + val d2 = async { KWorkflow.executeActivity("add", options, 3, 4) } + awaitAll(d1, d2) // Standard Deferred instances } ``` +This approach uses standard `Deferred` instead of a custom `KActivityHandle`, following the design principle of using idiomatic Kotlin patterns. + **Why `inline` + `reified`?** Kotlin generics are erased at runtime (like Java). Without `reified`, we cannot access `R::class.java`: ```kotlin // Does NOT work - R is erased at runtime -suspend fun executeActivity(activityName: String, vararg args: Any?, options: ActivityOptions): R { +suspend fun executeActivity(activityName: String, options: ActivityOptions, vararg args: Any?): R { val resultType = R::class.java // Compile error: Cannot use 'R' as reified type parameter } // WORKS - inline + reified captures type at compile time -inline suspend fun executeActivity(activityName: String, vararg args: Any?, options: ActivityOptions): R { +inline suspend fun executeActivity(activityName: String, options: ActivityOptions, vararg args: Any?): R { val resultType = R::class.java // OK: compiler substitutes actual type at call site } ``` @@ -681,10 +682,10 @@ inline suspend fun executeActivity(activityName: String, vararg args ```kotlin // User writes: -val result = KWorkflow.executeActivity("greet", name, options) +val result = KWorkflow.executeActivity("greet", options, name) // Compiler inlines to (conceptually): -val result = KWorkflow.executeActivityInternal("greet", String::class.java, arrayOf(name), options) as String +val result = KWorkflow.executeActivityInternal("greet", String::class.java, options, arrayOf(name)) as String ``` **Notes:** @@ -711,7 +712,7 @@ inline suspend fun executeActivity(...): R { ```kotlin // Works fine for simple types -executeActivity("greet", name, options) // R::class.java = String::class.java +executeActivity("greet", options, name) // R::class.java = String::class.java // Loses type parameter for generic types executeActivity>("getNames", options) // R::class.java = List::class.java (loses String) @@ -724,13 +725,13 @@ Kotlin's `typeOf()` (introduced in Kotlin 1.6) preserves full generic type in ```kotlin inline suspend fun executeActivity( activityName: String, - vararg args: Any?, - options: ActivityOptions + options: ActivityOptions, + vararg args: Any? ): R { val kType: KType = typeOf() // Preserves full generic info val javaType = kType.javaType // Converts to java.lang.reflect.Type - return executeActivityInternal(activityName, javaType, args, options) as R + return executeActivityInternal(activityName, javaType, options, args) as R } // Internal method accepts java.lang.reflect.Type (handles ParameterizedType) @@ -738,8 +739,8 @@ inline suspend fun executeActivity( internal suspend fun executeActivityInternal( activityName: String, resultType: java.lang.reflect.Type, // Can be Class or ParameterizedType - args: Array, - options: ActivityOptions + options: ActivityOptions, + args: Array ): Any? { val context = currentContext() val future = context.executeActivity(activityName, resultType, options, args) @@ -787,8 +788,8 @@ object KWorkflow { */ suspend fun executeActivity( activity: KFunction2, - arg1: A1, - options: ActivityOptions + options: ActivityOptions, + arg1: A1 ): R { val activityName = extractActivityName(activity) val resultType = extractReturnType(activity) @@ -830,8 +831,8 @@ object KWorkflow { // User writes: val result = KWorkflow.executeActivity( GreetingActivities::composeGreeting, // KFunction2 - name, - options + options, + name ) // At runtime: diff --git a/kotlin/sdk-proposal.md b/kotlin/sdk-proposal.md index 56aee02..22aed28 100644 --- a/kotlin/sdk-proposal.md +++ b/kotlin/sdk-proposal.md @@ -2,14 +2,28 @@ The Kotlin SDK proposal is split into two documents: +## Design Principle + +**Use idiomatic Kotlin language patterns wherever possible instead of custom APIs.** + +The Kotlin SDK should feel natural to Kotlin developers by leveraging standard `kotlinx.coroutines` primitives. Custom APIs should only be introduced when Temporal-specific semantics cannot be achieved through standard patterns. + +| Pattern | Standard Kotlin | Temporal Integration | +|---------|-----------------|----------------------| +| Parallel execution | `coroutineScope { async { ... } }` | Works via deterministic dispatcher | +| Await multiple | `awaitAll(d1, d2)` | Standard kotlinx.coroutines | +| Sleep/delay | `delay(duration)` | Intercepted via `Delay` interface | +| Deferred results | `Deferred` | Standard + `Promise.toDeferred()` | + ## [SDK API](./sdk-api.md) Public API and developer experience documentation including: -- Kotlin idioms (Duration, null safety, coroutines) +- Design principle: idiomatic Kotlin patterns +- Kotlin idioms (Duration, null safety, standard coroutines) - Workflow definition with suspend functions -- Activity definition and stubs -- Client API +- Activity definition (no stubs - options per call) +- Client API (leverages existing DSL extensions) - Worker API - Data conversion - Interceptors @@ -23,19 +37,20 @@ Internal architecture and implementation details including: - Relationship to Java SDK - Repository and package strategy -- Existing and new classes +- Existing DSL extensions (ActivityOptions, WorkflowOptions, etc.) +- New classes (KWorkflow, KotlinCoroutineDispatcher, etc.) - Unified worker architecture - Java SDK refactoring for pluggability - `WorkflowImplementationFactory` interface -- `KotlinCoroutineDispatcher` implementation +- `KotlinCoroutineDispatcher` with `Delay` implementation - `KotlinReplayWorkflow` implementation - Decision justifications - Open questions ## Phases -* **Phase 1** - Coroutine-based workflows, untyped activity stubs, pluggable WorkflowImplementationFactory, core Kotlin idioms (Duration, null safety) -* **Phase 2** - Typed activity stubs with suspend functions, signals/queries/updates, child workflows, property queries +* **Phase 1** - Coroutine-based workflows, untyped activity execution, pluggable WorkflowImplementationFactory, core Kotlin idioms (Duration, null safety, standard delay/async) +* **Phase 2** - Typed activity execution with method references, signals/queries/updates, child workflows, property queries * **Phase 3** - Testing framework > **Note:** Nexus support is a separate project and will be addressed independently. From 2153e4cf63f5e3ec05f1e455b7018cc3b78c9f17 Mon Sep 17 00:00:00 2001 From: Maxim Fateev Date: Tue, 30 Dec 2025 22:38:20 -0800 Subject: [PATCH 05/83] Kotlin SDK: Fix client API parameter order (options before args) --- kotlin/sdk-api.md | 32 ++++++++++++++++---------------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/kotlin/sdk-api.md b/kotlin/sdk-api.md index 7335bad..806bbef 100644 --- a/kotlin/sdk-api.md +++ b/kotlin/sdk-api.md @@ -181,11 +181,11 @@ class GreetingWorkflowImpl : GreetingWorkflow { // Client call - same pattern as activities, no stub needed val result = client.executeWorkflow( GreetingWorkflow::getGreeting, - "Temporal", WorkflowOptions { workflowId = "greeting-123" taskQueue = "greetings" - } + }, + "Temporal" ) ``` @@ -230,11 +230,11 @@ class OrderWorkflowImpl : OrderWorkflowSuspend { // Client call - same pattern, blocking for Option B val result = client.executeWorkflow( OrderWorkflow::processOrder, - order, WorkflowOptions { workflowId = "order-123" taskQueue = "orders" - } + }, + order ) ``` @@ -870,22 +870,22 @@ val client = WorkflowClient(service) { // Execute workflow and wait for result - no stub needed val result = client.executeWorkflow( GreetingWorkflow::getGreeting, - "Temporal", WorkflowOptions { workflowId = "greeting-123" taskQueue = "greeting-queue" workflowExecutionTimeout = 1.hours - } + }, + "Temporal" ) // Or start async and get handle val handle = client.startWorkflow( GreetingWorkflow::getGreeting, - "Temporal", WorkflowOptions { workflowId = "greeting-123" taskQueue = "greeting-queue" - } + }, + "Temporal" ) val result = handle.result() // Type inferred as String from method reference ``` @@ -1033,15 +1033,15 @@ interface KUpdateHandle { // startWorkflow captures result type from method reference suspend fun startWorkflow( workflow: KFunction2, // R is captured here - arg: A1, - options: WorkflowOptions + options: WorkflowOptions, + arg: A1 ): KTypedWorkflowHandle // R is preserved in return type // Usage - result type is inferred val handle = client.startWorkflow( OrderWorkflow::processOrder, // KFunction2 - order, - options + options, + order ) val result: OrderResult = handle.result() // No type parameter needed! @@ -1363,8 +1363,8 @@ Java workflows can be invoked from Kotlin clients: ```kotlin val result = client.executeWorkflow( JavaWorkflowInterface::execute, - input, - WorkflowOptions { taskQueue = "java-queue" } + WorkflowOptions { taskQueue = "java-queue" }, + input ) ``` @@ -1684,11 +1684,11 @@ fun main() = runBlocking { // Start workflow and get handle val handle = client.startWorkflow( OrderWorkflow::processOrder, - order, WorkflowOptions { workflowId = workflowId taskQueue = "orders" - } + }, + order ) println("Started workflow: ${handle.workflowId}") From 4a109924b2035a346d1066bab6e2083cc631ec6f Mon Sep 17 00:00:00 2001 From: Maxim Fateev Date: Tue, 30 Dec 2025 22:51:22 -0800 Subject: [PATCH 06/83] Kotlin SDK: Fix signalWithStart parameter order --- kotlin/sdk-api.md | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/kotlin/sdk-api.md b/kotlin/sdk-api.md index 806bbef..4345f88 100644 --- a/kotlin/sdk-api.md +++ b/kotlin/sdk-api.md @@ -899,15 +899,14 @@ Atomically start a workflow and send a signal. If the workflow already exists, o val handle = client.signalWithStart( // Workflow to start workflow = OrderWorkflow::processOrder, - workflowArg = order, - // Signal to send - signal = OrderWorkflow::cancelOrder, - signalArg = "Price changed", - // Options options = WorkflowOptions { workflowId = "order-123" taskQueue = "orders" - } + }, + workflowArg = order, + // Signal to send + signal = OrderWorkflow::cancelOrder, + signalArg = "Price changed" ) // Can use typed handle for queries/signals From a42eb800a1cc2982406de853c9f5910b48d3167e Mon Sep 17 00:00:00 2001 From: Maxim Fateev Date: Tue, 30 Dec 2025 22:53:09 -0800 Subject: [PATCH 07/83] Kotlin SDK: Fix client API parameter order in phase-2.4 --- kotlin/phases/phase-2.4-detailed.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/kotlin/phases/phase-2.4-detailed.md b/kotlin/phases/phase-2.4-detailed.md index ebf7b91..72132d0 100644 --- a/kotlin/phases/phase-2.4-detailed.md +++ b/kotlin/phases/phase-2.4-detailed.md @@ -107,7 +107,7 @@ Implement the Kotlin client API for starting workflows, getting handles, and int **Summary:** Add Kotlin extension for starting workflows with method references **Scope:** -- Add `WorkflowClient.startWorkflow(KFunction, args, options): KTypedWorkflowHandle` +- Add `WorkflowClient.startWorkflow(KFunction, options, args): KTypedWorkflowHandle` - Capture result type from method reference - Support KFunction1-7 overloads @@ -125,7 +125,7 @@ Implement the Kotlin client API for starting workflows, getting handles, and int **Summary:** Add extension for starting and waiting for workflow result **Scope:** -- Add `WorkflowClient.executeWorkflow(KFunction, args, options): R` +- Add `WorkflowClient.executeWorkflow(KFunction, options, args): R` - Start workflow and immediately await result - Support KFunction1-7 overloads @@ -161,7 +161,7 @@ Implement the Kotlin client API for starting workflows, getting handles, and int **Summary:** Add atomic signal-with-start operation **Scope:** -- Add `WorkflowClient.signalWithStart(workflow, workflowArg, signal, signalArg, options): KTypedWorkflowHandle` +- Add `WorkflowClient.signalWithStart(workflow, options, workflowArg, signal, signalArg): KTypedWorkflowHandle` - Atomically start or signal existing workflow - Return typed handle @@ -179,7 +179,7 @@ Implement the Kotlin client API for starting workflows, getting handles, and int **Summary:** Add atomic update-with-start operation **Scope:** -- Add `WorkflowClient.updateWithStart(workflow, workflowArg, update, updateArg, options): Pair` +- Add `WorkflowClient.updateWithStart(workflow, options, workflowArg, update, updateArg): Pair` - Atomically start or update existing workflow - Return handle and update result From 5b410e0a188af173b4ddab8f5bc136cec9f345f3 Mon Sep 17 00:00:00 2001 From: Maxim Fateev Date: Tue, 30 Dec 2025 22:55:05 -0800 Subject: [PATCH 08/83] Delete outdated docs --- kotlin/phases/phase-1.1-detailed.md | 119 ---- kotlin/phases/phase-1.2-detailed.md | 140 ---- kotlin/phases/phase-1.3-detailed.md | 164 ----- kotlin/phases/phase-1.4-detailed.md | 118 ---- .../phases/phase-1.5-plugin-architecture.md | 618 ------------------ kotlin/phases/phase-2.1-detailed.md | 137 ---- kotlin/phases/phase-2.2-detailed.md | 156 ----- kotlin/phases/phase-2.3-detailed.md | 154 ----- kotlin/phases/phase-2.4-detailed.md | 191 ------ kotlin/phases/phase-2.5-detailed.md | 138 ---- kotlin/phases/phase-3.1-detailed.md | 136 ---- kotlin/phases/phase-3.2-detailed.md | 170 ----- 12 files changed, 2241 deletions(-) delete mode 100644 kotlin/phases/phase-1.1-detailed.md delete mode 100644 kotlin/phases/phase-1.2-detailed.md delete mode 100644 kotlin/phases/phase-1.3-detailed.md delete mode 100644 kotlin/phases/phase-1.4-detailed.md delete mode 100644 kotlin/phases/phase-1.5-plugin-architecture.md delete mode 100644 kotlin/phases/phase-2.1-detailed.md delete mode 100644 kotlin/phases/phase-2.2-detailed.md delete mode 100644 kotlin/phases/phase-2.3-detailed.md delete mode 100644 kotlin/phases/phase-2.4-detailed.md delete mode 100644 kotlin/phases/phase-2.5-detailed.md delete mode 100644 kotlin/phases/phase-3.1-detailed.md delete mode 100644 kotlin/phases/phase-3.2-detailed.md diff --git a/kotlin/phases/phase-1.1-detailed.md b/kotlin/phases/phase-1.1-detailed.md deleted file mode 100644 index 3e41c56..0000000 --- a/kotlin/phases/phase-1.1-detailed.md +++ /dev/null @@ -1,119 +0,0 @@ -# Phase 1.1: Java SDK Refactoring - Detailed Plan - -## Overview - -Refactor the Java SDK to support pluggable workflow execution models. This enables the Kotlin module to provide coroutine-based workflow execution without modifying Java SDK internals. - -This phase must ensure full backwards compatibility for existing applications using Java SDK. - -## Prerequisites - -- None (this is the foundational phase) - -## Status - -- [x] Change 1: Add WorkflowImplementationFactory Interface -- [x] Change 2: Extract POJOWorkflowImplementationFactory -- [x] Change 3: Add Factory Registry to Worker -- [x] Change 4: Implement CompositeReplayWorkflowFactory -- [N/A] Change 5: ~~Expose ReplayWorkflow Interface for SPI~~ (Kotlin module accesses internal classes directly) -- [N/A] Change 6: ~~Expose ReplayWorkflowContext for SPI~~ (Kotlin module accesses internal classes directly) -- [Skip] Change 7: Add Async.await() Methods (implemented elsewhere) - -## Changes - -### Change 1: Add WorkflowImplementationFactory Interface ✓ - -**Summary:** Define the pluggable factory interface for creating workflow implementations - -**Scope:** -- Add `io.temporal.internal.worker.WorkflowImplementationFactory` interface (internal package - Kotlin module accesses directly) - -**Tests:** -- Compilation test for interface -- Javadoc validation - -**Dependencies:** None - ---- - -### Change 2: Extract POJOWorkflowImplementationFactory ✓ - -**Summary:** Refactor existing POJO workflow creation to implement the new interface - -**Scope:** -- Modify `POJOWorkflowImplementationFactory` to implement `WorkflowImplementationFactory` -- Extract workflow type registration logic -- No public API changes - -**Tests:** -- All existing workflow tests must pass -- Unit tests for factory method contracts - -**Dependencies:** Change 1 - ---- - -### Change 3: Add Factory Registry to Worker ✓ - -**Summary:** Update Worker to maintain a registry of workflow implementation factories - -**Scope:** -- Add `Worker.registerWorkflowImplementationFactory()` method -- Add internal factory registry -- Default factory remains `POJOWorkflowImplementationFactory` - -**Tests:** -- Test registering custom factory -- Test factory ordering/priority -- Existing tests unchanged - -**Dependencies:** Change 2 - ---- - -### Change 4: Implement CompositeReplayWorkflowFactory ✓ - -**Summary:** Create composite factory that delegates to registered factories - -**Scope:** -- Add `CompositeReplayWorkflowFactory` internal class -- Modify `SyncWorkflowWorker` to use composite factory -- Consult factories in registration order - -**Tests:** -- Test with multiple factories -- Test fallback to default factory -- Test workflow type routing - -**Dependencies:** Change 3 - ---- - -### Change 5: ~~Expose ReplayWorkflow Interface for SPI~~ [N/A] - -**Status:** Not applicable - Kotlin module accesses internal classes directly. No SPI package needed. - ---- - -### Change 6: ~~Expose ReplayWorkflowContext for SPI~~ [N/A] - -**Status:** Not applicable - Kotlin module accesses internal classes directly. No SPI package needed. - ---- - -### Change 7: Add Async.await() Methods - -**Summary:** Add Promise-returning await methods to Async class for coroutine support - -**Scope:** -- Add `Async.await(Supplier condition): Promise` -- Add `Async.await(Duration timeout, Supplier condition): Promise` -- Integrate with existing condition tracking mechanism - -**Tests:** -- Unit tests for Promise completion on condition -- Unit tests for timeout behavior -- Integration test with workflow execution - -**Dependencies:** Change 6 diff --git a/kotlin/phases/phase-1.2-detailed.md b/kotlin/phases/phase-1.2-detailed.md deleted file mode 100644 index 6a7e6e2..0000000 --- a/kotlin/phases/phase-1.2-detailed.md +++ /dev/null @@ -1,140 +0,0 @@ -# Phase 1.2: Kotlin Coroutine Runtime - Detailed Plan - -## Overview - -Implement the core coroutine-based workflow execution runtime in the temporal-kotlin module. This provides deterministic coroutine execution that integrates with Temporal's replay mechanism. - -## Prerequisites - -- Phase 1.1 complete (WorkflowImplementationFactory interface available) - -## Changes - -### Change 1: Add Coroutines Dependency and Base Infrastructure - -**Summary:** Add kotlinx-coroutines dependency and base internal classes - -**Scope:** -- Add `kotlinx-coroutines-core` dependency to temporal-kotlin -- Create `io.temporal.kotlin.internal` package -- Add internal marker annotations - -**Tests:** -- Verify dependency resolution -- Basic coroutine compilation test - -**Dependencies:** None - ---- - -### Change 2: Implement KotlinWorkflowContext - -**Summary:** Create internal context that wraps ReplayWorkflowContext for Kotlin workflows - -**Scope:** -- Add `KotlinWorkflowContext` internal class -- Wrap timer creation, activity execution, side effects -- Expose workflow info and current time - -**Tests:** -- Unit tests for context method delegation -- Test timer creation -- Test activity execution delegation - -**Dependencies:** Change 1 - ---- - -### Change 3: Implement KotlinCoroutineDispatcher - -**Summary:** Create deterministic coroutine dispatcher for workflow execution - -**Scope:** -- Add `KotlinCoroutineDispatcher` implementing `CoroutineDispatcher` -- Implement ready queue for deterministic execution -- Implement event loop processing -- Add deadlock detection - -**Tests:** -- Test deterministic execution order -- Test multiple coroutines queuing -- Test deadlock detection timeout - -**Dependencies:** Change 2 - ---- - -### Change 4: Implement KotlinDelay - -**Summary:** Create Delay implementation that maps to Temporal timers - -**Scope:** -- Add `KotlinDelay` implementing `kotlinx.coroutines.Delay` -- Map `delay()` calls to `KotlinWorkflowContext.createTimer()` -- Handle cancellation of timers - -**Tests:** -- Test delay creates Temporal timer -- Test delay cancellation -- Test delay completion resumes coroutine - -**Dependencies:** Change 3 - ---- - -### Change 5: Implement KotlinWorkflowDefinition - -**Summary:** Create metadata class for Kotlin workflow types - -**Scope:** -- Add `KotlinWorkflowDefinition` class -- Extract workflow type name from annotations -- Extract workflow method and parameter types -- Handle suspend function detection - -**Tests:** -- Test workflow type extraction -- Test suspend function detection -- Test parameter type extraction - -**Dependencies:** Change 4 - ---- - -### Change 6: Implement KotlinReplayWorkflow - -**Summary:** Create ReplayWorkflow implementation using coroutines - -**Scope:** -- Add `KotlinReplayWorkflow` implementing `ReplayWorkflow` -- Create coroutine scope with custom dispatcher -- Implement start(), eventLoop(), getOutput(), cancel(), close() -- Handle workflow completion and failure - -**Tests:** -- Test workflow start and completion -- Test workflow cancellation -- Test workflow failure handling -- Test event loop execution - -**Dependencies:** Change 5 - ---- - -### Change 7: Implement KotlinWorkflowImplementationFactory - -**Summary:** Create WorkflowImplementationFactory for Kotlin coroutine workflows - -**Scope:** -- Add `KotlinWorkflowImplementationFactory` implementing `WorkflowImplementationFactory` -- Implement `supportsType()` for suspend function detection -- Implement workflow registration and creation -- Wire up KotlinReplayWorkflow creation - -**Tests:** -- Test suspend function detection -- Test workflow registration -- Test workflow instance creation -- Integration test: full workflow execution - -**Dependencies:** Change 6 diff --git a/kotlin/phases/phase-1.3-detailed.md b/kotlin/phases/phase-1.3-detailed.md deleted file mode 100644 index e7e16ab..0000000 --- a/kotlin/phases/phase-1.3-detailed.md +++ /dev/null @@ -1,164 +0,0 @@ -# Phase 1.3: Core Kotlin APIs - Detailed Plan - -## Overview - -Implement the public Kotlin APIs for workflow development including the KWorkflow object, info wrappers, activity handles, and duration extensions. - -## Prerequisites - -- Phase 1.2 complete (Kotlin coroutine runtime available) - -## Changes - -### Change 1: Add Duration Extensions - -**Summary:** Add extension functions for Kotlin/Java Duration conversion - -**Scope:** -- Add `DurationExt.kt` in `io.temporal.kotlin` package -- Add `KotlinDuration.toJava()` extension -- Add `JavaDuration.toKotlin()` extension -- Add DSL property extensions for Options builders - -**Tests:** -- Test conversions in both directions -- Test edge cases (zero, max values) -- Test DSL usage in builders - -**Dependencies:** None - ---- - -### Change 2: Implement KWorkflowInfo - -**Summary:** Create Kotlin wrapper for WorkflowInfo with null safety - -**Scope:** -- Add `KWorkflowInfo` interface in `io.temporal.kotlin.workflow` -- Replace `Optional` with nullable types -- Add properties: workflowId, runId, parentWorkflowId?, parentRunId?, etc. -- Add internal implementation wrapping Java WorkflowInfo - -**Tests:** -- Test all property accessors -- Test nullable properties with and without values -- Test memo and search attribute access - -**Dependencies:** Change 1 - ---- - -### Change 3: Implement KActivityHandle - -**Summary:** Create handle type for async activity execution - -**Scope:** -- Add `KActivityHandle` interface -- Add `await(): R` suspend function -- Add `cancel()` method -- Add `isCompleted` property -- Add internal implementation wrapping CompletableFuture - -**Tests:** -- Test await completion -- Test cancellation -- Test isCompleted state transitions - -**Dependencies:** Change 2 - ---- - -### Change 4: Implement KWorkflow Object - Core Methods - -**Summary:** Create KWorkflow object with basic workflow utilities - -**Scope:** -- Add `KWorkflow` object in `io.temporal.kotlin.workflow` -- Add `getInfo(): KWorkflowInfo` -- Add `currentTime(): Instant` -- Add `randomUUID(): UUID` -- Add `getVersion()`, `sideEffect()`, `upsertSearchAttributes()` - -**Tests:** -- Test getInfo() returns correct values -- Test currentTime() during replay -- Test sideEffect determinism -- Test getVersion() marker recording - -**Dependencies:** Change 3 - ---- - -### Change 5: Implement KWorkflow.executeActivity (String-based) - -**Summary:** Add string-based activity execution to KWorkflow - -**Scope:** -- Add `executeActivity(name, options, vararg args): R` suspend function -- Add reified generic for return type extraction -- Wire to KotlinWorkflowContext.executeActivity() -- Add CompletableFuture.await() extension - -**Tests:** -- Test activity execution and result -- Test activity failure propagation -- Test activity timeout -- Test generic type handling - -**Dependencies:** Change 4 - ---- - -### Change 6: Implement KWorkflow.startActivity (String-based) - -**Summary:** Add async activity execution returning handle - -**Scope:** -- Add `startActivity(name, options, vararg args): KActivityHandle` -- Return handle immediately, execution in background -- Support parallel activity execution - -**Tests:** -- Test handle returned immediately -- Test parallel execution with multiple handles -- Test await on handle -- Test cancellation via handle - -**Dependencies:** Change 5 - ---- - -### Change 7: Implement KWorkflow.awaitCondition - -**Summary:** Add awaitCondition suspend function - -**Scope:** -- Add `awaitCondition(condition: () -> Boolean)` suspend function -- Add `awaitCondition(timeout: Duration, condition: () -> Boolean): Boolean` -- Wire to `Async.await()` Promise and suspend - -**Tests:** -- Test condition completion when true -- Test condition with timeout - success case -- Test condition with timeout - timeout case -- Test condition re-evaluation after events - -**Dependencies:** Change 6 - ---- - -### Change 8: Implement Local Activity Execution - -**Summary:** Add local activity execution methods - -**Scope:** -- Add `executeLocalActivity(name, options, vararg args): R` -- Add `startLocalActivity(name, options, vararg args): KActivityHandle` -- Wire to local activity execution in context - -**Tests:** -- Test local activity execution -- Test local activity options (timeout, retry) -- Test local activity failure - -**Dependencies:** Change 7 diff --git a/kotlin/phases/phase-1.4-detailed.md b/kotlin/phases/phase-1.4-detailed.md deleted file mode 100644 index a326256..0000000 --- a/kotlin/phases/phase-1.4-detailed.md +++ /dev/null @@ -1,118 +0,0 @@ -# Phase 1.4: Worker Integration - Detailed Plan - -## Overview - -Implement the worker integration layer that enables registration and execution of Kotlin coroutine workflows alongside Java workflows. - -## Prerequisites - -- Phase 1.3 complete (Core Kotlin APIs available) - -## Changes - -### Change 1: Implement KotlinPlugin Interface - -**Summary:** Define plugin interface for Kotlin coroutine support - -**Scope:** -- Add `KotlinPlugin` class in `io.temporal.kotlin.worker` -- Define configuration options (interceptors placeholder) -- Create factory for `KotlinWorkflowImplementationFactory` - -**Tests:** -- Test plugin instantiation -- Test configuration options -- Test factory creation - -**Dependencies:** None - ---- - -### Change 2: Add Plugin Support to WorkflowClient Extensions - -**Summary:** Extend WorkflowClient DSL to accept plugins - -**Scope:** -- Modify `WorkflowClientExt.kt` to support plugins parameter -- Store plugin reference for worker factory access -- Pass plugin configuration through client options - -**Tests:** -- Test client creation with plugin -- Test plugin accessible from client - -**Dependencies:** Change 1 - ---- - -### Change 3: Implement Suspend Function Detection - -**Summary:** Create utility for detecting suspend workflow methods - -**Scope:** -- Add `SuspendFunctionDetector` internal utility -- Check for `kotlin.coroutines.Continuation` parameter -- Validate workflow interface annotations - -**Tests:** -- Test detection of suspend functions -- Test detection of non-suspend functions -- Test mixed interface handling - -**Dependencies:** Change 2 - ---- - -### Change 4: Add Worker Extension for Kotlin Workflows - -**Summary:** Add extension function for registering Kotlin workflow types - -**Scope:** -- Add `Worker.registerWorkflowImplementationTypes(vararg KClass<*>)` extension -- Auto-detect suspend functions and route to Kotlin factory -- Fall back to Java factory for non-suspend workflows - -**Tests:** -- Test Kotlin workflow registration -- Test Java workflow registration unchanged -- Test mixed registration - -**Dependencies:** Change 3 - ---- - -### Change 5: Implement Automatic Factory Registration - -**Summary:** Automatically register KotlinWorkflowImplementationFactory when plugin is active - -**Scope:** -- Detect KotlinPlugin in WorkerFactory -- Auto-register `KotlinWorkflowImplementationFactory` with workers -- Ensure factory is registered before workflow types - -**Tests:** -- Test automatic factory registration -- Test factory precedence -- Test without plugin (Java-only behavior) - -**Dependencies:** Change 4 - ---- - -### Change 6: End-to-End Integration Test - -**Summary:** Create comprehensive integration test for Kotlin workflow execution - -**Scope:** -- Add integration test module/package -- Test full workflow: client → worker → workflow → activity → result -- Test workflow with delays, conditions, parallel activities -- Test alongside Java workflows on same worker - -**Tests:** -- E2E test: simple workflow -- E2E test: workflow with timer -- E2E test: workflow with parallel activities -- E2E test: mixed Java/Kotlin worker - -**Dependencies:** Change 5 diff --git a/kotlin/phases/phase-1.5-plugin-architecture.md b/kotlin/phases/phase-1.5-plugin-architecture.md deleted file mode 100644 index cb3c9d6..0000000 --- a/kotlin/phases/phase-1.5-plugin-architecture.md +++ /dev/null @@ -1,618 +0,0 @@ -# Phase 1.5: Plugin Architecture - Detailed Plan - -## Overview - -Implement a plugin architecture for the Java SDK that enables language-specific extensions like Kotlin coroutine support. This design intercepts workflow type registration and allows plugins to provide custom workflow implementation factories. - -## Motivation - -The current Phase 1.4 implementation provides worker-side Kotlin integration through extension functions: - -```kotlin -val plugin = KotlinPlugin { deadlockDetectionTimeout = 1500L } -worker.registerKotlinWorkflowImplementationTypes(plugin, MyWorkflowImpl::class) -``` - -This works but has limitations: -1. **Separate registration API** - Users must use Kotlin-specific extension functions -2. **No DataConverter propagation** - Plugin creates its own default DataConverter -3. **Not extensible** - Pattern doesn't generalize to other language integrations -4. **Mixed workflows require manual partitioning** - Users must know which workflows are suspend - -## Goals - -1. Create a plugin interface that generalizes to any language/framework integration -2. Enable automatic workflow type detection during standard `registerWorkflowImplementationTypes` calls -3. Propagate configuration (DataConverter) from client through to plugins -4. Maintain backward compatibility with existing extension function API - -## Non-Goals (Phase 1.5) - -1. Client-side plugins - Kotlin suspend workflows work with existing untyped client stubs -2. Activity plugins - Activity registration doesn't need special Kotlin handling -3. Complex plugin chaining/ordering - Keep initial implementation simple - -## Proposed Design - -### Core Concept: Registration Interception - -The key insight is that plugins should intercept workflow type registration rather than requiring a separate registration API: - -```kotlin -// User registers workflows with standard API -worker.registerWorkflowImplementationTypes( - MySuspendWorkflowImpl::class.java, // Plugin handles this - MyJavaWorkflowImpl::class.java // Default POJO factory handles this -) -``` - -When `registerWorkflowImplementationTypes` is called: -1. For each workflow class, ask plugins if they handle this type -2. First plugin to return a factory wins -3. If no plugin handles it, use the default POJO factory - -### Component 1: WorkerPlugin Interface (Java SDK) - -**Location:** `temporal-sdk/src/main/java/io/temporal/plugin/WorkerPlugin.java` - -```java -package io.temporal.plugin; - -import io.temporal.common.converter.DataConverter; -import io.temporal.internal.worker.WorkflowImplementationFactory; -import io.temporal.worker.Worker; -import io.temporal.worker.WorkerOptions; -import javax.annotation.Nullable; - -/** - * Plugin interface for extending Worker functionality. - * - * Plugins enable language-specific or framework-specific extensions to Temporal workers. - * They can: - * - Modify WorkerOptions before worker creation - * - Intercept workflow type registration and provide custom factories - * - Register activity implementations after worker creation - * - * Plugins are registered at the WorkerFactory level and are applied to all workers - * created by that factory. - * - * Example implementation: - *
{@code
- * public class MyLanguagePlugin implements WorkerPlugin {
- *     private MyWorkflowFactory factory;
- *
- *     @Override
- *     public WorkerOptions.Builder configureWorker(WorkerOptions.Builder builder) {
- *         return builder.setDefaultDeadlockDetectionTimeout(1500);
- *     }
- *
- *     @Override
- *     public WorkflowImplementationFactory getFactoryForType(
- *             Class clazz, DataConverter dataConverter) {
- *         if (!isMyLanguageWorkflow(clazz)) {
- *             return null; // Let default factory handle it
- *         }
- *         if (factory == null) {
- *             factory = new MyWorkflowFactory(dataConverter);
- *         }
- *         factory.registerWorkflowImplementationType(clazz);
- *         return factory;
- *     }
- * }
- * }
- * - * Usage with WorkerFactory: - *
{@code
- * WorkerFactory factory = WorkerFactory.newInstance(
- *     client,
- *     WorkerFactoryOptions.newBuilder()
- *         .addPlugin(new MyLanguagePlugin())
- *         .build()
- * );
- * Worker worker = factory.newWorker("task-queue");
- * // Plugin automatically handles workflow types during registration
- * worker.registerWorkflowImplementationTypes(
- *     MyLanguageWorkflow.class,  // Handled by plugin
- *     StandardJavaWorkflow.class  // Handled by default factory
- * );
- * }
- */ -public interface WorkerPlugin { - - /** - * Called before Worker is created to allow modification of worker options. - * - * @param builder the WorkerOptions builder to modify - * @return the modified builder (may be same instance or new) - */ - default WorkerOptions.Builder configureWorker(WorkerOptions.Builder builder) { - return builder; - } - - /** - * Called for each workflow implementation type being registered. - * - * If this plugin handles the workflow type, it should: - * 1. Register the type with its internal factory - * 2. Return the factory instance - * - * If this plugin does not handle the type, return null to let the next - * plugin or the default POJO factory handle it. - * - * @param workflowImplementationType the workflow implementation class being registered - * @param dataConverter the DataConverter from WorkflowClient options - * @return a factory that handles this workflow type, or null to delegate - */ - @Nullable - default WorkflowImplementationFactory getFactoryForType( - Class workflowImplementationType, DataConverter dataConverter) { - return null; - } - - /** - * Called after Worker is created to allow registration of activity implementations - * or other setup. - * - * Note: For workflow registration, prefer using getFactoryForType which integrates - * with the standard registerWorkflowImplementationTypes API. - * - * @param worker the created worker - * @param dataConverter the DataConverter from WorkflowClient options - */ - default void onWorkerCreated(Worker worker, DataConverter dataConverter) { - // Default no-op - } -} -``` - -**Key Design Points:** -- `getFactoryForType` is called for each workflow class during registration -- Plugins can lazily create and reuse a single factory instance -- The same factory can handle multiple workflow types -- Returning `null` means "I don't handle this type, try the next plugin" - -### Component 2: WorkerFactoryOptions Plugin Support - -**Location:** `temporal-sdk/src/main/java/io/temporal/worker/WorkerFactoryOptions.java` - -```java -// Add to existing WorkerFactoryOptions class: - -public final class WorkerFactoryOptions { - // ... existing fields ... - private final List plugins; - - // In Builder: - public static final class Builder { - private List plugins = new ArrayList<>(); - - /** - * Adds a plugin that will be applied to all workers created by this factory. - */ - public Builder addPlugin(WorkerPlugin plugin) { - this.plugins.add(Objects.requireNonNull(plugin)); - return this; - } - - /** - * Sets all plugins, replacing any previously added. - */ - public Builder setPlugins(List plugins) { - this.plugins = new ArrayList<>(plugins); - return this; - } - } - - public List getPlugins() { - return Collections.unmodifiableList(plugins); - } -} -``` - -### Component 3: Worker Registration Interception - -**Location:** `temporal-sdk/src/main/java/io/temporal/worker/Worker.java` - -The Worker class is modified to: -1. Store plugins and dataConverter passed from WorkerFactory -2. Intercept `registerWorkflowImplementationTypes` to call plugins - -```java -public final class Worker implements Suspendable { - // ... existing fields ... - private final List plugins; - private final DataConverter dataConverter; - private final List workflowImplementationFactories = new ArrayList<>(); - - // Updated constructor receives plugins and dataConverter - Worker( - WorkflowClient client, - String taskQueue, - WorkerFactoryOptions factoryOptions, - WorkerOptions options, - // ... other params ... - List plugins, - DataConverter dataConverter - ) { - // ... existing initialization ... - this.plugins = plugins; - this.dataConverter = dataConverter; - } - - /** - * Registers workflow implementation types. - * Plugins are consulted first for each type. - */ - public void registerWorkflowImplementationTypes( - WorkflowImplementationOptions options, - Class... workflowImplementationClasses) { - checkNotStarted(); - - for (Class clazz : workflowImplementationClasses) { - WorkflowImplementationFactory factory = null; - - // Ask plugins if they handle this type - for (WorkerPlugin plugin : plugins) { - factory = plugin.getFactoryForType(clazz, dataConverter); - if (factory != null) { - break; // First plugin to return a factory wins - } - } - - if (factory != null) { - // Plugin handles this type - track the factory - if (!workflowImplementationFactories.contains(factory)) { - workflowImplementationFactories.add(factory); - } - } else { - // No plugin handles it - use default POJO factory - workflowWorker.registerWorkflowImplementationTypes(options, clazz); - } - } - } - - // During worker start, register all plugin factories with the workflow worker - public void start() { - // ... existing code ... - for (WorkflowImplementationFactory factory : workflowImplementationFactories) { - workflowWorker.registerWorkflowImplementationFactory(factory); - } - // ... existing code ... - } -} -``` - -### Component 4: WorkerFactory Plugin Integration - -**Location:** `temporal-sdk/src/main/java/io/temporal/worker/WorkerFactory.java` - -```java -public synchronized Worker newWorker(String taskQueue, WorkerOptions options) { - // ... existing validation ... - - // Let plugins configure worker options - WorkerOptions.Builder optionsBuilder = WorkerOptions.newBuilder(options); - for (WorkerPlugin plugin : factoryOptions.getPlugins()) { - optionsBuilder = plugin.configureWorker(optionsBuilder); - } - WorkerOptions configuredOptions = optionsBuilder.build(); - - DataConverter dataConverter = workflowClient.getOptions().getDataConverter(); - - Worker worker = new Worker( - workflowClient, - taskQueue, - factoryOptions, - configuredOptions, - // ... other params ... - factoryOptions.getPlugins(), // Pass plugins to worker - dataConverter // Pass dataConverter to worker - ); - - workers.put(taskQueue, worker); - - // Notify plugins of worker creation (for activity registration etc.) - for (WorkerPlugin plugin : factoryOptions.getPlugins()) { - plugin.onWorkerCreated(worker, dataConverter); - } - - return worker; -} -``` - -### Component 5: KotlinPlugin Implementation - -**Location:** `temporal-kotlin/src/main/kotlin/io/temporal/kotlin/worker/KotlinPlugin.kt` - -```kotlin -package io.temporal.kotlin.worker - -import io.temporal.common.converter.DataConverter -import io.temporal.internal.worker.WorkflowImplementationFactory -import io.temporal.kotlin.TemporalDsl -import io.temporal.kotlin.internal.KotlinWorkflowDefinition -import io.temporal.kotlin.internal.KotlinWorkflowImplementationFactory -import io.temporal.plugin.WorkerPlugin -import io.temporal.worker.WorkerOptions - -/** - * Plugin for enabling Kotlin coroutine support in Temporal workflows. - * - * When registered with a WorkerFactory, this plugin automatically detects Kotlin - * suspend workflows during registration and routes them to the Kotlin coroutine - * execution runtime. Non-suspend workflows are handled by the default Java factory. - * - * Usage: - * ```kotlin - * val factory = WorkerFactory.newInstance( - * client, - * WorkerFactoryOptions.newBuilder() - * .addPlugin(KotlinPlugin { deadlockDetectionTimeout = 1500L }) - * .build() - * ) - * val worker = factory.newWorker("task-queue") - * - * // Suspend workflows auto-detected and routed to Kotlin factory - * // Non-suspend workflows use default Java factory - * worker.registerWorkflowImplementationTypes( - * MySuspendWorkflowImpl::class.java, - * MyJavaWorkflowImpl::class.java - * ) - * ``` - */ -public class KotlinPlugin private constructor( - private val options: KotlinPluginOptions -) : WorkerPlugin { - - /** Lazily created factory - shared across all workflow types handled by this plugin. */ - private var factory: KotlinWorkflowImplementationFactory? = null - - /** - * Configures worker options before worker creation. - * Sets deadlock detection timeout if configured. - */ - override fun configureWorker(builder: WorkerOptions.Builder): WorkerOptions.Builder { - if (options.configureDeadlockDetection) { - builder.setDefaultDeadlockDetectionTimeout(options.deadlockDetectionTimeout) - } - return builder - } - - /** - * Called for each workflow type during registration. - * - * If the workflow uses Kotlin suspend functions, this plugin handles it by: - * 1. Creating/reusing a KotlinWorkflowImplementationFactory - * 2. Registering the type with that factory - * 3. Returning the factory - * - * For non-suspend workflows, returns null to let the default POJO factory handle them. - */ - override fun getFactoryForType( - workflowImplementationType: Class<*>, - dataConverter: DataConverter - ): WorkflowImplementationFactory? { - // Check if this is a Kotlin suspend workflow - if (!KotlinWorkflowDefinition.isSuspendWorkflow(workflowImplementationType)) { - return null // Not a suspend workflow, let default factory handle it - } - - // Lazily create the factory - if (factory == null) { - factory = KotlinWorkflowImplementationFactory( - dataConverter = dataConverter, - deadlockDetectionTimeoutMs = options.deadlockDetectionTimeout - ) - } - - // Register the workflow type with our factory - factory!!.registerWorkflowImplementationType(workflowImplementationType) - return factory - } - - public val deadlockDetectionTimeout: Long - get() = options.deadlockDetectionTimeout - - public companion object { - @JvmStatic - public fun create(): KotlinPlugin = KotlinPlugin(KotlinPluginOptions()) - - @JvmStatic - public fun create(options: KotlinPluginOptions): KotlinPlugin = KotlinPlugin(options) - - @JvmStatic - public fun create(block: KotlinPluginOptions.Builder.() -> Unit): KotlinPlugin { - return KotlinPlugin(KotlinPluginOptions.Builder().apply(block).build()) - } - } -} - -/** - * Configuration options for the Kotlin plugin. - */ -public class KotlinPluginOptions( - public val deadlockDetectionTimeout: Long = DEFAULT_DEADLOCK_DETECTION_TIMEOUT, - public val configureDeadlockDetection: Boolean = true -) { - public companion object { - public const val DEFAULT_DEADLOCK_DETECTION_TIMEOUT: Long = 1000L - } - - @TemporalDsl - public class Builder { - public var deadlockDetectionTimeout: Long = DEFAULT_DEADLOCK_DETECTION_TIMEOUT - public var configureDeadlockDetection: Boolean = true - - public fun build(): KotlinPluginOptions = KotlinPluginOptions( - deadlockDetectionTimeout = deadlockDetectionTimeout, - configureDeadlockDetection = configureDeadlockDetection - ) - } -} - -// DSL functions for Kotlin-friendly construction -public fun KotlinPlugin( - options: @TemporalDsl KotlinPluginOptions.Builder.() -> Unit -): KotlinPlugin = KotlinPlugin.create(options) - -public fun KotlinPlugin(): KotlinPlugin = KotlinPlugin.create() -``` - -## Changes Summary - -### Change 1: Create WorkerPlugin Interface - -**Summary:** Define plugin interface with `getFactoryForType` method - -**Scope:** -- Create `io.temporal.plugin` package -- Add `WorkerPlugin` interface with `configureWorker`, `getFactoryForType`, and `onWorkerCreated` methods -- `getFactoryForType` returns a factory for workflow types the plugin handles - -**Tests:** -- Unit test for default method behavior -- Test plugin returning factory for specific types -- Test plugin returning null for unhandled types - ---- - -### Change 2: Add Plugin Support to WorkerFactoryOptions - -**Summary:** Enable plugin registration at factory level - -**Scope:** -- Add `plugins` field to `WorkerFactoryOptions` -- Add `addPlugin()` and `setPlugins()` builder methods -- Add `getPlugins()` accessor - -**Tests:** -- Test adding single plugin -- Test adding multiple plugins -- Test plugin list immutability - ---- - -### Change 3: Integrate Plugins in Worker Registration - -**Summary:** Intercept workflow registration to call plugins - -**Scope:** -- Add `plugins` and `dataConverter` fields to Worker -- Modify `registerWorkflowImplementationTypes` to call `plugin.getFactoryForType` for each class -- Track plugin-provided factories and register them at worker start -- Classes not handled by plugins use default POJO factory - -**Tests:** -- Test plugin factory is used for handled types -- Test default factory is used for unhandled types -- Test mixed workflow registration (some handled, some not) -- Test DataConverter is passed correctly - ---- - -### Change 4: Update WorkerFactory to Pass Plugins - -**Summary:** Wire plugins and DataConverter through to Worker - -**Scope:** -- Pass `factoryOptions.getPlugins()` to Worker constructor -- Pass `dataConverter` from client to Worker constructor -- Call `plugin.configureWorker()` before worker creation -- Call `plugin.onWorkerCreated()` after worker creation - ---- - -### Change 5: Simplify KotlinPlugin - -**Summary:** Implement `getFactoryForType` for automatic suspend workflow detection - -**Scope:** -- Implement `WorkerPlugin.getFactoryForType` -- Detect suspend workflows via `KotlinWorkflowDefinition.isSuspendWorkflow()` -- Create factory lazily, reuse for all suspend workflow types -- Return `null` for non-suspend workflows - -**Tests:** -- Test suspend workflow detection and routing -- Test non-suspend workflow delegation to default factory -- Test factory reuse across multiple workflow types -- Test DataConverter propagation to factory - -## Usage Examples - -### Standard Registration with Plugin (Recommended) - -```kotlin -// 1. Create factory with Kotlin plugin -val factory = WorkerFactory.newInstance( - client, - WorkerFactoryOptions.newBuilder() - .addPlugin(KotlinPlugin { deadlockDetectionTimeout = 1500L }) - .build() -) - -// 2. Create worker -val worker = factory.newWorker("my-task-queue") - -// 3. Register workflows - suspend workflows auto-detected -worker.registerWorkflowImplementationTypes( - MySuspendWorkflowImpl::class.java, // Plugin handles this - MyJavaWorkflowImpl::class.java // Default factory handles this -) - -// 4. Register activities -worker.registerActivitiesImplementations(MyActivityImpl()) - -// 5. Start factory -factory.start() -``` - -### With Custom DataConverter - -```kotlin -// DataConverter flows from client through to plugin automatically -val clientOptions = WorkflowClientOptions.newBuilder() - .setDataConverter(JacksonJsonDataConverter.newDefaultInstance()) - .build() -val client = WorkflowClient.newInstance(service, clientOptions) - -// Plugin receives the client's DataConverter in getFactoryForType -val factory = WorkerFactory.newInstance( - client, - WorkerFactoryOptions.newBuilder() - .addPlugin(KotlinPlugin()) - .build() -) -``` - -### Legacy Extension Functions (Still Supported) - -```kotlin -// These extension functions are deprecated but still work -val plugin = KotlinPlugin() -worker.registerKotlinWorkflowImplementationTypes( - plugin, - MyWorkflowImpl::class -) -``` - -## Backward Compatibility - -- Existing `Worker.registerKotlinWorkflowImplementationTypes()` extension functions are deprecated but continue to work -- Existing code without plugins works unchanged - default POJO factory handles all workflows -- Plugin-based approach is opt-in via `WorkerFactoryOptions.addPlugin()` - -## Advantages of This Design - -1. **Single registration API** - Users use standard `registerWorkflowImplementationTypes` for all workflows -2. **Automatic detection** - Suspend workflows are automatically routed to the Kotlin factory -3. **DataConverter propagation** - Plugins receive the client's DataConverter automatically -4. **Extensible** - Pattern generalizes to other language/framework integrations -5. **Composable** - Multiple plugins can coexist, each handling different workflow types - -## Future Considerations - -1. **Activity plugins** - Could extend `getFactoryForType` pattern to activities -2. **Plugin ordering/priority** - Could add explicit ordering for plugin consultation -3. **Plugin lifecycle** - Could add `onWorkerShutdown` hook -4. **Client-side plugins** - Could add interceptors for workflow stubs diff --git a/kotlin/phases/phase-2.1-detailed.md b/kotlin/phases/phase-2.1-detailed.md deleted file mode 100644 index 8950983..0000000 --- a/kotlin/phases/phase-2.1-detailed.md +++ /dev/null @@ -1,137 +0,0 @@ -# Phase 2.1: Typed Activity Stubs - Detailed Plan - -## Overview - -Implement type-safe activity execution using Kotlin method references. This provides compile-time type checking for activity arguments and return types without requiring stub creation. - -## Prerequisites - -- Phase 1 complete (Core infrastructure and string-based activities working) - -## Changes - -### Change 1: Add KFunction Metadata Extraction Utilities - -**Summary:** Create utilities for extracting activity metadata from KFunction references - -**Scope:** -- Add `ActivityMetadataExtractor` internal class -- Extract activity name from `@ActivityMethod` annotation or method name -- Extract return type (handling generic types with `typeOf`) -- Extract declaring interface class - -**Tests:** -- Test name extraction with annotation -- Test name extraction without annotation -- Test return type extraction (simple types) -- Test return type extraction (generic types) - -**Dependencies:** None - ---- - -### Change 2: Implement KWorkflow.executeActivity with KFunction1 - -**Summary:** Add typed activity execution for 0-argument activities - -**Scope:** -- Add `executeActivity(KFunction1, options): R` overload -- Extract metadata using utilities from Change 1 -- Delegate to string-based execution - -**Tests:** -- Test 0-arg activity execution -- Test type inference for return type -- Test activity not found error - -**Dependencies:** Change 1 - ---- - -### Change 3: Implement KWorkflow.executeActivity with KFunction2-4 - -**Summary:** Add typed activity execution for 1-3 argument activities - -**Scope:** -- Add `executeActivity(KFunction2, options, A1): R` -- Add `executeActivity(KFunction3, options, A1, A2): R` -- Add `executeActivity(KFunction4, options, A1, A2, A3): R` - -**Tests:** -- Test 1-arg activity with correct types -- Test 2-arg activity with correct types -- Test 3-arg activity with correct types -- Test compile-time type errors (manual verification) - -**Dependencies:** Change 2 - ---- - -### Change 4: Implement KWorkflow.executeActivity with KFunction5-7 - -**Summary:** Add typed activity execution for 4-6 argument activities - -**Scope:** -- Add overloads for KFunction5, KFunction6, KFunction7 -- Follow same pattern as Change 3 - -**Tests:** -- Test 4-arg activity -- Test 5-arg activity -- Test 6-arg activity - -**Dependencies:** Change 3 - ---- - -### Change 5: Implement KWorkflow.startActivity with KFunction Overloads - -**Summary:** Add async typed activity execution returning handles - -**Scope:** -- Add `startActivity(KFunction2, options, A1): KActivityHandle` -- Add overloads for KFunction1-7 -- Return handle for async await - -**Tests:** -- Test handle returned with correct type -- Test parallel typed activities -- Test await on typed handle - -**Dependencies:** Change 4 - ---- - -### Change 6: Implement Typed Local Activity Execution - -**Summary:** Add KFunction-based local activity execution - -**Scope:** -- Add `executeLocalActivity` overloads with KFunction1-7 -- Add `startLocalActivity` overloads with KFunction1-7 -- Wire to local activity execution - -**Tests:** -- Test typed local activity execution -- Test local activity options respected -- Test local activity return types - -**Dependencies:** Change 5 - ---- - -### Change 7: Add Java Activity Interface Support - -**Summary:** Ensure typed execution works with Java-defined activity interfaces - -**Scope:** -- Test and fix any issues with Java interface method references -- Handle Java Optional return types if present -- Document interop behavior - -**Tests:** -- Test calling Java activity interface from Kotlin workflow -- Test Java activity with various return types -- Integration test with Java activity implementation - -**Dependencies:** Change 6 diff --git a/kotlin/phases/phase-2.2-detailed.md b/kotlin/phases/phase-2.2-detailed.md deleted file mode 100644 index cff5882..0000000 --- a/kotlin/phases/phase-2.2-detailed.md +++ /dev/null @@ -1,156 +0,0 @@ -# Phase 2.2: Signals, Queries, Updates - Detailed Plan - -## Overview - -Implement support for workflow signals, queries, and updates with suspend function support for signal and update handlers. - -## Prerequisites - -- Phase 2.1 complete (Typed activity stubs available) - -## Changes - -### Change 1: Implement Signal Handler Registration - -**Summary:** Support @SignalMethod with suspend functions in Kotlin workflows - -**Scope:** -- Extend `KotlinWorkflowDefinition` to extract signal methods -- Support both suspend and non-suspend signal handlers -- Register handlers with `ReplayWorkflowContext` - -**Tests:** -- Test signal handler registration -- Test suspend signal handler -- Test non-suspend signal handler -- Test signal with arguments - -**Dependencies:** None - ---- - -### Change 2: Implement Signal Dispatch in KotlinReplayWorkflow - -**Summary:** Handle incoming signals in coroutine context - -**Scope:** -- Add signal dispatch to `KotlinReplayWorkflow` -- Launch signal handlers as child coroutines -- Handle signal handler exceptions - -**Tests:** -- Test signal delivery to handler -- Test multiple signals queued -- Test signal handler exception handling - -**Dependencies:** Change 1 - ---- - -### Change 3: Implement Query Handler Registration - -**Summary:** Support @QueryMethod including property syntax - -**Scope:** -- Extend `KotlinWorkflowDefinition` to extract query methods -- Support Kotlin property getters as queries -- Queries are always synchronous (never suspend) - -**Tests:** -- Test query method registration -- Test query property registration -- Test query with arguments -- Test query return value - -**Dependencies:** Change 2 - ---- - -### Change 4: Implement Query Dispatch - -**Summary:** Handle incoming queries in workflow - -**Scope:** -- Add query dispatch to `KotlinReplayWorkflow` -- Execute query synchronously (not in coroutine) -- Return serialized result - -**Tests:** -- Test query execution -- Test query during workflow execution -- Test query exception handling - -**Dependencies:** Change 3 - ---- - -### Change 5: Implement Update Handler Registration - -**Summary:** Support @UpdateMethod with suspend functions - -**Scope:** -- Extend `KotlinWorkflowDefinition` to extract update methods -- Support `@UpdateValidatorMethod` for validation -- Register handlers with context - -**Tests:** -- Test update handler registration -- Test update validator registration -- Test update with arguments - -**Dependencies:** Change 4 - ---- - -### Change 6: Implement Update Validation - -**Summary:** Execute update validators before update handlers - -**Scope:** -- Call validator synchronously before accepting update -- Validators are never suspend -- Reject update if validator throws - -**Tests:** -- Test validator called before handler -- Test validator rejection -- Test validator with arguments - -**Dependencies:** Change 5 - ---- - -### Change 7: Implement Update Dispatch - -**Summary:** Handle incoming updates in coroutine context - -**Scope:** -- Add update dispatch to `KotlinReplayWorkflow` -- Launch update handlers as coroutines -- Return update result to caller - -**Tests:** -- Test update execution -- Test update result returned -- Test update exception handling -- Test concurrent updates - -**Dependencies:** Change 6 - ---- - -### Change 8: Integration Tests for Signals/Queries/Updates - -**Summary:** End-to-end tests for all handler types - -**Scope:** -- Integration test combining signals, queries, updates -- Test interaction between handlers and main workflow -- Test state visibility in queries during updates - -**Tests:** -- E2E: signal triggers workflow progress -- E2E: query returns current state -- E2E: update modifies and returns state - -**Dependencies:** Change 7 diff --git a/kotlin/phases/phase-2.3-detailed.md b/kotlin/phases/phase-2.3-detailed.md deleted file mode 100644 index 3090fc1..0000000 --- a/kotlin/phases/phase-2.3-detailed.md +++ /dev/null @@ -1,154 +0,0 @@ -# Phase 2.3: Child Workflows - Detailed Plan - -## Overview - -Implement child workflow execution with both typed (method reference) and untyped (string-based) APIs. - -## Prerequisites - -- Phase 2.2 complete (Signals/Queries/Updates available) - -## Changes - -### Change 1: Add ChildWorkflowOptions DSL - -**Summary:** Create Kotlin DSL builder for ChildWorkflowOptions - -**Scope:** -- Add `ChildWorkflowOptions` DSL builder function -- Support Kotlin Duration for timeouts -- Include all options: workflowId, taskQueue, retryOptions, parentClosePolicy, etc. - -**Tests:** -- Test DSL builds correct options -- Test Duration conversion -- Test all option properties - -**Dependencies:** None - ---- - -### Change 2: Implement KChildWorkflowHandle - -**Summary:** Create handle type for child workflow execution - -**Scope:** -- Add `KChildWorkflowHandle` interface -- Add `await(): R` suspend function for result -- Add `signal()` methods for signaling child -- Add `workflowId` and `runId` properties - -**Tests:** -- Test await completion -- Test signal delivery -- Test handle properties - -**Dependencies:** Change 1 - ---- - -### Change 3: Implement KWorkflow.executeChildWorkflow (String-based) - -**Summary:** Add string-based child workflow execution - -**Scope:** -- Add `executeChildWorkflow(workflowType, options, vararg args): R` -- Wire to `KotlinWorkflowContext.executeChildWorkflow()` -- Handle result deserialization - -**Tests:** -- Test child workflow execution -- Test child workflow failure propagation -- Test child workflow cancellation - -**Dependencies:** Change 2 - ---- - -### Change 4: Implement KWorkflow.startChildWorkflow (String-based) - -**Summary:** Add async child workflow execution returning handle - -**Scope:** -- Add `startChildWorkflow(workflowType, options, vararg args): KChildWorkflowHandle` -- Return handle after child workflow started -- Support parallel child workflows - -**Tests:** -- Test handle returned after start -- Test parallel child workflows -- Test await on handle - -**Dependencies:** Change 3 - ---- - -### Change 5: Add Workflow Metadata Extraction Utilities - -**Summary:** Create utilities for extracting workflow metadata from KFunction - -**Scope:** -- Add `WorkflowMetadataExtractor` internal class -- Extract workflow type from `@WorkflowMethod` or method name -- Extract return type and parameter types - -**Tests:** -- Test type extraction with annotation -- Test type extraction without annotation -- Test return type handling - -**Dependencies:** Change 4 - ---- - -### Change 6: Implement KWorkflow.executeChildWorkflow (Typed) - -**Summary:** Add KFunction-based child workflow execution - -**Scope:** -- Add `executeChildWorkflow(KFunction2, options, A1): R` -- Add overloads for KFunction1-7 -- Extract metadata and delegate to string-based - -**Tests:** -- Test typed child workflow execution -- Test type inference -- Test with various argument counts - -**Dependencies:** Change 5 - ---- - -### Change 7: Implement KWorkflow.startChildWorkflow (Typed) - -**Summary:** Add KFunction-based async child workflow execution - -**Scope:** -- Add `startChildWorkflow(KFunction2, options, A1): KChildWorkflowHandle` -- Add overloads for KFunction1-7 -- Return typed handle - -**Tests:** -- Test typed handle -- Test parallel typed child workflows -- Test signal on typed handle - -**Dependencies:** Change 6 - ---- - -### Change 8: Implement Continue-As-New - -**Summary:** Add continue-as-new support for Kotlin workflows - -**Scope:** -- Add `KWorkflow.continueAsNew(vararg args)` function -- Add typed `KWorkflow.continueAsNew(KFunction, args)` overloads -- Throw `ContinueAsNewException` equivalent - -**Tests:** -- Test continue-as-new execution -- Test with different arguments -- Test typed continue-as-new - -**Dependencies:** Change 7 diff --git a/kotlin/phases/phase-2.4-detailed.md b/kotlin/phases/phase-2.4-detailed.md deleted file mode 100644 index 72132d0..0000000 --- a/kotlin/phases/phase-2.4-detailed.md +++ /dev/null @@ -1,191 +0,0 @@ -# Phase 2.4: Client API - Detailed Plan - -## Overview - -Implement the Kotlin client API for starting workflows, getting handles, and interacting with running workflows. - -## Prerequisites - -- Phase 2.3 complete (Child workflows available) - -## Changes - -### Change 1: Implement WorkflowHandle (Untyped) - -**Summary:** Create untyped workflow handle for string-based operations - -**Scope:** -- Add `WorkflowHandle` interface in `io.temporal.kotlin.client` -- Add `result()` suspend function -- Add `signal(name, vararg args)` method -- Add `query(name, vararg args)` method -- Add `cancel()`, `terminate()` methods - -**Tests:** -- Test result retrieval -- Test signal by name -- Test query by name -- Test cancel/terminate - -**Dependencies:** None - ---- - -### Change 2: Implement KWorkflowHandle (Typed) - -**Summary:** Create typed workflow handle for method reference operations - -**Scope:** -- Add `KWorkflowHandle` interface extending base operations -- Add `signal(KFunction, args)` overloads -- Add `query(KFunction): R` overloads -- Add `result()` requiring explicit type - -**Tests:** -- Test typed signal -- Test typed query -- Test property query syntax - -**Dependencies:** Change 1 - ---- - -### Change 3: Implement KTypedWorkflowHandle - -**Summary:** Create handle with captured result type from startWorkflow - -**Scope:** -- Add `KTypedWorkflowHandle` extending `KWorkflowHandle` -- Add `result(): R` with inferred type -- Returned by `startWorkflow` with method reference - -**Tests:** -- Test result type inference -- Test no explicit type needed - -**Dependencies:** Change 2 - ---- - -### Change 4: Implement KUpdateHandle - -**Summary:** Create handle for async update operations - -**Scope:** -- Add `KUpdateHandle` interface -- Add `updateId` property -- Add `result(): R` suspend function - -**Tests:** -- Test update result retrieval -- Test update ID access - -**Dependencies:** Change 3 - ---- - -### Change 5: Add Update Methods to KWorkflowHandle - -**Summary:** Add update execution methods to workflow handles - -**Scope:** -- Add `executeUpdate(KFunction, args): R` to `KWorkflowHandle` -- Add `startUpdate(KFunction, args): KUpdateHandle` -- Add `getKUpdateHandle(updateId)` for existing updates - -**Tests:** -- Test execute update and wait -- Test start update async -- Test get existing update handle - -**Dependencies:** Change 4 - ---- - -### Change 6: Implement WorkflowClient.startWorkflow - -**Summary:** Add Kotlin extension for starting workflows with method references - -**Scope:** -- Add `WorkflowClient.startWorkflow(KFunction, options, args): KTypedWorkflowHandle` -- Capture result type from method reference -- Support KFunction1-7 overloads - -**Tests:** -- Test workflow start -- Test handle type inference -- Test various argument counts - -**Dependencies:** Change 5 - ---- - -### Change 7: Implement WorkflowClient.executeWorkflow - -**Summary:** Add extension for starting and waiting for workflow result - -**Scope:** -- Add `WorkflowClient.executeWorkflow(KFunction, options, args): R` -- Start workflow and immediately await result -- Support KFunction1-7 overloads - -**Tests:** -- Test execute and wait -- Test result type inference -- Test failure propagation - -**Dependencies:** Change 6 - ---- - -### Change 8: Implement WorkflowClient.getKWorkflowHandle - -**Summary:** Add extension for getting typed handle to existing workflow - -**Scope:** -- Add `WorkflowClient.getKWorkflowHandle(workflowId): KWorkflowHandle` -- Add optional runId parameter -- Return typed handle for signal/query operations - -**Tests:** -- Test get handle by ID -- Test typed operations on handle -- Test with specific runId - -**Dependencies:** Change 7 - ---- - -### Change 9: Implement signalWithStart - -**Summary:** Add atomic signal-with-start operation - -**Scope:** -- Add `WorkflowClient.signalWithStart(workflow, options, workflowArg, signal, signalArg): KTypedWorkflowHandle` -- Atomically start or signal existing workflow -- Return typed handle - -**Tests:** -- Test start new workflow with signal -- Test signal existing workflow -- Test handle operations after - -**Dependencies:** Change 8 - ---- - -### Change 10: Implement updateWithStart - -**Summary:** Add atomic update-with-start operation - -**Scope:** -- Add `WorkflowClient.updateWithStart(workflow, options, workflowArg, update, updateArg): Pair` -- Atomically start or update existing workflow -- Return handle and update result - -**Tests:** -- Test start new workflow with update -- Test update existing workflow -- Test both return values - -**Dependencies:** Change 9 diff --git a/kotlin/phases/phase-2.5-detailed.md b/kotlin/phases/phase-2.5-detailed.md deleted file mode 100644 index e0ab09f..0000000 --- a/kotlin/phases/phase-2.5-detailed.md +++ /dev/null @@ -1,138 +0,0 @@ -# Phase 2.5: Kotlin Wrappers - Detailed Plan - -## Overview - -Implement Kotlin wrapper types for activity context and info, providing null-safe APIs and idiomatic Kotlin access patterns. - -## Prerequisites - -- Phase 2.4 complete (Client API available) - -## Changes - -### Change 1: Implement KActivityInfo - -**Summary:** Create Kotlin wrapper for ActivityInfo with null safety - -**Scope:** -- Add `KActivityInfo` interface in `io.temporal.kotlin.activity` -- Replace `Optional` with nullable types -- Add properties: activityId, activityType, workflowId, attempt, etc. -- Add `getHeartbeatDetails(): Payloads?` - -**Tests:** -- Test all property accessors -- Test nullable properties -- Test heartbeat details retrieval - -**Dependencies:** None - ---- - -### Change 2: Implement KActivityContext - -**Summary:** Create Kotlin wrapper for ActivityExecutionContext - -**Scope:** -- Add `KActivityContext` interface -- Add `info: KActivityInfo` property -- Add `heartbeat(details: Any?)` suspend function -- Add `doNotCompleteOnReturn()` method - -**Tests:** -- Test info access -- Test heartbeat call -- Test do not complete flag - -**Dependencies:** Change 1 - ---- - -### Change 3: Implement KActivity Object - -**Summary:** Create KActivity object as entry point for activity APIs - -**Scope:** -- Add `KActivity` object in `io.temporal.kotlin.activity` -- Add `getContext(): KActivityContext` function -- Add `getInfo(): KActivityInfo` shortcut -- Add internal context storage (thread-local or coroutine context) - -**Tests:** -- Test context retrieval in activity -- Test info shortcut -- Test context not available outside activity - -**Dependencies:** Change 2 - ---- - -### Change 4: Implement Suspend Activity Support - -**Summary:** Support suspend functions in activity implementations - -**Scope:** -- Detect suspend activity methods in registration -- Execute suspend activities with coroutine dispatcher -- Integrate with activity heartbeat - -**Tests:** -- Test suspend activity execution -- Test suspend activity with I/O -- Test heartbeat from suspend activity - -**Dependencies:** Change 3 - ---- - -### Change 5: Implement @KActivityImpl Annotation - -**Summary:** Add annotation for parallel activity interface pattern - -**Scope:** -- Add `@KActivityImpl(activities = KClass)` annotation -- Link suspend interface to non-suspend interface -- Support registration of implementations - -**Tests:** -- Test annotation processing -- Test interface linking -- Test registration with annotation - -**Dependencies:** Change 4 - ---- - -### Change 6: Implement Activity Registration Extensions - -**Summary:** Add worker extensions for registering Kotlin activity implementations - -**Scope:** -- Add `Worker.registerActivitiesImplementations()` extension -- Auto-detect suspend activities -- Support both direct and @KActivityImpl patterns - -**Tests:** -- Test direct suspend activity registration -- Test @KActivityImpl registration -- Test mixed registration - -**Dependencies:** Change 5 - ---- - -### Change 7: Documentation and Examples - -**Summary:** Add comprehensive documentation for activity patterns - -**Scope:** -- Document Option A (pure Kotlin) pattern -- Document Option B (Java interop) pattern -- Add example activity implementations -- Add migration guide section - -**Tests:** -- Example code compiles -- Examples pass execution tests - -**Dependencies:** Change 6 diff --git a/kotlin/phases/phase-3.1-detailed.md b/kotlin/phases/phase-3.1-detailed.md deleted file mode 100644 index d5add59..0000000 --- a/kotlin/phases/phase-3.1-detailed.md +++ /dev/null @@ -1,136 +0,0 @@ -# Phase 3.1: Test Environment - Detailed Plan - -## Overview - -Implement a Kotlin-friendly test environment for workflow testing, integrating with kotlinx-coroutines-test utilities. - -## Prerequisites - -- Phase 2 complete (Full Kotlin SDK API available) - -## Changes - -### Change 1: Add Test Dependencies - -**Summary:** Add kotlinx-coroutines-test and test infrastructure - -**Scope:** -- Add `kotlinx-coroutines-test` dependency -- Add test utility package `io.temporal.kotlin.testing` -- Set up test module structure - -**Tests:** -- Verify dependency resolution -- Basic compilation test - -**Dependencies:** None - ---- - -### Change 2: Implement KTestWorkflowEnvironment - -**Summary:** Create Kotlin wrapper for TestWorkflowEnvironment - -**Scope:** -- Add `KTestWorkflowEnvironment` class -- Wrap Java `TestWorkflowEnvironment` -- Add Kotlin DSL for configuration -- Add `client` property returning Kotlin client - -**Tests:** -- Test environment creation -- Test configuration options -- Test client access - -**Dependencies:** Change 1 - ---- - -### Change 3: Implement Test Worker Registration - -**Summary:** Add test worker with Kotlin workflow support - -**Scope:** -- Add `KTestWorkflowEnvironment.newWorker(taskQueue)` returning configured worker -- Auto-register Kotlin plugin for test workers -- Support workflow and activity registration - -**Tests:** -- Test worker creation -- Test workflow registration -- Test activity registration - -**Dependencies:** Change 2 - ---- - -### Change 4: Implement runWorkflowTest DSL - -**Summary:** Create DSL for writing workflow tests - -**Scope:** -- Add `runWorkflowTest { }` top-level function -- Auto-create environment and clean up -- Provide `environment`, `client`, `worker` in scope -- Integrate with coroutine test dispatcher - -**Tests:** -- Test DSL usage -- Test auto-cleanup -- Test coroutine integration - -**Dependencies:** Change 3 - ---- - -### Change 5: Implement Test Workflow Execution Helpers - -**Summary:** Add helper methods for common test patterns - -**Scope:** -- Add `KTestWorkflowEnvironment.executeWorkflow()` with timeout -- Add `KTestWorkflowEnvironment.startWorkflow()` returning test handle -- Add assertion helpers for workflow state - -**Tests:** -- Test execute with result -- Test start and interact -- Test timeout handling - -**Dependencies:** Change 4 - ---- - -### Change 6: Implement Test Activity Environment - -**Summary:** Add support for testing activities in isolation - -**Scope:** -- Add `KTestActivityEnvironment` class -- Support testing suspend activities -- Provide mock heartbeat recording - -**Tests:** -- Test activity execution -- Test heartbeat recording -- Test activity failure - -**Dependencies:** Change 5 - ---- - -### Change 7: Integration with JUnit 5 - -**Summary:** Add JUnit 5 extension for Kotlin workflow tests - -**Scope:** -- Add `@KTemporalTest` annotation -- Add `KTemporalTestExtension` implementing JUnit extension -- Auto-inject test environment into test class - -**Tests:** -- Test annotation processing -- Test environment injection -- Test lifecycle management - -**Dependencies:** Change 6 diff --git a/kotlin/phases/phase-3.2-detailed.md b/kotlin/phases/phase-3.2-detailed.md deleted file mode 100644 index d212f88..0000000 --- a/kotlin/phases/phase-3.2-detailed.md +++ /dev/null @@ -1,170 +0,0 @@ -# Phase 3.2: Mocking Support - Detailed Plan - -## Overview - -Implement activity mocking and time manipulation utilities for comprehensive workflow testing. - -## Prerequisites - -- Phase 3.1 complete (Test environment available) - -## Changes - -### Change 1: Implement Activity Mocking Interface - -**Summary:** Create interface for mocking activity behavior - -**Scope:** -- Add `KActivityMock` interface -- Support mocking by activity interface type -- Support mocking by activity name (string) - -**Tests:** -- Test mock interface creation -- Test type-safe mock setup - -**Dependencies:** None - ---- - -### Change 2: Implement Activity Mock Registration - -**Summary:** Add mock registration to test worker - -**Scope:** -- Add `Worker.registerActivityMock(mock)` extension -- Add `Worker.registerActivityMock(name, handler)` for untyped -- Mocks take precedence over real implementations - -**Tests:** -- Test mock registration -- Test mock precedence -- Test mixed mock and real - -**Dependencies:** Change 1 - ---- - -### Change 3: Implement Mock Response Builders - -**Summary:** Create DSL for configuring mock responses - -**Scope:** -- Add `KActivityMock.returns(value)` for success -- Add `KActivityMock.throws(exception)` for failure -- Add `KActivityMock.answers { args -> result }` for dynamic - -**Tests:** -- Test static return value -- Test exception throwing -- Test dynamic answers - -**Dependencies:** Change 2 - ---- - -### Change 4: Implement Suspend Activity Mocking - -**Summary:** Support mocking suspend activity functions - -**Scope:** -- Add `KActivityMock.coAnswers { args -> result }` for suspend -- Support delay simulation in mocks -- Handle coroutine context in mocks - -**Tests:** -- Test suspend mock -- Test mock with delay -- Test mock cancellation - -**Dependencies:** Change 3 - ---- - -### Change 5: Implement Mock Verification - -**Summary:** Add verification capabilities for mock calls - -**Scope:** -- Add `KActivityMock.verify(times)` for call count -- Add `KActivityMock.verifyArgs(matcher)` for argument verification -- Add `KActivityMock.verifyNever()` shortcut - -**Tests:** -- Test call count verification -- Test argument verification -- Test verify never called - -**Dependencies:** Change 4 - ---- - -### Change 6: Implement Time Skipping - -**Summary:** Add time manipulation for test environment - -**Scope:** -- Add `KTestWorkflowEnvironment.skipTime(duration)` function -- Add `KTestWorkflowEnvironment.skipUntil(instant)` function -- Advance workflow timers without real delay - -**Tests:** -- Test skip by duration -- Test skip to instant -- Test timer advancement - -**Dependencies:** Change 5 - ---- - -### Change 7: Implement Auto Time Skipping - -**Summary:** Add automatic time advancement mode - -**Scope:** -- Add `KTestWorkflowEnvironment.autoTimeSkipping` property -- Automatically advance time when workflow blocked on timer -- Configurable skip increment - -**Tests:** -- Test auto skip enabled -- Test skip increment configuration -- Test disable auto skip - -**Dependencies:** Change 6 - ---- - -### Change 8: Implement Test Clock Access - -**Summary:** Expose test clock for assertions - -**Scope:** -- Add `KTestWorkflowEnvironment.currentTime` property -- Add `KTestWorkflowEnvironment.setCurrentTime(instant)` for setup -- Coordinate with workflow's `KWorkflow.currentTime()` - -**Tests:** -- Test read current time -- Test set initial time -- Test time consistency in workflow - -**Dependencies:** Change 7 - ---- - -### Change 9: Documentation and Examples - -**Summary:** Add comprehensive testing documentation - -**Scope:** -- Document activity mocking patterns -- Document time skipping strategies -- Add complete test examples -- Add troubleshooting guide - -**Tests:** -- Example code compiles -- Examples pass execution - -**Dependencies:** Change 8 From 914d0d42212e67a86b0326821999ec5c40c61855 Mon Sep 17 00:00:00 2001 From: Maxim Fateev Date: Tue, 30 Dec 2025 23:50:23 -0800 Subject: [PATCH 09/83] Add KWorkflowClient, KWorkerFactory, and KChildWorkflowHandle to API - Add KWorkflowClient with DSL constructor and suspend functions for startWorkflow, executeWorkflow, signalWithStart, updateWithStart - Add KWorkerFactory that automatically enables KotlinPlugin - Add KChildWorkflowHandle for child workflow interaction (signal, cancel, result) via startChildWorkflow and getChildWorkflowHandle - Update implementation-plan.md with Phase 1 completion status - Reorganize Phase 2 to include new client and worker APIs - Remove api-implementation-discrepancies.md (was untracked) --- kotlin/implementation-plan.md | 94 +++++---- kotlin/sdk-api.md | 345 +++++++++++++++++++++++++++++----- kotlin/sdk-implementation.md | 65 ++++--- 3 files changed, 392 insertions(+), 112 deletions(-) diff --git a/kotlin/implementation-plan.md b/kotlin/implementation-plan.md index 7c63c0e..73d1551 100644 --- a/kotlin/implementation-plan.md +++ b/kotlin/implementation-plan.md @@ -1,65 +1,75 @@ # Kotlin SDK Implementation Plan -## Phase 1: Core Coroutine Infrastructure +## Phase 1: Core Coroutine Infrastructure ✅ COMPLETE ### 1.1 Java SDK Refactoring -- Add `WorkflowImplementationFactory` interface -- Update `Worker` to support multiple factories -- Update `SyncWorkflowWorker` with composite factory -- Add `Async.await()` methods returning `Promise` -- Expose `ReplayWorkflow` and `ReplayWorkflowContext` for SPI +- ✅ Add `WorkflowImplementationFactory` interface +- ✅ Update `Worker` to support multiple factories +- ✅ Update `SyncWorkflowWorker` with composite factory +- ✅ Add `Async.await()` methods returning `Promise` +- ✅ Expose `ReplayWorkflow` and `ReplayWorkflowContext` for SPI ### 1.2 Kotlin Coroutine Runtime -- Implement `KotlinCoroutineDispatcher` (deterministic execution) -- Implement `KotlinDelay` (timer integration) -- Implement `KotlinReplayWorkflow` -- Implement `KotlinWorkflowContext` -- Implement `KotlinWorkflowImplementationFactory` +- ✅ Implement `KotlinCoroutineDispatcher` (deterministic execution with `Delay`) +- ✅ Implement `KotlinReplayWorkflow` +- ✅ Implement `KotlinWorkflowContext` +- ✅ Implement `KotlinWorkflowImplementationFactory` ### 1.3 Core Kotlin APIs -- `KWorkflow` object with string-based activity execution -- `KWorkflowInfo` wrapper with null safety -- `KActivityHandle` for async activity execution -- Duration extensions (`kotlin.time.Duration` ↔ `java.time.Duration`) -- `KWorkflow.awaitCondition()` suspend function +- ✅ `KWorkflow` object with string-based activity/child workflow execution +- ✅ `KWorkflowInfo` wrapper with null safety +- ✅ Duration extensions (`kotlin.time.Duration` ↔ `java.time.Duration`) +- ✅ `KWorkflow.awaitCondition()` suspend function +- ✅ Standard `delay()` support via `Delay` interface +- ✅ Standard `coroutineScope { async { } }` for parallel execution ### 1.4 Worker Integration -- `KotlinPlugin` for enabling coroutine support -- Worker extension for registering Kotlin workflows -- Suspend function detection in registration +- ✅ `KotlinPlugin` for enabling coroutine support +- ✅ Worker extension for registering Kotlin workflows +- ✅ Suspend function detection in registration + +### 1.5 Signals, Queries, Updates +- ✅ Signal handler registration with suspend support +- ✅ Query methods (annotation-based and dynamic) +- ✅ Update methods with validators --- ## Phase 2: Typed APIs & Full Feature Set -### 2.1 Typed Activity Stubs +### 2.1 Typed Activity Execution - `KFunction`-based `executeActivity()` overloads - Type extraction from method references -- Local activity support - -### 2.2 Signals, Queries, Updates -- Signal handler registration with suspend support -- Query methods (including property syntax) -- Update methods with validators +- Local activity support with `executeLocalActivity()` -### 2.3 Child Workflows +### 2.2 Typed Child Workflow Execution - `executeChildWorkflow()` with method references -- `startChildWorkflow()` returning handle -- `ChildWorkflowOptions` DSL - -### 2.4 Client API -- `KWorkflowHandle` and `KTypedWorkflowHandle` -- `WorkflowClient` extensions for Kotlin -- `signalWithStart` and `updateWithStart` - -### 2.5 Kotlin Wrappers -- `KActivity` object and `KActivityContext` +- `startChildWorkflow()` returning `KChildWorkflowHandle` +- `getChildWorkflowHandle()` for existing child workflows +- `KChildWorkflowHandle` interface (signal, cancel, result) + +### 2.3 Client API +- `KWorkflowClient` - Kotlin client with suspend functions and DSL constructor +- `KWorkflowHandle` - typed handle for signals/queries/updates +- `KTypedWorkflowHandle` - extends KWorkflowHandle with typed result +- `WorkflowHandle` - untyped handle (string-based operations) +- `KUpdateHandle` - handle for async update execution +- `startWorkflow()`, `executeWorkflow()` suspend functions +- `signalWithStart()` and `updateWithStart()` +- `getWorkflowHandle()` and `getUntypedWorkflowHandle()` + +### 2.4 Worker API +- `KWorkerFactory` - Kotlin worker factory with KotlinPlugin pre-configured +- DSL builders for worker options + +### 2.5 Kotlin Activity Wrappers +- `KActivity` object (entry point for activity APIs) +- `KActivityContext` with suspend `heartbeat()` - `KActivityInfo` with null safety -- `KUpdateHandle` --- -## Phase 3: Testing Framework +## Phase 3: Testing Framework & Interceptors ### 3.1 Test Environment - Kotlin-friendly test workflow environment @@ -69,6 +79,12 @@ - Activity mocking with suspend functions - Time skipping utilities +### 3.3 Interceptors +- `KWorkerInterceptor` interface +- `KWorkflowInboundCallsInterceptor` with suspend functions +- `KWorkflowOutboundCallsInterceptor` +- `KActivityInboundCallsInterceptor` + --- ## Cross-Cutting Concerns (All Phases) diff --git a/kotlin/sdk-api.md b/kotlin/sdk-api.md index 4345f88..d155dec 100644 --- a/kotlin/sdk-api.md +++ b/kotlin/sdk-api.md @@ -147,8 +147,8 @@ interface OrderWorkflow { fun getItemCount(): Int // Method syntax } -// Client usage via typed handle -val handle = client.getKWorkflowHandle("order-123") +// Client usage via typed handle (using KWorkflowClient) +val handle = client.getWorkflowHandle("order-123") val status = handle.query(OrderWorkflow::status) val count = handle.query(OrderWorkflow::getItemCount) ``` @@ -178,7 +178,7 @@ class GreetingWorkflowImpl : GreetingWorkflow { } } -// Client call - same pattern as activities, no stub needed +// Client call using KWorkflowClient - same pattern as activities, no stub needed val result = client.executeWorkflow( GreetingWorkflow::getGreeting, WorkflowOptions { @@ -227,7 +227,7 @@ class OrderWorkflowImpl : OrderWorkflowSuspend { override val status: OrderStatus get() = OrderStatus.PENDING } -// Client call - same pattern, blocking for Option B +// Client call using KWorkflowClient - same pattern, suspends for result val result = client.executeWorkflow( OrderWorkflow::processOrder, WorkflowOptions { @@ -321,7 +321,137 @@ override suspend fun parentWorkflowParallel(): String = coroutineScope { } ``` -> **Note:** We use standard `coroutineScope { async { } }` instead of custom `startChildWorkflow` or `startActivity` methods. The deterministic dispatcher ensures these execute correctly during replay. +### Child Workflow Handle + +For cases where you need to interact with a child workflow (signal, query, cancel) rather than just wait for its result, use `startChildWorkflow` to get a handle: + +```kotlin +// Start child workflow and get handle for interaction +override suspend fun parentWorkflowWithHandle(): String { + val handle = KWorkflow.startChildWorkflow( + ChildWorkflow::doWork, + ChildWorkflowOptions { + workflowId = "child-workflow-id" + }, + "input" + ) + + // Can signal the child workflow + handle.signal(ChildWorkflow::updateProgress, 50) + + // Wait for result when ready + return handle.result() +} + +// Get handle to existing child workflow by ID +override suspend fun interactWithExistingChild(): String { + val handle = KWorkflow.getChildWorkflowHandle("child-workflow-id") + + // Signal the child workflow + handle.signal(ChildWorkflow::updatePriority, Priority.HIGH) + + return handle.result() +} + +// Parallel child workflows with handles for interaction +override suspend fun parallelChildrenWithHandles(): List = coroutineScope { + val handles = listOf("child-1", "child-2", "child-3").map { id -> + KWorkflow.startChildWorkflow( + ChildWorkflow::doWork, + ChildWorkflowOptions { workflowId = id }, + "input" + ) + } + + // Can interact with any child while they're running + handles.forEach { handle -> + handle.signal(ChildWorkflow::updatePriority, Priority.HIGH) + } + + // Wait for all results + handles.map { async { it.result() } }.awaitAll() +} +``` + +**KChildWorkflowHandle API:** + +```kotlin +/** + * Handle for interacting with a started child workflow. + * Returned by startChildWorkflow() with result type captured from method reference. + * + * @param T The child workflow interface type + * @param R The result type of the child workflow method + */ +interface KChildWorkflowHandle { + /** The child workflow's workflow ID */ + val workflowId: String + + /** The child workflow's first execution run ID */ + val firstExecutionRunId: String + + /** + * Wait for the child workflow to complete and return its result. + * Suspends until the child workflow finishes. + */ + suspend fun result(): R + + // Signals - type-safe method references + suspend fun signal(method: KFunction1) + suspend fun signal(method: KFunction2, arg: A1) + suspend fun signal(method: KFunction3, arg1: A1, arg2: A2) + + /** + * Request cancellation of the child workflow. + * The child workflow will receive a CancellationException at its next suspension point. + */ + suspend fun cancel() +} +``` + +**KWorkflow methods for child workflow handles:** + +```kotlin +object KWorkflow { + /** + * Start a child workflow and return a handle for interaction. + * Use this when you need to signal, query, or cancel the child workflow. + * + * For simple fire-and-wait cases, prefer executeChildWorkflow() instead. + */ + suspend fun startChildWorkflow( + workflow: KFunction2, + options: ChildWorkflowOptions, + arg: A1 + ): KChildWorkflowHandle + + // Overloads for 0-6 arguments... + suspend fun startChildWorkflow( + workflow: KFunction1, + options: ChildWorkflowOptions + ): KChildWorkflowHandle + + suspend fun startChildWorkflow( + workflow: KFunction3, + options: ChildWorkflowOptions, + arg1: A1, arg2: A2 + ): KChildWorkflowHandle + + /** + * Get a handle to an existing child workflow by workflow ID. + * Use this to interact with a child workflow started earlier in the same workflow execution. + * + * @param T The child workflow interface type + * @param R The expected result type (must match the child workflow's return type) + * @param workflowId The child workflow's workflow ID + */ + inline fun getChildWorkflowHandle( + workflowId: String + ): KChildWorkflowHandle +} +``` + +> **Note:** For simple parallel execution where you only need the result, use standard `coroutineScope { async { executeChildWorkflow(...) } }`. Use `startChildWorkflow` only when you need to interact with the child workflow while it's running. **ChildWorkflowOptions:** @@ -857,17 +987,114 @@ val sanitized = KWorkflow.executeLocalActivity( ```kotlin val service = WorkflowServiceStubs.newLocalServiceStubs() -// Uses existing WorkflowClient DSL extension -val client = WorkflowClient(service) { +// Create KWorkflowClient with DSL configuration +val client = KWorkflowClient(service) { setNamespace("default") setDataConverter(myConverter) } + +// For blocking calls from non-suspend contexts, use runBlocking +val result = runBlocking { + client.executeWorkflow(MyWorkflow::process, options, input) +} +``` + +### KWorkflowClient + +`KWorkflowClient` provides Kotlin-specific APIs with suspend functions for starting and interacting with workflows: + +```kotlin +/** + * Kotlin workflow client providing suspend functions and type-safe workflow APIs. + * + * @param service The WorkflowServiceStubs to connect to + * @param options DSL builder for WorkflowClientOptions + */ +class KWorkflowClient( + service: WorkflowServiceStubs, + options: WorkflowClientOptions.Builder.() -> Unit = {} +) { + /** The underlying WorkflowClient for advanced use cases */ + val workflowClient: WorkflowClient + + /** + * Start a workflow and return a handle for interaction. + * Does not wait for the workflow to complete. + */ + suspend fun startWorkflow( + workflow: KFunction1, + options: WorkflowOptions + ): KTypedWorkflowHandle + + suspend fun startWorkflow( + workflow: KFunction2, + options: WorkflowOptions, + arg: A1 + ): KTypedWorkflowHandle + + // Overloads for 2-6 arguments... + + /** + * Start a workflow and wait for its result. + * Suspends until the workflow completes. + */ + suspend fun executeWorkflow( + workflow: KFunction1, + options: WorkflowOptions + ): R + + suspend fun executeWorkflow( + workflow: KFunction2, + options: WorkflowOptions, + arg: A1 + ): R + + // Overloads for 2-6 arguments... + + /** + * Get a typed handle for an existing workflow by ID. + * Use this to signal, query, or get results from a workflow started elsewhere. + */ + inline fun getWorkflowHandle(workflowId: String): KWorkflowHandle + inline fun getWorkflowHandle(workflowId: String, runId: String): KWorkflowHandle + + /** + * Get an untyped handle for an existing workflow by ID. + * Use when you don't know the workflow type at compile time. + */ + fun getUntypedWorkflowHandle(workflowId: String): WorkflowHandle + fun getUntypedWorkflowHandle(workflowId: String, runId: String): WorkflowHandle + + /** + * Atomically start a workflow and send a signal. + * If the workflow already exists, only the signal is sent. + */ + suspend fun signalWithStart( + workflow: KFunction2, + options: WorkflowOptions, + workflowArg: A1, + signal: KFunction2, + signalArg: SA1 + ): KTypedWorkflowHandle + + /** + * Atomically start a workflow and send an update. + * Returns both the workflow handle and the update result. + */ + suspend fun updateWithStart( + workflow: KFunction2, + options: WorkflowOptions, + workflowArg: A1, + update: KFunction2, + updateArg: UA1 + ): Pair, UR> +} ``` ### Starting Workflows ```kotlin -// Execute workflow and wait for result - no stub needed +// Execute workflow and wait for result (suspend function) val result = client.executeWorkflow( GreetingWorkflow::getGreeting, WorkflowOptions { @@ -897,16 +1124,14 @@ Atomically start a workflow and send a signal. If the workflow already exists, o ```kotlin // Returns KTypedWorkflowHandle - result type captured from method reference val handle = client.signalWithStart( - // Workflow to start workflow = OrderWorkflow::processOrder, options = WorkflowOptions { workflowId = "order-123" taskQueue = "orders" }, workflowArg = order, - // Signal to send - signal = OrderWorkflow::cancelOrder, - signalArg = "Price changed" + signal = OrderWorkflow::updatePriority, + signalArg = Priority.HIGH ) // Can use typed handle for queries/signals @@ -923,17 +1148,14 @@ Atomically start a workflow and send an update. If the workflow already exists, // - handle with result type captured from workflow method reference // - updateResult typed by update method return type val (handle, updateResult: Boolean) = client.updateWithStart( - // Workflow to start workflow = OrderWorkflow::processOrder, - workflowArg = order, - // Update to send - update = OrderWorkflow::addItem, - updateArg = newItem, - // Options options = WorkflowOptions { workflowId = "order-123" taskQueue = "orders" - } + }, + workflowArg = order, + update = OrderWorkflow::addItem, + updateArg = newItem ) println("Item added: $updateResult") @@ -946,11 +1168,11 @@ val result = handle.result() // Type inferred as OrderResult For interacting with existing workflows (signals, queries, results, cancellation), use a typed or untyped handle: ```kotlin -// Get typed handle for existing workflow by ID (like Python's get_workflow_handle_for) -val handle = client.getKWorkflowHandle("order-123") +// Get typed handle for existing workflow by ID +val handle = client.getWorkflowHandle("order-123") // Send signal - method reference provides type safety -handle.signal(OrderWorkflow::cancelOrder, "Customer request") +handle.signal(OrderWorkflow::updatePriority, Priority.HIGH) // Query - method reference with compile-time type checking val status = handle.query(OrderWorkflow::status) @@ -1044,8 +1266,8 @@ val handle = client.startWorkflow( ) val result: OrderResult = handle.result() // No type parameter needed! -// getKWorkflowHandle doesn't know result type -val existingHandle = client.getKWorkflowHandle(workflowId) +// getWorkflowHandle doesn't know result type +val existingHandle = client.getWorkflowHandle(workflowId) val result = existingHandle.result() // Must specify type ``` @@ -1057,10 +1279,10 @@ For cases where you don't know the workflow type at compile time: ```kotlin // Untyped handle - signal/query by string name -val untypedHandle = client.getWorkflowHandle("order-123") +val untypedHandle = client.getUntypedWorkflowHandle("order-123") // Operations use string names instead of method references -untypedHandle.signal("cancelOrder", "Customer request") +untypedHandle.signal("updatePriority", Priority.HIGH) val status = untypedHandle.query("status") val result = untypedHandle.result() @@ -1085,19 +1307,16 @@ interface WorkflowHandle { ## Worker API -### Enabling Kotlin Coroutine Support +### KWorkerFactory (Recommended) -Kotlin coroutine support is enabled via a plugin passed to the client (similar to Python SDK's plugin system). The plugin provides custom workflow and activity executors that handle `suspend` functions. +For pure Kotlin applications, use `KWorkerFactory` which automatically enables coroutine support: ```kotlin val service = WorkflowServiceStubs.newLocalServiceStubs() +val client = KWorkflowClient(service) { ... } -// Plugin is passed to the client and propagates to workers -val client = WorkflowClient(service) { - plugins = listOf(KotlinPlugin()) -} - -val factory = WorkerFactory(client) { +// KWorkerFactory automatically enables Kotlin coroutine support +val factory = KWorkerFactory(client) { maxWorkflowThreadCount = 800 } @@ -1111,7 +1330,7 @@ worker.registerWorkflowImplementationTypes( OrderWorkflowImpl::class ) -// Register activities - plugin detects suspend functions automatically +// Register activities - suspend functions handled automatically worker.registerActivitiesImplementations( GreetingActivitiesImpl(), // Kotlin suspend activities JavaActivitiesImpl() // Java activities work too @@ -1121,7 +1340,46 @@ worker.registerActivitiesImplementations( factory.start() ``` -> **Note:** The plugin API will be defined separately. It follows the same pattern as Python SDK's plugin system, allowing custom workflow runners and activity executors. +**KWorkerFactory API:** + +```kotlin +/** + * Kotlin worker factory that automatically enables coroutine support. + * Wraps WorkerFactory with KotlinPlugin pre-configured. + */ +class KWorkerFactory( + client: KWorkflowClient, + options: WorkerFactoryOptions.Builder.() -> Unit = {} +) { + /** The underlying WorkerFactory for advanced use cases */ + val workerFactory: WorkerFactory + + fun newWorker(taskQueue: String, options: WorkerOptions.Builder.() -> Unit = {}): Worker + fun start() + fun shutdown() + fun shutdownNow() + suspend fun awaitTermination(timeout: Duration) +} +``` + +### KotlinPlugin (For Java Main) + +When your main application is written in Java and you need to register Kotlin workflows, use `KotlinPlugin` explicitly: + +```kotlin +// Java main or mixed Java/Kotlin setup +val service = WorkflowServiceStubs.newLocalServiceStubs() +val client = WorkflowClient.newInstance(service) + +val factory = WorkerFactory.newInstance(client, WorkerFactoryOptions.newBuilder() + .addPlugin(KotlinPlugin()) + .build()) + +val worker = factory.newWorker("task-queue") + +// Register Kotlin workflows - plugin handles suspend functions +worker.registerWorkflowImplementationTypes(KotlinWorkflowImpl::class.java) +``` ### Mixed Java and Kotlin @@ -1286,8 +1544,10 @@ val client = WorkflowClient(service) { | `Async.function(stub::method, arg)` | `coroutineScope { async { KWorkflow.executeActivity(...) } }` | | **Workflows** | | | `Workflow.newChildWorkflowStub(...)` | `KWorkflow.executeChildWorkflow(Interface::method, options, ...)` | +| `Async.function(childStub::method, arg)` | `KWorkflow.startChildWorkflow(Interface::method, options, ...)` → `KChildWorkflowHandle` | +| `Workflow.getWorkflowExecution(childStub)` | `childHandle.workflowId` / `childHandle.firstExecutionRunId` | | `client.newWorkflowStub(...)` | `client.startWorkflow(Interface::method, ...)` → `KTypedWorkflowHandle` | -| `client.newWorkflowStub(Cls, id)` | `client.getKWorkflowHandle(id)` → `KWorkflowHandle` | +| `client.newWorkflowStub(Cls, id)` | `client.getWorkflowHandle(id)` → `KWorkflowHandle` | | `stub.signal(arg)` | `handle.signal(T::method, arg)` | | `stub.query()` | `handle.query(T::method)` | | `handle.getResult()` | `handle.result()` (type inferred) or `handle.result()` | @@ -1651,12 +1911,11 @@ fun main() = runBlocking { val service = WorkflowServiceStubs.newLocalServiceStubs() - // Enable Kotlin coroutine support via plugin - val client = WorkflowClient(service) { - plugins = listOf(KotlinPlugin()) - } + // Create KWorkflowClient for Kotlin-specific APIs + val client = KWorkflowClient(service) - val factory = WorkerFactory(client) + // KWorkerFactory automatically enables Kotlin coroutine support + val factory = KWorkerFactory(client) val worker = factory.newWorker("orders") // Plugin handles suspend functions automatically @@ -1697,7 +1956,7 @@ fun main() = runBlocking { println("Status: $status, Progress: $progress%") // Or get handle for existing workflow by ID - val existingHandle = client.getKWorkflowHandle(workflowId) + val existingHandle = client.getWorkflowHandle(workflowId) // Send update and wait for result val newItem = OrderItem("prod-3", 1, 19.99.toBigDecimal()) diff --git a/kotlin/sdk-implementation.md b/kotlin/sdk-implementation.md index 0692b89..6733c37 100644 --- a/kotlin/sdk-implementation.md +++ b/kotlin/sdk-implementation.md @@ -6,9 +6,9 @@ For public API and developer experience, see [sdk-api.md](./sdk-api.md). ## Phases -* **Phase 1** - Coroutine-based workflows, untyped activity stubs, pluggable WorkflowImplementationFactory, core Kotlin idioms (Duration, null safety) -* **Phase 2** - Typed activity stubs with suspend functions, signals/queries/updates, child workflows, property queries -* **Phase 3** - Testing framework +* **Phase 1 (COMPLETE)** - Coroutine-based workflows, untyped activity/child workflow stubs, pluggable WorkflowImplementationFactory, core Kotlin idioms (Duration, null safety, KWorkflowInfo), signals/queries/updates (annotation + dynamic handlers), standard `delay()` and `coroutineScope { async { } }` support +* **Phase 2** - Typed activity stubs, typed child workflow stubs, KChildWorkflowHandle, KActivity/KActivityInfo/KActivityContext wrappers, client & worker API (KWorkflowClient, KWorkerFactory, KWorkflowHandle, startWorkflow, signalWithStart, etc.) +* **Phase 3** - Interceptor interfaces, testing framework > **Note:** Nexus support is a separate project and will be addressed independently. @@ -79,32 +79,37 @@ The `temporal-kotlin` module already provides Kotlin extensions for the Java SDK ## New Classes (Kotlin SDK) -| Class | Purpose | -|-------|---------| -| **Core Workflow** | | -| `KWorkflow` | Entry point for workflow APIs (like `Workflow` in Java) | -| `KotlinWorkflowContext` | Internal workflow execution context | -| `KotlinCoroutineDispatcher` | Deterministic coroutine dispatcher with `Delay` implementation | -| `KWorkerInterceptor` | Interceptor interface with suspend functions | -| **Factory/Registration** | | -| `KotlinPlugin` | Plugin for enabling coroutine support and registering interceptors | -| `KotlinWorkflowImplementationFactory` | Implements `WorkflowImplementationFactory` for coroutine workflows | -| `KotlinWorkflowDefinition` | Metadata about a Kotlin workflow type | -| `KotlinReplayWorkflow` | Implements `ReplayWorkflow` using coroutines | -| `WorkerExt.kt` (additions) | Extension `registerKotlinWorkflowImplementationTypes()` | -| **Kotlin Wrappers** | | -| `KWorkflowInfo` | Kotlin wrapper for WorkflowInfo with nullable types | -| `KActivityInfo` | Kotlin wrapper for ActivityInfo with nullable types | -| `KActivityContext` | Kotlin wrapper for ActivityExecutionContext | -| `WorkflowHandle` | Untyped workflow handle (string-based signals/queries) | -| `KWorkflowHandle` | Typed workflow handle for signals/queries/updates | -| `KTypedWorkflowHandle` | Extends KWorkflowHandle with typed result (returned by startWorkflow) | -| `KUpdateHandle` | Handle for async update execution | -| **Extensions** | | -| `DurationExt.kt` | Conversions between `kotlin.time.Duration` and `java.time.Duration` | -| `PromiseExt.kt` | `Promise.toDeferred()` to bridge Java SDK to standard coroutines | - -> **Note:** We deliberately **do not** have `KActivityHandle` or `KChildWorkflowHandle`. Instead, users use standard `coroutineScope { async { } }` with `Deferred` for parallel execution. This follows the design principle of using idiomatic Kotlin patterns instead of custom APIs. +| Class | Purpose | Status | +|-------|---------|--------| +| **Core Workflow** | | | +| `KWorkflow` | Entry point for workflow APIs (like `Workflow` in Java) | ✅ Done | +| `KotlinWorkflowContext` | Internal workflow execution context | ✅ Done | +| `KotlinCoroutineDispatcher` | Deterministic coroutine dispatcher with `Delay` implementation | ✅ Done | +| `KWorkerInterceptor` | Interceptor interface with suspend functions | Phase 3 | +| **Factory/Registration** | | | +| `KotlinPlugin` | Plugin for enabling coroutine support and registering interceptors | ✅ Done | +| `KotlinWorkflowImplementationFactory` | Implements `WorkflowImplementationFactory` for coroutine workflows | ✅ Done | +| `KotlinWorkflowDefinition` | Metadata about a Kotlin workflow type | ✅ Done | +| `KotlinReplayWorkflow` | Implements `ReplayWorkflow` using coroutines | ✅ Done | +| `WorkerExt.kt` (additions) | Extension `registerKotlinWorkflowImplementationTypes()` | Phase 2 | +| **Kotlin Wrappers** | | | +| `KWorkflowInfo` | Kotlin wrapper for WorkflowInfo with nullable types | ✅ Done | +| `KActivity` | Entry point for activity APIs (like `Activity` in Java) | Phase 2 | +| `KActivityInfo` | Kotlin wrapper for ActivityInfo with nullable types | Phase 2 | +| `KActivityContext` | Kotlin wrapper for ActivityExecutionContext | Phase 2 | +| **Client & Worker API** | | | +| `KWorkflowClient` | Kotlin client with suspend functions for starting/executing workflows | Phase 2 | +| `KWorkerFactory` | Kotlin worker factory with KotlinPlugin pre-configured | Phase 2 | +| `WorkflowHandle` | Untyped workflow handle (string-based signals/queries) | Phase 2 | +| `KWorkflowHandle` | Typed workflow handle for signals/queries/updates | Phase 2 | +| `KTypedWorkflowHandle` | Extends KWorkflowHandle with typed result (returned by startWorkflow) | Phase 2 | +| `KUpdateHandle` | Handle for async update execution | Phase 2 | +| `KChildWorkflowHandle` | Handle for interacting with started child workflows (signal, cancel, result) | Phase 2 | +| **Extensions** | | | +| `DurationExt.kt` | Conversions between `kotlin.time.Duration` and `java.time.Duration` | ✅ Done | +| `PromiseExt.kt` | `Promise.toDeferred()` to bridge Java SDK to standard coroutines | ✅ Done | + +> **Note:** We deliberately **do not** have `KActivityHandle`. Instead, users use standard `coroutineScope { async { } }` with `Deferred` for parallel activity execution. This follows the design principle of using idiomatic Kotlin patterns instead of custom APIs. However, `KChildWorkflowHandle` is provided because child workflows support signaling, which requires a handle. ## Unified Worker Architecture @@ -315,7 +320,7 @@ This allows Kotlin to wrap it as a suspend function in `KWorkflow`: * This is the Kotlin equivalent of Java's [Workflow.await]. * The condition is re-evaluated after each workflow event. */ -suspend fun condition(condition: () -> Boolean) { +suspend fun awaitCondition(condition: () -> Boolean) { Async.await { condition() }.await() // Promise.await() suspends } From b46e5308366fbab153c14da035fcd8edc64f8e71 Mon Sep 17 00:00:00 2001 From: Maxim Fateev Date: Tue, 30 Dec 2025 23:52:08 -0800 Subject: [PATCH 10/83] nexus images --- nexus/images/behavior.png | Bin 192047 -> 131 bytes nexus/images/full-client-id-flow.png | Bin 402976 -> 131 bytes nexus/images/lost-client-id.png | Bin 231111 -> 131 bytes nexus/images/nexus-flow.png | Bin 218330 -> 131 bytes 4 files changed, 0 insertions(+), 0 deletions(-) diff --git a/nexus/images/behavior.png b/nexus/images/behavior.png index 901f73d641e1440d8d53702c067cc804327f2c8d..2bd1679c3f73c08e6dfcce513c34c7a223350617 100644 GIT binary patch literal 131 zcmW;CI}*Ym5CG7gQ*Z&ppOD;UcUgoPm5h)So?gf5HSgm0X#FMYoX6Oey0>|Il(GD* zCoa^VY8*`JGNQLEM+F<=9w-1Jtr-E+#@cBMg@93;v_<1fV((laoikGi+5L+rZ~z!% N5~F=sn$2pziZ4tAD3<^L literal 192047 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 F{s$Ndder~` diff --git a/nexus/images/full-client-id-flow.png b/nexus/images/full-client-id-flow.png index 33e3b270a78d7dffa426e3a50fb75eb0abe0e8da..3200e809c1af71cb2e4e453d5e46a2a5ec8dd821 100644 GIT binary patch literal 131 zcmWN?%MrpL5CG6SRnUOp&$8)e1FSHkk`c_o>h)dTRo}CZmuzdDb64u#*X_Y```IJOc%WoRSFNdXa>+W+MjJ9j MV)k#9UR#7Heht$nqyPW_ literal 402976 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%0AL{uU>*CDO;7XwywQcE_nYY5K_j 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 M7?iXhY3pF5{yvK+ZU6uP literal 231111 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 diff --git a/nexus/images/nexus-flow.png b/nexus/images/nexus-flow.png index 938a198069a3088e5b1470c75a4c355b923ab7a0..fb3e5ad9700727559499976dd2c5fa94b4af99fb 100644 GIT binary patch literal 131 zcmWN?OA^8$3;@tQr{DsXwBZwa8$LmpQR$f4g{RlIc^AKB%$M%xdB|?eeVn&PljVQ^ z^ literal 218330 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%0AYu7m7pLzhIOkdl(mg73#u3<>DJB<_R67P}x`I+W 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

H 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 Date: Thu, 1 Jan 2026 19:30:30 -0800 Subject: [PATCH 11/83] Add Java SDK API Parity section to design document Documents the analysis of Java vs Kotlin SDK API differences: - APIs not needed in Kotlin (delay, cancellation scopes, sync primitives, wrap) - Cancellation support integration with Kotlin coroutines - Remaining gaps organized by priority (search attributes, memo, cron APIs) - KActivityInfo missing fields --- kotlin/sdk-api.md | 831 +++++++++++++++++++++++++++++++++++++++------- 1 file changed, 716 insertions(+), 115 deletions(-) diff --git a/kotlin/sdk-api.md b/kotlin/sdk-api.md index d155dec..fc482a5 100644 --- a/kotlin/sdk-api.md +++ b/kotlin/sdk-api.md @@ -21,14 +21,6 @@ The Kotlin SDK provides an idiomatic Kotlin experience for building Temporal wor * Minimum Kotlin version: 1.8.x * Coroutines library: kotlinx-coroutines-core 1.7.x+ -## Phases - -* **Phase 1** - Coroutine-based workflows, untyped activity stubs, core Kotlin idioms -* **Phase 2** - Typed activity stubs, signals/queries/updates, child workflows -* **Phase 3** - Testing framework - -> **Note:** Nexus support is a separate project and will be addressed independently. - ## Design Principle **Use idiomatic Kotlin language patterns wherever possible instead of custom APIs.** @@ -61,14 +53,14 @@ import kotlin.time.Duration.Companion.seconds import kotlin.time.Duration.Companion.minutes import kotlin.time.Duration.Companion.hours -// Activity with timeouts using existing DSL builders +// Activity with timeouts using KOptions val result = KWorkflow.executeActivity( "ProcessOrder", - ActivityOptions { - startToCloseTimeout = 30.seconds - scheduleToCloseTimeout = 5.minutes + KActivityOptions( + startToCloseTimeout = 30.seconds, + scheduleToCloseTimeout = 5.minutes, heartbeatTimeout = 10.seconds - }, + ), orderData ) @@ -76,7 +68,186 @@ val result = KWorkflow.executeActivity( delay(1.hours) ``` -> **Note:** The `ActivityOptions { }` DSL already exists in the `temporal-kotlin` module. The `Delay` interface intercepts standard `delay()` calls and routes them through Temporal's deterministic timer. +> **Note:** The `Delay` interface intercepts standard `delay()` calls and routes them through Temporal's deterministic timer. + +### KOptions Classes + +For a fully idiomatic Kotlin experience, the SDK provides dedicated `KOptions` data classes that accept `kotlin.time.Duration` directly, use named parameters with default values, and follow immutable data class patterns: + +```kotlin +import kotlin.time.Duration.Companion.seconds +import kotlin.time.Duration.Companion.minutes + +// KActivityOptions - Kotlin-native activity configuration +val result = KWorkflow.executeActivity( + GreetingActivities::composeGreeting, + KActivityOptions( + startToCloseTimeout = 30.seconds, + scheduleToCloseTimeout = 5.minutes, + heartbeatTimeout = 10.seconds, + retryOptions = KRetryOptions( + initialInterval = 1.seconds, + maximumAttempts = 3 + ) + ), + "Hello", "World" +) + +// KLocalActivityOptions +val validated = KWorkflow.executeLocalActivity( + ValidationActivities::validate, + KLocalActivityOptions( + startToCloseTimeout = 5.seconds, + localRetryThreshold = 10.seconds + ), + input +) + +// KChildWorkflowOptions +val childResult = KWorkflow.executeChildWorkflow( + ChildWorkflow::process, + KChildWorkflowOptions( + workflowId = "child-123", + workflowExecutionTimeout = 1.hours, + retryOptions = KRetryOptions(maximumAttempts = 3) + ), + data +) + +// KWorkflowOptions - for client workflow execution +val result = client.executeWorkflow( + OrderWorkflow::processOrder, + KWorkflowOptions( + workflowId = "order-123", + taskQueue = "orders", + workflowExecutionTimeout = 24.hours, + workflowRunTimeout = 1.hours + ), + order +) +``` + +**KOptions Data Classes:** + +```kotlin +/** + * Kotlin-native activity options with Duration support. + * All timeout properties accept kotlin.time.Duration directly. + */ +data class KActivityOptions( + val startToCloseTimeout: Duration? = null, + val scheduleToCloseTimeout: Duration? = null, + val scheduleToStartTimeout: Duration? = null, + val heartbeatTimeout: Duration? = null, + val taskQueue: String? = null, + val retryOptions: KRetryOptions? = null, + val cancellationType: ActivityCancellationType? = null, // Java default: TRY_CANCEL + val disableEagerExecution: Boolean = false +) + +/** + * Kotlin-native local activity options. + */ +data class KLocalActivityOptions( + val startToCloseTimeout: Duration? = null, + val scheduleToCloseTimeout: Duration? = null, + val localRetryThreshold: Duration? = null, + val retryOptions: KRetryOptions? = null +) + +/** + * Kotlin-native retry options. + */ +data class KRetryOptions( + val initialInterval: Duration = 1.seconds, + val backoffCoefficient: Double = 2.0, + val maximumInterval: Duration? = null, + val maximumAttempts: Int = 0, // 0 = unlimited + val doNotRetry: List = emptyList() +) + +/** + * Kotlin-native child workflow options. + * All fields are optional - null values inherit from parent workflow or use Java SDK defaults. + */ +data class KChildWorkflowOptions( + val namespace: String? = null, + val workflowId: String? = null, + val workflowIdReusePolicy: WorkflowIdReusePolicy? = null, + val workflowRunTimeout: Duration? = null, + val workflowExecutionTimeout: Duration? = null, + val workflowTaskTimeout: Duration? = null, + val taskQueue: String? = null, + val retryOptions: KRetryOptions? = null, + val cronSchedule: String? = null, + val parentClosePolicy: ParentClosePolicy? = null, + val memo: Map? = null, + val typedSearchAttributes: SearchAttributes? = null, + val cancellationType: ChildWorkflowCancellationType? = null, + val staticSummary: String? = null, + val staticDetails: String? = null, + val priority: Priority? = null +) + +/** + * Kotlin-native workflow options for client execution. + * workflowId and taskQueue are optional - if not provided, will use defaults or generate UUID. + */ +data class KWorkflowOptions( + val workflowId: String? = null, + val workflowIdReusePolicy: WorkflowIdReusePolicy? = null, + val workflowIdConflictPolicy: WorkflowIdConflictPolicy? = null, + val workflowRunTimeout: Duration? = null, + val workflowExecutionTimeout: Duration? = null, + val workflowTaskTimeout: Duration? = null, + val taskQueue: String? = null, + val retryOptions: KRetryOptions? = null, + val cronSchedule: String? = null, + val memo: Map? = null, + val typedSearchAttributes: SearchAttributes? = null, + val disableEagerExecution: Boolean = true, + val startDelay: Duration? = null, + val staticSummary: String? = null, + val staticDetails: String? = null, + val priority: Priority? = null +) +``` + +**KOptions vs DSL Builders:** + +The Kotlin SDK provides two approaches for configuring options: + +1. **KOptions (Recommended)** - Native Kotlin data classes designed for the Kotlin SDK +2. **DSL Builders** - Extension functions on Java SDK builders, provided as a stopgap for using Kotlin with the Java SDK + +> **Important:** When using the Kotlin SDK (`KWorkflow`, `KWorkflowClient`, etc.), always use KOptions classes. The DSL builders (`ActivityOptions { }`, `WorkflowOptions { }`, etc.) exist only for compatibility when using Kotlin with the Java SDK directly and should not be used with the Kotlin SDK APIs. + +| Aspect | DSL Builder (Java SDK interop) | KOptions (Kotlin SDK) | +|--------|--------------------------------|------------------------| +| Duration | Requires `.toJava()` conversion | Native `kotlin.time.Duration` | +| Syntax | `setStartToCloseTimeout(...)` | `startToCloseTimeout = ...` | +| Defaults | Must check Java defaults | Visible in constructor | +| Immutability | Mutable builder | Immutable data class | +| Copy | Manual rebuild | `copy()` function | +| IDE | Limited autocomplete | Full parameter hints | +| Usage | Java SDK only | Kotlin SDK | + +**Example - Copying with Modifications:** + +```kotlin +val baseOptions = KActivityOptions( + startToCloseTimeout = 30.seconds, + retryOptions = KRetryOptions(maximumAttempts = 3) +) + +// Create variant with different timeout +val longRunningOptions = baseOptions.copy( + startToCloseTimeout = 5.minutes, + heartbeatTimeout = 30.seconds +) +``` + +> **Implementation Note:** KOptions classes internally convert to Java SDK options. The conversion happens once when the activity/workflow is scheduled, so there's no runtime overhead during workflow execution. ### Null Safety @@ -104,14 +275,6 @@ interface KWorkflowInfo { // ... other properties } -// KActivityContext - activity execution context -interface KActivityContext { - val info: KActivityInfo - suspend fun heartbeat(details: Any? = null) - fun doNotCompleteOnReturn() - // ... other methods -} - // KActivityInfo - nullable instead of Optional interface KActivityInfo { val activityId: String @@ -122,13 +285,16 @@ interface KActivityInfo { // ... other properties } -// Access via KWorkflow / KActivity +// Access via KWorkflow / KActivity objects val info: KWorkflowInfo = KWorkflow.getInfo() val parentId: String? = info.parentWorkflowId +// KActivity.getContext() returns KActivityContext (matches Java's Activity.getExecutionContext()) val context: KActivityContext = KActivity.getContext() val activityInfo: KActivityInfo = context.info -context.heartbeat("progress") +context.heartbeat("progress") // Blocking version for regular activities +context.suspendHeartbeat("progress") // Suspend version for suspend activities +val logger = context.logger() // Get activity logger ``` This eliminates `.orElse(null)`, `.isPresent`, and other Optional ceremony. @@ -172,7 +338,7 @@ class GreetingWorkflowImpl : GreetingWorkflow { override suspend fun getGreeting(name: String): String { return KWorkflow.executeActivity( "composeGreeting", - ActivityOptions { startToCloseTimeout = 10.seconds }, + KActivityOptions(startToCloseTimeout = 10.seconds), "Hello", name ) } @@ -181,10 +347,10 @@ class GreetingWorkflowImpl : GreetingWorkflow { // Client call using KWorkflowClient - same pattern as activities, no stub needed val result = client.executeWorkflow( GreetingWorkflow::getGreeting, - WorkflowOptions { - workflowId = "greeting-123" + KWorkflowOptions( + workflowId = "greeting-123", taskQueue = "greetings" - }, + ), "Temporal" ) ``` @@ -230,10 +396,10 @@ class OrderWorkflowImpl : OrderWorkflowSuspend { // Client call using KWorkflowClient - same pattern, suspends for result val result = client.executeWorkflow( OrderWorkflow::processOrder, - WorkflowOptions { - workflowId = "order-123" + KWorkflowOptions( + workflowId = "order-123", taskQueue = "orders" - }, + ), order ) ``` @@ -291,9 +457,20 @@ Child workflows use the same stub-less pattern as activities: override suspend fun parentWorkflow(): String { return KWorkflow.executeChildWorkflow( ChildWorkflow::doWork, - ChildWorkflowOptions { - workflowId = "child-workflow-id" - }, + KChildWorkflowOptions(workflowId = "child-workflow-id"), + "input" + ) +} + +// With retry options +override suspend fun parentWorkflowWithRetry(): String { + return KWorkflow.executeChildWorkflow( + ChildWorkflow::doWork, + KChildWorkflowOptions( + workflowId = "child-workflow-id", + workflowExecutionTimeout = 1.hours, + retryOptions = KRetryOptions(maximumAttempts = 3) + ), "input" ) } @@ -304,14 +481,14 @@ override suspend fun parentWorkflowParallel(): String = coroutineScope { val childDeferred = async { KWorkflow.executeChildWorkflow( ChildWorkflow::doWork, - ChildWorkflowOptions { workflowId = "child-workflow-id" }, + KChildWorkflowOptions(workflowId = "child-workflow-id"), "input" ) } val activityDeferred = async { KWorkflow.executeActivity( SomeActivities::doSomething, - ActivityOptions { startToCloseTimeout = 30.seconds } + KActivityOptions(startToCloseTimeout = 30.seconds) ) } @@ -330,9 +507,7 @@ For cases where you need to interact with a child workflow (signal, query, cance override suspend fun parentWorkflowWithHandle(): String { val handle = KWorkflow.startChildWorkflow( ChildWorkflow::doWork, - ChildWorkflowOptions { - workflowId = "child-workflow-id" - }, + KChildWorkflowOptions(workflowId = "child-workflow-id"), "input" ) @@ -358,7 +533,7 @@ override suspend fun parallelChildrenWithHandles(): List = coroutineScop val handles = listOf("child-1", "child-2", "child-3").map { id -> KWorkflow.startChildWorkflow( ChildWorkflow::doWork, - ChildWorkflowOptions { workflowId = id }, + KChildWorkflowOptions(workflowId = id), "input" ) } @@ -448,26 +623,66 @@ object KWorkflow { inline fun getChildWorkflowHandle( workflowId: String ): KChildWorkflowHandle + + // --- KChildWorkflowOptions overloads (Kotlin-native) --- + + suspend fun startChildWorkflow( + workflow: KFunction2, + options: KChildWorkflowOptions, + arg: A1 + ): KChildWorkflowHandle + + suspend fun startChildWorkflow( + workflow: KFunction1, + options: KChildWorkflowOptions + ): KChildWorkflowHandle + + suspend fun startChildWorkflow( + workflow: KFunction3, + options: KChildWorkflowOptions, + arg1: A1, arg2: A2 + ): KChildWorkflowHandle + + // Overloads for executeChildWorkflow with KChildWorkflowOptions + suspend fun executeChildWorkflow( + workflow: KFunction2, + options: KChildWorkflowOptions, + arg: A1 + ): R + + suspend fun executeChildWorkflow( + workflow: KFunction1, + options: KChildWorkflowOptions + ): R + + // ... up to 6 arguments } ``` > **Note:** For simple parallel execution where you only need the result, use standard `coroutineScope { async { executeChildWorkflow(...) } }`. Use `startChildWorkflow` only when you need to interact with the child workflow while it's running. -**ChildWorkflowOptions:** +**KChildWorkflowOptions:** ```kotlin -ChildWorkflowOptions { - workflowId = "child-workflow-id" - taskQueue = "child-queue" // Optional: defaults to parent's task queue - workflowExecutionTimeout = 1.hours - workflowRunTimeout = 30.minutes - retryOptions = RetryOptions { - initialInterval = 1.seconds +// All fields are optional - null values inherit from parent workflow or use Java SDK defaults +KChildWorkflowOptions( + workflowId = "child-workflow-id", + taskQueue = "child-queue", // Optional: defaults to parent's task queue + workflowExecutionTimeout = 1.hours, + workflowRunTimeout = 30.minutes, + retryOptions = KRetryOptions( + initialInterval = 1.seconds, maximumAttempts = 3 - } - parentClosePolicy = ParentClosePolicy.TERMINATE - cancellationType = ChildWorkflowCancellationType.WAIT_CANCELLATION_COMPLETED -} + ), + parentClosePolicy = ParentClosePolicy.PARENT_CLOSE_POLICY_TERMINATE, // Optional + cancellationType = ChildWorkflowCancellationType.WAIT_CANCELLATION_COMPLETED // Optional +) + +// Options are optional - use default KChildWorkflowOptions() when not specified +val result = KWorkflow.executeChildWorkflow( + ChildWorkflow::processData, + inputData // No options needed for simple cases +) ``` ### Timers and Delays @@ -486,7 +701,7 @@ override suspend fun workflowWithTimer(): String { Use standard `coroutineScope { async { } }` for parallel execution: ```kotlin -val options = ActivityOptions { startToCloseTimeout = 30.seconds } +val options = KActivityOptions(startToCloseTimeout = 30.seconds) override suspend fun parallelWorkflow(items: List): List = coroutineScope { // Process all items in parallel using standard Kotlin patterns @@ -542,7 +757,164 @@ override suspend fun workflowWithTimeout(): String { } ``` -> **Note:** Versioning (`KWorkflow.getVersion`), continue-as-new (`KWorkflow.continueAsNew`), side effects (`KWorkflow.sideEffect`), and search attributes (`KWorkflow.upsertSearchAttributes`) use the same patterns as the Java SDK. +### Logging + +Use `KWorkflow.logger()` for workflow-safe logging: + +```kotlin +// Get logger using workflow type as name +val log = KWorkflow.logger() +log.info("Processing order") + +// Or with custom logger name +val customLog = KWorkflow.logger("my.custom.logger") + +// Or with class +val classLog = KWorkflow.logger(MyWorkflowImpl::class.java) +``` + +> **Note:** Versioning (`KWorkflow.getVersion`), side effects (`KWorkflow.sideEffect`), and search attributes (`KWorkflow.upsertSearchAttributes`) use the same patterns as the Java SDK. + +### Continue-As-New + +Continue-as-new completes the current workflow execution and immediately starts a new execution with fresh event history. This is essential for: + +- **Preventing history growth**: Long-running workflows accumulate event history which can impact performance. Continue-as-new resets the history. +- **Batch processing**: Process a batch of items, then continue-as-new with the next batch offset. +- **Implementing loops**: Instead of infinite loops that accumulate history, use continue-as-new. + +```kotlin +// Basic continue-as-new - same workflow type, inherit all options +KWorkflow.continueAsNew(nextBatchId, newOffset) + +// With modified options +KWorkflow.continueAsNew( + KContinueAsNewOptions( + taskQueue = "high-priority-queue", + workflowRunTimeout = 2.hours + ), + nextBatchId, newOffset +) + +// Continue as different workflow type (for versioning/migration) +KWorkflow.continueAsNew( + "OrderProcessorV2", + KContinueAsNewOptions(taskQueue = "orders-v2"), + migratedState +) + +// Type-safe continue as different workflow using method reference +KWorkflow.continueAsNew( + OrderProcessorV2::process, + KContinueAsNewOptions(), + migratedState +) +``` + +**KContinueAsNewOptions:** + +```kotlin +/** + * Options for continuing a workflow as a new execution. + * All fields are optional - null values inherit from the current workflow. + */ +data class KContinueAsNewOptions( + val workflowRunTimeout: Duration? = null, + val taskQueue: String? = null, + val retryOptions: KRetryOptions? = null, + val workflowTaskTimeout: Duration? = null, + val memo: Map? = null, + val typedSearchAttributes: SearchAttributes? = null, + val contextPropagators: List? = null +) +``` + +**Batch Processing Pattern:** + +```kotlin +class BatchProcessorImpl : BatchProcessor { + override suspend fun processBatches(startOffset: Int) { + val batchSize = 100 + val items = KWorkflow.executeActivity( + DataActivities::fetchBatch, + KActivityOptions(startToCloseTimeout = 1.minutes), + startOffset, batchSize + ) + + if (items.isEmpty()) { + return // All done, workflow completes normally + } + + // Process items... + for (item in items) { + KWorkflow.executeActivity( + DataActivities::processItem, + KActivityOptions(startToCloseTimeout = 30.seconds), + item + ) + } + + // Continue with the next batch + KWorkflow.continueAsNew(startOffset + batchSize) + } +} +``` + +**History Size Check Pattern:** + +```kotlin +override suspend fun execute(state: WorkflowState) { + while (true) { + // Check if history is getting too large + if (KWorkflow.getInfo().isContinueAsNewSuggested) { + KWorkflow.continueAsNew(state) + } + + // Wait for signals and process... + KWorkflow.awaitCondition { hasNewWork } + processWork() + } +} +``` + +**KWorkflow.continueAsNew API:** + +```kotlin +object KWorkflow { + /** + * Continue workflow as new with the same workflow type. + * This function never returns - it terminates the current execution. + */ + fun continueAsNew(vararg args: Any?): Nothing + + /** + * Continue workflow as new with modified options. + * Null option values inherit from the current workflow. + */ + fun continueAsNew(options: KContinueAsNewOptions, vararg args: Any?): Nothing + + /** + * Continue as a different workflow type. + * Useful for workflow versioning or migration. + */ + fun continueAsNew( + workflowType: String, + options: KContinueAsNewOptions, + vararg args: Any? + ): Nothing + + /** + * Type-safe continue as different workflow using method reference. + */ + fun continueAsNew( + workflow: KFunction<*>, + options: KContinueAsNewOptions, + vararg args: Any? + ): Nothing +} +``` + +> **Important:** `continueAsNew` never returns normally. It terminates the current workflow execution and signals Temporal to start a new execution. The return type `Nothing` indicates this in Kotlin's type system. ## Cancellation @@ -594,7 +966,7 @@ override suspend fun processOrder(order: Order): OrderResult { withContext(NonCancellable) { KWorkflow.executeActivity( OrderActivities::releaseReservation, - ActivityOptions { startToCloseTimeout = 30.seconds }, + KActivityOptions(startToCloseTimeout = 30.seconds), order ) } @@ -648,13 +1020,13 @@ For calling activities by name (useful for cross-language interop or dynamic act // Execute activity by string name - suspend function, awaits result val result = KWorkflow.executeActivity( "activityName", - ActivityOptions { - startToCloseTimeout = 30.seconds - retryOptions { - initialInterval = 1.seconds + KActivityOptions( + startToCloseTimeout = 30.seconds, + retryOptions = KRetryOptions( + initialInterval = 1.seconds, maximumAttempts = 3 - } - }, + ) + ), arg1, arg2 ) @@ -666,7 +1038,7 @@ val results = coroutineScope { } ``` -> **Note:** The `ActivityOptions { }` DSL already exists in `temporal-kotlin`. The nested `retryOptions { }` block is also supported. +> **Note:** The `ActivityOptions { }` DSL from `temporal-kotlin` exists for using Kotlin with the Java SDK directly and should not be used with Kotlin SDK APIs. ### Typed Activities (Phase 2) @@ -693,28 +1065,24 @@ interface GreetingActivities { // In workflow - direct method reference, no stub needed val greeting = KWorkflow.executeActivity( GreetingActivities::composeGreeting, // Direct reference to interface method - ActivityOptions { - startToCloseTimeout = 30.seconds - }, + KActivityOptions(startToCloseTimeout = 30.seconds), "Hello", "World" ) // Different activity, different options val result = KWorkflow.executeActivity( GreetingActivities::sendEmail, - ActivityOptions { - startToCloseTimeout = 2.minutes - retryOptions = RetryOptions { - maximumAttempts = 5 - } - }, + KActivityOptions( + startToCloseTimeout = 2.minutes, + retryOptions = KRetryOptions(maximumAttempts = 5) + ), email ) // Void activities work too KWorkflow.executeActivity( GreetingActivities::log, - ActivityOptions { startToCloseTimeout = 5.seconds }, + KActivityOptions(startToCloseTimeout = 5.seconds), "Processing started" ) ``` @@ -738,7 +1106,7 @@ override suspend fun parallelGreetings(names: List): List = coro async { KWorkflow.executeActivity( GreetingActivities::composeGreeting, - ActivityOptions { startToCloseTimeout = 10.seconds }, + KActivityOptions(startToCloseTimeout = 10.seconds), "Hello", name ) } @@ -765,7 +1133,7 @@ val (result1, result2) = coroutineScope { val result: PaymentResult = KWorkflow.executeActivity( JavaPaymentActivities::processPayment, - ActivityOptions { startToCloseTimeout = 2.minutes }, + KActivityOptions(startToCloseTimeout = 2.minutes), orderId, amount ) ``` @@ -804,6 +1172,30 @@ object KWorkflow { arg1: A1 ): R + // --- KOptions overloads (Kotlin-native) --- + + // Execute activity with KActivityOptions + suspend fun executeActivity( + activity: KFunction2, + options: KActivityOptions, + arg1: A1 + ): R + + suspend fun executeActivity( + activity: KFunction3, + options: KActivityOptions, + arg1: A1, arg2: A2 + ): R + + // ... up to 6 arguments + + // Execute local activity with KLocalActivityOptions + suspend fun executeLocalActivity( + activity: KFunction2, + options: KLocalActivityOptions, + arg1: A1 + ): R + // --- String-based overloads (untyped) --- // Execute activity by name @@ -819,6 +1211,22 @@ object KWorkflow { options: LocalActivityOptions, vararg args: Any? ): R + + // --- String-based with KOptions --- + + // Execute activity by name with KActivityOptions + suspend inline fun executeActivity( + activityName: String, + options: KActivityOptions, + vararg args: Any? + ): R + + // Execute local activity by name with KLocalActivityOptions + suspend inline fun executeLocalActivity( + activityName: String, + options: KLocalActivityOptions, + vararg args: Any? + ): R } ``` @@ -873,7 +1281,7 @@ class GreetingActivitiesImpl( // In workflow - direct method reference, no stub needed val greeting = KWorkflow.executeActivity( GreetingActivities::composeGreeting, - ActivityOptions { startToCloseTimeout = 30.seconds }, + KActivityOptions(startToCloseTimeout = 30.seconds), "Hello", "World" ) ``` @@ -920,7 +1328,7 @@ class OrderActivitiesImpl( // In workflow - use the non-suspend interface for method references val isValid = KWorkflow.executeActivity( OrderActivities::validateOrder, - ActivityOptions { startToCloseTimeout = 10.seconds }, + KActivityOptions(startToCloseTimeout = 10.seconds), order ) ``` @@ -957,22 +1365,74 @@ interface ValidationActivities { val isValid = KWorkflow.executeLocalActivity( ValidationActivities::validate, - LocalActivityOptions { - startToCloseTimeout = 5.seconds - }, + KLocalActivityOptions(startToCloseTimeout = 5.seconds), input ) val sanitized = KWorkflow.executeLocalActivity( ValidationActivities::sanitize, - LocalActivityOptions { - startToCloseTimeout = 1.seconds - }, + KLocalActivityOptions(startToCloseTimeout = 1.seconds), input ) ``` -> **Note:** Activity context, heartbeating, and async activity completion use the same APIs as the Java SDK (`Activity.getExecutionContext()`, `ActivityCompletionClient`, etc.). +### KActivity API + +`KActivity.getContext()` returns a `KActivityContext`, matching Java SDK's `Activity.getExecutionContext()` pattern: + +```kotlin +// In activity implementation + +// Get activity context (matches Java's Activity.getExecutionContext()) +val context = KActivity.getContext() + +// Get activity info +val info = context.info +println("Activity ${info.activityType}, attempt ${info.attempt}") + +// Heartbeat for long-running activities +// Use heartbeat() in regular activities +context.heartbeat(progressDetails) + +// Use suspendHeartbeat() in suspend activities for non-blocking operation +context.suspendHeartbeat(progressDetails) + +// Get heartbeat details from previous attempt (for retry scenarios) +val previousProgress: Int? = context.getHeartbeatDetails() + +// Logging - use activity logger for proper log context +val log = context.logger() // Uses activity type as logger name +log.info("Processing item") + +// Or with custom logger name +val customLog = context.logger("custom.logger") + +// Mark activity for async completion +context.doNotCompleteOnReturn() +val taskToken = context.taskToken +``` + +**KActivityContext Interface:** + +```kotlin +interface KActivityContext { + val info: KActivityInfo + fun heartbeat(details: Any? = null) + suspend fun suspendHeartbeat(details: Any? = null) + fun getHeartbeatDetails(detailsClass: Class): T? + val taskToken: ByteArray + fun doNotCompleteOnReturn() + val isDoNotCompleteOnReturn: Boolean + fun logger(): Logger + fun logger(name: String): Logger + fun logger(clazz: Class<*>): Logger +} + +// Reified extension for easier Kotlin usage +inline fun KActivityContext.getHeartbeatDetails(): T? +``` + +> **Note:** `ActivityCompletionClient` for async activity completion uses the same API as the Java SDK. ## Client API @@ -1051,6 +1511,40 @@ class KWorkflowClient( // Overloads for 2-6 arguments... + // --- KWorkflowOptions overloads (Kotlin-native) --- + + /** + * Start a workflow with KWorkflowOptions and return a handle. + */ + suspend fun startWorkflow( + workflow: KFunction1, + options: KWorkflowOptions + ): KTypedWorkflowHandle + + suspend fun startWorkflow( + workflow: KFunction2, + options: KWorkflowOptions, + arg: A1 + ): KTypedWorkflowHandle + + // Overloads for 2-6 arguments... + + /** + * Execute a workflow with KWorkflowOptions and wait for result. + */ + suspend fun executeWorkflow( + workflow: KFunction1, + options: KWorkflowOptions + ): R + + suspend fun executeWorkflow( + workflow: KFunction2, + options: KWorkflowOptions, + arg: A1 + ): R + + // Overloads for 2-6 arguments... + /** * Get a typed handle for an existing workflow by ID. * Use this to signal, query, or get results from a workflow started elsewhere. @@ -1088,6 +1582,24 @@ class KWorkflowClient( update: KFunction2, updateArg: UA1 ): Pair, UR> + + // --- KWorkflowOptions variants --- + + suspend fun signalWithStart( + workflow: KFunction2, + options: KWorkflowOptions, + workflowArg: A1, + signal: KFunction2, + signalArg: SA1 + ): KTypedWorkflowHandle + + suspend fun updateWithStart( + workflow: KFunction2, + options: KWorkflowOptions, + workflowArg: A1, + update: KFunction2, + updateArg: UA1 + ): Pair, UR> } ``` @@ -1097,21 +1609,21 @@ class KWorkflowClient( // Execute workflow and wait for result (suspend function) val result = client.executeWorkflow( GreetingWorkflow::getGreeting, - WorkflowOptions { - workflowId = "greeting-123" - taskQueue = "greeting-queue" + KWorkflowOptions( + workflowId = "greeting-123", + taskQueue = "greeting-queue", workflowExecutionTimeout = 1.hours - }, + ), "Temporal" ) // Or start async and get handle val handle = client.startWorkflow( GreetingWorkflow::getGreeting, - WorkflowOptions { - workflowId = "greeting-123" + KWorkflowOptions( + workflowId = "greeting-123", taskQueue = "greeting-queue" - }, + ), "Temporal" ) val result = handle.result() // Type inferred as String from method reference @@ -1125,10 +1637,10 @@ Atomically start a workflow and send a signal. If the workflow already exists, o // Returns KTypedWorkflowHandle - result type captured from method reference val handle = client.signalWithStart( workflow = OrderWorkflow::processOrder, - options = WorkflowOptions { - workflowId = "order-123" + options = KWorkflowOptions( + workflowId = "order-123", taskQueue = "orders" - }, + ), workflowArg = order, signal = OrderWorkflow::updatePriority, signalArg = Priority.HIGH @@ -1149,10 +1661,10 @@ Atomically start a workflow and send an update. If the workflow already exists, // - updateResult typed by update method return type val (handle, updateResult: Boolean) = client.updateWithStart( workflow = OrderWorkflow::processOrder, - options = WorkflowOptions { - workflowId = "order-123" + options = KWorkflowOptions( + workflowId = "order-123", taskQueue = "orders" - }, + ), workflowArg = order, update = OrderWorkflow::addItem, updateArg = newItem @@ -1559,6 +2071,12 @@ val client = WorkflowClient(service) { | `Duration.ofSeconds(30)` | `30.seconds` | | **Parallel Execution** | | | `Async.function(...)` + `Promise.get()` | `coroutineScope { async { ... } }` + `awaitAll()` | +| **Options** | | +| `ActivityOptions.newBuilder()...build()` | `KActivityOptions(...)` | +| `LocalActivityOptions.newBuilder()...build()` | `KLocalActivityOptions(...)` | +| `ChildWorkflowOptions.newBuilder()...build()` | `KChildWorkflowOptions(...)` | +| `WorkflowOptions.newBuilder()...build()` | `KWorkflowOptions(...)` | +| `RetryOptions.newBuilder()...build()` | `KRetryOptions(...)` | ### Before (Java) @@ -1595,7 +2113,7 @@ class GreetingWorkflowImpl : GreetingWorkflow { override suspend fun getGreeting(name: String): String { return KWorkflow.executeActivity( "greet", - ActivityOptions { startToCloseTimeout = 30.seconds }, + KActivityOptions(startToCloseTimeout = 30.seconds), name ) } @@ -1611,7 +2129,7 @@ override suspend fun processOrder(order: Order): String { // JavaActivities is a Java @ActivityInterface - no stub needed return KWorkflow.executeActivity( JavaActivities::process, - ActivityOptions { startToCloseTimeout = 30.seconds }, + KActivityOptions(startToCloseTimeout = 30.seconds), order ) } @@ -1622,11 +2140,94 @@ Java workflows can be invoked from Kotlin clients: ```kotlin val result = client.executeWorkflow( JavaWorkflowInterface::execute, - WorkflowOptions { taskQueue = "java-queue" }, + KWorkflowOptions(workflowId = "java-workflow", taskQueue = "java-queue"), input ) ``` +## Java SDK API Parity + +This section documents the intentional differences between Java and Kotlin SDK APIs, and identifies remaining gaps. + +### APIs Not Needed in Kotlin + +The following Java SDK APIs are **not needed** in the Kotlin SDK due to language differences: + +| Java SDK API | Reason Not Needed | +|--------------|-------------------| +| `Workflow.sleep(Duration)` | Use `kotlinx.coroutines.delay()` - intercepted by dispatcher | +| `Workflow.newTimer(Duration)` | Use `async { delay(duration) }` for racing timers | +| `Workflow.wrap(Exception)` | Kotlin has no checked exceptions - not needed | +| `Activity.wrap(Throwable)` | Kotlin has no checked exceptions - not needed | +| `Workflow.newCancellationScope(...)` | Use Kotlin's `coroutineScope { }` with structured concurrency | +| `Workflow.newDetachedCancellationScope(...)` | Use `supervisorScope { }` or launch in parent scope | +| `Workflow.newWorkflowLock()` | Not needed - cooperative coroutines don't have true concurrency | +| `Workflow.newWorkflowSemaphore(...)` | Use structured concurrency patterns (e.g., `chunked().map { async }.awaitAll()`) | +| `Workflow.newQueue(...)` / `newWorkflowQueue(...)` | Use `mutableListOf` + `awaitCondition` - no concurrent access in suspend model | +| `Workflow.newPromise()` / `newFailedPromise(...)` | Use `CompletableDeferred` from kotlinx.coroutines | +| `Workflow.setDefaultActivityOptions(...)` | Pass `KActivityOptions` per call, or define shared `val defaultOptions` | +| `Workflow.setActivityOptions(...)` | Pass options per call | +| `Workflow.applyActivityOptions(...)` | Pass options per call | +| `Workflow.setDefaultLocalActivityOptions(...)` | Pass `KLocalActivityOptions` per call | +| `Workflow.applyLocalActivityOptions(...)` | Pass options per call | + +### Cancellation Support + +Kotlin coroutine cancellation is fully integrated with Temporal workflow cancellation: + +- Workflow cancellation triggers `coroutineScope.cancel(CancellationException(...))` +- Cancellation propagates to all child coroutines (activities, timers, child workflows) +- Activities use `suspendCancellableCoroutine` with `invokeOnCancellation` to cancel via Temporal +- Use standard Kotlin patterns: `try/finally`, `use { }`, `invokeOnCompletion` + +### Remaining Gaps (Future Work) + +The following Java SDK APIs do not yet have Kotlin equivalents: + +#### High Priority + +| Java SDK API | Use Case | +|--------------|----------| +| `Workflow.getSearchAttribute(name)` | Read search attributes | +| `Workflow.getSearchAttributes()` | Read all search attributes | +| `Workflow.getTypedSearchAttributes()` | Read typed search attributes | +| `Workflow.upsertSearchAttributes(...)` | Update search attributes | +| `Workflow.upsertTypedSearchAttributes(...)` | Update typed search attributes | +| `Workflow.getMemo(key, class)` | Read workflow memo | +| `Workflow.upsertMemo(...)` | Update workflow memo | +| `Workflow.getLastCompletionResult(class)` | For cron/continue-as-new workflows | +| `Workflow.getPreviousRunFailure()` | For cron/continue-as-new workflows | +| `Workflow.isReplaying()` | Conditional logging/debugging | + +#### Medium Priority + +| Java SDK API | Use Case | +|--------------|----------| +| `Workflow.mutableSideEffect(...)` | Cache expensive computations across replays | +| `Workflow.getCurrentUpdateInfo()` | Get current update context | +| `Workflow.isEveryHandlerFinished()` | Check all handlers completed | +| `Workflow.setCurrentDetails(...)` | Set workflow details for UI | +| `Workflow.getCurrentDetails()` | Get workflow details | +| `Workflow.getMetricsScope()` | Custom metrics | +| `Workflow.registerListener(...)` | Register signal/query listeners dynamically | + +#### Lower Priority / Deferred + +| Java SDK API | Notes | +|--------------|-------| +| `Workflow.retry(...)` | Can implement in user code | +| `Workflow.newNexusServiceStub(...)` | Nexus support - separate project | +| `Workflow.startNexusOperation(...)` | Nexus support - separate project | +| `Workflow.getInstance()` | Advanced use case | + +### KActivityInfo Gaps + +| Java ActivityInfo Field | Status | +|------------------------|--------| +| `workflowType` | Missing - workflow type that called the activity | +| `currentAttemptScheduledTimestamp` | Missing - current attempt schedule time | +| `retryOptions` | Missing - activity retry options | + ## Complete Example This example uses Option A (pure Kotlin with suspend interfaces). @@ -1754,13 +2355,13 @@ class OrderWorkflowImpl : OrderWorkflow { override val progress get() = _progress // Reusable options for common cases - private val defaultOptions = ActivityOptions { - startToCloseTimeout = 30.seconds - retryOptions = RetryOptions { - initialInterval = 1.seconds + private val defaultOptions = KActivityOptions( + startToCloseTimeout = 30.seconds, + retryOptions = KRetryOptions( + initialInterval = 1.seconds, maximumAttempts = 3 - } - } + ) + ) override suspend fun processOrder(order: Order): OrderResult { _order = order @@ -1784,7 +2385,7 @@ class OrderWorkflowImpl : OrderWorkflow { _progress = 10 val isValid = KWorkflow.executeActivity( OrderActivities::validateOrder, - ActivityOptions { startToCloseTimeout = 10.seconds }, + KActivityOptions(startToCloseTimeout = 10.seconds), order ) if (!isValid) { @@ -1814,13 +2415,13 @@ class OrderWorkflowImpl : OrderWorkflow { val charged = withTimeout(2.minutes) { KWorkflow.executeActivity( OrderActivities::chargePayment, - ActivityOptions { - startToCloseTimeout = 2.minutes - retryOptions { - initialInterval = 5.seconds + KActivityOptions( + startToCloseTimeout = 2.minutes, + retryOptions = KRetryOptions( + initialInterval = 5.seconds, maximumAttempts = 5 - } - }, + ) + ), order ) } @@ -1942,10 +2543,10 @@ fun main() = runBlocking { // Start workflow and get handle val handle = client.startWorkflow( OrderWorkflow::processOrder, - WorkflowOptions { - workflowId = workflowId + KWorkflowOptions( + workflowId = workflowId, taskQueue = "orders" - }, + ), order ) println("Started workflow: ${handle.workflowId}") From 956c9a9074a2314d64102cf15cc206b278024a90 Mon Sep 17 00:00:00 2001 From: Maxim Fateev Date: Fri, 2 Jan 2026 12:39:12 -0800 Subject: [PATCH 12/83] Update docs to reflect Phase 2 completion and retry implementation - Mark Phase 2 as complete in implementation-plan.md - Add KWorkflow.retry() to implemented APIs - Update sdk-api.md to mark retry as implemented --- kotlin/implementation-plan.md | 81 +++++++++++++++++++++-------------- kotlin/sdk-api.md | 2 +- 2 files changed, 49 insertions(+), 34 deletions(-) diff --git a/kotlin/implementation-plan.md b/kotlin/implementation-plan.md index 73d1551..38f240f 100644 --- a/kotlin/implementation-plan.md +++ b/kotlin/implementation-plan.md @@ -29,43 +29,58 @@ - ✅ Suspend function detection in registration ### 1.5 Signals, Queries, Updates -- ✅ Signal handler registration with suspend support +- ✅ Signal handler registration (annotation-based and dynamic) with suspend support - ✅ Query methods (annotation-based and dynamic) -- ✅ Update methods with validators +- ✅ Update methods (annotation-based only) --- -## Phase 2: Typed APIs & Full Feature Set - -### 2.1 Typed Activity Execution -- `KFunction`-based `executeActivity()` overloads -- Type extraction from method references -- Local activity support with `executeLocalActivity()` - -### 2.2 Typed Child Workflow Execution -- `executeChildWorkflow()` with method references -- `startChildWorkflow()` returning `KChildWorkflowHandle` -- `getChildWorkflowHandle()` for existing child workflows -- `KChildWorkflowHandle` interface (signal, cancel, result) - -### 2.3 Client API -- `KWorkflowClient` - Kotlin client with suspend functions and DSL constructor -- `KWorkflowHandle` - typed handle for signals/queries/updates -- `KTypedWorkflowHandle` - extends KWorkflowHandle with typed result -- `WorkflowHandle` - untyped handle (string-based operations) -- `KUpdateHandle` - handle for async update execution -- `startWorkflow()`, `executeWorkflow()` suspend functions -- `signalWithStart()` and `updateWithStart()` -- `getWorkflowHandle()` and `getUntypedWorkflowHandle()` - -### 2.4 Worker API -- `KWorkerFactory` - Kotlin worker factory with KotlinPlugin pre-configured -- DSL builders for worker options - -### 2.5 Kotlin Activity Wrappers -- `KActivity` object (entry point for activity APIs) -- `KActivityContext` with suspend `heartbeat()` -- `KActivityInfo` with null safety +## Phase 2: Typed APIs & Full Feature Set ✅ COMPLETE + +### 2.1 Typed Activity Execution ✅ +- ✅ `KFunction`-based `executeActivity()` overloads (0-6 args) +- ✅ Type extraction from method references +- ✅ Local activity support with `executeLocalActivity()` +- ✅ Suspend activity support with `registerSuspendActivities()` + +### 2.2 Typed Child Workflow Execution ✅ +- ✅ Typed `executeChildWorkflow()` with method references +- ✅ `startChildWorkflow()` returning `KChildWorkflowHandle` +- ✅ `getChildWorkflowHandle()` for existing child workflows +- ✅ `KChildWorkflowHandle` interface (signal, cancel, result) +- ✅ Optional `KChildWorkflowOptions` (uses defaults when omitted) + +### 2.3 Update Enhancements +- Dynamic update handler registration (`registerUpdateHandler`, `registerDynamicUpdateHandler`) - TODO +- ✅ Update validator support (`@UpdateValidatorMethod`) - Uses Java SDK annotations + +### 2.4 Client API ✅ +- ✅ `KWorkflowClient` - Kotlin client with suspend functions and DSL constructor +- ✅ `KWorkflowHandle` - typed handle for signals/queries/updates +- ✅ `KTypedWorkflowHandle` - extends KWorkflowHandle with typed result +- ✅ `WorkflowHandle` - untyped handle (string-based operations) +- `KUpdateHandle` - handle for async update execution - TODO +- ✅ `startWorkflow()`, `executeWorkflow()` suspend functions (0-6 args) +- ✅ `signalWithStart()` +- `updateWithStart()` - TODO +- ✅ `getWorkflowHandle()` and `getUntypedWorkflowHandle()` + +### 2.5 Worker API ✅ +- ✅ `KWorkerFactory` - Kotlin worker factory with KotlinPlugin pre-configured +- ✅ DSL builders for worker options + +### 2.6 Kotlin Activity API ✅ +- ✅ `KActivity` object (entry point for activity APIs) +- ✅ `KActivity.getInfo()`, `heartbeat()`, `suspendHeartbeat()` methods +- ✅ `KActivity.logger()` for idiomatic logging +- ✅ `KActivityInfo` with null safety +- ✅ Suspend activity support via `SuspendActivityWrapper` + +### 2.7 Kotlin Workflow API ✅ +- ✅ `KWorkflow.logger()` for idiomatic logging +- ✅ `KWorkflow.async {}` for eager parallel execution +- ✅ `KWorkflow.continueAsNew()` with `KContinueAsNewOptions` +- ✅ `KWorkflow.retry()` for workflow-level retry with exponential backoff --- diff --git a/kotlin/sdk-api.md b/kotlin/sdk-api.md index fc482a5..0935dab 100644 --- a/kotlin/sdk-api.md +++ b/kotlin/sdk-api.md @@ -2215,7 +2215,7 @@ The following Java SDK APIs do not yet have Kotlin equivalents: | Java SDK API | Notes | |--------------|-------| -| `Workflow.retry(...)` | Can implement in user code | +| `Workflow.retry(...)` | ✅ Implemented as `KWorkflow.retry()` | | `Workflow.newNexusServiceStub(...)` | Nexus support - separate project | | `Workflow.startNexusOperation(...)` | Nexus support - separate project | | `Workflow.getInstance()` | Advanced use case | From 7e2cb868023b294e249c8291b088805995cf777a Mon Sep 17 00:00:00 2001 From: Maxim Fateev Date: Fri, 2 Jan 2026 13:18:48 -0800 Subject: [PATCH 13/83] Update documentation to reflect current implementation status - Mark KUpdateHandle as implemented in implementation-plan.md - Update sdk-api.md Java SDK API Parity section with accurate status - Most "High Priority" and "Medium Priority" gaps are now implemented - Reorganize into "Implemented APIs" and "Remaining Gaps" sections - Clarify KWorkerFactory vs unified architecture in sdk-implementation.md - Remove duplicate phases section from sdk-proposal.md --- kotlin/implementation-plan.md | 4 +- kotlin/sdk-api.md | 82 ++++++++++++++++++----------------- kotlin/sdk-implementation.md | 31 +++++++++++-- kotlin/sdk-proposal.md | 8 ---- 4 files changed, 73 insertions(+), 52 deletions(-) diff --git a/kotlin/implementation-plan.md b/kotlin/implementation-plan.md index 38f240f..60d7095 100644 --- a/kotlin/implementation-plan.md +++ b/kotlin/implementation-plan.md @@ -59,10 +59,10 @@ - ✅ `KWorkflowHandle` - typed handle for signals/queries/updates - ✅ `KTypedWorkflowHandle` - extends KWorkflowHandle with typed result - ✅ `WorkflowHandle` - untyped handle (string-based operations) -- `KUpdateHandle` - handle for async update execution - TODO +- ✅ `KUpdateHandle` - handle for async update execution - ✅ `startWorkflow()`, `executeWorkflow()` suspend functions (0-6 args) - ✅ `signalWithStart()` -- `updateWithStart()` - TODO +- `updateWithStart()` - TODO (lower priority) - ✅ `getWorkflowHandle()` and `getUntypedWorkflowHandle()` ### 2.5 Worker API ✅ diff --git a/kotlin/sdk-api.md b/kotlin/sdk-api.md index 0935dab..ac5df21 100644 --- a/kotlin/sdk-api.md +++ b/kotlin/sdk-api.md @@ -2180,45 +2180,49 @@ Kotlin coroutine cancellation is fully integrated with Temporal workflow cancell - Activities use `suspendCancellableCoroutine` with `invokeOnCancellation` to cancel via Temporal - Use standard Kotlin patterns: `try/finally`, `use { }`, `invokeOnCompletion` -### Remaining Gaps (Future Work) - -The following Java SDK APIs do not yet have Kotlin equivalents: - -#### High Priority - -| Java SDK API | Use Case | -|--------------|----------| -| `Workflow.getSearchAttribute(name)` | Read search attributes | -| `Workflow.getSearchAttributes()` | Read all search attributes | -| `Workflow.getTypedSearchAttributes()` | Read typed search attributes | -| `Workflow.upsertSearchAttributes(...)` | Update search attributes | -| `Workflow.upsertTypedSearchAttributes(...)` | Update typed search attributes | -| `Workflow.getMemo(key, class)` | Read workflow memo | -| `Workflow.upsertMemo(...)` | Update workflow memo | -| `Workflow.getLastCompletionResult(class)` | For cron/continue-as-new workflows | -| `Workflow.getPreviousRunFailure()` | For cron/continue-as-new workflows | -| `Workflow.isReplaying()` | Conditional logging/debugging | - -#### Medium Priority - -| Java SDK API | Use Case | -|--------------|----------| -| `Workflow.mutableSideEffect(...)` | Cache expensive computations across replays | -| `Workflow.getCurrentUpdateInfo()` | Get current update context | -| `Workflow.isEveryHandlerFinished()` | Check all handlers completed | -| `Workflow.setCurrentDetails(...)` | Set workflow details for UI | -| `Workflow.getCurrentDetails()` | Get workflow details | -| `Workflow.getMetricsScope()` | Custom metrics | -| `Workflow.registerListener(...)` | Register signal/query listeners dynamically | - -#### Lower Priority / Deferred - -| Java SDK API | Notes | -|--------------|-------| -| `Workflow.retry(...)` | ✅ Implemented as `KWorkflow.retry()` | -| `Workflow.newNexusServiceStub(...)` | Nexus support - separate project | -| `Workflow.startNexusOperation(...)` | Nexus support - separate project | -| `Workflow.getInstance()` | Advanced use case | +### Implemented APIs (Java SDK Parity) + +The following Java SDK workflow APIs have Kotlin equivalents in `KWorkflow`: + +#### Search Attributes & Memo ✅ +| Java SDK API | Kotlin SDK | +|--------------|------------| +| `Workflow.getTypedSearchAttributes()` | ✅ `KWorkflow.getTypedSearchAttributes()` | +| `Workflow.upsertTypedSearchAttributes(...)` | ✅ `KWorkflow.upsertTypedSearchAttributes()` | +| `Workflow.getMemo(key, class)` | ✅ `KWorkflow.getMemo()` | +| `Workflow.upsertMemo(...)` | ✅ `KWorkflow.upsertMemo()` | + +#### Workflow State & Context ✅ +| Java SDK API | Kotlin SDK | +|--------------|------------| +| `Workflow.getLastCompletionResult(class)` | ✅ `KWorkflow.getLastCompletionResult()` | +| `Workflow.getPreviousRunFailure()` | ✅ `KWorkflow.getPreviousRunFailure()` | +| `Workflow.isReplaying()` | ✅ `KWorkflow.isReplaying()` | +| `Workflow.getCurrentUpdateInfo()` | ✅ `KWorkflow.getCurrentUpdateInfo()` | +| `Workflow.isEveryHandlerFinished()` | ✅ `KWorkflow.isEveryHandlerFinished()` | +| `Workflow.setCurrentDetails(...)` | ✅ `KWorkflow.setCurrentDetails()` | +| `Workflow.getCurrentDetails()` | ✅ `KWorkflow.getCurrentDetails()` | +| `Workflow.getMetricsScope()` | ✅ `KWorkflow.getMetricsScope()` | + +#### Side Effects & Utilities ✅ +| Java SDK API | Kotlin SDK | +|--------------|------------| +| `Workflow.sideEffect(...)` | ✅ `KWorkflow.sideEffect()` | +| `Workflow.mutableSideEffect(...)` | ✅ `KWorkflow.mutableSideEffect()` | +| `Workflow.getVersion(...)` | ✅ `KWorkflow.getVersion()` | +| `Workflow.retry(...)` | ✅ `KWorkflow.retry()` | +| `Workflow.randomUUID()` | ✅ `KWorkflow.randomUUID()` | +| `Workflow.newRandom()` | ✅ `KWorkflow.newRandom()` | + +### Remaining Gaps + +| Java SDK API | Status | +|--------------|--------| +| `Workflow.registerListener(...)` | Dynamic signal/query/update handler registration - TODO | +| `Workflow.newNexusServiceStub(...)` | Nexus support - deferred to separate project | +| `Workflow.startNexusOperation(...)` | Nexus support - deferred to separate project | +| `Workflow.getInstance()` | Advanced use case - low priority | +| `KWorkflowClient.updateWithStart()` | Client API - TODO | ### KActivityInfo Gaps diff --git a/kotlin/sdk-implementation.md b/kotlin/sdk-implementation.md index 6733c37..f9a7dc5 100644 --- a/kotlin/sdk-implementation.md +++ b/kotlin/sdk-implementation.md @@ -6,8 +6,8 @@ For public API and developer experience, see [sdk-api.md](./sdk-api.md). ## Phases -* **Phase 1 (COMPLETE)** - Coroutine-based workflows, untyped activity/child workflow stubs, pluggable WorkflowImplementationFactory, core Kotlin idioms (Duration, null safety, KWorkflowInfo), signals/queries/updates (annotation + dynamic handlers), standard `delay()` and `coroutineScope { async { } }` support -* **Phase 2** - Typed activity stubs, typed child workflow stubs, KChildWorkflowHandle, KActivity/KActivityInfo/KActivityContext wrappers, client & worker API (KWorkflowClient, KWorkerFactory, KWorkflowHandle, startWorkflow, signalWithStart, etc.) +* **Phase 1 (COMPLETE)** - Coroutine-based workflows, untyped activity/child workflow execution, pluggable WorkflowImplementationFactory, core Kotlin idioms (Duration, null safety, KWorkflowInfo), signals/queries (annotation + dynamic handlers), updates (annotation-based), standard `delay()` and `coroutineScope { async { } }` support +* **Phase 2** - Typed activity execution, typed child workflow execution, KChildWorkflowHandle, dynamic update handlers, update validators, KActivity/KActivityInfo/KActivityContext wrappers, client & worker API (KWorkflowClient, KWorkerFactory, KWorkflowHandle, startWorkflow, signalWithStart, etc.) * **Phase 3** - Interceptor interfaces, testing framework > **Note:** Nexus support is a separate project and will be addressed independently. @@ -147,12 +147,37 @@ The SDK uses a **single unified `WorkerFactory`** that supports both Java and Ko | Benefit | Description | |---------|-------------| -| Single worker type | No need for separate `KotlinWorkerFactory` | +| Single worker type | No need for separate worker infrastructure | | Mixed workflows | Java and Kotlin workflows on the same task queue | | Gradual migration | Convert workflows one at a time | | No Kotlin in Java SDK | All coroutine code stays in `temporal-kotlin` | | Per-instance execution | Dispatcher is per workflow instance, not per worker | +### KWorkerFactory vs Unified Architecture + +The unified worker architecture describes the **internal implementation**—how Java and Kotlin workflows coexist in the same worker. The **API surface** provides two ways to configure this: + +| API | Use Case | What It Does | +|-----|----------|--------------| +| `KWorkerFactory` | Pure Kotlin applications | Convenience wrapper that creates `WorkerFactory` with `KotlinPlugin` pre-configured | +| `WorkerFactory` + `KotlinPlugin` | Mixed Java/Kotlin or Java-main apps | Explicit plugin registration for more control | + +Both approaches produce the same result—a worker that can handle both Java and Kotlin workflows on the same task queue: + +```kotlin +// Option 1: KWorkerFactory (recommended for Kotlin apps) +val factory = KWorkerFactory(client) { + maxWorkflowThreadCount = 800 +} + +// Option 2: Explicit plugin (for Java-main or mixed scenarios) +val factory = WorkerFactory.newInstance(client, WorkerFactoryOptions.newBuilder() + .addPlugin(KotlinPlugin()) + .build()) +``` + +`KWorkerFactory` is not a separate worker type—it's a Kotlin-idiomatic API that wraps the standard `WorkerFactory`. Under the hood, both options use the same unified architecture with pluggable `WorkflowImplementationFactory`. + ## Java SDK Refactoring for Pluggability To support the Kotlin coroutine-based execution model, the Java SDK requires refactoring to make workflow execution pluggable. The key principle is that the Java SDK remains **completely Kotlin-agnostic**—it only provides extension points that the `temporal-kotlin` module can use. diff --git a/kotlin/sdk-proposal.md b/kotlin/sdk-proposal.md index 22aed28..95b3653 100644 --- a/kotlin/sdk-proposal.md +++ b/kotlin/sdk-proposal.md @@ -47,14 +47,6 @@ Internal architecture and implementation details including: - Decision justifications - Open questions -## Phases - -* **Phase 1** - Coroutine-based workflows, untyped activity execution, pluggable WorkflowImplementationFactory, core Kotlin idioms (Duration, null safety, standard delay/async) -* **Phase 2** - Typed activity execution with method references, signals/queries/updates, child workflows, property queries -* **Phase 3** - Testing framework - -> **Note:** Nexus support is a separate project and will be addressed independently. - ## Quick Links - [Kotlin Coroutines Prototype PR #1792](https://github.com/temporalio/sdk-java/pull/1792) From 6422b7efd7c84e4114a0a369ef7e879ead7136bd Mon Sep 17 00:00:00 2001 From: Maxim Fateev Date: Sat, 3 Jan 2026 20:17:32 -0800 Subject: [PATCH 14/83] Update docs: KWorker moved to main module - Update implementation-plan.md to reflect KWorker is now part of temporal-kotlin worker package (not testing-only) - KWorkerFactory.newWorker() now returns KWorker - Mark Phase 2 commits (4-6) as complete in test framework plan --- kotlin/implementation-plan.md | 31 +- kotlin/test-framework-implementation-plan.md | 835 +++++++++++++++++++ 2 files changed, 855 insertions(+), 11 deletions(-) create mode 100644 kotlin/test-framework-implementation-plan.md diff --git a/kotlin/implementation-plan.md b/kotlin/implementation-plan.md index 60d7095..0cca7da 100644 --- a/kotlin/implementation-plan.md +++ b/kotlin/implementation-plan.md @@ -67,6 +67,8 @@ ### 2.5 Worker API ✅ - ✅ `KWorkerFactory` - Kotlin worker factory with KotlinPlugin pre-configured +- ✅ `KWorker` - Kotlin worker wrapper with reified generics, KClass support, DSL options for workflow/activity/Nexus registration +- ✅ `KWorkerFactory.newWorker()` returns `KWorker` for idiomatic Kotlin usage - ✅ DSL builders for worker options ### 2.6 Kotlin Activity API ✅ @@ -86,19 +88,26 @@ ## Phase 3: Testing Framework & Interceptors -### 3.1 Test Environment -- Kotlin-friendly test workflow environment -- Coroutine test utilities integration +### 3.1 Test Environment ✅ COMPLETE +- ✅ `KTestActivityEnvironment` - typed executeActivity/executeLocalActivity, suspend activity support, heartbeat/cancellation testing +- ✅ `KTestWorkflowEnvironment` - worker creation (returns `KWorker`), client access, time manipulation, delayed callbacks, lifecycle management +- ✅ `KTestEnvironmentOptions` and `KTestEnvironmentOptionsBuilder` - DSL configuration +- ✅ `KTestActivityExtension` - JUnit 5 extension with parameter resolution and lifecycle management +- ✅ `KTestWorkflowExtension` - JUnit 5 extension with workflow stub injection, diagnostics on failure +- ✅ `@WorkflowInitialTime` annotation for test-specific initial time +- ✅ Time skipping utilities (`sleep()`, `registerDelayedCallback()`) +- ✅ Search attribute registration in test environment ### 3.2 Mocking Support -- Activity mocking with suspend functions -- Time skipping utilities - -### 3.3 Interceptors -- `KWorkerInterceptor` interface -- `KWorkflowInboundCallsInterceptor` with suspend functions -- `KWorkflowOutboundCallsInterceptor` -- `KActivityInboundCallsInterceptor` +- Activity mocking with suspend functions - TODO (may use existing Java mocking patterns) + +### 3.3 Interceptors (Design Complete - see sdk-api.md) +- `KWorkerInterceptor` interface with `KWorkerInterceptorBase` +- `KWorkflowInboundCallsInterceptor` with suspend functions and input/output data classes +- `KWorkflowOutboundCallsInterceptor` with full API (activities, child workflows, timers, side effects, etc.) +- `KActivityInboundCallsInterceptor` with suspend support +- Base classes for convenience (`*Base` classes) +- Examples: logging, tracing (OpenTelemetry), metrics, auth --- diff --git a/kotlin/test-framework-implementation-plan.md b/kotlin/test-framework-implementation-plan.md new file mode 100644 index 0000000..3173177 --- /dev/null +++ b/kotlin/test-framework-implementation-plan.md @@ -0,0 +1,835 @@ +# Kotlin SDK Test Framework Implementation Plan + +## Overview + +This document outlines the implementation plan for the Kotlin SDK test framework, broken down into small, incremental commits. Each commit is designed to: + +- Be as small as possible while adding testable functionality +- Make sense in isolation +- Build on previous commits without breaking them +- Have clear test scenarios + +**Total commits: 26** + +--- + +## Phase 1: Foundation (Commits 1-3) + +### Commit 1: Add temporal-kotlin-testing module structure + +**Description:** Set up the new Gradle module with dependencies. + +**Files:** +``` +temporal-kotlin-testing/ +├── build.gradle.kts +└── src/ + ├── main/kotlin/io/temporal/kotlin/testing/ + │ └── .gitkeep + └── test/kotlin/io/temporal/kotlin/testing/ + └── .gitkeep +``` + +**Changes:** +- Create `temporal-kotlin-testing` directory structure +- Add `build.gradle.kts` with dependencies: + - `api(project(":temporal-kotlin"))` + - `api("io.temporal:temporal-testing")` + - `implementation("org.junit.jupiter:junit-jupiter-api")` + - `implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core")` +- Add module to root `settings.gradle.kts` + +**Test scenarios:** +- Module compiles successfully +- Dependencies resolve correctly + +--- + +### Commit 2: Add WorkflowInitialTime annotation + +**Description:** Add annotation for per-test initial time override. + +**Files:** +``` +temporal-kotlin-testing/src/main/kotlin/io/temporal/kotlin/testing/ +└── WorkflowInitialTime.kt +``` + +**Content:** +```kotlin +@Target(AnnotationTarget.FUNCTION) +@Retention(AnnotationRetention.RUNTIME) +annotation class WorkflowInitialTime(val value: String) +``` + +**Test scenarios:** +- Annotation can be applied to functions +- Value is accessible via reflection +- Invalid ISO-8601 strings detected at parse time (not annotation time) + +--- + +### Commit 3: Add KTestEnvironmentOptions with DSL builder + +**Description:** Add configuration options with Kotlin DSL support. + +**Files:** +``` +temporal-kotlin-testing/src/main/kotlin/io/temporal/kotlin/testing/ +└── KTestEnvironmentOptions.kt +``` + +**Content:** +- `KTestEnvironmentOptionsBuilder` class with: + - `namespace: String` (default: "UnitTest") + - `initialTime: Instant?` + - `useTimeskipping: Boolean` (default: true) + - `useExternalService: Boolean` (default: false) + - `target: String?` + - `metricsScope: Scope?` + - `workerFactoryOptions {}` DSL + - `workflowClientOptions {}` DSL + - `workflowServiceStubsOptions {}` DSL + - `searchAttributes {}` nested DSL with `register(name, type)` + - `build()` method returning `TestEnvironmentOptions` +- `KTestEnvironmentOptions` class with: + - `internal val javaOptions: TestEnvironmentOptions` + - Companion: `newBuilder {}`, `getDefaultInstance()` + +**Test scenarios:** +- Builder with defaults creates valid TestEnvironmentOptions +- Each property correctly maps to Java options +- Nested DSLs (workerFactoryOptions, searchAttributes) work correctly +- `@TemporalDsl` annotation prevents DSL leakage + +--- + +## Phase 2: KWorker (Commits 4-6) ✅ COMPLETE + +> **Note:** KWorker was initially implemented in `temporal-kotlin-testing` but has since been moved to `temporal-kotlin/src/main/kotlin/io/temporal/kotlin/worker/KWorker.kt` to make it available for production use. `KWorkerFactory.newWorker()` now returns `KWorker`. + +### Commit 4: Add KWorker with workflow registration ✅ + +**Description:** Add Kotlin worker wrapper with workflow registration methods. + +**Files:** +``` +temporal-kotlin/src/main/kotlin/io/temporal/kotlin/worker/ +└── KWorker.kt +``` + +**Content:** +```kotlin +class KWorker internal constructor(val worker: Worker) { + inline fun registerWorkflowImplementationTypes() + fun registerWorkflowImplementationTypes(vararg workflowClasses: KClass<*>) + fun registerWorkflowImplementationTypes( + options: WorkflowImplementationOptions, + vararg workflowClasses: KClass<*> + ) + inline fun registerWorkflowImplementationTypes( + options: WorkflowImplementationOptions.Builder.() -> Unit + ) +} +``` + +**Test scenarios:** +- Reified generic registration works: `registerWorkflowImplementationTypes()` +- KClass vararg registration works +- Options DSL correctly applies WorkflowImplementationOptions +- Underlying Worker receives correct registrations + +--- + +### Commit 5: Add KWorker activity registration methods ✅ + +**Description:** Add activity registration including suspend activity support. + +**Files:** +- `KWorker.kt` (modify) + +**Content:** +```kotlin +// Add to KWorker class: +fun registerActivitiesImplementations(vararg activities: Any) +fun registerSuspendActivities(vararg activities: Any) +``` + +**Test scenarios:** +- Regular activities registered and callable +- Suspend activities wrapped via `SuspendActivityWrapper` +- Mixed registration (regular + suspend) works + +--- + +### Commit 6: Add KWorker Nexus service registration ✅ + +**Description:** Add Nexus service registration method. + +**Files:** +- `KWorker.kt` (modify) + +**Content:** +```kotlin +// Add to KWorker class: +fun registerNexusServiceImplementations(vararg services: Any) +``` + +**Test scenarios:** +- Nexus services registered on underlying Worker +- Services callable via Nexus operations + +--- + +## Phase 3: KTestWorkflowEnvironment (Commits 7-12) + +### Commit 7: Add KTestWorkflowEnvironment basic structure + +**Description:** Add core test environment with worker creation. + +**Files:** +``` +temporal-kotlin-testing/src/main/kotlin/io/temporal/kotlin/testing/ +└── KTestWorkflowEnvironment.kt +``` + +**Content:** +```kotlin +class KTestWorkflowEnvironment private constructor( + private val testEnvironment: TestWorkflowEnvironment +) : Closeable { + val namespace: String + + fun newWorker(taskQueue: String): KWorker + fun newWorker(taskQueue: String, options: WorkerOptions.Builder.() -> Unit): KWorker + fun newWorker(taskQueue: String, options: WorkerOptions): KWorker + + companion object { + fun newInstance(): KTestWorkflowEnvironment + fun newInstance(options: KTestEnvironmentOptionsBuilder.() -> Unit): KTestWorkflowEnvironment + fun newInstance(options: KTestEnvironmentOptions): KTestWorkflowEnvironment + } +} +``` + +**Test scenarios:** +- Environment created with defaults +- Environment created with DSL options +- Workers created with correct task queue +- Workers return KWorker instances + +--- + +### Commit 8: Add KTestWorkflowEnvironment client access + +**Description:** Add workflow client and service stub access. + +**Files:** +- `KTestWorkflowEnvironment.kt` (modify) + +**Content:** +```kotlin +// Add to KTestWorkflowEnvironment: +val workflowClient: KWorkflowClient // lazy +val workflowServiceStubs: WorkflowServiceStubs +val operatorServiceStubs: OperatorServiceStubs +``` + +**Test scenarios:** +- `workflowClient` returns valid KWorkflowClient +- Client can create workflow stubs +- Service stubs accessible for advanced operations + +--- + +### Commit 9: Add KTestWorkflowEnvironment time manipulation + +**Description:** Add time-related properties and sleep function. + +**Files:** +- `KTestWorkflowEnvironment.kt` (modify) + +**Content:** +```kotlin +// Add to KTestWorkflowEnvironment: +val currentTimeMillis: Long +val currentTime: Instant + +suspend fun sleep(duration: kotlin.time.Duration) +suspend fun sleep(duration: java.time.Duration) +``` + +**Test scenarios:** +- `currentTime` returns test environment time (not system time) +- `sleep()` advances test time without real delay +- Both Kotlin and Java Duration overloads work +- Workflow timers fire after sleep advances time + +--- + +### Commit 10: Add KTestWorkflowEnvironment delayed callbacks + +**Description:** Add delayed callback registration. + +**Files:** +- `KTestWorkflowEnvironment.kt` (modify) + +**Content:** +```kotlin +// Add to KTestWorkflowEnvironment: +fun registerDelayedCallback(delay: kotlin.time.Duration, callback: () -> Unit) +fun registerDelayedCallback(delay: java.time.Duration, callback: () -> Unit) +``` + +**Test scenarios:** +- Callback executes when test time reaches delay +- Multiple callbacks execute in correct order +- Both Duration types work + +--- + +### Commit 11: Add KTestWorkflowEnvironment lifecycle management + +**Description:** Add start, shutdown, and termination methods. + +**Files:** +- `KTestWorkflowEnvironment.kt` (modify) + +**Content:** +```kotlin +// Add to KTestWorkflowEnvironment: +val isStarted: Boolean +val isShutdown: Boolean +val isTerminated: Boolean + +fun start() +fun shutdown() +fun shutdownNow() +suspend fun awaitTermination(timeout: kotlin.time.Duration) +suspend fun awaitTermination(timeout: java.time.Duration) +override fun close() + +// Extension function: +inline fun KTestWorkflowEnvironment.use(block: (KTestWorkflowEnvironment) -> T): T +``` + +**Test scenarios:** +- `start()` enables workflow execution +- State properties reflect correct lifecycle state +- `shutdown()` stops accepting new work +- `shutdownNow()` interrupts in-progress work +- `awaitTermination()` suspends until terminated +- `close()` combines shutdownNow + awaitTermination +- `use {}` auto-closes on block exit + +--- + +### Commit 12: Add KTestWorkflowEnvironment advanced features + +**Description:** Add search attributes, Nexus endpoints, and diagnostics. + +**Files:** +- `KTestWorkflowEnvironment.kt` (modify) + +**Content:** +```kotlin +// Add to KTestWorkflowEnvironment: +fun registerSearchAttribute(name: String, type: IndexedValueType): Boolean +fun createNexusEndpoint(name: String, taskQueue: String): Endpoint +fun deleteNexusEndpoint(endpoint: Endpoint) +fun getDiagnostics(): String +``` + +**Test scenarios:** +- Search attributes registered and queryable +- Nexus endpoints created and usable +- Nexus endpoints deleted successfully +- Diagnostics returns workflow histories + +--- + +## Phase 4: KTestWorkflowExtension (Commits 13-18) + +### Commit 13: Add KTestWorkflowExtension builder basics + +**Description:** Add extension builder with workflow/activity registration. + +**Files:** +``` +temporal-kotlin-testing/src/main/kotlin/io/temporal/kotlin/testing/ +└── KTestWorkflowExtension.kt +``` + +**Content:** +```kotlin +class KTestWorkflowExtension private constructor(config: ExtensionConfig) { + private data class ExtensionConfig(...) + + class Builder { + var namespace: String + var initialTime: Instant? + var useTimeskipping: Boolean + var doNotStart: Boolean + + inline fun registerWorkflowImplementationTypes() + inline fun registerWorkflowImplementationTypes( + options: WorkflowImplementationOptions.Builder.() -> Unit + ) + fun registerWorkflowImplementationTypes(vararg classes: Class<*>) + fun setActivityImplementations(vararg activities: Any) + fun setSuspendActivityImplementations(vararg activities: Any) + fun setNexusServiceImplementations(vararg services: Any) + fun build(): KTestWorkflowExtension + } + + companion object { + fun newBuilder(): Builder + } +} + +fun kTestWorkflowExtension(block: KTestWorkflowExtension.Builder.() -> Unit): KTestWorkflowExtension +``` + +**Test scenarios:** +- Builder creates extension with correct config +- DSL function `kTestWorkflowExtension {}` works +- Workflow types stored correctly +- Activity implementations stored correctly + +--- + +### Commit 14: Add KTestWorkflowExtension service configuration + +**Description:** Add worker, client, and service configuration DSLs. + +**Files:** +- `KTestWorkflowExtension.kt` (modify) + +**Content:** +```kotlin +// Add to Builder: +fun workerOptions(block: WorkerOptions.Builder.() -> Unit) +fun workerFactoryOptions(block: WorkerFactoryOptions.Builder.() -> Unit) +fun workflowClientOptions(block: WorkflowClientOptions.Builder.() -> Unit) +fun useInternalService() +fun useExternalService() +fun useExternalService(address: String) +fun searchAttributes(block: SearchAttributesBuilder.() -> Unit) +``` + +**Test scenarios:** +- Worker options flow through to worker creation +- Factory options configure WorkerFactory +- Client options configure WorkflowClient +- `useExternalService()` connects to real Temporal +- Search attributes registered on test server + +--- + +### Commit 15: Add KTestWorkflowExtension lifecycle callbacks + +**Description:** Implement JUnit 5 BeforeEach/AfterEach callbacks. + +**Files:** +- `KTestWorkflowExtension.kt` (modify) + +**Content:** +```kotlin +// Implement interfaces: +class KTestWorkflowExtension : BeforeEachCallback, AfterEachCallback { + override fun beforeEach(context: ExtensionContext) { + // Create test environment + // Create worker with unique task queue + // Register workflows and activities + // Start if not doNotStart + // Store in ExtensionContext.Store + } + + override fun afterEach(context: ExtensionContext) { + // Close test environment + } +} +``` + +**Test scenarios:** +- Each test gets isolated environment +- Workers registered before test runs +- Environment closed after test completes +- Test isolation verified (no cross-test contamination) + +--- + +### Commit 16: Add KTestWorkflowExtension basic parameter injection + +**Description:** Implement ParameterResolver for core types. + +**Files:** +- `KTestWorkflowExtension.kt` (modify) + +**Content:** +```kotlin +// Implement ParameterResolver: +class KTestWorkflowExtension : ParameterResolver { + override fun supportsParameter(parameterContext, extensionContext): Boolean { + // Support: KTestWorkflowEnvironment, KWorkflowClient, + // KWorkflowOptions, KWorker, Worker + } + + override fun resolveParameter(parameterContext, extensionContext): Any { + // Return appropriate instance from store + } +} +``` + +**Test scenarios:** +- `KTestWorkflowEnvironment` injected correctly +- `KWorkflowClient` injected correctly +- `KWorkflowOptions` injected with correct task queue +- `KWorker` injected correctly +- `Worker` (Java) injected correctly + +--- + +### Commit 17: Add KTestWorkflowExtension workflow stub injection + +**Description:** Add workflow interface stub injection. + +**Files:** +- `KTestWorkflowExtension.kt` (modify) + +**Content:** +```kotlin +// Enhance parameter resolution: +private val supportedParameterTypes: MutableSet> + +// In constructor: populate from registered workflow types +// In supportsParameter: check workflow interfaces +// In resolveParameter: create workflow stub +``` + +**Test scenarios:** +- Workflow interface parameters injected as stubs +- Stubs have correct task queue +- Stubs can execute workflows +- Dynamic workflow support works + +--- + +### Commit 18: Add KTestWorkflowExtension annotations and diagnostics + +**Description:** Add WorkflowInitialTime support and test failure diagnostics. + +**Files:** +- `KTestWorkflowExtension.kt` (modify) + +**Content:** +```kotlin +// Implement TestWatcher: +class KTestWorkflowExtension : TestWatcher { + override fun testFailed(context: ExtensionContext, cause: Throwable) { + // Print diagnostics to stderr + } +} + +// In beforeEach: +// Check for @WorkflowInitialTime annotation +// Override initial time if present +``` + +**Test scenarios:** +- `@WorkflowInitialTime` overrides initial time for specific test +- Test failure prints workflow histories to stderr +- Diagnostics help debug failing tests + +--- + +## Phase 5: KTestActivityEnvironment (Commits 19-24) + +### Commit 19: Add KTestActivityEnvironment basic structure + +**Description:** Add core activity test environment with registration. + +**Files:** +``` +temporal-kotlin-testing/src/main/kotlin/io/temporal/kotlin/testing/ +└── KTestActivityEnvironment.kt +``` + +**Content:** +```kotlin +class KTestActivityEnvironment private constructor( + private val testEnvironment: TestActivityEnvironment +) : Closeable { + fun registerActivitiesImplementations(vararg activities: Any) + fun registerSuspendActivities(vararg activities: Any) + override fun close() + + companion object { + fun newInstance(): KTestActivityEnvironment + fun newInstance(options: KTestEnvironmentOptionsBuilder.() -> Unit): KTestActivityEnvironment + fun newInstance(options: KTestEnvironmentOptions): KTestActivityEnvironment + } +} +``` + +**Test scenarios:** +- Environment created with defaults +- Activities registered successfully +- Suspend activities wrapped and registered +- Environment closes cleanly + +--- + +### Commit 20: Add KTestActivityEnvironment executeActivity (0-2 args) + +**Description:** Add activity execution via method references. + +**Files:** +- `KTestActivityEnvironment.kt` (modify) + +**Content:** +```kotlin +// Add to KTestActivityEnvironment: +private fun createActivityStub(activity: KFunction<*>, options: ActivityOptions): T + +fun executeActivity(activity: KFunction1, options: KActivityOptions): R +fun executeActivity(activity: KFunction2, options: KActivityOptions, arg1: A1): R +fun executeActivity(activity: KFunction3, options: KActivityOptions, arg1: A1, arg2: A2): R +``` + +**Test scenarios:** +- Activity with no args executes correctly +- Activity with 1 arg executes correctly +- Activity with 2 args executes correctly +- Method reference extracts correct interface + +--- + +### Commit 21: Add KTestActivityEnvironment executeActivity (3+ args) + +**Description:** Add activity execution for higher arities. + +**Files:** +- `KTestActivityEnvironment.kt` (modify) + +**Content:** +```kotlin +// Add to KTestActivityEnvironment: +fun executeActivity( + activity: KFunction4, + options: KActivityOptions, + arg1: A1, arg2: A2, arg3: A3 +): R + +// Additional arities as needed (4, 5, 6 args) +``` + +**Test scenarios:** +- Activity with 3 args executes correctly +- Activity with 4+ args executes correctly + +--- + +### Commit 22: Add KTestActivityEnvironment suspend activity support + +**Description:** Add suspend function overloads for activity execution. + +**Files:** +- `KTestActivityEnvironment.kt` (modify) + +**Content:** +```kotlin +// Add suspend overloads: +@JvmName("executeSuspendActivity0") +suspend fun executeActivity(activity: KSuspendFunction1, options: KActivityOptions): R + +@JvmName("executeSuspendActivity1") +suspend fun executeActivity(activity: KSuspendFunction2, options: KActivityOptions, arg1: A1): R + +// Continue for 2, 3+ args +``` + +**Test scenarios:** +- Suspend activity with no args executes +- Suspend activity with args executes +- Coroutine context preserved correctly + +--- + +### Commit 23: Add KTestActivityEnvironment local activity support + +**Description:** Add local activity execution methods. + +**Files:** +- `KTestActivityEnvironment.kt` (modify) + +**Content:** +```kotlin +// Add to KTestActivityEnvironment: +private fun createLocalActivityStub(activity: KFunction<*>, options: LocalActivityOptions): T + +fun executeLocalActivity(activity: KFunction1, options: KLocalActivityOptions): R +fun executeLocalActivity(activity: KFunction2, options: KLocalActivityOptions, arg1: A1): R +fun executeLocalActivity(activity: KFunction3, options: KLocalActivityOptions, arg1: A1, arg2: A2): R +fun executeLocalActivity(activity: KFunction4, options: KLocalActivityOptions, arg1: A1, arg2: A2, arg3: A3): R +``` + +**Test scenarios:** +- Local activity executes with correct options +- Local activity timeout behavior works +- Local activity retry behavior works + +--- + +### Commit 24: Add KTestActivityEnvironment heartbeat and cancellation + +**Description:** Add heartbeat testing and cancellation support. + +**Files:** +- `KTestActivityEnvironment.kt` (modify) + +**Content:** +```kotlin +// Add to KTestActivityEnvironment: +fun setHeartbeatDetails(details: T) + +inline fun setActivityHeartbeatListener(noinline listener: (T) -> Unit) +fun setActivityHeartbeatListener(detailsClass: Class, detailsType: Type, listener: (T) -> Unit) + +fun requestCancelActivity() +``` + +**Test scenarios:** +- `setHeartbeatDetails` provides checkpoint to activity +- Heartbeat listener receives heartbeat calls +- `requestCancelActivity` triggers cancellation on next heartbeat +- `ActivityCanceledException` thrown after cancellation + +--- + +## Phase 6: KTestActivityExtension (Commits 25-26) + +### Commit 25: Add KTestActivityExtension builder + +**Description:** Add extension builder with DSL. + +**Files:** +``` +temporal-kotlin-testing/src/main/kotlin/io/temporal/kotlin/testing/ +└── KTestActivityExtension.kt +``` + +**Content:** +```kotlin +class KTestActivityExtension private constructor(config: ExtensionConfig) { + private data class ExtensionConfig( + val testEnvironmentOptions: TestEnvironmentOptions, + val activityImplementations: Array, + val suspendActivityImplementations: Array + ) + + class Builder { + fun testEnvironmentOptions(block: KTestEnvironmentOptionsBuilder.() -> Unit) + fun setActivityImplementations(vararg activities: Any) + fun setSuspendActivityImplementations(vararg activities: Any) + fun build(): KTestActivityExtension + } + + companion object { + fun newBuilder(): Builder + } +} + +fun kTestActivityExtension(block: KTestActivityExtension.Builder.() -> Unit): KTestActivityExtension +``` + +**Test scenarios:** +- Builder creates extension with correct config +- DSL function works +- Activity implementations stored correctly + +--- + +### Commit 26: Add KTestActivityExtension lifecycle and injection + +**Description:** Implement JUnit 5 callbacks and parameter injection. + +**Files:** +- `KTestActivityExtension.kt` (modify) + +**Content:** +```kotlin +class KTestActivityExtension : ParameterResolver, BeforeEachCallback, AfterEachCallback { + override fun supportsParameter(parameterContext, extensionContext): Boolean { + // Support KTestActivityEnvironment + } + + override fun resolveParameter(parameterContext, extensionContext): Any { + // Return KTestActivityEnvironment from store + } + + override fun beforeEach(context: ExtensionContext) { + // Create environment + // Register activities + // Store in context + } + + override fun afterEach(context: ExtensionContext) { + // Close environment + } +} +``` + +**Test scenarios:** +- `KTestActivityEnvironment` injected into test methods +- Each test gets isolated environment +- Activities callable via injected environment +- Environment closed after test + +--- + +## Summary + +| Phase | Commits | Description | +|-------|---------|-------------| +| 1. Foundation | 1-3 | Module setup, annotation, options | +| 2. KWorker | 4-6 | Worker wrapper with registrations | +| 3. KTestWorkflowEnvironment | 7-12 | Main test environment | +| 4. KTestWorkflowExtension | 13-18 | JUnit 5 workflow extension | +| 5. KTestActivityEnvironment | 19-24 | Activity test environment | +| 6. KTestActivityExtension | 25-26 | JUnit 5 activity extension | + +**Total: 26 commits** + +## Dependencies Graph + +``` +Commit 1 (module) + └── Commit 2 (annotation) + └── Commit 3 (options) + └── Commits 4-6 (KWorker) + └── Commits 7-12 (KTestWorkflowEnvironment) + └── Commits 13-18 (KTestWorkflowExtension) + └── Commits 19-24 (KTestActivityEnvironment) + └── Commits 25-26 (KTestActivityExtension) +``` + +## Testing Strategy + +Each commit should include: + +1. **Unit tests** for the new functionality +2. **Integration tests** where applicable (especially for environment/extension commits) +3. **Example usage** in test code demonstrating the API + +Test files follow the pattern: +``` +temporal-kotlin-testing/src/test/kotlin/io/temporal/kotlin/testing/ +├── WorkflowInitialTimeTest.kt +├── KTestEnvironmentOptionsTest.kt +├── KWorkerTest.kt +├── KTestWorkflowEnvironmentTest.kt +├── KTestWorkflowExtensionTest.kt +├── KTestActivityEnvironmentTest.kt +└── KTestActivityExtensionTest.kt +``` From 164fa223d80c122531493788f9122dc4db05cd3d Mon Sep 17 00:00:00 2001 From: Maxim Fateev Date: Sun, 4 Jan 2026 14:32:31 -0800 Subject: [PATCH 15/83] Update implementation plan with updateWithStart and mocking completion - Mark updateWithStart as complete with details on implemented methods - Mark mocking support (3.2) as complete --- kotlin/implementation-plan.md | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/kotlin/implementation-plan.md b/kotlin/implementation-plan.md index 0cca7da..b252939 100644 --- a/kotlin/implementation-plan.md +++ b/kotlin/implementation-plan.md @@ -62,7 +62,11 @@ - ✅ `KUpdateHandle` - handle for async update execution - ✅ `startWorkflow()`, `executeWorkflow()` suspend functions (0-6 args) - ✅ `signalWithStart()` -- `updateWithStart()` - TODO (lower priority) +- ✅ `updateWithStart()` - Atomically start workflow + send update + - ✅ `withStartWorkflowOperation()` factory methods (0-6 workflow args, regular and suspend) + - ✅ `startUpdateWithStart()` / `executeUpdateWithStart()` with `KUpdateWithStartOptions` (0-6 update args) + - ✅ `KWithStartWorkflowOperation` to capture workflow start metadata + - ✅ `KUpdateWithStartOptions` with waitForStage and updateId options - ✅ `getWorkflowHandle()` and `getUntypedWorkflowHandle()` ### 2.5 Worker API ✅ @@ -98,8 +102,11 @@ - ✅ Time skipping utilities (`sleep()`, `registerDelayedCallback()`) - ✅ Search attribute registration in test environment -### 3.2 Mocking Support -- Activity mocking with suspend functions - TODO (may use existing Java mocking patterns) +### 3.2 Mocking Support ✅ COMPLETE +- ✅ `KActivityMockRegistry` - thread-safe registry for activity mocks +- ✅ `KMockDynamicActivityHandler` - dynamic activity handler routing calls to mocks +- ✅ Support for both regular and suspend activity mocks +- ✅ Integration with `KTestWorkflowExtension` for automatic mock registration ### 3.3 Interceptors (Design Complete - see sdk-api.md) - `KWorkerInterceptor` interface with `KWorkerInterceptorBase` From 701f306afa7e69326ec840ba367a198dc6a30467 Mon Sep 17 00:00:00 2001 From: Maxim Fateev Date: Sun, 4 Jan 2026 14:36:02 -0800 Subject: [PATCH 16/83] Update SDK API doc with updateWithStart design Documents the implemented updateWithStart API: - KWithStartWorkflowOperation class - KUpdateWithStartOptions data class - withStartWorkflowOperation factory methods - startUpdateWithStart and executeUpdateWithStart methods --- kotlin/sdk-api.md | 952 ++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 887 insertions(+), 65 deletions(-) diff --git a/kotlin/sdk-api.md b/kotlin/sdk-api.md index ac5df21..ec1173d 100644 --- a/kotlin/sdk-api.md +++ b/kotlin/sdk-api.md @@ -1571,19 +1571,74 @@ class KWorkflowClient( signalArg: SA1 ): KTypedWorkflowHandle + // --- Update With Start --- + /** - * Atomically start a workflow and send an update. - * Returns both the workflow handle and the update result. + * Create a workflow start operation for use with update-with-start. + * Captures the workflow method, arguments, and options for atomic execution. */ - suspend fun updateWithStart( + fun withStartWorkflowOperation( + workflow: KFunction1, + options: KWorkflowOptions + ): KWithStartWorkflowOperation + + fun withStartWorkflowOperation( workflow: KFunction2, - options: WorkflowOptions, - workflowArg: A1, - update: KFunction2, - updateArg: UA1 - ): Pair, UR> + options: KWorkflowOptions, + arg1: A1 + ): KWithStartWorkflowOperation + + // Overloads for 2-6 workflow arguments... + // Also KSuspendFunction variants for suspend workflow methods... + + /** + * Atomically start a workflow and send an update, returning immediately after + * the update reaches the specified wait stage. + * + * If the workflow is not running, starts it and sends the update. + * Behavior for existing workflows depends on [KWorkflowOptions.workflowIdConflictPolicy]: + * - USE_EXISTING: sends update to existing workflow + * - FAIL: throws WorkflowExecutionAlreadyStarted + * + * @param update Update method reference (must be suspend) + * @param options Options containing the start operation and wait stage + * @return Handle to track the update result + */ + suspend fun startUpdateWithStart( + update: KSuspendFunction1, + options: KUpdateWithStartOptions + ): KUpdateHandle + + suspend fun startUpdateWithStart( + update: KSuspendFunction2, + options: KUpdateWithStartOptions, + updateArg1: UA1 + ): KUpdateHandle + + // Overloads for 2-6 update arguments... + + /** + * Atomically start a workflow and execute an update, waiting for completion. + * Convenience method equivalent to startUpdateWithStart with waitForStage=COMPLETED. + * + * @param update Update method reference (must be suspend) + * @param options Options containing the start operation + * @return The update result + */ + suspend fun executeUpdateWithStart( + update: KSuspendFunction1, + options: KUpdateWithStartOptions + ): UR - // --- KWorkflowOptions variants --- + suspend fun executeUpdateWithStart( + update: KSuspendFunction2, + options: KUpdateWithStartOptions, + updateArg1: UA1 + ): UR + + // Overloads for 2-6 update arguments... + + // --- Signal With Start --- suspend fun signalWithStart( workflow: KFunction2, @@ -1593,13 +1648,7 @@ class KWorkflowClient( signalArg: SA1 ): KTypedWorkflowHandle - suspend fun updateWithStart( - workflow: KFunction2, - options: KWorkflowOptions, - workflowArg: A1, - update: KFunction2, - updateArg: UA1 - ): Pair, UR> + // Overloads for different argument counts... } ``` @@ -1653,26 +1702,107 @@ val result = handle.result() // Type inferred as OrderResult ### UpdateWithStart -Atomically start a workflow and send an update. If the workflow already exists, only the update is sent: +Atomically start a workflow and send an update. Behavior depends on `workflowIdConflictPolicy`: +- `USE_EXISTING`: sends update to existing workflow +- `FAIL`: throws exception if workflow already exists + +**Supporting Types:** ```kotlin -// Returns Pair, Boolean> -// - handle with result type captured from workflow method reference -// - updateResult typed by update method return type -val (handle, updateResult: Boolean) = client.updateWithStart( - workflow = OrderWorkflow::processOrder, - options = KWorkflowOptions( +/** + * Represents a workflow start operation for use with update-with-start. + * Created via KWorkflowClient.withStartWorkflowOperation(). + */ +class KWithStartWorkflowOperation { + /** Get the workflow result after the operation completes. */ + fun getResult(): R +} + +/** + * Options for update-with-start operations. + * Bundles the start operation with update-specific options. + */ +data class KUpdateWithStartOptions( + /** The workflow start operation (required) */ + val startWorkflowOperation: KWithStartWorkflowOperation, + + /** Stage to wait for before returning (required for startUpdateWithStart) */ + val waitForStage: WorkflowUpdateStage = WorkflowUpdateStage.ACCEPTED, + + /** Optional update ID for idempotency */ + val updateId: String? = null +) +``` + +**Usage - Execute and wait for completion:** + +```kotlin +// Step 1: Create the workflow start operation +val startOp = client.withStartWorkflowOperation( + OrderWorkflow::processOrder, + KWorkflowOptions( workflowId = "order-123", - taskQueue = "orders" + taskQueue = "orders", + workflowIdConflictPolicy = WorkflowIdConflictPolicy.USE_EXISTING // Required ), - workflowArg = order, - update = OrderWorkflow::addItem, - updateArg = newItem + order +) + +// Step 2: Execute update with start (waits for update completion) +val updateResult: Boolean = client.executeUpdateWithStart( + OrderWorkflow::addItem, // Must be suspend function + KUpdateWithStartOptions(startWorkflowOperation = startOp), + newItem ) println("Item added: $updateResult") -// Can use typed handle for further operations -val result = handle.result() // Type inferred as OrderResult +// Step 3: Access workflow result if needed +val workflowResult: OrderResult = startOp.getResult() +``` + +**Usage - Start async (don't wait for completion):** + +```kotlin +val startOp = client.withStartWorkflowOperation( + OrderWorkflow::processOrder, + KWorkflowOptions( + workflowId = "order-456", + taskQueue = "orders", + workflowIdConflictPolicy = WorkflowIdConflictPolicy.FAIL + ), + order +) + +// Start update and return immediately after it's accepted +val updateHandle: KUpdateHandle = client.startUpdateWithStart( + OrderWorkflow::addItem, + KUpdateWithStartOptions( + startWorkflowOperation = startOp, + waitForStage = WorkflowUpdateStage.ACCEPTED + ), + newItem +) + +// Later: get the update result +val result = updateHandle.result() +``` + +**Usage - No arguments:** + +```kotlin +val startOp = client.withStartWorkflowOperation( + GreetingWorkflow::greet, + KWorkflowOptions( + workflowId = "greeting-789", + taskQueue = "greetings", + workflowIdConflictPolicy = WorkflowIdConflictPolicy.USE_EXISTING + ) +) + +val status: String = client.executeUpdateWithStart( + GreetingWorkflow::getStatus, // suspend fun getStatus(): String + KUpdateWithStartOptions(startWorkflowOperation = startOp) +) ``` ### Workflow Handle @@ -1832,7 +1962,7 @@ val factory = KWorkerFactory(client) { maxWorkflowThreadCount = 800 } -val worker = factory.newWorker("task-queue") { +val worker: KWorker = factory.newWorker("task-queue") { maxConcurrentActivityExecutionSize = 100 } @@ -1866,7 +1996,7 @@ class KWorkerFactory( /** The underlying WorkerFactory for advanced use cases */ val workerFactory: WorkerFactory - fun newWorker(taskQueue: String, options: WorkerOptions.Builder.() -> Unit = {}): Worker + fun newWorker(taskQueue: String, options: WorkerOptions.Builder.() -> Unit = {}): KWorker fun start() fun shutdown() fun shutdownNow() @@ -1874,6 +2004,47 @@ class KWorkerFactory( } ``` +**KWorker API:** + +```kotlin +/** + * Kotlin worker that provides idiomatic APIs for registering + * Kotlin workflows and suspend activities. + * + * Use KWorker for pure Kotlin implementations. For mixed Java/Kotlin + * scenarios, access the underlying Worker via the [worker] property. + */ +class KWorker { + /** The underlying Java Worker for interop scenarios */ + val worker: Worker + + /** Register Kotlin workflow implementation types using reified generics */ + inline fun registerWorkflowImplementationTypes() + + /** Register Kotlin workflow implementation types using KClass */ + fun registerWorkflowImplementationTypes(vararg workflowClasses: KClass<*>) + + /** Register Kotlin workflow implementation types with options */ + fun registerWorkflowImplementationTypes( + options: WorkflowImplementationOptions, + vararg workflowClasses: KClass<*> + ) + + /** Register activity implementations (automatically detects suspend functions) */ + fun registerActivitiesImplementations(vararg activities: Any) + + /** Register suspend activity implementations explicitly */ + fun registerSuspendActivities(vararg activities: Any) + + /** Register Nexus service implementations */ + fun registerNexusServiceImplementations(vararg services: Any) +} +``` + +**When to use KWorker vs Worker:** +- Use `KWorker` for pure Kotlin implementations (recommended) +- Use `Worker` (via `kworker.worker`) when mixing Java and Kotlin workflows/activities on the same worker + ### KotlinPlugin (For Java Main) When your main application is written in Java and you need to register Kotlin workflows, use `KotlinPlugin` explicitly: @@ -1972,53 +2143,688 @@ val client = WorkflowClient(service) { ## Interceptors -### Kotlin Interceptor Interface +Interceptors allow you to intercept workflow and activity executions to add cross-cutting concerns like logging, metrics, tracing, and custom error handling. The Kotlin SDK provides suspend-function-aware interceptors that integrate naturally with coroutines. + +### KWorkerInterceptor + +The main entry point for interceptors. Registered with `KWorkerFactory` and called when workflows or activities are instantiated. ```kotlin +/** + * Intercepts workflow and activity executions. + * + * Prefer extending [KWorkerInterceptorBase] and overriding only the methods you need. + */ interface KWorkerInterceptor { - fun interceptWorkflow(next: KWorkflowInboundCallsInterceptor): KWorkflowInboundCallsInterceptor { - return next - } + /** + * Called when a workflow is instantiated. May create a [KWorkflowInboundCallsInterceptor]. + * The returned interceptor must forward all calls to [next]. + */ + fun interceptWorkflow(next: KWorkflowInboundCallsInterceptor): KWorkflowInboundCallsInterceptor - fun interceptActivity(next: KActivityInboundCallsInterceptor): KActivityInboundCallsInterceptor { - return next - } + /** + * Called when an activity task is received. May create a [KActivityInboundCallsInterceptor]. + * The returned interceptor must forward all calls to [next]. + */ + fun interceptActivity(next: KActivityInboundCallsInterceptor): KActivityInboundCallsInterceptor +} + +/** + * Base implementation that passes through all calls. Extend this class and override only needed methods. + */ +open class KWorkerInterceptorBase : KWorkerInterceptor { + override fun interceptWorkflow(next: KWorkflowInboundCallsInterceptor) = next + override fun interceptActivity(next: KActivityInboundCallsInterceptor) = next } +``` +### KWorkflowInboundCallsInterceptor + +Intercepts inbound calls to workflow execution (workflow method, signals, queries, updates). + +```kotlin +/** + * Intercepts inbound calls to workflow execution. + * + * All methods except [handleQuery] are suspend functions, allowing coroutine-based interception. + * The [init] method receives the outbound interceptor for intercepting outgoing calls. + * + * Prefer extending [KWorkflowInboundCallsInterceptorBase] and overriding only needed methods. + */ interface KWorkflowInboundCallsInterceptor { + + /** + * Called when the workflow is instantiated. Use this to wrap the outbound interceptor. + */ suspend fun init(outboundCalls: KWorkflowOutboundCallsInterceptor) - suspend fun execute(input: WorkflowInput): WorkflowOutput - suspend fun handleSignal(input: SignalInput) - suspend fun handleUpdate(input: UpdateInput): UpdateOutput - fun handleQuery(input: QueryInput): QueryOutput + + /** + * Called when the workflow main method is invoked. + */ + suspend fun execute(input: KWorkflowInput): KWorkflowOutput + + /** + * Called when a signal is delivered to the workflow. + */ + suspend fun handleSignal(input: KSignalInput) + + /** + * Called when a query is made to the workflow. + * Note: Queries must be synchronous and cannot modify workflow state. + */ + fun handleQuery(input: KQueryInput): KQueryOutput + + /** + * Called to validate an update before execution. + * Throw an exception to reject the update. + */ + fun validateUpdate(input: KUpdateInput) + + /** + * Called to execute an update after validation passes. + */ + suspend fun executeUpdate(input: KUpdateInput): KUpdateOutput } +/** + * Base implementation that forwards all calls to the next interceptor. + */ +open class KWorkflowInboundCallsInterceptorBase( + protected val next: KWorkflowInboundCallsInterceptor +) : KWorkflowInboundCallsInterceptor { + override suspend fun init(outboundCalls: KWorkflowOutboundCallsInterceptor) = next.init(outboundCalls) + override suspend fun execute(input: KWorkflowInput) = next.execute(input) + override suspend fun handleSignal(input: KSignalInput) = next.handleSignal(input) + override fun handleQuery(input: KQueryInput) = next.handleQuery(input) + override fun validateUpdate(input: KUpdateInput) = next.validateUpdate(input) + override suspend fun executeUpdate(input: KUpdateInput) = next.executeUpdate(input) +} +``` + +#### Workflow Inbound Input/Output Classes + +```kotlin +/** + * Input to workflow execution. + */ +data class KWorkflowInput( + val header: Header, + val arguments: Array +) + +/** + * Output from workflow execution. + */ +data class KWorkflowOutput( + val result: Any? +) + +/** + * Input to signal handler. + */ +data class KSignalInput( + val signalName: String, + val arguments: Array, + val eventId: Long, + val header: Header +) + +/** + * Input to query handler. + */ +data class KQueryInput( + val queryName: String, + val arguments: Array, + val header: Header +) + +/** + * Output from query handler. + */ +data class KQueryOutput( + val result: Any? +) + +/** + * Input to update handler (validation and execution). + */ +data class KUpdateInput( + val updateName: String, + val arguments: Array, + val header: Header +) + +/** + * Output from update execution. + */ +data class KUpdateOutput( + val result: Any? +) +``` + +### KWorkflowOutboundCallsInterceptor + +Intercepts outbound calls from workflow code to Temporal APIs (activities, child workflows, timers, etc.). + +```kotlin +/** + * Intercepts outbound calls from workflow code to Temporal APIs. + * + * All calls execute in workflow context and must follow determinism rules. + * + * Prefer extending [KWorkflowOutboundCallsInterceptorBase] and overriding only needed methods. + */ interface KWorkflowOutboundCallsInterceptor { - suspend fun executeActivity(input: ActivityInput): ActivityOutput - fun startActivity(input: ActivityInput): KActivityHandle - suspend fun executeChildWorkflow(input: ChildWorkflowInput): ChildWorkflowOutput - fun startChildWorkflow(input: ChildWorkflowInput): ChildWorkflowHandle + + // --- Activities --- + + /** + * Intercepts activity execution. Returns a Deferred that completes when the activity completes. + */ + fun executeActivity(input: KActivityInvocationInput): Deferred + + /** + * Intercepts local activity execution. + */ + fun executeLocalActivity(input: KLocalActivityInvocationInput): Deferred + + // --- Child Workflows --- + + /** + * Intercepts child workflow execution. + */ + fun executeChildWorkflow(input: KChildWorkflowInvocationInput): KChildWorkflowInvocationOutput + + // --- Timers and Delays --- + + /** + * Intercepts delay/sleep calls. + */ + suspend fun delay(duration: Duration) + + /** + * Intercepts timer creation. + */ + fun newTimer(duration: Duration): Deferred + + // --- Await Conditions --- + + /** + * Intercepts await with timeout. + * @return true if condition was satisfied, false if timed out + */ + suspend fun awaitCondition(timeout: Duration, reason: String, condition: () -> Boolean): Boolean + + /** + * Intercepts await without timeout. + */ + suspend fun awaitCondition(reason: String, condition: () -> Boolean) + + // --- Side Effects --- + + /** + * Intercepts side effect execution. + */ + fun sideEffect(resultClass: Class, func: () -> R): R + + /** + * Intercepts mutable side effect execution. + */ + fun mutableSideEffect( + id: String, + resultClass: Class, + updated: (R?, R?) -> Boolean, + func: () -> R + ): R + + // --- Versioning --- + + /** + * Intercepts version check. + */ + fun getVersion(changeId: String, minSupported: Int, maxSupported: Int): Int + + // --- Continue As New --- + + /** + * Intercepts continue-as-new. + */ + fun continueAsNew(input: KContinueAsNewInput): Nothing + + // --- External Workflow Communication --- + + /** + * Intercepts signal to external workflow. + */ + fun signalExternalWorkflow(input: KSignalExternalInput): Deferred + + /** + * Intercepts cancel request to external workflow. + */ + fun cancelWorkflow(input: KCancelWorkflowInput): Deferred + + // --- Search Attributes and Memo --- + + /** + * Intercepts search attribute updates. + */ + fun upsertTypedSearchAttributes(vararg updates: SearchAttributeUpdate<*>) + + /** + * Intercepts memo updates. + */ + fun upsertMemo(memo: Map) + + // --- Utilities --- + + /** + * Intercepts random number generation. + */ + fun newRandom(): Random + + /** + * Intercepts UUID generation. + */ + fun randomUUID(): UUID + + /** + * Intercepts current time access. + */ + fun currentTimeMillis(): Long } +/** + * Base implementation that forwards all calls to the next interceptor. + */ +open class KWorkflowOutboundCallsInterceptorBase( + protected val next: KWorkflowOutboundCallsInterceptor +) : KWorkflowOutboundCallsInterceptor { + override fun executeActivity(input: KActivityInvocationInput) = next.executeActivity(input) + override fun executeLocalActivity(input: KLocalActivityInvocationInput) = next.executeLocalActivity(input) + override fun executeChildWorkflow(input: KChildWorkflowInvocationInput) = next.executeChildWorkflow(input) + override suspend fun delay(duration: Duration) = next.delay(duration) + override fun newTimer(duration: Duration) = next.newTimer(duration) + override suspend fun awaitCondition(timeout: Duration, reason: String, condition: () -> Boolean) = + next.awaitCondition(timeout, reason, condition) + override suspend fun awaitCondition(reason: String, condition: () -> Boolean) = + next.awaitCondition(reason, condition) + override fun sideEffect(resultClass: Class, func: () -> R) = next.sideEffect(resultClass, func) + override fun mutableSideEffect(id: String, resultClass: Class, updated: (R?, R?) -> Boolean, func: () -> R) = + next.mutableSideEffect(id, resultClass, updated, func) + override fun getVersion(changeId: String, minSupported: Int, maxSupported: Int) = + next.getVersion(changeId, minSupported, maxSupported) + override fun continueAsNew(input: KContinueAsNewInput) = next.continueAsNew(input) + override fun signalExternalWorkflow(input: KSignalExternalInput) = next.signalExternalWorkflow(input) + override fun cancelWorkflow(input: KCancelWorkflowInput) = next.cancelWorkflow(input) + override fun upsertTypedSearchAttributes(vararg updates: SearchAttributeUpdate<*>) = + next.upsertTypedSearchAttributes(*updates) + override fun upsertMemo(memo: Map) = next.upsertMemo(memo) + override fun newRandom() = next.newRandom() + override fun randomUUID() = next.randomUUID() + override fun currentTimeMillis() = next.currentTimeMillis() +} +``` + +#### Workflow Outbound Input/Output Classes + +```kotlin +/** + * Input for activity invocation. + */ +data class KActivityInvocationInput( + val activityName: String, + val resultClass: Class, + val resultType: Type, + val arguments: Array, + val options: KActivityOptions, + val header: Header +) + +/** + * Input for local activity invocation. + */ +data class KLocalActivityInvocationInput( + val activityName: String, + val resultClass: Class, + val resultType: Type, + val arguments: Array, + val options: KLocalActivityOptions, + val header: Header +) + +/** + * Input for child workflow invocation. + */ +data class KChildWorkflowInvocationInput( + val workflowId: String, + val workflowType: String, + val resultClass: Class, + val resultType: Type, + val arguments: Array, + val options: KChildWorkflowOptions, + val header: Header +) + +/** + * Output from child workflow start. + */ +data class KChildWorkflowInvocationOutput( + val result: Deferred, + val workflowExecution: Deferred +) + +/** + * Input for continue-as-new. + */ +data class KContinueAsNewInput( + val workflowType: String?, + val options: KContinueAsNewOptions?, + val arguments: Array, + val header: Header +) + +/** + * Input for signaling external workflow. + */ +data class KSignalExternalInput( + val execution: WorkflowExecution, + val signalName: String, + val arguments: Array, + val header: Header +) + +/** + * Input for canceling external workflow. + */ +data class KCancelWorkflowInput( + val execution: WorkflowExecution, + val reason: String? +) +``` + +### KActivityInboundCallsInterceptor + +Intercepts inbound calls to activity execution. + +```kotlin +/** + * Intercepts inbound calls to activity execution. + * + * The [execute] method is a suspend function, supporting both regular and suspend activities. + * + * Prefer extending [KActivityInboundCallsInterceptorBase] and overriding only needed methods. + */ interface KActivityInboundCallsInterceptor { - suspend fun execute(input: ActivityInput): ActivityOutput + + /** + * Called when activity is initialized. Provides access to the activity execution context. + */ + fun init(context: ActivityExecutionContext) + + /** + * Called when activity method is invoked. + * This is a suspend function to support suspend activities. + */ + suspend fun execute(input: KActivityExecutionInput): KActivityExecutionOutput } + +/** + * Base implementation that forwards all calls to the next interceptor. + */ +open class KActivityInboundCallsInterceptorBase( + protected val next: KActivityInboundCallsInterceptor +) : KActivityInboundCallsInterceptor { + override fun init(context: ActivityExecutionContext) = next.init(context) + override suspend fun execute(input: KActivityExecutionInput) = next.execute(input) +} + +/** + * Input to activity execution. + */ +data class KActivityExecutionInput( + val header: Header, + val arguments: Array +) + +/** + * Output from activity execution. + */ +data class KActivityExecutionOutput( + val result: Any? +) +``` + +### Registering Interceptors + +Interceptors are registered via `KWorkerFactory`: + +```kotlin +val factory = KWorkerFactory(client) { + workerInterceptors = listOf( + LoggingInterceptor(), + MetricsInterceptor(), + TracingInterceptor() + ) +} + +val worker = factory.newWorker("task-queue") +worker.registerWorkflowImplementationTypes() +worker.registerActivitiesImplementations(MyActivitiesImpl()) + +factory.start() ``` ### Example: Logging Interceptor ```kotlin -class LoggingInterceptor : KWorkerInterceptor { +class LoggingInterceptor : KWorkerInterceptorBase() { + + override fun interceptWorkflow( + next: KWorkflowInboundCallsInterceptor + ): KWorkflowInboundCallsInterceptor { + return LoggingWorkflowInterceptor(next) + } + + override fun interceptActivity( + next: KActivityInboundCallsInterceptor + ): KActivityInboundCallsInterceptor { + return LoggingActivityInterceptor(next) + } +} + +private class LoggingWorkflowInterceptor( + next: KWorkflowInboundCallsInterceptor +) : KWorkflowInboundCallsInterceptorBase(next) { + + private val log = KWorkflow.logger() + + override suspend fun execute(input: KWorkflowInput): KWorkflowOutput { + log.info("Workflow started with ${input.arguments.size} arguments") + return try { + next.execute(input) + } catch (e: Exception) { + log.error("Workflow failed", e) + throw e + } finally { + log.info("Workflow completed") + } + } + + override suspend fun handleSignal(input: KSignalInput) { + log.info("Signal received: ${input.signalName}") + next.handleSignal(input) + } + + override fun handleQuery(input: KQueryInput): KQueryOutput { + log.debug("Query received: ${input.queryName}") + return next.handleQuery(input) + } +} + +private class LoggingActivityInterceptor( + next: KActivityInboundCallsInterceptor +) : KActivityInboundCallsInterceptorBase(next) { + + override suspend fun execute(input: KActivityExecutionInput): KActivityExecutionOutput { + val info = KActivity.getInfo() + val log = KActivity.logger() + + log.info("Activity ${info.activityType} started") + val startTime = System.currentTimeMillis() + + return try { + next.execute(input) + } finally { + val duration = System.currentTimeMillis() - startTime + log.info("Activity ${info.activityType} completed in ${duration}ms") + } + } +} +``` + +### Example: Tracing Interceptor with OpenTelemetry + +```kotlin +class TracingInterceptor( + private val tracer: Tracer +) : KWorkerInterceptorBase() { + + override fun interceptWorkflow( + next: KWorkflowInboundCallsInterceptor + ): KWorkflowInboundCallsInterceptor { + return TracingWorkflowInboundInterceptor(next, tracer) + } + + override fun interceptActivity( + next: KActivityInboundCallsInterceptor + ): KActivityInboundCallsInterceptor { + return TracingActivityInterceptor(next, tracer) + } +} + +private class TracingWorkflowInboundInterceptor( + next: KWorkflowInboundCallsInterceptor, + private val tracer: Tracer +) : KWorkflowInboundCallsInterceptorBase(next) { + + private lateinit var outboundInterceptor: TracingWorkflowOutboundInterceptor + + override suspend fun init(outboundCalls: KWorkflowOutboundCallsInterceptor) { + // Wrap the outbound interceptor to trace outgoing calls + outboundInterceptor = TracingWorkflowOutboundInterceptor(outboundCalls, tracer) + next.init(outboundInterceptor) + } + + override suspend fun execute(input: KWorkflowInput): KWorkflowOutput { + // Extract trace context from header + val parentContext = extractContext(input.header) + + val span = tracer.spanBuilder("workflow.execute") + .setParent(parentContext) + .setAttribute("workflow.type", KWorkflow.getInfo().workflowType) + .startSpan() + + return try { + withContext(span.asContextElement()) { + next.execute(input) + } + } catch (e: Exception) { + span.recordException(e) + span.setStatus(StatusCode.ERROR) + throw e + } finally { + span.end() + } + } +} + +private class TracingWorkflowOutboundInterceptor( + next: KWorkflowOutboundCallsInterceptor, + private val tracer: Tracer +) : KWorkflowOutboundCallsInterceptorBase(next) { + + override fun executeActivity(input: KActivityInvocationInput): Deferred { + val span = tracer.spanBuilder("activity.schedule") + .setAttribute("activity.name", input.activityName) + .startSpan() + + // Inject trace context into header + val headerWithTrace = injectContext(input.header, span.context) + val inputWithTrace = input.copy(header = headerWithTrace) + + span.end() + return next.executeActivity(inputWithTrace) + } + + override fun executeChildWorkflow( + input: KChildWorkflowInvocationInput + ): KChildWorkflowInvocationOutput { + val span = tracer.spanBuilder("child_workflow.start") + .setAttribute("workflow.type", input.workflowType) + .setAttribute("workflow.id", input.workflowId) + .startSpan() + + val headerWithTrace = injectContext(input.header, span.context) + val inputWithTrace = input.copy(header = headerWithTrace) + + span.end() + return next.executeChildWorkflow(inputWithTrace) + } +} +``` + +### Example: Metrics Interceptor + +```kotlin +class MetricsInterceptor( + private val meterProvider: MeterProvider +) : KWorkerInterceptorBase() { + + private val meter = meterProvider.get("temporal.sdk") + private val workflowCounter = meter.counterBuilder("workflow.executions").build() + private val activityCounter = meter.counterBuilder("activity.executions").build() + private val activityDuration = meter.histogramBuilder("activity.duration").build() + override fun interceptWorkflow( next: KWorkflowInboundCallsInterceptor ): KWorkflowInboundCallsInterceptor { return object : KWorkflowInboundCallsInterceptorBase(next) { - override suspend fun execute(input: WorkflowInput): WorkflowOutput { - log.info("Workflow started: ${input.workflowType}") + override suspend fun execute(input: KWorkflowInput): KWorkflowOutput { + val workflowType = KWorkflow.getInfo().workflowType + workflowCounter.add(1, Attributes.of( + AttributeKey.stringKey("workflow.type"), workflowType + )) + return next.execute(input) + } + } + } + + override fun interceptActivity( + next: KActivityInboundCallsInterceptor + ): KActivityInboundCallsInterceptor { + return object : KActivityInboundCallsInterceptorBase(next) { + override suspend fun execute(input: KActivityExecutionInput): KActivityExecutionOutput { + val info = KActivity.getInfo() + val startTime = System.nanoTime() + return try { - super.execute(input) + val result = next.execute(input) + activityCounter.add(1, Attributes.of( + AttributeKey.stringKey("activity.type"), info.activityType, + AttributeKey.stringKey("status"), "success" + )) + result + } catch (e: Exception) { + activityCounter.add(1, Attributes.of( + AttributeKey.stringKey("activity.type"), info.activityType, + AttributeKey.stringKey("status"), "failure" + )) + throw e } finally { - log.info("Workflow completed: ${input.workflowType}") + val durationMs = (System.nanoTime() - startTime) / 1_000_000.0 + activityDuration.record(durationMs, Attributes.of( + AttributeKey.stringKey("activity.type"), info.activityType + )) } } } @@ -2026,22 +2832,38 @@ class LoggingInterceptor : KWorkerInterceptor { } ``` -### Registering Interceptors - -Kotlin interceptors are registered via the `KotlinPlugin`, which propagates them to all workers: +### Example: Authentication/Authorization Interceptor ```kotlin -val client = WorkflowClient(service) { - plugins = listOf( - KotlinPlugin { - workerInterceptors = listOf( - LoggingInterceptor(), - MetricsInterceptor() - ) +class AuthInterceptor( + private val authService: AuthService +) : KWorkerInterceptorBase() { + + override fun interceptWorkflow( + next: KWorkflowInboundCallsInterceptor + ): KWorkflowInboundCallsInterceptor { + return object : KWorkflowInboundCallsInterceptorBase(next) { + + override suspend fun handleSignal(input: KSignalInput) { + // Validate authorization from header before processing signal + val authToken = input.header["authorization"]?.firstOrNull() + if (authToken != null && !authService.isAuthorized(authToken, "signal:${input.signalName}")) { + throw IllegalAccessException("Unauthorized signal: ${input.signalName}") + } + next.handleSignal(input) + } + + override suspend fun executeUpdate(input: KUpdateInput): KUpdateOutput { + // Validate authorization for updates + val authToken = input.header["authorization"]?.firstOrNull() + if (authToken != null && !authService.isAuthorized(authToken, "update:${input.updateName}")) { + throw IllegalAccessException("Unauthorized update: ${input.updateName}") + } + return next.executeUpdate(input) + } } - ) + } } -``` ## Migration from Java SDK @@ -2521,7 +3343,7 @@ fun main() = runBlocking { // KWorkerFactory automatically enables Kotlin coroutine support val factory = KWorkerFactory(client) - val worker = factory.newWorker("orders") + val worker: KWorker = factory.newWorker("orders") // Plugin handles suspend functions automatically worker.registerWorkflowImplementationTypes(OrderWorkflowImpl::class) From b927c06edd2808559823cfaf6b738c556a10a374 Mon Sep 17 00:00:00 2001 From: Maxim Fateev Date: Sun, 4 Jan 2026 15:17:58 -0800 Subject: [PATCH 17/83] Document dynamic handler registration APIs in Kotlin SDK - Add Dynamic Handler Registration section with usage examples - Add implemented APIs table for all handler registration methods - Remove dynamic handler registration from Remaining Gaps (now implemented) - Remove updateWithStart from Remaining Gaps (was implemented earlier) --- kotlin/sdk-api.md | 69 +++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 67 insertions(+), 2 deletions(-) diff --git a/kotlin/sdk-api.md b/kotlin/sdk-api.md index ec1173d..ba9c89d 100644 --- a/kotlin/sdk-api.md +++ b/kotlin/sdk-api.md @@ -448,6 +448,62 @@ interface OrderWorkflow { } ``` +#### Dynamic Handler Registration + +For workflows that need to handle signals, queries, or updates dynamically (without annotations), use the registration APIs: + +```kotlin +class DynamicWorkflowImpl : DynamicWorkflow { + private var state = mutableMapOf() + + override suspend fun execute(input: String): String { + // Register a named update handler with validator + KWorkflow.registerUpdateHandler( + "updateState", + validator = { args -> + val key = args.get(0, String::class.java) + require(key.isNotBlank()) { "Key cannot be blank" } + }, + handler = { args -> + val key = args.get(0, String::class.java) + val value = args.get(1, Any::class.java) + state[key] = value + "Updated $key" + } + ) + + // Register a named signal handler + KWorkflow.registerSignalHandler("notify") { args -> + val message = args.get(0, String::class.java) + println("Received: $message") + } + + // Register a named query handler + KWorkflow.registerQueryHandler("getState") { args -> + val key = args.get(0, String::class.java) + state[key] + } + + // Register dynamic handlers for unknown names (catch-all) + KWorkflow.registerDynamicUpdateHandler { updateName, args -> + "Handled unknown update: $updateName" + } + + KWorkflow.registerDynamicSignalHandler { signalName, args -> + println("Unknown signal: $signalName") + } + + KWorkflow.registerDynamicQueryHandler { queryName, args, resultClass -> + "Unknown query: $queryName" + } + + // Wait for completion signal + KWorkflow.awaitCondition { state["done"] == true } + return "Completed" + } +} +``` + ### Child Workflows Child workflows use the same stub-less pattern as activities: @@ -3036,15 +3092,24 @@ The following Java SDK workflow APIs have Kotlin equivalents in `KWorkflow`: | `Workflow.randomUUID()` | ✅ `KWorkflow.randomUUID()` | | `Workflow.newRandom()` | ✅ `KWorkflow.newRandom()` | +#### Dynamic Handler Registration ✅ +| Java SDK API | Kotlin SDK | +|--------------|------------| +| `Workflow.registerListener(DynamicSignalHandler)` | ✅ `KWorkflow.registerDynamicSignalHandler()` | +| `Workflow.registerListener(DynamicQueryHandler)` | ✅ `KWorkflow.registerDynamicQueryHandler()` | +| `Workflow.registerListener(DynamicUpdateHandler)` | ✅ `KWorkflow.registerDynamicUpdateHandler()` | +| N/A (new in Kotlin SDK) | ✅ `KWorkflow.registerSignalHandler()` | +| N/A (new in Kotlin SDK) | ✅ `KWorkflow.registerQueryHandler()` | +| N/A (new in Kotlin SDK) | ✅ `KWorkflow.registerUpdateHandler()` | +| N/A (new in Kotlin SDK) | ✅ `KWorkflow.registerDynamicUpdateValidator()` | + ### Remaining Gaps | Java SDK API | Status | |--------------|--------| -| `Workflow.registerListener(...)` | Dynamic signal/query/update handler registration - TODO | | `Workflow.newNexusServiceStub(...)` | Nexus support - deferred to separate project | | `Workflow.startNexusOperation(...)` | Nexus support - deferred to separate project | | `Workflow.getInstance()` | Advanced use case - low priority | -| `KWorkflowClient.updateWithStart()` | Client API - TODO | ### KActivityInfo Gaps From 516b83703a7e3b1305c286d352105bb407b0ccf1 Mon Sep 17 00:00:00 2001 From: Maxim Fateev Date: Sun, 4 Jan 2026 21:39:03 -0800 Subject: [PATCH 18/83] Refactor sdk-api.md into hierarchical documentation structure Break down the large sdk-api.md (3,479 lines) into a navigable hierarchy: - README.md: Main entry point with navigation - kotlin-idioms.md: Duration, null safety, property syntax - migration.md: Java SDK migration guide - api-parity.md: Java SDK comparison and gaps - workflows/: Workflow definition, signals/queries/updates, child workflows, timers, cancellation, continue-as-new - activities/: Definition, implementation, local activities, KActivity API - client/: KWorkflowClient, workflow handles, signalWithStart, updateWithStart - worker/: KWorkerFactory, KWorker setup - configuration/: KOptions, data conversion, interceptors - implementation/: Moved internal design docs (sdk-proposal, sdk-implementation, implementation-plan, test-framework-*, suspend-activities-design) All internal links verified and updated. --- kotlin/README.md | 119 + kotlin/activities/README.md | 69 + kotlin/activities/definition.md | 169 + kotlin/activities/implementation.md | 156 + kotlin/activities/local-activities.md | 149 + kotlin/api-parity.md | 104 + kotlin/client/README.md | 87 + kotlin/client/advanced.md | 194 + kotlin/client/workflow-client.md | 144 + kotlin/client/workflow-handle.md | 156 + kotlin/configuration/README.md | 69 + kotlin/configuration/data-conversion.md | 124 + kotlin/configuration/interceptors.md | 533 +++ kotlin/configuration/koptions.md | 226 ++ kotlin/implementation/README.md | 19 + .../implementation-plan.md | 2 +- .../sdk-implementation.md | 2 +- kotlin/{ => implementation}/sdk-proposal.md | 2 +- .../suspend-activities-design.md | 996 +++++ .../implementation/test-framework-design.md | 970 +++++ .../test-framework-implementation-design.md | 1756 +++++++++ .../test-framework-implementation-plan.md | 0 kotlin/kotlin-idioms.md | 105 + kotlin/migration.md | 125 + kotlin/sdk-api.md | 3479 ----------------- kotlin/worker/README.md | 46 + kotlin/worker/setup.md | 173 + kotlin/workflows/README.md | 282 ++ kotlin/workflows/cancellation.md | 147 + kotlin/workflows/child-workflows.md | 226 ++ kotlin/workflows/continue-as-new.md | 147 + kotlin/workflows/definition.md | 124 + kotlin/workflows/signals-queries.md | 146 + kotlin/workflows/timers-parallel.md | 121 + 34 files changed, 7685 insertions(+), 3482 deletions(-) create mode 100644 kotlin/README.md create mode 100644 kotlin/activities/README.md create mode 100644 kotlin/activities/definition.md create mode 100644 kotlin/activities/implementation.md create mode 100644 kotlin/activities/local-activities.md create mode 100644 kotlin/api-parity.md create mode 100644 kotlin/client/README.md create mode 100644 kotlin/client/advanced.md create mode 100644 kotlin/client/workflow-client.md create mode 100644 kotlin/client/workflow-handle.md create mode 100644 kotlin/configuration/README.md create mode 100644 kotlin/configuration/data-conversion.md create mode 100644 kotlin/configuration/interceptors.md create mode 100644 kotlin/configuration/koptions.md create mode 100644 kotlin/implementation/README.md rename kotlin/{ => implementation}/implementation-plan.md (98%) rename kotlin/{ => implementation}/sdk-implementation.md (99%) rename kotlin/{ => implementation}/sdk-proposal.md (98%) create mode 100644 kotlin/implementation/suspend-activities-design.md create mode 100644 kotlin/implementation/test-framework-design.md create mode 100644 kotlin/implementation/test-framework-implementation-design.md rename kotlin/{ => implementation}/test-framework-implementation-plan.md (100%) create mode 100644 kotlin/kotlin-idioms.md create mode 100644 kotlin/migration.md delete mode 100644 kotlin/sdk-api.md create mode 100644 kotlin/worker/README.md create mode 100644 kotlin/worker/setup.md create mode 100644 kotlin/workflows/README.md create mode 100644 kotlin/workflows/cancellation.md create mode 100644 kotlin/workflows/child-workflows.md create mode 100644 kotlin/workflows/continue-as-new.md create mode 100644 kotlin/workflows/definition.md create mode 100644 kotlin/workflows/signals-queries.md create mode 100644 kotlin/workflows/timers-parallel.md diff --git a/kotlin/README.md b/kotlin/README.md new file mode 100644 index 0000000..1dfc997 --- /dev/null +++ b/kotlin/README.md @@ -0,0 +1,119 @@ +# Kotlin SDK API Proposal + +This document describes the public API and developer experience for the Temporal Kotlin SDK. + +For implementation details, see [implementation/](./implementation/). + +## Overview + +The Kotlin SDK provides an idiomatic Kotlin experience for building Temporal workflows using coroutines and suspend functions. + +**Key Features:** + +* Coroutine-based workflows with `suspend fun` +* Full interoperability with Java SDK +* Kotlin Duration support (`30.seconds`) +* DSL builders for configuration +* Null safety (no `Optional`) + +**Requirements:** + +* Minimum Kotlin version: 1.8.x +* Coroutines library: kotlinx-coroutines-core 1.7.x+ + +## Design Principle + +**Use idiomatic Kotlin language patterns wherever possible instead of custom APIs.** + +The Kotlin SDK should feel natural to Kotlin developers by leveraging standard `kotlinx.coroutines` primitives. Custom APIs should only be introduced when Temporal-specific semantics cannot be achieved through standard patterns. + +| Pattern | Standard Kotlin | Temporal Integration | +|---------|-----------------|----------------------| +| Parallel execution | `coroutineScope { async { ... } }` | Works via deterministic dispatcher | +| Await multiple | `awaitAll(d1, d2)` | Standard kotlinx.coroutines | +| Sleep/delay | `delay(duration)` | Intercepted via `Delay` interface | +| Deferred results | `Deferred` | Standard + `Promise.toDeferred()` | + +This approach provides: +- **Familiar patterns**: Kotlin developers use patterns they already know +- **IDE support**: Full autocomplete and documentation for standard APIs +- **Ecosystem compatibility**: Works with existing coroutine libraries and utilities +- **Smaller API surface**: Less custom code to learn and maintain + +## Documentation Structure + +### Core Concepts + +- **[Kotlin Idioms](./kotlin-idioms.md)** - Duration, null safety, property syntax for queries +- **[Configuration](./configuration/)** - KOptions classes, data conversion, interceptors + +### Building Blocks + +- **[Workflows](./workflows/)** - Defining and implementing workflows + - [Definition](./workflows/definition.md) - Interfaces, suspend methods, Java interop + - [Signals, Queries & Updates](./workflows/signals-queries.md) - Communication patterns + - [Child Workflows](./workflows/child-workflows.md) - Orchestrating child workflows + - [Timers & Parallel Execution](./workflows/timers-parallel.md) - Delays, async patterns + - [Cancellation](./workflows/cancellation.md) - Handling cancellation, cleanup + - [Continue-As-New](./workflows/continue-as-new.md) - Long-running workflow patterns + +- **[Activities](./activities/)** - Defining and implementing activities + - [Definition](./activities/definition.md) - Interfaces, typed/string-based execution + - [Implementation](./activities/implementation.md) - Suspend activities, heartbeating + - [Local Activities](./activities/local-activities.md) - Short-lived local activities + +### Infrastructure + +- **[Client](./client/)** - Interacting with workflows + - [Workflow Client](./client/workflow-client.md) - KWorkflowClient, starting workflows + - [Workflow Handles](./client/workflow-handle.md) - Signals, queries, results + - [Advanced Operations](./client/advanced.md) - SignalWithStart, UpdateWithStart + +- **[Worker](./worker/)** - Running workflows and activities + - [Setup](./worker/setup.md) - KWorkerFactory, KWorker, registration + +### Reference + +- **[Migration Guide](./migration.md)** - Migrating from Java SDK +- **[API Parity](./api-parity.md)** - Java SDK comparison, gaps, not-needed APIs + +### Implementation + +- **[Implementation Details](./implementation/)** - Internal design documents + +## Quick Start + +```kotlin +// Define workflow interface +@WorkflowInterface +interface GreetingWorkflow { + @WorkflowMethod + suspend fun getGreeting(name: String): String +} + +// Implement workflow +class GreetingWorkflowImpl : GreetingWorkflow { + override suspend fun getGreeting(name: String): String { + return KWorkflow.executeActivity( + "composeGreeting", + KActivityOptions(startToCloseTimeout = 10.seconds), + "Hello", name + ) + } +} + +// Start worker +val factory = KWorkerFactory(client) +val worker = factory.newWorker("greetings") +worker.registerWorkflowImplementationTypes(GreetingWorkflowImpl::class) +factory.start() + +// Execute workflow +val result = client.executeWorkflow( + GreetingWorkflow::getGreeting, + KWorkflowOptions(workflowId = "greeting-123", taskQueue = "greetings"), + "Temporal" +) +``` + +See the [Complete Example](./workflows/README.md#complete-example) for a full order processing workflow demonstrating all features. diff --git a/kotlin/activities/README.md b/kotlin/activities/README.md new file mode 100644 index 0000000..e150c86 --- /dev/null +++ b/kotlin/activities/README.md @@ -0,0 +1,69 @@ +# Activities + +This section covers defining and implementing Temporal activities in Kotlin. + +## Overview + +Activities are the building blocks for interacting with external systems. The Kotlin SDK provides type-safe activity execution with suspend function support. + +## Documents + +| Document | Description | +|----------|-------------| +| [Definition](./definition.md) | Activity interfaces, typed and string-based execution | +| [Implementation](./implementation.md) | Implementing activities with suspend functions | +| [Local Activities](./local-activities.md) | Short-lived local activities, KActivity API | + +## Quick Reference + +### Basic Activity + +```kotlin +// Define activity interface +@ActivityInterface +interface GreetingActivities { + @ActivityMethod + suspend fun composeGreeting(greeting: String, name: String): String +} + +// Implement activity +class GreetingActivitiesImpl : GreetingActivities { + override suspend fun composeGreeting(greeting: String, name: String): String { + return "$greeting, $name!" + } +} +``` + +### Calling Activities from Workflows + +```kotlin +// Type-safe method reference +val greeting = KWorkflow.executeActivity( + GreetingActivities::composeGreeting, + KActivityOptions(startToCloseTimeout = 30.seconds), + "Hello", "World" +) + +// String-based (for cross-language interop) +val result = KWorkflow.executeActivity( + "composeGreeting", + KActivityOptions(startToCloseTimeout = 30.seconds), + "Hello", "World" +) +``` + +### Key Patterns + +| Pattern | API | +|---------|-----| +| Execute activity | `KWorkflow.executeActivity(Interface::method, options, args)` | +| Execute by name | `KWorkflow.executeActivity("name", options, args)` | +| Local activity | `KWorkflow.executeLocalActivity(Interface::method, options, args)` | +| Heartbeat | `KActivity.getContext().heartbeat(details)` | +| Suspend heartbeat | `KActivity.getContext().suspendHeartbeat(details)` | + +## Next Steps + +- Start with [Activity Definition](./definition.md) for interface patterns +- Learn about [Implementation](./implementation.md) for suspend activity patterns +- See [Local Activities](./local-activities.md) for short-lived activities diff --git a/kotlin/activities/definition.md b/kotlin/activities/definition.md new file mode 100644 index 0000000..4ce6361 --- /dev/null +++ b/kotlin/activities/definition.md @@ -0,0 +1,169 @@ +# Activity Definition + +## String-based Activity Execution + +For calling activities by name (useful for cross-language interop or dynamic activity names): + +```kotlin +// Execute activity by string name - suspend function, awaits result +val result = KWorkflow.executeActivity( + "activityName", + KActivityOptions( + startToCloseTimeout = 30.seconds, + retryOptions = KRetryOptions( + initialInterval = 1.seconds, + maximumAttempts = 3 + ) + ), + arg1, arg2 +) + +// Parallel execution - use standard coroutineScope { async {} } +val results = coroutineScope { + val d1 = async { KWorkflow.executeActivity("activity1", options, arg1) } + val d2 = async { KWorkflow.executeActivity("activity2", options, arg2) } + awaitAll(d1, d2) // Returns List +} +``` + +## Typed Activities + +The typed activity API uses direct method references - no stub creation needed. This approach: +- Provides full compile-time type safety for arguments and return types +- Allows different options (timeouts, retry policies) per activity call +- Works with both Kotlin `suspend` and Java non-suspend activity interfaces +- Similar to TypeScript and Python SDK patterns + +```kotlin +// Define activity interface +@ActivityInterface +interface GreetingActivities { + @ActivityMethod + suspend fun composeGreeting(greeting: String, name: String): String + + @ActivityMethod + suspend fun sendEmail(email: Email): SendResult + + @ActivityMethod + suspend fun log(message: String) +} + +// In workflow - direct method reference, no stub needed +val greeting = KWorkflow.executeActivity( + GreetingActivities::composeGreeting, // Direct reference to interface method + KActivityOptions(startToCloseTimeout = 30.seconds), + "Hello", "World" +) + +// Different activity, different options +val result = KWorkflow.executeActivity( + GreetingActivities::sendEmail, + KActivityOptions( + startToCloseTimeout = 2.minutes, + retryOptions = KRetryOptions(maximumAttempts = 5) + ), + email +) + +// Void activities work too +KWorkflow.executeActivity( + GreetingActivities::log, + KActivityOptions(startToCloseTimeout = 5.seconds), + "Processing started" +) +``` + +## Type Safety + +The API uses `KFunction` reflection to extract method metadata and provides compile-time type checking: + +```kotlin +// Compile error! Wrong argument types +KWorkflow.executeActivity( + GreetingActivities::composeGreeting, + options, + 123, true // ✗ Type mismatch: expected String, String +) +``` + +## Parallel Execution + +Use standard `coroutineScope { async { } }` for concurrent execution: + +```kotlin +override suspend fun parallelGreetings(names: List): List = coroutineScope { + names.map { name -> + async { + KWorkflow.executeActivity( + GreetingActivities::composeGreeting, + KActivityOptions(startToCloseTimeout = 10.seconds), + "Hello", name + ) + } + }.awaitAll() // Standard kotlinx.coroutines.awaitAll +} + +// Multiple different activities in parallel +val (result1, result2) = coroutineScope { + val d1 = async { KWorkflow.executeActivity(Activities::operation1, options, arg1) } + val d2 = async { KWorkflow.executeActivity(Activities::operation2, options, arg2) } + awaitAll(d1, d2) +} +``` + +> **Note:** We use standard Kotlin `async` instead of a custom `startActivity` method. The workflow's deterministic dispatcher ensures correct replay behavior. + +## Java Activity Interoperability + +Method references work regardless of whether the activity is defined in Kotlin or Java: + +```kotlin +// Java activity interface works seamlessly +// public interface JavaPaymentActivities { +// PaymentResult processPayment(String orderId, BigDecimal amount); +// } + +val result: PaymentResult = KWorkflow.executeActivity( + JavaPaymentActivities::processPayment, + KActivityOptions(startToCloseTimeout = 2.minutes), + orderId, amount +) +``` + +## Activity Execution API + +The `KWorkflow` object provides type-safe overloads using `KFunction` types: + +```kotlin +object KWorkflow { + // 1 argument + suspend fun executeActivity( + activity: KFunction2, + options: KActivityOptions, + arg1: A1 + ): R + + // 2 arguments + suspend fun executeActivity( + activity: KFunction3, + options: KActivityOptions, + arg1: A1, arg2: A2 + ): R + + // ... up to 6 arguments + + // String-based overloads + suspend inline fun executeActivity( + activityName: String, + options: KActivityOptions, + vararg args: Any? + ): R +} +``` + +> **Implementation Note:** `KFunction` provides `.name` for the method name and `.parameters[0].type` for the declaring interface. This metadata is used to make Temporal activity calls by name. + +## Next Steps + +- [Activity Implementation](./implementation.md) - Implementing activity classes +- [Local Activities](./local-activities.md) - Short-lived local activities diff --git a/kotlin/activities/implementation.md b/kotlin/activities/implementation.md new file mode 100644 index 0000000..062e4f4 --- /dev/null +++ b/kotlin/activities/implementation.md @@ -0,0 +1,156 @@ +# Activity Implementation + +There are two approaches for activity implementation, depending on whether you need Java interoperability. + +## Option A: Pure Kotlin (Recommended) + +For pure Kotlin codebases, define interfaces with `suspend` methods directly: + +```kotlin +@ActivityInterface +interface GreetingActivities { + @ActivityMethod + suspend fun composeGreeting(greeting: String, name: String): String + + @ActivityMethod + suspend fun sendEmail(email: Email): SendResult +} + +class GreetingActivitiesImpl( + private val emailService: EmailService +) : GreetingActivities { + + override suspend fun composeGreeting(greeting: String, name: String): String { + // Full coroutine support - use withContext, async I/O, etc. + return withContext(Dispatchers.IO) { + "$greeting, $name!" + } + } + + override suspend fun sendEmail(email: Email): SendResult { + // Suspend functions for async I/O + return emailService.send(email) + } +} + +// In workflow - direct method reference, no stub needed +val greeting = KWorkflow.executeActivity( + GreetingActivities::composeGreeting, + KActivityOptions(startToCloseTimeout = 30.seconds), + "Hello", "World" +) +``` + +## Option B: Java Interoperability (Parallel Interface Pattern) + +When you need to share activity interfaces with Java code (e.g., activities implemented in Java, or interfaces defined in a shared Java module), use the parallel interface pattern: + +```kotlin +// Interface for workflow calls - non-suspend for Java compatibility +// This interface can be defined in Java or Kotlin +@ActivityInterface +interface OrderActivities { + @ActivityMethod + fun validateOrder(order: Order): Boolean + + @ActivityMethod + fun chargePayment(order: Order): PaymentResult +} + +// Parallel suspend interface for Kotlin implementation +// Linked to the original interface via annotation +@KActivityImpl(activities = OrderActivities::class) +interface OrderActivitiesSuspend { + suspend fun validateOrder(order: Order): Boolean + suspend fun chargePayment(order: Order): PaymentResult +} + +// Kotlin implementation uses the suspend interface +class OrderActivitiesImpl( + private val paymentService: PaymentService +) : OrderActivitiesSuspend { + + override suspend fun validateOrder(order: Order): Boolean { + return order.items.isNotEmpty() && order.total > 0 + } + + override suspend fun chargePayment(order: Order): PaymentResult { + // Full suspend support for async operations + return paymentService.charge(order) + } +} + +// In workflow - use the non-suspend interface for method references +val isValid = KWorkflow.executeActivity( + OrderActivities::validateOrder, + KActivityOptions(startToCloseTimeout = 10.seconds), + order +) +``` + +## When to Use Which + +| Scenario | Approach | +|----------|----------| +| Pure Kotlin codebase | Option A - suspend interfaces | +| Calling Java-defined activities | Option A works (executeActivity handles it) | +| Kotlin activities with Java-defined interface | Option B - parallel interface | +| Shared interface library with Java | Option B - parallel interface | + +## Registering Activities + +```kotlin +// Option A: Register suspend implementation directly +worker.registerActivitiesImplementations(GreetingActivitiesImpl(emailService)) + +// Option B: Register implementation - binding inferred from @KActivityImpl annotation +worker.registerActivitiesImplementations(OrderActivitiesImpl(paymentService)) +``` + +## Heartbeating + +For long-running activities, use heartbeating to report progress and detect cancellation: + +```kotlin +class LongRunningActivitiesImpl : LongRunningActivities { + override suspend fun processLargeFile(filePath: String): ProcessResult { + val context = KActivity.getContext() + val lines = File(filePath).readLines() + + lines.forEachIndexed { index, line -> + // Use suspendHeartbeat in suspend activities + context.suspendHeartbeat(index) + + // Process line... + processLine(line) + } + + return ProcessResult(linesProcessed = lines.size) + } +} +``` + +## Heartbeat Details Recovery + +Retrieve heartbeat details from a previous failed attempt: + +```kotlin +override suspend fun resumableProcess(data: List): ProcessResult { + val context = KActivity.getContext() + + // Get progress from previous attempt if available + val startIndex: Int = context.getHeartbeatDetails() ?: 0 + + for (i in startIndex until data.size) { + context.suspendHeartbeat(i) + processItem(data[i]) + } + + return ProcessResult(success = true) +} +``` + +## Next Steps + +- [Local Activities](./local-activities.md) - Short-lived activities +- [Activity Definition](./definition.md) - Interface patterns diff --git a/kotlin/activities/local-activities.md b/kotlin/activities/local-activities.md new file mode 100644 index 0000000..d9286a4 --- /dev/null +++ b/kotlin/activities/local-activities.md @@ -0,0 +1,149 @@ +# Local Activities + +Local activities use the same stub-less pattern as regular activities. + +## Local Activity Execution + +```kotlin +@ActivityInterface +interface ValidationActivities { + fun validate(input: String): Boolean + fun sanitize(input: String): String +} + +val isValid = KWorkflow.executeLocalActivity( + ValidationActivities::validate, + KLocalActivityOptions(startToCloseTimeout = 5.seconds), + input +) + +val sanitized = KWorkflow.executeLocalActivity( + ValidationActivities::sanitize, + KLocalActivityOptions(startToCloseTimeout = 1.seconds), + input +) +``` + +## KWorkflow Local Activity Methods + +```kotlin +object KWorkflow { + /** + * Execute a local activity with type-safe method reference. + */ + suspend fun executeLocalActivity( + activity: KFunction2, + options: KLocalActivityOptions, + arg1: A1 + ): R + + suspend fun executeLocalActivity( + activity: KFunction1, + options: KLocalActivityOptions + ): R + + // ... up to 6 arguments +} +``` + +## KLocalActivityOptions + +```kotlin +/** + * Kotlin-native local activity options. + */ +data class KLocalActivityOptions( + val startToCloseTimeout: Duration? = null, + val scheduleToCloseTimeout: Duration? = null, + val localRetryThreshold: Duration? = null, + val retryOptions: KRetryOptions? = null +) +``` + +## When to Use Local Activities + +Local activities are best for: +- Short-lived operations (< 10 seconds) +- Operations that don't need to survive worker restarts +- High-frequency operations where scheduling overhead matters + +Use regular activities for: +- Long-running operations +- Operations that need heartbeating +- Operations that must survive worker failures + +## KActivity API + +`KActivity.getContext()` returns a `KActivityContext`, matching Java SDK's `Activity.getExecutionContext()` pattern: + +```kotlin +// In activity implementation + +// Get activity context (matches Java's Activity.getExecutionContext()) +val context = KActivity.getContext() + +// Get activity info +val info = context.info +println("Activity ${info.activityType}, attempt ${info.attempt}") + +// Heartbeat for long-running activities +// Use heartbeat() in regular activities +context.heartbeat(progressDetails) + +// Use suspendHeartbeat() in suspend activities for non-blocking operation +context.suspendHeartbeat(progressDetails) + +// Get heartbeat details from previous attempt (for retry scenarios) +val previousProgress: Int? = context.getHeartbeatDetails() + +// Logging - use activity logger for proper log context +val log = context.logger() // Uses activity type as logger name +log.info("Processing item") + +// Or with custom logger name +val customLog = context.logger("custom.logger") + +// Mark activity for async completion +context.doNotCompleteOnReturn() +val taskToken = context.taskToken +``` + +## KActivityContext Interface + +```kotlin +interface KActivityContext { + val info: KActivityInfo + fun heartbeat(details: Any? = null) + suspend fun suspendHeartbeat(details: Any? = null) + fun getHeartbeatDetails(detailsClass: Class): T? + val taskToken: ByteArray + fun doNotCompleteOnReturn() + val isDoNotCompleteOnReturn: Boolean + fun logger(): Logger + fun logger(name: String): Logger + fun logger(clazz: Class<*>): Logger +} + +// Reified extension for easier Kotlin usage +inline fun KActivityContext.getHeartbeatDetails(): T? +``` + +## KActivityInfo Interface + +```kotlin +interface KActivityInfo { + val activityId: String + val activityType: String + val workflowId: String + val attempt: Int + fun getHeartbeatDetails(): Payloads? + // ... other properties +} +``` + +> **Note:** `ActivityCompletionClient` for async activity completion uses the same API as the Java SDK. + +## Next Steps + +- [Activity Definition](./definition.md) - Interface patterns +- [Activity Implementation](./implementation.md) - Full implementation details diff --git a/kotlin/api-parity.md b/kotlin/api-parity.md new file mode 100644 index 0000000..1231870 --- /dev/null +++ b/kotlin/api-parity.md @@ -0,0 +1,104 @@ +# Java SDK API Parity + +This document describes the intentional differences between Java and Kotlin SDK APIs, and identifies remaining gaps. + +## APIs Not Needed in Kotlin + +The following Java SDK APIs are **not needed** in the Kotlin SDK due to language differences: + +| Java SDK API | Reason Not Needed | +|--------------|-------------------| +| `Workflow.sleep(Duration)` | Use `kotlinx.coroutines.delay()` - intercepted by dispatcher | +| `Workflow.newTimer(Duration)` | Use `async { delay(duration) }` for racing timers | +| `Workflow.wrap(Exception)` | Kotlin has no checked exceptions - not needed | +| `Activity.wrap(Throwable)` | Kotlin has no checked exceptions - not needed | +| `Workflow.newCancellationScope(...)` | Use Kotlin's `coroutineScope { }` with structured concurrency | +| `Workflow.newDetachedCancellationScope(...)` | Use `supervisorScope { }` or launch in parent scope | +| `Workflow.newWorkflowLock()` | Not needed - cooperative coroutines don't have true concurrency | +| `Workflow.newWorkflowSemaphore(...)` | Use structured concurrency patterns (e.g., `chunked().map { async }.awaitAll()`) | +| `Workflow.newQueue(...)` / `newWorkflowQueue(...)` | Use `mutableListOf` + `awaitCondition` - no concurrent access in suspend model | +| `Workflow.newPromise()` / `newFailedPromise(...)` | Use `CompletableDeferred` from kotlinx.coroutines | +| `Workflow.setDefaultActivityOptions(...)` | Pass `KActivityOptions` per call, or define shared `val defaultOptions` | +| `Workflow.setActivityOptions(...)` | Pass options per call | +| `Workflow.applyActivityOptions(...)` | Pass options per call | +| `Workflow.setDefaultLocalActivityOptions(...)` | Pass `KLocalActivityOptions` per call | +| `Workflow.applyLocalActivityOptions(...)` | Pass options per call | + +## Cancellation Support + +Kotlin coroutine cancellation is fully integrated with Temporal workflow cancellation: + +- Workflow cancellation triggers `coroutineScope.cancel(CancellationException(...))` +- Cancellation propagates to all child coroutines (activities, timers, child workflows) +- Activities use `suspendCancellableCoroutine` with `invokeOnCancellation` to cancel via Temporal +- Use standard Kotlin patterns: `try/finally`, `use { }`, `invokeOnCompletion` + +## Implemented APIs (Java SDK Parity) + +The following Java SDK workflow APIs have Kotlin equivalents in `KWorkflow`: + +### Search Attributes & Memo + +| Java SDK API | Kotlin SDK | +|--------------|------------| +| `Workflow.getTypedSearchAttributes()` | `KWorkflow.getTypedSearchAttributes()` | +| `Workflow.upsertTypedSearchAttributes(...)` | `KWorkflow.upsertTypedSearchAttributes()` | +| `Workflow.getMemo(key, class)` | `KWorkflow.getMemo()` | +| `Workflow.upsertMemo(...)` | `KWorkflow.upsertMemo()` | + +### Workflow State & Context + +| Java SDK API | Kotlin SDK | +|--------------|------------| +| `Workflow.getLastCompletionResult(class)` | `KWorkflow.getLastCompletionResult()` | +| `Workflow.getPreviousRunFailure()` | `KWorkflow.getPreviousRunFailure()` | +| `Workflow.isReplaying()` | `KWorkflow.isReplaying()` | +| `Workflow.getCurrentUpdateInfo()` | `KWorkflow.getCurrentUpdateInfo()` | +| `Workflow.isEveryHandlerFinished()` | `KWorkflow.isEveryHandlerFinished()` | +| `Workflow.setCurrentDetails(...)` | `KWorkflow.setCurrentDetails()` | +| `Workflow.getCurrentDetails()` | `KWorkflow.getCurrentDetails()` | +| `Workflow.getMetricsScope()` | `KWorkflow.getMetricsScope()` | + +### Side Effects & Utilities + +| Java SDK API | Kotlin SDK | +|--------------|------------| +| `Workflow.sideEffect(...)` | `KWorkflow.sideEffect()` | +| `Workflow.mutableSideEffect(...)` | `KWorkflow.mutableSideEffect()` | +| `Workflow.getVersion(...)` | `KWorkflow.getVersion()` | +| `Workflow.retry(...)` | `KWorkflow.retry()` | +| `Workflow.randomUUID()` | `KWorkflow.randomUUID()` | +| `Workflow.newRandom()` | `KWorkflow.newRandom()` | + +### Dynamic Handler Registration + +| Java SDK API | Kotlin SDK | +|--------------|------------| +| `Workflow.registerListener(DynamicSignalHandler)` | `KWorkflow.registerDynamicSignalHandler()` | +| `Workflow.registerListener(DynamicQueryHandler)` | `KWorkflow.registerDynamicQueryHandler()` | +| `Workflow.registerListener(DynamicUpdateHandler)` | `KWorkflow.registerDynamicUpdateHandler()` | +| N/A (new in Kotlin SDK) | `KWorkflow.registerSignalHandler()` | +| N/A (new in Kotlin SDK) | `KWorkflow.registerQueryHandler()` | +| N/A (new in Kotlin SDK) | `KWorkflow.registerUpdateHandler()` | +| N/A (new in Kotlin SDK) | `KWorkflow.registerDynamicUpdateValidator()` | + +## Remaining Gaps + +| Java SDK API | Status | +|--------------|--------| +| `Workflow.newNexusServiceStub(...)` | Nexus support - deferred to separate project | +| `Workflow.startNexusOperation(...)` | Nexus support - deferred to separate project | +| `Workflow.getInstance()` | Advanced use case - low priority | + +## KActivityInfo Gaps + +| Java ActivityInfo Field | Status | +|------------------------|--------| +| `workflowType` | Missing - workflow type that called the activity | +| `currentAttemptScheduledTimestamp` | Missing - current attempt schedule time | +| `retryOptions` | Missing - activity retry options | + +## Next Steps + +- [Migration Guide](./migration.md) - Practical migration steps +- [Kotlin Idioms](./kotlin-idioms.md) - Kotlin-specific patterns diff --git a/kotlin/client/README.md b/kotlin/client/README.md new file mode 100644 index 0000000..420e3da --- /dev/null +++ b/kotlin/client/README.md @@ -0,0 +1,87 @@ +# Client API + +This section covers the Kotlin client API for interacting with Temporal workflows. + +## Overview + +The Kotlin SDK provides `KWorkflowClient` with suspend functions and type-safe workflow APIs for starting and interacting with workflows. + +## Documents + +| Document | Description | +|----------|-------------| +| [Workflow Client](./workflow-client.md) | KWorkflowClient, starting workflows | +| [Workflow Handles](./workflow-handle.md) | Typed/Untyped handles, signals, queries, results | +| [Advanced Operations](./advanced.md) | SignalWithStart, UpdateWithStart | + +## Quick Reference + +### Creating a Client + +```kotlin +val service = WorkflowServiceStubs.newLocalServiceStubs() + +val client = KWorkflowClient(service) { + setNamespace("default") + setDataConverter(myConverter) +} +``` + +### Starting Workflows + +```kotlin +// Execute and wait for result +val result = client.executeWorkflow( + GreetingWorkflow::getGreeting, + KWorkflowOptions( + workflowId = "greeting-123", + taskQueue = "greetings" + ), + "Temporal" +) + +// Start async and get handle +val handle = client.startWorkflow( + GreetingWorkflow::getGreeting, + KWorkflowOptions( + workflowId = "greeting-123", + taskQueue = "greetings" + ), + "Temporal" +) +val result = handle.result() +``` + +### Interacting with Workflows + +```kotlin +val handle = client.getWorkflowHandle("order-123") + +// Signal +handle.signal(OrderWorkflow::updatePriority, Priority.HIGH) + +// Query +val status = handle.query(OrderWorkflow::status) + +// Update +val result = handle.executeUpdate(OrderWorkflow::addItem, newItem) + +// Cancel +handle.cancel() +``` + +### Key Patterns + +| Pattern | API | +|---------|-----| +| Execute workflow | `client.executeWorkflow(Interface::method, options, args)` | +| Start workflow | `client.startWorkflow(Interface::method, options, args)` | +| Get handle by ID | `client.getWorkflowHandle(workflowId)` | +| Signal with start | `client.signalWithStart(...)` | +| Update with start | `client.executeUpdateWithStart(...)` | + +## Next Steps + +- Start with [Workflow Client](./workflow-client.md) for client creation and basic operations +- Learn about [Workflow Handles](./workflow-handle.md) for interacting with running workflows +- See [Advanced Operations](./advanced.md) for atomic operations diff --git a/kotlin/client/advanced.md b/kotlin/client/advanced.md new file mode 100644 index 0000000..2f7f738 --- /dev/null +++ b/kotlin/client/advanced.md @@ -0,0 +1,194 @@ +# Advanced Client Operations + +## SignalWithStart + +Atomically start a workflow and send a signal. If the workflow already exists, only the signal is sent: + +```kotlin +// Returns KTypedWorkflowHandle - result type captured from method reference +val handle = client.signalWithStart( + workflow = OrderWorkflow::processOrder, + options = KWorkflowOptions( + workflowId = "order-123", + taskQueue = "orders" + ), + workflowArg = order, + signal = OrderWorkflow::updatePriority, + signalArg = Priority.HIGH +) + +// Can use typed handle for queries/signals +val status = handle.query(OrderWorkflow::status) +val result = handle.result() // Type inferred as OrderResult +``` + +## UpdateWithStart + +Atomically start a workflow and send an update. Behavior depends on `workflowIdConflictPolicy`: +- `USE_EXISTING`: sends update to existing workflow +- `FAIL`: throws exception if workflow already exists + +### Supporting Types + +```kotlin +/** + * Represents a workflow start operation for use with update-with-start. + * Created via KWorkflowClient.withStartWorkflowOperation(). + */ +class KWithStartWorkflowOperation { + /** Get the workflow result after the operation completes. */ + fun getResult(): R +} + +/** + * Options for update-with-start operations. + * Bundles the start operation with update-specific options. + */ +data class KUpdateWithStartOptions( + /** The workflow start operation (required) */ + val startWorkflowOperation: KWithStartWorkflowOperation, + + /** Stage to wait for before returning (required for startUpdateWithStart) */ + val waitForStage: WorkflowUpdateStage = WorkflowUpdateStage.ACCEPTED, + + /** Optional update ID for idempotency */ + val updateId: String? = null +) +``` + +### Execute and Wait for Completion + +```kotlin +// Step 1: Create the workflow start operation +val startOp = client.withStartWorkflowOperation( + OrderWorkflow::processOrder, + KWorkflowOptions( + workflowId = "order-123", + taskQueue = "orders", + workflowIdConflictPolicy = WorkflowIdConflictPolicy.USE_EXISTING // Required + ), + order +) + +// Step 2: Execute update with start (waits for update completion) +val updateResult: Boolean = client.executeUpdateWithStart( + OrderWorkflow::addItem, // Must be suspend function + KUpdateWithStartOptions(startWorkflowOperation = startOp), + newItem +) +println("Item added: $updateResult") + +// Step 3: Access workflow result if needed +val workflowResult: OrderResult = startOp.getResult() +``` + +### Start Async (Don't Wait for Completion) + +```kotlin +val startOp = client.withStartWorkflowOperation( + OrderWorkflow::processOrder, + KWorkflowOptions( + workflowId = "order-456", + taskQueue = "orders", + workflowIdConflictPolicy = WorkflowIdConflictPolicy.FAIL + ), + order +) + +// Start update and return immediately after it's accepted +val updateHandle: KUpdateHandle = client.startUpdateWithStart( + OrderWorkflow::addItem, + KUpdateWithStartOptions( + startWorkflowOperation = startOp, + waitForStage = WorkflowUpdateStage.ACCEPTED + ), + newItem +) + +// Later: get the update result +val result = updateHandle.result() +``` + +### No Arguments + +```kotlin +val startOp = client.withStartWorkflowOperation( + GreetingWorkflow::greet, + KWorkflowOptions( + workflowId = "greeting-789", + taskQueue = "greetings", + workflowIdConflictPolicy = WorkflowIdConflictPolicy.USE_EXISTING + ) +) + +val status: String = client.executeUpdateWithStart( + GreetingWorkflow::getStatus, // suspend fun getStatus(): String + KUpdateWithStartOptions(startWorkflowOperation = startOp) +) +``` + +## KWorkflowClient API for Advanced Operations + +```kotlin +class KWorkflowClient { + /** + * Atomically start a workflow and send a signal. + * If the workflow already exists, only the signal is sent. + */ + suspend fun signalWithStart( + workflow: KFunction2, + options: KWorkflowOptions, + workflowArg: A1, + signal: KFunction2, + signalArg: SA1 + ): KTypedWorkflowHandle + + /** + * Create a workflow start operation for use with update-with-start. + */ + fun withStartWorkflowOperation( + workflow: KFunction1, + options: KWorkflowOptions + ): KWithStartWorkflowOperation + + fun withStartWorkflowOperation( + workflow: KFunction2, + options: KWorkflowOptions, + arg1: A1 + ): KWithStartWorkflowOperation + + /** + * Atomically start a workflow and send an update, returning immediately after + * the update reaches the specified wait stage. + */ + suspend fun startUpdateWithStart( + update: KSuspendFunction1, + options: KUpdateWithStartOptions + ): KUpdateHandle + + suspend fun startUpdateWithStart( + update: KSuspendFunction2, + options: KUpdateWithStartOptions, + updateArg1: UA1 + ): KUpdateHandle + + /** + * Atomically start a workflow and execute an update, waiting for completion. + */ + suspend fun executeUpdateWithStart( + update: KSuspendFunction1, + options: KUpdateWithStartOptions + ): UR + + suspend fun executeUpdateWithStart( + update: KSuspendFunction2, + options: KUpdateWithStartOptions, + updateArg1: UA1 + ): UR +} +``` + +## Next Steps + +- [Workflow Client](./workflow-client.md) - Basic client operations +- [Workflow Handles](./workflow-handle.md) - Interacting with workflows diff --git a/kotlin/client/workflow-client.md b/kotlin/client/workflow-client.md new file mode 100644 index 0000000..c196296 --- /dev/null +++ b/kotlin/client/workflow-client.md @@ -0,0 +1,144 @@ +# Workflow Client + +## Creating a Client + +```kotlin +val service = WorkflowServiceStubs.newLocalServiceStubs() + +// Create KWorkflowClient with DSL configuration +val client = KWorkflowClient(service) { + setNamespace("default") + setDataConverter(myConverter) +} + +// For blocking calls from non-suspend contexts, use runBlocking +val result = runBlocking { + client.executeWorkflow(MyWorkflow::process, options, input) +} +``` + +## KWorkflowClient + +`KWorkflowClient` provides Kotlin-specific APIs with suspend functions for starting and interacting with workflows: + +```kotlin +/** + * Kotlin workflow client providing suspend functions and type-safe workflow APIs. + * + * @param service The WorkflowServiceStubs to connect to + * @param options DSL builder for WorkflowClientOptions + */ +class KWorkflowClient( + service: WorkflowServiceStubs, + options: WorkflowClientOptions.Builder.() -> Unit = {} +) { + /** The underlying WorkflowClient for advanced use cases */ + val workflowClient: WorkflowClient + + /** + * Start a workflow and return a handle for interaction. + * Does not wait for the workflow to complete. + */ + suspend fun startWorkflow( + workflow: KFunction1, + options: KWorkflowOptions + ): KTypedWorkflowHandle + + suspend fun startWorkflow( + workflow: KFunction2, + options: KWorkflowOptions, + arg: A1 + ): KTypedWorkflowHandle + + // Overloads for 2-6 arguments... + + /** + * Start a workflow and wait for its result. + * Suspends until the workflow completes. + */ + suspend fun executeWorkflow( + workflow: KFunction1, + options: KWorkflowOptions + ): R + + suspend fun executeWorkflow( + workflow: KFunction2, + options: KWorkflowOptions, + arg: A1 + ): R + + // Overloads for 2-6 arguments... + + /** + * Get a typed handle for an existing workflow by ID. + * Use this to signal, query, or get results from a workflow started elsewhere. + */ + inline fun getWorkflowHandle(workflowId: String): KWorkflowHandle + inline fun getWorkflowHandle(workflowId: String, runId: String): KWorkflowHandle + + /** + * Get an untyped handle for an existing workflow by ID. + * Use when you don't know the workflow type at compile time. + */ + fun getUntypedWorkflowHandle(workflowId: String): WorkflowHandle + fun getUntypedWorkflowHandle(workflowId: String, runId: String): WorkflowHandle +} +``` + +## Starting Workflows + +```kotlin +// Execute workflow and wait for result (suspend function) +val result = client.executeWorkflow( + GreetingWorkflow::getGreeting, + KWorkflowOptions( + workflowId = "greeting-123", + taskQueue = "greeting-queue", + workflowExecutionTimeout = 1.hours + ), + "Temporal" +) + +// Or start async and get handle +val handle = client.startWorkflow( + GreetingWorkflow::getGreeting, + KWorkflowOptions( + workflowId = "greeting-123", + taskQueue = "greeting-queue" + ), + "Temporal" +) +val result = handle.result() // Type inferred as String from method reference +``` + +## KWorkflowOptions + +```kotlin +/** + * Kotlin-native workflow options for client execution. + * workflowId and taskQueue are optional - if not provided, will use defaults or generate UUID. + */ +data class KWorkflowOptions( + val workflowId: String? = null, + val workflowIdReusePolicy: WorkflowIdReusePolicy? = null, + val workflowIdConflictPolicy: WorkflowIdConflictPolicy? = null, + val workflowRunTimeout: Duration? = null, + val workflowExecutionTimeout: Duration? = null, + val workflowTaskTimeout: Duration? = null, + val taskQueue: String? = null, + val retryOptions: KRetryOptions? = null, + val cronSchedule: String? = null, + val memo: Map? = null, + val typedSearchAttributes: SearchAttributes? = null, + val disableEagerExecution: Boolean = true, + val startDelay: Duration? = null, + val staticSummary: String? = null, + val staticDetails: String? = null, + val priority: Priority? = null +) +``` + +## Next Steps + +- [Workflow Handles](./workflow-handle.md) - Interacting with workflows +- [Advanced Operations](./advanced.md) - SignalWithStart, UpdateWithStart diff --git a/kotlin/client/workflow-handle.md b/kotlin/client/workflow-handle.md new file mode 100644 index 0000000..8e14138 --- /dev/null +++ b/kotlin/client/workflow-handle.md @@ -0,0 +1,156 @@ +# Workflow Handles + +For interacting with existing workflows (signals, queries, results, cancellation), use a typed or untyped handle. + +## Typed Handles + +```kotlin +// Get typed handle for existing workflow by ID +val handle = client.getWorkflowHandle("order-123") + +// Send signal - method reference provides type safety +handle.signal(OrderWorkflow::updatePriority, Priority.HIGH) + +// Query - method reference with compile-time type checking +val status = handle.query(OrderWorkflow::status) +val count = handle.query(OrderWorkflow::getItemCount) + +// Get result (suspends until workflow completes) +val result = handle.result() + +// Updates - execute and wait for result +val updateResult = handle.executeUpdate(OrderWorkflow::addItem, newItem) + +// Or start update async and get handle +val updateHandle = handle.startUpdate(OrderWorkflow::addItem, newItem) +val asyncResult = updateHandle.result() + +// Cancel or terminate +handle.cancel() +handle.terminate("No longer needed") + +// Workflow metadata +val info = handle.describe() +println("Workflow ID: ${handle.workflowId}, Run ID: ${handle.runId}") +``` + +## KWorkflowHandle API + +```kotlin +// Base handle - returned by getWorkflowHandle(id) +// Result type is unknown, must specify when calling result() +interface KWorkflowHandle { + val workflowId: String + val runId: String? + + // Result - requires explicit type since we don't know it + suspend fun result(): R + + // Signals - type-safe method references + suspend fun signal(method: KFunction1) + suspend fun signal(method: KFunction2, arg: A1) + suspend fun signal(method: KFunction3, arg1: A1, arg2: A2) + + // Queries - type-safe method references + fun query(method: KFunction1): R + fun query(method: KFunction2, arg: A1): R + + // Updates - execute and wait for result + suspend fun executeUpdate(method: KFunction1): R + suspend fun executeUpdate(method: KFunction2, arg: A1): R + + // Updates - start and get handle for async result + suspend fun startUpdate(method: KFunction1): KUpdateHandle + suspend fun startUpdate(method: KFunction2, arg: A1): KUpdateHandle + + // Get handle for existing update by ID + fun getKUpdateHandle(updateId: String): KUpdateHandle + + // Lifecycle + suspend fun cancel() + suspend fun terminate(reason: String? = null) + fun describe(): WorkflowExecutionInfo +} +``` + +## KTypedWorkflowHandle + +Extended handle returned by `startWorkflow()` - result type R is captured from the workflow method reference: + +```kotlin +interface KTypedWorkflowHandle : KWorkflowHandle { + // Result type is known from method reference - no type parameter needed + suspend fun result(): R +} +``` + +**How result type is captured:** + +```kotlin +// startWorkflow captures result type from method reference +suspend fun startWorkflow( + workflow: KFunction2, // R is captured here + options: WorkflowOptions, + arg: A1 +): KTypedWorkflowHandle // R is preserved in return type + +// Usage - result type is inferred +val handle = client.startWorkflow( + OrderWorkflow::processOrder, // KFunction2 + options, + order +) +val result: OrderResult = handle.result() // No type parameter needed! + +// getWorkflowHandle doesn't know result type +val existingHandle = client.getWorkflowHandle(workflowId) +val result = existingHandle.result() // Must specify type +``` + +## KUpdateHandle + +```kotlin +interface KUpdateHandle { + val updateId: String + suspend fun result(): R +} +``` + +## Untyped Handles + +For cases where you don't know the workflow type at compile time: + +```kotlin +// Untyped handle - signal/query by string name +val untypedHandle = client.getUntypedWorkflowHandle("order-123") + +// Operations use string names instead of method references +untypedHandle.signal("updatePriority", Priority.HIGH) +val status = untypedHandle.query("status") +val result = untypedHandle.result() + +// Cancel/terminate work the same +untypedHandle.cancel() +``` + +```kotlin +interface WorkflowHandle { + val workflowId: String + val runId: String? + + suspend fun result(): R + suspend fun signal(signalName: String, vararg args: Any?) + fun query(queryName: String, vararg args: Any?): R + suspend fun executeUpdate(updateName: String, vararg args: Any?): Any? + suspend fun cancel() + suspend fun terminate(reason: String? = null) + fun describe(): WorkflowExecutionInfo +} +``` + +This pattern matches Python SDK's `WorkflowHandle` with the same method names (`signal`, `query`, `result`, `cancel`, `terminate`, `execute_update`, `start_update`). + +## Next Steps + +- [Advanced Operations](./advanced.md) - SignalWithStart, UpdateWithStart +- [Workflow Client](./workflow-client.md) - Creating clients diff --git a/kotlin/configuration/README.md b/kotlin/configuration/README.md new file mode 100644 index 0000000..022d092 --- /dev/null +++ b/kotlin/configuration/README.md @@ -0,0 +1,69 @@ +# Configuration + +This section covers configuration options for the Kotlin SDK. + +## Documents + +| Document | Description | +|----------|-------------| +| [KOptions](./koptions.md) | Kotlin-native option classes (Activity, Workflow, Retry, etc.) | +| [Data Conversion](./data-conversion.md) | kotlinx.serialization, Jackson configuration | +| [Interceptors](./interceptors.md) | Worker, Workflow, Activity interceptors | + +## Overview + +The Kotlin SDK provides native Kotlin configuration classes that: +- Accept `kotlin.time.Duration` directly +- Use named parameters with default values +- Follow immutable data class patterns +- Support `copy()` for creating variants + +## Quick Reference + +### KOptions Classes + +```kotlin +// Activity options +KActivityOptions( + startToCloseTimeout = 30.seconds, + retryOptions = KRetryOptions(maximumAttempts = 3) +) + +// Local activity options +KLocalActivityOptions( + startToCloseTimeout = 5.seconds, + localRetryThreshold = 10.seconds +) + +// Child workflow options +KChildWorkflowOptions( + workflowId = "child-123", + workflowExecutionTimeout = 1.hours +) + +// Workflow options (for client) +KWorkflowOptions( + workflowId = "order-123", + taskQueue = "orders", + workflowExecutionTimeout = 24.hours +) +``` + +### Data Conversion + +```kotlin +// kotlinx.serialization (default) +@Serializable +data class Order(val id: String, val items: List) + +// Jackson (for Java interop) +val converter = DefaultDataConverter.newDefaultInstance().withPayloadConverterOverrides( + JacksonJsonPayloadConverter(KotlinObjectMapperFactory.new()) +) +``` + +## Next Steps + +- [KOptions](./koptions.md) - Full options reference +- [Data Conversion](./data-conversion.md) - Serialization configuration +- [Interceptors](./interceptors.md) - Cross-cutting concerns diff --git a/kotlin/configuration/data-conversion.md b/kotlin/configuration/data-conversion.md new file mode 100644 index 0000000..b0b8c90 --- /dev/null +++ b/kotlin/configuration/data-conversion.md @@ -0,0 +1,124 @@ +# Data Conversion + +The Kotlin SDK uses `kotlinx.serialization` by default for JSON serialization. It provides compile-time safety, no reflection overhead, and native Kotlin support. + +## kotlinx.serialization (Default) + +Annotate data classes with `@Serializable`: + +```kotlin +@Serializable +data class Order( + val id: String, + val items: List, + val status: OrderStatus +) + +@Serializable +data class OrderItem( + val productId: String, + val quantity: Int +) + +@Serializable +enum class OrderStatus { PENDING, PROCESSING, COMPLETED } +``` + +No additional configuration needed—the SDK automatically uses `kotlinx.serialization` for classes annotated with `@Serializable`. + +### Custom JSON Configuration + +```kotlin +val client = WorkflowClient(service) { + dataConverter = KotlinxSerializationDataConverter { + ignoreUnknownKeys = true + prettyPrint = false // default + encodeDefaults = true + } +} +``` + +### Polymorphic Types + +For sealed classes and interfaces: + +```kotlin +@Serializable +sealed class PaymentMethod { + @Serializable + @SerialName("credit_card") + data class CreditCard(val number: String, val expiry: String) : PaymentMethod() + + @Serializable + @SerialName("bank_transfer") + data class BankTransfer(val accountNumber: String) : PaymentMethod() +} +``` + +### Custom Serializers + +For types that need custom serialization: + +```kotlin +@Serializable(with = BigDecimalSerializer::class) +data class Money(val amount: BigDecimal, val currency: String) + +object BigDecimalSerializer : KSerializer { + override val descriptor = PrimitiveSerialDescriptor("BigDecimal", PrimitiveKind.STRING) + override fun serialize(encoder: Encoder, value: BigDecimal) = encoder.encodeString(value.toPlainString()) + override fun deserialize(decoder: Decoder) = BigDecimal(decoder.decodeString()) +} +``` + +## Jackson (Optional, for Java Interop) + +For mixed Java/Kotlin codebases or when integrating with existing Jackson-based infrastructure: + +```kotlin +val converter = DefaultDataConverter.newDefaultInstance().withPayloadConverterOverrides( + JacksonJsonPayloadConverter( + KotlinObjectMapperFactory.new() + ) +) + +val client = WorkflowClient(service) { + dataConverter = converter +} +``` + +> **Note:** Jackson requires the `jackson-module-kotlin` dependency and uses runtime reflection. Prefer `kotlinx.serialization` for pure Kotlin projects. + +### Custom Jackson Configuration + +```kotlin +val objectMapper = KotlinObjectMapperFactory.new().apply { + configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false) + registerModule(JavaTimeModule()) +} + +val converter = DefaultDataConverter.newDefaultInstance().withPayloadConverterOverrides( + JacksonJsonPayloadConverter(objectMapper) +) +``` + +## Comparison + +| Feature | kotlinx.serialization | Jackson | +|---------|----------------------|---------| +| Reflection | None (compile-time) | Required | +| Performance | Faster | Slower | +| Kotlin support | Native | Via module | +| Java interop | Limited | Excellent | +| Setup | `@Serializable` annotation | Automatic | +| Bundle size | Smaller | Larger | + +## Recommendations + +- **Pure Kotlin projects**: Use `kotlinx.serialization` +- **Mixed Java/Kotlin**: Consider Jackson for shared data types +- **Existing Jackson infrastructure**: Use Jackson for consistency + +## Next Steps + +- [KOptions](./koptions.md) - Configuration options +- [Interceptors](./interceptors.md) - Cross-cutting concerns diff --git a/kotlin/configuration/interceptors.md b/kotlin/configuration/interceptors.md new file mode 100644 index 0000000..887bb41 --- /dev/null +++ b/kotlin/configuration/interceptors.md @@ -0,0 +1,533 @@ +# Interceptors + +Interceptors allow you to intercept workflow and activity executions to add cross-cutting concerns like logging, metrics, tracing, and custom error handling. The Kotlin SDK provides suspend-function-aware interceptors that integrate naturally with coroutines. + +## KWorkerInterceptor + +The main entry point for interceptors. Registered with `KWorkerFactory` and called when workflows or activities are instantiated. + +```kotlin +/** + * Intercepts workflow and activity executions. + * + * Prefer extending [KWorkerInterceptorBase] and overriding only the methods you need. + */ +interface KWorkerInterceptor { + /** + * Called when a workflow is instantiated. May create a [KWorkflowInboundCallsInterceptor]. + * The returned interceptor must forward all calls to [next]. + */ + fun interceptWorkflow(next: KWorkflowInboundCallsInterceptor): KWorkflowInboundCallsInterceptor + + /** + * Called when an activity task is received. May create a [KActivityInboundCallsInterceptor]. + * The returned interceptor must forward all calls to [next]. + */ + fun interceptActivity(next: KActivityInboundCallsInterceptor): KActivityInboundCallsInterceptor +} + +/** + * Base implementation that passes through all calls. Extend this class and override only needed methods. + */ +open class KWorkerInterceptorBase : KWorkerInterceptor { + override fun interceptWorkflow(next: KWorkflowInboundCallsInterceptor) = next + override fun interceptActivity(next: KActivityInboundCallsInterceptor) = next +} +``` + +## Registering Interceptors + +Interceptors are registered via `KWorkerFactory`: + +```kotlin +val factory = KWorkerFactory(client) { + workerInterceptors = listOf( + LoggingInterceptor(), + MetricsInterceptor(), + TracingInterceptor() + ) +} + +val worker = factory.newWorker("task-queue") +worker.registerWorkflowImplementationTypes() +worker.registerActivitiesImplementations(MyActivitiesImpl()) + +factory.start() +``` + +## KWorkflowInboundCallsInterceptor + +Intercepts inbound calls to workflow execution (workflow method, signals, queries, updates). + +```kotlin +interface KWorkflowInboundCallsInterceptor { + /** Called when the workflow is instantiated. Use this to wrap the outbound interceptor. */ + suspend fun init(outboundCalls: KWorkflowOutboundCallsInterceptor) + + /** Called when the workflow main method is invoked. */ + suspend fun execute(input: KWorkflowInput): KWorkflowOutput + + /** Called when a signal is delivered to the workflow. */ + suspend fun handleSignal(input: KSignalInput) + + /** Called when a query is made to the workflow. Note: Queries must be synchronous. */ + fun handleQuery(input: KQueryInput): KQueryOutput + + /** Called to validate an update before execution. Throw an exception to reject. */ + fun validateUpdate(input: KUpdateInput) + + /** Called to execute an update after validation passes. */ + suspend fun executeUpdate(input: KUpdateInput): KUpdateOutput +} + +/** Base implementation that forwards all calls to the next interceptor. */ +open class KWorkflowInboundCallsInterceptorBase( + protected val next: KWorkflowInboundCallsInterceptor +) : KWorkflowInboundCallsInterceptor { + override suspend fun init(outboundCalls: KWorkflowOutboundCallsInterceptor) = next.init(outboundCalls) + override suspend fun execute(input: KWorkflowInput) = next.execute(input) + override suspend fun handleSignal(input: KSignalInput) = next.handleSignal(input) + override fun handleQuery(input: KQueryInput) = next.handleQuery(input) + override fun validateUpdate(input: KUpdateInput) = next.validateUpdate(input) + override suspend fun executeUpdate(input: KUpdateInput) = next.executeUpdate(input) +} +``` + +### Input/Output Classes + +```kotlin +data class KWorkflowInput(val header: Header, val arguments: Array) +data class KWorkflowOutput(val result: Any?) +data class KSignalInput(val signalName: String, val arguments: Array, val eventId: Long, val header: Header) +data class KQueryInput(val queryName: String, val arguments: Array, val header: Header) +data class KQueryOutput(val result: Any?) +data class KUpdateInput(val updateName: String, val arguments: Array, val header: Header) +data class KUpdateOutput(val result: Any?) +``` + +## KWorkflowOutboundCallsInterceptor + +Intercepts outbound calls from workflow code to Temporal APIs (activities, child workflows, timers, etc.). + +```kotlin +interface KWorkflowOutboundCallsInterceptor { + // Activities + fun executeActivity(input: KActivityInvocationInput): Deferred + fun executeLocalActivity(input: KLocalActivityInvocationInput): Deferred + + // Child Workflows + fun executeChildWorkflow(input: KChildWorkflowInvocationInput): KChildWorkflowInvocationOutput + + // Timers and Delays + suspend fun delay(duration: Duration) + fun newTimer(duration: Duration): Deferred + + // Await Conditions + suspend fun awaitCondition(timeout: Duration, reason: String, condition: () -> Boolean): Boolean + suspend fun awaitCondition(reason: String, condition: () -> Boolean) + + // Side Effects + fun sideEffect(resultClass: Class, func: () -> R): R + fun mutableSideEffect(id: String, resultClass: Class, updated: (R?, R?) -> Boolean, func: () -> R): R + + // Versioning + fun getVersion(changeId: String, minSupported: Int, maxSupported: Int): Int + + // Continue As New + fun continueAsNew(input: KContinueAsNewInput): Nothing + + // External Workflow Communication + fun signalExternalWorkflow(input: KSignalExternalInput): Deferred + fun cancelWorkflow(input: KCancelWorkflowInput): Deferred + + // Search Attributes and Memo + fun upsertTypedSearchAttributes(vararg updates: SearchAttributeUpdate<*>) + fun upsertMemo(memo: Map) + + // Utilities + fun newRandom(): Random + fun randomUUID(): UUID + fun currentTimeMillis(): Long +} + +/** + * Base implementation that forwards all calls to the next interceptor. + */ +open class KWorkflowOutboundCallsInterceptorBase( + protected val next: KWorkflowOutboundCallsInterceptor +) : KWorkflowOutboundCallsInterceptor { + override fun executeActivity(input: KActivityInvocationInput) = next.executeActivity(input) + override fun executeLocalActivity(input: KLocalActivityInvocationInput) = next.executeLocalActivity(input) + override fun executeChildWorkflow(input: KChildWorkflowInvocationInput) = next.executeChildWorkflow(input) + override suspend fun delay(duration: Duration) = next.delay(duration) + override fun newTimer(duration: Duration) = next.newTimer(duration) + override suspend fun awaitCondition(timeout: Duration, reason: String, condition: () -> Boolean) = + next.awaitCondition(timeout, reason, condition) + override suspend fun awaitCondition(reason: String, condition: () -> Boolean) = + next.awaitCondition(reason, condition) + override fun sideEffect(resultClass: Class, func: () -> R) = next.sideEffect(resultClass, func) + override fun mutableSideEffect(id: String, resultClass: Class, updated: (R?, R?) -> Boolean, func: () -> R) = + next.mutableSideEffect(id, resultClass, updated, func) + override fun getVersion(changeId: String, minSupported: Int, maxSupported: Int) = + next.getVersion(changeId, minSupported, maxSupported) + override fun continueAsNew(input: KContinueAsNewInput) = next.continueAsNew(input) + override fun signalExternalWorkflow(input: KSignalExternalInput) = next.signalExternalWorkflow(input) + override fun cancelWorkflow(input: KCancelWorkflowInput) = next.cancelWorkflow(input) + override fun upsertTypedSearchAttributes(vararg updates: SearchAttributeUpdate<*>) = + next.upsertTypedSearchAttributes(*updates) + override fun upsertMemo(memo: Map) = next.upsertMemo(memo) + override fun newRandom() = next.newRandom() + override fun randomUUID() = next.randomUUID() + override fun currentTimeMillis() = next.currentTimeMillis() +} +``` + +### Outbound Input/Output Classes + +```kotlin +/** + * Input for activity invocation. + */ +data class KActivityInvocationInput( + val activityName: String, + val resultClass: Class, + val resultType: Type, + val arguments: Array, + val options: KActivityOptions, + val header: Header +) + +/** + * Input for local activity invocation. + */ +data class KLocalActivityInvocationInput( + val activityName: String, + val resultClass: Class, + val resultType: Type, + val arguments: Array, + val options: KLocalActivityOptions, + val header: Header +) + +/** + * Input for child workflow invocation. + */ +data class KChildWorkflowInvocationInput( + val workflowId: String, + val workflowType: String, + val resultClass: Class, + val resultType: Type, + val arguments: Array, + val options: KChildWorkflowOptions, + val header: Header +) + +/** + * Output from child workflow start. + */ +data class KChildWorkflowInvocationOutput( + val result: Deferred, + val workflowExecution: Deferred +) + +/** + * Input for continue-as-new. + */ +data class KContinueAsNewInput( + val workflowType: String?, + val options: KContinueAsNewOptions?, + val arguments: Array, + val header: Header +) + +/** + * Input for signaling external workflow. + */ +data class KSignalExternalInput( + val execution: WorkflowExecution, + val signalName: String, + val arguments: Array, + val header: Header +) + +/** + * Input for canceling external workflow. + */ +data class KCancelWorkflowInput( + val execution: WorkflowExecution, + val reason: String? +) +``` + +## KActivityInboundCallsInterceptor + +Intercepts inbound calls to activity execution. + +```kotlin +interface KActivityInboundCallsInterceptor { + /** Called when activity is initialized. Provides access to the activity execution context. */ + fun init(context: ActivityExecutionContext) + + /** Called when activity method is invoked. This is a suspend function to support suspend activities. */ + suspend fun execute(input: KActivityExecutionInput): KActivityExecutionOutput +} + +open class KActivityInboundCallsInterceptorBase( + protected val next: KActivityInboundCallsInterceptor +) : KActivityInboundCallsInterceptor { + override fun init(context: ActivityExecutionContext) = next.init(context) + override suspend fun execute(input: KActivityExecutionInput) = next.execute(input) +} + +data class KActivityExecutionInput(val header: Header, val arguments: Array) +data class KActivityExecutionOutput(val result: Any?) +``` + +## Example: Logging Interceptor + +```kotlin +class LoggingInterceptor : KWorkerInterceptorBase() { + + override fun interceptWorkflow( + next: KWorkflowInboundCallsInterceptor + ): KWorkflowInboundCallsInterceptor { + return LoggingWorkflowInterceptor(next) + } + + override fun interceptActivity( + next: KActivityInboundCallsInterceptor + ): KActivityInboundCallsInterceptor { + return LoggingActivityInterceptor(next) + } +} + +private class LoggingWorkflowInterceptor( + next: KWorkflowInboundCallsInterceptor +) : KWorkflowInboundCallsInterceptorBase(next) { + + private val log = KWorkflow.logger() + + override suspend fun execute(input: KWorkflowInput): KWorkflowOutput { + log.info("Workflow started with ${input.arguments.size} arguments") + return try { + next.execute(input) + } catch (e: Exception) { + log.error("Workflow failed", e) + throw e + } finally { + log.info("Workflow completed") + } + } + + override suspend fun handleSignal(input: KSignalInput) { + log.info("Signal received: ${input.signalName}") + next.handleSignal(input) + } + + override fun handleQuery(input: KQueryInput): KQueryOutput { + log.debug("Query received: ${input.queryName}") + return next.handleQuery(input) + } +} + +private class LoggingActivityInterceptor( + next: KActivityInboundCallsInterceptor +) : KActivityInboundCallsInterceptorBase(next) { + + override suspend fun execute(input: KActivityExecutionInput): KActivityExecutionOutput { + val info = KActivity.getInfo() + val log = KActivity.logger() + + log.info("Activity ${info.activityType} started") + val startTime = System.currentTimeMillis() + + return try { + next.execute(input) + } finally { + val duration = System.currentTimeMillis() - startTime + log.info("Activity ${info.activityType} completed in ${duration}ms") + } + } +} +``` + +## Example: Metrics Interceptor + +```kotlin +class MetricsInterceptor( + private val meterProvider: MeterProvider +) : KWorkerInterceptorBase() { + + private val meter = meterProvider.get("temporal.sdk") + private val workflowCounter = meter.counterBuilder("workflow.executions").build() + private val activityCounter = meter.counterBuilder("activity.executions").build() + private val activityDuration = meter.histogramBuilder("activity.duration").build() + + override fun interceptWorkflow( + next: KWorkflowInboundCallsInterceptor + ): KWorkflowInboundCallsInterceptor { + return object : KWorkflowInboundCallsInterceptorBase(next) { + override suspend fun execute(input: KWorkflowInput): KWorkflowOutput { + val workflowType = KWorkflow.getInfo().workflowType + workflowCounter.add(1, Attributes.of( + AttributeKey.stringKey("workflow.type"), workflowType + )) + return next.execute(input) + } + } + } + + override fun interceptActivity( + next: KActivityInboundCallsInterceptor + ): KActivityInboundCallsInterceptor { + return object : KActivityInboundCallsInterceptorBase(next) { + override suspend fun execute(input: KActivityExecutionInput): KActivityExecutionOutput { + val info = KActivity.getInfo() + val startTime = System.nanoTime() + + return try { + val result = next.execute(input) + activityCounter.add(1, Attributes.of( + AttributeKey.stringKey("activity.type"), info.activityType, + AttributeKey.stringKey("status"), "success" + )) + result + } catch (e: Exception) { + activityCounter.add(1, Attributes.of( + AttributeKey.stringKey("activity.type"), info.activityType, + AttributeKey.stringKey("status"), "failure" + )) + throw e + } finally { + val durationMs = (System.nanoTime() - startTime) / 1_000_000.0 + activityDuration.record(durationMs, Attributes.of( + AttributeKey.stringKey("activity.type"), info.activityType + )) + } + } + } + } +} +``` + +## Example: Tracing with OpenTelemetry + +```kotlin +class TracingInterceptor( + private val tracer: Tracer +) : KWorkerInterceptorBase() { + + override fun interceptWorkflow( + next: KWorkflowInboundCallsInterceptor + ): KWorkflowInboundCallsInterceptor { + return TracingWorkflowInboundInterceptor(next, tracer) + } +} + +private class TracingWorkflowInboundInterceptor( + next: KWorkflowInboundCallsInterceptor, + private val tracer: Tracer +) : KWorkflowInboundCallsInterceptorBase(next) { + + private lateinit var outboundInterceptor: TracingWorkflowOutboundInterceptor + + override suspend fun init(outboundCalls: KWorkflowOutboundCallsInterceptor) { + // Wrap the outbound interceptor to trace outgoing calls + outboundInterceptor = TracingWorkflowOutboundInterceptor(outboundCalls, tracer) + next.init(outboundInterceptor) + } + + override suspend fun execute(input: KWorkflowInput): KWorkflowOutput { + // Extract trace context from header + val parentContext = extractContext(input.header) + + val span = tracer.spanBuilder("workflow.execute") + .setParent(parentContext) + .setAttribute("workflow.type", KWorkflow.getInfo().workflowType) + .startSpan() + + return try { + withContext(span.asContextElement()) { + next.execute(input) + } + } catch (e: Exception) { + span.recordException(e) + span.setStatus(StatusCode.ERROR) + throw e + } finally { + span.end() + } + } +} + +private class TracingWorkflowOutboundInterceptor( + next: KWorkflowOutboundCallsInterceptor, + private val tracer: Tracer +) : KWorkflowOutboundCallsInterceptorBase(next) { + + override fun executeActivity(input: KActivityInvocationInput): Deferred { + val span = tracer.spanBuilder("activity.schedule") + .setAttribute("activity.name", input.activityName) + .startSpan() + + // Inject trace context into header + val headerWithTrace = injectContext(input.header, span.context) + val inputWithTrace = input.copy(header = headerWithTrace) + + span.end() + return next.executeActivity(inputWithTrace) + } + + override fun executeChildWorkflow( + input: KChildWorkflowInvocationInput + ): KChildWorkflowInvocationOutput { + val span = tracer.spanBuilder("child_workflow.start") + .setAttribute("workflow.type", input.workflowType) + .setAttribute("workflow.id", input.workflowId) + .startSpan() + + val headerWithTrace = injectContext(input.header, span.context) + val inputWithTrace = input.copy(header = headerWithTrace) + + span.end() + return next.executeChildWorkflow(inputWithTrace) + } +} +``` + +## Example: Authentication Interceptor + +```kotlin +class AuthInterceptor( + private val authService: AuthService +) : KWorkerInterceptorBase() { + + override fun interceptWorkflow( + next: KWorkflowInboundCallsInterceptor + ): KWorkflowInboundCallsInterceptor { + return object : KWorkflowInboundCallsInterceptorBase(next) { + + override suspend fun handleSignal(input: KSignalInput) { + val authToken = input.header["authorization"]?.firstOrNull() + if (authToken != null && !authService.isAuthorized(authToken, "signal:${input.signalName}")) { + throw IllegalAccessException("Unauthorized signal: ${input.signalName}") + } + next.handleSignal(input) + } + + override suspend fun executeUpdate(input: KUpdateInput): KUpdateOutput { + val authToken = input.header["authorization"]?.firstOrNull() + if (authToken != null && !authService.isAuthorized(authToken, "update:${input.updateName}")) { + throw IllegalAccessException("Unauthorized update: ${input.updateName}") + } + return next.executeUpdate(input) + } + } + } +} +``` + +## Next Steps + +- [KOptions](./koptions.md) - Configuration options +- [Worker Setup](../worker/setup.md) - Registering interceptors diff --git a/kotlin/configuration/koptions.md b/kotlin/configuration/koptions.md new file mode 100644 index 0000000..a25fc21 --- /dev/null +++ b/kotlin/configuration/koptions.md @@ -0,0 +1,226 @@ +# KOptions Classes + +For a fully idiomatic Kotlin experience, the SDK provides dedicated `KOptions` data classes that accept `kotlin.time.Duration` directly, use named parameters with default values, and follow immutable data class patterns. + +## KActivityOptions + +```kotlin +/** + * Kotlin-native activity options with Duration support. + * All timeout properties accept kotlin.time.Duration directly. + */ +data class KActivityOptions( + val startToCloseTimeout: Duration? = null, + val scheduleToCloseTimeout: Duration? = null, + val scheduleToStartTimeout: Duration? = null, + val heartbeatTimeout: Duration? = null, + val taskQueue: String? = null, + val retryOptions: KRetryOptions? = null, + val cancellationType: ActivityCancellationType? = null, // Java default: TRY_CANCEL + val disableEagerExecution: Boolean = false +) +``` + +**Usage:** + +```kotlin +val result = KWorkflow.executeActivity( + GreetingActivities::composeGreeting, + KActivityOptions( + startToCloseTimeout = 30.seconds, + scheduleToCloseTimeout = 5.minutes, + heartbeatTimeout = 10.seconds, + retryOptions = KRetryOptions( + initialInterval = 1.seconds, + maximumAttempts = 3 + ) + ), + "Hello", "World" +) +``` + +## KLocalActivityOptions + +```kotlin +/** + * Kotlin-native local activity options. + */ +data class KLocalActivityOptions( + val startToCloseTimeout: Duration? = null, + val scheduleToCloseTimeout: Duration? = null, + val localRetryThreshold: Duration? = null, + val retryOptions: KRetryOptions? = null +) +``` + +**Usage:** + +```kotlin +val validated = KWorkflow.executeLocalActivity( + ValidationActivities::validate, + KLocalActivityOptions( + startToCloseTimeout = 5.seconds, + localRetryThreshold = 10.seconds + ), + input +) +``` + +## KRetryOptions + +```kotlin +/** + * Kotlin-native retry options. + */ +data class KRetryOptions( + val initialInterval: Duration = 1.seconds, + val backoffCoefficient: Double = 2.0, + val maximumInterval: Duration? = null, + val maximumAttempts: Int = 0, // 0 = unlimited + val doNotRetry: List = emptyList() +) +``` + +## KChildWorkflowOptions + +```kotlin +/** + * Kotlin-native child workflow options. + * All fields are optional - null values inherit from parent workflow or use Java SDK defaults. + */ +data class KChildWorkflowOptions( + val namespace: String? = null, + val workflowId: String? = null, + val workflowIdReusePolicy: WorkflowIdReusePolicy? = null, + val workflowRunTimeout: Duration? = null, + val workflowExecutionTimeout: Duration? = null, + val workflowTaskTimeout: Duration? = null, + val taskQueue: String? = null, + val retryOptions: KRetryOptions? = null, + val cronSchedule: String? = null, + val parentClosePolicy: ParentClosePolicy? = null, + val memo: Map? = null, + val typedSearchAttributes: SearchAttributes? = null, + val cancellationType: ChildWorkflowCancellationType? = null, + val staticSummary: String? = null, + val staticDetails: String? = null, + val priority: Priority? = null +) +``` + +**Usage:** + +```kotlin +val childResult = KWorkflow.executeChildWorkflow( + ChildWorkflow::process, + KChildWorkflowOptions( + workflowId = "child-123", + workflowExecutionTimeout = 1.hours, + retryOptions = KRetryOptions(maximumAttempts = 3) + ), + data +) +``` + +## KWorkflowOptions + +```kotlin +/** + * Kotlin-native workflow options for client execution. + * workflowId and taskQueue are optional - if not provided, will use defaults or generate UUID. + */ +data class KWorkflowOptions( + val workflowId: String? = null, + val workflowIdReusePolicy: WorkflowIdReusePolicy? = null, + val workflowIdConflictPolicy: WorkflowIdConflictPolicy? = null, + val workflowRunTimeout: Duration? = null, + val workflowExecutionTimeout: Duration? = null, + val workflowTaskTimeout: Duration? = null, + val taskQueue: String? = null, + val retryOptions: KRetryOptions? = null, + val cronSchedule: String? = null, + val memo: Map? = null, + val typedSearchAttributes: SearchAttributes? = null, + val disableEagerExecution: Boolean = true, + val startDelay: Duration? = null, + val staticSummary: String? = null, + val staticDetails: String? = null, + val priority: Priority? = null +) +``` + +**Usage:** + +```kotlin +val result = client.executeWorkflow( + OrderWorkflow::processOrder, + KWorkflowOptions( + workflowId = "order-123", + taskQueue = "orders", + workflowExecutionTimeout = 24.hours, + workflowRunTimeout = 1.hours + ), + order +) +``` + +## KContinueAsNewOptions + +```kotlin +/** + * Options for continuing a workflow as a new execution. + * All fields are optional - null values inherit from the current workflow. + */ +data class KContinueAsNewOptions( + val workflowRunTimeout: Duration? = null, + val taskQueue: String? = null, + val retryOptions: KRetryOptions? = null, + val workflowTaskTimeout: Duration? = null, + val memo: Map? = null, + val typedSearchAttributes: SearchAttributes? = null, + val contextPropagators: List? = null +) +``` + +## Copying with Modifications + +Data classes support the `copy()` function for creating variants: + +```kotlin +val baseOptions = KActivityOptions( + startToCloseTimeout = 30.seconds, + retryOptions = KRetryOptions(maximumAttempts = 3) +) + +// Create variant with different timeout +val longRunningOptions = baseOptions.copy( + startToCloseTimeout = 5.minutes, + heartbeatTimeout = 30.seconds +) +``` + +## KOptions vs DSL Builders + +The Kotlin SDK provides two approaches for configuring options: + +1. **KOptions (Recommended)** - Native Kotlin data classes designed for the Kotlin SDK +2. **DSL Builders** - Extension functions on Java SDK builders, provided as a stopgap for using Kotlin with the Java SDK + +> **Important:** When using the Kotlin SDK (`KWorkflow`, `KWorkflowClient`, etc.), always use KOptions classes. The DSL builders (`ActivityOptions { }`, `WorkflowOptions { }`, etc.) exist only for compatibility when using Kotlin with the Java SDK directly and should not be used with the Kotlin SDK APIs. + +| Aspect | DSL Builder (Java SDK interop) | KOptions (Kotlin SDK) | +|--------|--------------------------------|------------------------| +| Duration | Requires `.toJava()` conversion | Native `kotlin.time.Duration` | +| Syntax | `setStartToCloseTimeout(...)` | `startToCloseTimeout = ...` | +| Defaults | Must check Java defaults | Visible in constructor | +| Immutability | Mutable builder | Immutable data class | +| Copy | Manual rebuild | `copy()` function | +| IDE | Limited autocomplete | Full parameter hints | +| Usage | Java SDK only | Kotlin SDK | + +> **Implementation Note:** KOptions classes internally convert to Java SDK options. The conversion happens once when the activity/workflow is scheduled, so there's no runtime overhead during workflow execution. + +## Next Steps + +- [Data Conversion](./data-conversion.md) - Serialization configuration +- [Interceptors](./interceptors.md) - Cross-cutting concerns diff --git a/kotlin/implementation/README.md b/kotlin/implementation/README.md new file mode 100644 index 0000000..cac02c5 --- /dev/null +++ b/kotlin/implementation/README.md @@ -0,0 +1,19 @@ +# Implementation Details + +This folder contains internal design documents and implementation plans for the Kotlin SDK. + +## Documents + +| Document | Description | +|----------|-------------| +| [sdk-proposal.md](./sdk-proposal.md) | Original SDK proposal | +| [sdk-implementation.md](./sdk-implementation.md) | Implementation details and architecture | +| [implementation-plan.md](./implementation-plan.md) | Implementation phases and milestones | +| [suspend-activities-design.md](./suspend-activities-design.md) | Design for suspend activity support | +| [test-framework-design.md](./test-framework-design.md) | Testing framework design | +| [test-framework-implementation-design.md](./test-framework-implementation-design.md) | Testing framework implementation details | +| [test-framework-implementation-plan.md](./test-framework-implementation-plan.md) | Testing framework implementation plan | + +## Related + +For public API documentation, see the [parent folder](../). diff --git a/kotlin/implementation-plan.md b/kotlin/implementation/implementation-plan.md similarity index 98% rename from kotlin/implementation-plan.md rename to kotlin/implementation/implementation-plan.md index b252939..8bd03be 100644 --- a/kotlin/implementation-plan.md +++ b/kotlin/implementation/implementation-plan.md @@ -108,7 +108,7 @@ - ✅ Support for both regular and suspend activity mocks - ✅ Integration with `KTestWorkflowExtension` for automatic mock registration -### 3.3 Interceptors (Design Complete - see sdk-api.md) +### 3.3 Interceptors (Design Complete - see [interceptors.md](../configuration/interceptors.md)) - `KWorkerInterceptor` interface with `KWorkerInterceptorBase` - `KWorkflowInboundCallsInterceptor` with suspend functions and input/output data classes - `KWorkflowOutboundCallsInterceptor` with full API (activities, child workflows, timers, side effects, etc.) diff --git a/kotlin/sdk-implementation.md b/kotlin/implementation/sdk-implementation.md similarity index 99% rename from kotlin/sdk-implementation.md rename to kotlin/implementation/sdk-implementation.md index f9a7dc5..1a52400 100644 --- a/kotlin/sdk-implementation.md +++ b/kotlin/implementation/sdk-implementation.md @@ -2,7 +2,7 @@ This document describes the internal architecture and implementation details for the Temporal Kotlin SDK. -For public API and developer experience, see [sdk-api.md](./sdk-api.md). +For public API and developer experience, see the [SDK API documentation](../README.md). ## Phases diff --git a/kotlin/sdk-proposal.md b/kotlin/implementation/sdk-proposal.md similarity index 98% rename from kotlin/sdk-proposal.md rename to kotlin/implementation/sdk-proposal.md index 95b3653..95e61a2 100644 --- a/kotlin/sdk-proposal.md +++ b/kotlin/implementation/sdk-proposal.md @@ -15,7 +15,7 @@ The Kotlin SDK should feel natural to Kotlin developers by leveraging standard ` | Sleep/delay | `delay(duration)` | Intercepted via `Delay` interface | | Deferred results | `Deferred` | Standard + `Promise.toDeferred()` | -## [SDK API](./sdk-api.md) +## [SDK API](../README.md) Public API and developer experience documentation including: diff --git a/kotlin/implementation/suspend-activities-design.md b/kotlin/implementation/suspend-activities-design.md new file mode 100644 index 0000000..8d3850a --- /dev/null +++ b/kotlin/implementation/suspend-activities-design.md @@ -0,0 +1,996 @@ +# Design: Suspend Function Support for Kotlin Activities (Revised) + +## Overview + +This document proposes adding support for Kotlin suspend functions as activity implementations. This enables activities to use Kotlin's coroutine-based async I/O libraries (Ktor, R2DBC, etc.) without blocking threads. + +## Current State + +### Activity Execution Model + +Activities in the Java SDK are **thread-based**: + +``` +Worker Thread Pool + ↓ +ActivityTaskHandlerImpl.handle() + ↓ +POJOActivityTaskExecutor.execute() + ↓ +Method.invoke() [blocking] + ↓ +Return result → sendReply() [blocking gRPC] + ↓ +Release slot permit +``` + +- Activities run on dedicated threads from a thread pool +- Context is stored in thread-local storage (`ActivityInternal.currentActivityExecutionContext`) +- Activities can block the thread (e.g., HTTP calls, database queries) +- Result is sent to server via **blocking** gRPC call + +### Existing Async Pattern: Local Manual Completion + +The Java SDK already supports async activity completion via `useLocalManualCompletion()`: + +```java +public int execute() { + ActivityExecutionContext context = Activity.getExecutionContext(); + ManualActivityCompletionClient client = context.useLocalManualCompletion(); + + // Offload to thread pool - activity method returns immediately + ForkJoinPool.commonPool().execute(() -> { + try { + // Async work happens here + Object result = doAsyncWork(); + client.complete(result); // Complete when done + } catch (Exception e) { + client.fail(e); // Or fail on error + } + }); + + return 0; // Return immediately, thread is freed +} +``` + +**Key properties:** +- `useLocalManualCompletion()` marks activity for manual completion +- Returns `ManualActivityCompletionClient` for completing later +- Respects `maxConcurrentActivityExecutionSize` limit (slot stays reserved) +- Releases slot permit when `complete()`, `fail()`, or `reportCancellation()` is called + +### Problem + +Using `runBlocking` in a suspend activity wrapper defeats the purpose - it still ties up the thread pool thread during the entire coroutine execution. We need truly non-blocking execution. + +## Proposed Design + +### Key Insight + +Leverage the existing `useLocalManualCompletion()` pattern to achieve truly async suspend activities without modifying the core Java SDK. + +### Architecture Overview + +``` +┌─────────────────────────────────────────────────────────────────────┐ +│ Activity Task Arrives │ +└───────────────────────────────┬─────────────────────────────────────┘ + │ + ▼ +┌─────────────────────────────────────────────────────────────────────┐ +│ SuspendActivityTaskExecutor │ +│ │ +│ 1. Get ManualActivityCompletionClient via useLocalManualCompletion()│ +│ 2. Launch coroutine on CoroutineDispatcher │ +│ 3. Return immediately (thread freed) │ +└───────────────────────────────┬─────────────────────────────────────┘ + │ + ┌───────────────────────┴───────────────────────┐ + │ │ + ▼ ▼ +┌─────────────────────┐ ┌─────────────────────────┐ +│ Thread Pool Thread │ │ Coroutine Dispatcher │ +│ (freed immediately) │ │ │ +└─────────────────────┘ │ Execute suspend fun │ + │ │ │ + │ ▼ │ + │ On I/O suspend: │ + │ Thread released! │ + │ │ │ + │ ▼ │ + │ Resume on I/O complete │ + │ │ │ + │ ▼ │ + │ Complete via client │ + └─────────────────────────┘ + │ + ▼ + ┌─────────────────────────┐ + │ ManualActivityCompletion│ + │ Client.complete(result) │ + │ │ + │ - Sends result to server│ + │ - Releases slot permit │ + └─────────────────────────┘ +``` + +### Component 1: SuspendActivityInvoker + +A wrapper that intercepts suspend activity method invocations: + +```kotlin +// io.temporal.kotlin.internal.SuspendActivityInvoker + +internal class SuspendActivityInvoker( + private val activityInstance: Any, + private val method: KFunction<*>, + private val dispatcher: CoroutineDispatcher, + private val heartbeatInterval: Duration? = null // Optional auto-heartbeat +) { + /** + * Called by the activity framework. Returns immediately after launching coroutine. + */ + fun invoke( + context: ActivityExecutionContext, + args: Array + ): Any? { + // Get manual completion client - marks activity for async completion + val completionClient = context.useLocalManualCompletion() + + // Create cancellation-aware scope + val job = SupervisorJob() + val scope = CoroutineScope(dispatcher + job) + + // Launch coroutine - this returns immediately! + scope.launch { + // Create activity context element for coroutine + val kContext = KActivityContext(context, completionClient, job) + + withContext(KActivityContextElement(kContext)) { + try { + // Start auto-heartbeat if configured + val heartbeatJob = heartbeatInterval?.let { interval -> + launch { autoHeartbeat(completionClient, interval, job) } + } + + // Execute the suspend function + val result = method.callSuspend(activityInstance, *args) + + // Cancel heartbeat job + heartbeatJob?.cancel() + + // Complete successfully (uses Dispatchers.IO for blocking gRPC) + withContext(Dispatchers.IO) { + completionClient.complete(result) + } + } catch (e: CancellationException) { + // Coroutine was cancelled (activity cancellation or timeout) + withContext(Dispatchers.IO + NonCancellable) { + completionClient.reportCancellation(null) + } + } catch (e: Throwable) { + // Activity failed + withContext(Dispatchers.IO + NonCancellable) { + completionClient.fail(e) + } + } + } + } + + // Return immediately - actual result sent via completion client + return null + } + + /** + * Background job that sends heartbeats and monitors for cancellation. + */ + private suspend fun autoHeartbeat( + client: ManualActivityCompletionClient, + interval: Duration, + parentJob: Job + ) { + while (isActive) { + delay(interval) + try { + // recordHeartbeat throws CanceledFailure if activity is cancelled + withContext(Dispatchers.IO) { + client.recordHeartbeat(null) + } + } catch (e: CanceledFailure) { + // Activity was cancelled - cancel the parent job + parentJob.cancel(CancellationException("Activity cancelled", e)) + break + } + } + } +} +``` + +### Component 2: KActivityContext (Coroutine-aware) + +Activity context accessible from within coroutines: + +```kotlin +// io.temporal.kotlin.internal.KActivityContext + +internal class KActivityContext( + private val javaContext: ActivityExecutionContext, + private val completionClient: ManualActivityCompletionClient, + private val parentJob: Job +) { + val info: ActivityInfo get() = javaContext.info + val taskToken: ByteArray get() = javaContext.taskToken + + /** + * Sends a heartbeat. This is a suspend function that doesn't block. + * Throws CancellationException if the activity has been cancelled. + */ + suspend fun heartbeat(details: Any? = null) { + try { + withContext(Dispatchers.IO) { + completionClient.recordHeartbeat(details) + } + } catch (e: CanceledFailure) { + // Convert to coroutine cancellation + parentJob.cancel(CancellationException("Activity cancelled", e)) + throw CancellationException("Activity cancelled", e) + } + } + + /** + * Gets heartbeat details from a previous attempt. + */ + inline fun getHeartbeatDetails(): T? { + return javaContext.getHeartbeatDetails(T::class.java).orElse(null) + } +} + +// Coroutine context element +internal class KActivityContextElement( + val context: KActivityContext +) : AbstractCoroutineContextElement(Key) { + companion object Key : CoroutineContext.Key +} +``` + +### Component 3: KActivity Public API + +```kotlin +// io.temporal.kotlin.activity.KActivity + +public object KActivity { + + /** + * Gets activity info for the current execution. + * Works in both suspend and regular activities. + */ + public fun getInfo(): KActivityInfo { + // Try coroutine context first (suspend activities) + val kContext = currentCoroutineContextOrNull()?.get(KActivityContextElement)?.context + if (kContext != null) { + return KActivityInfo(kContext.info) + } + // Fall back to thread-local (regular activities) + return KActivityInfo(Activity.getExecutionContext().info) + } + + /** + * Sends a heartbeat with optional details. + * In suspend activities, this is non-blocking. + * + * @throws CancellationException if the activity has been cancelled + */ + public suspend fun heartbeat(details: T? = null) { + val kContext = coroutineContext[KActivityContextElement]?.context + ?: throw IllegalStateException("heartbeat() must be called from within an activity") + kContext.heartbeat(details) + } + + /** + * Gets heartbeat details from a previous attempt. + */ + public inline fun getHeartbeatDetails(): T? { + val kContext = currentCoroutineContextOrNull()?.get(KActivityContextElement)?.context + if (kContext != null) { + return kContext.getHeartbeatDetails() + } + return Activity.getExecutionContext() + .getHeartbeatDetails(T::class.java) + .orElse(null) + } + + /** + * Gets the task token for external completion scenarios. + */ + public fun getTaskToken(): ByteArray { + val kContext = currentCoroutineContextOrNull()?.get(KActivityContextElement)?.context + if (kContext != null) { + return kContext.taskToken + } + return Activity.getExecutionContext().taskToken + } +} + +// Helper to get coroutine context without throwing +private fun currentCoroutineContextOrNull(): CoroutineContext? { + return try { + // This works if we're in a coroutine + runBlocking { coroutineContext } + } catch (e: Exception) { + null + } +} +``` + +### Component 4: Activity Registration + +Detect suspend functions at registration and create appropriate invokers: + +```kotlin +// io.temporal.kotlin.worker.KWorkerFactory (enhanced) + +public class KWorkerFactory(client: KWorkflowClient) { + + private val activityDispatcher: CoroutineDispatcher = Dispatchers.Default + private val defaultHeartbeatInterval: Duration? = null // Optional + + /** + * Configure the dispatcher for suspend activities. + */ + public fun setActivityDispatcher(dispatcher: CoroutineDispatcher) { + this.activityDispatcher = dispatcher + } + + /** + * Register activity implementations. + * Automatically detects suspend functions and creates appropriate handlers. + */ + public fun registerActivitiesImplementations(vararg activityImplementations: Any) { + for (impl in activityImplementations) { + val activityInterfaces = findActivityInterfaces(impl::class) + + for (iface in activityInterfaces) { + for (method in iface.memberFunctions) { + if (isActivityMethod(method)) { + if (method.isSuspend) { + // Create suspend-aware invoker + val invoker = SuspendActivityInvoker( + activityInstance = impl, + method = method, + dispatcher = activityDispatcher, + heartbeatInterval = defaultHeartbeatInterval + ) + registerSuspendActivity(method, invoker) + } else { + // Use existing POJO registration + registerPOJOActivity(method, impl) + } + } + } + } + } + } + + private fun registerSuspendActivity(method: KFunction<*>, invoker: SuspendActivityInvoker) { + // Create a wrapper that the Java SDK can invoke + val wrapper = object : ActivityMethod { + fun execute(context: ActivityExecutionContext, args: Array): Any? { + return invoker.invoke(context, args) + } + } + // Register with Java SDK's activity registry + // ... registration logic + } +} +``` + +## Thread/Coroutine Flow Diagram + +``` +Timeline: +─────────────────────────────────────────────────────────────────────────────► + +Thread Pool Thread #1: +│ +├─ Poll for task ───────────────────────────────────────────────────────────── +│ │ +│ ├─ Receive activity task +│ │ │ +│ │ ├─ Call SuspendActivityInvoker.invoke() +│ │ │ │ +│ │ │ ├─ Get ManualActivityCompletionClient +│ │ │ ├─ Launch coroutine (async!) +│ │ │ └─ Return immediately +│ │ │ +│ │ └─ Thread freed! ◄───────────────────── +│ │ +│ └─ Poll for next task ──────────────────────────────────────────────────── + +Coroutine on Dispatcher: + │ + ├─ Start executing suspend fun + │ │ + │ ├─ Await HTTP call (suspend) + │ │ │ + │ │ └─ Thread released while waiting + │ │ + │ ├─ Resume on HTTP response + │ │ │ + │ │ └─ Continue processing + │ │ + │ └─ Return result + │ + ├─ Call client.complete(result) + │ │ + │ └─ gRPC to server (on Dispatchers.IO) + │ + └─ Slot permit released +``` + +## Usage Examples + +### Example 1: Basic Suspend Activity + +```kotlin +@ActivityInterface +interface HttpActivities { + suspend fun fetchUser(userId: String): User +} + +class HttpActivitiesImpl( + private val client: HttpClient // Ktor client +) : HttpActivities { + + override suspend fun fetchUser(userId: String): User { + // Non-blocking HTTP call - thread is released during I/O wait + return client.get("https://api.example.com/users/$userId").body() + } +} +``` + +### Example 2: Activity with Heartbeat + +```kotlin +@ActivityInterface +interface BatchActivities { + suspend fun processBatch(items: List): BatchResult +} + +class BatchActivitiesImpl : BatchActivities { + + override suspend fun processBatch(items: List): BatchResult { + val results = mutableListOf() + + for ((index, item) in items.withIndex()) { + // Process item asynchronously + val result = processItem(item) + results.add(result) + + // Non-blocking heartbeat - throws CancellationException if cancelled + KActivity.heartbeat(Progress(index + 1, items.size)) + } + + return BatchResult(results) + } + + private suspend fun processItem(item: Item): ItemResult { + // Async processing using Ktor, R2DBC, etc. + delay(100) // Simulated async work + return ItemResult(item.id, "processed") + } +} +``` + +### Example 3: Mixed Activities (Suspend and Regular) + +```kotlin +@ActivityInterface +interface MixedActivities { + // Regular activity - runs on thread pool, blocks during execution + fun computeHash(data: ByteArray): String + + // Suspend activity - thread freed during I/O + suspend fun fetchRemoteData(url: String): ByteArray +} + +class MixedActivitiesImpl : MixedActivities { + + // CPU-bound work - regular function is fine (uses thread pool) + override fun computeHash(data: ByteArray): String { + return MessageDigest.getInstance("SHA-256") + .digest(data) + .toHexString() + } + + // I/O-bound work - suspend function for efficiency + override suspend fun fetchRemoteData(url: String): ByteArray { + return httpClient.get(url).body() + } +} +``` + +### Example 4: Database Activity with R2DBC + +```kotlin +@ActivityInterface +interface DatabaseActivities { + suspend fun findUser(id: Long): User? + suspend fun saveUser(user: User): Long +} + +class DatabaseActivitiesImpl( + private val connectionFactory: ConnectionFactory // R2DBC +) : DatabaseActivities { + + override suspend fun findUser(id: Long): User? { + return connectionFactory.create().awaitSingle().use { conn -> + conn.createStatement("SELECT * FROM users WHERE id = $1") + .bind("$1", id) + .execute() + .awaitFirst() + .map { row -> row.toUser() } + .awaitFirstOrNull() + } + } + + override suspend fun saveUser(user: User): Long { + return connectionFactory.create().awaitSingle().use { conn -> + conn.createStatement( + "INSERT INTO users (name, email) VALUES ($1, $2) RETURNING id" + ) + .bind("$1", user.name) + .bind("$2", user.email) + .execute() + .awaitFirst() + .map { row -> row.get("id", Long::class.java)!! } + .awaitFirst() + } + } +} +``` + +### Example 5: Parallel Async Operations + +```kotlin +@ActivityInterface +interface AggregatorActivities { + suspend fun aggregateData(sources: List): AggregatedResult +} + +class AggregatorActivitiesImpl : AggregatorActivities { + + override suspend fun aggregateData(sources: List): AggregatedResult { + // Fetch from all sources in parallel - truly concurrent! + val results = coroutineScope { + sources.map { source -> + async { + fetchFromSource(source) + } + }.awaitAll() + } + + return AggregatedResult(results) + } + + private suspend fun fetchFromSource(source: String): SourceData { + return httpClient.get(source).body() + } +} +``` + +## Cancellation Handling + +### Cancellation Flow + +``` +Server sends cancellation + │ + ▼ +┌─────────────────────────────────────┐ +│ Heartbeat throws CanceledFailure │ +│ (either auto-heartbeat or manual) │ +└───────────────────┬─────────────────┘ + │ + ▼ +┌─────────────────────────────────────┐ +│ parentJob.cancel() called │ +│ - Cancels main coroutine │ +│ - Cancels all child coroutines │ +└───────────────────┬─────────────────┘ + │ + ▼ +┌─────────────────────────────────────┐ +│ CancellationException propagates │ +│ through coroutine hierarchy │ +└───────────────────┬─────────────────┘ + │ + ▼ +┌─────────────────────────────────────┐ +│ Caught in SuspendActivityInvoker │ +│ - Reports cancellation to server │ +│ - Releases slot permit │ +└─────────────────────────────────────┘ +``` + +### Cancellation Detection Options + +1. **Auto-heartbeat** (recommended for long-running activities): + - Configure heartbeat interval at registration + - Background job periodically heartbeats + - Cancellation detected within heartbeat interval + +2. **Manual heartbeat**: + - Activity explicitly calls `KActivity.heartbeat()` + - Cancellation detected at heartbeat points + +3. **Polling check** (for tight loops): + ```kotlin + suspend fun processItems(items: List) { + for (item in items) { + ensureActive() // Check for cancellation + process(item) + } + } + ``` + +## Coroutine Dispatcher: Thread Model + +### Where Do Coroutine Threads Come From? + +When a suspend activity is invoked, the coroutine runs on threads provided by a `CoroutineDispatcher`. +This is a **separate thread pool** from the Java SDK's activity executor thread pool. + +``` +┌─────────────────────────────────────────────────────────────────────────────┐ +│ JAVA SDK THREAD POOL │ +│ (ActivityWorker's executor) │ +│ │ +│ ┌─────────┐ ┌─────────┐ ┌─────────┐ ┌─────────┐ │ +│ │Thread-1 │ │Thread-2 │ │Thread-3 │ │Thread-4 │ ... │ +│ └────┬────┘ └────┬────┘ └────┬────┘ └────┬────┘ │ +│ │ │ │ │ │ +│ │ poll() │ poll() │ poll() │ poll() │ +│ │ │ │ │ │ +│ ▼ │ │ │ │ +│ invoke() ─────────┼────────────┼────────────┼──────────────────────┐ │ +│ │ │ │ │ │ │ +│ │ launch() │ │ │ │ │ +│ │ │ │ │ │ │ +│ ▼ │ │ │ │ │ +│ return (FREE!) │ │ │ │ │ +│ │ │ │ │ │ │ +│ ▼ │ │ │ │ │ +│ poll() again │ │ │ │ │ +│ │ │ │ │ │ +└─────────────────────┼────────────┼────────────┼──────────────────────┼──────┘ + │ │ │ │ + │ │ │ │ +┌─────────────────────┼────────────┼────────────┼──────────────────────┼──────┐ +│ │ │ │ │ │ +│ COROUTINE DISPATCHER THREAD POOL │ │ +│ (Kotlin coroutine threads) │ │ +│ │ │ +│ ┌─────────┐ ┌─────────┐ ┌─────────┐ ┌─────────┐ │ │ +│ │ Coro-1 │ │ Coro-2 │ │ Coro-3 │ │ Coro-4 │ ... │ │ +│ └────┬────┘ └────┬────┘ └────┬────┘ └────┬────┘ │ │ +│ │ │ │ │ │ │ +│ ◄────────────┼────────────┼────────────┼──────────────────────┘ │ +│ │ │ │ │ │ +│ ▼ │ │ │ │ +│ Execute suspend │ │ │ │ +│ function │ │ │ │ +│ │ │ │ │ │ +│ (suspend on I/O) │ │ │ │ +│ │ │ │ │ │ +│ ▼ │ │ │ │ +│ Thread released │ │ │ │ +│ . │ │ │ │ +│ . │ │ │ │ +│ . (waiting) │ │ │ │ +│ . │ │ │ │ +│ ▼ │ │ │ │ +│ Resume on Coro-3 ─┼────────────► │ │ +│ │ │ │ │ +│ │ ▼ │ │ +│ │ Continue execution │ │ +│ │ │ │ │ +│ │ ▼ │ │ +│ │ complete(result) │ │ +│ │ │ │ │ +└─────────────────────┴────────────┴────────────┴──────────────────────────────┘ +``` + +**Key point**: The coroutine can start on one thread (Coro-1), suspend, and resume on a completely +different thread (Coro-3). This is normal Kotlin coroutine behavior. + +### Built-in Dispatchers + +Kotlin provides several built-in dispatchers: + +| Dispatcher | Thread Pool | Size | Best For | +|------------|-------------|------|----------| +| `Dispatchers.Default` | Shared, fixed | CPU cores (min 2) | CPU-bound + async I/O | +| `Dispatchers.IO` | Shared, elastic | Up to 64+ threads | Blocking I/O calls | +| `Dispatchers.Unconfined` | Caller's thread | N/A | Testing only | + +```kotlin +// Dispatchers.Default - recommended for most cases +// Backed by a shared ForkJoinPool +val dispatcher = Dispatchers.Default + +// Dispatchers.IO - for blocking calls within suspend functions +// Shares threads with Default but can grow larger +val dispatcher = Dispatchers.IO + +// Limited parallelism - caps concurrent coroutines +val dispatcher = Dispatchers.Default.limitedParallelism(100) +``` + +### Custom Dispatcher from Java Executor + +You can create a dispatcher from any Java `Executor` or `ExecutorService`: + +```kotlin +import kotlinx.coroutines.asCoroutineDispatcher +import java.util.concurrent.Executors + +// From a fixed thread pool +val executor = Executors.newFixedThreadPool(50) +val dispatcher = executor.asCoroutineDispatcher() + +// From a cached thread pool (elastic) +val executor = Executors.newCachedThreadPool() +val dispatcher = executor.asCoroutineDispatcher() + +// From a custom ThreadPoolExecutor +val executor = ThreadPoolExecutor( + 10, // core pool size + 100, // max pool size + 60L, // keep-alive time + TimeUnit.SECONDS, + LinkedBlockingQueue() +) +val dispatcher = executor.asCoroutineDispatcher() + +// IMPORTANT: Remember to close the dispatcher when done +dispatcher.close() // Also shuts down the executor +``` + +### Configuration API + +```kotlin +// Option 1: Use built-in dispatcher (simplest) +val worker = factory.newWorker("my-task-queue") { + suspendActivityDispatcher = Dispatchers.Default +} + +// Option 2: Limited parallelism for backpressure +val worker = factory.newWorker("my-task-queue") { + // Max 100 concurrent suspend activities + suspendActivityDispatcher = Dispatchers.Default.limitedParallelism(100) +} + +// Option 3: Custom executor for full control +val activityExecutor = Executors.newFixedThreadPool(200) +val worker = factory.newWorker("my-task-queue") { + suspendActivityDispatcher = activityExecutor.asCoroutineDispatcher() +} + +// Option 4: Match Java SDK's activity thread pool size +val worker = factory.newWorker("my-task-queue") { + val maxConcurrent = workerOptions.maxConcurrentActivityExecutionSize + suspendActivityDispatcher = Dispatchers.Default.limitedParallelism(maxConcurrent) +} +``` + +### Dispatcher Lifecycle Management + +```kotlin +class KWorkerFactory(client: KWorkflowClient) : Closeable { + + private var customDispatcher: CloseableCoroutineDispatcher? = null + + fun newWorker(taskQueue: String, configure: WorkerConfig.() -> Unit): KWorker { + val config = WorkerConfig().apply(configure) + + // Track custom dispatchers for cleanup + if (config.suspendActivityDispatcher is CloseableCoroutineDispatcher) { + customDispatcher = config.suspendActivityDispatcher + } + + // ... + } + + override fun close() { + // Clean up custom dispatchers + customDispatcher?.close() + // ... + } +} +``` + +### Thread Flow Example + +``` +Time ──────────────────────────────────────────────────────────────────────────► + +Java SDK Thread Pool (4 threads): +┌──────────────────────────────────────────────────────────────────────────────┐ +│ T1: [poll]──[invoke]──[return]──[poll]──[invoke]──[return]──[poll]──... │ +│ T2: [poll]──────────────────────[invoke]──[return]──[poll]──... │ +│ T3: [poll]──[invoke]──[return]──[poll]──... │ +│ T4: [poll]──────────────────────────────[invoke]──[return]──[poll]──... │ +└──────────────────────────────────────────────────────────────────────────────┘ + │ │ │ │ + │ launch │ launch │ launch │ launch + ▼ ▼ ▼ ▼ + +Coroutine Dispatcher (separate pool): +┌──────────────────────────────────────────────────────────────────────────────┐ +│ C1: [run]─[suspend]─────────────[resume]─[complete] │ +│ C2: [run]─[suspend]───────────────────────[resume]─[suspend]─[resume] │ +│ C3: [run]─[complete] │ +│ C4: [run]─[suspend]─────────[resume]─[complete] │ +└──────────────────────────────────────────────────────────────────────────────┘ + +Legend: +- [poll] = Waiting for activity task from server +- [invoke] = Calling SuspendActivityInvoker.invoke() +- [return] = Method returns, thread freed +- [launch] = Coroutine scheduled on dispatcher +- [run] = Coroutine executing code +- [suspend] = Coroutine suspended (waiting for I/O) +- [resume] = Coroutine resumed after I/O +- [complete]= Activity completed via ManualActivityCompletionClient +``` + +### Recommendations + +1. **Start with `Dispatchers.Default`** - It's well-tuned for most workloads + +2. **Use `limitedParallelism()` for backpressure** - Prevents unbounded coroutine growth: + ```kotlin + Dispatchers.Default.limitedParallelism(maxConcurrentActivities) + ``` + +3. **Use `Dispatchers.IO` for blocking calls** - If your suspend activity must call blocking APIs: + ```kotlin + suspend fun myActivity() { + withContext(Dispatchers.IO) { + // Blocking call here won't block Default dispatcher + legacyBlockingApi.call() + } + } + ``` + +4. **Custom executor for isolation** - If you need suspend activities isolated from other coroutines: + ```kotlin + val dedicatedExecutor = Executors.newFixedThreadPool(50) + val dedicatedDispatcher = dedicatedExecutor.asCoroutineDispatcher() + ``` + +## Heartbeat Configuration + +```kotlin +val worker = factory.newWorker("my-task-queue") { + // Dispatcher for suspend activities + suspendActivityDispatcher = Dispatchers.Default.limitedParallelism(100) + + // Auto-heartbeat interval for suspend activities (optional) + // If set, a background coroutine sends heartbeats automatically + suspendActivityHeartbeatInterval = 30.seconds +} + +// Per-activity configuration via annotation (future enhancement) +@SuspendActivityOptions( + heartbeatInterval = "10s" +) +suspend fun myActivity(): Result +``` + +## Implementation Plan + +### Phase 1: Core Infrastructure +1. Create `SuspendActivityInvoker` class +2. Create `KActivityContext` with coroutine support +3. Create `KActivityContextElement` for coroutine context +4. Implement basic suspend activity execution + +### Phase 2: Cancellation Handling +1. Implement auto-heartbeat background job +2. Map `CanceledFailure` to coroutine cancellation +3. Ensure proper cleanup on cancellation + +### Phase 3: Activity Registration +1. Detect suspend functions at registration time +2. Create `SuspendActivityInvoker` for suspend methods +3. Register with Java SDK's activity framework + +### Phase 4: API and Configuration +1. Update `KActivity` API for coroutine support +2. Add dispatcher configuration options +3. Add heartbeat interval configuration + +### Phase 5: Testing +1. Unit tests for suspend activity execution +2. Integration tests with real async I/O (Ktor) +3. Cancellation handling tests +4. Concurrent execution tests + +## Comparison: Old Design vs New Design + +| Aspect | Old Design (runBlocking) | New Design (useLocalManualCompletion) | +|--------|--------------------------|---------------------------------------| +| Thread during suspend | **Blocked** | **Free** | +| I/O efficiency | Low | High | +| Concurrent activities | Limited by threads | Limited by coroutines (much higher) | +| Java SDK changes | None needed | None needed | +| Complexity | Lower | Moderate | +| Cancellation | Immediate | Within heartbeat interval | + +## Considerations + +### Dispatcher Selection + +**Dispatchers.Default** (recommended): +- Shared thread pool sized to CPU cores +- Good for mixed CPU/IO workloads +- Can be limited with `limitedParallelism(n)` + +**Dispatchers.IO**: +- Elastic thread pool for blocking I/O +- Overhead of thread creation +- Use if calling blocking APIs from suspend context + +**Custom dispatcher**: +- Full control over thread pool +- Can match `maxConcurrentActivityExecutionSize` for parity + +### Heartbeat Timing + +The cancellation detection latency equals the heartbeat interval. For activities that need fast cancellation response: +- Use shorter heartbeat intervals (e.g., 5 seconds) +- Or call `KActivity.heartbeat()` explicitly at key points + +### Error Handling + +| Exception Type | Handling | +|----------------|----------| +| Regular exception | `client.fail(e)` - Activity fails, may retry | +| `CancellationException` | `client.reportCancellation(null)` - Clean cancellation | +| Error during completion | Logged, slot released, activity marked failed | + +### Structured Concurrency + +Activities can use `coroutineScope` for structured concurrency: +```kotlin +suspend fun processInParallel(items: List): List { + return coroutineScope { + items.map { async { process(it) } }.awaitAll() + } +} +``` + +All child coroutines are cancelled if the activity is cancelled. + +## Open Questions + +1. **Default heartbeat interval**: Should we have a default auto-heartbeat interval for suspend activities, or require explicit configuration? + +2. **Exception translation**: Should we map specific Kotlin exceptions (e.g., `TimeoutCancellationException`) to specific Temporal failures? + +3. **Context propagation**: How to propagate MDC/tracing context through coroutines? + +4. **Metrics**: Should we expose coroutine-specific metrics (active coroutines, suspension points)? + +## Conclusion + +This design leverages the existing `useLocalManualCompletion()` pattern in the Java SDK to achieve truly non-blocking suspend activities. Key benefits: + +- **No thread blocking**: Executor thread is freed immediately after launching coroutine +- **High concurrency**: Can run many more concurrent I/O-bound activities than threads +- **No Java SDK changes**: Works with existing infrastructure +- **Kotlin-idiomatic**: Uses standard coroutine patterns and context +- **Backward compatible**: Regular (non-suspend) activities continue to work unchanged + +The approach provides efficient async I/O support while maintaining Temporal's reliability guarantees for activity execution, retries, and timeouts. diff --git a/kotlin/implementation/test-framework-design.md b/kotlin/implementation/test-framework-design.md new file mode 100644 index 0000000..843c2c5 --- /dev/null +++ b/kotlin/implementation/test-framework-design.md @@ -0,0 +1,970 @@ +# Kotlin SDK Test Framework Design + +## Overview + +This document describes the user experience for testing Temporal workflows and activities in the Kotlin SDK. The test framework provides Kotlin-idiomatic APIs while maintaining consistency with the Java SDK where appropriate. + +## Design Principles + +1. **Kotlin-Idiomatic**: DSL builders, suspend functions, extension functions, null safety +2. **Coroutine-Native**: Full support for suspend functions and coroutine-based testing +3. **Minimal Boilerplate**: Sensible defaults, concise APIs +4. **Java Interoperability**: Can test Kotlin workflows calling Java activities and vice versa +5. **Familiar to Java Users**: Similar structure and concepts as Java SDK test framework + +--- + +## Package Structure + +Following the Java SDK pattern: +``` +io.temporal.kotlin.testing +├── KTestWorkflowEnvironment +├── KTestWorkflowExtension +├── KTestActivityEnvironment +├── KTestActivityExtension +└── KTestEnvironmentOptions +``` + +--- + +## 1. KTestWorkflowEnvironment + +The core interface for workflow testing, providing an in-memory Temporal service with automatic time skipping. + +### Creating a Test Environment + +```kotlin +// Simple creation with defaults +val testEnv = KTestWorkflowEnvironment.newInstance() + +// With configuration DSL +val testEnv = KTestWorkflowEnvironment.newInstance { + namespace = "test-namespace" + initialTime = Instant.parse("2024-01-01T00:00:00Z") + useTimeskipping = true + + workerFactoryOptions { + // WorkerFactoryOptions configuration + } + + workflowClientOptions { + // WorkflowClientOptions configuration + } + + searchAttributes { + register("CustomKeyword", IndexedValueType.INDEXED_VALUE_TYPE_KEYWORD) + register("CustomInt", IndexedValueType.INDEXED_VALUE_TYPE_INT) + } +} +``` + +### Worker Management + +```kotlin +// Create a KWorker for a task queue (returns KWorker for pure Kotlin) +val worker: KWorker = testEnv.newWorker("my-task-queue") + +// With worker options DSL +val worker: KWorker = testEnv.newWorker("my-task-queue") { + maxConcurrentActivityExecutionSize = 100 + maxConcurrentWorkflowTaskExecutionSize = 50 +} + +// Register workflow and activity implementations +worker.registerWorkflowImplementationTypes() +worker.registerActivitiesImplementations(MyActivitiesImpl()) + +// Register Kotlin suspend activities +worker.registerSuspendActivities(MySuspendActivitiesImpl()) +``` + +### KWorker API + +`KWorker` is the Kotlin-idiomatic worker for pure Kotlin implementations: + +```kotlin +/** + * Kotlin worker that provides idiomatic APIs for registering + * Kotlin workflows and suspend activities. + */ +class KWorker { + /** The underlying Java Worker for interop scenarios */ + val worker: Worker + + /** Register Kotlin workflow implementation types */ + inline fun registerWorkflowImplementationTypes() + fun registerWorkflowImplementationTypes(vararg workflowClasses: KClass<*>) + fun registerWorkflowImplementationTypes( + options: WorkflowImplementationOptions, + vararg workflowClasses: KClass<*> + ) + + /** Register activity implementations (both regular and suspend) */ + fun registerActivitiesImplementations(vararg activities: Any) + + /** Register suspend activity implementations */ + fun registerSuspendActivities(vararg activities: Any) + + /** Register Nexus service implementations */ + fun registerNexusServiceImplementations(vararg services: Any) +} +``` + +**When to use `KWorker` vs `Worker`:** +- Use `KWorker` for pure Kotlin implementations (workflows and activities) +- Use `Worker` (via `worker` property) when mixing Java and Kotlin workflows/activities + +### Client Access + +```kotlin +// Get the workflow client (KWorkflowClient) +val client: KWorkflowClient = testEnv.workflowClient + +// Start and execute workflows +val result = client.executeWorkflow( + MyWorkflow::execute, + KWorkflowOptions(taskQueue = "my-task-queue"), + "input" +) +``` + +### Time Manipulation + +```kotlin +// Get current test time +val currentTime: Instant = testEnv.currentTime +val currentTimeMillis: Long = testEnv.currentTimeMillis + +// Sleep with time skipping (suspend function) +testEnv.sleep(5.minutes) +testEnv.sleep(Duration.ofMinutes(5)) // Java Duration also supported + +// Register delayed callbacks +testEnv.registerDelayedCallback(10.seconds) { + // This executes when test time reaches 10 seconds + println("10 seconds have passed in test time") +} +``` + +### Lifecycle Management + +```kotlin +// Start all registered workers +testEnv.start() + +// Shutdown +testEnv.shutdown() // Graceful shutdown +testEnv.shutdownNow() // Immediate shutdown + +// Await termination (suspend function) +testEnv.awaitTermination(30.seconds) + +// Check status +val isStarted: Boolean = testEnv.isStarted +val isShutdown: Boolean = testEnv.isShutdown +val isTerminated: Boolean = testEnv.isTerminated + +// Auto-close with use {} +KTestWorkflowEnvironment.newInstance().use { testEnv -> + // Test code here + // Automatically closed when block exits +} +``` + +### Diagnostics + +```kotlin +// Get diagnostic information (workflow histories) +val diagnostics: String = testEnv.getDiagnostics() +``` + +### Service Access (Advanced) + +```kotlin +// Direct access to gRPC stubs +val workflowStubs: WorkflowServiceStubs = testEnv.workflowServiceStubs +val operatorStubs: OperatorServiceStubs = testEnv.operatorServiceStubs +val namespace: String = testEnv.namespace +``` + +--- + +## 2. KTestWorkflowExtension (JUnit 5) + +A JUnit 5 extension that simplifies workflow testing with automatic lifecycle management and parameter injection. + +### Basic Usage + +```kotlin +class MyWorkflowTest { + companion object { + @JvmField + @RegisterExtension + val testWorkflow = kTestWorkflowExtension { + registerWorkflowImplementationTypes() + setActivityImplementations(MyActivitiesImpl()) + } + } + + @Test + fun `test workflow execution`(workflow: MyWorkflow) { + val result = workflow.execute("input") + assertEquals("expected", result) + } + + @Test + fun `test with environment access`( + workflow: MyWorkflow, + testEnv: KTestWorkflowEnvironment, + client: KWorkflowClient + ) { + // Access to all test components via parameter injection + testEnv.sleep(5.minutes) + val result = workflow.execute("input") + assertEquals("expected", result) + } +} +``` + +### Configuration DSL + +```kotlin +val testWorkflow = kTestWorkflowExtension { + // Workflow Registration + registerWorkflowImplementationTypes() + registerWorkflowImplementationTypes() + + // With workflow implementation options + registerWorkflowImplementationTypes { + failWorkflowExceptionTypes = listOf(MyBusinessException::class.java) + } + + // Activity Registration + setActivityImplementations(MyActivitiesImpl()) + + // Suspend Activity Registration + setSuspendActivityImplementations(MySuspendActivitiesImpl()) + + // Nexus Service Registration + setNexusServiceImplementations(MyNexusServiceImpl()) + + // Worker Configuration + workerOptions { + maxConcurrentActivityExecutionSize = 100 + } + + workerFactoryOptions { + // WorkerFactoryOptions configuration + } + + // Client Configuration + workflowClientOptions { + // WorkflowClientOptions configuration + } + + namespace = "TestNamespace" // Default: "UnitTest" + + // Service Selection + useInternalService() // Default - in-memory service + // OR + useExternalService() // Uses ClientConfigProfile.load() - reads from env vars (TEMPORAL_ADDRESS, etc.) + // OR + useExternalService("custom-host:7233") // Explicit address override + + // Time Configuration + initialTime = Instant.parse("2024-01-01T00:00:00Z") + useTimeskipping = true // Default: true + + // Search Attributes + searchAttributes { + register("CustomAttr", IndexedValueType.INDEXED_VALUE_TYPE_KEYWORD) + } + + // Defer worker startup (for mock registration) + doNotStart = true +} +``` + +### Parameter Injection + +The extension supports injecting the following types into test methods: + +| Type | Description | +|------|-------------| +| `KTestWorkflowEnvironment` | The test environment | +| `KWorkflowClient` | The workflow client | +| `KWorkflowOptions` | Default workflow options | +| `KWorker` | The Kotlin worker instance | +| `Worker` | The Java worker instance (for mixed Java/Kotlin) | +| `` (workflow interface) | A workflow stub ready to execute | + +**Note:** Use `KWorker` for pure Kotlin implementations. Use `Worker` only when mixing Java and Kotlin workflows/activities in the same test. + +```kotlin +@Test +fun `test with all injected parameters`( + testEnv: KTestWorkflowEnvironment, + client: KWorkflowClient, + worker: KWorker, + options: KWorkflowOptions, + workflow: MyWorkflow // Automatically creates stub +) { + // All parameters automatically injected +} +``` + +### Initial Time Annotation + +Override the initial time for specific tests: + +```kotlin +@Test +@WorkflowInitialTime("2024-06-15T12:00:00Z") +fun `test with specific initial time`(workflow: MyWorkflow) { + // Test runs with specified initial time +} +``` + +### Testing with Mocks + +```kotlin +class MockedActivityTest { + companion object { + @JvmField + @RegisterExtension + val testWorkflow = kTestWorkflowExtension { + registerWorkflowImplementationTypes() + doNotStart = true // Important: defer startup for mock registration + } + } + + @Test + fun `test with mocked activities`( + testEnv: KTestWorkflowEnvironment, + worker: KWorker, + workflow: MyWorkflow + ) { + // Create mock + val mockActivity = mock { + on { doSomething(any()) } doReturn "mocked result" + } + + // Register mock and start + worker.registerActivitiesImplementations(mockActivity) + testEnv.start() + + // Execute workflow + val result = workflow.execute("input") + assertEquals("mocked result", result) + + // Verify + verify(mockActivity).doSomething(any()) + } +} +``` + +--- + +## 3. KTestActivityEnvironment + +For unit testing activity implementations in isolation. + +### Creating an Activity Test Environment + +```kotlin +// Simple creation +val activityEnv = KTestActivityEnvironment.newInstance() + +// With configuration +val activityEnv = KTestActivityEnvironment.newInstance { + workflowClientOptions { + // Configuration + } +} +``` + +### Registering and Testing Activities + +```kotlin +// Register activity implementations +activityEnv.registerActivitiesImplementations(MyActivitiesImpl()) + +// Register suspend activities +activityEnv.registerSuspendActivities(MySuspendActivitiesImpl()) + +// Execute activity using method reference (same pattern as KWorkflow.executeActivity) +val result = activityEnv.executeActivity( + MyActivity::doSomething, + KActivityOptions(startToCloseTimeout = 30.seconds), + "input" +) +assertEquals("expected", result) + +// With retry options +val result = activityEnv.executeActivity( + MyActivity::processOrder, + KActivityOptions( + startToCloseTimeout = 30.seconds, + retryOptions = KRetryOptions(maximumAttempts = 3) + ), + orderData +) +``` + +### Testing Local Activities + +```kotlin +val result = activityEnv.executeLocalActivity( + MyActivity::validate, + KLocalActivityOptions(startToCloseTimeout = 10.seconds), + input +) +assertEquals("valid", result) +``` + +### Heartbeat Testing + +```kotlin +// Set heartbeat details for the next activity call (simulates retry with heartbeat) +activityEnv.setHeartbeatDetails("checkpoint-data") + +// Listen for heartbeats during activity execution +activityEnv.setActivityHeartbeatListener { details -> + println("Activity heartbeat: $details") +} + +// With complex types +activityEnv.setActivityHeartbeatListener { progress -> + println("Progress: ${progress.percentage}%") +} +``` + +### Cancellation Testing + +```kotlin +@Test +fun `test activity cancellation handling`() { + val activityEnv = KTestActivityEnvironment.newInstance() + activityEnv.registerActivitiesImplementations(MyActivitiesImpl()) + + // Request cancellation during activity execution + activityEnv.requestCancelActivity() + + assertThrows { + activityEnv.executeActivity( + MyActivity::longRunningTask, + KActivityOptions(startToCloseTimeout = 1.minutes) + ) + } +} +``` + +### Lifecycle + +```kotlin +// Auto-close with use {} +KTestActivityEnvironment.newInstance().use { activityEnv -> + // Test code +} + +// Manual close +activityEnv.close() +``` + +--- + +## 4. KTestActivityExtension (JUnit 5) + +A JUnit 5 extension for simplified activity testing. + +### Basic Usage + +```kotlin +class MyActivityTest { + companion object { + @JvmField + @RegisterExtension + val testActivity = kTestActivityExtension { + setActivityImplementations(MyActivitiesImpl()) + } + } + + @Test + fun `test activity`(activityEnv: KTestActivityEnvironment) { + val result = activityEnv.executeActivity( + MyActivity::doSomething, + KActivityOptions(startToCloseTimeout = 30.seconds), + "input" + ) + assertEquals("expected", result) + } + + @Test + fun `test with heartbeat`(activityEnv: KTestActivityEnvironment) { + activityEnv.setHeartbeatDetails("checkpoint") + val result = activityEnv.executeActivity( + MyActivity::resumableTask, + KActivityOptions( + startToCloseTimeout = 1.minutes, + heartbeatTimeout = 10.seconds + ) + ) + assertEquals("completed", result) + } +} +``` + +### Configuration + +```kotlin +val testActivity = kTestActivityExtension { + setActivityImplementations(MyActivitiesImpl()) + setSuspendActivityImplementations(MySuspendActivitiesImpl()) + + testEnvironmentOptions { + workflowClientOptions { + // Configuration + } + } +} +``` + +--- + +## 5. Testing Suspend Activities + +The Kotlin SDK provides first-class support for testing suspend activities. + +### Unit Testing Suspend Activities + +```kotlin +class SuspendActivityTest { + companion object { + @JvmField + @RegisterExtension + val testActivity = kTestActivityExtension { + setSuspendActivityImplementations(MySuspendActivitiesImpl()) + } + } + + @Test + fun `test suspend activity`(activityEnv: KTestActivityEnvironment) = runTest { + val result = activityEnv.executeActivity( + MySuspendActivities::fetchDataAsync, + KActivityOptions(startToCloseTimeout = 30.seconds), + "key" + ) + assertEquals("value", result) + } +} +``` + +### Integration Testing with Workflows + +```kotlin +class WorkflowWithSuspendActivitiesTest { + companion object { + @JvmField + @RegisterExtension + val testWorkflow = kTestWorkflowExtension { + registerWorkflowImplementationTypes() + setSuspendActivityImplementations(MySuspendActivitiesImpl()) + } + } + + @Test + fun `workflow calls suspend activities`(workflow: MyWorkflow) { + val result = workflow.processData("input") + assertEquals("processed", result) + } +} +``` + +--- + +## 6. Common Testing Patterns + +### Pattern 1: Basic Workflow Test + +```kotlin +class BasicWorkflowTest { + companion object { + @JvmField + @RegisterExtension + val testWorkflow = kTestWorkflowExtension { + registerWorkflowImplementationTypes() + setActivityImplementations(GreetingActivitiesImpl()) + } + } + + @Test + fun `greets user correctly`(workflow: GreetingWorkflow) { + val result = workflow.greet("World") + assertEquals("Hello, World!", result) + } +} +``` + +### Pattern 2: Testing Signals and Queries + +```kotlin +@Test +fun `handles signals correctly`( + workflow: OrderWorkflow, + testEnv: KTestWorkflowEnvironment, + client: KWorkflowClient +) = runBlocking { + // Start workflow asynchronously + val handle = client.startWorkflow( + OrderWorkflow::processOrder, + KWorkflowOptions( + taskQueue = "orders", + workflowId = "order-workflow-1" + ), + "order-123" + ) + + // Let workflow start + testEnv.sleep(1.seconds) + + // Query current state + val status = handle.query(OrderWorkflow::getStatus) + assertEquals("PENDING", status) + + // Send signal + handle.signal(OrderWorkflow::approve, "manager-1") + + // Wait for completion + val result = handle.result() + assertEquals("COMPLETED", result) +} +``` + +### Pattern 3: Testing Updates + +```kotlin +@Test +fun `handles updates correctly`( + workflow: CounterWorkflow, + testEnv: KTestWorkflowEnvironment, + client: KWorkflowClient +) = runBlocking { + val handle = client.startWorkflow( + CounterWorkflow::run, + KWorkflowOptions( + taskQueue = "counters", + workflowId = "counter-1" + ) + ) + + testEnv.sleep(1.seconds) + + // Execute update and get result + val newValue = handle.executeUpdate(CounterWorkflow::increment, 5) + assertEquals(5, newValue) + + val finalValue = handle.executeUpdate(CounterWorkflow::increment, 3) + assertEquals(8, finalValue) +} +``` + +### Pattern 4: Testing Time-Dependent Workflows + +```kotlin +@Test +@WorkflowInitialTime("2024-01-01T00:00:00Z") +fun `processes scheduled tasks`( + workflow: ScheduledWorkflow, + testEnv: KTestWorkflowEnvironment +) { + // Start workflow that waits for specific time + val future = async { workflow.waitUntilTime() } + + // Advance time by 1 hour (time skipping makes this instant) + testEnv.sleep(1.hours) + + val result = future.await() + assertEquals("executed at scheduled time", result) +} +``` + +### Pattern 5: Testing Retries and Failures + +```kotlin +@Test +fun `retries failed activities`( + testEnv: KTestWorkflowEnvironment, + worker: KWorker, + workflow: RetryWorkflow +) { + var attempts = 0 + val mockActivity = mock { + on { unreliableCall() } doAnswer { + attempts++ + if (attempts < 3) { + throw RuntimeException("Temporary failure") + } + "success" + } + } + + worker.registerActivitiesImplementations(mockActivity) + testEnv.start() + + val result = workflow.executeWithRetry() + + assertEquals("success", result) + assertEquals(3, attempts) +} +``` + +### Pattern 6: Testing Child Workflows + +```kotlin +@Test +fun `executes child workflows`(workflow: ParentWorkflow) { + val result = workflow.processWithChildren(listOf("a", "b", "c")) + assertEquals(listOf("processed-a", "processed-b", "processed-c"), result) +} +``` + +### Pattern 7: Testing Continue-As-New + +```kotlin +@Test +fun `continues as new after threshold`( + workflow: BatchWorkflow, + testEnv: KTestWorkflowEnvironment +) { + // Workflow that continues-as-new every 100 items + val result = workflow.processBatch((1..250).toList()) + + assertEquals(250, result.processedCount) + assertEquals(3, result.continuations) // 100 + 100 + 50 +} +``` + +### Pattern 8: Async Activity Completion + +```kotlin +@Test +fun `handles async activity completion`(activityEnv: KTestActivityEnvironment) { + activityEnv.registerActivitiesImplementations(AsyncActivityImpl()) + + // Activity signals async completion + assertThrows { + activityEnv.executeActivity( + AsyncActivity::startAsyncOperation, + KActivityOptions(startToCloseTimeout = 1.minutes) + ) + } +} +``` + +### Pattern 9: Testing with External Service + +```kotlin +class ExternalServiceTest { + companion object { + @JvmField + @RegisterExtension + val testWorkflow = kTestWorkflowExtension { + registerWorkflowImplementationTypes() + setActivityImplementations(MyActivitiesImpl()) + // Uses ClientConfigProfile.load() which reads from: + // - TEMPORAL_ADDRESS (service address) + // - TEMPORAL_NAMESPACE (namespace) + // - TEMPORAL_API_KEY (optional API key) + // - TEMPORAL_TLS_* (TLS configuration) + useExternalService() + } + } + + @Test + fun `works with real temporal service`(workflow: MyWorkflow) { + val result = workflow.execute("input") + assertEquals("expected", result) + } +} + +// Alternative: explicit address override +class ExplicitAddressTest { + companion object { + @JvmField + @RegisterExtension + val testWorkflow = kTestWorkflowExtension { + registerWorkflowImplementationTypes() + setActivityImplementations(MyActivitiesImpl()) + useExternalService("localhost:7233") // Explicit address + namespace = "test-namespace" + } + } +} +``` + +### Pattern 10: Diagnostics on Failure + +```kotlin +class DiagnosticTest { + companion object { + @JvmField + @RegisterExtension + val testWorkflow = kTestWorkflowExtension { + registerWorkflowImplementationTypes() + setActivityImplementations(MyActivitiesImpl()) + } + } + + @Test + fun `test with diagnostics on failure`( + workflow: MyWorkflow, + testEnv: KTestWorkflowEnvironment + ) { + try { + workflow.execute("input") + } catch (e: Exception) { + // Print workflow histories for debugging + println(testEnv.getDiagnostics()) + throw e + } + } +} +``` + +--- + +## 7. Builder Functions + +For cases where the DSL is not preferred, traditional builders are available: + +```kotlin +// Test environment +val testEnv = KTestWorkflowEnvironment.newInstance( + KTestEnvironmentOptions.newBuilder() + .setNamespace("test") + .setInitialTime(Instant.now()) + .setUseTimeskipping(true) + .build() +) + +// Test extension +val testWorkflow = KTestWorkflowExtension.newBuilder() + .registerWorkflowImplementationTypes(MyWorkflowImpl::class.java) + .setActivityImplementations(MyActivitiesImpl()) + .build() +``` + +--- + +## 8. Integration with kotlinx-coroutines-test + +The test framework integrates with `kotlinx-coroutines-test` for coroutine testing: + +```kotlin +@Test +fun `test with coroutine test utilities`( + workflow: MyWorkflow, + testEnv: KTestWorkflowEnvironment +) = runTest { + // Use kotlinx-coroutines-test utilities + val result = workflow.execute("input") + assertEquals("expected", result) +} +``` + +--- + +## 9. Summary: Java vs Kotlin Comparison + +| Java SDK | Kotlin SDK | Notes | +|----------|------------|-------| +| `TestWorkflowEnvironment.newInstance()` | `KTestWorkflowEnvironment.newInstance()` | Same pattern | +| `TestWorkflowEnvironment.newInstance(options)` | `KTestWorkflowEnvironment.newInstance { }` | DSL builder | +| `TestWorkflowExtension.newBuilder()...build()` | `kTestWorkflowExtension { }` | DSL builder | +| `testEnv.newWorker()` returns `Worker` | `testEnv.newWorker()` returns `KWorker` | Kotlin wrapper | +| `Worker` | `KWorker` (with `worker` property for interop) | Kotlin-idiomatic registration | +| `testEnv.sleep(Duration)` | `testEnv.sleep(Duration)` | Suspend function, supports kotlin.time | +| `testEnv.currentTimeMillis()` | `testEnv.currentTimeMillis` / `testEnv.currentTime` | Property access + Instant | +| `TestActivityEnvironment` | `KTestActivityEnvironment` | Same pattern | +| `activityEnv.newActivityStub(T.class, options)` | `activityEnv.executeActivity(T::method, options, args)` | Handle approach (consistent with SDK) | +| `testEnv.getWorkflowClient()` | `testEnv.workflowClient` | Property access | +| N/A | `setSuspendActivityImplementations()` | Kotlin-specific | +| Builder pattern | DSL + Builder pattern | Both available | + +--- + +## 10. External Service Configuration + +When using `useExternalService()` without an explicit address, the test framework uses `ClientConfigProfile.load()` to load configuration from environment variables and config files. + +### Environment Variables + +| Variable | Description | +|----------|-------------| +| `TEMPORAL_ADDRESS` | Service address (e.g., `localhost:7233` or `myns.tmprl.cloud:7233`) | +| `TEMPORAL_NAMESPACE` | Namespace to use | +| `TEMPORAL_API_KEY` | API key for authentication (Temporal Cloud) | +| `TEMPORAL_TLS` | Enable TLS (`true`/`false`) | +| `TEMPORAL_TLS_CLIENT_CERT_PATH` | Path to client certificate file | +| `TEMPORAL_TLS_CLIENT_KEY_PATH` | Path to client key file | +| `TEMPORAL_TLS_SERVER_CA_CERT_PATH` | Path to CA certificate file | +| `TEMPORAL_TLS_SERVER_NAME` | Server name for TLS verification | +| `TEMPORAL_TLS_DISABLE_HOST_VERIFICATION` | Disable host verification (`true`/`false`) | +| `TEMPORAL_PROFILE` | Config file profile to use (default: `default`) | +| `TEMPORAL_GRPC_META_*` | Custom gRPC metadata (e.g., `TEMPORAL_GRPC_META_MY_HEADER=value`) | + +### Example: Running Tests Against Temporal Cloud + +```bash +export TEMPORAL_ADDRESS="myns.tmprl.cloud:7233" +export TEMPORAL_NAMESPACE="myns" +export TEMPORAL_API_KEY="my-api-key" + +./gradlew test +``` + +### Example: Running Tests Against Local Server with mTLS + +```bash +export TEMPORAL_ADDRESS="localhost:7233" +export TEMPORAL_NAMESPACE="default" +export TEMPORAL_TLS=true +export TEMPORAL_TLS_CLIENT_CERT_PATH="/path/to/client.pem" +export TEMPORAL_TLS_CLIENT_KEY_PATH="/path/to/client.key" +export TEMPORAL_TLS_SERVER_CA_CERT_PATH="/path/to/ca.pem" + +./gradlew test +``` + +--- + +## 11. Dependencies + +```kotlin +// build.gradle.kts +dependencies { + testImplementation("io.temporal:temporal-kotlin-testing:VERSION") + + // JUnit 5 + testImplementation("org.junit.jupiter:junit-jupiter:5.10.0") + + // Coroutine testing (optional) + testImplementation("org.jetbrains.kotlinx:kotlinx-coroutines-test:1.7.3") + + // Mocking (optional) + testImplementation("org.mockito.kotlin:mockito-kotlin:5.1.0") +} +``` + +--- + +## 12. Implementation Notes + +1. **KTestWorkflowEnvironment** wraps `TestWorkflowEnvironment` and returns `KWorkflowClient` +2. **KWorker** wraps `Worker` with Kotlin-idiomatic registration APIs; exposes underlying `Worker` for interop +3. **Time functions** use `kotlin.time.Duration` with `java.time.Duration` overloads +4. **Suspend functions** for `sleep()` and `awaitTermination()` +5. **Extension functions** provide Kotlin-idiomatic APIs on Java types +6. **DSL builders** internally use Java builders for configuration +7. **Parameter injection** in extensions works via JUnit 5's `ParameterResolver` diff --git a/kotlin/implementation/test-framework-implementation-design.md b/kotlin/implementation/test-framework-implementation-design.md new file mode 100644 index 0000000..ebed3c6 --- /dev/null +++ b/kotlin/implementation/test-framework-implementation-design.md @@ -0,0 +1,1756 @@ +# Kotlin SDK Test Framework Implementation Design + +## Overview + +This document describes the implementation design for the Kotlin SDK test framework based on the [Test Framework Design API](./test-framework-design.md). The implementation wraps the Java SDK's test framework while providing Kotlin-idiomatic APIs. + +## Package Structure + +``` +io.temporal.kotlin.testing/ +├── KTestWorkflowEnvironment.kt # Main test environment +├── KTestWorkflowExtension.kt # JUnit 5 workflow extension +├── KTestActivityEnvironment.kt # Activity test environment +├── KTestActivityExtension.kt # JUnit 5 activity extension +├── KTestEnvironmentOptions.kt # Configuration options +├── KWorker.kt # Kotlin worker wrapper +├── WorkflowInitialTime.kt # Annotation for test initial time +└── internal/ + └── KTestActivityEnvironmentInternal.kt # Internal implementation +``` + +--- + +## 1. KTestEnvironmentOptions + +**Purpose:** Configuration options for test environments with DSL support. + +### Implementation + +```kotlin +package io.temporal.kotlin.testing + +import com.uber.m3.tally.Scope +import io.temporal.api.enums.v1.IndexedValueType +import io.temporal.client.WorkflowClientOptions +import io.temporal.kotlin.TemporalDsl +import io.temporal.serviceclient.WorkflowServiceStubsOptions +import io.temporal.testing.TestEnvironmentOptions +import io.temporal.worker.WorkerFactoryOptions +import java.time.Instant + +/** + * Kotlin DSL builder for test environment options. + */ +@TemporalDsl +public class KTestEnvironmentOptionsBuilder internal constructor() { + + /** Namespace to use for testing. Default: "UnitTest" */ + public var namespace: String = "UnitTest" + + /** Initial time for the workflow virtual clock. Default: current time */ + public var initialTime: Instant? = null + + /** Whether to enable time skipping. Default: true */ + public var useTimeskipping: Boolean = true + + /** Whether to use external Temporal service. Default: false (in-memory) */ + public var useExternalService: Boolean = false + + /** Target endpoint for external service. */ + public var target: String? = null + + /** Metrics scope for reporting. */ + public var metricsScope: Scope? = null + + private var workerFactoryOptionsBuilder: (WorkerFactoryOptions.Builder.() -> Unit)? = null + private var workflowClientOptionsBuilder: (WorkflowClientOptions.Builder.() -> Unit)? = null + private var workflowServiceStubsOptionsBuilder: (WorkflowServiceStubsOptions.Builder.() -> Unit)? = null + private val searchAttributes: MutableMap = mutableMapOf() + + /** + * Configure WorkerFactoryOptions. + */ + public fun workerFactoryOptions(block: WorkerFactoryOptions.Builder.() -> Unit) { + workerFactoryOptionsBuilder = block + } + + /** + * Configure WorkflowClientOptions. + */ + public fun workflowClientOptions(block: WorkflowClientOptions.Builder.() -> Unit) { + workflowClientOptionsBuilder = block + } + + /** + * Configure WorkflowServiceStubsOptions. + */ + public fun workflowServiceStubsOptions(block: WorkflowServiceStubsOptions.Builder.() -> Unit) { + workflowServiceStubsOptionsBuilder = block + } + + /** + * Register search attributes. + */ + public fun searchAttributes(block: SearchAttributesBuilder.() -> Unit) { + SearchAttributesBuilder(searchAttributes).apply(block) + } + + @TemporalDsl + public class SearchAttributesBuilder internal constructor( + private val attributes: MutableMap + ) { + public fun register(name: String, type: IndexedValueType) { + attributes[name] = type + } + } + + internal fun build(): TestEnvironmentOptions { + val builder = TestEnvironmentOptions.newBuilder() + + // Apply namespace via WorkflowClientOptions + val clientOptions = WorkflowClientOptions.newBuilder().apply { + setNamespace(namespace) + workflowClientOptionsBuilder?.invoke(this) + }.build() + builder.setWorkflowClientOptions(clientOptions) + + // Apply other options + workerFactoryOptionsBuilder?.let { block -> + builder.setWorkerFactoryOptions( + WorkerFactoryOptions.newBuilder().apply(block).build() + ) + } + + workflowServiceStubsOptionsBuilder?.let { block -> + builder.setWorkflowServiceStubsOptions( + WorkflowServiceStubsOptions.newBuilder().apply(block).build() + ) + } + + initialTime?.let { builder.setInitialTime(it) } + builder.setUseTimeskipping(useTimeskipping) + builder.setUseExternalService(useExternalService) + target?.let { builder.setTarget(it) } + metricsScope?.let { builder.setMetricsScope(it) } + + searchAttributes.forEach { (name, type) -> + builder.registerSearchAttribute(name, type) + } + + return builder.build() + } +} + +/** + * Immutable configuration for test environments. + */ +public class KTestEnvironmentOptions private constructor( + internal val javaOptions: TestEnvironmentOptions +) { + + public companion object { + /** + * Create options using DSL builder. + */ + public fun newBuilder(block: KTestEnvironmentOptionsBuilder.() -> Unit = {}): KTestEnvironmentOptions { + return KTestEnvironmentOptions( + KTestEnvironmentOptionsBuilder().apply(block).build() + ) + } + + /** + * Get default options. + */ + public fun getDefaultInstance(): KTestEnvironmentOptions { + return KTestEnvironmentOptions(TestEnvironmentOptions.getDefaultInstance()) + } + } +} +``` + +### Key Design Decisions + +1. **DSL Builder Pattern**: Uses `@TemporalDsl` annotation for type-safe DSL +2. **Wraps Java Options**: Internally converts to `TestEnvironmentOptions` +3. **Nested DSL Builders**: Supports nested configuration for search attributes + +--- + +## 2. KWorker + +**Purpose:** Kotlin wrapper for Worker with idiomatic registration APIs. + +### Implementation + +```kotlin +package io.temporal.kotlin.testing + +import io.temporal.kotlin.TemporalDsl +import io.temporal.kotlin.activity.SuspendActivityWrapper +import io.temporal.worker.Worker +import io.temporal.worker.WorkerOptions +import io.temporal.worker.WorkflowImplementationOptions +import kotlin.reflect.KClass + +/** + * Kotlin worker that provides idiomatic APIs for registering + * Kotlin workflows and suspend activities. + * + * Use [worker] property for direct access to the underlying Java Worker + * when interoperating with Java workflows/activities. + */ +public class KWorker internal constructor( + /** The underlying Java Worker for interop scenarios */ + public val worker: Worker +) { + + /** + * Register Kotlin workflow implementation types using reified generics. + */ + public inline fun registerWorkflowImplementationTypes() { + worker.registerWorkflowImplementationTypes(T::class.java) + } + + /** + * Register Kotlin workflow implementation types using KClass. + */ + public fun registerWorkflowImplementationTypes(vararg workflowClasses: KClass<*>) { + worker.registerWorkflowImplementationTypes( + *workflowClasses.map { it.java }.toTypedArray() + ) + } + + /** + * Register Kotlin workflow implementation types with options using KClass. + */ + public fun registerWorkflowImplementationTypes( + options: WorkflowImplementationOptions, + vararg workflowClasses: KClass<*> + ) { + worker.registerWorkflowImplementationTypes( + options, + *workflowClasses.map { it.java }.toTypedArray() + ) + } + + /** + * Register Kotlin workflow implementation types with options DSL. + */ + public inline fun registerWorkflowImplementationTypes( + options: WorkflowImplementationOptions.Builder.() -> Unit + ) { + val opts = WorkflowImplementationOptions.newBuilder().apply(options).build() + worker.registerWorkflowImplementationTypes(opts, T::class.java) + } + + /** + * Register activity implementations. + * Works with both regular and suspend activity implementations. + */ + public fun registerActivitiesImplementations(vararg activities: Any) { + worker.registerActivitiesImplementations(*activities) + } + + /** + * Register suspend activity implementations. + * Wraps suspend functions for execution in the Temporal activity context. + * + * @param activities Activity implementation objects containing suspend functions + */ + public fun registerSuspendActivities(vararg activities: Any) { + activities.forEach { activity -> + val wrapper = SuspendActivityWrapper.wrap(activity) + worker.registerActivitiesImplementations(wrapper) + } + } + + /** + * Register Nexus service implementations. + */ + public fun registerNexusServiceImplementations(vararg services: Any) { + worker.registerNexusServiceImplementation(*services) + } +} +``` + +### Key Design Decisions + +1. **Exposes Underlying Worker**: Via `worker` property for interop +2. **Reified Generics**: For type-safe registration without reflection +3. **KClass Support**: For vararg registration patterns +4. **Suspend Activity Support**: Via `SuspendActivityWrapper` integration + +--- + +## 3. KTestWorkflowEnvironment + +**Purpose:** Main test environment providing in-memory Temporal service with time skipping. + +### Implementation + +```kotlin +package io.temporal.kotlin.testing + +import io.temporal.api.enums.v1.IndexedValueType +import io.temporal.api.nexus.v1.Endpoint +import io.temporal.kotlin.client.KWorkflowClient +import io.temporal.kotlin.toJavaDuration +import io.temporal.kotlin.worker.KotlinPlugin +import io.temporal.serviceclient.OperatorServiceStubs +import io.temporal.serviceclient.WorkflowServiceStubs +import io.temporal.testing.TestWorkflowEnvironment +import io.temporal.worker.Worker +import io.temporal.worker.WorkerOptions +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.withContext +import java.io.Closeable +import java.time.Duration +import java.time.Instant +import java.util.concurrent.TimeUnit +import kotlin.time.Duration as KDuration +import kotlin.time.toJavaDuration as kotlinToJava + +/** + * Kotlin test environment for workflow unit testing. + * + * Provides an in-memory Temporal service with automatic time skipping, + * allowing workflows that run for hours/days to be tested in milliseconds. + * + * Example: + * ```kotlin + * val testEnv = KTestWorkflowEnvironment.newInstance { + * namespace = "test-namespace" + * initialTime = Instant.parse("2024-01-01T00:00:00Z") + * } + * + * val worker = testEnv.newWorker("task-queue") + * worker.registerWorkflowImplementationTypes() + * worker.registerActivitiesImplementations(MyActivitiesImpl()) + * + * testEnv.start() + * + * val result = testEnv.workflowClient.executeWorkflow( + * MyWorkflow::execute, + * KWorkflowOptions(taskQueue = "task-queue"), + * "input" + * ) + * + * testEnv.close() + * ``` + */ +public class KTestWorkflowEnvironment private constructor( + private val testEnvironment: TestWorkflowEnvironment +) : Closeable { + + /** + * The Kotlin workflow client for interacting with workflows. + */ + public val workflowClient: KWorkflowClient by lazy { + KWorkflowClient(testEnvironment.workflowClient) + } + + /** + * Current test time in milliseconds since epoch. + * May differ from system time due to time skipping. + */ + public val currentTimeMillis: Long + get() = testEnvironment.currentTimeMillis() + + /** + * Current test time as an Instant. + */ + public val currentTime: Instant + get() = Instant.ofEpochMilli(currentTimeMillis) + + /** + * The namespace used by this test environment. + */ + public val namespace: String + get() = testEnvironment.namespace + + /** + * Whether the workers have been started. + */ + public val isStarted: Boolean + get() = testEnvironment.isStarted + + /** + * Whether shutdown has been initiated. + */ + public val isShutdown: Boolean + get() = testEnvironment.isShutdown + + /** + * Whether all workers have terminated. + */ + public val isTerminated: Boolean + get() = testEnvironment.isTerminated + + /** + * Access to WorkflowServiceStubs for advanced scenarios. + */ + public val workflowServiceStubs: WorkflowServiceStubs + get() = testEnvironment.workflowServiceStubs + + /** + * Access to OperatorServiceStubs for advanced scenarios. + */ + public val operatorServiceStubs: OperatorServiceStubs + get() = testEnvironment.operatorServiceStubs + + /** + * Create a new Kotlin worker for the specified task queue. + * + * @param taskQueue The task queue name + * @return A KWorker instance + */ + public fun newWorker(taskQueue: String): KWorker { + return KWorker(testEnvironment.newWorker(taskQueue)) + } + + /** + * Create a new Kotlin worker with options. + * + * @param taskQueue The task queue name + * @param options DSL builder for WorkerOptions + * @return A KWorker instance + */ + public fun newWorker( + taskQueue: String, + options: WorkerOptions.Builder.() -> Unit + ): KWorker { + val workerOptions = WorkerOptions.newBuilder().apply(options).build() + return KWorker(testEnvironment.newWorker(taskQueue, workerOptions)) + } + + /** + * Create a new Kotlin worker with WorkerOptions. + * + * @param taskQueue The task queue name + * @param options WorkerOptions instance + * @return A KWorker instance + */ + public fun newWorker(taskQueue: String, options: WorkerOptions): KWorker { + return KWorker(testEnvironment.newWorker(taskQueue, options)) + } + + /** + * Start all registered workers. + */ + public fun start() { + testEnvironment.start() + } + + /** + * Sleep for the specified duration with time skipping. + * This is a suspend function that advances test time without blocking. + * + * @param duration Kotlin Duration to sleep + */ + public suspend fun sleep(duration: KDuration) { + withContext(Dispatchers.IO) { + testEnvironment.sleep(duration.kotlinToJava()) + } + } + + /** + * Sleep for the specified duration with time skipping. + * + * @param duration Java Duration to sleep + */ + public suspend fun sleep(duration: Duration) { + withContext(Dispatchers.IO) { + testEnvironment.sleep(duration) + } + } + + /** + * Register a callback to execute after the specified delay in test time. + * + * @param delay Kotlin Duration delay + * @param callback The callback to execute + */ + public fun registerDelayedCallback(delay: KDuration, callback: () -> Unit) { + testEnvironment.registerDelayedCallback(delay.kotlinToJava()) { callback() } + } + + /** + * Register a callback to execute after the specified delay in test time. + * + * @param delay Java Duration delay + * @param callback The callback to execute + */ + public fun registerDelayedCallback(delay: Duration, callback: () -> Unit) { + testEnvironment.registerDelayedCallback(delay) { callback() } + } + + /** + * Register a search attribute with the test server. + * + * @param name Search attribute name + * @param type Search attribute type + * @return true if registered, false if already exists + */ + public fun registerSearchAttribute(name: String, type: IndexedValueType): Boolean { + return testEnvironment.registerSearchAttribute(name, type) + } + + /** + * Create a Nexus endpoint for testing. + * + * @param name Endpoint name + * @param taskQueue Task queue for the endpoint + * @return The created Endpoint + */ + public fun createNexusEndpoint(name: String, taskQueue: String): Endpoint { + return testEnvironment.createNexusEndpoint(name, taskQueue) + } + + /** + * Delete a Nexus endpoint. + * + * @param endpoint The endpoint to delete + */ + public fun deleteNexusEndpoint(endpoint: Endpoint) { + testEnvironment.deleteNexusEndpoint(endpoint) + } + + /** + * Get diagnostic information including workflow histories. + * Useful for debugging test failures. + */ + public fun getDiagnostics(): String { + return testEnvironment.diagnostics + } + + /** + * Initiate graceful shutdown. + * Workers stop accepting new tasks but complete in-progress work. + */ + public fun shutdown() { + testEnvironment.shutdown() + } + + /** + * Initiate immediate shutdown. + * Attempts to stop all processing immediately. + */ + public fun shutdownNow() { + testEnvironment.shutdownNow() + } + + /** + * Wait for all workers to terminate. + * + * @param timeout Maximum time to wait + */ + public suspend fun awaitTermination(timeout: KDuration) { + withContext(Dispatchers.IO) { + testEnvironment.awaitTermination( + timeout.inWholeMilliseconds, + TimeUnit.MILLISECONDS + ) + } + } + + /** + * Wait for all workers to terminate. + * + * @param timeout Maximum time to wait (Java Duration) + */ + public suspend fun awaitTermination(timeout: Duration) { + withContext(Dispatchers.IO) { + testEnvironment.awaitTermination( + timeout.toMillis(), + TimeUnit.MILLISECONDS + ) + } + } + + /** + * Close the test environment. + * Calls shutdownNow() and awaitTermination(). + */ + override fun close() { + testEnvironment.close() + } + + public companion object { + /** + * Create a new test environment with default options. + */ + public fun newInstance(): KTestWorkflowEnvironment { + return KTestWorkflowEnvironment(TestWorkflowEnvironment.newInstance()) + } + + /** + * Create a new test environment with DSL configuration. + * + * Example: + * ```kotlin + * val testEnv = KTestWorkflowEnvironment.newInstance { + * namespace = "test-namespace" + * initialTime = Instant.parse("2024-01-01T00:00:00Z") + * useTimeskipping = true + * + * workerFactoryOptions { + * maxWorkflowThreadCount = 800 + * } + * + * searchAttributes { + * register("CustomKeyword", IndexedValueType.INDEXED_VALUE_TYPE_KEYWORD) + * } + * } + * ``` + */ + public fun newInstance( + options: KTestEnvironmentOptionsBuilder.() -> Unit + ): KTestWorkflowEnvironment { + val javaOptions = KTestEnvironmentOptionsBuilder().apply(options).build() + return KTestWorkflowEnvironment(TestWorkflowEnvironment.newInstance(javaOptions)) + } + + /** + * Create a new test environment with pre-built options. + */ + public fun newInstance(options: KTestEnvironmentOptions): KTestWorkflowEnvironment { + return KTestWorkflowEnvironment( + TestWorkflowEnvironment.newInstance(options.javaOptions) + ) + } + } +} + +/** + * Use the test environment with automatic cleanup. + * + * Example: + * ```kotlin + * KTestWorkflowEnvironment.newInstance().use { testEnv -> + * // Test code here + * } // Automatically closed + * ``` + */ +public inline fun KTestWorkflowEnvironment.use(block: (KTestWorkflowEnvironment) -> T): T { + return try { + block(this) + } finally { + close() + } +} +``` + +### Key Design Decisions + +1. **Wraps TestWorkflowEnvironment**: Provides Kotlin-idiomatic API over Java implementation +2. **Suspend Functions**: `sleep()` and `awaitTermination()` are suspend functions +3. **Dual Duration Support**: Accepts both `kotlin.time.Duration` and `java.time.Duration` +4. **Property Access**: Uses Kotlin properties instead of getter methods +5. **DSL Builder**: Static `newInstance { }` DSL for configuration +6. **KWorkflowClient Integration**: Returns `KWorkflowClient` for workflow operations +7. **KWorker Return**: `newWorker()` returns `KWorker` instead of Java `Worker` + +--- + +## 4. WorkflowInitialTime Annotation + +**Purpose:** Override initial time for specific test methods. + +### Implementation + +```kotlin +package io.temporal.kotlin.testing + +/** + * Annotation to specify the initial time for a workflow test. + * Overrides the initial time configured in the extension. + * + * Example: + * ```kotlin + * @Test + * @WorkflowInitialTime("2024-06-15T12:00:00Z") + * fun `test with specific initial time`(workflow: MyWorkflow) { + * // Test runs with June 15, 2024 as initial time + * } + * ``` + */ +@Target(AnnotationTarget.FUNCTION) +@Retention(AnnotationRetention.RUNTIME) +public annotation class WorkflowInitialTime( + /** + * ISO-8601 formatted timestamp for the initial time. + * Example: "2024-01-01T00:00:00Z" + */ + val value: String +) +``` + +--- + +## 5. KTestWorkflowExtension + +**Purpose:** JUnit 5 extension for simplified workflow testing. + +### Implementation + +```kotlin +package io.temporal.kotlin.testing + +import io.temporal.api.enums.v1.IndexedValueType +import io.temporal.client.WorkflowClientOptions +import io.temporal.client.WorkflowOptions +import io.temporal.common.metadata.POJOWorkflowImplMetadata +import io.temporal.common.metadata.POJOWorkflowInterfaceMetadata +import io.temporal.kotlin.TemporalDsl +import io.temporal.kotlin.client.KWorkflowClient +import io.temporal.kotlin.client.KWorkflowOptions +import io.temporal.testing.TestWorkflowEnvironment +import io.temporal.worker.Worker +import io.temporal.worker.WorkerFactoryOptions +import io.temporal.worker.WorkerOptions +import io.temporal.worker.WorkflowImplementationOptions +import io.temporal.workflow.DynamicWorkflow +import org.junit.jupiter.api.extension.* +import org.junit.platform.commons.support.AnnotationSupport +import java.lang.reflect.Constructor +import java.lang.reflect.Parameter +import java.time.Instant +import java.util.* +import kotlin.reflect.KClass + +/** + * JUnit 5 extension for testing Temporal workflows with Kotlin-idiomatic APIs. + * + * Example: + * ```kotlin + * class MyWorkflowTest { + * companion object { + * @JvmField + * @RegisterExtension + * val testWorkflow = kTestWorkflowExtension { + * registerWorkflowImplementationTypes() + * setActivityImplementations(MyActivitiesImpl()) + * } + * } + * + * @Test + * fun `test workflow execution`(workflow: MyWorkflow) { + * val result = workflow.execute("input") + * assertEquals("expected", result) + * } + * } + * ``` + */ +public class KTestWorkflowExtension private constructor( + private val config: ExtensionConfig +) : ParameterResolver, TestWatcher, BeforeEachCallback, AfterEachCallback { + + private data class ExtensionConfig( + val namespace: String, + val workflowTypes: Map, WorkflowImplementationOptions>, + val activityImplementations: Array, + val suspendActivityImplementations: Array, + val nexusServiceImplementations: Array, + val workerOptions: WorkerOptions, + val workerFactoryOptions: WorkerFactoryOptions?, + val workflowClientOptions: WorkflowClientOptions?, + val useExternalService: Boolean, + val target: String?, + val doNotStart: Boolean, + val initialTimeMillis: Long, + val useTimeskipping: Boolean, + val searchAttributes: Map + ) + + private val supportedParameterTypes = mutableSetOf>().apply { + add(KTestWorkflowEnvironment::class.java) + add(KWorkflowClient::class.java) + add(KWorkflowOptions::class.java) + add(KWorker::class.java) + add(Worker::class.java) + // Add workflow interface types + config.workflowTypes.keys.forEach { workflowType -> + if (!DynamicWorkflow::class.java.isAssignableFrom(workflowType)) { + val metadata = POJOWorkflowImplMetadata.newInstance(workflowType) + metadata.workflowInterfaces.forEach { add(it.interfaceClass) } + } + } + } + + private val includesDynamicWorkflow = config.workflowTypes.keys.any { + DynamicWorkflow::class.java.isAssignableFrom(it) + } + + override fun supportsParameter( + parameterContext: ParameterContext, + extensionContext: ExtensionContext + ): Boolean { + val parameter = parameterContext.parameter + if (parameter.declaringExecutable is Constructor<*>) return false + + val parameterType = parameter.type + if (supportedParameterTypes.contains(parameterType)) return true + + if (!includesDynamicWorkflow) return false + + return try { + POJOWorkflowInterfaceMetadata.newInstance(parameterType) + true + } catch (e: Exception) { + false + } + } + + override fun resolveParameter( + parameterContext: ParameterContext, + extensionContext: ExtensionContext + ): Any { + val parameterType = parameterContext.parameter.type + val store = getStore(extensionContext) + + return when (parameterType) { + KTestWorkflowEnvironment::class.java -> getTestEnvironment(store) + KWorkflowClient::class.java -> getTestEnvironment(store).workflowClient + KWorkflowOptions::class.java -> getWorkflowOptions(store) + KWorker::class.java -> getKWorker(store) + Worker::class.java -> getKWorker(store).worker + else -> { + // Create workflow stub + val testEnv = getTestEnvironment(store) + val options = getWorkflowOptions(store) + testEnv.workflowClient.workflowClient.newWorkflowStub( + parameterType, + options.toJavaOptions() + ) + } + } + } + + override fun beforeEach(context: ExtensionContext) { + val currentInitialTimeMillis = AnnotationSupport.findAnnotation( + context.element, + WorkflowInitialTime::class.java + ).map { Instant.parse(it.value).toEpochMilli() } + .orElse(config.initialTimeMillis) + + val testEnvOptions = io.temporal.testing.TestEnvironmentOptions.newBuilder().apply { + setUseExternalService(config.useExternalService) + setUseTimeskipping(config.useTimeskipping) + config.target?.let { setTarget(it) } + if (currentInitialTimeMillis > 0) setInitialTimeMillis(currentInitialTimeMillis) + + val clientOptions = (config.workflowClientOptions?.let { + WorkflowClientOptions.newBuilder(it) + } ?: WorkflowClientOptions.newBuilder()) + .setNamespace(config.namespace) + .build() + setWorkflowClientOptions(clientOptions) + + config.workerFactoryOptions?.let { setWorkerFactoryOptions(it) } + config.searchAttributes.forEach { (name, type) -> + registerSearchAttribute(name, type) + } + }.build() + + val javaTestEnv = TestWorkflowEnvironment.newInstance(testEnvOptions) + val testEnvironment = KTestWorkflowEnvironment.newInstance { + // Options already applied to javaTestEnv + } + + // Create task queue + val taskQueue = "WorkflowTest-${context.displayName}-${context.uniqueId}" + val worker = javaTestEnv.newWorker(taskQueue, config.workerOptions) + val kWorker = KWorker(worker) + + // Register workflows + config.workflowTypes.forEach { (wfType, options) -> + worker.registerWorkflowImplementationTypes(options, wfType) + } + + // Register activities + worker.registerActivitiesImplementations(*config.activityImplementations) + + // Register suspend activities + config.suspendActivityImplementations.forEach { activity -> + kWorker.registerSuspendActivities(activity) + } + + // Register Nexus services + if (config.nexusServiceImplementations.isNotEmpty()) { + worker.registerNexusServiceImplementation(*config.nexusServiceImplementations) + } + + if (!config.doNotStart) { + javaTestEnv.start() + } + + // Store in extension context + val store = getStore(context) + store.put(TEST_ENVIRONMENT_KEY, KTestWorkflowEnvironment.Companion::class.java.getDeclaredMethod( + "newInstance" + ).let { + // Wrap the Java test environment + val field = KTestWorkflowEnvironment::class.java.getDeclaredField("testEnvironment") + field.isAccessible = true + KTestWorkflowEnvironment::class.java.getDeclaredConstructor( + TestWorkflowEnvironment::class.java + ).apply { isAccessible = true }.newInstance(javaTestEnv) + }) + store.put(KWORKER_KEY, kWorker) + store.put(WORKFLOW_OPTIONS_KEY, KWorkflowOptions(taskQueue = taskQueue)) + } + + override fun afterEach(context: ExtensionContext) { + val testEnv = getStore(context).get(TEST_ENVIRONMENT_KEY, KTestWorkflowEnvironment::class.java) + testEnv?.close() + } + + override fun testFailed(context: ExtensionContext, cause: Throwable) { + val testEnv = getStore(context).get(TEST_ENVIRONMENT_KEY, KTestWorkflowEnvironment::class.java) + testEnv?.let { + System.err.println("Workflow execution histories:\n${it.getDiagnostics()}") + } + } + + private fun getStore(context: ExtensionContext): ExtensionContext.Store { + val namespace = ExtensionContext.Namespace.create( + KTestWorkflowExtension::class.java, + context.requiredTestMethod + ) + return context.getStore(namespace) + } + + private fun getTestEnvironment(store: ExtensionContext.Store): KTestWorkflowEnvironment { + return store.get(TEST_ENVIRONMENT_KEY, KTestWorkflowEnvironment::class.java) + ?: throw IllegalStateException("Test environment not initialized") + } + + private fun getKWorker(store: ExtensionContext.Store): KWorker { + return store.get(KWORKER_KEY, KWorker::class.java) + ?: throw IllegalStateException("Worker not initialized") + } + + private fun getWorkflowOptions(store: ExtensionContext.Store): KWorkflowOptions { + return store.get(WORKFLOW_OPTIONS_KEY, KWorkflowOptions::class.java) + ?: throw IllegalStateException("Workflow options not initialized") + } + + public companion object { + private const val TEST_ENVIRONMENT_KEY = "testEnvironment" + private const val KWORKER_KEY = "kWorker" + private const val WORKFLOW_OPTIONS_KEY = "workflowOptions" + + /** + * Create a new extension builder. + */ + public fun newBuilder(): Builder = Builder() + } + + /** + * Builder for KTestWorkflowExtension. + */ + @TemporalDsl + public class Builder internal constructor() { + private var namespace: String = "UnitTest" + private val workflowTypes = mutableMapOf, WorkflowImplementationOptions>() + private var activityImplementations: Array = emptyArray() + private var suspendActivityImplementations: Array = emptyArray() + private var nexusServiceImplementations: Array = emptyArray() + private var workerOptions: WorkerOptions = WorkerOptions.getDefaultInstance() + private var workerFactoryOptions: WorkerFactoryOptions? = null + private var workflowClientOptions: WorkflowClientOptions? = null + private var useExternalService: Boolean = false + private var target: String? = null + private var doNotStart: Boolean = false + private var initialTimeMillis: Long = 0 + private var useTimeskipping: Boolean = true + private val searchAttributes = mutableMapOf() + + /** Set the namespace. Default: "UnitTest" */ + public var namespace_: String + get() = namespace + set(value) { namespace = value } + + /** Initial time for the test. */ + public var initialTime: Instant? = null + set(value) { + field = value + initialTimeMillis = value?.toEpochMilli() ?: 0 + } + + /** Whether to enable time skipping. Default: true */ + public var useTimeskipping_: Boolean + get() = useTimeskipping + set(value) { useTimeskipping = value } + + /** Whether to defer worker startup. Default: false */ + public var doNotStart_: Boolean + get() = doNotStart + set(value) { doNotStart = value } + + /** + * Register workflow implementation types using reified generics. + */ + public inline fun registerWorkflowImplementationTypes() { + workflowTypes[T::class.java] = WorkflowImplementationOptions.newBuilder().build() + } + + /** + * Register workflow implementation types with options DSL. + */ + public inline fun registerWorkflowImplementationTypes( + options: WorkflowImplementationOptions.Builder.() -> Unit + ) { + workflowTypes[T::class.java] = WorkflowImplementationOptions.newBuilder().apply(options).build() + } + + /** + * Register workflow implementation types. + */ + public fun registerWorkflowImplementationTypes(vararg classes: Class<*>) { + val defaultOptions = WorkflowImplementationOptions.newBuilder().build() + classes.forEach { workflowTypes[it] = defaultOptions } + } + + /** + * Register workflow implementation types with options. + */ + public fun registerWorkflowImplementationTypes( + options: WorkflowImplementationOptions, + vararg classes: Class<*> + ) { + classes.forEach { workflowTypes[it] = options } + } + + /** + * Set activity implementations. + */ + public fun setActivityImplementations(vararg activities: Any) { + activityImplementations = arrayOf(*activities) + } + + /** + * Set suspend activity implementations. + */ + public fun setSuspendActivityImplementations(vararg activities: Any) { + suspendActivityImplementations = arrayOf(*activities) + } + + /** + * Set Nexus service implementations. + */ + public fun setNexusServiceImplementations(vararg services: Any) { + nexusServiceImplementations = arrayOf(*services) + } + + /** + * Configure worker options. + */ + public fun workerOptions(block: WorkerOptions.Builder.() -> Unit) { + workerOptions = WorkerOptions.newBuilder().apply(block).build() + } + + /** + * Configure worker factory options. + */ + public fun workerFactoryOptions(block: WorkerFactoryOptions.Builder.() -> Unit) { + workerFactoryOptions = WorkerFactoryOptions.newBuilder().apply(block).build() + } + + /** + * Configure workflow client options. + */ + public fun workflowClientOptions(block: WorkflowClientOptions.Builder.() -> Unit) { + workflowClientOptions = WorkflowClientOptions.newBuilder().apply(block).build() + } + + /** + * Use internal in-memory service (default). + */ + public fun useInternalService() { + useExternalService = false + target = null + } + + /** + * Use external Temporal service with ClientConfigProfile. + */ + public fun useExternalService() { + useExternalService = true + target = null + } + + /** + * Use external Temporal service with explicit address. + */ + public fun useExternalService(address: String) { + useExternalService = true + target = address + } + + /** + * Configure search attributes. + */ + public fun searchAttributes(block: SearchAttributesBuilder.() -> Unit) { + SearchAttributesBuilder(searchAttributes).apply(block) + } + + @TemporalDsl + public class SearchAttributesBuilder internal constructor( + private val attributes: MutableMap + ) { + public fun register(name: String, type: IndexedValueType) { + attributes[name] = type + } + } + + public fun build(): KTestWorkflowExtension { + return KTestWorkflowExtension( + ExtensionConfig( + namespace = namespace, + workflowTypes = workflowTypes.toMap(), + activityImplementations = activityImplementations, + suspendActivityImplementations = suspendActivityImplementations, + nexusServiceImplementations = nexusServiceImplementations, + workerOptions = workerOptions, + workerFactoryOptions = workerFactoryOptions, + workflowClientOptions = workflowClientOptions, + useExternalService = useExternalService, + target = target, + doNotStart = doNotStart, + initialTimeMillis = initialTimeMillis, + useTimeskipping = useTimeskipping, + searchAttributes = searchAttributes.toMap() + ) + ) + } + } +} + +/** + * DSL function to create a KTestWorkflowExtension. + * + * Example: + * ```kotlin + * companion object { + * @JvmField + * @RegisterExtension + * val testWorkflow = kTestWorkflowExtension { + * registerWorkflowImplementationTypes() + * setActivityImplementations(MyActivitiesImpl()) + * } + * } + * ``` + */ +public fun kTestWorkflowExtension( + block: KTestWorkflowExtension.Builder.() -> Unit +): KTestWorkflowExtension { + return KTestWorkflowExtension.newBuilder().apply(block).build() +} +``` + +### Key Design Decisions + +1. **DSL Function**: `kTestWorkflowExtension { }` top-level function +2. **Reified Generics**: `registerWorkflowImplementationTypes()` for type-safe registration +3. **Parameter Injection**: Supports `KTestWorkflowEnvironment`, `KWorkflowClient`, `KWorkflowOptions`, `KWorker`, `Worker`, and workflow stubs +4. **Initial Time Annotation**: Supports `@WorkflowInitialTime` per-test override +5. **Test Failure Diagnostics**: Automatically prints workflow histories on failure +6. **External Service Support**: `useExternalService()` and `useExternalService(address)` + +--- + +## 6. KTestActivityEnvironment + +**Purpose:** Environment for unit testing activity implementations in isolation. + +### Implementation + +```kotlin +package io.temporal.kotlin.testing + +import io.temporal.activity.ActivityOptions +import io.temporal.activity.LocalActivityOptions +import io.temporal.kotlin.TemporalDsl +import io.temporal.kotlin.activity.KActivityOptions +import io.temporal.kotlin.activity.KLocalActivityOptions +import io.temporal.kotlin.activity.SuspendActivityWrapper +import io.temporal.testing.TestActivityEnvironment +import io.temporal.workflow.Functions +import java.io.Closeable +import java.lang.reflect.Type +import kotlin.reflect.KFunction1 +import kotlin.reflect.KFunction2 +import kotlin.reflect.KFunction3 +import kotlin.reflect.KFunction4 +import kotlin.reflect.KSuspendFunction1 +import kotlin.reflect.KSuspendFunction2 +import kotlin.reflect.KSuspendFunction3 +import kotlin.reflect.KSuspendFunction4 +import kotlin.reflect.jvm.javaMethod + +/** + * Kotlin test environment for activity unit testing. + * + * Supports testing both regular and suspend activity implementations + * in isolation without needing workflows. + * + * Example: + * ```kotlin + * val activityEnv = KTestActivityEnvironment.newInstance() + * activityEnv.registerActivitiesImplementations(MyActivitiesImpl()) + * + * val result = activityEnv.executeActivity( + * MyActivities::doSomething, + * KActivityOptions(startToCloseTimeout = 30.seconds), + * "input" + * ) + * + * assertEquals("expected", result) + * activityEnv.close() + * ``` + */ +public class KTestActivityEnvironment private constructor( + private val testEnvironment: TestActivityEnvironment +) : Closeable { + + /** + * Register activity implementations. + */ + public fun registerActivitiesImplementations(vararg activities: Any) { + testEnvironment.registerActivitiesImplementations(*activities) + } + + /** + * Register suspend activity implementations. + */ + public fun registerSuspendActivities(vararg activities: Any) { + activities.forEach { activity -> + val wrapper = SuspendActivityWrapper.wrap(activity) + testEnvironment.registerActivitiesImplementations(wrapper) + } + } + + // ========== Execute Activity (1-4 args) ========== + + /** + * Execute an activity with no arguments. + */ + public fun executeActivity( + activity: KFunction1, + options: KActivityOptions + ): R { + val stub = createActivityStub(activity, options.toJavaOptions()) + @Suppress("UNCHECKED_CAST") + return activity.call(stub) as R + } + + /** + * Execute an activity with one argument. + */ + public fun executeActivity( + activity: KFunction2, + options: KActivityOptions, + arg1: A1 + ): R { + val stub = createActivityStub(activity, options.toJavaOptions()) + @Suppress("UNCHECKED_CAST") + return activity.call(stub, arg1) as R + } + + /** + * Execute an activity with two arguments. + */ + public fun executeActivity( + activity: KFunction3, + options: KActivityOptions, + arg1: A1, + arg2: A2 + ): R { + val stub = createActivityStub(activity, options.toJavaOptions()) + @Suppress("UNCHECKED_CAST") + return activity.call(stub, arg1, arg2) as R + } + + /** + * Execute an activity with three arguments. + */ + public fun executeActivity( + activity: KFunction4, + options: KActivityOptions, + arg1: A1, + arg2: A2, + arg3: A3 + ): R { + val stub = createActivityStub(activity, options.toJavaOptions()) + @Suppress("UNCHECKED_CAST") + return activity.call(stub, arg1, arg2, arg3) as R + } + + // ========== Execute Suspend Activity (1-4 args) ========== + + /** + * Execute a suspend activity with no arguments. + */ + @JvmName("executeSuspendActivity0") + public suspend fun executeActivity( + activity: KSuspendFunction1, + options: KActivityOptions + ): R { + val stub = createActivityStub(activity, options.toJavaOptions()) + @Suppress("UNCHECKED_CAST") + return activity.call(stub) as R + } + + /** + * Execute a suspend activity with one argument. + */ + @JvmName("executeSuspendActivity1") + public suspend fun executeActivity( + activity: KSuspendFunction2, + options: KActivityOptions, + arg1: A1 + ): R { + val stub = createActivityStub(activity, options.toJavaOptions()) + @Suppress("UNCHECKED_CAST") + return activity.call(stub, arg1) as R + } + + /** + * Execute a suspend activity with two arguments. + */ + @JvmName("executeSuspendActivity2") + public suspend fun executeActivity( + activity: KSuspendFunction3, + options: KActivityOptions, + arg1: A1, + arg2: A2 + ): R { + val stub = createActivityStub(activity, options.toJavaOptions()) + @Suppress("UNCHECKED_CAST") + return activity.call(stub, arg1, arg2) as R + } + + /** + * Execute a suspend activity with three arguments. + */ + @JvmName("executeSuspendActivity3") + public suspend fun executeActivity( + activity: KSuspendFunction4, + options: KActivityOptions, + arg1: A1, + arg2: A2, + arg3: A3 + ): R { + val stub = createActivityStub(activity, options.toJavaOptions()) + @Suppress("UNCHECKED_CAST") + return activity.call(stub, arg1, arg2, arg3) as R + } + + // ========== Execute Local Activity (1-4 args) ========== + + /** + * Execute a local activity with no arguments. + */ + public fun executeLocalActivity( + activity: KFunction1, + options: KLocalActivityOptions + ): R { + val stub = createLocalActivityStub(activity, options.toJavaOptions()) + @Suppress("UNCHECKED_CAST") + return activity.call(stub) as R + } + + /** + * Execute a local activity with one argument. + */ + public fun executeLocalActivity( + activity: KFunction2, + options: KLocalActivityOptions, + arg1: A1 + ): R { + val stub = createLocalActivityStub(activity, options.toJavaOptions()) + @Suppress("UNCHECKED_CAST") + return activity.call(stub, arg1) as R + } + + /** + * Execute a local activity with two arguments. + */ + public fun executeLocalActivity( + activity: KFunction3, + options: KLocalActivityOptions, + arg1: A1, + arg2: A2 + ): R { + val stub = createLocalActivityStub(activity, options.toJavaOptions()) + @Suppress("UNCHECKED_CAST") + return activity.call(stub, arg1, arg2) as R + } + + /** + * Execute a local activity with three arguments. + */ + public fun executeLocalActivity( + activity: KFunction4, + options: KLocalActivityOptions, + arg1: A1, + arg2: A2, + arg3: A3 + ): R { + val stub = createLocalActivityStub(activity, options.toJavaOptions()) + @Suppress("UNCHECKED_CAST") + return activity.call(stub, arg1, arg2, arg3) as R + } + + // ========== Heartbeat Testing ========== + + /** + * Set heartbeat details for the next activity execution. + * Simulates activity retry with heartbeat checkpoint. + */ + public fun setHeartbeatDetails(details: T) { + testEnvironment.setHeartbeatDetails(details) + } + + /** + * Set a listener for activity heartbeats. + */ + public inline fun setActivityHeartbeatListener( + noinline listener: (T) -> Unit + ) { + testEnvironment.setActivityHeartbeatListener( + T::class.java, + Functions.Proc1 { listener(it) } + ) + } + + /** + * Set a listener for activity heartbeats with type information. + */ + public fun setActivityHeartbeatListener( + detailsClass: Class, + detailsType: Type, + listener: (T) -> Unit + ) { + testEnvironment.setActivityHeartbeatListener( + detailsClass, + detailsType, + Functions.Proc1 { listener(it) } + ) + } + + // ========== Cancellation Testing ========== + + /** + * Request cancellation of the currently executing activity. + * Cancellation is delivered on the next heartbeat. + */ + public fun requestCancelActivity() { + testEnvironment.requestCancelActivity() + } + + // ========== Lifecycle ========== + + override fun close() { + testEnvironment.close() + } + + // ========== Internal Helpers ========== + + @Suppress("UNCHECKED_CAST") + private fun createActivityStub( + activity: kotlin.reflect.KFunction<*>, + options: ActivityOptions + ): T { + val activityClass = activity.javaMethod?.declaringClass + ?: throw IllegalArgumentException("Cannot determine activity interface") + return testEnvironment.newActivityStub(activityClass, options) as T + } + + @Suppress("UNCHECKED_CAST") + private fun createLocalActivityStub( + activity: kotlin.reflect.KFunction<*>, + options: LocalActivityOptions + ): T { + val activityClass = activity.javaMethod?.declaringClass + ?: throw IllegalArgumentException("Cannot determine activity interface") + return testEnvironment.newLocalActivityStub(activityClass, options, emptyMap()) as T + } + + public companion object { + /** + * Create a new activity test environment with default options. + */ + public fun newInstance(): KTestActivityEnvironment { + return KTestActivityEnvironment(TestActivityEnvironment.newInstance()) + } + + /** + * Create a new activity test environment with DSL configuration. + */ + public fun newInstance( + options: KTestEnvironmentOptionsBuilder.() -> Unit + ): KTestActivityEnvironment { + val javaOptions = KTestEnvironmentOptionsBuilder().apply(options).build() + return KTestActivityEnvironment(TestActivityEnvironment.newInstance(javaOptions)) + } + + /** + * Create a new activity test environment with pre-built options. + */ + public fun newInstance(options: KTestEnvironmentOptions): KTestActivityEnvironment { + return KTestActivityEnvironment( + TestActivityEnvironment.newInstance(options.javaOptions) + ) + } + } +} +``` + +### Key Design Decisions + +1. **Method Reference API**: `executeActivity(MyActivity::method, options, args)` +2. **Suspend Function Support**: Separate overloads for suspend activities +3. **Local Activity Support**: `executeLocalActivity` methods +4. **Heartbeat Testing**: `setHeartbeatDetails` and `setActivityHeartbeatListener` +5. **Cancellation Testing**: `requestCancelActivity()` + +--- + +## 7. KTestActivityExtension + +**Purpose:** JUnit 5 extension for simplified activity testing. + +### Implementation + +```kotlin +package io.temporal.kotlin.testing + +import io.temporal.kotlin.TemporalDsl +import io.temporal.kotlin.activity.SuspendActivityWrapper +import io.temporal.testing.TestActivityEnvironment +import io.temporal.testing.TestEnvironmentOptions +import org.junit.jupiter.api.extension.* +import java.lang.reflect.Constructor + +/** + * JUnit 5 extension for testing Temporal activities. + * + * Example: + * ```kotlin + * class MyActivityTest { + * companion object { + * @JvmField + * @RegisterExtension + * val testActivity = kTestActivityExtension { + * setActivityImplementations(MyActivitiesImpl()) + * } + * } + * + * @Test + * fun `test activity`(activityEnv: KTestActivityEnvironment) { + * val result = activityEnv.executeActivity( + * MyActivities::doSomething, + * KActivityOptions(startToCloseTimeout = 30.seconds), + * "input" + * ) + * assertEquals("expected", result) + * } + * } + * ``` + */ +public class KTestActivityExtension private constructor( + private val config: ExtensionConfig +) : ParameterResolver, BeforeEachCallback, AfterEachCallback { + + private data class ExtensionConfig( + val testEnvironmentOptions: TestEnvironmentOptions, + val activityImplementations: Array, + val suspendActivityImplementations: Array + ) + + override fun supportsParameter( + parameterContext: ParameterContext, + extensionContext: ExtensionContext + ): Boolean { + val parameter = parameterContext.parameter + if (parameter.declaringExecutable is Constructor<*>) return false + return parameter.type == KTestActivityEnvironment::class.java + } + + override fun resolveParameter( + parameterContext: ParameterContext, + extensionContext: ExtensionContext + ): Any { + return getStore(extensionContext).get( + TEST_ENVIRONMENT_KEY, + KTestActivityEnvironment::class.java + ) ?: throw IllegalStateException("Activity environment not initialized") + } + + override fun beforeEach(context: ExtensionContext) { + val javaEnv = TestActivityEnvironment.newInstance(config.testEnvironmentOptions) + + // Register regular activities + javaEnv.registerActivitiesImplementations(*config.activityImplementations) + + // Register suspend activities + config.suspendActivityImplementations.forEach { activity -> + val wrapper = SuspendActivityWrapper.wrap(activity) + javaEnv.registerActivitiesImplementations(wrapper) + } + + val kEnv = KTestActivityEnvironment::class.java + .getDeclaredConstructor(TestActivityEnvironment::class.java) + .apply { isAccessible = true } + .newInstance(javaEnv) + + getStore(context).put(TEST_ENVIRONMENT_KEY, kEnv) + } + + override fun afterEach(context: ExtensionContext) { + getStore(context).get(TEST_ENVIRONMENT_KEY, KTestActivityEnvironment::class.java)?.close() + } + + private fun getStore(context: ExtensionContext): ExtensionContext.Store { + val namespace = ExtensionContext.Namespace.create( + KTestActivityExtension::class.java, + context.requiredTestMethod + ) + return context.getStore(namespace) + } + + public companion object { + private const val TEST_ENVIRONMENT_KEY = "testEnvironment" + + public fun newBuilder(): Builder = Builder() + } + + @TemporalDsl + public class Builder internal constructor() { + private var testEnvironmentOptions: TestEnvironmentOptions = + TestEnvironmentOptions.getDefaultInstance() + private var activityImplementations: Array = emptyArray() + private var suspendActivityImplementations: Array = emptyArray() + + /** + * Configure test environment options. + */ + public fun testEnvironmentOptions(block: KTestEnvironmentOptionsBuilder.() -> Unit) { + testEnvironmentOptions = KTestEnvironmentOptionsBuilder().apply(block).build() + } + + /** + * Set activity implementations. + */ + public fun setActivityImplementations(vararg activities: Any) { + activityImplementations = arrayOf(*activities) + } + + /** + * Set suspend activity implementations. + */ + public fun setSuspendActivityImplementations(vararg activities: Any) { + suspendActivityImplementations = arrayOf(*activities) + } + + public fun build(): KTestActivityExtension { + return KTestActivityExtension( + ExtensionConfig( + testEnvironmentOptions = testEnvironmentOptions, + activityImplementations = activityImplementations, + suspendActivityImplementations = suspendActivityImplementations + ) + ) + } + } +} + +/** + * DSL function to create a KTestActivityExtension. + */ +public fun kTestActivityExtension( + block: KTestActivityExtension.Builder.() -> Unit +): KTestActivityExtension { + return KTestActivityExtension.newBuilder().apply(block).build() +} +``` + +--- + +## 8. Implementation Notes + +### Dependencies + +The test framework module will need the following dependencies: + +```kotlin +// build.gradle.kts +dependencies { + api(project(":temporal-kotlin")) + api("io.temporal:temporal-testing:VERSION") + + implementation("org.junit.jupiter:junit-jupiter-api:5.10.0") + implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.7.3") + + testImplementation("org.junit.jupiter:junit-jupiter:5.10.0") + testImplementation("org.jetbrains.kotlinx:kotlinx-coroutines-test:1.7.3") +} +``` + +### Module Structure + +``` +temporal-kotlin-testing/ +├── src/main/kotlin/io/temporal/kotlin/testing/ +│ ├── KTestWorkflowEnvironment.kt +│ ├── KTestWorkflowExtension.kt +│ ├── KTestActivityEnvironment.kt +│ ├── KTestActivityExtension.kt +│ ├── KTestEnvironmentOptions.kt +│ ├── KWorker.kt +│ └── WorkflowInitialTime.kt +└── src/test/kotlin/io/temporal/kotlin/testing/ + ├── KTestWorkflowEnvironmentTest.kt + ├── KTestWorkflowExtensionTest.kt + ├── KTestActivityEnvironmentTest.kt + └── KTestActivityExtensionTest.kt +``` + +### Key Integration Points + +1. **SuspendActivityWrapper**: Reuse existing implementation for suspend activity support +2. **KotlinPlugin**: Ensure Kotlin workflow support via existing plugin +3. **KWorkflowClient**: Return Kotlin client from test environment +4. **KWorkflowOptions**: Use existing options classes + +### Thread Safety + +- Test environments are designed for single-threaded test execution +- Each test method gets its own environment instance (via JUnit extension lifecycle) +- Suspend functions use `Dispatchers.IO` for blocking operations + +--- + +## 9. Summary + +This implementation design provides: + +| Component | Purpose | +|-----------|---------| +| `KTestEnvironmentOptions` | Configuration with DSL builder | +| `KWorker` | Kotlin wrapper for Worker with suspend activity support | +| `KTestWorkflowEnvironment` | Main test environment with time skipping | +| `KTestWorkflowExtension` | JUnit 5 extension for workflow tests | +| `KTestActivityEnvironment` | Activity unit testing environment | +| `KTestActivityExtension` | JUnit 5 extension for activity tests | +| `WorkflowInitialTime` | Annotation for per-test initial time | + +The design: +- Wraps Java SDK test framework for full compatibility +- Provides Kotlin-idiomatic DSL builders +- Supports suspend functions throughout +- Integrates with existing Kotlin SDK components +- Follows established Kotlin SDK patterns diff --git a/kotlin/test-framework-implementation-plan.md b/kotlin/implementation/test-framework-implementation-plan.md similarity index 100% rename from kotlin/test-framework-implementation-plan.md rename to kotlin/implementation/test-framework-implementation-plan.md diff --git a/kotlin/kotlin-idioms.md b/kotlin/kotlin-idioms.md new file mode 100644 index 0000000..4f3e838 --- /dev/null +++ b/kotlin/kotlin-idioms.md @@ -0,0 +1,105 @@ +# Kotlin Idioms + +The SDK leverages Kotlin-specific language features for an idiomatic experience. + +## Kotlin Duration + +Use `kotlin.time.Duration` for readable time expressions: + +```kotlin +import kotlin.time.Duration.Companion.seconds +import kotlin.time.Duration.Companion.minutes +import kotlin.time.Duration.Companion.hours + +// Activity with timeouts using KOptions +val result = KWorkflow.executeActivity( + "ProcessOrder", + KActivityOptions( + startToCloseTimeout = 30.seconds, + scheduleToCloseTimeout = 5.minutes, + heartbeatTimeout = 10.seconds + ), + orderData +) + +// Timers - standard kotlinx.coroutines delay (intercepted for determinism) +delay(1.hours) +``` + +> **Note:** The `Delay` interface intercepts standard `delay()` calls and routes them through Temporal's deterministic timer. + +## Null Safety + +Nullable types replace `Optional` throughout the API. The following Java SDK types have Kotlin equivalents with null-safe APIs: + +| Java SDK | Kotlin SDK | +|----------|------------| +| `io.temporal.workflow.Workflow` | `KWorkflow` object | +| `io.temporal.workflow.WorkflowInfo` | `KWorkflowInfo` | +| `io.temporal.workflow.Promise` | Standard `Deferred` via `Promise.toDeferred()` | +| `io.temporal.activity.Activity` | `KActivity` object | +| `io.temporal.activity.ActivityExecutionContext` | `KActivityContext` | +| `io.temporal.activity.ActivityInfo` | `KActivityInfo` | +| `io.temporal.client.WorkflowStub` | `KWorkflowHandle` / `KTypedWorkflowHandle` | + +```kotlin +// KWorkflowInfo - nullable instead of Optional +interface KWorkflowInfo { + val workflowId: String + val runId: String + val parentWorkflowId: String? // Optional in Java + val parentRunId: String? // Optional in Java + fun getMemo(key: String): String? // Optional in Java + fun getSearchAttribute(key: String): Any? + // ... other properties +} + +// KActivityInfo - nullable instead of Optional +interface KActivityInfo { + val activityId: String + val activityType: String + val workflowId: String + val attempt: Int + fun getHeartbeatDetails(): Payloads? // Optional in Java + // ... other properties +} + +// Access via KWorkflow / KActivity objects +val info: KWorkflowInfo = KWorkflow.getInfo() +val parentId: String? = info.parentWorkflowId + +// KActivity.getContext() returns KActivityContext (matches Java's Activity.getExecutionContext()) +val context: KActivityContext = KActivity.getContext() +val activityInfo: KActivityInfo = context.info +context.heartbeat("progress") // Blocking version for regular activities +context.suspendHeartbeat("progress") // Suspend version for suspend activities +val logger = context.logger() // Get activity logger +``` + +This eliminates `.orElse(null)`, `.isPresent`, and other Optional ceremony. + +## Property Syntax for Queries + +Queries can be defined as Kotlin properties in the workflow interface: + +```kotlin +@WorkflowInterface +interface OrderWorkflow { + @QueryMethod + val status: OrderStatus // Property syntax + + @QueryMethod + fun getItemCount(): Int // Method syntax +} + +// Client usage via typed handle (using KWorkflowClient) +val handle = client.getWorkflowHandle("order-123") +val status = handle.query(OrderWorkflow::status) +val count = handle.query(OrderWorkflow::getItemCount) +``` + +## Next Steps + +- [KOptions Classes](./configuration/koptions.md) - Kotlin-native configuration +- [Workflow Definition](./workflows/definition.md) - Using these idioms in workflows +- [Activity Definition](./activities/definition.md) - Using these idioms in activities diff --git a/kotlin/migration.md b/kotlin/migration.md new file mode 100644 index 0000000..d832245 --- /dev/null +++ b/kotlin/migration.md @@ -0,0 +1,125 @@ +# Migration from Java SDK + +## API Mapping + +| Java SDK | Kotlin SDK | +|----------|------------| +| **Activities** | | +| `Workflow.newUntypedActivityStub(opts)` | *(not needed - options passed per call)* | +| `activities.execute("name", Cls, arg)` | `KWorkflow.executeActivity("name", options, arg)` | +| `stub.method(arg)` (typed activity) | `KWorkflow.executeActivity(Interface::method, options, arg)` | +| `Async.function(stub::method, arg)` | `coroutineScope { async { KWorkflow.executeActivity(...) } }` | +| **Workflows** | | +| `Workflow.newChildWorkflowStub(...)` | `KWorkflow.executeChildWorkflow(Interface::method, options, ...)` | +| `Async.function(childStub::method, arg)` | `KWorkflow.startChildWorkflow(Interface::method, options, ...)` → `KChildWorkflowHandle` | +| `Workflow.getWorkflowExecution(childStub)` | `childHandle.workflowId` / `childHandle.firstExecutionRunId` | +| `client.newWorkflowStub(...)` | `client.startWorkflow(Interface::method, ...)` → `KTypedWorkflowHandle` | +| `client.newWorkflowStub(Cls, id)` | `client.getWorkflowHandle(id)` → `KWorkflowHandle` | +| `stub.signal(arg)` | `handle.signal(T::method, arg)` | +| `stub.query()` | `handle.query(T::method)` | +| `handle.getResult()` | `handle.result()` (type inferred) or `handle.result()` | +| **Primitives** | | +| `Workflow.sleep(duration)` | `delay(duration)` - standard kotlinx.coroutines | +| `Workflow.await(() -> cond)` | `KWorkflow.awaitCondition { cond }` | +| `Promise` | Standard `Deferred` via `Promise.toDeferred()` | +| `Optional` | `T?` | +| `Duration.ofSeconds(30)` | `30.seconds` | +| **Parallel Execution** | | +| `Async.function(...)` + `Promise.get()` | `coroutineScope { async { ... } }` + `awaitAll()` | +| **Options** | | +| `ActivityOptions.newBuilder()...build()` | `KActivityOptions(...)` | +| `LocalActivityOptions.newBuilder()...build()` | `KLocalActivityOptions(...)` | +| `ChildWorkflowOptions.newBuilder()...build()` | `KChildWorkflowOptions(...)` | +| `WorkflowOptions.newBuilder()...build()` | `KWorkflowOptions(...)` | +| `RetryOptions.newBuilder()...build()` | `KRetryOptions(...)` | + +## Before (Java) + +```java +@WorkflowInterface +public interface GreetingWorkflow { + @WorkflowMethod + String getGreeting(String name); +} + +public class GreetingWorkflowImpl implements GreetingWorkflow { + @Override + public String getGreeting(String name) { + ActivityStub activities = Workflow.newUntypedActivityStub( + ActivityOptions.newBuilder() + .setStartToCloseTimeout(Duration.ofSeconds(30)) + .build() + ); + return activities.execute("greet", String.class, name); + } +} +``` + +## After (Kotlin) + +```kotlin +@WorkflowInterface +interface GreetingWorkflow { + @WorkflowMethod + suspend fun getGreeting(name: String): String +} + +class GreetingWorkflowImpl : GreetingWorkflow { + override suspend fun getGreeting(name: String): String { + return KWorkflow.executeActivity( + "greet", + KActivityOptions(startToCloseTimeout = 30.seconds), + name + ) + } +} +``` + +## Interoperability + +### Kotlin Workflows Calling Java Activities + +```kotlin +override suspend fun processOrder(order: Order): String { + // JavaActivities is a Java @ActivityInterface - no stub needed + return KWorkflow.executeActivity( + JavaActivities::process, + KActivityOptions(startToCloseTimeout = 30.seconds), + order + ) +} +``` + +### Java Clients Calling Kotlin Workflows + +```kotlin +val result = client.executeWorkflow( + JavaWorkflowInterface::execute, + KWorkflowOptions(workflowId = "java-workflow", taskQueue = "java-queue"), + input +) +``` + +### Mixed Workers + +A single worker can host both Java and Kotlin workflows: + +```kotlin +// Java workflows (thread-based) +worker.registerWorkflowImplementationTypes( + OrderWorkflowJavaImpl::class.java +) + +// Kotlin workflows (coroutine-based) +worker.registerWorkflowImplementationTypes( + GreetingWorkflowImpl::class +) + +// Both run on the same worker - execution model is per-workflow-instance +factory.start() +``` + +## Next Steps + +- [API Parity](./api-parity.md) - Full Java SDK comparison +- [Kotlin Idioms](./kotlin-idioms.md) - Kotlin-specific patterns diff --git a/kotlin/sdk-api.md b/kotlin/sdk-api.md deleted file mode 100644 index ba9c89d..0000000 --- a/kotlin/sdk-api.md +++ /dev/null @@ -1,3479 +0,0 @@ -# Kotlin SDK API Proposal - -This document describes the public API and developer experience for the Temporal Kotlin SDK. - -For implementation details, see [sdk-implementation.md](./sdk-implementation.md). - -## Overview - -The Kotlin SDK provides an idiomatic Kotlin experience for building Temporal workflows using coroutines and suspend functions. - -**Key Features:** - -* Coroutine-based workflows with `suspend fun` -* Full interoperability with Java SDK -* Kotlin Duration support (`30.seconds`) -* DSL builders for configuration -* Null safety (no `Optional`) - -**Requirements:** - -* Minimum Kotlin version: 1.8.x -* Coroutines library: kotlinx-coroutines-core 1.7.x+ - -## Design Principle - -**Use idiomatic Kotlin language patterns wherever possible instead of custom APIs.** - -The Kotlin SDK should feel natural to Kotlin developers by leveraging standard `kotlinx.coroutines` primitives. Custom APIs should only be introduced when Temporal-specific semantics cannot be achieved through standard patterns. - -| Pattern | Standard Kotlin | Temporal Integration | -|---------|-----------------|----------------------| -| Parallel execution | `coroutineScope { async { ... } }` | Works via deterministic dispatcher | -| Await multiple | `awaitAll(d1, d2)` | Standard kotlinx.coroutines | -| Sleep/delay | `delay(duration)` | Intercepted via `Delay` interface | -| Deferred results | `Deferred` | Standard + `Promise.toDeferred()` | - -This approach provides: -- **Familiar patterns**: Kotlin developers use patterns they already know -- **IDE support**: Full autocomplete and documentation for standard APIs -- **Ecosystem compatibility**: Works with existing coroutine libraries and utilities -- **Smaller API surface**: Less custom code to learn and maintain - -## Kotlin Idioms - -The SDK leverages Kotlin-specific language features for an idiomatic experience. - -### Kotlin Duration - -Use `kotlin.time.Duration` for readable time expressions: - -```kotlin -import kotlin.time.Duration.Companion.seconds -import kotlin.time.Duration.Companion.minutes -import kotlin.time.Duration.Companion.hours - -// Activity with timeouts using KOptions -val result = KWorkflow.executeActivity( - "ProcessOrder", - KActivityOptions( - startToCloseTimeout = 30.seconds, - scheduleToCloseTimeout = 5.minutes, - heartbeatTimeout = 10.seconds - ), - orderData -) - -// Timers - standard kotlinx.coroutines delay (intercepted for determinism) -delay(1.hours) -``` - -> **Note:** The `Delay` interface intercepts standard `delay()` calls and routes them through Temporal's deterministic timer. - -### KOptions Classes - -For a fully idiomatic Kotlin experience, the SDK provides dedicated `KOptions` data classes that accept `kotlin.time.Duration` directly, use named parameters with default values, and follow immutable data class patterns: - -```kotlin -import kotlin.time.Duration.Companion.seconds -import kotlin.time.Duration.Companion.minutes - -// KActivityOptions - Kotlin-native activity configuration -val result = KWorkflow.executeActivity( - GreetingActivities::composeGreeting, - KActivityOptions( - startToCloseTimeout = 30.seconds, - scheduleToCloseTimeout = 5.minutes, - heartbeatTimeout = 10.seconds, - retryOptions = KRetryOptions( - initialInterval = 1.seconds, - maximumAttempts = 3 - ) - ), - "Hello", "World" -) - -// KLocalActivityOptions -val validated = KWorkflow.executeLocalActivity( - ValidationActivities::validate, - KLocalActivityOptions( - startToCloseTimeout = 5.seconds, - localRetryThreshold = 10.seconds - ), - input -) - -// KChildWorkflowOptions -val childResult = KWorkflow.executeChildWorkflow( - ChildWorkflow::process, - KChildWorkflowOptions( - workflowId = "child-123", - workflowExecutionTimeout = 1.hours, - retryOptions = KRetryOptions(maximumAttempts = 3) - ), - data -) - -// KWorkflowOptions - for client workflow execution -val result = client.executeWorkflow( - OrderWorkflow::processOrder, - KWorkflowOptions( - workflowId = "order-123", - taskQueue = "orders", - workflowExecutionTimeout = 24.hours, - workflowRunTimeout = 1.hours - ), - order -) -``` - -**KOptions Data Classes:** - -```kotlin -/** - * Kotlin-native activity options with Duration support. - * All timeout properties accept kotlin.time.Duration directly. - */ -data class KActivityOptions( - val startToCloseTimeout: Duration? = null, - val scheduleToCloseTimeout: Duration? = null, - val scheduleToStartTimeout: Duration? = null, - val heartbeatTimeout: Duration? = null, - val taskQueue: String? = null, - val retryOptions: KRetryOptions? = null, - val cancellationType: ActivityCancellationType? = null, // Java default: TRY_CANCEL - val disableEagerExecution: Boolean = false -) - -/** - * Kotlin-native local activity options. - */ -data class KLocalActivityOptions( - val startToCloseTimeout: Duration? = null, - val scheduleToCloseTimeout: Duration? = null, - val localRetryThreshold: Duration? = null, - val retryOptions: KRetryOptions? = null -) - -/** - * Kotlin-native retry options. - */ -data class KRetryOptions( - val initialInterval: Duration = 1.seconds, - val backoffCoefficient: Double = 2.0, - val maximumInterval: Duration? = null, - val maximumAttempts: Int = 0, // 0 = unlimited - val doNotRetry: List = emptyList() -) - -/** - * Kotlin-native child workflow options. - * All fields are optional - null values inherit from parent workflow or use Java SDK defaults. - */ -data class KChildWorkflowOptions( - val namespace: String? = null, - val workflowId: String? = null, - val workflowIdReusePolicy: WorkflowIdReusePolicy? = null, - val workflowRunTimeout: Duration? = null, - val workflowExecutionTimeout: Duration? = null, - val workflowTaskTimeout: Duration? = null, - val taskQueue: String? = null, - val retryOptions: KRetryOptions? = null, - val cronSchedule: String? = null, - val parentClosePolicy: ParentClosePolicy? = null, - val memo: Map? = null, - val typedSearchAttributes: SearchAttributes? = null, - val cancellationType: ChildWorkflowCancellationType? = null, - val staticSummary: String? = null, - val staticDetails: String? = null, - val priority: Priority? = null -) - -/** - * Kotlin-native workflow options for client execution. - * workflowId and taskQueue are optional - if not provided, will use defaults or generate UUID. - */ -data class KWorkflowOptions( - val workflowId: String? = null, - val workflowIdReusePolicy: WorkflowIdReusePolicy? = null, - val workflowIdConflictPolicy: WorkflowIdConflictPolicy? = null, - val workflowRunTimeout: Duration? = null, - val workflowExecutionTimeout: Duration? = null, - val workflowTaskTimeout: Duration? = null, - val taskQueue: String? = null, - val retryOptions: KRetryOptions? = null, - val cronSchedule: String? = null, - val memo: Map? = null, - val typedSearchAttributes: SearchAttributes? = null, - val disableEagerExecution: Boolean = true, - val startDelay: Duration? = null, - val staticSummary: String? = null, - val staticDetails: String? = null, - val priority: Priority? = null -) -``` - -**KOptions vs DSL Builders:** - -The Kotlin SDK provides two approaches for configuring options: - -1. **KOptions (Recommended)** - Native Kotlin data classes designed for the Kotlin SDK -2. **DSL Builders** - Extension functions on Java SDK builders, provided as a stopgap for using Kotlin with the Java SDK - -> **Important:** When using the Kotlin SDK (`KWorkflow`, `KWorkflowClient`, etc.), always use KOptions classes. The DSL builders (`ActivityOptions { }`, `WorkflowOptions { }`, etc.) exist only for compatibility when using Kotlin with the Java SDK directly and should not be used with the Kotlin SDK APIs. - -| Aspect | DSL Builder (Java SDK interop) | KOptions (Kotlin SDK) | -|--------|--------------------------------|------------------------| -| Duration | Requires `.toJava()` conversion | Native `kotlin.time.Duration` | -| Syntax | `setStartToCloseTimeout(...)` | `startToCloseTimeout = ...` | -| Defaults | Must check Java defaults | Visible in constructor | -| Immutability | Mutable builder | Immutable data class | -| Copy | Manual rebuild | `copy()` function | -| IDE | Limited autocomplete | Full parameter hints | -| Usage | Java SDK only | Kotlin SDK | - -**Example - Copying with Modifications:** - -```kotlin -val baseOptions = KActivityOptions( - startToCloseTimeout = 30.seconds, - retryOptions = KRetryOptions(maximumAttempts = 3) -) - -// Create variant with different timeout -val longRunningOptions = baseOptions.copy( - startToCloseTimeout = 5.minutes, - heartbeatTimeout = 30.seconds -) -``` - -> **Implementation Note:** KOptions classes internally convert to Java SDK options. The conversion happens once when the activity/workflow is scheduled, so there's no runtime overhead during workflow execution. - -### Null Safety - -Nullable types replace `Optional` throughout the API. The following Java SDK types have Kotlin equivalents with null-safe APIs: - -| Java SDK | Kotlin SDK | -|----------|------------| -| `io.temporal.workflow.Workflow` | `KWorkflow` object | -| `io.temporal.workflow.WorkflowInfo` | `KWorkflowInfo` | -| `io.temporal.workflow.Promise` | Standard `Deferred` via `Promise.toDeferred()` | -| `io.temporal.activity.Activity` | `KActivity` object | -| `io.temporal.activity.ActivityExecutionContext` | `KActivityContext` | -| `io.temporal.activity.ActivityInfo` | `KActivityInfo` | -| `io.temporal.client.WorkflowStub` | `KWorkflowHandle` / `KTypedWorkflowHandle` | - -```kotlin -// KWorkflowInfo - nullable instead of Optional -interface KWorkflowInfo { - val workflowId: String - val runId: String - val parentWorkflowId: String? // Optional in Java - val parentRunId: String? // Optional in Java - fun getMemo(key: String): String? // Optional in Java - fun getSearchAttribute(key: String): Any? - // ... other properties -} - -// KActivityInfo - nullable instead of Optional -interface KActivityInfo { - val activityId: String - val activityType: String - val workflowId: String - val attempt: Int - fun getHeartbeatDetails(): Payloads? // Optional in Java - // ... other properties -} - -// Access via KWorkflow / KActivity objects -val info: KWorkflowInfo = KWorkflow.getInfo() -val parentId: String? = info.parentWorkflowId - -// KActivity.getContext() returns KActivityContext (matches Java's Activity.getExecutionContext()) -val context: KActivityContext = KActivity.getContext() -val activityInfo: KActivityInfo = context.info -context.heartbeat("progress") // Blocking version for regular activities -context.suspendHeartbeat("progress") // Suspend version for suspend activities -val logger = context.logger() // Get activity logger -``` - -This eliminates `.orElse(null)`, `.isPresent`, and other Optional ceremony. - -### Property Syntax for Queries - -Queries can be defined as Kotlin properties in the workflow interface: - -```kotlin -@WorkflowInterface -interface OrderWorkflow { - @QueryMethod - val status: OrderStatus // Property syntax - - @QueryMethod - fun getItemCount(): Int // Method syntax -} - -// Client usage via typed handle (using KWorkflowClient) -val handle = client.getWorkflowHandle("order-123") -val status = handle.query(OrderWorkflow::status) -val count = handle.query(OrderWorkflow::getItemCount) -``` - -## Workflow Definition - -There are two approaches for workflow definition, depending on whether you need Java interoperability. - -### Option A: Pure Kotlin (Recommended for Kotlin-only codebases) - -For pure Kotlin codebases, define interfaces with `suspend` methods: - -```kotlin -@WorkflowInterface -interface GreetingWorkflow { - @WorkflowMethod - suspend fun getGreeting(name: String): String -} - -class GreetingWorkflowImpl : GreetingWorkflow { - override suspend fun getGreeting(name: String): String { - return KWorkflow.executeActivity( - "composeGreeting", - KActivityOptions(startToCloseTimeout = 10.seconds), - "Hello", name - ) - } -} - -// Client call using KWorkflowClient - same pattern as activities, no stub needed -val result = client.executeWorkflow( - GreetingWorkflow::getGreeting, - KWorkflowOptions( - workflowId = "greeting-123", - taskQueue = "greetings" - ), - "Temporal" -) -``` - -### Option B: Java Interoperability (Parallel Interface Pattern) - -When you need to share workflow interfaces with Java code, use the parallel interface pattern: - -```kotlin -// Interface for client calls - non-suspend for Java compatibility -@WorkflowInterface -interface OrderWorkflow { - @WorkflowMethod - fun processOrder(order: Order): OrderResult - - @SignalMethod - fun cancelOrder(reason: String) - - @QueryMethod - val status: OrderStatus -} - -// Parallel suspend interface for Kotlin implementation -@KWorkflowImpl(workflowInterface = OrderWorkflow::class) -interface OrderWorkflowSuspend { - suspend fun processOrder(order: Order): OrderResult - suspend fun cancelOrder(reason: String) - val status: OrderStatus // Queries are never suspend -} - -// Kotlin implementation uses the suspend interface -class OrderWorkflowImpl : OrderWorkflowSuspend { - override suspend fun processOrder(order: Order): OrderResult { - // Full coroutine support - delay(), async, etc. - delay(1.hours) - return OrderResult(success = true) - } - - override suspend fun cancelOrder(reason: String) { /* ... */ } - override val status: OrderStatus get() = OrderStatus.PENDING -} - -// Client call using KWorkflowClient - same pattern, suspends for result -val result = client.executeWorkflow( - OrderWorkflow::processOrder, - KWorkflowOptions( - workflowId = "order-123", - taskQueue = "orders" - ), - order -) -``` - -**When to use which:** - -| Scenario | Approach | -|----------|----------| -| Pure Kotlin codebase | Option A - suspend interfaces | -| Calling Java-defined workflows | Option A works (from coroutine context) | -| Kotlin workflows with Java-defined interface | Option B - parallel interface | -| Shared interface library with Java | Option B - parallel interface | - -### Key Characteristics - -* Use `coroutineScope`, `async`, `launch` for concurrent execution -* Use `delay()` for timers (maps to Temporal timers, not `Thread.sleep`) -* Reuses `@WorkflowInterface` and `@WorkflowMethod` annotations from Java SDK -* Data classes work naturally for parameters and results - -### Signals, Queries, and Updates - -Signals and updates follow the same suspend/non-suspend pattern as workflow methods. Queries are always synchronous (never suspend) and can be defined as properties: - -```kotlin -@WorkflowInterface -interface OrderWorkflow { - @WorkflowMethod - suspend fun processOrder(order: Order): OrderResult - - @SignalMethod - suspend fun cancelOrder(reason: String) - - @UpdateMethod - suspend fun addItem(item: OrderItem): Boolean - - @UpdateValidatorMethod(updateMethod = "addItem") - fun validateAddItem(item: OrderItem) - - // Queries - always synchronous, can use property syntax - @QueryMethod - val status: OrderStatus - - @QueryMethod - fun getItemCount(): Int -} -``` - -#### Dynamic Handler Registration - -For workflows that need to handle signals, queries, or updates dynamically (without annotations), use the registration APIs: - -```kotlin -class DynamicWorkflowImpl : DynamicWorkflow { - private var state = mutableMapOf() - - override suspend fun execute(input: String): String { - // Register a named update handler with validator - KWorkflow.registerUpdateHandler( - "updateState", - validator = { args -> - val key = args.get(0, String::class.java) - require(key.isNotBlank()) { "Key cannot be blank" } - }, - handler = { args -> - val key = args.get(0, String::class.java) - val value = args.get(1, Any::class.java) - state[key] = value - "Updated $key" - } - ) - - // Register a named signal handler - KWorkflow.registerSignalHandler("notify") { args -> - val message = args.get(0, String::class.java) - println("Received: $message") - } - - // Register a named query handler - KWorkflow.registerQueryHandler("getState") { args -> - val key = args.get(0, String::class.java) - state[key] - } - - // Register dynamic handlers for unknown names (catch-all) - KWorkflow.registerDynamicUpdateHandler { updateName, args -> - "Handled unknown update: $updateName" - } - - KWorkflow.registerDynamicSignalHandler { signalName, args -> - println("Unknown signal: $signalName") - } - - KWorkflow.registerDynamicQueryHandler { queryName, args, resultClass -> - "Unknown query: $queryName" - } - - // Wait for completion signal - KWorkflow.awaitCondition { state["done"] == true } - return "Completed" - } -} -``` - -### Child Workflows - -Child workflows use the same stub-less pattern as activities: - -```kotlin -// Simple case - execute child workflow and wait for result -override suspend fun parentWorkflow(): String { - return KWorkflow.executeChildWorkflow( - ChildWorkflow::doWork, - KChildWorkflowOptions(workflowId = "child-workflow-id"), - "input" - ) -} - -// With retry options -override suspend fun parentWorkflowWithRetry(): String { - return KWorkflow.executeChildWorkflow( - ChildWorkflow::doWork, - KChildWorkflowOptions( - workflowId = "child-workflow-id", - workflowExecutionTimeout = 1.hours, - retryOptions = KRetryOptions(maximumAttempts = 3) - ), - "input" - ) -} - -// Parallel case - use standard coroutineScope { async {} } -override suspend fun parentWorkflowParallel(): String = coroutineScope { - // Start child and activity in parallel using standard Kotlin async - val childDeferred = async { - KWorkflow.executeChildWorkflow( - ChildWorkflow::doWork, - KChildWorkflowOptions(workflowId = "child-workflow-id"), - "input" - ) - } - val activityDeferred = async { - KWorkflow.executeActivity( - SomeActivities::doSomething, - KActivityOptions(startToCloseTimeout = 30.seconds) - ) - } - - // Wait for both using standard awaitAll - val (childResult, activityResult) = awaitAll(childDeferred, activityDeferred) - "$childResult - $activityResult" -} -``` - -### Child Workflow Handle - -For cases where you need to interact with a child workflow (signal, query, cancel) rather than just wait for its result, use `startChildWorkflow` to get a handle: - -```kotlin -// Start child workflow and get handle for interaction -override suspend fun parentWorkflowWithHandle(): String { - val handle = KWorkflow.startChildWorkflow( - ChildWorkflow::doWork, - KChildWorkflowOptions(workflowId = "child-workflow-id"), - "input" - ) - - // Can signal the child workflow - handle.signal(ChildWorkflow::updateProgress, 50) - - // Wait for result when ready - return handle.result() -} - -// Get handle to existing child workflow by ID -override suspend fun interactWithExistingChild(): String { - val handle = KWorkflow.getChildWorkflowHandle("child-workflow-id") - - // Signal the child workflow - handle.signal(ChildWorkflow::updatePriority, Priority.HIGH) - - return handle.result() -} - -// Parallel child workflows with handles for interaction -override suspend fun parallelChildrenWithHandles(): List = coroutineScope { - val handles = listOf("child-1", "child-2", "child-3").map { id -> - KWorkflow.startChildWorkflow( - ChildWorkflow::doWork, - KChildWorkflowOptions(workflowId = id), - "input" - ) - } - - // Can interact with any child while they're running - handles.forEach { handle -> - handle.signal(ChildWorkflow::updatePriority, Priority.HIGH) - } - - // Wait for all results - handles.map { async { it.result() } }.awaitAll() -} -``` - -**KChildWorkflowHandle API:** - -```kotlin -/** - * Handle for interacting with a started child workflow. - * Returned by startChildWorkflow() with result type captured from method reference. - * - * @param T The child workflow interface type - * @param R The result type of the child workflow method - */ -interface KChildWorkflowHandle { - /** The child workflow's workflow ID */ - val workflowId: String - - /** The child workflow's first execution run ID */ - val firstExecutionRunId: String - - /** - * Wait for the child workflow to complete and return its result. - * Suspends until the child workflow finishes. - */ - suspend fun result(): R - - // Signals - type-safe method references - suspend fun signal(method: KFunction1) - suspend fun signal(method: KFunction2, arg: A1) - suspend fun signal(method: KFunction3, arg1: A1, arg2: A2) - - /** - * Request cancellation of the child workflow. - * The child workflow will receive a CancellationException at its next suspension point. - */ - suspend fun cancel() -} -``` - -**KWorkflow methods for child workflow handles:** - -```kotlin -object KWorkflow { - /** - * Start a child workflow and return a handle for interaction. - * Use this when you need to signal, query, or cancel the child workflow. - * - * For simple fire-and-wait cases, prefer executeChildWorkflow() instead. - */ - suspend fun startChildWorkflow( - workflow: KFunction2, - options: ChildWorkflowOptions, - arg: A1 - ): KChildWorkflowHandle - - // Overloads for 0-6 arguments... - suspend fun startChildWorkflow( - workflow: KFunction1, - options: ChildWorkflowOptions - ): KChildWorkflowHandle - - suspend fun startChildWorkflow( - workflow: KFunction3, - options: ChildWorkflowOptions, - arg1: A1, arg2: A2 - ): KChildWorkflowHandle - - /** - * Get a handle to an existing child workflow by workflow ID. - * Use this to interact with a child workflow started earlier in the same workflow execution. - * - * @param T The child workflow interface type - * @param R The expected result type (must match the child workflow's return type) - * @param workflowId The child workflow's workflow ID - */ - inline fun getChildWorkflowHandle( - workflowId: String - ): KChildWorkflowHandle - - // --- KChildWorkflowOptions overloads (Kotlin-native) --- - - suspend fun startChildWorkflow( - workflow: KFunction2, - options: KChildWorkflowOptions, - arg: A1 - ): KChildWorkflowHandle - - suspend fun startChildWorkflow( - workflow: KFunction1, - options: KChildWorkflowOptions - ): KChildWorkflowHandle - - suspend fun startChildWorkflow( - workflow: KFunction3, - options: KChildWorkflowOptions, - arg1: A1, arg2: A2 - ): KChildWorkflowHandle - - // Overloads for executeChildWorkflow with KChildWorkflowOptions - suspend fun executeChildWorkflow( - workflow: KFunction2, - options: KChildWorkflowOptions, - arg: A1 - ): R - - suspend fun executeChildWorkflow( - workflow: KFunction1, - options: KChildWorkflowOptions - ): R - - // ... up to 6 arguments -} -``` - -> **Note:** For simple parallel execution where you only need the result, use standard `coroutineScope { async { executeChildWorkflow(...) } }`. Use `startChildWorkflow` only when you need to interact with the child workflow while it's running. - -**KChildWorkflowOptions:** - -```kotlin -// All fields are optional - null values inherit from parent workflow or use Java SDK defaults -KChildWorkflowOptions( - workflowId = "child-workflow-id", - taskQueue = "child-queue", // Optional: defaults to parent's task queue - workflowExecutionTimeout = 1.hours, - workflowRunTimeout = 30.minutes, - retryOptions = KRetryOptions( - initialInterval = 1.seconds, - maximumAttempts = 3 - ), - parentClosePolicy = ParentClosePolicy.PARENT_CLOSE_POLICY_TERMINATE, // Optional - cancellationType = ChildWorkflowCancellationType.WAIT_CANCELLATION_COMPLETED // Optional -) - -// Options are optional - use default KChildWorkflowOptions() when not specified -val result = KWorkflow.executeChildWorkflow( - ChildWorkflow::processData, - inputData // No options needed for simple cases -) -``` - -### Timers and Delays - -```kotlin -override suspend fun workflowWithTimer(): String { - // Simple delay using Kotlin Duration - delay(5.minutes) - - return "completed" -} -``` - -### Parallel Execution - -Use standard `coroutineScope { async { } }` for parallel execution: - -```kotlin -val options = KActivityOptions(startToCloseTimeout = 30.seconds) - -override suspend fun parallelWorkflow(items: List): List = coroutineScope { - // Process all items in parallel using standard Kotlin patterns - items.map { item -> - async { - KWorkflow.executeActivity("process", options, item) - } - }.awaitAll() // Standard kotlinx.coroutines.awaitAll -} - -// Another example: parallel activities with different results -override suspend fun getGreetings(name: String): String = coroutineScope { - val hello = async { KWorkflow.executeActivity("greet", options, "Hello", name) } - val goodbye = async { KWorkflow.executeActivity("greet", options, "Goodbye", name) } - - // Standard awaitAll works with any Deferred - val (helloResult, goodbyeResult) = awaitAll(hello, goodbye) - "$helloResult\n$goodbyeResult" -} -``` - -**Why this works deterministically:** -- All coroutines inherit the workflow's deterministic dispatcher -- The dispatcher executes tasks in a FIFO queue ensuring consistent ordering -- Same execution order during replay - -> **Note:** `coroutineScope` provides structured concurrency—if one `async` fails, all others are automatically cancelled. This maps naturally to Temporal's cancellation semantics. - -### Await Condition - -Wait for a condition to become true (equivalent to Java's `Workflow.await()`): - -```kotlin -override suspend fun workflowWithCondition(): String { - var approved = false - - // Signal handler sets approved = true - // ... - - // Wait until approved (blocks workflow until condition is true) - KWorkflow.awaitCondition { approved } - - return "Approved" -} - -// With timeout - returns false if timed out -override suspend fun workflowWithTimeout(): String { - var approved = false - - val wasApproved = KWorkflow.awaitCondition(timeout = 24.hours) { approved } - - return if (wasApproved) "Approved" else "Timed out" -} -``` - -### Logging - -Use `KWorkflow.logger()` for workflow-safe logging: - -```kotlin -// Get logger using workflow type as name -val log = KWorkflow.logger() -log.info("Processing order") - -// Or with custom logger name -val customLog = KWorkflow.logger("my.custom.logger") - -// Or with class -val classLog = KWorkflow.logger(MyWorkflowImpl::class.java) -``` - -> **Note:** Versioning (`KWorkflow.getVersion`), side effects (`KWorkflow.sideEffect`), and search attributes (`KWorkflow.upsertSearchAttributes`) use the same patterns as the Java SDK. - -### Continue-As-New - -Continue-as-new completes the current workflow execution and immediately starts a new execution with fresh event history. This is essential for: - -- **Preventing history growth**: Long-running workflows accumulate event history which can impact performance. Continue-as-new resets the history. -- **Batch processing**: Process a batch of items, then continue-as-new with the next batch offset. -- **Implementing loops**: Instead of infinite loops that accumulate history, use continue-as-new. - -```kotlin -// Basic continue-as-new - same workflow type, inherit all options -KWorkflow.continueAsNew(nextBatchId, newOffset) - -// With modified options -KWorkflow.continueAsNew( - KContinueAsNewOptions( - taskQueue = "high-priority-queue", - workflowRunTimeout = 2.hours - ), - nextBatchId, newOffset -) - -// Continue as different workflow type (for versioning/migration) -KWorkflow.continueAsNew( - "OrderProcessorV2", - KContinueAsNewOptions(taskQueue = "orders-v2"), - migratedState -) - -// Type-safe continue as different workflow using method reference -KWorkflow.continueAsNew( - OrderProcessorV2::process, - KContinueAsNewOptions(), - migratedState -) -``` - -**KContinueAsNewOptions:** - -```kotlin -/** - * Options for continuing a workflow as a new execution. - * All fields are optional - null values inherit from the current workflow. - */ -data class KContinueAsNewOptions( - val workflowRunTimeout: Duration? = null, - val taskQueue: String? = null, - val retryOptions: KRetryOptions? = null, - val workflowTaskTimeout: Duration? = null, - val memo: Map? = null, - val typedSearchAttributes: SearchAttributes? = null, - val contextPropagators: List? = null -) -``` - -**Batch Processing Pattern:** - -```kotlin -class BatchProcessorImpl : BatchProcessor { - override suspend fun processBatches(startOffset: Int) { - val batchSize = 100 - val items = KWorkflow.executeActivity( - DataActivities::fetchBatch, - KActivityOptions(startToCloseTimeout = 1.minutes), - startOffset, batchSize - ) - - if (items.isEmpty()) { - return // All done, workflow completes normally - } - - // Process items... - for (item in items) { - KWorkflow.executeActivity( - DataActivities::processItem, - KActivityOptions(startToCloseTimeout = 30.seconds), - item - ) - } - - // Continue with the next batch - KWorkflow.continueAsNew(startOffset + batchSize) - } -} -``` - -**History Size Check Pattern:** - -```kotlin -override suspend fun execute(state: WorkflowState) { - while (true) { - // Check if history is getting too large - if (KWorkflow.getInfo().isContinueAsNewSuggested) { - KWorkflow.continueAsNew(state) - } - - // Wait for signals and process... - KWorkflow.awaitCondition { hasNewWork } - processWork() - } -} -``` - -**KWorkflow.continueAsNew API:** - -```kotlin -object KWorkflow { - /** - * Continue workflow as new with the same workflow type. - * This function never returns - it terminates the current execution. - */ - fun continueAsNew(vararg args: Any?): Nothing - - /** - * Continue workflow as new with modified options. - * Null option values inherit from the current workflow. - */ - fun continueAsNew(options: KContinueAsNewOptions, vararg args: Any?): Nothing - - /** - * Continue as a different workflow type. - * Useful for workflow versioning or migration. - */ - fun continueAsNew( - workflowType: String, - options: KContinueAsNewOptions, - vararg args: Any? - ): Nothing - - /** - * Type-safe continue as different workflow using method reference. - */ - fun continueAsNew( - workflow: KFunction<*>, - options: KContinueAsNewOptions, - vararg args: Any? - ): Nothing -} -``` - -> **Important:** `continueAsNew` never returns normally. It terminates the current workflow execution and signals Temporal to start a new execution. The return type `Nothing` indicates this in Kotlin's type system. - -## Cancellation - -Kotlin's built-in coroutine cancellation replaces Java SDK's `CancellationScope`. This provides a more idiomatic experience while maintaining full Temporal semantics. - -### How Cancellation Works - -When a workflow is cancelled (from the server or programmatically), the Kotlin SDK translates this into coroutine cancellation. Cancellation is **cooperative**—it's checked at suspension points (`delay`, activity execution, child workflow execution, etc.): - -```kotlin -override suspend fun processOrder(order: Order): OrderResult = coroutineScope { - // Cancellation is checked at each suspension point - val validated = KWorkflow.executeActivity(...) // ← cancellation checked here - delay(5.minutes) // ← cancellation checked here - val result = KWorkflow.executeActivity(...) // ← cancellation checked here - result -} -``` - -### Explicit Cancellation Checks - -In rare cases where workflow code doesn't have suspension points (e.g., tight loops), you can check cancellation explicitly using `isActive` or `ensureActive()`. However, CPU-bound work should typically be delegated to activities. - -### Parallel Execution and Cancellation - -`coroutineScope` provides structured concurrency—if one child fails or the scope is cancelled, all children are automatically cancelled: - -```kotlin -override suspend fun parallelWorkflow(): String = coroutineScope { - val a = async { KWorkflow.executeActivity(...) } - val b = async { KWorkflow.executeActivity(...) } - - // If either activity fails, the other is cancelled - // If workflow is cancelled, both activities are cancelled - "${a.await()} - ${b.await()}" -} -``` - -### Detached Scopes (Cleanup Logic) - -Use `withContext(NonCancellable)` for cleanup code that must run even when the workflow is cancelled: - -```kotlin -override suspend fun processOrder(order: Order): OrderResult { - try { - return doProcessOrder(order) - } catch (e: CancellationException) { - // Cleanup runs even though workflow is cancelled - withContext(NonCancellable) { - KWorkflow.executeActivity( - OrderActivities::releaseReservation, - KActivityOptions(startToCloseTimeout = 30.seconds), - order - ) - } - throw e // Re-throw to propagate cancellation - } -} -``` - -This is equivalent to Java's `Workflow.newDetachedCancellationScope()`. - -### Cancellation with Timeout - -Use `withTimeout` to cancel a block after a duration: - -```kotlin -override suspend fun processWithDeadline(order: Order): OrderResult { - return withTimeout(1.hours) { - // Everything in this block is cancelled if it takes > 1 hour - val validated = KWorkflow.executeActivity(...) - val charged = KWorkflow.executeActivity(...) - OrderResult(success = true) - } -} - -// Or use withTimeoutOrNull to get null instead of exception -override suspend fun tryProcess(order: Order): OrderResult? { - return withTimeoutOrNull(30.minutes) { - KWorkflow.executeActivity(...) - } -} -``` - -### Comparison with Java SDK - -| Java SDK | Kotlin SDK | -|----------|------------| -| `Workflow.newCancellationScope(() -> { ... })` | `coroutineScope { ... }` | -| `Workflow.newDetachedCancellationScope(() -> { ... })` | `withContext(NonCancellable) { ... }` | -| `CancellationScope.cancel()` | `job.cancel()` | -| `CancellationScope.isCancelRequested()` | `!isActive` | -| `CancellationScope.throwCanceled()` | `ensureActive()` | -| `scope.run()` with timeout | `withTimeout(duration) { ... }` | - -## Activity Definition - -### String-based Activity Execution (Phase 1) - -For calling activities by name (useful for cross-language interop or dynamic activity names): - -```kotlin -// Execute activity by string name - suspend function, awaits result -val result = KWorkflow.executeActivity( - "activityName", - KActivityOptions( - startToCloseTimeout = 30.seconds, - retryOptions = KRetryOptions( - initialInterval = 1.seconds, - maximumAttempts = 3 - ) - ), - arg1, arg2 -) - -// Parallel execution - use standard coroutineScope { async {} } -val results = coroutineScope { - val d1 = async { KWorkflow.executeActivity("activity1", options, arg1) } - val d2 = async { KWorkflow.executeActivity("activity2", options, arg2) } - awaitAll(d1, d2) // Returns List -} -``` - -> **Note:** The `ActivityOptions { }` DSL from `temporal-kotlin` exists for using Kotlin with the Java SDK directly and should not be used with Kotlin SDK APIs. - -### Typed Activities (Phase 2) - -The typed activity API uses direct method references - no stub creation needed. This approach: -- Provides full compile-time type safety for arguments and return types -- Allows different options (timeouts, retry policies) per activity call -- Works with both Kotlin `suspend` and Java non-suspend activity interfaces -- Similar to TypeScript and Python SDK patterns - -```kotlin -// Define activity interface -@ActivityInterface -interface GreetingActivities { - @ActivityMethod - suspend fun composeGreeting(greeting: String, name: String): String - - @ActivityMethod - suspend fun sendEmail(email: Email): SendResult - - @ActivityMethod - suspend fun log(message: String) -} - -// In workflow - direct method reference, no stub needed -val greeting = KWorkflow.executeActivity( - GreetingActivities::composeGreeting, // Direct reference to interface method - KActivityOptions(startToCloseTimeout = 30.seconds), - "Hello", "World" -) - -// Different activity, different options -val result = KWorkflow.executeActivity( - GreetingActivities::sendEmail, - KActivityOptions( - startToCloseTimeout = 2.minutes, - retryOptions = KRetryOptions(maximumAttempts = 5) - ), - email -) - -// Void activities work too -KWorkflow.executeActivity( - GreetingActivities::log, - KActivityOptions(startToCloseTimeout = 5.seconds), - "Processing started" -) -``` - -**Type Safety:** The API uses `KFunction` reflection to extract method metadata and provides compile-time type checking: - -```kotlin -// Compile error! Wrong argument types -KWorkflow.executeActivity( - GreetingActivities::composeGreeting, - options, - 123, true // ✗ Type mismatch: expected String, String -) -``` - -**Parallel Execution:** Use standard `coroutineScope { async { } }` for concurrent execution: - -```kotlin -override suspend fun parallelGreetings(names: List): List = coroutineScope { - names.map { name -> - async { - KWorkflow.executeActivity( - GreetingActivities::composeGreeting, - KActivityOptions(startToCloseTimeout = 10.seconds), - "Hello", name - ) - } - }.awaitAll() // Standard kotlinx.coroutines.awaitAll -} - -// Multiple different activities in parallel -val (result1, result2) = coroutineScope { - val d1 = async { KWorkflow.executeActivity(Activities::operation1, options, arg1) } - val d2 = async { KWorkflow.executeActivity(Activities::operation2, options, arg2) } - awaitAll(d1, d2) -} -``` - -> **Note:** We use standard Kotlin `async` instead of a custom `startActivity` method. The workflow's deterministic dispatcher ensures correct replay behavior. - -**Java Activity Interoperability:** Method references work regardless of whether the activity is defined in Kotlin or Java: - -```kotlin -// Java activity interface works seamlessly -// public interface JavaPaymentActivities { -// PaymentResult processPayment(String orderId, BigDecimal amount); -// } - -val result: PaymentResult = KWorkflow.executeActivity( - JavaPaymentActivities::processPayment, - KActivityOptions(startToCloseTimeout = 2.minutes), - orderId, amount -) -``` - -### Activity Execution API - -The `KWorkflow` object provides type-safe overloads using `KFunction` types. The interface method reference (`Interface::method`) is used to extract metadata (interface class, method name) via reflection - the function is never invoked directly. - -```kotlin -import kotlin.reflect.KFunction - -object KWorkflow { - // Execute activity - uses KFunction for metadata extraction - // T = activity interface, A1..An = arguments, R = return type - - // 1 argument - suspend fun executeActivity( - activity: KFunction2, // T::method with 1 arg - options: ActivityOptions, - arg1: A1 - ): R - - // 2 arguments - suspend fun executeActivity( - activity: KFunction3, // T::method with 2 args - options: ActivityOptions, - arg1: A1, arg2: A2 - ): R - - // ... up to 6 arguments - - // Local activity - same pattern - suspend fun executeLocalActivity( - activity: KFunction2, - options: LocalActivityOptions, - arg1: A1 - ): R - - // --- KOptions overloads (Kotlin-native) --- - - // Execute activity with KActivityOptions - suspend fun executeActivity( - activity: KFunction2, - options: KActivityOptions, - arg1: A1 - ): R - - suspend fun executeActivity( - activity: KFunction3, - options: KActivityOptions, - arg1: A1, arg2: A2 - ): R - - // ... up to 6 arguments - - // Execute local activity with KLocalActivityOptions - suspend fun executeLocalActivity( - activity: KFunction2, - options: KLocalActivityOptions, - arg1: A1 - ): R - - // --- String-based overloads (untyped) --- - - // Execute activity by name - suspend inline fun executeActivity( - activityName: String, - options: ActivityOptions, - vararg args: Any? - ): R - - // Execute local activity by name - suspend inline fun executeLocalActivity( - activityName: String, - options: LocalActivityOptions, - vararg args: Any? - ): R - - // --- String-based with KOptions --- - - // Execute activity by name with KActivityOptions - suspend inline fun executeActivity( - activityName: String, - options: KActivityOptions, - vararg args: Any? - ): R - - // Execute local activity by name with KLocalActivityOptions - suspend inline fun executeLocalActivity( - activityName: String, - options: KLocalActivityOptions, - vararg args: Any? - ): R -} -``` - -**Parallel Execution:** Use standard `coroutineScope { async { } }` instead of custom handle types: - -```kotlin -// Standard Kotlin pattern for parallel execution -val results = coroutineScope { - val d1 = async { KWorkflow.executeActivity("add", options, 1, 2) } - val d2 = async { KWorkflow.executeActivity("add", options, 3, 4) } - awaitAll(d1, d2) // Returns standard Deferred instances -} -``` - -> **Implementation Note:** `KFunction` provides `.name` for the method name and `.parameters[0].type` for the declaring interface. This metadata is used to make Temporal activity calls by name. - -### Activity Implementation - -There are two approaches for activity implementation, depending on whether you need Java interoperability. - -#### Option A: Pure Kotlin (Recommended for Kotlin-only codebases) - -For pure Kotlin codebases, define interfaces with `suspend` methods directly: - -```kotlin -@ActivityInterface -interface GreetingActivities { - @ActivityMethod - suspend fun composeGreeting(greeting: String, name: String): String - - @ActivityMethod - suspend fun sendEmail(email: Email): SendResult -} - -class GreetingActivitiesImpl( - private val emailService: EmailService -) : GreetingActivities { - - override suspend fun composeGreeting(greeting: String, name: String): String { - // Full coroutine support - use withContext, async I/O, etc. - return withContext(Dispatchers.IO) { - "$greeting, $name!" - } - } - - override suspend fun sendEmail(email: Email): SendResult { - // Suspend functions for async I/O - return emailService.send(email) - } -} - -// In workflow - direct method reference, no stub needed -val greeting = KWorkflow.executeActivity( - GreetingActivities::composeGreeting, - KActivityOptions(startToCloseTimeout = 30.seconds), - "Hello", "World" -) -``` - -#### Option B: Java Interoperability (Parallel Interface Pattern) - -When you need to share activity interfaces with Java code (e.g., activities implemented in Java, or interfaces defined in a shared Java module), use the parallel interface pattern: - -```kotlin -// Interface for workflow calls - non-suspend for Java compatibility -// This interface can be defined in Java or Kotlin -@ActivityInterface -interface OrderActivities { - @ActivityMethod - fun validateOrder(order: Order): Boolean - - @ActivityMethod - fun chargePayment(order: Order): PaymentResult -} - -// Parallel suspend interface for Kotlin implementation -// Linked to the original interface via annotation -@KActivityImpl(activities = OrderActivities::class) -interface OrderActivitiesSuspend { - suspend fun validateOrder(order: Order): Boolean - suspend fun chargePayment(order: Order): PaymentResult -} - -// Kotlin implementation uses the suspend interface -class OrderActivitiesImpl( - private val paymentService: PaymentService -) : OrderActivitiesSuspend { - - override suspend fun validateOrder(order: Order): Boolean { - return order.items.isNotEmpty() && order.total > 0 - } - - override suspend fun chargePayment(order: Order): PaymentResult { - // Full suspend support for async operations - return paymentService.charge(order) - } -} - -// In workflow - use the non-suspend interface for method references -val isValid = KWorkflow.executeActivity( - OrderActivities::validateOrder, - KActivityOptions(startToCloseTimeout = 10.seconds), - order -) -``` - -**When to use which:** - -| Scenario | Approach | -|----------|----------| -| Pure Kotlin codebase | Option A - suspend interfaces | -| Calling Java-defined activities | Option A works (executeActivity handles it) | -| Kotlin activities with Java-defined interface | Option B - parallel interface | -| Shared interface library with Java | Option B - parallel interface | - -#### Registering Activities - -```kotlin -// Option A: Register suspend implementation directly -worker.registerActivitiesImplementations(GreetingActivitiesImpl(emailService)) - -// Option B: Register implementation - binding inferred from @KActivityImpl annotation -worker.registerActivitiesImplementations(OrderActivitiesImpl(paymentService)) -``` - -### Local Activities - -Local activities use the same stub-less pattern: - -```kotlin -@ActivityInterface -interface ValidationActivities { - fun validate(input: String): Boolean - fun sanitize(input: String): String -} - -val isValid = KWorkflow.executeLocalActivity( - ValidationActivities::validate, - KLocalActivityOptions(startToCloseTimeout = 5.seconds), - input -) - -val sanitized = KWorkflow.executeLocalActivity( - ValidationActivities::sanitize, - KLocalActivityOptions(startToCloseTimeout = 1.seconds), - input -) -``` - -### KActivity API - -`KActivity.getContext()` returns a `KActivityContext`, matching Java SDK's `Activity.getExecutionContext()` pattern: - -```kotlin -// In activity implementation - -// Get activity context (matches Java's Activity.getExecutionContext()) -val context = KActivity.getContext() - -// Get activity info -val info = context.info -println("Activity ${info.activityType}, attempt ${info.attempt}") - -// Heartbeat for long-running activities -// Use heartbeat() in regular activities -context.heartbeat(progressDetails) - -// Use suspendHeartbeat() in suspend activities for non-blocking operation -context.suspendHeartbeat(progressDetails) - -// Get heartbeat details from previous attempt (for retry scenarios) -val previousProgress: Int? = context.getHeartbeatDetails() - -// Logging - use activity logger for proper log context -val log = context.logger() // Uses activity type as logger name -log.info("Processing item") - -// Or with custom logger name -val customLog = context.logger("custom.logger") - -// Mark activity for async completion -context.doNotCompleteOnReturn() -val taskToken = context.taskToken -``` - -**KActivityContext Interface:** - -```kotlin -interface KActivityContext { - val info: KActivityInfo - fun heartbeat(details: Any? = null) - suspend fun suspendHeartbeat(details: Any? = null) - fun getHeartbeatDetails(detailsClass: Class): T? - val taskToken: ByteArray - fun doNotCompleteOnReturn() - val isDoNotCompleteOnReturn: Boolean - fun logger(): Logger - fun logger(name: String): Logger - fun logger(clazz: Class<*>): Logger -} - -// Reified extension for easier Kotlin usage -inline fun KActivityContext.getHeartbeatDetails(): T? -``` - -> **Note:** `ActivityCompletionClient` for async activity completion uses the same API as the Java SDK. - -## Client API - -> **Note:** Many client extensions already exist in the `temporal-kotlin` module, including: -> - `WorkflowClient { }` DSL constructor -> - `WorkflowClient.newWorkflowStub { }` with reified generics -> - `WorkflowStub.getResult()` with reified generics -> - `WorkflowOptions { }`, `WorkflowClientOptions { }` DSL builders - -### Creating a Client - -```kotlin -val service = WorkflowServiceStubs.newLocalServiceStubs() - -// Create KWorkflowClient with DSL configuration -val client = KWorkflowClient(service) { - setNamespace("default") - setDataConverter(myConverter) -} - -// For blocking calls from non-suspend contexts, use runBlocking -val result = runBlocking { - client.executeWorkflow(MyWorkflow::process, options, input) -} -``` - -### KWorkflowClient - -`KWorkflowClient` provides Kotlin-specific APIs with suspend functions for starting and interacting with workflows: - -```kotlin -/** - * Kotlin workflow client providing suspend functions and type-safe workflow APIs. - * - * @param service The WorkflowServiceStubs to connect to - * @param options DSL builder for WorkflowClientOptions - */ -class KWorkflowClient( - service: WorkflowServiceStubs, - options: WorkflowClientOptions.Builder.() -> Unit = {} -) { - /** The underlying WorkflowClient for advanced use cases */ - val workflowClient: WorkflowClient - - /** - * Start a workflow and return a handle for interaction. - * Does not wait for the workflow to complete. - */ - suspend fun startWorkflow( - workflow: KFunction1, - options: WorkflowOptions - ): KTypedWorkflowHandle - - suspend fun startWorkflow( - workflow: KFunction2, - options: WorkflowOptions, - arg: A1 - ): KTypedWorkflowHandle - - // Overloads for 2-6 arguments... - - /** - * Start a workflow and wait for its result. - * Suspends until the workflow completes. - */ - suspend fun executeWorkflow( - workflow: KFunction1, - options: WorkflowOptions - ): R - - suspend fun executeWorkflow( - workflow: KFunction2, - options: WorkflowOptions, - arg: A1 - ): R - - // Overloads for 2-6 arguments... - - // --- KWorkflowOptions overloads (Kotlin-native) --- - - /** - * Start a workflow with KWorkflowOptions and return a handle. - */ - suspend fun startWorkflow( - workflow: KFunction1, - options: KWorkflowOptions - ): KTypedWorkflowHandle - - suspend fun startWorkflow( - workflow: KFunction2, - options: KWorkflowOptions, - arg: A1 - ): KTypedWorkflowHandle - - // Overloads for 2-6 arguments... - - /** - * Execute a workflow with KWorkflowOptions and wait for result. - */ - suspend fun executeWorkflow( - workflow: KFunction1, - options: KWorkflowOptions - ): R - - suspend fun executeWorkflow( - workflow: KFunction2, - options: KWorkflowOptions, - arg: A1 - ): R - - // Overloads for 2-6 arguments... - - /** - * Get a typed handle for an existing workflow by ID. - * Use this to signal, query, or get results from a workflow started elsewhere. - */ - inline fun getWorkflowHandle(workflowId: String): KWorkflowHandle - inline fun getWorkflowHandle(workflowId: String, runId: String): KWorkflowHandle - - /** - * Get an untyped handle for an existing workflow by ID. - * Use when you don't know the workflow type at compile time. - */ - fun getUntypedWorkflowHandle(workflowId: String): WorkflowHandle - fun getUntypedWorkflowHandle(workflowId: String, runId: String): WorkflowHandle - - /** - * Atomically start a workflow and send a signal. - * If the workflow already exists, only the signal is sent. - */ - suspend fun signalWithStart( - workflow: KFunction2, - options: WorkflowOptions, - workflowArg: A1, - signal: KFunction2, - signalArg: SA1 - ): KTypedWorkflowHandle - - // --- Update With Start --- - - /** - * Create a workflow start operation for use with update-with-start. - * Captures the workflow method, arguments, and options for atomic execution. - */ - fun withStartWorkflowOperation( - workflow: KFunction1, - options: KWorkflowOptions - ): KWithStartWorkflowOperation - - fun withStartWorkflowOperation( - workflow: KFunction2, - options: KWorkflowOptions, - arg1: A1 - ): KWithStartWorkflowOperation - - // Overloads for 2-6 workflow arguments... - // Also KSuspendFunction variants for suspend workflow methods... - - /** - * Atomically start a workflow and send an update, returning immediately after - * the update reaches the specified wait stage. - * - * If the workflow is not running, starts it and sends the update. - * Behavior for existing workflows depends on [KWorkflowOptions.workflowIdConflictPolicy]: - * - USE_EXISTING: sends update to existing workflow - * - FAIL: throws WorkflowExecutionAlreadyStarted - * - * @param update Update method reference (must be suspend) - * @param options Options containing the start operation and wait stage - * @return Handle to track the update result - */ - suspend fun startUpdateWithStart( - update: KSuspendFunction1, - options: KUpdateWithStartOptions - ): KUpdateHandle - - suspend fun startUpdateWithStart( - update: KSuspendFunction2, - options: KUpdateWithStartOptions, - updateArg1: UA1 - ): KUpdateHandle - - // Overloads for 2-6 update arguments... - - /** - * Atomically start a workflow and execute an update, waiting for completion. - * Convenience method equivalent to startUpdateWithStart with waitForStage=COMPLETED. - * - * @param update Update method reference (must be suspend) - * @param options Options containing the start operation - * @return The update result - */ - suspend fun executeUpdateWithStart( - update: KSuspendFunction1, - options: KUpdateWithStartOptions - ): UR - - suspend fun executeUpdateWithStart( - update: KSuspendFunction2, - options: KUpdateWithStartOptions, - updateArg1: UA1 - ): UR - - // Overloads for 2-6 update arguments... - - // --- Signal With Start --- - - suspend fun signalWithStart( - workflow: KFunction2, - options: KWorkflowOptions, - workflowArg: A1, - signal: KFunction2, - signalArg: SA1 - ): KTypedWorkflowHandle - - // Overloads for different argument counts... -} -``` - -### Starting Workflows - -```kotlin -// Execute workflow and wait for result (suspend function) -val result = client.executeWorkflow( - GreetingWorkflow::getGreeting, - KWorkflowOptions( - workflowId = "greeting-123", - taskQueue = "greeting-queue", - workflowExecutionTimeout = 1.hours - ), - "Temporal" -) - -// Or start async and get handle -val handle = client.startWorkflow( - GreetingWorkflow::getGreeting, - KWorkflowOptions( - workflowId = "greeting-123", - taskQueue = "greeting-queue" - ), - "Temporal" -) -val result = handle.result() // Type inferred as String from method reference -``` - -### SignalWithStart - -Atomically start a workflow and send a signal. If the workflow already exists, only the signal is sent: - -```kotlin -// Returns KTypedWorkflowHandle - result type captured from method reference -val handle = client.signalWithStart( - workflow = OrderWorkflow::processOrder, - options = KWorkflowOptions( - workflowId = "order-123", - taskQueue = "orders" - ), - workflowArg = order, - signal = OrderWorkflow::updatePriority, - signalArg = Priority.HIGH -) - -// Can use typed handle for queries/signals -val status = handle.query(OrderWorkflow::status) -val result = handle.result() // Type inferred as OrderResult -``` - -### UpdateWithStart - -Atomically start a workflow and send an update. Behavior depends on `workflowIdConflictPolicy`: -- `USE_EXISTING`: sends update to existing workflow -- `FAIL`: throws exception if workflow already exists - -**Supporting Types:** - -```kotlin -/** - * Represents a workflow start operation for use with update-with-start. - * Created via KWorkflowClient.withStartWorkflowOperation(). - */ -class KWithStartWorkflowOperation { - /** Get the workflow result after the operation completes. */ - fun getResult(): R -} - -/** - * Options for update-with-start operations. - * Bundles the start operation with update-specific options. - */ -data class KUpdateWithStartOptions( - /** The workflow start operation (required) */ - val startWorkflowOperation: KWithStartWorkflowOperation, - - /** Stage to wait for before returning (required for startUpdateWithStart) */ - val waitForStage: WorkflowUpdateStage = WorkflowUpdateStage.ACCEPTED, - - /** Optional update ID for idempotency */ - val updateId: String? = null -) -``` - -**Usage - Execute and wait for completion:** - -```kotlin -// Step 1: Create the workflow start operation -val startOp = client.withStartWorkflowOperation( - OrderWorkflow::processOrder, - KWorkflowOptions( - workflowId = "order-123", - taskQueue = "orders", - workflowIdConflictPolicy = WorkflowIdConflictPolicy.USE_EXISTING // Required - ), - order -) - -// Step 2: Execute update with start (waits for update completion) -val updateResult: Boolean = client.executeUpdateWithStart( - OrderWorkflow::addItem, // Must be suspend function - KUpdateWithStartOptions(startWorkflowOperation = startOp), - newItem -) -println("Item added: $updateResult") - -// Step 3: Access workflow result if needed -val workflowResult: OrderResult = startOp.getResult() -``` - -**Usage - Start async (don't wait for completion):** - -```kotlin -val startOp = client.withStartWorkflowOperation( - OrderWorkflow::processOrder, - KWorkflowOptions( - workflowId = "order-456", - taskQueue = "orders", - workflowIdConflictPolicy = WorkflowIdConflictPolicy.FAIL - ), - order -) - -// Start update and return immediately after it's accepted -val updateHandle: KUpdateHandle = client.startUpdateWithStart( - OrderWorkflow::addItem, - KUpdateWithStartOptions( - startWorkflowOperation = startOp, - waitForStage = WorkflowUpdateStage.ACCEPTED - ), - newItem -) - -// Later: get the update result -val result = updateHandle.result() -``` - -**Usage - No arguments:** - -```kotlin -val startOp = client.withStartWorkflowOperation( - GreetingWorkflow::greet, - KWorkflowOptions( - workflowId = "greeting-789", - taskQueue = "greetings", - workflowIdConflictPolicy = WorkflowIdConflictPolicy.USE_EXISTING - ) -) - -val status: String = client.executeUpdateWithStart( - GreetingWorkflow::getStatus, // suspend fun getStatus(): String - KUpdateWithStartOptions(startWorkflowOperation = startOp) -) -``` - -### Workflow Handle - -For interacting with existing workflows (signals, queries, results, cancellation), use a typed or untyped handle: - -```kotlin -// Get typed handle for existing workflow by ID -val handle = client.getWorkflowHandle("order-123") - -// Send signal - method reference provides type safety -handle.signal(OrderWorkflow::updatePriority, Priority.HIGH) - -// Query - method reference with compile-time type checking -val status = handle.query(OrderWorkflow::status) -val count = handle.query(OrderWorkflow::getItemCount) - -// Get result (suspends until workflow completes) -val result = handle.result() - -// Updates - execute and wait for result -val updateResult = handle.executeUpdate(OrderWorkflow::addItem, newItem) - -// Or start update async and get handle -val updateHandle = handle.startUpdate(OrderWorkflow::addItem, newItem) -val asyncResult = updateHandle.result() - -// Cancel or terminate -handle.cancel() -handle.terminate("No longer needed") - -// Workflow metadata -val info = handle.describe() -println("Workflow ID: ${handle.workflowId}, Run ID: ${handle.runId}") -``` - -**KWorkflowHandle API:** - -```kotlin -// Base handle - returned by getKWorkflowHandle(id) -// Result type is unknown, must specify when calling result() -interface KWorkflowHandle { - val workflowId: String - val runId: String? - - // Result - requires explicit type since we don't know it - suspend fun result(): R - - // Signals - type-safe method references - suspend fun signal(method: KFunction1) - suspend fun signal(method: KFunction2, arg: A1) - suspend fun signal(method: KFunction3, arg1: A1, arg2: A2) - - // Queries - type-safe method references - fun query(method: KFunction1): R - fun query(method: KFunction2, arg: A1): R - - // Updates - execute and wait for result - suspend fun executeUpdate(method: KFunction1): R - suspend fun executeUpdate(method: KFunction2, arg: A1): R - - // Updates - start and get handle for async result - suspend fun startUpdate(method: KFunction1): KUpdateHandle - suspend fun startUpdate(method: KFunction2, arg: A1): KUpdateHandle - - // Get handle for existing update by ID - fun getKUpdateHandle(updateId: String): KUpdateHandle - - // Lifecycle - suspend fun cancel() - suspend fun terminate(reason: String? = null) - fun describe(): WorkflowExecutionInfo -} - -// Extended handle - returned by startWorkflow() -// Result type R is captured from the workflow method reference -interface KTypedWorkflowHandle : KWorkflowHandle { - // Result type is known from method reference - no type parameter needed - suspend fun result(): R -} - -interface KUpdateHandle { - val updateId: String - suspend fun result(): R -} -``` - -**How result type is captured:** - -```kotlin -// startWorkflow captures result type from method reference -suspend fun startWorkflow( - workflow: KFunction2, // R is captured here - options: WorkflowOptions, - arg: A1 -): KTypedWorkflowHandle // R is preserved in return type - -// Usage - result type is inferred -val handle = client.startWorkflow( - OrderWorkflow::processOrder, // KFunction2 - options, - order -) -val result: OrderResult = handle.result() // No type parameter needed! - -// getWorkflowHandle doesn't know result type -val existingHandle = client.getWorkflowHandle(workflowId) -val result = existingHandle.result() // Must specify type -``` - -This pattern matches Python SDK's `WorkflowHandle` with the same method names (`signal`, `query`, `result`, `cancel`, `terminate`, `execute_update`, `start_update`). - -**Untyped Handle (like Python's `get_workflow_handle`):** - -For cases where you don't know the workflow type at compile time: - -```kotlin -// Untyped handle - signal/query by string name -val untypedHandle = client.getUntypedWorkflowHandle("order-123") - -// Operations use string names instead of method references -untypedHandle.signal("updatePriority", Priority.HIGH) -val status = untypedHandle.query("status") -val result = untypedHandle.result() - -// Cancel/terminate work the same -untypedHandle.cancel() -``` - -```kotlin -interface WorkflowHandle { - val workflowId: String - val runId: String? - - suspend fun result(): R - suspend fun signal(signalName: String, vararg args: Any?) - fun query(queryName: String, vararg args: Any?): R - suspend fun executeUpdate(updateName: String, vararg args: Any?): Any? - suspend fun cancel() - suspend fun terminate(reason: String? = null) - fun describe(): WorkflowExecutionInfo -} -``` - -## Worker API - -### KWorkerFactory (Recommended) - -For pure Kotlin applications, use `KWorkerFactory` which automatically enables coroutine support: - -```kotlin -val service = WorkflowServiceStubs.newLocalServiceStubs() -val client = KWorkflowClient(service) { ... } - -// KWorkerFactory automatically enables Kotlin coroutine support -val factory = KWorkerFactory(client) { - maxWorkflowThreadCount = 800 -} - -val worker: KWorker = factory.newWorker("task-queue") { - maxConcurrentActivityExecutionSize = 100 -} - -// Register Kotlin coroutine workflows -worker.registerWorkflowImplementationTypes( - GreetingWorkflowImpl::class, - OrderWorkflowImpl::class -) - -// Register activities - suspend functions handled automatically -worker.registerActivitiesImplementations( - GreetingActivitiesImpl(), // Kotlin suspend activities - JavaActivitiesImpl() // Java activities work too -) - -// Start the worker -factory.start() -``` - -**KWorkerFactory API:** - -```kotlin -/** - * Kotlin worker factory that automatically enables coroutine support. - * Wraps WorkerFactory with KotlinPlugin pre-configured. - */ -class KWorkerFactory( - client: KWorkflowClient, - options: WorkerFactoryOptions.Builder.() -> Unit = {} -) { - /** The underlying WorkerFactory for advanced use cases */ - val workerFactory: WorkerFactory - - fun newWorker(taskQueue: String, options: WorkerOptions.Builder.() -> Unit = {}): KWorker - fun start() - fun shutdown() - fun shutdownNow() - suspend fun awaitTermination(timeout: Duration) -} -``` - -**KWorker API:** - -```kotlin -/** - * Kotlin worker that provides idiomatic APIs for registering - * Kotlin workflows and suspend activities. - * - * Use KWorker for pure Kotlin implementations. For mixed Java/Kotlin - * scenarios, access the underlying Worker via the [worker] property. - */ -class KWorker { - /** The underlying Java Worker for interop scenarios */ - val worker: Worker - - /** Register Kotlin workflow implementation types using reified generics */ - inline fun registerWorkflowImplementationTypes() - - /** Register Kotlin workflow implementation types using KClass */ - fun registerWorkflowImplementationTypes(vararg workflowClasses: KClass<*>) - - /** Register Kotlin workflow implementation types with options */ - fun registerWorkflowImplementationTypes( - options: WorkflowImplementationOptions, - vararg workflowClasses: KClass<*> - ) - - /** Register activity implementations (automatically detects suspend functions) */ - fun registerActivitiesImplementations(vararg activities: Any) - - /** Register suspend activity implementations explicitly */ - fun registerSuspendActivities(vararg activities: Any) - - /** Register Nexus service implementations */ - fun registerNexusServiceImplementations(vararg services: Any) -} -``` - -**When to use KWorker vs Worker:** -- Use `KWorker` for pure Kotlin implementations (recommended) -- Use `Worker` (via `kworker.worker`) when mixing Java and Kotlin workflows/activities on the same worker - -### KotlinPlugin (For Java Main) - -When your main application is written in Java and you need to register Kotlin workflows, use `KotlinPlugin` explicitly: - -```kotlin -// Java main or mixed Java/Kotlin setup -val service = WorkflowServiceStubs.newLocalServiceStubs() -val client = WorkflowClient.newInstance(service) - -val factory = WorkerFactory.newInstance(client, WorkerFactoryOptions.newBuilder() - .addPlugin(KotlinPlugin()) - .build()) - -val worker = factory.newWorker("task-queue") - -// Register Kotlin workflows - plugin handles suspend functions -worker.registerWorkflowImplementationTypes(KotlinWorkflowImpl::class.java) -``` - -### Mixed Java and Kotlin - -A single worker supports both Java and Kotlin workflows on the same task queue: - -```kotlin -// Java workflows (thread-based) -worker.registerWorkflowImplementationTypes( - OrderWorkflowJavaImpl::class.java -) - -// Kotlin workflows (coroutine-based) - same method, plugin handles execution -worker.registerWorkflowImplementationTypes( - GreetingWorkflowImpl::class -) - -// Both run on the same worker - execution model is per-workflow-instance -factory.start() -``` - -## Data Conversion - -The Kotlin SDK uses `kotlinx.serialization` by default for JSON serialization. It provides compile-time safety, no reflection overhead, and native Kotlin support. - -### kotlinx.serialization (Default) - -Annotate data classes with `@Serializable`: - -```kotlin -@Serializable -data class Order( - val id: String, - val items: List, - val status: OrderStatus -) - -@Serializable -data class OrderItem( - val productId: String, - val quantity: Int -) - -@Serializable -enum class OrderStatus { PENDING, PROCESSING, COMPLETED } -``` - -No additional configuration needed—the SDK automatically uses `kotlinx.serialization` for classes annotated with `@Serializable`. - -**Custom JSON configuration:** - -```kotlin -val client = WorkflowClient(service) { - dataConverter = KotlinxSerializationDataConverter { - ignoreUnknownKeys = true - prettyPrint = false // default - encodeDefaults = true - } -} -``` - -### Jackson (Optional, for Java Interop) - -For mixed Java/Kotlin codebases or when integrating with existing Jackson-based infrastructure: - -```kotlin -val converter = DefaultDataConverter.newDefaultInstance().withPayloadConverterOverrides( - JacksonJsonPayloadConverter( - KotlinObjectMapperFactory.new() - ) -) - -val client = WorkflowClient(service) { - dataConverter = converter -} -``` - -> **Note:** Jackson requires the `jackson-module-kotlin` dependency and uses runtime reflection. Prefer `kotlinx.serialization` for pure Kotlin projects. - -## Interceptors - -Interceptors allow you to intercept workflow and activity executions to add cross-cutting concerns like logging, metrics, tracing, and custom error handling. The Kotlin SDK provides suspend-function-aware interceptors that integrate naturally with coroutines. - -### KWorkerInterceptor - -The main entry point for interceptors. Registered with `KWorkerFactory` and called when workflows or activities are instantiated. - -```kotlin -/** - * Intercepts workflow and activity executions. - * - * Prefer extending [KWorkerInterceptorBase] and overriding only the methods you need. - */ -interface KWorkerInterceptor { - /** - * Called when a workflow is instantiated. May create a [KWorkflowInboundCallsInterceptor]. - * The returned interceptor must forward all calls to [next]. - */ - fun interceptWorkflow(next: KWorkflowInboundCallsInterceptor): KWorkflowInboundCallsInterceptor - - /** - * Called when an activity task is received. May create a [KActivityInboundCallsInterceptor]. - * The returned interceptor must forward all calls to [next]. - */ - fun interceptActivity(next: KActivityInboundCallsInterceptor): KActivityInboundCallsInterceptor -} - -/** - * Base implementation that passes through all calls. Extend this class and override only needed methods. - */ -open class KWorkerInterceptorBase : KWorkerInterceptor { - override fun interceptWorkflow(next: KWorkflowInboundCallsInterceptor) = next - override fun interceptActivity(next: KActivityInboundCallsInterceptor) = next -} -``` - -### KWorkflowInboundCallsInterceptor - -Intercepts inbound calls to workflow execution (workflow method, signals, queries, updates). - -```kotlin -/** - * Intercepts inbound calls to workflow execution. - * - * All methods except [handleQuery] are suspend functions, allowing coroutine-based interception. - * The [init] method receives the outbound interceptor for intercepting outgoing calls. - * - * Prefer extending [KWorkflowInboundCallsInterceptorBase] and overriding only needed methods. - */ -interface KWorkflowInboundCallsInterceptor { - - /** - * Called when the workflow is instantiated. Use this to wrap the outbound interceptor. - */ - suspend fun init(outboundCalls: KWorkflowOutboundCallsInterceptor) - - /** - * Called when the workflow main method is invoked. - */ - suspend fun execute(input: KWorkflowInput): KWorkflowOutput - - /** - * Called when a signal is delivered to the workflow. - */ - suspend fun handleSignal(input: KSignalInput) - - /** - * Called when a query is made to the workflow. - * Note: Queries must be synchronous and cannot modify workflow state. - */ - fun handleQuery(input: KQueryInput): KQueryOutput - - /** - * Called to validate an update before execution. - * Throw an exception to reject the update. - */ - fun validateUpdate(input: KUpdateInput) - - /** - * Called to execute an update after validation passes. - */ - suspend fun executeUpdate(input: KUpdateInput): KUpdateOutput -} - -/** - * Base implementation that forwards all calls to the next interceptor. - */ -open class KWorkflowInboundCallsInterceptorBase( - protected val next: KWorkflowInboundCallsInterceptor -) : KWorkflowInboundCallsInterceptor { - override suspend fun init(outboundCalls: KWorkflowOutboundCallsInterceptor) = next.init(outboundCalls) - override suspend fun execute(input: KWorkflowInput) = next.execute(input) - override suspend fun handleSignal(input: KSignalInput) = next.handleSignal(input) - override fun handleQuery(input: KQueryInput) = next.handleQuery(input) - override fun validateUpdate(input: KUpdateInput) = next.validateUpdate(input) - override suspend fun executeUpdate(input: KUpdateInput) = next.executeUpdate(input) -} -``` - -#### Workflow Inbound Input/Output Classes - -```kotlin -/** - * Input to workflow execution. - */ -data class KWorkflowInput( - val header: Header, - val arguments: Array -) - -/** - * Output from workflow execution. - */ -data class KWorkflowOutput( - val result: Any? -) - -/** - * Input to signal handler. - */ -data class KSignalInput( - val signalName: String, - val arguments: Array, - val eventId: Long, - val header: Header -) - -/** - * Input to query handler. - */ -data class KQueryInput( - val queryName: String, - val arguments: Array, - val header: Header -) - -/** - * Output from query handler. - */ -data class KQueryOutput( - val result: Any? -) - -/** - * Input to update handler (validation and execution). - */ -data class KUpdateInput( - val updateName: String, - val arguments: Array, - val header: Header -) - -/** - * Output from update execution. - */ -data class KUpdateOutput( - val result: Any? -) -``` - -### KWorkflowOutboundCallsInterceptor - -Intercepts outbound calls from workflow code to Temporal APIs (activities, child workflows, timers, etc.). - -```kotlin -/** - * Intercepts outbound calls from workflow code to Temporal APIs. - * - * All calls execute in workflow context and must follow determinism rules. - * - * Prefer extending [KWorkflowOutboundCallsInterceptorBase] and overriding only needed methods. - */ -interface KWorkflowOutboundCallsInterceptor { - - // --- Activities --- - - /** - * Intercepts activity execution. Returns a Deferred that completes when the activity completes. - */ - fun executeActivity(input: KActivityInvocationInput): Deferred - - /** - * Intercepts local activity execution. - */ - fun executeLocalActivity(input: KLocalActivityInvocationInput): Deferred - - // --- Child Workflows --- - - /** - * Intercepts child workflow execution. - */ - fun executeChildWorkflow(input: KChildWorkflowInvocationInput): KChildWorkflowInvocationOutput - - // --- Timers and Delays --- - - /** - * Intercepts delay/sleep calls. - */ - suspend fun delay(duration: Duration) - - /** - * Intercepts timer creation. - */ - fun newTimer(duration: Duration): Deferred - - // --- Await Conditions --- - - /** - * Intercepts await with timeout. - * @return true if condition was satisfied, false if timed out - */ - suspend fun awaitCondition(timeout: Duration, reason: String, condition: () -> Boolean): Boolean - - /** - * Intercepts await without timeout. - */ - suspend fun awaitCondition(reason: String, condition: () -> Boolean) - - // --- Side Effects --- - - /** - * Intercepts side effect execution. - */ - fun sideEffect(resultClass: Class, func: () -> R): R - - /** - * Intercepts mutable side effect execution. - */ - fun mutableSideEffect( - id: String, - resultClass: Class, - updated: (R?, R?) -> Boolean, - func: () -> R - ): R - - // --- Versioning --- - - /** - * Intercepts version check. - */ - fun getVersion(changeId: String, minSupported: Int, maxSupported: Int): Int - - // --- Continue As New --- - - /** - * Intercepts continue-as-new. - */ - fun continueAsNew(input: KContinueAsNewInput): Nothing - - // --- External Workflow Communication --- - - /** - * Intercepts signal to external workflow. - */ - fun signalExternalWorkflow(input: KSignalExternalInput): Deferred - - /** - * Intercepts cancel request to external workflow. - */ - fun cancelWorkflow(input: KCancelWorkflowInput): Deferred - - // --- Search Attributes and Memo --- - - /** - * Intercepts search attribute updates. - */ - fun upsertTypedSearchAttributes(vararg updates: SearchAttributeUpdate<*>) - - /** - * Intercepts memo updates. - */ - fun upsertMemo(memo: Map) - - // --- Utilities --- - - /** - * Intercepts random number generation. - */ - fun newRandom(): Random - - /** - * Intercepts UUID generation. - */ - fun randomUUID(): UUID - - /** - * Intercepts current time access. - */ - fun currentTimeMillis(): Long -} - -/** - * Base implementation that forwards all calls to the next interceptor. - */ -open class KWorkflowOutboundCallsInterceptorBase( - protected val next: KWorkflowOutboundCallsInterceptor -) : KWorkflowOutboundCallsInterceptor { - override fun executeActivity(input: KActivityInvocationInput) = next.executeActivity(input) - override fun executeLocalActivity(input: KLocalActivityInvocationInput) = next.executeLocalActivity(input) - override fun executeChildWorkflow(input: KChildWorkflowInvocationInput) = next.executeChildWorkflow(input) - override suspend fun delay(duration: Duration) = next.delay(duration) - override fun newTimer(duration: Duration) = next.newTimer(duration) - override suspend fun awaitCondition(timeout: Duration, reason: String, condition: () -> Boolean) = - next.awaitCondition(timeout, reason, condition) - override suspend fun awaitCondition(reason: String, condition: () -> Boolean) = - next.awaitCondition(reason, condition) - override fun sideEffect(resultClass: Class, func: () -> R) = next.sideEffect(resultClass, func) - override fun mutableSideEffect(id: String, resultClass: Class, updated: (R?, R?) -> Boolean, func: () -> R) = - next.mutableSideEffect(id, resultClass, updated, func) - override fun getVersion(changeId: String, minSupported: Int, maxSupported: Int) = - next.getVersion(changeId, minSupported, maxSupported) - override fun continueAsNew(input: KContinueAsNewInput) = next.continueAsNew(input) - override fun signalExternalWorkflow(input: KSignalExternalInput) = next.signalExternalWorkflow(input) - override fun cancelWorkflow(input: KCancelWorkflowInput) = next.cancelWorkflow(input) - override fun upsertTypedSearchAttributes(vararg updates: SearchAttributeUpdate<*>) = - next.upsertTypedSearchAttributes(*updates) - override fun upsertMemo(memo: Map) = next.upsertMemo(memo) - override fun newRandom() = next.newRandom() - override fun randomUUID() = next.randomUUID() - override fun currentTimeMillis() = next.currentTimeMillis() -} -``` - -#### Workflow Outbound Input/Output Classes - -```kotlin -/** - * Input for activity invocation. - */ -data class KActivityInvocationInput( - val activityName: String, - val resultClass: Class, - val resultType: Type, - val arguments: Array, - val options: KActivityOptions, - val header: Header -) - -/** - * Input for local activity invocation. - */ -data class KLocalActivityInvocationInput( - val activityName: String, - val resultClass: Class, - val resultType: Type, - val arguments: Array, - val options: KLocalActivityOptions, - val header: Header -) - -/** - * Input for child workflow invocation. - */ -data class KChildWorkflowInvocationInput( - val workflowId: String, - val workflowType: String, - val resultClass: Class, - val resultType: Type, - val arguments: Array, - val options: KChildWorkflowOptions, - val header: Header -) - -/** - * Output from child workflow start. - */ -data class KChildWorkflowInvocationOutput( - val result: Deferred, - val workflowExecution: Deferred -) - -/** - * Input for continue-as-new. - */ -data class KContinueAsNewInput( - val workflowType: String?, - val options: KContinueAsNewOptions?, - val arguments: Array, - val header: Header -) - -/** - * Input for signaling external workflow. - */ -data class KSignalExternalInput( - val execution: WorkflowExecution, - val signalName: String, - val arguments: Array, - val header: Header -) - -/** - * Input for canceling external workflow. - */ -data class KCancelWorkflowInput( - val execution: WorkflowExecution, - val reason: String? -) -``` - -### KActivityInboundCallsInterceptor - -Intercepts inbound calls to activity execution. - -```kotlin -/** - * Intercepts inbound calls to activity execution. - * - * The [execute] method is a suspend function, supporting both regular and suspend activities. - * - * Prefer extending [KActivityInboundCallsInterceptorBase] and overriding only needed methods. - */ -interface KActivityInboundCallsInterceptor { - - /** - * Called when activity is initialized. Provides access to the activity execution context. - */ - fun init(context: ActivityExecutionContext) - - /** - * Called when activity method is invoked. - * This is a suspend function to support suspend activities. - */ - suspend fun execute(input: KActivityExecutionInput): KActivityExecutionOutput -} - -/** - * Base implementation that forwards all calls to the next interceptor. - */ -open class KActivityInboundCallsInterceptorBase( - protected val next: KActivityInboundCallsInterceptor -) : KActivityInboundCallsInterceptor { - override fun init(context: ActivityExecutionContext) = next.init(context) - override suspend fun execute(input: KActivityExecutionInput) = next.execute(input) -} - -/** - * Input to activity execution. - */ -data class KActivityExecutionInput( - val header: Header, - val arguments: Array -) - -/** - * Output from activity execution. - */ -data class KActivityExecutionOutput( - val result: Any? -) -``` - -### Registering Interceptors - -Interceptors are registered via `KWorkerFactory`: - -```kotlin -val factory = KWorkerFactory(client) { - workerInterceptors = listOf( - LoggingInterceptor(), - MetricsInterceptor(), - TracingInterceptor() - ) -} - -val worker = factory.newWorker("task-queue") -worker.registerWorkflowImplementationTypes() -worker.registerActivitiesImplementations(MyActivitiesImpl()) - -factory.start() -``` - -### Example: Logging Interceptor - -```kotlin -class LoggingInterceptor : KWorkerInterceptorBase() { - - override fun interceptWorkflow( - next: KWorkflowInboundCallsInterceptor - ): KWorkflowInboundCallsInterceptor { - return LoggingWorkflowInterceptor(next) - } - - override fun interceptActivity( - next: KActivityInboundCallsInterceptor - ): KActivityInboundCallsInterceptor { - return LoggingActivityInterceptor(next) - } -} - -private class LoggingWorkflowInterceptor( - next: KWorkflowInboundCallsInterceptor -) : KWorkflowInboundCallsInterceptorBase(next) { - - private val log = KWorkflow.logger() - - override suspend fun execute(input: KWorkflowInput): KWorkflowOutput { - log.info("Workflow started with ${input.arguments.size} arguments") - return try { - next.execute(input) - } catch (e: Exception) { - log.error("Workflow failed", e) - throw e - } finally { - log.info("Workflow completed") - } - } - - override suspend fun handleSignal(input: KSignalInput) { - log.info("Signal received: ${input.signalName}") - next.handleSignal(input) - } - - override fun handleQuery(input: KQueryInput): KQueryOutput { - log.debug("Query received: ${input.queryName}") - return next.handleQuery(input) - } -} - -private class LoggingActivityInterceptor( - next: KActivityInboundCallsInterceptor -) : KActivityInboundCallsInterceptorBase(next) { - - override suspend fun execute(input: KActivityExecutionInput): KActivityExecutionOutput { - val info = KActivity.getInfo() - val log = KActivity.logger() - - log.info("Activity ${info.activityType} started") - val startTime = System.currentTimeMillis() - - return try { - next.execute(input) - } finally { - val duration = System.currentTimeMillis() - startTime - log.info("Activity ${info.activityType} completed in ${duration}ms") - } - } -} -``` - -### Example: Tracing Interceptor with OpenTelemetry - -```kotlin -class TracingInterceptor( - private val tracer: Tracer -) : KWorkerInterceptorBase() { - - override fun interceptWorkflow( - next: KWorkflowInboundCallsInterceptor - ): KWorkflowInboundCallsInterceptor { - return TracingWorkflowInboundInterceptor(next, tracer) - } - - override fun interceptActivity( - next: KActivityInboundCallsInterceptor - ): KActivityInboundCallsInterceptor { - return TracingActivityInterceptor(next, tracer) - } -} - -private class TracingWorkflowInboundInterceptor( - next: KWorkflowInboundCallsInterceptor, - private val tracer: Tracer -) : KWorkflowInboundCallsInterceptorBase(next) { - - private lateinit var outboundInterceptor: TracingWorkflowOutboundInterceptor - - override suspend fun init(outboundCalls: KWorkflowOutboundCallsInterceptor) { - // Wrap the outbound interceptor to trace outgoing calls - outboundInterceptor = TracingWorkflowOutboundInterceptor(outboundCalls, tracer) - next.init(outboundInterceptor) - } - - override suspend fun execute(input: KWorkflowInput): KWorkflowOutput { - // Extract trace context from header - val parentContext = extractContext(input.header) - - val span = tracer.spanBuilder("workflow.execute") - .setParent(parentContext) - .setAttribute("workflow.type", KWorkflow.getInfo().workflowType) - .startSpan() - - return try { - withContext(span.asContextElement()) { - next.execute(input) - } - } catch (e: Exception) { - span.recordException(e) - span.setStatus(StatusCode.ERROR) - throw e - } finally { - span.end() - } - } -} - -private class TracingWorkflowOutboundInterceptor( - next: KWorkflowOutboundCallsInterceptor, - private val tracer: Tracer -) : KWorkflowOutboundCallsInterceptorBase(next) { - - override fun executeActivity(input: KActivityInvocationInput): Deferred { - val span = tracer.spanBuilder("activity.schedule") - .setAttribute("activity.name", input.activityName) - .startSpan() - - // Inject trace context into header - val headerWithTrace = injectContext(input.header, span.context) - val inputWithTrace = input.copy(header = headerWithTrace) - - span.end() - return next.executeActivity(inputWithTrace) - } - - override fun executeChildWorkflow( - input: KChildWorkflowInvocationInput - ): KChildWorkflowInvocationOutput { - val span = tracer.spanBuilder("child_workflow.start") - .setAttribute("workflow.type", input.workflowType) - .setAttribute("workflow.id", input.workflowId) - .startSpan() - - val headerWithTrace = injectContext(input.header, span.context) - val inputWithTrace = input.copy(header = headerWithTrace) - - span.end() - return next.executeChildWorkflow(inputWithTrace) - } -} -``` - -### Example: Metrics Interceptor - -```kotlin -class MetricsInterceptor( - private val meterProvider: MeterProvider -) : KWorkerInterceptorBase() { - - private val meter = meterProvider.get("temporal.sdk") - private val workflowCounter = meter.counterBuilder("workflow.executions").build() - private val activityCounter = meter.counterBuilder("activity.executions").build() - private val activityDuration = meter.histogramBuilder("activity.duration").build() - - override fun interceptWorkflow( - next: KWorkflowInboundCallsInterceptor - ): KWorkflowInboundCallsInterceptor { - return object : KWorkflowInboundCallsInterceptorBase(next) { - override suspend fun execute(input: KWorkflowInput): KWorkflowOutput { - val workflowType = KWorkflow.getInfo().workflowType - workflowCounter.add(1, Attributes.of( - AttributeKey.stringKey("workflow.type"), workflowType - )) - return next.execute(input) - } - } - } - - override fun interceptActivity( - next: KActivityInboundCallsInterceptor - ): KActivityInboundCallsInterceptor { - return object : KActivityInboundCallsInterceptorBase(next) { - override suspend fun execute(input: KActivityExecutionInput): KActivityExecutionOutput { - val info = KActivity.getInfo() - val startTime = System.nanoTime() - - return try { - val result = next.execute(input) - activityCounter.add(1, Attributes.of( - AttributeKey.stringKey("activity.type"), info.activityType, - AttributeKey.stringKey("status"), "success" - )) - result - } catch (e: Exception) { - activityCounter.add(1, Attributes.of( - AttributeKey.stringKey("activity.type"), info.activityType, - AttributeKey.stringKey("status"), "failure" - )) - throw e - } finally { - val durationMs = (System.nanoTime() - startTime) / 1_000_000.0 - activityDuration.record(durationMs, Attributes.of( - AttributeKey.stringKey("activity.type"), info.activityType - )) - } - } - } - } -} -``` - -### Example: Authentication/Authorization Interceptor - -```kotlin -class AuthInterceptor( - private val authService: AuthService -) : KWorkerInterceptorBase() { - - override fun interceptWorkflow( - next: KWorkflowInboundCallsInterceptor - ): KWorkflowInboundCallsInterceptor { - return object : KWorkflowInboundCallsInterceptorBase(next) { - - override suspend fun handleSignal(input: KSignalInput) { - // Validate authorization from header before processing signal - val authToken = input.header["authorization"]?.firstOrNull() - if (authToken != null && !authService.isAuthorized(authToken, "signal:${input.signalName}")) { - throw IllegalAccessException("Unauthorized signal: ${input.signalName}") - } - next.handleSignal(input) - } - - override suspend fun executeUpdate(input: KUpdateInput): KUpdateOutput { - // Validate authorization for updates - val authToken = input.header["authorization"]?.firstOrNull() - if (authToken != null && !authService.isAuthorized(authToken, "update:${input.updateName}")) { - throw IllegalAccessException("Unauthorized update: ${input.updateName}") - } - return next.executeUpdate(input) - } - } - } -} - -## Migration from Java SDK - -### API Mapping - -| Java SDK | Kotlin SDK | -|----------|------------| -| **Activities** | | -| `Workflow.newUntypedActivityStub(opts)` | *(not needed - options passed per call)* | -| `activities.execute("name", Cls, arg)` | `KWorkflow.executeActivity("name", options, arg)` | -| `stub.method(arg)` (typed activity) | `KWorkflow.executeActivity(Interface::method, options, arg)` | -| `Async.function(stub::method, arg)` | `coroutineScope { async { KWorkflow.executeActivity(...) } }` | -| **Workflows** | | -| `Workflow.newChildWorkflowStub(...)` | `KWorkflow.executeChildWorkflow(Interface::method, options, ...)` | -| `Async.function(childStub::method, arg)` | `KWorkflow.startChildWorkflow(Interface::method, options, ...)` → `KChildWorkflowHandle` | -| `Workflow.getWorkflowExecution(childStub)` | `childHandle.workflowId` / `childHandle.firstExecutionRunId` | -| `client.newWorkflowStub(...)` | `client.startWorkflow(Interface::method, ...)` → `KTypedWorkflowHandle` | -| `client.newWorkflowStub(Cls, id)` | `client.getWorkflowHandle(id)` → `KWorkflowHandle` | -| `stub.signal(arg)` | `handle.signal(T::method, arg)` | -| `stub.query()` | `handle.query(T::method)` | -| `handle.getResult()` | `handle.result()` (type inferred) or `handle.result()` | -| **Primitives** | | -| `Workflow.sleep(duration)` | `delay(duration)` - standard kotlinx.coroutines | -| `Workflow.await(() -> cond)` | `KWorkflow.awaitCondition { cond }` | -| `Promise` | Standard `Deferred` via `Promise.toDeferred()` | -| `Optional` | `T?` | -| `Duration.ofSeconds(30)` | `30.seconds` | -| **Parallel Execution** | | -| `Async.function(...)` + `Promise.get()` | `coroutineScope { async { ... } }` + `awaitAll()` | -| **Options** | | -| `ActivityOptions.newBuilder()...build()` | `KActivityOptions(...)` | -| `LocalActivityOptions.newBuilder()...build()` | `KLocalActivityOptions(...)` | -| `ChildWorkflowOptions.newBuilder()...build()` | `KChildWorkflowOptions(...)` | -| `WorkflowOptions.newBuilder()...build()` | `KWorkflowOptions(...)` | -| `RetryOptions.newBuilder()...build()` | `KRetryOptions(...)` | - -### Before (Java) - -```java -@WorkflowInterface -public interface GreetingWorkflow { - @WorkflowMethod - String getGreeting(String name); -} - -public class GreetingWorkflowImpl implements GreetingWorkflow { - @Override - public String getGreeting(String name) { - ActivityStub activities = Workflow.newUntypedActivityStub( - ActivityOptions.newBuilder() - .setStartToCloseTimeout(Duration.ofSeconds(30)) - .build() - ); - return activities.execute("greet", String.class, name); - } -} -``` - -### After (Kotlin) - -```kotlin -@WorkflowInterface -interface GreetingWorkflow { - @WorkflowMethod - suspend fun getGreeting(name: String): String -} - -class GreetingWorkflowImpl : GreetingWorkflow { - override suspend fun getGreeting(name: String): String { - return KWorkflow.executeActivity( - "greet", - KActivityOptions(startToCloseTimeout = 30.seconds), - name - ) - } -} -``` - -### Interoperability - -Kotlin workflows can call Java activities using direct method references: - -```kotlin -override suspend fun processOrder(order: Order): String { - // JavaActivities is a Java @ActivityInterface - no stub needed - return KWorkflow.executeActivity( - JavaActivities::process, - KActivityOptions(startToCloseTimeout = 30.seconds), - order - ) -} -``` - -Java workflows can be invoked from Kotlin clients: - -```kotlin -val result = client.executeWorkflow( - JavaWorkflowInterface::execute, - KWorkflowOptions(workflowId = "java-workflow", taskQueue = "java-queue"), - input -) -``` - -## Java SDK API Parity - -This section documents the intentional differences between Java and Kotlin SDK APIs, and identifies remaining gaps. - -### APIs Not Needed in Kotlin - -The following Java SDK APIs are **not needed** in the Kotlin SDK due to language differences: - -| Java SDK API | Reason Not Needed | -|--------------|-------------------| -| `Workflow.sleep(Duration)` | Use `kotlinx.coroutines.delay()` - intercepted by dispatcher | -| `Workflow.newTimer(Duration)` | Use `async { delay(duration) }` for racing timers | -| `Workflow.wrap(Exception)` | Kotlin has no checked exceptions - not needed | -| `Activity.wrap(Throwable)` | Kotlin has no checked exceptions - not needed | -| `Workflow.newCancellationScope(...)` | Use Kotlin's `coroutineScope { }` with structured concurrency | -| `Workflow.newDetachedCancellationScope(...)` | Use `supervisorScope { }` or launch in parent scope | -| `Workflow.newWorkflowLock()` | Not needed - cooperative coroutines don't have true concurrency | -| `Workflow.newWorkflowSemaphore(...)` | Use structured concurrency patterns (e.g., `chunked().map { async }.awaitAll()`) | -| `Workflow.newQueue(...)` / `newWorkflowQueue(...)` | Use `mutableListOf` + `awaitCondition` - no concurrent access in suspend model | -| `Workflow.newPromise()` / `newFailedPromise(...)` | Use `CompletableDeferred` from kotlinx.coroutines | -| `Workflow.setDefaultActivityOptions(...)` | Pass `KActivityOptions` per call, or define shared `val defaultOptions` | -| `Workflow.setActivityOptions(...)` | Pass options per call | -| `Workflow.applyActivityOptions(...)` | Pass options per call | -| `Workflow.setDefaultLocalActivityOptions(...)` | Pass `KLocalActivityOptions` per call | -| `Workflow.applyLocalActivityOptions(...)` | Pass options per call | - -### Cancellation Support - -Kotlin coroutine cancellation is fully integrated with Temporal workflow cancellation: - -- Workflow cancellation triggers `coroutineScope.cancel(CancellationException(...))` -- Cancellation propagates to all child coroutines (activities, timers, child workflows) -- Activities use `suspendCancellableCoroutine` with `invokeOnCancellation` to cancel via Temporal -- Use standard Kotlin patterns: `try/finally`, `use { }`, `invokeOnCompletion` - -### Implemented APIs (Java SDK Parity) - -The following Java SDK workflow APIs have Kotlin equivalents in `KWorkflow`: - -#### Search Attributes & Memo ✅ -| Java SDK API | Kotlin SDK | -|--------------|------------| -| `Workflow.getTypedSearchAttributes()` | ✅ `KWorkflow.getTypedSearchAttributes()` | -| `Workflow.upsertTypedSearchAttributes(...)` | ✅ `KWorkflow.upsertTypedSearchAttributes()` | -| `Workflow.getMemo(key, class)` | ✅ `KWorkflow.getMemo()` | -| `Workflow.upsertMemo(...)` | ✅ `KWorkflow.upsertMemo()` | - -#### Workflow State & Context ✅ -| Java SDK API | Kotlin SDK | -|--------------|------------| -| `Workflow.getLastCompletionResult(class)` | ✅ `KWorkflow.getLastCompletionResult()` | -| `Workflow.getPreviousRunFailure()` | ✅ `KWorkflow.getPreviousRunFailure()` | -| `Workflow.isReplaying()` | ✅ `KWorkflow.isReplaying()` | -| `Workflow.getCurrentUpdateInfo()` | ✅ `KWorkflow.getCurrentUpdateInfo()` | -| `Workflow.isEveryHandlerFinished()` | ✅ `KWorkflow.isEveryHandlerFinished()` | -| `Workflow.setCurrentDetails(...)` | ✅ `KWorkflow.setCurrentDetails()` | -| `Workflow.getCurrentDetails()` | ✅ `KWorkflow.getCurrentDetails()` | -| `Workflow.getMetricsScope()` | ✅ `KWorkflow.getMetricsScope()` | - -#### Side Effects & Utilities ✅ -| Java SDK API | Kotlin SDK | -|--------------|------------| -| `Workflow.sideEffect(...)` | ✅ `KWorkflow.sideEffect()` | -| `Workflow.mutableSideEffect(...)` | ✅ `KWorkflow.mutableSideEffect()` | -| `Workflow.getVersion(...)` | ✅ `KWorkflow.getVersion()` | -| `Workflow.retry(...)` | ✅ `KWorkflow.retry()` | -| `Workflow.randomUUID()` | ✅ `KWorkflow.randomUUID()` | -| `Workflow.newRandom()` | ✅ `KWorkflow.newRandom()` | - -#### Dynamic Handler Registration ✅ -| Java SDK API | Kotlin SDK | -|--------------|------------| -| `Workflow.registerListener(DynamicSignalHandler)` | ✅ `KWorkflow.registerDynamicSignalHandler()` | -| `Workflow.registerListener(DynamicQueryHandler)` | ✅ `KWorkflow.registerDynamicQueryHandler()` | -| `Workflow.registerListener(DynamicUpdateHandler)` | ✅ `KWorkflow.registerDynamicUpdateHandler()` | -| N/A (new in Kotlin SDK) | ✅ `KWorkflow.registerSignalHandler()` | -| N/A (new in Kotlin SDK) | ✅ `KWorkflow.registerQueryHandler()` | -| N/A (new in Kotlin SDK) | ✅ `KWorkflow.registerUpdateHandler()` | -| N/A (new in Kotlin SDK) | ✅ `KWorkflow.registerDynamicUpdateValidator()` | - -### Remaining Gaps - -| Java SDK API | Status | -|--------------|--------| -| `Workflow.newNexusServiceStub(...)` | Nexus support - deferred to separate project | -| `Workflow.startNexusOperation(...)` | Nexus support - deferred to separate project | -| `Workflow.getInstance()` | Advanced use case - low priority | - -### KActivityInfo Gaps - -| Java ActivityInfo Field | Status | -|------------------------|--------| -| `workflowType` | Missing - workflow type that called the activity | -| `currentAttemptScheduledTimestamp` | Missing - current attempt schedule time | -| `retryOptions` | Missing - activity retry options | - -## Complete Example - -This example uses Option A (pure Kotlin with suspend interfaces). - -```kotlin -// === Domain Types === - -@Serializable -data class Order( - val id: String, - val customerId: String, - val items: List, - val priority: Priority = Priority.NORMAL -) { - val total: BigDecimal get() = items.sumOf { it.price * it.quantity.toBigDecimal() } -} - -@Serializable -data class OrderItem( - val productId: String, - val quantity: Int, - val price: BigDecimal -) - -@Serializable -data class OrderResult(val success: Boolean, val trackingNumber: String?) - -enum class OrderStatus { PENDING, PROCESSING, SHIPPED, CANCELED } -enum class Priority { LOW, NORMAL, HIGH } - -// === Workflow Interface === - -@WorkflowInterface -interface OrderWorkflow { - @WorkflowMethod - suspend fun processOrder(order: Order): OrderResult - - @UpdateMethod - suspend fun addItem(item: OrderItem): Boolean - - @UpdateValidatorMethod(updateMethod = "addItem") - fun validateAddItem(item: OrderItem) - - @QueryMethod - val status: OrderStatus - - @QueryMethod - val progress: Int -} - -// === Activity Interface === - -@ActivityInterface -interface OrderActivities { - @ActivityMethod - suspend fun validateOrder(order: Order): Boolean - - @ActivityMethod - suspend fun reserveInventory(item: OrderItem): Boolean - - @ActivityMethod - suspend fun releaseInventory(item: OrderItem): Boolean - - @ActivityMethod - suspend fun chargePayment(order: Order): Boolean - - @ActivityMethod - suspend fun refundPayment(order: Order): Boolean - - @ActivityMethod - suspend fun shipOrder(order: Order): String - - @ActivityMethod - suspend fun notifyCustomer(customerId: String, message: String) -} - -// === Activity Implementation === - -class OrderActivitiesImpl( - private val inventoryService: InventoryService, - private val paymentService: PaymentService, - private val shippingService: ShippingService, - private val notificationService: NotificationService -) : OrderActivities { - - override suspend fun validateOrder(order: Order): Boolean { - return order.items.isNotEmpty() && order.items.all { it.quantity > 0 } - } - - override suspend fun reserveInventory(item: OrderItem): Boolean { - return inventoryService.reserve(item.productId, item.quantity) - } - - override suspend fun releaseInventory(item: OrderItem): Boolean { - return inventoryService.release(item.productId, item.quantity) - } - - override suspend fun chargePayment(order: Order): Boolean { - return paymentService.charge(order.customerId, order.total) - } - - override suspend fun refundPayment(order: Order): Boolean { - return paymentService.refund(order.customerId, order.total) - } - - override suspend fun shipOrder(order: Order): String { - return shippingService.createShipment(order) - } - - override suspend fun notifyCustomer(customerId: String, message: String) { - notificationService.send(customerId, message) - } -} - -// === Workflow Implementation === - -class OrderWorkflowImpl : OrderWorkflow { - private var _status = OrderStatus.PENDING - private var _progress = 0 - private var _order: Order? = null - private var paymentCharged = false - private var reservedItems = mutableListOf() - - override val status get() = _status - override val progress get() = _progress - - // Reusable options for common cases - private val defaultOptions = KActivityOptions( - startToCloseTimeout = 30.seconds, - retryOptions = KRetryOptions( - initialInterval = 1.seconds, - maximumAttempts = 3 - ) - ) - - override suspend fun processOrder(order: Order): OrderResult { - _order = order - _status = OrderStatus.PROCESSING - - return try { - doProcessOrder(order) - } catch (e: CancellationException) { - // Workflow was cancelled - run cleanup in detached scope - // This code runs even though the workflow is cancelled - withContext(NonCancellable) { - cleanup(order) - } - _status = OrderStatus.CANCELED - throw e // Re-throw to complete workflow as cancelled - } - } - - private suspend fun doProcessOrder(order: Order): OrderResult = coroutineScope { - // Validate order - direct method reference, no stub needed - _progress = 10 - val isValid = KWorkflow.executeActivity( - OrderActivities::validateOrder, - KActivityOptions(startToCloseTimeout = 10.seconds), - order - ) - if (!isValid) { - return@coroutineScope OrderResult(success = false, trackingNumber = null) - } - - // Reserve inventory for all items in parallel using standard async - // If workflow is cancelled here, all parallel activities are cancelled - _progress = 30 - order.items.map { item -> - async { - val reserved = KWorkflow.executeActivity( - OrderActivities::reserveInventory, - defaultOptions, - item - ) - if (reserved) { - reservedItems.add(item) // Track for cleanup - } - reserved - } - }.awaitAll() // Standard kotlinx.coroutines.awaitAll - - _progress = 60 - - // Charge payment with timeout - auto-cancels if takes too long - val charged = withTimeout(2.minutes) { - KWorkflow.executeActivity( - OrderActivities::chargePayment, - KActivityOptions( - startToCloseTimeout = 2.minutes, - retryOptions = KRetryOptions( - initialInterval = 5.seconds, - maximumAttempts = 5 - ) - ), - order - ) - } - if (!charged) { - return@coroutineScope OrderResult(success = false, trackingNumber = null) - } - paymentCharged = true - - _progress = 80 - - // Ship order - val trackingNumber = KWorkflow.executeActivity( - OrderActivities::shipOrder, - defaultOptions, - order - ) - - _status = OrderStatus.SHIPPED - _progress = 100 - - OrderResult(success = true, trackingNumber = trackingNumber) - } - - /** - * Cleanup logic that runs even when workflow is cancelled. - * Called from within withContext(NonCancellable) block. - */ - private suspend fun cleanup(order: Order) { - // Release any reserved inventory - for (item in reservedItems) { - try { - KWorkflow.executeActivity( - OrderActivities::releaseInventory, - defaultOptions, - item - ) - } catch (e: Exception) { - // Log but continue cleanup - } - } - - // Refund payment if it was charged - if (paymentCharged) { - try { - KWorkflow.executeActivity( - OrderActivities::refundPayment, - defaultOptions, - order - ) - } catch (e: Exception) { - // Log but continue cleanup - } - } - - // Notify customer of cancellation - try { - KWorkflow.executeActivity( - OrderActivities::notifyCustomer, - defaultOptions, - order.customerId, "Your order has been cancelled" - ) - } catch (e: Exception) { - // Best effort notification - } - } - - override fun validateAddItem(item: OrderItem) { - require(item.quantity > 0) { "Quantity must be positive" } - require(item.price >= BigDecimal.ZERO) { "Price cannot be negative" } - require(_status == OrderStatus.PENDING) { "Cannot add items after processing started" } - } - - override suspend fun addItem(item: OrderItem): Boolean { - // Validator already checked status, so this is safe - // Update would modify order items here - return true - } -} - -// === Worker Setup === - -fun main() = runBlocking { - // Initialize services (could use DI framework) - val inventoryService = InventoryServiceImpl() - val paymentService = PaymentServiceImpl() - val shippingService = ShippingServiceImpl() - val notificationService = NotificationServiceImpl() - - val service = WorkflowServiceStubs.newLocalServiceStubs() - - // Create KWorkflowClient for Kotlin-specific APIs - val client = KWorkflowClient(service) - - // KWorkerFactory automatically enables Kotlin coroutine support - val factory = KWorkerFactory(client) - val worker: KWorker = factory.newWorker("orders") - - // Plugin handles suspend functions automatically - worker.registerWorkflowImplementationTypes(OrderWorkflowImpl::class) - worker.registerActivitiesImplementations( - OrderActivitiesImpl(inventoryService, paymentService, shippingService, notificationService) - ) - - factory.start() - - // === Client Usage === - - val order = Order( - id = "12345", - customerId = "cust-789", - items = listOf( - OrderItem("prod-1", 2, 29.99.toBigDecimal()), - OrderItem("prod-2", 1, 49.99.toBigDecimal()) - ) - ) - - val workflowId = "order-${UUID.randomUUID()}" - - // Start workflow and get handle - val handle = client.startWorkflow( - OrderWorkflow::processOrder, - KWorkflowOptions( - workflowId = workflowId, - taskQueue = "orders" - ), - order - ) - println("Started workflow: ${handle.workflowId}") - - // Query workflow state using typed handle - val status = handle.query(OrderWorkflow::status) - val progress = handle.query(OrderWorkflow::progress) - println("Status: $status, Progress: $progress%") - - // Or get handle for existing workflow by ID - val existingHandle = client.getWorkflowHandle(workflowId) - - // Send update and wait for result - val newItem = OrderItem("prod-3", 1, 19.99.toBigDecimal()) - val added = existingHandle.executeUpdate(OrderWorkflow::addItem, newItem) - println("Item added: $added") - - // === Cancellation === - - // Cancel workflow - triggers CancellationException, cleanup block runs - // existingHandle.cancel() - - // Or terminate immediately - no cleanup runs - // existingHandle.terminate("Emergency shutdown") - - // Wait for result - type inferred from startWorkflow method reference - // If workflow was cancelled, this throws WorkflowFailedException - try { - val result = handle.result() - println("Order result: $result") - } catch (e: WorkflowFailedException) { - if (e.cause is CanceledFailure) { - println("Order was cancelled - cleanup activities were executed") - } else { - throw e - } - } -} -``` diff --git a/kotlin/worker/README.md b/kotlin/worker/README.md new file mode 100644 index 0000000..abff341 --- /dev/null +++ b/kotlin/worker/README.md @@ -0,0 +1,46 @@ +# Worker API + +This section covers setting up workers to execute Temporal workflows and activities in Kotlin. + +## Overview + +The Kotlin SDK provides `KWorkerFactory` and `KWorker` which automatically enable coroutine support for Kotlin workflows and suspend activities. + +## Documents + +| Document | Description | +|----------|-------------| +| [Setup](./setup.md) | KWorkerFactory, KWorker, KotlinPlugin, registration | + +## Quick Reference + +### Basic Worker Setup + +```kotlin +val service = WorkflowServiceStubs.newLocalServiceStubs() +val client = KWorkflowClient(service) + +// KWorkerFactory automatically enables Kotlin coroutine support +val factory = KWorkerFactory(client) +val worker = factory.newWorker("task-queue") + +// Register workflows and activities +worker.registerWorkflowImplementationTypes(MyWorkflowImpl::class) +worker.registerActivitiesImplementations(MyActivitiesImpl()) + +// Start the worker +factory.start() +``` + +### Key Components + +| Component | Purpose | +|-----------|---------| +| `KWorkerFactory` | Creates workers with automatic coroutine support | +| `KWorker` | Registers Kotlin workflows and suspend activities | +| `KotlinPlugin` | Plugin for Java main apps (used automatically by KWorkerFactory) | + +## Next Steps + +- See [Worker Setup](./setup.md) for detailed configuration options +- Learn about [Interceptors](../configuration/interceptors.md) for cross-cutting concerns diff --git a/kotlin/worker/setup.md b/kotlin/worker/setup.md new file mode 100644 index 0000000..31b6fe4 --- /dev/null +++ b/kotlin/worker/setup.md @@ -0,0 +1,173 @@ +# Worker Setup + +## KWorkerFactory (Recommended) + +For pure Kotlin applications, use `KWorkerFactory` which automatically enables coroutine support: + +```kotlin +val service = WorkflowServiceStubs.newLocalServiceStubs() +val client = KWorkflowClient(service) { ... } + +// KWorkerFactory automatically enables Kotlin coroutine support +val factory = KWorkerFactory(client) { + maxWorkflowThreadCount = 800 +} + +val worker: KWorker = factory.newWorker("task-queue") { + maxConcurrentActivityExecutionSize = 100 +} + +// Register Kotlin coroutine workflows +worker.registerWorkflowImplementationTypes( + GreetingWorkflowImpl::class, + OrderWorkflowImpl::class +) + +// Register activities - suspend functions handled automatically +worker.registerActivitiesImplementations( + GreetingActivitiesImpl(), // Kotlin suspend activities + JavaActivitiesImpl() // Java activities work too +) + +// Start the worker +factory.start() +``` + +## KWorkerFactory API + +```kotlin +/** + * Kotlin worker factory that automatically enables coroutine support. + * Wraps WorkerFactory with KotlinPlugin pre-configured. + */ +class KWorkerFactory( + client: KWorkflowClient, + options: WorkerFactoryOptions.Builder.() -> Unit = {} +) { + /** The underlying WorkerFactory for advanced use cases */ + val workerFactory: WorkerFactory + + fun newWorker(taskQueue: String, options: WorkerOptions.Builder.() -> Unit = {}): KWorker + fun start() + fun shutdown() + fun shutdownNow() + suspend fun awaitTermination(timeout: Duration) +} +``` + +## KWorker API + +```kotlin +/** + * Kotlin worker that provides idiomatic APIs for registering + * Kotlin workflows and suspend activities. + * + * Use KWorker for pure Kotlin implementations. For mixed Java/Kotlin + * scenarios, access the underlying Worker via the [worker] property. + */ +class KWorker { + /** The underlying Java Worker for interop scenarios */ + val worker: Worker + + /** Register Kotlin workflow implementation types using reified generics */ + inline fun registerWorkflowImplementationTypes() + + /** Register Kotlin workflow implementation types using KClass */ + fun registerWorkflowImplementationTypes(vararg workflowClasses: KClass<*>) + + /** Register Kotlin workflow implementation types with options */ + fun registerWorkflowImplementationTypes( + options: WorkflowImplementationOptions, + vararg workflowClasses: KClass<*> + ) + + /** Register activity implementations (automatically detects suspend functions) */ + fun registerActivitiesImplementations(vararg activities: Any) + + /** Register suspend activity implementations explicitly */ + fun registerSuspendActivities(vararg activities: Any) + + /** Register Nexus service implementations */ + fun registerNexusServiceImplementations(vararg services: Any) +} +``` + +**When to use KWorker vs Worker:** +- Use `KWorker` for pure Kotlin implementations (recommended) +- Use `Worker` (via `kworker.worker`) when mixing Java and Kotlin workflows/activities on the same worker + +## KotlinPlugin (For Java Main) + +When your main application is written in Java and you need to register Kotlin workflows, use `KotlinPlugin` explicitly: + +```kotlin +// Java main or mixed Java/Kotlin setup +val service = WorkflowServiceStubs.newLocalServiceStubs() +val client = WorkflowClient.newInstance(service) + +val factory = WorkerFactory.newInstance(client, WorkerFactoryOptions.newBuilder() + .addPlugin(KotlinPlugin()) + .build()) + +val worker = factory.newWorker("task-queue") + +// Register Kotlin workflows - plugin handles suspend functions +worker.registerWorkflowImplementationTypes(KotlinWorkflowImpl::class.java) +``` + +## Mixed Java and Kotlin + +A single worker supports both Java and Kotlin workflows on the same task queue: + +```kotlin +// Java workflows (thread-based) +worker.registerWorkflowImplementationTypes( + OrderWorkflowJavaImpl::class.java +) + +// Kotlin workflows (coroutine-based) - same method, plugin handles execution +worker.registerWorkflowImplementationTypes( + GreetingWorkflowImpl::class +) + +// Both run on the same worker - execution model is per-workflow-instance +factory.start() +``` + +## Complete Worker Example + +```kotlin +fun main() = runBlocking { + // Initialize services (could use DI framework) + val inventoryService = InventoryServiceImpl() + val paymentService = PaymentServiceImpl() + val shippingService = ShippingServiceImpl() + val notificationService = NotificationServiceImpl() + + val service = WorkflowServiceStubs.newLocalServiceStubs() + + // Create KWorkflowClient for Kotlin-specific APIs + val client = KWorkflowClient(service) + + // KWorkerFactory automatically enables Kotlin coroutine support + val factory = KWorkerFactory(client) + val worker: KWorker = factory.newWorker("orders") + + // Plugin handles suspend functions automatically + worker.registerWorkflowImplementationTypes(OrderWorkflowImpl::class) + worker.registerActivitiesImplementations( + OrderActivitiesImpl(inventoryService, paymentService, shippingService, notificationService) + ) + + factory.start() + + // Keep running until shutdown signal + // ... +} +``` + +## Next Steps + +- [Interceptors](../configuration/interceptors.md) - Adding cross-cutting concerns +- [Workflows](../workflows/) - Defining workflows to register +- [Activities](../activities/) - Defining activities to register diff --git a/kotlin/workflows/README.md b/kotlin/workflows/README.md new file mode 100644 index 0000000..9d4a768 --- /dev/null +++ b/kotlin/workflows/README.md @@ -0,0 +1,282 @@ +# Workflows + +This section covers defining and implementing Temporal workflows in Kotlin. + +## Overview + +Kotlin workflows use coroutines and suspend functions for an idiomatic async experience while maintaining Temporal's determinism guarantees. + +## Documents + +| Document | Description | +|----------|-------------| +| [Definition](./definition.md) | Workflow interfaces, suspend methods, Java interop patterns | +| [Signals, Queries & Updates](./signals-queries.md) | Communication with running workflows | +| [Child Workflows](./child-workflows.md) | Orchestrating child workflow execution | +| [Timers & Parallel Execution](./timers-parallel.md) | Delays, async patterns, await conditions | +| [Cancellation](./cancellation.md) | Handling cancellation, cleanup with NonCancellable | +| [Continue-As-New](./continue-as-new.md) | Long-running workflow patterns | + +## Quick Reference + +### Basic Workflow + +```kotlin +@WorkflowInterface +interface GreetingWorkflow { + @WorkflowMethod + suspend fun getGreeting(name: String): String +} + +class GreetingWorkflowImpl : GreetingWorkflow { + override suspend fun getGreeting(name: String): String { + return KWorkflow.executeActivity( + GreetingActivities::composeGreeting, + KActivityOptions(startToCloseTimeout = 10.seconds), + "Hello", name + ) + } +} +``` + +### Key Patterns + +| Pattern | Kotlin SDK | +|---------|------------| +| Execute activity | `KWorkflow.executeActivity(Interface::method, options, args)` | +| Execute child workflow | `KWorkflow.executeChildWorkflow(Interface::method, options, args)` | +| Timer/delay | `delay(duration)` - standard kotlinx.coroutines | +| Wait for condition | `KWorkflow.awaitCondition { condition }` | +| Parallel execution | `coroutineScope { async { ... } }.awaitAll()` | +| Cancellation cleanup | `withContext(NonCancellable) { ... }` | + +## Complete Example + +This example demonstrates a full order processing workflow with activities, cancellation handling, updates, and queries. + +```kotlin +// === Domain Types === + +@Serializable +data class Order( + val id: String, + val customerId: String, + val items: List, + val priority: Priority = Priority.NORMAL +) { + val total: BigDecimal get() = items.sumOf { it.price * it.quantity.toBigDecimal() } +} + +@Serializable +data class OrderItem( + val productId: String, + val quantity: Int, + val price: BigDecimal +) + +@Serializable +data class OrderResult(val success: Boolean, val trackingNumber: String?) + +enum class OrderStatus { PENDING, PROCESSING, SHIPPED, CANCELED } +enum class Priority { LOW, NORMAL, HIGH } + +// === Workflow Interface === + +@WorkflowInterface +interface OrderWorkflow { + @WorkflowMethod + suspend fun processOrder(order: Order): OrderResult + + @UpdateMethod + suspend fun addItem(item: OrderItem): Boolean + + @UpdateValidatorMethod(updateMethod = "addItem") + fun validateAddItem(item: OrderItem) + + @QueryMethod + val status: OrderStatus + + @QueryMethod + val progress: Int +} + +// === Activity Interface === + +@ActivityInterface +interface OrderActivities { + @ActivityMethod + suspend fun validateOrder(order: Order): Boolean + + @ActivityMethod + suspend fun reserveInventory(item: OrderItem): Boolean + + @ActivityMethod + suspend fun releaseInventory(item: OrderItem): Boolean + + @ActivityMethod + suspend fun chargePayment(order: Order): Boolean + + @ActivityMethod + suspend fun refundPayment(order: Order): Boolean + + @ActivityMethod + suspend fun shipOrder(order: Order): String + + @ActivityMethod + suspend fun notifyCustomer(customerId: String, message: String) +} + +// === Workflow Implementation === + +class OrderWorkflowImpl : OrderWorkflow { + private var _status = OrderStatus.PENDING + private var _progress = 0 + private var _order: Order? = null + private var paymentCharged = false + private var reservedItems = mutableListOf() + + override val status get() = _status + override val progress get() = _progress + + // Reusable options for common cases + private val defaultOptions = KActivityOptions( + startToCloseTimeout = 30.seconds, + retryOptions = KRetryOptions( + initialInterval = 1.seconds, + maximumAttempts = 3 + ) + ) + + override suspend fun processOrder(order: Order): OrderResult { + _order = order + _status = OrderStatus.PROCESSING + + return try { + doProcessOrder(order) + } catch (e: CancellationException) { + // Workflow was cancelled - run cleanup in detached scope + withContext(NonCancellable) { + cleanup(order) + } + _status = OrderStatus.CANCELED + throw e + } + } + + private suspend fun doProcessOrder(order: Order): OrderResult = coroutineScope { + // Validate order + _progress = 10 + val isValid = KWorkflow.executeActivity( + OrderActivities::validateOrder, + KActivityOptions(startToCloseTimeout = 10.seconds), + order + ) + if (!isValid) { + return@coroutineScope OrderResult(success = false, trackingNumber = null) + } + + // Reserve inventory for all items in parallel + _progress = 30 + order.items.map { item -> + async { + val reserved = KWorkflow.executeActivity( + OrderActivities::reserveInventory, + defaultOptions, + item + ) + if (reserved) { + reservedItems.add(item) + } + reserved + } + }.awaitAll() + + _progress = 60 + + // Charge payment with timeout + val charged = withTimeout(2.minutes) { + KWorkflow.executeActivity( + OrderActivities::chargePayment, + KActivityOptions( + startToCloseTimeout = 2.minutes, + retryOptions = KRetryOptions(maximumAttempts = 5) + ), + order + ) + } + if (!charged) { + return@coroutineScope OrderResult(success = false, trackingNumber = null) + } + paymentCharged = true + + _progress = 80 + + // Ship order + val trackingNumber = KWorkflow.executeActivity( + OrderActivities::shipOrder, + defaultOptions, + order + ) + + _status = OrderStatus.SHIPPED + _progress = 100 + + OrderResult(success = true, trackingNumber = trackingNumber) + } + + private suspend fun cleanup(order: Order) { + // Release any reserved inventory + for (item in reservedItems) { + try { + KWorkflow.executeActivity( + OrderActivities::releaseInventory, + defaultOptions, + item + ) + } catch (e: Exception) { + // Log but continue cleanup + } + } + + // Refund payment if it was charged + if (paymentCharged) { + try { + KWorkflow.executeActivity( + OrderActivities::refundPayment, + defaultOptions, + order + ) + } catch (e: Exception) { + // Log but continue cleanup + } + } + + // Notify customer of cancellation + try { + KWorkflow.executeActivity( + OrderActivities::notifyCustomer, + defaultOptions, + order.customerId, "Your order has been cancelled" + ) + } catch (e: Exception) { + // Best effort notification + } + } + + override fun validateAddItem(item: OrderItem) { + require(item.quantity > 0) { "Quantity must be positive" } + require(item.price >= BigDecimal.ZERO) { "Price cannot be negative" } + require(_status == OrderStatus.PENDING) { "Cannot add items after processing started" } + } + + override suspend fun addItem(item: OrderItem): Boolean { + return true + } +} +``` + +## Next Steps + +- Start with [Workflow Definition](./definition.md) to understand the basic patterns +- Learn about [Activities](../activities/) that workflows orchestrate +- See [Client](../client/) for starting and interacting with workflows diff --git a/kotlin/workflows/cancellation.md b/kotlin/workflows/cancellation.md new file mode 100644 index 0000000..93fe2fb --- /dev/null +++ b/kotlin/workflows/cancellation.md @@ -0,0 +1,147 @@ +# Cancellation + +Kotlin's built-in coroutine cancellation replaces Java SDK's `CancellationScope`. This provides a more idiomatic experience while maintaining full Temporal semantics. + +## How Cancellation Works + +When a workflow is cancelled (from the server or programmatically), the Kotlin SDK translates this into coroutine cancellation. Cancellation is **cooperative**—it's checked at suspension points (`delay`, activity execution, child workflow execution, etc.): + +```kotlin +override suspend fun processOrder(order: Order): OrderResult = coroutineScope { + // Cancellation is checked at each suspension point + val validated = KWorkflow.executeActivity(...) // ← cancellation checked here + delay(5.minutes) // ← cancellation checked here + val result = KWorkflow.executeActivity(...) // ← cancellation checked here + result +} +``` + +## Explicit Cancellation Checks + +In rare cases where workflow code doesn't have suspension points (e.g., tight loops), you can check cancellation explicitly using `isActive` or `ensureActive()`. However, CPU-bound work should typically be delegated to activities. + +## Parallel Execution and Cancellation + +`coroutineScope` provides structured concurrency—if one child fails or the scope is cancelled, all children are automatically cancelled: + +```kotlin +override suspend fun parallelWorkflow(): String = coroutineScope { + val a = async { KWorkflow.executeActivity(...) } + val b = async { KWorkflow.executeActivity(...) } + + // If either activity fails, the other is cancelled + // If workflow is cancelled, both activities are cancelled + "${a.await()} - ${b.await()}" +} +``` + +## Detached Scopes (Cleanup Logic) + +Use `withContext(NonCancellable)` for cleanup code that must run even when the workflow is cancelled: + +```kotlin +override suspend fun processOrder(order: Order): OrderResult { + try { + return doProcessOrder(order) + } catch (e: CancellationException) { + // Cleanup runs even though workflow is cancelled + withContext(NonCancellable) { + KWorkflow.executeActivity( + OrderActivities::releaseReservation, + KActivityOptions(startToCloseTimeout = 30.seconds), + order + ) + } + throw e // Re-throw to propagate cancellation + } +} +``` + +This is equivalent to Java's `Workflow.newDetachedCancellationScope()`. + +## Cancellation with Timeout + +Use `withTimeout` to cancel a block after a duration: + +```kotlin +override suspend fun processWithDeadline(order: Order): OrderResult { + return withTimeout(1.hours) { + // Everything in this block is cancelled if it takes > 1 hour + val validated = KWorkflow.executeActivity(...) + val charged = KWorkflow.executeActivity(...) + OrderResult(success = true) + } +} + +// Or use withTimeoutOrNull to get null instead of exception +override suspend fun tryProcess(order: Order): OrderResult? { + return withTimeoutOrNull(30.minutes) { + KWorkflow.executeActivity(...) + } +} +``` + +## Complete Cleanup Example + +```kotlin +class OrderWorkflowImpl : OrderWorkflow { + private var paymentCharged = false + private var reservedItems = mutableListOf() + + override suspend fun processOrder(order: Order): OrderResult { + return try { + doProcessOrder(order) + } catch (e: CancellationException) { + // Workflow was cancelled - run cleanup in detached scope + withContext(NonCancellable) { + cleanup(order) + } + throw e // Re-throw to complete workflow as cancelled + } + } + + private suspend fun cleanup(order: Order) { + // Release any reserved inventory + for (item in reservedItems) { + try { + KWorkflow.executeActivity( + OrderActivities::releaseInventory, + KActivityOptions(startToCloseTimeout = 30.seconds), + item + ) + } catch (e: Exception) { + // Log but continue cleanup + } + } + + // Refund payment if it was charged + if (paymentCharged) { + try { + KWorkflow.executeActivity( + OrderActivities::refundPayment, + KActivityOptions(startToCloseTimeout = 30.seconds), + order + ) + } catch (e: Exception) { + // Log but continue cleanup + } + } + } +} +``` + +## Comparison with Java SDK + +| Java SDK | Kotlin SDK | +|----------|------------| +| `Workflow.newCancellationScope(() -> { ... })` | `coroutineScope { ... }` | +| `Workflow.newDetachedCancellationScope(() -> { ... })` | `withContext(NonCancellable) { ... }` | +| `CancellationScope.cancel()` | `job.cancel()` | +| `CancellationScope.isCancelRequested()` | `!isActive` | +| `CancellationScope.throwCanceled()` | `ensureActive()` | +| `scope.run()` with timeout | `withTimeout(duration) { ... }` | + +## Next Steps + +- [Continue-As-New](./continue-as-new.md) - Long-running workflow patterns +- [Timers & Parallel](./timers-parallel.md) - Timeout patterns diff --git a/kotlin/workflows/child-workflows.md b/kotlin/workflows/child-workflows.md new file mode 100644 index 0000000..2b4bab5 --- /dev/null +++ b/kotlin/workflows/child-workflows.md @@ -0,0 +1,226 @@ +# Child Workflows + +Child workflows use the same stub-less pattern as activities. + +## Execute and Wait + +```kotlin +// Simple case - execute child workflow and wait for result +override suspend fun parentWorkflow(): String { + return KWorkflow.executeChildWorkflow( + ChildWorkflow::doWork, + KChildWorkflowOptions(workflowId = "child-workflow-id"), + "input" + ) +} + +// With retry options +override suspend fun parentWorkflowWithRetry(): String { + return KWorkflow.executeChildWorkflow( + ChildWorkflow::doWork, + KChildWorkflowOptions( + workflowId = "child-workflow-id", + workflowExecutionTimeout = 1.hours, + retryOptions = KRetryOptions(maximumAttempts = 3) + ), + "input" + ) +} +``` + +## Parallel Execution + +Use standard `coroutineScope { async {} }` for parallel child workflows: + +```kotlin +override suspend fun parentWorkflowParallel(): String = coroutineScope { + // Start child and activity in parallel using standard Kotlin async + val childDeferred = async { + KWorkflow.executeChildWorkflow( + ChildWorkflow::doWork, + KChildWorkflowOptions(workflowId = "child-workflow-id"), + "input" + ) + } + val activityDeferred = async { + KWorkflow.executeActivity( + SomeActivities::doSomething, + KActivityOptions(startToCloseTimeout = 30.seconds) + ) + } + + // Wait for both using standard awaitAll + val (childResult, activityResult) = awaitAll(childDeferred, activityDeferred) + "$childResult - $activityResult" +} +``` + +## Child Workflow Handles + +For cases where you need to interact with a child workflow (signal, query, cancel) rather than just wait for its result, use `startChildWorkflow` to get a handle: + +```kotlin +// Start child workflow and get handle for interaction +override suspend fun parentWorkflowWithHandle(): String { + val handle = KWorkflow.startChildWorkflow( + ChildWorkflow::doWork, + KChildWorkflowOptions(workflowId = "child-workflow-id"), + "input" + ) + + // Can signal the child workflow + handle.signal(ChildWorkflow::updateProgress, 50) + + // Wait for result when ready + return handle.result() +} + +// Get handle to existing child workflow by ID +override suspend fun interactWithExistingChild(): String { + val handle = KWorkflow.getChildWorkflowHandle("child-workflow-id") + + // Signal the child workflow + handle.signal(ChildWorkflow::updatePriority, Priority.HIGH) + + return handle.result() +} + +// Parallel child workflows with handles for interaction +override suspend fun parallelChildrenWithHandles(): List = coroutineScope { + val handles = listOf("child-1", "child-2", "child-3").map { id -> + KWorkflow.startChildWorkflow( + ChildWorkflow::doWork, + KChildWorkflowOptions(workflowId = id), + "input" + ) + } + + // Can interact with any child while they're running + handles.forEach { handle -> + handle.signal(ChildWorkflow::updatePriority, Priority.HIGH) + } + + // Wait for all results + handles.map { async { it.result() } }.awaitAll() +} +``` + +## KWorkflow Child Workflow Methods + +```kotlin +object KWorkflow { + /** + * Execute a child workflow and wait for its result. + * For fire-and-wait cases where you don't need to interact with the child. + */ + suspend fun executeChildWorkflow( + workflow: KFunction2, + options: KChildWorkflowOptions, + arg: A1 + ): R + + suspend fun executeChildWorkflow( + workflow: KFunction1, + options: KChildWorkflowOptions + ): R + + // ... up to 6 arguments + + /** + * Start a child workflow and return a handle for interaction. + * Use this when you need to signal, query, or cancel the child workflow. + * For simple fire-and-wait cases, prefer executeChildWorkflow() instead. + */ + suspend fun startChildWorkflow( + workflow: KFunction2, + options: KChildWorkflowOptions, + arg: A1 + ): KChildWorkflowHandle + + suspend fun startChildWorkflow( + workflow: KFunction1, + options: KChildWorkflowOptions + ): KChildWorkflowHandle + + suspend fun startChildWorkflow( + workflow: KFunction3, + options: KChildWorkflowOptions, + arg1: A1, + arg2: A2 + ): KChildWorkflowHandle + + // ... up to 6 arguments + + /** + * Get a handle to an existing child workflow by ID. + */ + fun getChildWorkflowHandle(workflowId: String): KChildWorkflowHandle +} +``` + +## KChildWorkflowHandle API + +```kotlin +/** + * Handle for interacting with a started child workflow. + * Returned by startChildWorkflow() with result type captured from method reference. + * + * @param T The child workflow interface type + * @param R The result type of the child workflow method + */ +interface KChildWorkflowHandle { + /** The child workflow's workflow ID */ + val workflowId: String + + /** The child workflow's first execution run ID */ + val firstExecutionRunId: String + + /** + * Wait for the child workflow to complete and return its result. + * Suspends until the child workflow finishes. + */ + suspend fun result(): R + + // Signals - type-safe method references + suspend fun signal(method: KFunction1) + suspend fun signal(method: KFunction2, arg: A1) + suspend fun signal(method: KFunction3, arg1: A1, arg2: A2) + + /** + * Request cancellation of the child workflow. + * The child workflow will receive a CancellationException at its next suspension point. + */ + suspend fun cancel() +} +``` + +## KChildWorkflowOptions + +```kotlin +// All fields are optional - null values inherit from parent workflow or use Java SDK defaults +KChildWorkflowOptions( + workflowId = "child-workflow-id", + taskQueue = "child-queue", // Optional: defaults to parent's task queue + workflowExecutionTimeout = 1.hours, + workflowRunTimeout = 30.minutes, + retryOptions = KRetryOptions( + initialInterval = 1.seconds, + maximumAttempts = 3 + ), + parentClosePolicy = ParentClosePolicy.PARENT_CLOSE_POLICY_TERMINATE, // Optional + cancellationType = ChildWorkflowCancellationType.WAIT_CANCELLATION_COMPLETED // Optional +) + +// Options are optional - use default KChildWorkflowOptions() when not specified +val result = KWorkflow.executeChildWorkflow( + ChildWorkflow::processData, + inputData // No options needed for simple cases +) +``` + +> **Note:** For simple parallel execution where you only need the result, use standard `coroutineScope { async { executeChildWorkflow(...) } }`. Use `startChildWorkflow` only when you need to interact with the child workflow while it's running. + +## Next Steps + +- [Timers & Parallel Execution](./timers-parallel.md) - More parallel patterns +- [Cancellation](./cancellation.md) - How cancellation propagates to child workflows diff --git a/kotlin/workflows/continue-as-new.md b/kotlin/workflows/continue-as-new.md new file mode 100644 index 0000000..773de28 --- /dev/null +++ b/kotlin/workflows/continue-as-new.md @@ -0,0 +1,147 @@ +# Continue-As-New + +Continue-as-new completes the current workflow execution and immediately starts a new execution with fresh event history. This is essential for: + +- **Preventing history growth**: Long-running workflows accumulate event history which can impact performance. Continue-as-new resets the history. +- **Batch processing**: Process a batch of items, then continue-as-new with the next batch offset. +- **Implementing loops**: Instead of infinite loops that accumulate history, use continue-as-new. + +## Basic Usage + +```kotlin +// Basic continue-as-new - same workflow type, inherit all options +KWorkflow.continueAsNew(nextBatchId, newOffset) + +// With modified options +KWorkflow.continueAsNew( + KContinueAsNewOptions( + taskQueue = "high-priority-queue", + workflowRunTimeout = 2.hours + ), + nextBatchId, newOffset +) + +// Continue as different workflow type (for versioning/migration) +KWorkflow.continueAsNew( + "OrderProcessorV2", + KContinueAsNewOptions(taskQueue = "orders-v2"), + migratedState +) + +// Type-safe continue as different workflow using method reference +KWorkflow.continueAsNew( + OrderProcessorV2::process, + KContinueAsNewOptions(), + migratedState +) +``` + +## KContinueAsNewOptions + +```kotlin +/** + * Options for continuing a workflow as a new execution. + * All fields are optional - null values inherit from the current workflow. + */ +data class KContinueAsNewOptions( + val workflowRunTimeout: Duration? = null, + val taskQueue: String? = null, + val retryOptions: KRetryOptions? = null, + val workflowTaskTimeout: Duration? = null, + val memo: Map? = null, + val typedSearchAttributes: SearchAttributes? = null, + val contextPropagators: List? = null +) +``` + +## Batch Processing Pattern + +```kotlin +class BatchProcessorImpl : BatchProcessor { + override suspend fun processBatches(startOffset: Int) { + val batchSize = 100 + val items = KWorkflow.executeActivity( + DataActivities::fetchBatch, + KActivityOptions(startToCloseTimeout = 1.minutes), + startOffset, batchSize + ) + + if (items.isEmpty()) { + return // All done, workflow completes normally + } + + // Process items... + for (item in items) { + KWorkflow.executeActivity( + DataActivities::processItem, + KActivityOptions(startToCloseTimeout = 30.seconds), + item + ) + } + + // Continue with the next batch + KWorkflow.continueAsNew(startOffset + batchSize) + } +} +``` + +## History Size Check Pattern + +```kotlin +override suspend fun execute(state: WorkflowState) { + while (true) { + // Check if history is getting too large + if (KWorkflow.getInfo().isContinueAsNewSuggested) { + KWorkflow.continueAsNew(state) + } + + // Wait for signals and process... + KWorkflow.awaitCondition { hasNewWork } + processWork() + } +} +``` + +## KWorkflow.continueAsNew API + +```kotlin +object KWorkflow { + /** + * Continue workflow as new with the same workflow type. + * This function never returns - it terminates the current execution. + */ + fun continueAsNew(vararg args: Any?): Nothing + + /** + * Continue workflow as new with modified options. + * Null option values inherit from the current workflow. + */ + fun continueAsNew(options: KContinueAsNewOptions, vararg args: Any?): Nothing + + /** + * Continue as a different workflow type. + * Useful for workflow versioning or migration. + */ + fun continueAsNew( + workflowType: String, + options: KContinueAsNewOptions, + vararg args: Any? + ): Nothing + + /** + * Type-safe continue as different workflow using method reference. + */ + fun continueAsNew( + workflow: KFunction<*>, + options: KContinueAsNewOptions, + vararg args: Any? + ): Nothing +} +``` + +> **Important:** `continueAsNew` never returns normally. It terminates the current workflow execution and signals Temporal to start a new execution. The return type `Nothing` indicates this in Kotlin's type system. + +## Next Steps + +- [Workflow Definition](./definition.md) - Back to basics +- [Migration Guide](../migration.md) - Java SDK comparison diff --git a/kotlin/workflows/definition.md b/kotlin/workflows/definition.md new file mode 100644 index 0000000..108a245 --- /dev/null +++ b/kotlin/workflows/definition.md @@ -0,0 +1,124 @@ +# Workflow Definition + +There are two approaches for workflow definition, depending on whether you need Java interoperability. + +## Option A: Pure Kotlin (Recommended) + +For pure Kotlin codebases, define interfaces with `suspend` methods: + +```kotlin +@WorkflowInterface +interface GreetingWorkflow { + @WorkflowMethod + suspend fun getGreeting(name: String): String +} + +class GreetingWorkflowImpl : GreetingWorkflow { + override suspend fun getGreeting(name: String): String { + return KWorkflow.executeActivity( + "composeGreeting", + KActivityOptions(startToCloseTimeout = 10.seconds), + "Hello", name + ) + } +} + +// Client call using KWorkflowClient - same pattern as activities, no stub needed +val result = client.executeWorkflow( + GreetingWorkflow::getGreeting, + KWorkflowOptions( + workflowId = "greeting-123", + taskQueue = "greetings" + ), + "Temporal" +) +``` + +## Option B: Java Interoperability (Parallel Interface Pattern) + +When you need to share workflow interfaces with Java code, use the parallel interface pattern: + +```kotlin +// Interface for client calls - non-suspend for Java compatibility +@WorkflowInterface +interface OrderWorkflow { + @WorkflowMethod + fun processOrder(order: Order): OrderResult + + @SignalMethod + fun cancelOrder(reason: String) + + @QueryMethod + val status: OrderStatus +} + +// Parallel suspend interface for Kotlin implementation +@KWorkflowImpl(workflowInterface = OrderWorkflow::class) +interface OrderWorkflowSuspend { + suspend fun processOrder(order: Order): OrderResult + suspend fun cancelOrder(reason: String) + val status: OrderStatus // Queries are never suspend +} + +// Kotlin implementation uses the suspend interface +class OrderWorkflowImpl : OrderWorkflowSuspend { + override suspend fun processOrder(order: Order): OrderResult { + // Full coroutine support - delay(), async, etc. + delay(1.hours) + return OrderResult(success = true) + } + + override suspend fun cancelOrder(reason: String) { /* ... */ } + override val status: OrderStatus get() = OrderStatus.PENDING +} + +// Client call using KWorkflowClient - same pattern, suspends for result +val result = client.executeWorkflow( + OrderWorkflow::processOrder, + KWorkflowOptions( + workflowId = "order-123", + taskQueue = "orders" + ), + order +) +``` + +## When to Use Which + +| Scenario | Approach | +|----------|----------| +| Pure Kotlin codebase | Option A - suspend interfaces | +| Calling Java-defined workflows | Option A works (from coroutine context) | +| Kotlin workflows with Java-defined interface | Option B - parallel interface | +| Shared interface library with Java | Option B - parallel interface | + +## Key Characteristics + +* Use `coroutineScope`, `async`, `launch` for concurrent execution +* Use `delay()` for timers (maps to Temporal timers, not `Thread.sleep`) +* Reuses `@WorkflowInterface` and `@WorkflowMethod` annotations from Java SDK +* Data classes work naturally for parameters and results + +## Logging + +Use `KWorkflow.logger()` for workflow-safe logging: + +```kotlin +// Get logger using workflow type as name +val log = KWorkflow.logger() +log.info("Processing order") + +// Or with custom logger name +val customLog = KWorkflow.logger("my.custom.logger") + +// Or with class +val classLog = KWorkflow.logger(MyWorkflowImpl::class.java) +``` + +> **Note:** Versioning (`KWorkflow.getVersion`), side effects (`KWorkflow.sideEffect`), and search attributes (`KWorkflow.upsertSearchAttributes`) use the same patterns as the Java SDK. + +## Next Steps + +- [Signals, Queries & Updates](./signals-queries.md) - Communication patterns +- [Child Workflows](./child-workflows.md) - Orchestrating child workflows +- [Timers & Parallel Execution](./timers-parallel.md) - Delays and async patterns diff --git a/kotlin/workflows/signals-queries.md b/kotlin/workflows/signals-queries.md new file mode 100644 index 0000000..c5840ed --- /dev/null +++ b/kotlin/workflows/signals-queries.md @@ -0,0 +1,146 @@ +# Signals, Queries, and Updates + +Signals and updates follow the same suspend/non-suspend pattern as workflow methods. Queries are always synchronous (never suspend) and can be defined as properties. + +## Defining Handlers + +```kotlin +@WorkflowInterface +interface OrderWorkflow { + @WorkflowMethod + suspend fun processOrder(order: Order): OrderResult + + @SignalMethod + suspend fun cancelOrder(reason: String) + + @UpdateMethod + suspend fun addItem(item: OrderItem): Boolean + + @UpdateValidatorMethod(updateMethod = "addItem") + fun validateAddItem(item: OrderItem) + + // Queries - always synchronous, can use property syntax + @QueryMethod + val status: OrderStatus + + @QueryMethod + fun getItemCount(): Int +} +``` + +## Dynamic Handler Registration + +For workflows that need to handle signals, queries, or updates dynamically (without annotations), use the registration APIs: + +```kotlin +class DynamicWorkflowImpl : DynamicWorkflow { + private var state = mutableMapOf() + + override suspend fun execute(input: String): String { + // Register a named update handler with validator + KWorkflow.registerUpdateHandler( + "updateState", + validator = { args -> + val key = args.get(0, String::class.java) + require(key.isNotBlank()) { "Key cannot be blank" } + }, + handler = { args -> + val key = args.get(0, String::class.java) + val value = args.get(1, Any::class.java) + state[key] = value + "Updated $key" + } + ) + + // Register a named signal handler + KWorkflow.registerSignalHandler("notify") { args -> + val message = args.get(0, String::class.java) + println("Received: $message") + } + + // Register a named query handler + KWorkflow.registerQueryHandler("getState") { args -> + val key = args.get(0, String::class.java) + state[key] + } + + // Register dynamic handlers for unknown names (catch-all) + KWorkflow.registerDynamicUpdateHandler { updateName, args -> + "Handled unknown update: $updateName" + } + + KWorkflow.registerDynamicSignalHandler { signalName, args -> + println("Unknown signal: $signalName") + } + + KWorkflow.registerDynamicQueryHandler { queryName, args, resultClass -> + "Unknown query: $queryName" + } + + // Wait for completion signal + KWorkflow.awaitCondition { state["done"] == true } + return "Completed" + } +} +``` + +## Client-Side Interaction + +### Sending Signals + +```kotlin +val handle = client.getWorkflowHandle("order-123") + +// Type-safe signal using method reference +handle.signal(OrderWorkflow::cancelOrder, "Customer request") +``` + +### Querying Workflows + +```kotlin +val handle = client.getWorkflowHandle("order-123") + +// Query using property reference +val status = handle.query(OrderWorkflow::status) + +// Query using method reference +val count = handle.query(OrderWorkflow::getItemCount) +``` + +### Executing Updates + +```kotlin +val handle = client.getWorkflowHandle("order-123") + +// Execute update and wait for result +val added = handle.executeUpdate(OrderWorkflow::addItem, newItem) + +// Or start update async and get handle +val updateHandle = handle.startUpdate(OrderWorkflow::addItem, newItem) +val result = updateHandle.result() +``` + +## Update Validation + +Update validators run synchronously before the update handler. They can reject updates by throwing exceptions: + +```kotlin +@UpdateValidatorMethod(updateMethod = "addItem") +fun validateAddItem(item: OrderItem) { + require(item.quantity > 0) { "Quantity must be positive" } + require(item.price >= BigDecimal.ZERO) { "Price cannot be negative" } + require(_status == OrderStatus.PENDING) { "Cannot add items after processing started" } +} + +@UpdateMethod +suspend fun addItem(item: OrderItem): Boolean { + // Validator already passed - safe to proceed + _items.add(item) + return true +} +``` + +## Next Steps + +- [Child Workflows](./child-workflows.md) - Orchestrating child workflows +- [Client API](../client/workflow-handle.md) - More on workflow handles diff --git a/kotlin/workflows/timers-parallel.md b/kotlin/workflows/timers-parallel.md new file mode 100644 index 0000000..a6b3b37 --- /dev/null +++ b/kotlin/workflows/timers-parallel.md @@ -0,0 +1,121 @@ +# Timers and Parallel Execution + +## Timers and Delays + +```kotlin +override suspend fun workflowWithTimer(): String { + // Simple delay using Kotlin Duration + delay(5.minutes) + + return "completed" +} +``` + +The standard `kotlinx.coroutines.delay()` is intercepted by Temporal's deterministic dispatcher and creates durable timers. + +## Parallel Execution + +Use standard `coroutineScope { async { } }` for parallel execution: + +```kotlin +val options = KActivityOptions(startToCloseTimeout = 30.seconds) + +override suspend fun parallelWorkflow(items: List): List = coroutineScope { + // Process all items in parallel using standard Kotlin patterns + items.map { item -> + async { + KWorkflow.executeActivity("process", options, item) + } + }.awaitAll() // Standard kotlinx.coroutines.awaitAll +} + +// Another example: parallel activities with different results +override suspend fun getGreetings(name: String): String = coroutineScope { + val hello = async { KWorkflow.executeActivity("greet", options, "Hello", name) } + val goodbye = async { KWorkflow.executeActivity("greet", options, "Goodbye", name) } + + // Standard awaitAll works with any Deferred + val (helloResult, goodbyeResult) = awaitAll(hello, goodbye) + "$helloResult\n$goodbyeResult" +} +``` + +**Why this works deterministically:** +- All coroutines inherit the workflow's deterministic dispatcher +- The dispatcher executes tasks in a FIFO queue ensuring consistent ordering +- Same execution order during replay + +> **Note:** `coroutineScope` provides structured concurrency—if one `async` fails, all others are automatically cancelled. This maps naturally to Temporal's cancellation semantics. + +## Await Condition + +Wait for a condition to become true (equivalent to Java's `Workflow.await()`): + +```kotlin +override suspend fun workflowWithCondition(): String { + var approved = false + + // Signal handler sets approved = true + // ... + + // Wait until approved (blocks workflow until condition is true) + KWorkflow.awaitCondition { approved } + + return "Approved" +} + +// With timeout - returns false if timed out +override suspend fun workflowWithTimeout(): String { + var approved = false + + val wasApproved = KWorkflow.awaitCondition(timeout = 24.hours) { approved } + + return if (wasApproved) "Approved" else "Timed out" +} +``` + +## Timeout Patterns + +Use `withTimeout` to cancel a block after a duration: + +```kotlin +override suspend fun processWithDeadline(order: Order): OrderResult { + return withTimeout(1.hours) { + // Everything in this block is cancelled if it takes > 1 hour + val validated = KWorkflow.executeActivity(...) + val charged = KWorkflow.executeActivity(...) + OrderResult(success = true) + } +} + +// Or use withTimeoutOrNull to get null instead of exception +override suspend fun tryProcess(order: Order): OrderResult? { + return withTimeoutOrNull(30.minutes) { + KWorkflow.executeActivity(...) + } +} +``` + +## Racing Patterns + +Race multiple operations and take the first result: + +```kotlin +override suspend fun raceOperations(): String = coroutineScope { + // Start multiple operations + val fast = async { KWorkflow.executeActivity(Activities::fastOperation, options) } + val slow = async { KWorkflow.executeActivity(Activities::slowOperation, options) } + + // Use select to get first result + select { + fast.onAwait { "Fast: $it" } + slow.onAwait { "Slow: $it" } + } + // Note: The other coroutine is still running but will be cancelled when scope exits +} +``` + +## Next Steps + +- [Cancellation](./cancellation.md) - How cancellation works with parallel execution +- [Child Workflows](./child-workflows.md) - Parallel child workflow patterns From e25c01723fa02c87b26d2e5630f327207f9e2748 Mon Sep 17 00:00:00 2001 From: Maxim Fateev Date: Sun, 4 Jan 2026 21:49:26 -0800 Subject: [PATCH 19/83] Remove legacy getHeartbeatDetails(): Payloads? from KActivityInfo The type-safe version on KActivityContext is the preferred API: fun getHeartbeatDetails(detailsClass: Class): T? inline fun KActivityContext.getHeartbeatDetails(): T? --- kotlin/activities/local-activities.md | 1 - kotlin/kotlin-idioms.md | 10 ---------- 2 files changed, 11 deletions(-) diff --git a/kotlin/activities/local-activities.md b/kotlin/activities/local-activities.md index d9286a4..976cd80 100644 --- a/kotlin/activities/local-activities.md +++ b/kotlin/activities/local-activities.md @@ -136,7 +136,6 @@ interface KActivityInfo { val activityType: String val workflowId: String val attempt: Int - fun getHeartbeatDetails(): Payloads? // ... other properties } ``` diff --git a/kotlin/kotlin-idioms.md b/kotlin/kotlin-idioms.md index 4f3e838..d9e0161 100644 --- a/kotlin/kotlin-idioms.md +++ b/kotlin/kotlin-idioms.md @@ -54,16 +54,6 @@ interface KWorkflowInfo { // ... other properties } -// KActivityInfo - nullable instead of Optional -interface KActivityInfo { - val activityId: String - val activityType: String - val workflowId: String - val attempt: Int - fun getHeartbeatDetails(): Payloads? // Optional in Java - // ... other properties -} - // Access via KWorkflow / KActivity objects val info: KWorkflowInfo = KWorkflow.getInfo() val parentId: String? = info.parentWorkflowId From e6ab5ee2bc63fb0fb69aeb76f2a0c527060aaa98 Mon Sep 17 00:00:00 2001 From: Maxim Fateev Date: Sun, 4 Jan 2026 22:12:04 -0800 Subject: [PATCH 20/83] Remove suspendHeartbeat API, keep only heartbeat() Heartbeat is a short non-blocking operation, so a single heartbeat() API is sufficient for both sync and suspend activities. --- kotlin/activities/README.md | 1 - kotlin/activities/implementation.md | 5 ++--- kotlin/activities/local-activities.md | 5 ----- kotlin/implementation/implementation-plan.md | 2 +- kotlin/kotlin-idioms.md | 3 +-- 5 files changed, 4 insertions(+), 12 deletions(-) diff --git a/kotlin/activities/README.md b/kotlin/activities/README.md index e150c86..daab195 100644 --- a/kotlin/activities/README.md +++ b/kotlin/activities/README.md @@ -60,7 +60,6 @@ val result = KWorkflow.executeActivity( | Execute by name | `KWorkflow.executeActivity("name", options, args)` | | Local activity | `KWorkflow.executeLocalActivity(Interface::method, options, args)` | | Heartbeat | `KActivity.getContext().heartbeat(details)` | -| Suspend heartbeat | `KActivity.getContext().suspendHeartbeat(details)` | ## Next Steps diff --git a/kotlin/activities/implementation.md b/kotlin/activities/implementation.md index 062e4f4..01e4a7d 100644 --- a/kotlin/activities/implementation.md +++ b/kotlin/activities/implementation.md @@ -118,8 +118,7 @@ class LongRunningActivitiesImpl : LongRunningActivities { val lines = File(filePath).readLines() lines.forEachIndexed { index, line -> - // Use suspendHeartbeat in suspend activities - context.suspendHeartbeat(index) + context.heartbeat(index) // Process line... processLine(line) @@ -142,7 +141,7 @@ override suspend fun resumableProcess(data: List): ProcessResult { val startIndex: Int = context.getHeartbeatDetails() ?: 0 for (i in startIndex until data.size) { - context.suspendHeartbeat(i) + context.heartbeat(i) processItem(data[i]) } diff --git a/kotlin/activities/local-activities.md b/kotlin/activities/local-activities.md index 976cd80..e1ab133 100644 --- a/kotlin/activities/local-activities.md +++ b/kotlin/activities/local-activities.md @@ -87,12 +87,8 @@ val info = context.info println("Activity ${info.activityType}, attempt ${info.attempt}") // Heartbeat for long-running activities -// Use heartbeat() in regular activities context.heartbeat(progressDetails) -// Use suspendHeartbeat() in suspend activities for non-blocking operation -context.suspendHeartbeat(progressDetails) - // Get heartbeat details from previous attempt (for retry scenarios) val previousProgress: Int? = context.getHeartbeatDetails() @@ -114,7 +110,6 @@ val taskToken = context.taskToken interface KActivityContext { val info: KActivityInfo fun heartbeat(details: Any? = null) - suspend fun suspendHeartbeat(details: Any? = null) fun getHeartbeatDetails(detailsClass: Class): T? val taskToken: ByteArray fun doNotCompleteOnReturn() diff --git a/kotlin/implementation/implementation-plan.md b/kotlin/implementation/implementation-plan.md index 8bd03be..b7b7188 100644 --- a/kotlin/implementation/implementation-plan.md +++ b/kotlin/implementation/implementation-plan.md @@ -77,7 +77,7 @@ ### 2.6 Kotlin Activity API ✅ - ✅ `KActivity` object (entry point for activity APIs) -- ✅ `KActivity.getInfo()`, `heartbeat()`, `suspendHeartbeat()` methods +- ✅ `KActivity.getInfo()`, `heartbeat()` methods - ✅ `KActivity.logger()` for idiomatic logging - ✅ `KActivityInfo` with null safety - ✅ Suspend activity support via `SuspendActivityWrapper` diff --git a/kotlin/kotlin-idioms.md b/kotlin/kotlin-idioms.md index d9e0161..08679ef 100644 --- a/kotlin/kotlin-idioms.md +++ b/kotlin/kotlin-idioms.md @@ -61,8 +61,7 @@ val parentId: String? = info.parentWorkflowId // KActivity.getContext() returns KActivityContext (matches Java's Activity.getExecutionContext()) val context: KActivityContext = KActivity.getContext() val activityInfo: KActivityInfo = context.info -context.heartbeat("progress") // Blocking version for regular activities -context.suspendHeartbeat("progress") // Suspend version for suspend activities +context.heartbeat("progress") // Record heartbeat progress val logger = context.logger() // Get activity logger ``` From 234b0728203ca52843550bc07a2803e058513233 Mon Sep 17 00:00:00 2001 From: Maxim Fateev Date: Sun, 4 Jan 2026 22:13:19 -0800 Subject: [PATCH 21/83] Document Activity API design decisions in api-parity.md - Single heartbeat() API for both sync and suspend activities - Support for both sync and suspend activity methods --- kotlin/api-parity.md | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/kotlin/api-parity.md b/kotlin/api-parity.md index 1231870..bef4cfd 100644 --- a/kotlin/api-parity.md +++ b/kotlin/api-parity.md @@ -24,6 +24,32 @@ The following Java SDK APIs are **not needed** in the Kotlin SDK due to language | `Workflow.setDefaultLocalActivityOptions(...)` | Pass `KLocalActivityOptions` per call | | `Workflow.applyLocalActivityOptions(...)` | Pass options per call | +## Activity API Design Decisions + +### Single heartbeat() API + +The Kotlin SDK provides a single `heartbeat()` method on `KActivityContext` for both sync and suspend activities: + +```kotlin +context.heartbeat(progressDetails) +``` + +**Rationale:** Heartbeat is a short, non-blocking operation that records progress locally. The actual network call happens asynchronously in the background. A separate `suspendHeartbeat()` API is unnecessary. + +### Both Sync and Suspend Activities Supported + +The Kotlin SDK supports both regular and suspend activity methods: + +```kotlin +@ActivityInterface +interface OrderActivities { + fun validate(order: Order): Boolean // Sync - runs on thread pool + suspend fun fetchExternal(id: String): Data // Suspend - runs on coroutine +} +``` + +**Rationale:** This matches other Kotlin frameworks (Spring, Micronaut, gRPC-Kotlin) that detect method signatures and handle them appropriately. It reduces migration friction from Java SDK and gives developers choice. + ## Cancellation Support Kotlin coroutine cancellation is fully integrated with Temporal workflow cancellation: From 94417b56c99c30b3343be699324e8b4d1c9465b5 Mon Sep 17 00:00:00 2001 From: Maxim Fateev Date: Sun, 4 Jan 2026 22:15:08 -0800 Subject: [PATCH 22/83] Fix Null Safety section example in kotlin-idioms.md Replace unrelated KActivity usage example with getHeartbeatDetails() which demonstrates nullable return type and elvis operator. --- kotlin/kotlin-idioms.md | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/kotlin/kotlin-idioms.md b/kotlin/kotlin-idioms.md index 08679ef..baef0d8 100644 --- a/kotlin/kotlin-idioms.md +++ b/kotlin/kotlin-idioms.md @@ -54,15 +54,13 @@ interface KWorkflowInfo { // ... other properties } -// Access via KWorkflow / KActivity objects +// Access via KWorkflow object val info: KWorkflowInfo = KWorkflow.getInfo() -val parentId: String? = info.parentWorkflowId +val parentId: String? = info.parentWorkflowId // null if no parent -// KActivity.getContext() returns KActivityContext (matches Java's Activity.getExecutionContext()) -val context: KActivityContext = KActivity.getContext() -val activityInfo: KActivityInfo = context.info -context.heartbeat("progress") // Record heartbeat progress -val logger = context.logger() // Get activity logger +// Activity heartbeat details - nullable instead of Optional +val progress: Int? = KActivity.getContext().getHeartbeatDetails() +val startIndex = progress ?: 0 // Kotlin's elvis operator ``` This eliminates `.orElse(null)`, `.isPresent`, and other Optional ceremony. From 66e6ee5de8450cf981e7b69016b2d18546d5e137 Mon Sep 17 00:00:00 2001 From: Maxim Fateev Date: Mon, 5 Jan 2026 11:34:10 -0800 Subject: [PATCH 23/83] Remove @KWorkflowImpl parallel interface pattern from proposal Java clients can call Kotlin suspend workflows using untyped stubs, making the parallel interface pattern unnecessary. Updated docs to show the simpler approach with Java interop via untyped stubs. --- kotlin/workflows/definition.md | 85 +++++++++++++--------------------- 1 file changed, 32 insertions(+), 53 deletions(-) diff --git a/kotlin/workflows/definition.md b/kotlin/workflows/definition.md index 108a245..eedcae8 100644 --- a/kotlin/workflows/definition.md +++ b/kotlin/workflows/definition.md @@ -1,10 +1,6 @@ # Workflow Definition -There are two approaches for workflow definition, depending on whether you need Java interoperability. - -## Option A: Pure Kotlin (Recommended) - -For pure Kotlin codebases, define interfaces with `suspend` methods: +Define workflow interfaces with `suspend` methods for full Kotlin coroutine support: ```kotlin @WorkflowInterface @@ -34,63 +30,46 @@ val result = client.executeWorkflow( ) ``` -## Option B: Java Interoperability (Parallel Interface Pattern) +## Java Interoperability -When you need to share workflow interfaces with Java code, use the parallel interface pattern: +Java clients can invoke Kotlin suspend workflows using **untyped workflow stubs**. The workflow type name defaults to the interface name. -```kotlin -// Interface for client calls - non-suspend for Java compatibility -@WorkflowInterface -interface OrderWorkflow { - @WorkflowMethod - fun processOrder(order: Order): OrderResult +```java +// Java client calling a Kotlin suspend workflow +WorkflowStub stub = client.newUntypedWorkflowStub( + "GreetingWorkflow", // Workflow type = interface name + WorkflowOptions.newBuilder() + .setTaskQueue("greetings") + .setWorkflowId("greeting-123") + .build() +); - @SignalMethod - fun cancelOrder(reason: String) - - @QueryMethod - val status: OrderStatus -} - -// Parallel suspend interface for Kotlin implementation -@KWorkflowImpl(workflowInterface = OrderWorkflow::class) -interface OrderWorkflowSuspend { - suspend fun processOrder(order: Order): OrderResult - suspend fun cancelOrder(reason: String) - val status: OrderStatus // Queries are never suspend -} +stub.start("Temporal"); +String result = stub.getResult(String.class); +``` -// Kotlin implementation uses the suspend interface -class OrderWorkflowImpl : OrderWorkflowSuspend { - override suspend fun processOrder(order: Order): OrderResult { - // Full coroutine support - delay(), async, etc. - delay(1.hours) - return OrderResult(success = true) - } +Alternatively, Java clients can define their own Java interface with the **same name** as the Kotlin interface to use typed stubs: - override suspend fun cancelOrder(reason: String) { /* ... */ } - override val status: OrderStatus get() = OrderStatus.PENDING +```java +// Java interface matching the Kotlin workflow type +@WorkflowInterface +public interface GreetingWorkflow { + @WorkflowMethod + String getGreeting(String name); } -// Client call using KWorkflowClient - same pattern, suspends for result -val result = client.executeWorkflow( - OrderWorkflow::processOrder, - KWorkflowOptions( - workflowId = "order-123", - taskQueue = "orders" - ), - order -) +// Java client using typed stub +GreetingWorkflow workflow = client.newWorkflowStub( + GreetingWorkflow.class, + WorkflowOptions.newBuilder() + .setTaskQueue("greetings") + .setWorkflowId("greeting-123") + .build() +); +String result = workflow.getGreeting("Temporal"); ``` -## When to Use Which - -| Scenario | Approach | -|----------|----------| -| Pure Kotlin codebase | Option A - suspend interfaces | -| Calling Java-defined workflows | Option A works (from coroutine context) | -| Kotlin workflows with Java-defined interface | Option B - parallel interface | -| Shared interface library with Java | Option B - parallel interface | +> **Note:** Java cannot directly use Kotlin suspend interfaces because suspend functions compile to methods with an extra `Continuation` parameter. The untyped stub approach is recommended for Java clients. ## Key Characteristics From 56d8e0c1bdb95ce6e9a5e4e2f044891d0357eca8 Mon Sep 17 00:00:00 2001 From: Maxim Fateev Date: Mon, 5 Jan 2026 11:37:00 -0800 Subject: [PATCH 24/83] Use typed activity call in workflow definition example --- kotlin/workflows/definition.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/kotlin/workflows/definition.md b/kotlin/workflows/definition.md index eedcae8..ab6511d 100644 --- a/kotlin/workflows/definition.md +++ b/kotlin/workflows/definition.md @@ -11,8 +11,8 @@ interface GreetingWorkflow { class GreetingWorkflowImpl : GreetingWorkflow { override suspend fun getGreeting(name: String): String { - return KWorkflow.executeActivity( - "composeGreeting", + return KWorkflow.executeActivity( + GreetingActivities::composeGreeting, KActivityOptions(startToCloseTimeout = 10.seconds), "Hello", name ) From 7a2b51f17694b12ee7bf7aa7587b56aa96cc50da Mon Sep 17 00:00:00 2001 From: Maxim Fateev Date: Mon, 5 Jan 2026 11:38:41 -0800 Subject: [PATCH 25/83] Use typed activity call in README Quick Start example --- kotlin/README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/kotlin/README.md b/kotlin/README.md index 1dfc997..5b8e67a 100644 --- a/kotlin/README.md +++ b/kotlin/README.md @@ -94,8 +94,8 @@ interface GreetingWorkflow { // Implement workflow class GreetingWorkflowImpl : GreetingWorkflow { override suspend fun getGreeting(name: String): String { - return KWorkflow.executeActivity( - "composeGreeting", + return KWorkflow.executeActivity( + GreetingActivities::composeGreeting, KActivityOptions(startToCloseTimeout = 10.seconds), "Hello", name ) From c70697c0c86c54f3d3316cccd597027dd2955ccf Mon Sep 17 00:00:00 2001 From: Maxim Fateev Date: Mon, 5 Jan 2026 11:47:45 -0800 Subject: [PATCH 26/83] Use typed activity calls in timers-parallel.md examples --- kotlin/workflows/timers-parallel.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/kotlin/workflows/timers-parallel.md b/kotlin/workflows/timers-parallel.md index a6b3b37..b9c1668 100644 --- a/kotlin/workflows/timers-parallel.md +++ b/kotlin/workflows/timers-parallel.md @@ -24,15 +24,15 @@ override suspend fun parallelWorkflow(items: List): List = corouti // Process all items in parallel using standard Kotlin patterns items.map { item -> async { - KWorkflow.executeActivity("process", options, item) + KWorkflow.executeActivity(ProcessingActivities::process, options, item) } }.awaitAll() // Standard kotlinx.coroutines.awaitAll } // Another example: parallel activities with different results override suspend fun getGreetings(name: String): String = coroutineScope { - val hello = async { KWorkflow.executeActivity("greet", options, "Hello", name) } - val goodbye = async { KWorkflow.executeActivity("greet", options, "Goodbye", name) } + val hello = async { KWorkflow.executeActivity(GreetingActivities::greet, options, "Hello", name) } + val goodbye = async { KWorkflow.executeActivity(GreetingActivities::greet, options, "Goodbye", name) } // Standard awaitAll works with any Deferred val (helloResult, goodbyeResult) = awaitAll(hello, goodbye) From 9152ca6d1d6cb75842fea13619623002dc606294 Mon Sep 17 00:00:00 2001 From: Maxim Fateev Date: Mon, 5 Jan 2026 12:13:06 -0800 Subject: [PATCH 27/83] Rename Next Steps to Related, add linear Next navigation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Renamed "Next Steps" sections to "Related" across all docs - Added "Next: " links for linear document traversal - Reading order follows README structure: Idioms → Config → Workflows → Activities → Client → Worker → Migration → API Parity --- kotlin/activities/README.md | 11 +++++++---- kotlin/activities/definition.md | 8 ++++++-- kotlin/activities/implementation.md | 8 ++++++-- kotlin/activities/local-activities.md | 6 +++++- kotlin/api-parity.md | 3 ++- kotlin/client/README.md | 11 +++++++---- kotlin/client/advanced.md | 6 +++++- kotlin/client/workflow-client.md | 8 ++++++-- kotlin/client/workflow-handle.md | 8 ++++++-- kotlin/configuration/README.md | 6 +++++- kotlin/configuration/data-conversion.md | 6 +++++- kotlin/configuration/interceptors.md | 6 +++++- kotlin/configuration/koptions.md | 6 +++++- kotlin/kotlin-idioms.md | 6 +++++- kotlin/migration.md | 8 ++++++-- kotlin/worker/README.md | 11 ++++++++--- kotlin/worker/setup.md | 6 +++++- kotlin/workflows/README.md | 11 +++++++---- kotlin/workflows/cancellation.md | 8 ++++++-- kotlin/workflows/child-workflows.md | 8 ++++++-- kotlin/workflows/continue-as-new.md | 8 ++++++-- kotlin/workflows/definition.md | 7 +++++-- kotlin/workflows/signals-queries.md | 8 ++++++-- kotlin/workflows/timers-parallel.md | 8 ++++++-- 24 files changed, 136 insertions(+), 46 deletions(-) diff --git a/kotlin/activities/README.md b/kotlin/activities/README.md index daab195..07e50f5 100644 --- a/kotlin/activities/README.md +++ b/kotlin/activities/README.md @@ -61,8 +61,11 @@ val result = KWorkflow.executeActivity( | Local activity | `KWorkflow.executeLocalActivity(Interface::method, options, args)` | | Heartbeat | `KActivity.getContext().heartbeat(details)` | -## Next Steps +## Related -- Start with [Activity Definition](./definition.md) for interface patterns -- Learn about [Implementation](./implementation.md) for suspend activity patterns -- See [Local Activities](./local-activities.md) for short-lived activities +- [Implementation](./implementation.md) - Suspend activity patterns +- [Local Activities](./local-activities.md) - Short-lived activities + +--- + +**Next:** [Activity Definition](./definition.md) diff --git a/kotlin/activities/definition.md b/kotlin/activities/definition.md index 4ce6361..084e168 100644 --- a/kotlin/activities/definition.md +++ b/kotlin/activities/definition.md @@ -163,7 +163,11 @@ object KWorkflow { > **Implementation Note:** `KFunction` provides `.name` for the method name and `.parameters[0].type` for the declaring interface. This metadata is used to make Temporal activity calls by name. -## Next Steps +## Related -- [Activity Implementation](./implementation.md) - Implementing activity classes - [Local Activities](./local-activities.md) - Short-lived local activities +- [Workflows](../workflows/) - Calling activities from workflows + +--- + +**Next:** [Activity Implementation](./implementation.md) diff --git a/kotlin/activities/implementation.md b/kotlin/activities/implementation.md index 01e4a7d..a0c4cbb 100644 --- a/kotlin/activities/implementation.md +++ b/kotlin/activities/implementation.md @@ -149,7 +149,11 @@ override suspend fun resumableProcess(data: List): ProcessResult { } ``` -## Next Steps +## Related -- [Local Activities](./local-activities.md) - Short-lived activities - [Activity Definition](./definition.md) - Interface patterns +- [Worker Setup](../worker/setup.md) - Registering activities + +--- + +**Next:** [Local Activities](./local-activities.md) diff --git a/kotlin/activities/local-activities.md b/kotlin/activities/local-activities.md index e1ab133..9ec5668 100644 --- a/kotlin/activities/local-activities.md +++ b/kotlin/activities/local-activities.md @@ -137,7 +137,11 @@ interface KActivityInfo { > **Note:** `ActivityCompletionClient` for async activity completion uses the same API as the Java SDK. -## Next Steps +## Related - [Activity Definition](./definition.md) - Interface patterns - [Activity Implementation](./implementation.md) - Full implementation details + +--- + +**Next:** [Client](../client/) diff --git a/kotlin/api-parity.md b/kotlin/api-parity.md index bef4cfd..d1ad42b 100644 --- a/kotlin/api-parity.md +++ b/kotlin/api-parity.md @@ -124,7 +124,8 @@ The following Java SDK workflow APIs have Kotlin equivalents in `KWorkflow`: | `currentAttemptScheduledTimestamp` | Missing - current attempt schedule time | | `retryOptions` | Missing - activity retry options | -## Next Steps +## Related - [Migration Guide](./migration.md) - Practical migration steps - [Kotlin Idioms](./kotlin-idioms.md) - Kotlin-specific patterns +- [README](./README.md) - Back to documentation home diff --git a/kotlin/client/README.md b/kotlin/client/README.md index 420e3da..cf0b076 100644 --- a/kotlin/client/README.md +++ b/kotlin/client/README.md @@ -80,8 +80,11 @@ handle.cancel() | Signal with start | `client.signalWithStart(...)` | | Update with start | `client.executeUpdateWithStart(...)` | -## Next Steps +## Related -- Start with [Workflow Client](./workflow-client.md) for client creation and basic operations -- Learn about [Workflow Handles](./workflow-handle.md) for interacting with running workflows -- See [Advanced Operations](./advanced.md) for atomic operations +- [Workflow Handles](./workflow-handle.md) - Interacting with running workflows +- [Advanced Operations](./advanced.md) - Atomic operations + +--- + +**Next:** [Workflow Client](./workflow-client.md) diff --git a/kotlin/client/advanced.md b/kotlin/client/advanced.md index 2f7f738..2ad167c 100644 --- a/kotlin/client/advanced.md +++ b/kotlin/client/advanced.md @@ -188,7 +188,11 @@ class KWorkflowClient { } ``` -## Next Steps +## Related - [Workflow Client](./workflow-client.md) - Basic client operations - [Workflow Handles](./workflow-handle.md) - Interacting with workflows + +--- + +**Next:** [Worker](../worker/) diff --git a/kotlin/client/workflow-client.md b/kotlin/client/workflow-client.md index c196296..112f22e 100644 --- a/kotlin/client/workflow-client.md +++ b/kotlin/client/workflow-client.md @@ -138,7 +138,11 @@ data class KWorkflowOptions( ) ``` -## Next Steps +## Related -- [Workflow Handles](./workflow-handle.md) - Interacting with workflows - [Advanced Operations](./advanced.md) - SignalWithStart, UpdateWithStart +- [KOptions](../configuration/koptions.md) - KWorkflowOptions reference + +--- + +**Next:** [Workflow Handles](./workflow-handle.md) diff --git a/kotlin/client/workflow-handle.md b/kotlin/client/workflow-handle.md index 8e14138..ed3877e 100644 --- a/kotlin/client/workflow-handle.md +++ b/kotlin/client/workflow-handle.md @@ -150,7 +150,11 @@ interface WorkflowHandle { This pattern matches Python SDK's `WorkflowHandle` with the same method names (`signal`, `query`, `result`, `cancel`, `terminate`, `execute_update`, `start_update`). -## Next Steps +## Related -- [Advanced Operations](./advanced.md) - SignalWithStart, UpdateWithStart - [Workflow Client](./workflow-client.md) - Creating clients +- [Signals, Queries & Updates](../workflows/signals-queries.md) - Handler definitions + +--- + +**Next:** [Advanced Operations](./advanced.md) diff --git a/kotlin/configuration/README.md b/kotlin/configuration/README.md index 022d092..d2bd78f 100644 --- a/kotlin/configuration/README.md +++ b/kotlin/configuration/README.md @@ -62,8 +62,12 @@ val converter = DefaultDataConverter.newDefaultInstance().withPayloadConverterOv ) ``` -## Next Steps +## Related - [KOptions](./koptions.md) - Full options reference - [Data Conversion](./data-conversion.md) - Serialization configuration - [Interceptors](./interceptors.md) - Cross-cutting concerns + +--- + +**Next:** [KOptions](./koptions.md) diff --git a/kotlin/configuration/data-conversion.md b/kotlin/configuration/data-conversion.md index b0b8c90..cf4d874 100644 --- a/kotlin/configuration/data-conversion.md +++ b/kotlin/configuration/data-conversion.md @@ -118,7 +118,11 @@ val converter = DefaultDataConverter.newDefaultInstance().withPayloadConverterOv - **Mixed Java/Kotlin**: Consider Jackson for shared data types - **Existing Jackson infrastructure**: Use Jackson for consistency -## Next Steps +## Related - [KOptions](./koptions.md) - Configuration options - [Interceptors](./interceptors.md) - Cross-cutting concerns + +--- + +**Next:** [Interceptors](./interceptors.md) diff --git a/kotlin/configuration/interceptors.md b/kotlin/configuration/interceptors.md index 887bb41..1a2f47a 100644 --- a/kotlin/configuration/interceptors.md +++ b/kotlin/configuration/interceptors.md @@ -527,7 +527,11 @@ class AuthInterceptor( } ``` -## Next Steps +## Related - [KOptions](./koptions.md) - Configuration options - [Worker Setup](../worker/setup.md) - Registering interceptors + +--- + +**Next:** [Workflows](../workflows/) diff --git a/kotlin/configuration/koptions.md b/kotlin/configuration/koptions.md index a25fc21..bc8a091 100644 --- a/kotlin/configuration/koptions.md +++ b/kotlin/configuration/koptions.md @@ -220,7 +220,11 @@ The Kotlin SDK provides two approaches for configuring options: > **Implementation Note:** KOptions classes internally convert to Java SDK options. The conversion happens once when the activity/workflow is scheduled, so there's no runtime overhead during workflow execution. -## Next Steps +## Related - [Data Conversion](./data-conversion.md) - Serialization configuration - [Interceptors](./interceptors.md) - Cross-cutting concerns + +--- + +**Next:** [Data Conversion](./data-conversion.md) diff --git a/kotlin/kotlin-idioms.md b/kotlin/kotlin-idioms.md index baef0d8..cab2d35 100644 --- a/kotlin/kotlin-idioms.md +++ b/kotlin/kotlin-idioms.md @@ -85,8 +85,12 @@ val status = handle.query(OrderWorkflow::status) val count = handle.query(OrderWorkflow::getItemCount) ``` -## Next Steps +## Related - [KOptions Classes](./configuration/koptions.md) - Kotlin-native configuration - [Workflow Definition](./workflows/definition.md) - Using these idioms in workflows - [Activity Definition](./activities/definition.md) - Using these idioms in activities + +--- + +**Next:** [Configuration](./configuration/) diff --git a/kotlin/migration.md b/kotlin/migration.md index d832245..d3d1a84 100644 --- a/kotlin/migration.md +++ b/kotlin/migration.md @@ -119,7 +119,11 @@ worker.registerWorkflowImplementationTypes( factory.start() ``` -## Next Steps +## Related -- [API Parity](./api-parity.md) - Full Java SDK comparison - [Kotlin Idioms](./kotlin-idioms.md) - Kotlin-specific patterns +- [Worker Setup](./worker/setup.md) - Mixed Java/Kotlin workers + +--- + +**Next:** [API Parity](./api-parity.md) diff --git a/kotlin/worker/README.md b/kotlin/worker/README.md index abff341..6c6ac1a 100644 --- a/kotlin/worker/README.md +++ b/kotlin/worker/README.md @@ -40,7 +40,12 @@ factory.start() | `KWorker` | Registers Kotlin workflows and suspend activities | | `KotlinPlugin` | Plugin for Java main apps (used automatically by KWorkerFactory) | -## Next Steps +## Related -- See [Worker Setup](./setup.md) for detailed configuration options -- Learn about [Interceptors](../configuration/interceptors.md) for cross-cutting concerns +- [Interceptors](../configuration/interceptors.md) - Cross-cutting concerns +- [Workflows](../workflows/) - Defining workflows to register +- [Activities](../activities/) - Defining activities to register + +--- + +**Next:** [Worker Setup](./setup.md) diff --git a/kotlin/worker/setup.md b/kotlin/worker/setup.md index 31b6fe4..27e28fa 100644 --- a/kotlin/worker/setup.md +++ b/kotlin/worker/setup.md @@ -166,8 +166,12 @@ fun main() = runBlocking { } ``` -## Next Steps +## Related - [Interceptors](../configuration/interceptors.md) - Adding cross-cutting concerns - [Workflows](../workflows/) - Defining workflows to register - [Activities](../activities/) - Defining activities to register + +--- + +**Next:** [Migration Guide](../migration.md) diff --git a/kotlin/workflows/README.md b/kotlin/workflows/README.md index 9d4a768..74e9d18 100644 --- a/kotlin/workflows/README.md +++ b/kotlin/workflows/README.md @@ -275,8 +275,11 @@ class OrderWorkflowImpl : OrderWorkflow { } ``` -## Next Steps +## Related -- Start with [Workflow Definition](./definition.md) to understand the basic patterns -- Learn about [Activities](../activities/) that workflows orchestrate -- See [Client](../client/) for starting and interacting with workflows +- [Activities](../activities/) - What workflows orchestrate +- [Client](../client/) - Starting and interacting with workflows + +--- + +**Next:** [Workflow Definition](./definition.md) diff --git a/kotlin/workflows/cancellation.md b/kotlin/workflows/cancellation.md index 93fe2fb..3e5aa95 100644 --- a/kotlin/workflows/cancellation.md +++ b/kotlin/workflows/cancellation.md @@ -141,7 +141,11 @@ class OrderWorkflowImpl : OrderWorkflow { | `CancellationScope.throwCanceled()` | `ensureActive()` | | `scope.run()` with timeout | `withTimeout(duration) { ... }` | -## Next Steps +## Related -- [Continue-As-New](./continue-as-new.md) - Long-running workflow patterns - [Timers & Parallel](./timers-parallel.md) - Timeout patterns +- [Child Workflows](./child-workflows.md) - Cancellation propagation to children + +--- + +**Next:** [Continue-As-New](./continue-as-new.md) diff --git a/kotlin/workflows/child-workflows.md b/kotlin/workflows/child-workflows.md index 2b4bab5..1f3dba3 100644 --- a/kotlin/workflows/child-workflows.md +++ b/kotlin/workflows/child-workflows.md @@ -220,7 +220,11 @@ val result = KWorkflow.executeChildWorkflow( > **Note:** For simple parallel execution where you only need the result, use standard `coroutineScope { async { executeChildWorkflow(...) } }`. Use `startChildWorkflow` only when you need to interact with the child workflow while it's running. -## Next Steps +## Related -- [Timers & Parallel Execution](./timers-parallel.md) - More parallel patterns - [Cancellation](./cancellation.md) - How cancellation propagates to child workflows +- [Workflow Definition](./definition.md) - Basic workflow patterns + +--- + +**Next:** [Timers & Parallel Execution](./timers-parallel.md) diff --git a/kotlin/workflows/continue-as-new.md b/kotlin/workflows/continue-as-new.md index 773de28..12e7900 100644 --- a/kotlin/workflows/continue-as-new.md +++ b/kotlin/workflows/continue-as-new.md @@ -141,7 +141,11 @@ object KWorkflow { > **Important:** `continueAsNew` never returns normally. It terminates the current workflow execution and signals Temporal to start a new execution. The return type `Nothing` indicates this in Kotlin's type system. -## Next Steps +## Related -- [Workflow Definition](./definition.md) - Back to basics +- [Workflow Definition](./definition.md) - Basic workflow patterns - [Migration Guide](../migration.md) - Java SDK comparison + +--- + +**Next:** [Activities](../activities/) diff --git a/kotlin/workflows/definition.md b/kotlin/workflows/definition.md index ab6511d..3fec996 100644 --- a/kotlin/workflows/definition.md +++ b/kotlin/workflows/definition.md @@ -96,8 +96,11 @@ val classLog = KWorkflow.logger(MyWorkflowImpl::class.java) > **Note:** Versioning (`KWorkflow.getVersion`), side effects (`KWorkflow.sideEffect`), and search attributes (`KWorkflow.upsertSearchAttributes`) use the same patterns as the Java SDK. -## Next Steps +## Related -- [Signals, Queries & Updates](./signals-queries.md) - Communication patterns - [Child Workflows](./child-workflows.md) - Orchestrating child workflows - [Timers & Parallel Execution](./timers-parallel.md) - Delays and async patterns + +--- + +**Next:** [Signals, Queries & Updates](./signals-queries.md) diff --git a/kotlin/workflows/signals-queries.md b/kotlin/workflows/signals-queries.md index c5840ed..6335160 100644 --- a/kotlin/workflows/signals-queries.md +++ b/kotlin/workflows/signals-queries.md @@ -140,7 +140,11 @@ suspend fun addItem(item: OrderItem): Boolean { } ``` -## Next Steps +## Related -- [Child Workflows](./child-workflows.md) - Orchestrating child workflows - [Client API](../client/workflow-handle.md) - More on workflow handles +- [Workflow Definition](./definition.md) - Basic workflow patterns + +--- + +**Next:** [Child Workflows](./child-workflows.md) diff --git a/kotlin/workflows/timers-parallel.md b/kotlin/workflows/timers-parallel.md index b9c1668..b81812d 100644 --- a/kotlin/workflows/timers-parallel.md +++ b/kotlin/workflows/timers-parallel.md @@ -115,7 +115,11 @@ override suspend fun raceOperations(): String = coroutineScope { } ``` -## Next Steps +## Related -- [Cancellation](./cancellation.md) - How cancellation works with parallel execution - [Child Workflows](./child-workflows.md) - Parallel child workflow patterns +- [Workflow Definition](./definition.md) - Basic workflow patterns + +--- + +**Next:** [Cancellation](./cancellation.md) From d713d0c6ced4adaf1feea6a4df8fc7ad9864a68a Mon Sep 17 00:00:00 2001 From: Maxim Fateev Date: Mon, 5 Jan 2026 12:15:44 -0800 Subject: [PATCH 28/83] Add Start Review link to README as entry point for linear navigation --- kotlin/README.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/kotlin/README.md b/kotlin/README.md index 5b8e67a..c452132 100644 --- a/kotlin/README.md +++ b/kotlin/README.md @@ -117,3 +117,7 @@ val result = client.executeWorkflow( ``` See the [Complete Example](./workflows/README.md#complete-example) for a full order processing workflow demonstrating all features. + +--- + +**[Start Review →](./kotlin-idioms.md)** From 9ae3ffb6a49be1941113e70a27fbee77d51e528c Mon Sep 17 00:00:00 2001 From: Maxim Fateev Date: Mon, 5 Jan 2026 12:17:23 -0800 Subject: [PATCH 29/83] Expand kotlin-idioms.md with suspend functions, concurrency, cancellation, timeouts Added sections for: - Suspend functions (workflow and activity methods) - Coroutines and concurrency (async, awaitAll, coroutineScope) - Cancellation patterns (CancellationException, NonCancellable) - Timeouts (withTimeout, withTimeoutOrNull) - Mapping tables showing Kotlin patterns to Temporal/Java equivalents --- kotlin/kotlin-idioms.md | 138 ++++++++++++++++++++++++++++++++++++---- 1 file changed, 126 insertions(+), 12 deletions(-) diff --git a/kotlin/kotlin-idioms.md b/kotlin/kotlin-idioms.md index cab2d35..c979ab7 100644 --- a/kotlin/kotlin-idioms.md +++ b/kotlin/kotlin-idioms.md @@ -1,6 +1,123 @@ # Kotlin Idioms -The SDK leverages Kotlin-specific language features for an idiomatic experience. +The SDK leverages Kotlin-specific language features for an idiomatic experience. The design principle is to use **standard Kotlin patterns** wherever possible instead of custom APIs. + +## Suspend Functions + +Workflows and activities use `suspend fun` for natural coroutine integration: + +```kotlin +// Workflow interface - suspend methods +@WorkflowInterface +interface OrderWorkflow { + @WorkflowMethod + suspend fun processOrder(order: Order): OrderResult + + @SignalMethod + suspend fun updatePriority(priority: Priority) + + @UpdateMethod + suspend fun addItem(item: OrderItem): Boolean + + @QueryMethod // Queries are NOT suspend - they must be synchronous + val status: OrderStatus +} + +// Activity interface - suspend methods +@ActivityInterface +interface OrderActivities { + @ActivityMethod + suspend fun validateOrder(order: Order): Boolean + + @ActivityMethod + suspend fun chargePayment(order: Order): PaymentResult +} +``` + +> **Note:** Query methods are never `suspend` because queries must return immediately without blocking. + +## Coroutines and Concurrency + +Use standard `kotlinx.coroutines` patterns for parallel execution: + +```kotlin +override suspend fun processOrder(order: Order): OrderResult = coroutineScope { + // Parallel execution using standard async + val validationDeferred = async { + KWorkflow.executeActivity(OrderActivities::validateOrder, options, order) + } + val inventoryDeferred = async { + KWorkflow.executeActivity(OrderActivities::checkInventory, options, order) + } + + // Wait for all - standard awaitAll + val (isValid, hasInventory) = awaitAll(validationDeferred, inventoryDeferred) + + if (!isValid || !hasInventory) { + return@coroutineScope OrderResult(success = false) + } + + // Sequential execution + val charged = KWorkflow.executeActivity(OrderActivities::chargePayment, options, order) + val shipped = KWorkflow.executeActivity(OrderActivities::shipOrder, options, order) + + OrderResult(success = true, trackingNumber = shipped) +} +``` + +| Kotlin Pattern | Temporal Equivalent | +|----------------|---------------------| +| `coroutineScope { async { } }` | Parallel execution | +| `awaitAll(d1, d2)` | Wait for multiple operations | +| `delay(duration)` | Temporal timer (deterministic) | +| `Deferred` | `Promise.toDeferred()` | +| `CompletableDeferred` | `Workflow.newPromise()` | + +## Cancellation + +Use standard Kotlin cancellation patterns: + +```kotlin +override suspend fun processOrder(order: Order): OrderResult { + return try { + doProcessOrder(order) + } catch (e: CancellationException) { + // Workflow was cancelled - run cleanup in non-cancellable context + withContext(NonCancellable) { + KWorkflow.executeActivity(OrderActivities::releaseInventory, options, order) + KWorkflow.executeActivity(OrderActivities::refundPayment, options, order) + } + throw e // Re-throw to propagate cancellation + } +} +``` + +| Kotlin Pattern | Java SDK Equivalent | +|----------------|---------------------| +| `try { } catch (e: CancellationException)` | `CancellationScope` callback | +| `withContext(NonCancellable) { }` | `Workflow.newDetachedCancellationScope()` | +| `coroutineScope { }` | `Workflow.newCancellationScope()` | +| `isActive` / `ensureActive()` | `CancellationScope.isCancelRequested()` | + +## Timeouts + +Use `withTimeout` for deadline-based cancellation: + +```kotlin +override suspend fun processWithDeadline(order: Order): OrderResult { + return withTimeout(1.hours) { + // Everything here is cancelled if it takes > 1 hour + KWorkflow.executeActivity(OrderActivities::validateOrder, options, order) + KWorkflow.executeActivity(OrderActivities::chargePayment, options, order) + OrderResult(success = true) + } +} + +// Or use withTimeoutOrNull to get null instead of exception +val result = withTimeoutOrNull(30.minutes) { + KWorkflow.executeActivity(OrderActivities::slowOperation, options, data) +} +``` ## Kotlin Duration @@ -11,22 +128,19 @@ import kotlin.time.Duration.Companion.seconds import kotlin.time.Duration.Companion.minutes import kotlin.time.Duration.Companion.hours -// Activity with timeouts using KOptions -val result = KWorkflow.executeActivity( - "ProcessOrder", - KActivityOptions( - startToCloseTimeout = 30.seconds, - scheduleToCloseTimeout = 5.minutes, - heartbeatTimeout = 10.seconds - ), - orderData +// Activity options with Duration +val options = KActivityOptions( + startToCloseTimeout = 30.seconds, + scheduleToCloseTimeout = 5.minutes, + heartbeatTimeout = 10.seconds ) // Timers - standard kotlinx.coroutines delay (intercepted for determinism) delay(1.hours) -``` -> **Note:** The `Delay` interface intercepts standard `delay()` calls and routes them through Temporal's deterministic timer. +// Timeouts +withTimeout(30.minutes) { ... } +``` ## Null Safety From 917baaae72096798dcb8c80f392417c5ac995288 Mon Sep 17 00:00:00 2001 From: Maxim Fateev Date: Mon, 5 Jan 2026 12:22:01 -0800 Subject: [PATCH 30/83] Improve kotlin-idioms.md flow and content MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Add opening summary table of Java→Kotlin mappings - Streamline code examples (less verbose) - Improve table descriptions (Purpose instead of Temporal Equivalent) - Add Data Classes section for DTOs with @Serializable - Remove redundant Property Syntax section (already in Suspend Functions) - Clean up Null Safety section (remove unrelated type mappings) --- kotlin/kotlin-idioms.md | 113 ++++++++++++++++++---------------------- 1 file changed, 50 insertions(+), 63 deletions(-) diff --git a/kotlin/kotlin-idioms.md b/kotlin/kotlin-idioms.md index c979ab7..09b42dd 100644 --- a/kotlin/kotlin-idioms.md +++ b/kotlin/kotlin-idioms.md @@ -1,13 +1,22 @@ # Kotlin Idioms -The SDK leverages Kotlin-specific language features for an idiomatic experience. The design principle is to use **standard Kotlin patterns** wherever possible instead of custom APIs. +The Kotlin SDK uses **standard Kotlin patterns** wherever possible instead of custom APIs. + +| Java SDK | Kotlin SDK | +|----------|------------| +| `void method()` | `suspend fun method()` | +| `Async.function(() -> ...)` | `async { ... }` | +| `Promise.allOf(...).get()` | `awaitAll(d1, d2)` | +| `Workflow.sleep(duration)` | `delay(duration)` | +| `Workflow.newDetachedCancellationScope()` | `withContext(NonCancellable)` | +| `Duration.ofSeconds(30)` | `30.seconds` | +| `Optional` | `T?` | ## Suspend Functions Workflows and activities use `suspend fun` for natural coroutine integration: ```kotlin -// Workflow interface - suspend methods @WorkflowInterface interface OrderWorkflow { @WorkflowMethod @@ -19,11 +28,10 @@ interface OrderWorkflow { @UpdateMethod suspend fun addItem(item: OrderItem): Boolean - @QueryMethod // Queries are NOT suspend - they must be synchronous + @QueryMethod // Queries are NOT suspend - must be synchronous val status: OrderStatus } -// Activity interface - suspend methods @ActivityInterface interface OrderActivities { @ActivityMethod @@ -43,15 +51,11 @@ Use standard `kotlinx.coroutines` patterns for parallel execution: ```kotlin override suspend fun processOrder(order: Order): OrderResult = coroutineScope { // Parallel execution using standard async - val validationDeferred = async { - KWorkflow.executeActivity(OrderActivities::validateOrder, options, order) - } - val inventoryDeferred = async { - KWorkflow.executeActivity(OrderActivities::checkInventory, options, order) - } + val validation = async { KWorkflow.executeActivity(OrderActivities::validateOrder, options, order) } + val inventory = async { KWorkflow.executeActivity(OrderActivities::checkInventory, options, order) } // Wait for all - standard awaitAll - val (isValid, hasInventory) = awaitAll(validationDeferred, inventoryDeferred) + val (isValid, hasInventory) = awaitAll(validation, inventory) if (!isValid || !hasInventory) { return@coroutineScope OrderResult(success = false) @@ -65,13 +69,12 @@ override suspend fun processOrder(order: Order): OrderResult = coroutineScope { } ``` -| Kotlin Pattern | Temporal Equivalent | -|----------------|---------------------| -| `coroutineScope { async { } }` | Parallel execution | -| `awaitAll(d1, d2)` | Wait for multiple operations | +| Kotlin Pattern | Purpose | +|----------------|---------| +| `coroutineScope { }` | Structured concurrency - if one fails, all cancel | +| `async { }` | Start parallel operation, returns `Deferred` | +| `awaitAll(d1, d2)` | Wait for multiple deferreds | | `delay(duration)` | Temporal timer (deterministic) | -| `Deferred` | `Promise.toDeferred()` | -| `CompletableDeferred` | `Workflow.newPromise()` | ## Cancellation @@ -94,7 +97,7 @@ override suspend fun processOrder(order: Order): OrderResult { | Kotlin Pattern | Java SDK Equivalent | |----------------|---------------------| -| `try { } catch (e: CancellationException)` | `CancellationScope` callback | +| `catch (e: CancellationException)` | `CancellationScope` failure callback | | `withContext(NonCancellable) { }` | `Workflow.newDetachedCancellationScope()` | | `coroutineScope { }` | `Workflow.newCancellationScope()` | | `isActive` / `ensureActive()` | `CancellationScope.isCancelRequested()` | @@ -106,14 +109,14 @@ Use `withTimeout` for deadline-based cancellation: ```kotlin override suspend fun processWithDeadline(order: Order): OrderResult { return withTimeout(1.hours) { - // Everything here is cancelled if it takes > 1 hour + // Everything here cancels if it takes > 1 hour KWorkflow.executeActivity(OrderActivities::validateOrder, options, order) KWorkflow.executeActivity(OrderActivities::chargePayment, options, order) OrderResult(success = true) } } -// Or use withTimeoutOrNull to get null instead of exception +// Or get null instead of exception val result = withTimeoutOrNull(30.minutes) { KWorkflow.executeActivity(OrderActivities::slowOperation, options, data) } @@ -128,77 +131,61 @@ import kotlin.time.Duration.Companion.seconds import kotlin.time.Duration.Companion.minutes import kotlin.time.Duration.Companion.hours -// Activity options with Duration val options = KActivityOptions( startToCloseTimeout = 30.seconds, scheduleToCloseTimeout = 5.minutes, heartbeatTimeout = 10.seconds ) -// Timers - standard kotlinx.coroutines delay (intercepted for determinism) delay(1.hours) - -// Timeouts withTimeout(30.minutes) { ... } ``` ## Null Safety -Nullable types replace `Optional` throughout the API. The following Java SDK types have Kotlin equivalents with null-safe APIs: - -| Java SDK | Kotlin SDK | -|----------|------------| -| `io.temporal.workflow.Workflow` | `KWorkflow` object | -| `io.temporal.workflow.WorkflowInfo` | `KWorkflowInfo` | -| `io.temporal.workflow.Promise` | Standard `Deferred` via `Promise.toDeferred()` | -| `io.temporal.activity.Activity` | `KActivity` object | -| `io.temporal.activity.ActivityExecutionContext` | `KActivityContext` | -| `io.temporal.activity.ActivityInfo` | `KActivityInfo` | -| `io.temporal.client.WorkflowStub` | `KWorkflowHandle` / `KTypedWorkflowHandle` | +Nullable types replace `Optional`: ```kotlin -// KWorkflowInfo - nullable instead of Optional -interface KWorkflowInfo { - val workflowId: String - val runId: String - val parentWorkflowId: String? // Optional in Java - val parentRunId: String? // Optional in Java - fun getMemo(key: String): String? // Optional in Java - fun getSearchAttribute(key: String): Any? - // ... other properties -} - -// Access via KWorkflow object -val info: KWorkflowInfo = KWorkflow.getInfo() +// KWorkflowInfo uses nullable instead of Optional +val info = KWorkflow.getInfo() val parentId: String? = info.parentWorkflowId // null if no parent -// Activity heartbeat details - nullable instead of Optional +// Activity heartbeat details val progress: Int? = KActivity.getContext().getHeartbeatDetails() -val startIndex = progress ?: 0 // Kotlin's elvis operator +val startIndex = progress ?: 0 // Elvis operator for default ``` This eliminates `.orElse(null)`, `.isPresent`, and other Optional ceremony. -## Property Syntax for Queries +## Data Classes -Queries can be defined as Kotlin properties in the workflow interface: +Use Kotlin data classes with `@Serializable` for workflow inputs/outputs: ```kotlin -@WorkflowInterface -interface OrderWorkflow { - @QueryMethod - val status: OrderStatus // Property syntax +@Serializable +data class Order( + val id: String, + val customerId: String, + val items: List, + val priority: Priority = Priority.NORMAL +) - @QueryMethod - fun getItemCount(): Int // Method syntax -} +@Serializable +data class OrderResult( + val success: Boolean, + val trackingNumber: String? = null, + val errorMessage: String? = null +) -// Client usage via typed handle (using KWorkflowClient) -val handle = client.getWorkflowHandle("order-123") -val status = handle.query(OrderWorkflow::status) -val count = handle.query(OrderWorkflow::getItemCount) +@Serializable +enum class Priority { LOW, NORMAL, HIGH } ``` +Data classes provide: +- Automatic `equals()`, `hashCode()`, `toString()` +- `copy()` for creating modified instances +- Destructuring: `val (id, customerId) = order` + ## Related - [KOptions Classes](./configuration/koptions.md) - Kotlin-native configuration From 901cf2b360b4516fe2d55a9c98d972d3ef587794 Mon Sep 17 00:00:00 2001 From: Maxim Fateev Date: Mon, 5 Jan 2026 12:25:16 -0800 Subject: [PATCH 31/83] Replace directory links with README.md links MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit All links to directories now point to README.md files: - ./configuration/ → ./configuration/README.md - ../workflows/ → ../workflows/README.md - etc. --- .claude/skills/implement-change.md | 42 ++++++++++++++++++ .claude/skills/implement.md | 62 ++++++++++++++++++++++++++ .claude/skills/plan-change.md | 40 +++++++++++++++++ .claude/skills/plan-phase.md | 63 +++++++++++++++++++++++++++ .idea/.gitignore | 8 ++++ .idea/google-java-format.xml | 6 +++ .idea/misc.xml | 6 +++ .idea/modules.xml | 8 ++++ .idea/proposals:kotlin-sdk.iml | 9 ++++ .idea/vcs.xml | 6 +++ kotlin/README.md | 14 +++--- kotlin/activities/definition.md | 2 +- kotlin/activities/local-activities.md | 2 +- kotlin/client/advanced.md | 2 +- kotlin/configuration/interceptors.md | 2 +- kotlin/kotlin-idioms.md | 2 +- kotlin/worker/README.md | 4 +- kotlin/worker/setup.md | 4 +- kotlin/workflows/README.md | 4 +- kotlin/workflows/continue-as-new.md | 2 +- 20 files changed, 269 insertions(+), 19 deletions(-) create mode 100644 .claude/skills/implement-change.md create mode 100644 .claude/skills/implement.md create mode 100644 .claude/skills/plan-change.md create mode 100644 .claude/skills/plan-phase.md create mode 100644 .idea/.gitignore create mode 100644 .idea/google-java-format.xml create mode 100644 .idea/misc.xml create mode 100644 .idea/modules.xml create mode 100644 .idea/proposals:kotlin-sdk.iml create mode 100644 .idea/vcs.xml diff --git a/.claude/skills/implement-change.md b/.claude/skills/implement-change.md new file mode 100644 index 0000000..57e06e0 --- /dev/null +++ b/.claude/skills/implement-change.md @@ -0,0 +1,42 @@ +# Implement Change Skill + +Implement a change based on a detailed plan from the change-planner agent. + +## Usage + +``` +/implement-change . +``` + +Examples: +- `/implement-change 1.1.1` - Implement Phase 1.1, Change 1 +- `/implement-change 2.3.5` - Implement Phase 2.3, Change 5 + +## Behavior + +1. Read the change plan from `kotlin/phases/changes/phase-X.Y-change-N.md` +2. Verify dependencies are met +3. Create new files as specified +4. Modify existing files as specified +5. Implement all unit tests +6. Verify code compiles and tests pass +7. Report summary of changes + +## Prerequisites + +The change plan must exist. Run `/plan-change X.Y.N` first if needed. + +## Output + +- Production code files (created/modified) +- Test files +- Summary of what was done +- Any deviations from plan with justification + +## Quality Gates + +The agent will verify: +- Code compiles without warnings +- All new tests pass +- Existing tests still pass +- Acceptance criteria from plan are met diff --git a/.claude/skills/implement.md b/.claude/skills/implement.md new file mode 100644 index 0000000..7d28f82 --- /dev/null +++ b/.claude/skills/implement.md @@ -0,0 +1,62 @@ +# Implement Skill + +Fully implement a change from a phase plan, orchestrating planning and implementation. + +## Usage + +``` +/implement . +``` + +Examples: +- `/implement 1.1.1` - Implement Phase 1.1, Change 1 +- `/implement 2.3.5` - Implement Phase 2.3, Change 5 + +## Behavior + +This skill orchestrates the full implementation workflow: + +1. **Validate** - Check dependencies are met +2. **Plan** - Generate detailed implementation plan (change-planner) +3. **Review** - Verify plan is feasible and complete +4. **Implement** - Write code and tests (change-implementer) +5. **Verify** - Ensure compilation and tests pass +6. **Retry** - Replan or fix if issues encountered + +## Automatic Recovery + +The orchestrator handles failures automatically: + +| Issue | Action | +|-------|--------| +| Plan incomplete | Replan with feedback (up to 2x) | +| Compilation error | Fix and retry (up to 3x) | +| Test failure | Diagnose and fix or replan | +| Blocking issue | Stop and report | + +## Output + +- Completed implementation with passing tests +- Change plan document at `kotlin/phases/changes/phase-X.Y-change-N.md` +- Summary of files created/modified +- Verification report + +## Prerequisites + +- Phase plan must exist at `kotlin/phases/phase-X.Y-detailed.md` +- Prior changes in the phase should be complete + +## Workflow Visualization + +``` +/implement 1.1.3 + │ + ▼ +┌─────────────┐ ┌─────────────┐ ┌─────────────┐ +│ Plan Change │ ──▶ │ Implement │ ──▶ │ Verify │ +│ │ │ │ │ │ +└─────────────┘ └─────────────┘ └─────────────┘ + ▲ │ │ + │ │ │ + └────── replan ──────┴─────── fix ────────┘ +``` diff --git a/.claude/skills/plan-change.md b/.claude/skills/plan-change.md new file mode 100644 index 0000000..661e979 --- /dev/null +++ b/.claude/skills/plan-change.md @@ -0,0 +1,40 @@ +# Plan Change Skill + +Create a detailed implementation plan for a single change from a phase plan. + +## Usage + +``` +/plan-change . +``` + +Examples: +- `/plan-change 1.1.1` - Plan Phase 1.1, Change 1 +- `/plan-change 2.3.5` - Plan Phase 2.3, Change 5 + +## Behavior + +1. Read the phase plan from `kotlin/phases/phase-X.Y-detailed.md` +2. Read SDK proposal docs and relevant source code +3. Validate the change is feasible +4. Design the simplest, most readable implementation +5. Specify complete test coverage +6. Write output to `kotlin/phases/changes/phase-X.Y-change-N.md` + +## Output Contents + +The implementation plan includes: + +- **Feasibility validation** - Confirm change is possible +- **File changes** - Exact files to create/modify with code skeletons +- **Test specifications** - Complete unit test definitions +- **Acceptance criteria** - Checklist for completion +- **Implementation notes** - Gotchas, patterns, references + +## Requirements + +The output must be **self-contained**. A developer should be able to implement the change using only: +- This document +- The SDK source code + +No need to consult proposal documents or other planning materials. diff --git a/.claude/skills/plan-phase.md b/.claude/skills/plan-phase.md new file mode 100644 index 0000000..2d25005 --- /dev/null +++ b/.claude/skills/plan-phase.md @@ -0,0 +1,63 @@ +# Plan Phase Skill + +Break down a phase of the Kotlin SDK implementation into detailed, self-contained changes. + +## Usage + +``` +/plan-phase +``` + +Examples: +- `/plan-phase 1.1` - Plan "Java SDK Refactoring" +- `/plan-phase 2.1` - Plan "Typed Activity Stubs" + +## Behavior + +1. Read the implementation plan from `kotlin/implementation-plan.md` +2. Read context from `kotlin/sdk-api.md` and `kotlin/sdk-implementation.md` +3. For the specified phase, produce a detailed breakdown where each item: + - Is a self-contained, submittable PR + - Includes full test coverage + - Has clear dependencies on prior items + - Can be reviewed and merged independently + +## Output Format + +Write the detailed plan to `kotlin/phases/phase-X.Y-detailed.md` with this structure: + +```markdown +# Phase X.Y: [Name] - Detailed Plan + +## Overview +Brief description of what this phase accomplishes. + +## Prerequisites +Any required setup or prior phases. + +## Changes + +### Change 1: [Title] +**Summary:** One-line description + +**Scope:** +- Files to add/modify +- Public API changes (if any) + +**Tests:** +- Unit tests required +- Integration tests required + +**Dependencies:** None | Change N + +--- +(repeat for each change) +``` + +## Guidelines + +- **Atomic**: Each change does ONE thing +- **Testable**: Every change includes tests +- **Ordered**: Dependencies flow forward only +- **Compilable**: Code compiles after each change +- **No Design**: Don't go into implementation details - just scope and structure diff --git a/.idea/.gitignore b/.idea/.gitignore new file mode 100644 index 0000000..13566b8 --- /dev/null +++ b/.idea/.gitignore @@ -0,0 +1,8 @@ +# Default ignored files +/shelf/ +/workspace.xml +# Editor-based HTTP Client requests +/httpRequests/ +# Datasource local storage ignored files +/dataSources/ +/dataSources.local.xml diff --git a/.idea/google-java-format.xml b/.idea/google-java-format.xml new file mode 100644 index 0000000..2aa056d --- /dev/null +++ b/.idea/google-java-format.xml @@ -0,0 +1,6 @@ + + + + + \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml new file mode 100644 index 0000000..eeb80f7 --- /dev/null +++ b/.idea/misc.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/.idea/modules.xml b/.idea/modules.xml new file mode 100644 index 0000000..03a1c1e --- /dev/null +++ b/.idea/modules.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/.idea/proposals:kotlin-sdk.iml b/.idea/proposals:kotlin-sdk.iml new file mode 100644 index 0000000..d6ebd48 --- /dev/null +++ b/.idea/proposals:kotlin-sdk.iml @@ -0,0 +1,9 @@ + + + + + + + + + \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml new file mode 100644 index 0000000..35eb1dd --- /dev/null +++ b/.idea/vcs.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/kotlin/README.md b/kotlin/README.md index c452132..5a725be 100644 --- a/kotlin/README.md +++ b/kotlin/README.md @@ -2,7 +2,7 @@ This document describes the public API and developer experience for the Temporal Kotlin SDK. -For implementation details, see [implementation/](./implementation/). +For implementation details, see [implementation/](./implementation/README.md). ## Overview @@ -45,11 +45,11 @@ This approach provides: ### Core Concepts - **[Kotlin Idioms](./kotlin-idioms.md)** - Duration, null safety, property syntax for queries -- **[Configuration](./configuration/)** - KOptions classes, data conversion, interceptors +- **[Configuration](./configuration/README.md)** - KOptions classes, data conversion, interceptors ### Building Blocks -- **[Workflows](./workflows/)** - Defining and implementing workflows +- **[Workflows](./workflows/README.md)** - Defining and implementing workflows - [Definition](./workflows/definition.md) - Interfaces, suspend methods, Java interop - [Signals, Queries & Updates](./workflows/signals-queries.md) - Communication patterns - [Child Workflows](./workflows/child-workflows.md) - Orchestrating child workflows @@ -57,19 +57,19 @@ This approach provides: - [Cancellation](./workflows/cancellation.md) - Handling cancellation, cleanup - [Continue-As-New](./workflows/continue-as-new.md) - Long-running workflow patterns -- **[Activities](./activities/)** - Defining and implementing activities +- **[Activities](./activities/README.md)** - Defining and implementing activities - [Definition](./activities/definition.md) - Interfaces, typed/string-based execution - [Implementation](./activities/implementation.md) - Suspend activities, heartbeating - [Local Activities](./activities/local-activities.md) - Short-lived local activities ### Infrastructure -- **[Client](./client/)** - Interacting with workflows +- **[Client](./client/README.md)** - Interacting with workflows - [Workflow Client](./client/workflow-client.md) - KWorkflowClient, starting workflows - [Workflow Handles](./client/workflow-handle.md) - Signals, queries, results - [Advanced Operations](./client/advanced.md) - SignalWithStart, UpdateWithStart -- **[Worker](./worker/)** - Running workflows and activities +- **[Worker](./worker/README.md)** - Running workflows and activities - [Setup](./worker/setup.md) - KWorkerFactory, KWorker, registration ### Reference @@ -79,7 +79,7 @@ This approach provides: ### Implementation -- **[Implementation Details](./implementation/)** - Internal design documents +- **[Implementation Details](./implementation/README.md)** - Internal design documents ## Quick Start diff --git a/kotlin/activities/definition.md b/kotlin/activities/definition.md index 084e168..e7aa4e8 100644 --- a/kotlin/activities/definition.md +++ b/kotlin/activities/definition.md @@ -166,7 +166,7 @@ object KWorkflow { ## Related - [Local Activities](./local-activities.md) - Short-lived local activities -- [Workflows](../workflows/) - Calling activities from workflows +- [Workflows](../workflows/README.md) - Calling activities from workflows --- diff --git a/kotlin/activities/local-activities.md b/kotlin/activities/local-activities.md index 9ec5668..8b5c9c9 100644 --- a/kotlin/activities/local-activities.md +++ b/kotlin/activities/local-activities.md @@ -144,4 +144,4 @@ interface KActivityInfo { --- -**Next:** [Client](../client/) +**Next:** [Client](../client/README.md) diff --git a/kotlin/client/advanced.md b/kotlin/client/advanced.md index 2ad167c..8cc7c37 100644 --- a/kotlin/client/advanced.md +++ b/kotlin/client/advanced.md @@ -195,4 +195,4 @@ class KWorkflowClient { --- -**Next:** [Worker](../worker/) +**Next:** [Worker](../worker/README.md) diff --git a/kotlin/configuration/interceptors.md b/kotlin/configuration/interceptors.md index 1a2f47a..7092adf 100644 --- a/kotlin/configuration/interceptors.md +++ b/kotlin/configuration/interceptors.md @@ -534,4 +534,4 @@ class AuthInterceptor( --- -**Next:** [Workflows](../workflows/) +**Next:** [Workflows](../workflows/README.md) diff --git a/kotlin/kotlin-idioms.md b/kotlin/kotlin-idioms.md index 09b42dd..faa5c27 100644 --- a/kotlin/kotlin-idioms.md +++ b/kotlin/kotlin-idioms.md @@ -194,4 +194,4 @@ Data classes provide: --- -**Next:** [Configuration](./configuration/) +**Next:** [Configuration](./configuration/README.md) diff --git a/kotlin/worker/README.md b/kotlin/worker/README.md index 6c6ac1a..4672fe9 100644 --- a/kotlin/worker/README.md +++ b/kotlin/worker/README.md @@ -43,8 +43,8 @@ factory.start() ## Related - [Interceptors](../configuration/interceptors.md) - Cross-cutting concerns -- [Workflows](../workflows/) - Defining workflows to register -- [Activities](../activities/) - Defining activities to register +- [Workflows](../workflows/README.md) - Defining workflows to register +- [Activities](../activities/README.md) - Defining activities to register --- diff --git a/kotlin/worker/setup.md b/kotlin/worker/setup.md index 27e28fa..435164a 100644 --- a/kotlin/worker/setup.md +++ b/kotlin/worker/setup.md @@ -169,8 +169,8 @@ fun main() = runBlocking { ## Related - [Interceptors](../configuration/interceptors.md) - Adding cross-cutting concerns -- [Workflows](../workflows/) - Defining workflows to register -- [Activities](../activities/) - Defining activities to register +- [Workflows](../workflows/README.md) - Defining workflows to register +- [Activities](../activities/README.md) - Defining activities to register --- diff --git a/kotlin/workflows/README.md b/kotlin/workflows/README.md index 74e9d18..9e45355 100644 --- a/kotlin/workflows/README.md +++ b/kotlin/workflows/README.md @@ -277,8 +277,8 @@ class OrderWorkflowImpl : OrderWorkflow { ## Related -- [Activities](../activities/) - What workflows orchestrate -- [Client](../client/) - Starting and interacting with workflows +- [Activities](../activities/README.md) - What workflows orchestrate +- [Client](../client/README.md) - Starting and interacting with workflows --- diff --git a/kotlin/workflows/continue-as-new.md b/kotlin/workflows/continue-as-new.md index 12e7900..d2ace02 100644 --- a/kotlin/workflows/continue-as-new.md +++ b/kotlin/workflows/continue-as-new.md @@ -148,4 +148,4 @@ object KWorkflow { --- -**Next:** [Activities](../activities/) +**Next:** [Activities](../activities/README.md) From 37af43dcea49742acbc153cb35cf61329f0e19cb Mon Sep 17 00:00:00 2001 From: Maxim Fateev Date: Mon, 5 Jan 2026 12:28:48 -0800 Subject: [PATCH 32/83] Remove implementation links from design review documents Implementation details are internal and not part of the API design review. --- kotlin/README.md | 6 ------ 1 file changed, 6 deletions(-) diff --git a/kotlin/README.md b/kotlin/README.md index 5a725be..c0c3353 100644 --- a/kotlin/README.md +++ b/kotlin/README.md @@ -2,8 +2,6 @@ This document describes the public API and developer experience for the Temporal Kotlin SDK. -For implementation details, see [implementation/](./implementation/README.md). - ## Overview The Kotlin SDK provides an idiomatic Kotlin experience for building Temporal workflows using coroutines and suspend functions. @@ -77,10 +75,6 @@ This approach provides: - **[Migration Guide](./migration.md)** - Migrating from Java SDK - **[API Parity](./api-parity.md)** - Java SDK comparison, gaps, not-needed APIs -### Implementation - -- **[Implementation Details](./implementation/README.md)** - Internal design documents - ## Quick Start ```kotlin From 72d707e78b8ea0ff75b4c428f4fb1a9af4d3f207 Mon Sep 17 00:00:00 2001 From: Maxim Fateev Date: Mon, 5 Jan 2026 12:31:28 -0800 Subject: [PATCH 33/83] Add activity definition and implementation to Quick Start example - Added GreetingActivities interface with @ActivityInterface - Added GreetingActivitiesImpl class - Added worker.registerActivitiesImplementations() call --- kotlin/README.md | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/kotlin/README.md b/kotlin/README.md index c0c3353..27f5b71 100644 --- a/kotlin/README.md +++ b/kotlin/README.md @@ -78,6 +78,20 @@ This approach provides: ## Quick Start ```kotlin +// Define activity interface +@ActivityInterface +interface GreetingActivities { + @ActivityMethod + suspend fun composeGreeting(greeting: String, name: String): String +} + +// Implement activity +class GreetingActivitiesImpl : GreetingActivities { + override suspend fun composeGreeting(greeting: String, name: String): String { + return "$greeting, $name!" + } +} + // Define workflow interface @WorkflowInterface interface GreetingWorkflow { @@ -100,6 +114,7 @@ class GreetingWorkflowImpl : GreetingWorkflow { val factory = KWorkerFactory(client) val worker = factory.newWorker("greetings") worker.registerWorkflowImplementationTypes(GreetingWorkflowImpl::class) +worker.registerActivitiesImplementations(GreetingActivitiesImpl()) factory.start() // Execute workflow From 1d282d7997f94397516061fcfbe45f8f6a9d3dda Mon Sep 17 00:00:00 2001 From: Maxim Fateev Date: Mon, 5 Jan 2026 12:40:59 -0800 Subject: [PATCH 34/83] Remove Complete Example reference from README --- kotlin/README.md | 2 -- 1 file changed, 2 deletions(-) diff --git a/kotlin/README.md b/kotlin/README.md index 27f5b71..a01c38b 100644 --- a/kotlin/README.md +++ b/kotlin/README.md @@ -125,8 +125,6 @@ val result = client.executeWorkflow( ) ``` -See the [Complete Example](./workflows/README.md#complete-example) for a full order processing workflow demonstrating all features. - --- **[Start Review →](./kotlin-idioms.md)** From 5fef37f0c9840b18f2ff8ebc83590d19d19ea5d5 Mon Sep 17 00:00:00 2001 From: Maxim Fateev Date: Mon, 5 Jan 2026 12:46:18 -0800 Subject: [PATCH 35/83] Remove Complete Example section from workflows/README.md --- kotlin/workflows/README.md | 225 ------------------------------------- 1 file changed, 225 deletions(-) diff --git a/kotlin/workflows/README.md b/kotlin/workflows/README.md index 9e45355..37cf645 100644 --- a/kotlin/workflows/README.md +++ b/kotlin/workflows/README.md @@ -50,231 +50,6 @@ class GreetingWorkflowImpl : GreetingWorkflow { | Parallel execution | `coroutineScope { async { ... } }.awaitAll()` | | Cancellation cleanup | `withContext(NonCancellable) { ... }` | -## Complete Example - -This example demonstrates a full order processing workflow with activities, cancellation handling, updates, and queries. - -```kotlin -// === Domain Types === - -@Serializable -data class Order( - val id: String, - val customerId: String, - val items: List, - val priority: Priority = Priority.NORMAL -) { - val total: BigDecimal get() = items.sumOf { it.price * it.quantity.toBigDecimal() } -} - -@Serializable -data class OrderItem( - val productId: String, - val quantity: Int, - val price: BigDecimal -) - -@Serializable -data class OrderResult(val success: Boolean, val trackingNumber: String?) - -enum class OrderStatus { PENDING, PROCESSING, SHIPPED, CANCELED } -enum class Priority { LOW, NORMAL, HIGH } - -// === Workflow Interface === - -@WorkflowInterface -interface OrderWorkflow { - @WorkflowMethod - suspend fun processOrder(order: Order): OrderResult - - @UpdateMethod - suspend fun addItem(item: OrderItem): Boolean - - @UpdateValidatorMethod(updateMethod = "addItem") - fun validateAddItem(item: OrderItem) - - @QueryMethod - val status: OrderStatus - - @QueryMethod - val progress: Int -} - -// === Activity Interface === - -@ActivityInterface -interface OrderActivities { - @ActivityMethod - suspend fun validateOrder(order: Order): Boolean - - @ActivityMethod - suspend fun reserveInventory(item: OrderItem): Boolean - - @ActivityMethod - suspend fun releaseInventory(item: OrderItem): Boolean - - @ActivityMethod - suspend fun chargePayment(order: Order): Boolean - - @ActivityMethod - suspend fun refundPayment(order: Order): Boolean - - @ActivityMethod - suspend fun shipOrder(order: Order): String - - @ActivityMethod - suspend fun notifyCustomer(customerId: String, message: String) -} - -// === Workflow Implementation === - -class OrderWorkflowImpl : OrderWorkflow { - private var _status = OrderStatus.PENDING - private var _progress = 0 - private var _order: Order? = null - private var paymentCharged = false - private var reservedItems = mutableListOf() - - override val status get() = _status - override val progress get() = _progress - - // Reusable options for common cases - private val defaultOptions = KActivityOptions( - startToCloseTimeout = 30.seconds, - retryOptions = KRetryOptions( - initialInterval = 1.seconds, - maximumAttempts = 3 - ) - ) - - override suspend fun processOrder(order: Order): OrderResult { - _order = order - _status = OrderStatus.PROCESSING - - return try { - doProcessOrder(order) - } catch (e: CancellationException) { - // Workflow was cancelled - run cleanup in detached scope - withContext(NonCancellable) { - cleanup(order) - } - _status = OrderStatus.CANCELED - throw e - } - } - - private suspend fun doProcessOrder(order: Order): OrderResult = coroutineScope { - // Validate order - _progress = 10 - val isValid = KWorkflow.executeActivity( - OrderActivities::validateOrder, - KActivityOptions(startToCloseTimeout = 10.seconds), - order - ) - if (!isValid) { - return@coroutineScope OrderResult(success = false, trackingNumber = null) - } - - // Reserve inventory for all items in parallel - _progress = 30 - order.items.map { item -> - async { - val reserved = KWorkflow.executeActivity( - OrderActivities::reserveInventory, - defaultOptions, - item - ) - if (reserved) { - reservedItems.add(item) - } - reserved - } - }.awaitAll() - - _progress = 60 - - // Charge payment with timeout - val charged = withTimeout(2.minutes) { - KWorkflow.executeActivity( - OrderActivities::chargePayment, - KActivityOptions( - startToCloseTimeout = 2.minutes, - retryOptions = KRetryOptions(maximumAttempts = 5) - ), - order - ) - } - if (!charged) { - return@coroutineScope OrderResult(success = false, trackingNumber = null) - } - paymentCharged = true - - _progress = 80 - - // Ship order - val trackingNumber = KWorkflow.executeActivity( - OrderActivities::shipOrder, - defaultOptions, - order - ) - - _status = OrderStatus.SHIPPED - _progress = 100 - - OrderResult(success = true, trackingNumber = trackingNumber) - } - - private suspend fun cleanup(order: Order) { - // Release any reserved inventory - for (item in reservedItems) { - try { - KWorkflow.executeActivity( - OrderActivities::releaseInventory, - defaultOptions, - item - ) - } catch (e: Exception) { - // Log but continue cleanup - } - } - - // Refund payment if it was charged - if (paymentCharged) { - try { - KWorkflow.executeActivity( - OrderActivities::refundPayment, - defaultOptions, - order - ) - } catch (e: Exception) { - // Log but continue cleanup - } - } - - // Notify customer of cancellation - try { - KWorkflow.executeActivity( - OrderActivities::notifyCustomer, - defaultOptions, - order.customerId, "Your order has been cancelled" - ) - } catch (e: Exception) { - // Best effort notification - } - } - - override fun validateAddItem(item: OrderItem) { - require(item.quantity > 0) { "Quantity must be positive" } - require(item.price >= BigDecimal.ZERO) { "Price cannot be negative" } - require(_status == OrderStatus.PENDING) { "Cannot add items after processing started" } - } - - override suspend fun addItem(item: OrderItem): Boolean { - return true - } -} -``` - ## Related - [Activities](../activities/README.md) - What workflows orchestrate From 636edde2809e1f814a0ae458d7032ac594fd1402 Mon Sep 17 00:00:00 2001 From: Maxim Fateev Date: Mon, 5 Jan 2026 12:49:07 -0800 Subject: [PATCH 36/83] Remove implementation note from koptions.md --- kotlin/configuration/koptions.md | 2 -- 1 file changed, 2 deletions(-) diff --git a/kotlin/configuration/koptions.md b/kotlin/configuration/koptions.md index bc8a091..87ef5a7 100644 --- a/kotlin/configuration/koptions.md +++ b/kotlin/configuration/koptions.md @@ -218,8 +218,6 @@ The Kotlin SDK provides two approaches for configuring options: | IDE | Limited autocomplete | Full parameter hints | | Usage | Java SDK only | Kotlin SDK | -> **Implementation Note:** KOptions classes internally convert to Java SDK options. The conversion happens once when the activity/workflow is scheduled, so there's no runtime overhead during workflow execution. - ## Related - [Data Conversion](./data-conversion.md) - Serialization configuration From 5ab800619c53ae296a802e1573f8ec1e31d7c29d Mon Sep 17 00:00:00 2001 From: Maxim Fateev Date: Mon, 5 Jan 2026 12:52:12 -0800 Subject: [PATCH 37/83] Remove implementation note from activities/definition.md --- kotlin/activities/definition.md | 2 -- 1 file changed, 2 deletions(-) diff --git a/kotlin/activities/definition.md b/kotlin/activities/definition.md index e7aa4e8..36b036f 100644 --- a/kotlin/activities/definition.md +++ b/kotlin/activities/definition.md @@ -161,8 +161,6 @@ object KWorkflow { } ``` -> **Implementation Note:** `KFunction` provides `.name` for the method name and `.parameters[0].type` for the declaring interface. This metadata is used to make Temporal activity calls by name. - ## Related - [Local Activities](./local-activities.md) - Short-lived local activities From 95e79852c5c58f687439633dbe2444be9852500f Mon Sep 17 00:00:00 2001 From: Maxim Fateev Date: Mon, 5 Jan 2026 14:12:56 -0800 Subject: [PATCH 38/83] Refactor interceptor API to use suspend functions consistently - Change KWorkflowOutboundCallsInterceptor to use suspend functions instead of returning Deferred (more idiomatic Kotlin) - Remove newTimer() - use async { delay() } instead - Replace executeChildWorkflow with startChildWorkflow returning handle - Update getChildWorkflowHandle to use method reference for type inference - Remove metrics, tracing, auth interceptor examples (keep logging only) - Add duration and error logging to LoggingWorkflowInterceptor - Add executeUpdate to LoggingWorkflowInterceptor - Add Kotlin calling Java workflows section to definition.md - Fix child-workflows.md intro to not forward-reference activities --- kotlin/configuration/interceptors.md | 242 +++---------------- kotlin/implementation/implementation-plan.md | 2 +- kotlin/workflows/child-workflows.md | 15 +- kotlin/workflows/definition.md | 25 ++ 4 files changed, 77 insertions(+), 207 deletions(-) diff --git a/kotlin/configuration/interceptors.md b/kotlin/configuration/interceptors.md index 7092adf..2c8d28e 100644 --- a/kotlin/configuration/interceptors.md +++ b/kotlin/configuration/interceptors.md @@ -109,18 +109,19 @@ data class KUpdateOutput(val result: Any?) Intercepts outbound calls from workflow code to Temporal APIs (activities, child workflows, timers, etc.). +All async operations are `suspend` functions. For parallel execution, use standard `async { }` pattern. + ```kotlin interface KWorkflowOutboundCallsInterceptor { - // Activities - fun executeActivity(input: KActivityInvocationInput): Deferred - fun executeLocalActivity(input: KLocalActivityInvocationInput): Deferred + // Activities - suspend, use async {} for parallel execution + suspend fun executeActivity(input: KActivityInvocationInput): R + suspend fun executeLocalActivity(input: KLocalActivityInvocationInput): R - // Child Workflows - fun executeChildWorkflow(input: KChildWorkflowInvocationInput): KChildWorkflowInvocationOutput + // Child Workflows - returns handle with Deferred result + suspend fun startChildWorkflow(input: KChildWorkflowInvocationInput): KChildWorkflowHandle - // Timers and Delays + // Timers suspend fun delay(duration: Duration) - fun newTimer(duration: Duration): Deferred // Await Conditions suspend fun awaitCondition(timeout: Duration, reason: String, condition: () -> Boolean): Boolean @@ -137,8 +138,8 @@ interface KWorkflowOutboundCallsInterceptor { fun continueAsNew(input: KContinueAsNewInput): Nothing // External Workflow Communication - fun signalExternalWorkflow(input: KSignalExternalInput): Deferred - fun cancelWorkflow(input: KCancelWorkflowInput): Deferred + suspend fun signalExternalWorkflow(input: KSignalExternalInput) + suspend fun cancelWorkflow(input: KCancelWorkflowInput) // Search Attributes and Memo fun upsertTypedSearchAttributes(vararg updates: SearchAttributeUpdate<*>) @@ -156,11 +157,10 @@ interface KWorkflowOutboundCallsInterceptor { open class KWorkflowOutboundCallsInterceptorBase( protected val next: KWorkflowOutboundCallsInterceptor ) : KWorkflowOutboundCallsInterceptor { - override fun executeActivity(input: KActivityInvocationInput) = next.executeActivity(input) - override fun executeLocalActivity(input: KLocalActivityInvocationInput) = next.executeLocalActivity(input) - override fun executeChildWorkflow(input: KChildWorkflowInvocationInput) = next.executeChildWorkflow(input) + override suspend fun executeActivity(input: KActivityInvocationInput) = next.executeActivity(input) + override suspend fun executeLocalActivity(input: KLocalActivityInvocationInput) = next.executeLocalActivity(input) + override suspend fun startChildWorkflow(input: KChildWorkflowInvocationInput) = next.startChildWorkflow(input) override suspend fun delay(duration: Duration) = next.delay(duration) - override fun newTimer(duration: Duration) = next.newTimer(duration) override suspend fun awaitCondition(timeout: Duration, reason: String, condition: () -> Boolean) = next.awaitCondition(timeout, reason, condition) override suspend fun awaitCondition(reason: String, condition: () -> Boolean) = @@ -171,8 +171,8 @@ open class KWorkflowOutboundCallsInterceptorBase( override fun getVersion(changeId: String, minSupported: Int, maxSupported: Int) = next.getVersion(changeId, minSupported, maxSupported) override fun continueAsNew(input: KContinueAsNewInput) = next.continueAsNew(input) - override fun signalExternalWorkflow(input: KSignalExternalInput) = next.signalExternalWorkflow(input) - override fun cancelWorkflow(input: KCancelWorkflowInput) = next.cancelWorkflow(input) + override suspend fun signalExternalWorkflow(input: KSignalExternalInput) = next.signalExternalWorkflow(input) + override suspend fun cancelWorkflow(input: KCancelWorkflowInput) = next.cancelWorkflow(input) override fun upsertTypedSearchAttributes(vararg updates: SearchAttributeUpdate<*>) = next.upsertTypedSearchAttributes(*updates) override fun upsertMemo(memo: Map) = next.upsertMemo(memo) @@ -222,14 +222,6 @@ data class KChildWorkflowInvocationInput( val header: Header ) -/** - * Output from child workflow start. - */ -data class KChildWorkflowInvocationOutput( - val result: Deferred, - val workflowExecution: Deferred -) - /** * Input for continue-as-new. */ @@ -309,13 +301,17 @@ private class LoggingWorkflowInterceptor( override suspend fun execute(input: KWorkflowInput): KWorkflowOutput { log.info("Workflow started with ${input.arguments.size} arguments") + val startTime = KWorkflow.currentTimeMillis() + return try { - next.execute(input) + val result = next.execute(input) + val duration = KWorkflow.currentTimeMillis() - startTime + log.info("Workflow completed in ${duration}ms") + result } catch (e: Exception) { - log.error("Workflow failed", e) + val duration = KWorkflow.currentTimeMillis() - startTime + log.error("Workflow failed after ${duration}ms", e) throw e - } finally { - log.info("Workflow completed") } } @@ -328,6 +324,18 @@ private class LoggingWorkflowInterceptor( log.debug("Query received: ${input.queryName}") return next.handleQuery(input) } + + override suspend fun executeUpdate(input: KUpdateInput): KUpdateOutput { + log.info("Update received: ${input.updateName}") + return try { + val result = next.executeUpdate(input) + log.info("Update ${input.updateName} completed") + result + } catch (e: Exception) { + log.error("Update ${input.updateName} failed", e) + throw e + } + } } private class LoggingActivityInterceptor( @@ -342,186 +350,14 @@ private class LoggingActivityInterceptor( val startTime = System.currentTimeMillis() return try { - next.execute(input) - } finally { + val result = next.execute(input) val duration = System.currentTimeMillis() - startTime log.info("Activity ${info.activityType} completed in ${duration}ms") - } - } -} -``` - -## Example: Metrics Interceptor - -```kotlin -class MetricsInterceptor( - private val meterProvider: MeterProvider -) : KWorkerInterceptorBase() { - - private val meter = meterProvider.get("temporal.sdk") - private val workflowCounter = meter.counterBuilder("workflow.executions").build() - private val activityCounter = meter.counterBuilder("activity.executions").build() - private val activityDuration = meter.histogramBuilder("activity.duration").build() - - override fun interceptWorkflow( - next: KWorkflowInboundCallsInterceptor - ): KWorkflowInboundCallsInterceptor { - return object : KWorkflowInboundCallsInterceptorBase(next) { - override suspend fun execute(input: KWorkflowInput): KWorkflowOutput { - val workflowType = KWorkflow.getInfo().workflowType - workflowCounter.add(1, Attributes.of( - AttributeKey.stringKey("workflow.type"), workflowType - )) - return next.execute(input) - } - } - } - - override fun interceptActivity( - next: KActivityInboundCallsInterceptor - ): KActivityInboundCallsInterceptor { - return object : KActivityInboundCallsInterceptorBase(next) { - override suspend fun execute(input: KActivityExecutionInput): KActivityExecutionOutput { - val info = KActivity.getInfo() - val startTime = System.nanoTime() - - return try { - val result = next.execute(input) - activityCounter.add(1, Attributes.of( - AttributeKey.stringKey("activity.type"), info.activityType, - AttributeKey.stringKey("status"), "success" - )) - result - } catch (e: Exception) { - activityCounter.add(1, Attributes.of( - AttributeKey.stringKey("activity.type"), info.activityType, - AttributeKey.stringKey("status"), "failure" - )) - throw e - } finally { - val durationMs = (System.nanoTime() - startTime) / 1_000_000.0 - activityDuration.record(durationMs, Attributes.of( - AttributeKey.stringKey("activity.type"), info.activityType - )) - } - } - } - } -} -``` - -## Example: Tracing with OpenTelemetry - -```kotlin -class TracingInterceptor( - private val tracer: Tracer -) : KWorkerInterceptorBase() { - - override fun interceptWorkflow( - next: KWorkflowInboundCallsInterceptor - ): KWorkflowInboundCallsInterceptor { - return TracingWorkflowInboundInterceptor(next, tracer) - } -} - -private class TracingWorkflowInboundInterceptor( - next: KWorkflowInboundCallsInterceptor, - private val tracer: Tracer -) : KWorkflowInboundCallsInterceptorBase(next) { - - private lateinit var outboundInterceptor: TracingWorkflowOutboundInterceptor - - override suspend fun init(outboundCalls: KWorkflowOutboundCallsInterceptor) { - // Wrap the outbound interceptor to trace outgoing calls - outboundInterceptor = TracingWorkflowOutboundInterceptor(outboundCalls, tracer) - next.init(outboundInterceptor) - } - - override suspend fun execute(input: KWorkflowInput): KWorkflowOutput { - // Extract trace context from header - val parentContext = extractContext(input.header) - - val span = tracer.spanBuilder("workflow.execute") - .setParent(parentContext) - .setAttribute("workflow.type", KWorkflow.getInfo().workflowType) - .startSpan() - - return try { - withContext(span.asContextElement()) { - next.execute(input) - } + result } catch (e: Exception) { - span.recordException(e) - span.setStatus(StatusCode.ERROR) + val duration = System.currentTimeMillis() - startTime + log.error("Activity ${info.activityType} failed after ${duration}ms", e) throw e - } finally { - span.end() - } - } -} - -private class TracingWorkflowOutboundInterceptor( - next: KWorkflowOutboundCallsInterceptor, - private val tracer: Tracer -) : KWorkflowOutboundCallsInterceptorBase(next) { - - override fun executeActivity(input: KActivityInvocationInput): Deferred { - val span = tracer.spanBuilder("activity.schedule") - .setAttribute("activity.name", input.activityName) - .startSpan() - - // Inject trace context into header - val headerWithTrace = injectContext(input.header, span.context) - val inputWithTrace = input.copy(header = headerWithTrace) - - span.end() - return next.executeActivity(inputWithTrace) - } - - override fun executeChildWorkflow( - input: KChildWorkflowInvocationInput - ): KChildWorkflowInvocationOutput { - val span = tracer.spanBuilder("child_workflow.start") - .setAttribute("workflow.type", input.workflowType) - .setAttribute("workflow.id", input.workflowId) - .startSpan() - - val headerWithTrace = injectContext(input.header, span.context) - val inputWithTrace = input.copy(header = headerWithTrace) - - span.end() - return next.executeChildWorkflow(inputWithTrace) - } -} -``` - -## Example: Authentication Interceptor - -```kotlin -class AuthInterceptor( - private val authService: AuthService -) : KWorkerInterceptorBase() { - - override fun interceptWorkflow( - next: KWorkflowInboundCallsInterceptor - ): KWorkflowInboundCallsInterceptor { - return object : KWorkflowInboundCallsInterceptorBase(next) { - - override suspend fun handleSignal(input: KSignalInput) { - val authToken = input.header["authorization"]?.firstOrNull() - if (authToken != null && !authService.isAuthorized(authToken, "signal:${input.signalName}")) { - throw IllegalAccessException("Unauthorized signal: ${input.signalName}") - } - next.handleSignal(input) - } - - override suspend fun executeUpdate(input: KUpdateInput): KUpdateOutput { - val authToken = input.header["authorization"]?.firstOrNull() - if (authToken != null && !authService.isAuthorized(authToken, "update:${input.updateName}")) { - throw IllegalAccessException("Unauthorized update: ${input.updateName}") - } - return next.executeUpdate(input) - } } } } diff --git a/kotlin/implementation/implementation-plan.md b/kotlin/implementation/implementation-plan.md index b7b7188..41904aa 100644 --- a/kotlin/implementation/implementation-plan.md +++ b/kotlin/implementation/implementation-plan.md @@ -114,7 +114,7 @@ - `KWorkflowOutboundCallsInterceptor` with full API (activities, child workflows, timers, side effects, etc.) - `KActivityInboundCallsInterceptor` with suspend support - Base classes for convenience (`*Base` classes) -- Examples: logging, tracing (OpenTelemetry), metrics, auth +- Example: logging interceptor --- diff --git a/kotlin/workflows/child-workflows.md b/kotlin/workflows/child-workflows.md index 1f3dba3..86462bd 100644 --- a/kotlin/workflows/child-workflows.md +++ b/kotlin/workflows/child-workflows.md @@ -1,6 +1,6 @@ # Child Workflows -Child workflows use the same stub-less pattern as activities. +Child workflows are invoked using direct method references - no stub creation needed. ## Execute and Wait @@ -77,7 +77,7 @@ override suspend fun parentWorkflowWithHandle(): String { // Get handle to existing child workflow by ID override suspend fun interactWithExistingChild(): String { - val handle = KWorkflow.getChildWorkflowHandle("child-workflow-id") + val handle = KWorkflow.getChildWorkflowHandle(ChildWorkflow::doWork, "child-workflow-id") // Signal the child workflow handle.signal(ChildWorkflow::updatePriority, Priority.HIGH) @@ -153,8 +153,17 @@ object KWorkflow { /** * Get a handle to an existing child workflow by ID. + * Types are inferred from the method reference. */ - fun getChildWorkflowHandle(workflowId: String): KChildWorkflowHandle + fun getChildWorkflowHandle( + workflow: KFunction2, + workflowId: String + ): KChildWorkflowHandle + + fun getChildWorkflowHandle( + workflow: KFunction1, + workflowId: String + ): KChildWorkflowHandle } ``` diff --git a/kotlin/workflows/definition.md b/kotlin/workflows/definition.md index 3fec996..195af7d 100644 --- a/kotlin/workflows/definition.md +++ b/kotlin/workflows/definition.md @@ -32,6 +32,31 @@ val result = client.executeWorkflow( ## Java Interoperability +### Kotlin Calling Java Workflows + +Kotlin clients can call Java workflows using typed method references: + +```kotlin +// Java workflow interface (defined in Java) +// @WorkflowInterface +// public interface OrderWorkflow { +// @WorkflowMethod +// OrderResult processOrder(Order order); +// } + +// Kotlin client calling Java workflow - works seamlessly +val result: OrderResult = client.executeWorkflow( + OrderWorkflow::processOrder, + KWorkflowOptions( + workflowId = "order-123", + taskQueue = "orders" + ), + order +) +``` + +### Java Calling Kotlin Workflows + Java clients can invoke Kotlin suspend workflows using **untyped workflow stubs**. The workflow type name defaults to the interface name. ```java From 1d0a46834c4898536f3ad2b933d2ee0ff4cbbc35 Mon Sep 17 00:00:00 2001 From: Maxim Fateev Date: Mon, 5 Jan 2026 14:17:05 -0800 Subject: [PATCH 39/83] Remove getChildWorkflowHandle - use external workflow handle instead Java SDK doesn't have getChildWorkflowHandle, only newExternalWorkflowStub for signaling external workflows. Removed from Kotlin SDK docs to match. --- kotlin/implementation/implementation-plan.md | 1 - kotlin/workflows/child-workflows.md | 24 -------------------- 2 files changed, 25 deletions(-) diff --git a/kotlin/implementation/implementation-plan.md b/kotlin/implementation/implementation-plan.md index 41904aa..cf34575 100644 --- a/kotlin/implementation/implementation-plan.md +++ b/kotlin/implementation/implementation-plan.md @@ -46,7 +46,6 @@ ### 2.2 Typed Child Workflow Execution ✅ - ✅ Typed `executeChildWorkflow()` with method references - ✅ `startChildWorkflow()` returning `KChildWorkflowHandle` -- ✅ `getChildWorkflowHandle()` for existing child workflows - ✅ `KChildWorkflowHandle` interface (signal, cancel, result) - ✅ Optional `KChildWorkflowOptions` (uses defaults when omitted) diff --git a/kotlin/workflows/child-workflows.md b/kotlin/workflows/child-workflows.md index 86462bd..336cc89 100644 --- a/kotlin/workflows/child-workflows.md +++ b/kotlin/workflows/child-workflows.md @@ -75,16 +75,6 @@ override suspend fun parentWorkflowWithHandle(): String { return handle.result() } -// Get handle to existing child workflow by ID -override suspend fun interactWithExistingChild(): String { - val handle = KWorkflow.getChildWorkflowHandle(ChildWorkflow::doWork, "child-workflow-id") - - // Signal the child workflow - handle.signal(ChildWorkflow::updatePriority, Priority.HIGH) - - return handle.result() -} - // Parallel child workflows with handles for interaction override suspend fun parallelChildrenWithHandles(): List = coroutineScope { val handles = listOf("child-1", "child-2", "child-3").map { id -> @@ -150,20 +140,6 @@ object KWorkflow { ): KChildWorkflowHandle // ... up to 6 arguments - - /** - * Get a handle to an existing child workflow by ID. - * Types are inferred from the method reference. - */ - fun getChildWorkflowHandle( - workflow: KFunction2, - workflowId: String - ): KChildWorkflowHandle - - fun getChildWorkflowHandle( - workflow: KFunction1, - workflowId: String - ): KChildWorkflowHandle } ``` From 60981f0356cca7cf3d4fae9df558a67f1387db14 Mon Sep 17 00:00:00 2001 From: Maxim Fateev Date: Mon, 5 Jan 2026 14:18:25 -0800 Subject: [PATCH 40/83] Add runId to KChildWorkflowHandle --- kotlin/workflows/child-workflows.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/kotlin/workflows/child-workflows.md b/kotlin/workflows/child-workflows.md index 336cc89..18fdaaa 100644 --- a/kotlin/workflows/child-workflows.md +++ b/kotlin/workflows/child-workflows.md @@ -160,6 +160,9 @@ interface KChildWorkflowHandle { /** The child workflow's first execution run ID */ val firstExecutionRunId: String + /** The child workflow's current run ID */ + val runId: String + /** * Wait for the child workflow to complete and return its result. * Suspends until the child workflow finishes. From 813ae1805b7ed17d0fe1cdc50eca4ad3b00cac97 Mon Sep 17 00:00:00 2001 From: Maxim Fateev Date: Mon, 5 Jan 2026 14:21:51 -0800 Subject: [PATCH 41/83] Change KChildWorkflowHandle to use suspend getExecution() Replace firstExecutionRunId and runId properties with suspend fun getExecution() that returns WorkflowExecution. Matches Java SDK's ChildWorkflowStub.getExecution() pattern - suspends until child starts. --- kotlin/workflows/child-workflows.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/kotlin/workflows/child-workflows.md b/kotlin/workflows/child-workflows.md index 18fdaaa..315346f 100644 --- a/kotlin/workflows/child-workflows.md +++ b/kotlin/workflows/child-workflows.md @@ -157,11 +157,11 @@ interface KChildWorkflowHandle { /** The child workflow's workflow ID */ val workflowId: String - /** The child workflow's first execution run ID */ - val firstExecutionRunId: String - - /** The child workflow's current run ID */ - val runId: String + /** + * Get the child workflow's execution info. + * Suspends until the child workflow starts and execution info is available. + */ + suspend fun getExecution(): WorkflowExecution /** * Wait for the child workflow to complete and return its result. From 614b176594a5c4459422362b8a12bbb7dfad31e1 Mon Sep 17 00:00:00 2001 From: Maxim Fateev Date: Mon, 5 Jan 2026 14:31:40 -0800 Subject: [PATCH 42/83] Simplify KChildWorkflowHandle: use suspend fun runId() instead of getExecution() --- kotlin/workflows/child-workflows.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/kotlin/workflows/child-workflows.md b/kotlin/workflows/child-workflows.md index 315346f..9c7067e 100644 --- a/kotlin/workflows/child-workflows.md +++ b/kotlin/workflows/child-workflows.md @@ -154,14 +154,14 @@ object KWorkflow { * @param R The result type of the child workflow method */ interface KChildWorkflowHandle { - /** The child workflow's workflow ID */ + /** The child workflow's workflow ID (available immediately) */ val workflowId: String /** - * Get the child workflow's execution info. - * Suspends until the child workflow starts and execution info is available. + * Get the child workflow's run ID. + * Suspends until the child workflow starts. */ - suspend fun getExecution(): WorkflowExecution + suspend fun runId(): String /** * Wait for the child workflow to complete and return its result. From f2d2a5099e6cb65bc8a425d18f74fbfbc45dd2c6 Mon Sep 17 00:00:00 2001 From: Maxim Fateev Date: Mon, 5 Jan 2026 17:34:23 -0800 Subject: [PATCH 43/83] Remove contextPropagators from KContinueAsNewOptions --- kotlin/configuration/koptions.md | 3 +-- kotlin/workflows/continue-as-new.md | 3 +-- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/kotlin/configuration/koptions.md b/kotlin/configuration/koptions.md index 87ef5a7..706d94f 100644 --- a/kotlin/configuration/koptions.md +++ b/kotlin/configuration/koptions.md @@ -177,8 +177,7 @@ data class KContinueAsNewOptions( val retryOptions: KRetryOptions? = null, val workflowTaskTimeout: Duration? = null, val memo: Map? = null, - val typedSearchAttributes: SearchAttributes? = null, - val contextPropagators: List? = null + val typedSearchAttributes: SearchAttributes? = null ) ``` diff --git a/kotlin/workflows/continue-as-new.md b/kotlin/workflows/continue-as-new.md index d2ace02..1c9149e 100644 --- a/kotlin/workflows/continue-as-new.md +++ b/kotlin/workflows/continue-as-new.md @@ -49,8 +49,7 @@ data class KContinueAsNewOptions( val retryOptions: KRetryOptions? = null, val workflowTaskTimeout: Duration? = null, val memo: Map? = null, - val typedSearchAttributes: SearchAttributes? = null, - val contextPropagators: List? = null + val typedSearchAttributes: SearchAttributes? = null ) ``` From e541eb686a0d23da9a67b25b4975925081c57154 Mon Sep 17 00:00:00 2001 From: Maxim Fateev Date: Tue, 6 Jan 2026 17:04:08 -0800 Subject: [PATCH 44/83] Use Kotlin properties instead of getter methods in API MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Replace getter methods with idiomatic Kotlin properties: - KWorkflow.getInfo() → KWorkflow.info - KActivity.getContext() → KActivity.context - KActivity.getInfo() → KActivity.info - Other getter patterns → property access Update api-parity.md table to show property-based API. --- kotlin/activities/README.md | 2 +- kotlin/activities/implementation.md | 4 ++-- kotlin/activities/local-activities.md | 4 ++-- kotlin/api-parity.md | 16 ++++++++-------- kotlin/configuration/interceptors.md | 2 +- kotlin/implementation/implementation-plan.md | 2 +- kotlin/kotlin-idioms.md | 4 ++-- kotlin/workflows/continue-as-new.md | 2 +- 8 files changed, 18 insertions(+), 18 deletions(-) diff --git a/kotlin/activities/README.md b/kotlin/activities/README.md index 07e50f5..da9bcc9 100644 --- a/kotlin/activities/README.md +++ b/kotlin/activities/README.md @@ -59,7 +59,7 @@ val result = KWorkflow.executeActivity( | Execute activity | `KWorkflow.executeActivity(Interface::method, options, args)` | | Execute by name | `KWorkflow.executeActivity("name", options, args)` | | Local activity | `KWorkflow.executeLocalActivity(Interface::method, options, args)` | -| Heartbeat | `KActivity.getContext().heartbeat(details)` | +| Heartbeat | `KActivity.context.heartbeat(details)` | ## Related diff --git a/kotlin/activities/implementation.md b/kotlin/activities/implementation.md index a0c4cbb..8ca99fb 100644 --- a/kotlin/activities/implementation.md +++ b/kotlin/activities/implementation.md @@ -114,7 +114,7 @@ For long-running activities, use heartbeating to report progress and detect canc ```kotlin class LongRunningActivitiesImpl : LongRunningActivities { override suspend fun processLargeFile(filePath: String): ProcessResult { - val context = KActivity.getContext() + val context = KActivity.context val lines = File(filePath).readLines() lines.forEachIndexed { index, line -> @@ -135,7 +135,7 @@ Retrieve heartbeat details from a previous failed attempt: ```kotlin override suspend fun resumableProcess(data: List): ProcessResult { - val context = KActivity.getContext() + val context = KActivity.context // Get progress from previous attempt if available val startIndex: Int = context.getHeartbeatDetails() ?: 0 diff --git a/kotlin/activities/local-activities.md b/kotlin/activities/local-activities.md index 8b5c9c9..5f6a95a 100644 --- a/kotlin/activities/local-activities.md +++ b/kotlin/activities/local-activities.md @@ -74,13 +74,13 @@ Use regular activities for: ## KActivity API -`KActivity.getContext()` returns a `KActivityContext`, matching Java SDK's `Activity.getExecutionContext()` pattern: +`KActivity.context` returns a `KActivityContext`, matching Java SDK's `Activity.getExecutionContext()` pattern: ```kotlin // In activity implementation // Get activity context (matches Java's Activity.getExecutionContext()) -val context = KActivity.getContext() +val context = KActivity.context // Get activity info val info = context.info diff --git a/kotlin/api-parity.md b/kotlin/api-parity.md index d1ad42b..4994a8b 100644 --- a/kotlin/api-parity.md +++ b/kotlin/api-parity.md @@ -76,14 +76,14 @@ The following Java SDK workflow APIs have Kotlin equivalents in `KWorkflow`: | Java SDK API | Kotlin SDK | |--------------|------------| -| `Workflow.getLastCompletionResult(class)` | `KWorkflow.getLastCompletionResult()` | -| `Workflow.getPreviousRunFailure()` | `KWorkflow.getPreviousRunFailure()` | -| `Workflow.isReplaying()` | `KWorkflow.isReplaying()` | -| `Workflow.getCurrentUpdateInfo()` | `KWorkflow.getCurrentUpdateInfo()` | -| `Workflow.isEveryHandlerFinished()` | `KWorkflow.isEveryHandlerFinished()` | -| `Workflow.setCurrentDetails(...)` | `KWorkflow.setCurrentDetails()` | -| `Workflow.getCurrentDetails()` | `KWorkflow.getCurrentDetails()` | -| `Workflow.getMetricsScope()` | `KWorkflow.getMetricsScope()` | +| `Workflow.getLastCompletionResult(class)` | `KWorkflow.getLastCompletionResult()` | +| `Workflow.getPreviousRunFailure()` | `KWorkflow.previousRunFailure` | +| `Workflow.isReplaying()` | `KWorkflow.isReplaying` | +| `Workflow.getCurrentUpdateInfo()` | `KWorkflow.currentUpdateInfo` | +| `Workflow.isEveryHandlerFinished()` | `KWorkflow.isEveryHandlerFinished` | +| `Workflow.setCurrentDetails(...)` | `KWorkflow.currentDetails = ...` | +| `Workflow.getCurrentDetails()` | `KWorkflow.currentDetails` | +| `Workflow.getMetricsScope()` | `KWorkflow.metricsScope` | ### Side Effects & Utilities diff --git a/kotlin/configuration/interceptors.md b/kotlin/configuration/interceptors.md index 2c8d28e..f769278 100644 --- a/kotlin/configuration/interceptors.md +++ b/kotlin/configuration/interceptors.md @@ -343,7 +343,7 @@ private class LoggingActivityInterceptor( ) : KActivityInboundCallsInterceptorBase(next) { override suspend fun execute(input: KActivityExecutionInput): KActivityExecutionOutput { - val info = KActivity.getInfo() + val info = KActivity.info val log = KActivity.logger() log.info("Activity ${info.activityType} started") diff --git a/kotlin/implementation/implementation-plan.md b/kotlin/implementation/implementation-plan.md index cf34575..b94ee5e 100644 --- a/kotlin/implementation/implementation-plan.md +++ b/kotlin/implementation/implementation-plan.md @@ -76,7 +76,7 @@ ### 2.6 Kotlin Activity API ✅ - ✅ `KActivity` object (entry point for activity APIs) -- ✅ `KActivity.getInfo()`, `heartbeat()` methods +- ✅ `KActivity.info`, `context`, `heartbeat()` properties/methods - ✅ `KActivity.logger()` for idiomatic logging - ✅ `KActivityInfo` with null safety - ✅ Suspend activity support via `SuspendActivityWrapper` diff --git a/kotlin/kotlin-idioms.md b/kotlin/kotlin-idioms.md index faa5c27..3706ef6 100644 --- a/kotlin/kotlin-idioms.md +++ b/kotlin/kotlin-idioms.md @@ -147,11 +147,11 @@ Nullable types replace `Optional`: ```kotlin // KWorkflowInfo uses nullable instead of Optional -val info = KWorkflow.getInfo() +val info = KWorkflow.info val parentId: String? = info.parentWorkflowId // null if no parent // Activity heartbeat details -val progress: Int? = KActivity.getContext().getHeartbeatDetails() +val progress: Int? = KActivity.context.getHeartbeatDetails() val startIndex = progress ?: 0 // Elvis operator for default ``` diff --git a/kotlin/workflows/continue-as-new.md b/kotlin/workflows/continue-as-new.md index 1c9149e..557c731 100644 --- a/kotlin/workflows/continue-as-new.md +++ b/kotlin/workflows/continue-as-new.md @@ -90,7 +90,7 @@ class BatchProcessorImpl : BatchProcessor { override suspend fun execute(state: WorkflowState) { while (true) { // Check if history is getting too large - if (KWorkflow.getInfo().isContinueAsNewSuggested) { + if (KWorkflow.info.isContinueAsNewSuggested) { KWorkflow.continueAsNew(state) } From 71000b345bd310ff7ae3c1fd8f114a3420a3f818 Mon Sep 17 00:00:00 2001 From: Maxim Fateev Date: Tue, 6 Jan 2026 17:07:46 -0800 Subject: [PATCH 45/83] Fix client documentation consistency - Use KWorkflowOptions instead of WorkflowOptions in workflow-handle.md - Change KWithStartWorkflowOperation.getResult() to suspend fun result() --- kotlin/client/advanced.md | 8 ++++---- kotlin/client/workflow-handle.md | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/kotlin/client/advanced.md b/kotlin/client/advanced.md index 8cc7c37..eb3b15e 100644 --- a/kotlin/client/advanced.md +++ b/kotlin/client/advanced.md @@ -36,8 +36,8 @@ Atomically start a workflow and send an update. Behavior depends on `workflowIdC * Created via KWorkflowClient.withStartWorkflowOperation(). */ class KWithStartWorkflowOperation { - /** Get the workflow result after the operation completes. */ - fun getResult(): R + /** Suspends until the workflow completes and returns its result. */ + suspend fun result(): R } /** @@ -78,8 +78,8 @@ val updateResult: Boolean = client.executeUpdateWithStart( ) println("Item added: $updateResult") -// Step 3: Access workflow result if needed -val workflowResult: OrderResult = startOp.getResult() +// Step 3: Access workflow result if needed (suspends until workflow completes) +val workflowResult: OrderResult = startOp.result() ``` ### Start Async (Don't Wait for Completion) diff --git a/kotlin/client/workflow-handle.md b/kotlin/client/workflow-handle.md index ed3877e..1f478f2 100644 --- a/kotlin/client/workflow-handle.md +++ b/kotlin/client/workflow-handle.md @@ -90,7 +90,7 @@ interface KTypedWorkflowHandle : KWorkflowHandle { // startWorkflow captures result type from method reference suspend fun startWorkflow( workflow: KFunction2, // R is captured here - options: WorkflowOptions, + options: KWorkflowOptions, arg: A1 ): KTypedWorkflowHandle // R is preserved in return type From 8411e2e637369ef911a0238a83c6d6afc268e7e6 Mon Sep 17 00:00:00 2001 From: Maxim Fateev Date: Tue, 6 Jan 2026 17:39:50 -0800 Subject: [PATCH 46/83] Remove stale interceptor references from registration example --- kotlin/configuration/interceptors.md | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/kotlin/configuration/interceptors.md b/kotlin/configuration/interceptors.md index f769278..dac8af1 100644 --- a/kotlin/configuration/interceptors.md +++ b/kotlin/configuration/interceptors.md @@ -42,9 +42,7 @@ Interceptors are registered via `KWorkerFactory`: ```kotlin val factory = KWorkerFactory(client) { workerInterceptors = listOf( - LoggingInterceptor(), - MetricsInterceptor(), - TracingInterceptor() + LoggingInterceptor() ) } From c33631268da1af4b0f5ebc94888bda9de984e4de Mon Sep 17 00:00:00 2001 From: Maxim Fateev Date: Tue, 6 Jan 2026 19:09:28 -0800 Subject: [PATCH 47/83] Simplify activity implementation - remove @KActivityImpl - Activity interfaces can have both suspend and non-suspend methods - Worker handles both automatically - Java interop uses string-based activity names (like workflows) - Remove parallel interface pattern and @KActivityImpl annotation --- kotlin/activities/implementation.md | 107 +++++++++------------------- 1 file changed, 34 insertions(+), 73 deletions(-) diff --git a/kotlin/activities/implementation.md b/kotlin/activities/implementation.md index 8ca99fb..3b02993 100644 --- a/kotlin/activities/implementation.md +++ b/kotlin/activities/implementation.md @@ -1,109 +1,70 @@ # Activity Implementation -There are two approaches for activity implementation, depending on whether you need Java interoperability. +Activity interfaces can have both suspend and non-suspend methods. The worker handles both automatically. -## Option A: Pure Kotlin (Recommended) - -For pure Kotlin codebases, define interfaces with `suspend` methods directly: +## Defining Activities ```kotlin @ActivityInterface -interface GreetingActivities { +interface OrderActivities { + // Suspend method - uses coroutines @ActivityMethod - suspend fun composeGreeting(greeting: String, name: String): String + suspend fun chargePayment(order: Order): PaymentResult + // Non-suspend method - runs on thread pool @ActivityMethod - suspend fun sendEmail(email: Email): SendResult + fun validateOrder(order: Order): Boolean } -class GreetingActivitiesImpl( - private val emailService: EmailService -) : GreetingActivities { +class OrderActivitiesImpl( + private val paymentService: PaymentService +) : OrderActivities { - override suspend fun composeGreeting(greeting: String, name: String): String { + override suspend fun chargePayment(order: Order): PaymentResult { // Full coroutine support - use withContext, async I/O, etc. - return withContext(Dispatchers.IO) { - "$greeting, $name!" - } + return paymentService.charge(order) } - override suspend fun sendEmail(email: Email): SendResult { - // Suspend functions for async I/O - return emailService.send(email) + override fun validateOrder(order: Order): Boolean { + return order.items.isNotEmpty() && order.total > 0 } } - -// In workflow - direct method reference, no stub needed -val greeting = KWorkflow.executeActivity( - GreetingActivities::composeGreeting, - KActivityOptions(startToCloseTimeout = 30.seconds), - "Hello", "World" -) ``` -## Option B: Java Interoperability (Parallel Interface Pattern) - -When you need to share activity interfaces with Java code (e.g., activities implemented in Java, or interfaces defined in a shared Java module), use the parallel interface pattern: +## Calling Activities from Workflows ```kotlin -// Interface for workflow calls - non-suspend for Java compatibility -// This interface can be defined in Java or Kotlin -@ActivityInterface -interface OrderActivities { - @ActivityMethod - fun validateOrder(order: Order): Boolean - - @ActivityMethod - fun chargePayment(order: Order): PaymentResult -} - -// Parallel suspend interface for Kotlin implementation -// Linked to the original interface via annotation -@KActivityImpl(activities = OrderActivities::class) -interface OrderActivitiesSuspend { - suspend fun validateOrder(order: Order): Boolean - suspend fun chargePayment(order: Order): PaymentResult -} - -// Kotlin implementation uses the suspend interface -class OrderActivitiesImpl( - private val paymentService: PaymentService -) : OrderActivitiesSuspend { - - override suspend fun validateOrder(order: Order): Boolean { - return order.items.isNotEmpty() && order.total > 0 - } - - override suspend fun chargePayment(order: Order): PaymentResult { - // Full suspend support for async operations - return paymentService.charge(order) - } -} - -// In workflow - use the non-suspend interface for method references +// Kotlin workflows use method references val isValid = KWorkflow.executeActivity( OrderActivities::validateOrder, KActivityOptions(startToCloseTimeout = 10.seconds), order ) + +val payment = KWorkflow.executeActivity( + OrderActivities::chargePayment, + KActivityOptions(startToCloseTimeout = 30.seconds), + order +) ``` -## When to Use Which +## Java Interoperability + +Java workflows call Kotlin activities using string-based activity names: -| Scenario | Approach | -|----------|----------| -| Pure Kotlin codebase | Option A - suspend interfaces | -| Calling Java-defined activities | Option A works (executeActivity handles it) | -| Kotlin activities with Java-defined interface | Option B - parallel interface | -| Shared interface library with Java | Option B - parallel interface | +```java +// Java workflow calling Kotlin activity +String result = Workflow.newActivityStub(ActivityOptions.newBuilder() + .setStartToCloseTimeout(Duration.ofSeconds(30)) + .build()) + .execute("chargePayment", PaymentResult.class, order); +``` + +This mirrors how Java workflows call Kotlin workflows - using string names for cross-language interop. ## Registering Activities ```kotlin -// Option A: Register suspend implementation directly -worker.registerActivitiesImplementations(GreetingActivitiesImpl(emailService)) - -// Option B: Register implementation - binding inferred from @KActivityImpl annotation worker.registerActivitiesImplementations(OrderActivitiesImpl(paymentService)) ``` From ba584ce2f29ac77baa762df3d2f47d91ddf20e16 Mon Sep 17 00:00:00 2001 From: Maxim Fateev Date: Tue, 6 Jan 2026 19:56:54 -0800 Subject: [PATCH 48/83] Fix heartbeatDetails API - use reified generic and consistent naming - Rename getHeartbeatDetails() to heartbeatDetails() - Use reified generic for type-safe deserialization - Usage: context.heartbeatDetails() ?: 0 --- kotlin/activities/implementation.md | 2 +- kotlin/activities/local-activities.md | 6 +++--- kotlin/kotlin-idioms.md | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/kotlin/activities/implementation.md b/kotlin/activities/implementation.md index 3b02993..e199817 100644 --- a/kotlin/activities/implementation.md +++ b/kotlin/activities/implementation.md @@ -99,7 +99,7 @@ override suspend fun resumableProcess(data: List): ProcessResult { val context = KActivity.context // Get progress from previous attempt if available - val startIndex: Int = context.getHeartbeatDetails() ?: 0 + val startIndex = context.heartbeatDetails() ?: 0 for (i in startIndex until data.size) { context.heartbeat(i) diff --git a/kotlin/activities/local-activities.md b/kotlin/activities/local-activities.md index 5f6a95a..3d56b83 100644 --- a/kotlin/activities/local-activities.md +++ b/kotlin/activities/local-activities.md @@ -90,7 +90,7 @@ println("Activity ${info.activityType}, attempt ${info.attempt}") context.heartbeat(progressDetails) // Get heartbeat details from previous attempt (for retry scenarios) -val previousProgress: Int? = context.getHeartbeatDetails() +val previousProgress = context.heartbeatDetails() // Logging - use activity logger for proper log context val log = context.logger() // Uses activity type as logger name @@ -110,7 +110,7 @@ val taskToken = context.taskToken interface KActivityContext { val info: KActivityInfo fun heartbeat(details: Any? = null) - fun getHeartbeatDetails(detailsClass: Class): T? + fun heartbeatDetails(detailsClass: Class): T? val taskToken: ByteArray fun doNotCompleteOnReturn() val isDoNotCompleteOnReturn: Boolean @@ -120,7 +120,7 @@ interface KActivityContext { } // Reified extension for easier Kotlin usage -inline fun KActivityContext.getHeartbeatDetails(): T? +inline fun KActivityContext.heartbeatDetails(): T? ``` ## KActivityInfo Interface diff --git a/kotlin/kotlin-idioms.md b/kotlin/kotlin-idioms.md index 3706ef6..179440a 100644 --- a/kotlin/kotlin-idioms.md +++ b/kotlin/kotlin-idioms.md @@ -151,7 +151,7 @@ val info = KWorkflow.info val parentId: String? = info.parentWorkflowId // null if no parent // Activity heartbeat details -val progress: Int? = KActivity.context.getHeartbeatDetails() +val progress = KActivity.context.heartbeatDetails() val startIndex = progress ?: 0 // Elvis operator for default ``` From 011eae296040272301d30a3fae2f103b37188398 Mon Sep 17 00:00:00 2001 From: Maxim Fateev Date: Tue, 6 Jan 2026 19:59:59 -0800 Subject: [PATCH 49/83] Move KActivity API from local-activities.md to implementation.md --- kotlin/activities/README.md | 4 +- kotlin/activities/implementation.md | 59 ++++++++++++++++++++++++ kotlin/activities/local-activities.md | 65 --------------------------- 3 files changed, 61 insertions(+), 67 deletions(-) diff --git a/kotlin/activities/README.md b/kotlin/activities/README.md index da9bcc9..08da097 100644 --- a/kotlin/activities/README.md +++ b/kotlin/activities/README.md @@ -11,8 +11,8 @@ Activities are the building blocks for interacting with external systems. The Ko | Document | Description | |----------|-------------| | [Definition](./definition.md) | Activity interfaces, typed and string-based execution | -| [Implementation](./implementation.md) | Implementing activities with suspend functions | -| [Local Activities](./local-activities.md) | Short-lived local activities, KActivity API | +| [Implementation](./implementation.md) | Implementing activities, KActivity API, heartbeating | +| [Local Activities](./local-activities.md) | Short-lived local activities | ## Quick Reference diff --git a/kotlin/activities/implementation.md b/kotlin/activities/implementation.md index e199817..14e7393 100644 --- a/kotlin/activities/implementation.md +++ b/kotlin/activities/implementation.md @@ -110,6 +110,65 @@ override suspend fun resumableProcess(data: List): ProcessResult { } ``` +## KActivity API + +`KActivity.context` provides access to the activity execution context: + +```kotlin +// In activity implementation +val context = KActivity.context + +// Get activity info +val info = context.info +println("Activity ${info.activityType}, attempt ${info.attempt}") + +// Heartbeat for long-running activities +context.heartbeat(progressDetails) + +// Get heartbeat details from previous attempt +val previousProgress = context.heartbeatDetails() + +// Logging - use activity logger for proper log context +val log = context.logger() // Uses activity type as logger name + +// Mark activity for async completion +context.doNotCompleteOnReturn() +val taskToken = context.taskToken +``` + +## KActivityContext Interface + +```kotlin +interface KActivityContext { + val info: KActivityInfo + fun heartbeat(details: Any? = null) + fun heartbeatDetails(detailsClass: Class): T? + val taskToken: ByteArray + fun doNotCompleteOnReturn() + val isDoNotCompleteOnReturn: Boolean + fun logger(): Logger + fun logger(name: String): Logger + fun logger(clazz: Class<*>): Logger +} + +// Reified extension for easier Kotlin usage +inline fun KActivityContext.heartbeatDetails(): T? +``` + +## KActivityInfo Interface + +```kotlin +interface KActivityInfo { + val activityId: String + val activityType: String + val workflowId: String + val attempt: Int + // ... other properties +} +``` + +> **Note:** `ActivityCompletionClient` for async activity completion uses the same API as the Java SDK. + ## Related - [Activity Definition](./definition.md) - Interface patterns diff --git a/kotlin/activities/local-activities.md b/kotlin/activities/local-activities.md index 3d56b83..e395958 100644 --- a/kotlin/activities/local-activities.md +++ b/kotlin/activities/local-activities.md @@ -72,71 +72,6 @@ Use regular activities for: - Operations that need heartbeating - Operations that must survive worker failures -## KActivity API - -`KActivity.context` returns a `KActivityContext`, matching Java SDK's `Activity.getExecutionContext()` pattern: - -```kotlin -// In activity implementation - -// Get activity context (matches Java's Activity.getExecutionContext()) -val context = KActivity.context - -// Get activity info -val info = context.info -println("Activity ${info.activityType}, attempt ${info.attempt}") - -// Heartbeat for long-running activities -context.heartbeat(progressDetails) - -// Get heartbeat details from previous attempt (for retry scenarios) -val previousProgress = context.heartbeatDetails() - -// Logging - use activity logger for proper log context -val log = context.logger() // Uses activity type as logger name -log.info("Processing item") - -// Or with custom logger name -val customLog = context.logger("custom.logger") - -// Mark activity for async completion -context.doNotCompleteOnReturn() -val taskToken = context.taskToken -``` - -## KActivityContext Interface - -```kotlin -interface KActivityContext { - val info: KActivityInfo - fun heartbeat(details: Any? = null) - fun heartbeatDetails(detailsClass: Class): T? - val taskToken: ByteArray - fun doNotCompleteOnReturn() - val isDoNotCompleteOnReturn: Boolean - fun logger(): Logger - fun logger(name: String): Logger - fun logger(clazz: Class<*>): Logger -} - -// Reified extension for easier Kotlin usage -inline fun KActivityContext.heartbeatDetails(): T? -``` - -## KActivityInfo Interface - -```kotlin -interface KActivityInfo { - val activityId: String - val activityType: String - val workflowId: String - val attempt: Int - // ... other properties -} -``` - -> **Note:** `ActivityCompletionClient` for async activity completion uses the same API as the Java SDK. - ## Related - [Activity Definition](./definition.md) - Interface patterns From e259002cac508b974d589c30b6220e827dd115ed Mon Sep 17 00:00:00 2001 From: Maxim Fateev Date: Tue, 6 Jan 2026 20:08:16 -0800 Subject: [PATCH 50/83] Show suspend method support in local activities example --- kotlin/activities/local-activities.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/kotlin/activities/local-activities.md b/kotlin/activities/local-activities.md index e395958..c0566e7 100644 --- a/kotlin/activities/local-activities.md +++ b/kotlin/activities/local-activities.md @@ -7,8 +7,8 @@ Local activities use the same stub-less pattern as regular activities. ```kotlin @ActivityInterface interface ValidationActivities { - fun validate(input: String): Boolean - fun sanitize(input: String): String + suspend fun validate(input: String): Boolean + fun sanitize(input: String): String // Non-suspend also supported } val isValid = KWorkflow.executeLocalActivity( From 08bc977331160272dca0652dde96bd7b99eb521c Mon Sep 17 00:00:00 2001 From: Maxim Fateev Date: Tue, 6 Jan 2026 20:09:02 -0800 Subject: [PATCH 51/83] Remove 'When to Use Local Activities' - assumes Temporal knowledge --- kotlin/activities/local-activities.md | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/kotlin/activities/local-activities.md b/kotlin/activities/local-activities.md index c0566e7..b722b67 100644 --- a/kotlin/activities/local-activities.md +++ b/kotlin/activities/local-activities.md @@ -60,18 +60,6 @@ data class KLocalActivityOptions( ) ``` -## When to Use Local Activities - -Local activities are best for: -- Short-lived operations (< 10 seconds) -- Operations that don't need to survive worker restarts -- High-frequency operations where scheduling overhead matters - -Use regular activities for: -- Long-running operations -- Operations that need heartbeating -- Operations that must survive worker failures - ## Related - [Activity Definition](./definition.md) - Interface patterns From a35e4977b17f22c02865fd74762d90a5eb1f002a Mon Sep 17 00:00:00 2001 From: Maxim Fateev Date: Tue, 6 Jan 2026 20:14:08 -0800 Subject: [PATCH 52/83] Document KActivity.context availability for local activities - KActivity.context available for both regular and local activities - heartbeat() is no-op for local activities - heartbeatDetails() returns null for local activities - taskToken and doNotCompleteOnReturn() throw for local activities - Add isLocal property to KActivityInfo --- kotlin/activities/implementation.md | 26 ++++++++++++++------------ 1 file changed, 14 insertions(+), 12 deletions(-) diff --git a/kotlin/activities/implementation.md b/kotlin/activities/implementation.md index 14e7393..9582b68 100644 --- a/kotlin/activities/implementation.md +++ b/kotlin/activities/implementation.md @@ -112,26 +112,27 @@ override suspend fun resumableProcess(data: List): ProcessResult { ## KActivity API -`KActivity.context` provides access to the activity execution context: +`KActivity.context` provides access to the activity execution context for both regular and local activities: ```kotlin // In activity implementation val context = KActivity.context -// Get activity info +// Get activity info (works for both regular and local activities) val info = context.info println("Activity ${info.activityType}, attempt ${info.attempt}") +println("Is local: ${info.isLocal}") -// Heartbeat for long-running activities +// Heartbeat for long-running activities (no-op for local activities) context.heartbeat(progressDetails) -// Get heartbeat details from previous attempt +// Get heartbeat details from previous attempt (empty for local activities) val previousProgress = context.heartbeatDetails() -// Logging - use activity logger for proper log context -val log = context.logger() // Uses activity type as logger name +// Logging (works for both) +val log = context.logger() -// Mark activity for async completion +// Regular activities only - throws UnsupportedOperationException for local activities context.doNotCompleteOnReturn() val taskToken = context.taskToken ``` @@ -141,10 +142,10 @@ val taskToken = context.taskToken ```kotlin interface KActivityContext { val info: KActivityInfo - fun heartbeat(details: Any? = null) - fun heartbeatDetails(detailsClass: Class): T? - val taskToken: ByteArray - fun doNotCompleteOnReturn() + fun heartbeat(details: Any? = null) // No-op for local activities + fun heartbeatDetails(detailsClass: Class): T? // Empty for local activities + val taskToken: ByteArray // Throws for local activities + fun doNotCompleteOnReturn() // Throws for local activities val isDoNotCompleteOnReturn: Boolean fun logger(): Logger fun logger(name: String): Logger @@ -163,11 +164,12 @@ interface KActivityInfo { val activityType: String val workflowId: String val attempt: Int + val isLocal: Boolean // True for local activities // ... other properties } ``` -> **Note:** `ActivityCompletionClient` for async activity completion uses the same API as the Java SDK. +> **Note:** `ActivityCompletionClient` for async activity completion uses the same API as the Java SDK. Async completion is not supported for local activities. ## Related From 9f492c26a17bb27675601a8a4977a3c045da8f47 Mon Sep 17 00:00:00 2001 From: Maxim Fateev Date: Tue, 6 Jan 2026 20:15:40 -0800 Subject: [PATCH 53/83] Add KActivity.context limitations table to local-activities.md --- kotlin/activities/local-activities.md | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/kotlin/activities/local-activities.md b/kotlin/activities/local-activities.md index b722b67..3a4f513 100644 --- a/kotlin/activities/local-activities.md +++ b/kotlin/activities/local-activities.md @@ -60,6 +60,19 @@ data class KLocalActivityOptions( ) ``` +## KActivity.context in Local Activities + +`KActivity.context` is available in local activities with limited functionality: + +| Feature | Local Activity Behavior | +|---------|------------------------| +| `context.info` | Works (`info.isLocal` returns `true`) | +| `context.logger()` | Works | +| `context.heartbeat()` | No-op (ignored) | +| `context.heartbeatDetails()` | Returns `null` | +| `context.taskToken` | Throws `UnsupportedOperationException` | +| `context.doNotCompleteOnReturn()` | Throws `UnsupportedOperationException` | + ## Related - [Activity Definition](./definition.md) - Interface patterns From 75878d66dbc998c29bee1ded9ad8e807c24b1dc1 Mon Sep 17 00:00:00 2001 From: Maxim Fateev Date: Tue, 6 Jan 2026 20:22:53 -0800 Subject: [PATCH 54/83] Make SignalWithStart and UpdateWithStart consistent Both now use KWithStartWorkflowOperation: - withStartWorkflowOperation() creates the start operation - signalWithStart(startOp, signal, arg) sends signal - executeUpdateWithStart(startOp, update, options, arg) executes update - startUpdateWithStart(startOp, update, options, arg) starts update async Simplified KUpdateWithStartOptions - removed startWorkflowOperation field --- kotlin/client/advanced.md | 147 ++++++++++++++++++-------------------- 1 file changed, 68 insertions(+), 79 deletions(-) diff --git a/kotlin/client/advanced.md b/kotlin/client/advanced.md index eb3b15e..6b48cf2 100644 --- a/kotlin/client/advanced.md +++ b/kotlin/client/advanced.md @@ -1,20 +1,41 @@ # Advanced Client Operations +Both SignalWithStart and UpdateWithStart use `KWithStartWorkflowOperation` for consistency. + +## KWithStartWorkflowOperation + +```kotlin +/** + * Represents a workflow start operation for use with signal-with-start or update-with-start. + * Created via KWorkflowClient.withStartWorkflowOperation(). + */ +class KWithStartWorkflowOperation { + /** The workflow ID from the options. */ + val workflowId: String + + /** Suspends until the workflow completes and returns its result. */ + suspend fun result(): R +} +``` + ## SignalWithStart Atomically start a workflow and send a signal. If the workflow already exists, only the signal is sent: ```kotlin -// Returns KTypedWorkflowHandle - result type captured from method reference -val handle = client.signalWithStart( - workflow = OrderWorkflow::processOrder, - options = KWorkflowOptions( +val startOp = client.withStartWorkflowOperation( + OrderWorkflow::processOrder, + KWorkflowOptions( workflowId = "order-123", taskQueue = "orders" ), - workflowArg = order, - signal = OrderWorkflow::updatePriority, - signalArg = Priority.HIGH + order +) + +val handle = client.signalWithStart( + startOp, + OrderWorkflow::updatePriority, + Priority.HIGH ) // Can use typed handle for queries/signals @@ -28,26 +49,13 @@ Atomically start a workflow and send an update. Behavior depends on `workflowIdC - `USE_EXISTING`: sends update to existing workflow - `FAIL`: throws exception if workflow already exists -### Supporting Types +### KUpdateWithStartOptions ```kotlin -/** - * Represents a workflow start operation for use with update-with-start. - * Created via KWorkflowClient.withStartWorkflowOperation(). - */ -class KWithStartWorkflowOperation { - /** Suspends until the workflow completes and returns its result. */ - suspend fun result(): R -} - /** * Options for update-with-start operations. - * Bundles the start operation with update-specific options. */ -data class KUpdateWithStartOptions( - /** The workflow start operation (required) */ - val startWorkflowOperation: KWithStartWorkflowOperation, - +data class KUpdateWithStartOptions( /** Stage to wait for before returning (required for startUpdateWithStart) */ val waitForStage: WorkflowUpdateStage = WorkflowUpdateStage.ACCEPTED, @@ -59,26 +67,25 @@ data class KUpdateWithStartOptions( ### Execute and Wait for Completion ```kotlin -// Step 1: Create the workflow start operation val startOp = client.withStartWorkflowOperation( OrderWorkflow::processOrder, KWorkflowOptions( workflowId = "order-123", taskQueue = "orders", - workflowIdConflictPolicy = WorkflowIdConflictPolicy.USE_EXISTING // Required + workflowIdConflictPolicy = WorkflowIdConflictPolicy.USE_EXISTING ), order ) -// Step 2: Execute update with start (waits for update completion) +// Execute update with start (waits for update completion) val updateResult: Boolean = client.executeUpdateWithStart( - OrderWorkflow::addItem, // Must be suspend function - KUpdateWithStartOptions(startWorkflowOperation = startOp), + startOp, + OrderWorkflow::addItem, + KUpdateWithStartOptions(), newItem ) -println("Item added: $updateResult") -// Step 3: Access workflow result if needed (suspends until workflow completes) +// Access workflow result if needed val workflowResult: OrderResult = startOp.result() ``` @@ -97,11 +104,9 @@ val startOp = client.withStartWorkflowOperation( // Start update and return immediately after it's accepted val updateHandle: KUpdateHandle = client.startUpdateWithStart( + startOp, OrderWorkflow::addItem, - KUpdateWithStartOptions( - startWorkflowOperation = startOp, - waitForStage = WorkflowUpdateStage.ACCEPTED - ), + KUpdateWithStartOptions(waitForStage = WorkflowUpdateStage.ACCEPTED), newItem ) @@ -109,42 +114,12 @@ val updateHandle: KUpdateHandle = client.startUpdateWithStart( val result = updateHandle.result() ``` -### No Arguments - -```kotlin -val startOp = client.withStartWorkflowOperation( - GreetingWorkflow::greet, - KWorkflowOptions( - workflowId = "greeting-789", - taskQueue = "greetings", - workflowIdConflictPolicy = WorkflowIdConflictPolicy.USE_EXISTING - ) -) - -val status: String = client.executeUpdateWithStart( - GreetingWorkflow::getStatus, // suspend fun getStatus(): String - KUpdateWithStartOptions(startWorkflowOperation = startOp) -) -``` - ## KWorkflowClient API for Advanced Operations ```kotlin class KWorkflowClient { /** - * Atomically start a workflow and send a signal. - * If the workflow already exists, only the signal is sent. - */ - suspend fun signalWithStart( - workflow: KFunction2, - options: KWorkflowOptions, - workflowArg: A1, - signal: KFunction2, - signalArg: SA1 - ): KTypedWorkflowHandle - - /** - * Create a workflow start operation for use with update-with-start. + * Create a workflow start operation for use with signal-with-start or update-with-start. */ fun withStartWorkflowOperation( workflow: KFunction1, @@ -158,33 +133,47 @@ class KWorkflowClient { ): KWithStartWorkflowOperation /** - * Atomically start a workflow and send an update, returning immediately after - * the update reaches the specified wait stage. + * Atomically start a workflow and send a signal. + * If the workflow already exists, only the signal is sent. */ - suspend fun startUpdateWithStart( - update: KSuspendFunction1, - options: KUpdateWithStartOptions - ): KUpdateHandle - - suspend fun startUpdateWithStart( - update: KSuspendFunction2, - options: KUpdateWithStartOptions, - updateArg1: UA1 - ): KUpdateHandle + suspend fun signalWithStart( + startOp: KWithStartWorkflowOperation, + signal: KFunction2, + signalArg: SA1 + ): KTypedWorkflowHandle /** * Atomically start a workflow and execute an update, waiting for completion. */ suspend fun executeUpdateWithStart( + startOp: KWithStartWorkflowOperation, update: KSuspendFunction1, - options: KUpdateWithStartOptions + options: KUpdateWithStartOptions = KUpdateWithStartOptions() ): UR suspend fun executeUpdateWithStart( + startOp: KWithStartWorkflowOperation, update: KSuspendFunction2, - options: KUpdateWithStartOptions, - updateArg1: UA1 + options: KUpdateWithStartOptions = KUpdateWithStartOptions(), + updateArg: UA1 ): UR + + /** + * Atomically start a workflow and send an update, returning immediately after + * the update reaches the specified wait stage. + */ + suspend fun startUpdateWithStart( + startOp: KWithStartWorkflowOperation, + update: KSuspendFunction1, + options: KUpdateWithStartOptions = KUpdateWithStartOptions() + ): KUpdateHandle + + suspend fun startUpdateWithStart( + startOp: KWithStartWorkflowOperation, + update: KSuspendFunction2, + options: KUpdateWithStartOptions = KUpdateWithStartOptions(), + updateArg: UA1 + ): KUpdateHandle } ``` From 553d34bab86a9774b2b11e9974281bd2ae1d730c Mon Sep 17 00:00:00 2001 From: Maxim Fateev Date: Tue, 6 Jan 2026 20:26:27 -0800 Subject: [PATCH 55/83] Change KWithStartWorkflowOperation from class to interface --- kotlin/client/advanced.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kotlin/client/advanced.md b/kotlin/client/advanced.md index 6b48cf2..8ee2076 100644 --- a/kotlin/client/advanced.md +++ b/kotlin/client/advanced.md @@ -9,7 +9,7 @@ Both SignalWithStart and UpdateWithStart use `KWithStartWorkflowOperation` for c * Represents a workflow start operation for use with signal-with-start or update-with-start. * Created via KWorkflowClient.withStartWorkflowOperation(). */ -class KWithStartWorkflowOperation { +interface KWithStartWorkflowOperation { /** The workflow ID from the options. */ val workflowId: String From b70eac119aef236e0e2cc09f16e0506b2f16f216 Mon Sep 17 00:00:00 2001 From: Maxim Fateev Date: Tue, 6 Jan 2026 21:31:09 -0800 Subject: [PATCH 56/83] Simplify with-start API and use KSuspendFunction consistently - withStartWorkflowOperation now returns KTypedWorkflowHandle directly (handle is not usable until signalWithStart/updateWithStart is called) - signalWithStart returns void instead of handle - Remove separate KWithStartWorkflowOperation interface - Use KSuspendFunction for workflow methods, signals, and updates - Keep KFunction for queries (queries are not suspend) --- kotlin/client/advanced.md | 79 ++++++++++++++------------------ kotlin/client/workflow-client.md | 8 ++-- kotlin/client/workflow-handle.md | 26 +++++------ 3 files changed, 52 insertions(+), 61 deletions(-) diff --git a/kotlin/client/advanced.md b/kotlin/client/advanced.md index 8ee2076..135923a 100644 --- a/kotlin/client/advanced.md +++ b/kotlin/client/advanced.md @@ -1,29 +1,14 @@ # Advanced Client Operations -Both SignalWithStart and UpdateWithStart use `KWithStartWorkflowOperation` for consistency. - -## KWithStartWorkflowOperation - -```kotlin -/** - * Represents a workflow start operation for use with signal-with-start or update-with-start. - * Created via KWorkflowClient.withStartWorkflowOperation(). - */ -interface KWithStartWorkflowOperation { - /** The workflow ID from the options. */ - val workflowId: String - - /** Suspends until the workflow completes and returns its result. */ - suspend fun result(): R -} -``` +Both SignalWithStart and UpdateWithStart use `withStartWorkflowOperation` to create a handle that becomes usable after the atomic operation completes. ## SignalWithStart Atomically start a workflow and send a signal. If the workflow already exists, only the signal is sent: ```kotlin -val startOp = client.withStartWorkflowOperation( +// Create handle (not yet started) +val handle = client.withStartWorkflowOperation( OrderWorkflow::processOrder, KWorkflowOptions( workflowId = "order-123", @@ -32,13 +17,10 @@ val startOp = client.withStartWorkflowOperation( order ) -val handle = client.signalWithStart( - startOp, - OrderWorkflow::updatePriority, - Priority.HIGH -) +// Atomically start workflow and send signal +client.signalWithStart(handle, OrderWorkflow::updatePriority, Priority.HIGH) -// Can use typed handle for queries/signals +// Handle is now usable for queries/signals/result val status = handle.query(OrderWorkflow::status) val result = handle.result() // Type inferred as OrderResult ``` @@ -67,7 +49,8 @@ data class KUpdateWithStartOptions( ### Execute and Wait for Completion ```kotlin -val startOp = client.withStartWorkflowOperation( +// Create handle (not yet started) +val handle = client.withStartWorkflowOperation( OrderWorkflow::processOrder, KWorkflowOptions( workflowId = "order-123", @@ -79,20 +62,21 @@ val startOp = client.withStartWorkflowOperation( // Execute update with start (waits for update completion) val updateResult: Boolean = client.executeUpdateWithStart( - startOp, + handle, OrderWorkflow::addItem, KUpdateWithStartOptions(), newItem ) -// Access workflow result if needed -val workflowResult: OrderResult = startOp.result() +// Handle is now usable +val workflowResult: OrderResult = handle.result() ``` ### Start Async (Don't Wait for Completion) ```kotlin -val startOp = client.withStartWorkflowOperation( +// Create handle (not yet started) +val handle = client.withStartWorkflowOperation( OrderWorkflow::processOrder, KWorkflowOptions( workflowId = "order-456", @@ -104,14 +88,17 @@ val startOp = client.withStartWorkflowOperation( // Start update and return immediately after it's accepted val updateHandle: KUpdateHandle = client.startUpdateWithStart( - startOp, + handle, OrderWorkflow::addItem, KUpdateWithStartOptions(waitForStage = WorkflowUpdateStage.ACCEPTED), newItem ) -// Later: get the update result -val result = updateHandle.result() +// Handle is now usable, get workflow result +val workflowResult = handle.result() + +// Get update result when needed +val updateResult = updateHandle.result() ``` ## KWorkflowClient API for Advanced Operations @@ -119,40 +106,43 @@ val result = updateHandle.result() ```kotlin class KWorkflowClient { /** - * Create a workflow start operation for use with signal-with-start or update-with-start. + * Create a workflow handle for use with signal-with-start or update-with-start. + * The handle is not usable until signalWithStart or an update-with-start method is called. */ fun withStartWorkflowOperation( - workflow: KFunction1, + workflow: KSuspendFunction1, options: KWorkflowOptions - ): KWithStartWorkflowOperation + ): KTypedWorkflowHandle fun withStartWorkflowOperation( - workflow: KFunction2, + workflow: KSuspendFunction2, options: KWorkflowOptions, arg1: A1 - ): KWithStartWorkflowOperation + ): KTypedWorkflowHandle /** * Atomically start a workflow and send a signal. * If the workflow already exists, only the signal is sent. + * After this call, the handle becomes usable. */ suspend fun signalWithStart( - startOp: KWithStartWorkflowOperation, - signal: KFunction2, + handle: KTypedWorkflowHandle, + signal: KSuspendFunction2, signalArg: SA1 - ): KTypedWorkflowHandle + ) /** * Atomically start a workflow and execute an update, waiting for completion. + * After this call, the handle becomes usable. */ suspend fun executeUpdateWithStart( - startOp: KWithStartWorkflowOperation, + handle: KTypedWorkflowHandle, update: KSuspendFunction1, options: KUpdateWithStartOptions = KUpdateWithStartOptions() ): UR suspend fun executeUpdateWithStart( - startOp: KWithStartWorkflowOperation, + handle: KTypedWorkflowHandle, update: KSuspendFunction2, options: KUpdateWithStartOptions = KUpdateWithStartOptions(), updateArg: UA1 @@ -161,15 +151,16 @@ class KWorkflowClient { /** * Atomically start a workflow and send an update, returning immediately after * the update reaches the specified wait stage. + * After this call, the handle becomes usable. */ suspend fun startUpdateWithStart( - startOp: KWithStartWorkflowOperation, + handle: KTypedWorkflowHandle, update: KSuspendFunction1, options: KUpdateWithStartOptions = KUpdateWithStartOptions() ): KUpdateHandle suspend fun startUpdateWithStart( - startOp: KWithStartWorkflowOperation, + handle: KTypedWorkflowHandle, update: KSuspendFunction2, options: KUpdateWithStartOptions = KUpdateWithStartOptions(), updateArg: UA1 diff --git a/kotlin/client/workflow-client.md b/kotlin/client/workflow-client.md index 112f22e..a04280b 100644 --- a/kotlin/client/workflow-client.md +++ b/kotlin/client/workflow-client.md @@ -40,12 +40,12 @@ class KWorkflowClient( * Does not wait for the workflow to complete. */ suspend fun startWorkflow( - workflow: KFunction1, + workflow: KSuspendFunction1, options: KWorkflowOptions ): KTypedWorkflowHandle suspend fun startWorkflow( - workflow: KFunction2, + workflow: KSuspendFunction2, options: KWorkflowOptions, arg: A1 ): KTypedWorkflowHandle @@ -57,12 +57,12 @@ class KWorkflowClient( * Suspends until the workflow completes. */ suspend fun executeWorkflow( - workflow: KFunction1, + workflow: KSuspendFunction1, options: KWorkflowOptions ): R suspend fun executeWorkflow( - workflow: KFunction2, + workflow: KSuspendFunction2, options: KWorkflowOptions, arg: A1 ): R diff --git a/kotlin/client/workflow-handle.md b/kotlin/client/workflow-handle.md index 1f478f2..82aa420 100644 --- a/kotlin/client/workflow-handle.md +++ b/kotlin/client/workflow-handle.md @@ -46,22 +46,22 @@ interface KWorkflowHandle { // Result - requires explicit type since we don't know it suspend fun result(): R - // Signals - type-safe method references - suspend fun signal(method: KFunction1) - suspend fun signal(method: KFunction2, arg: A1) - suspend fun signal(method: KFunction3, arg1: A1, arg2: A2) + // Signals - type-safe method references (suspend functions) + suspend fun signal(method: KSuspendFunction1) + suspend fun signal(method: KSuspendFunction2, arg: A1) + suspend fun signal(method: KSuspendFunction3, arg1: A1, arg2: A2) - // Queries - type-safe method references + // Queries - type-safe method references (NOT suspend) fun query(method: KFunction1): R fun query(method: KFunction2, arg: A1): R - // Updates - execute and wait for result - suspend fun executeUpdate(method: KFunction1): R - suspend fun executeUpdate(method: KFunction2, arg: A1): R + // Updates - execute and wait for result (suspend functions) + suspend fun executeUpdate(method: KSuspendFunction1): R + suspend fun executeUpdate(method: KSuspendFunction2, arg: A1): R - // Updates - start and get handle for async result - suspend fun startUpdate(method: KFunction1): KUpdateHandle - suspend fun startUpdate(method: KFunction2, arg: A1): KUpdateHandle + // Updates - start and get handle for async result (suspend functions) + suspend fun startUpdate(method: KSuspendFunction1): KUpdateHandle + suspend fun startUpdate(method: KSuspendFunction2, arg: A1): KUpdateHandle // Get handle for existing update by ID fun getKUpdateHandle(updateId: String): KUpdateHandle @@ -89,14 +89,14 @@ interface KTypedWorkflowHandle : KWorkflowHandle { ```kotlin // startWorkflow captures result type from method reference suspend fun startWorkflow( - workflow: KFunction2, // R is captured here + workflow: KSuspendFunction2, // R is captured here options: KWorkflowOptions, arg: A1 ): KTypedWorkflowHandle // R is preserved in return type // Usage - result type is inferred val handle = client.startWorkflow( - OrderWorkflow::processOrder, // KFunction2 + OrderWorkflow::processOrder, // KSuspendFunction2 options, order ) From 86382291cd21c02eaea6a41299bdacd4414947f1 Mon Sep 17 00:00:00 2001 From: Maxim Fateev Date: Tue, 6 Jan 2026 21:37:00 -0800 Subject: [PATCH 57/83] Clean up worker setup documentation - Remove maxWorkflowThreadCount example (not needed for coroutines) - Remove redundant Complete Worker Example section --- kotlin/worker/setup.md | 36 +----------------------------------- 1 file changed, 1 insertion(+), 35 deletions(-) diff --git a/kotlin/worker/setup.md b/kotlin/worker/setup.md index 435164a..e67fedc 100644 --- a/kotlin/worker/setup.md +++ b/kotlin/worker/setup.md @@ -9,9 +9,7 @@ val service = WorkflowServiceStubs.newLocalServiceStubs() val client = KWorkflowClient(service) { ... } // KWorkerFactory automatically enables Kotlin coroutine support -val factory = KWorkerFactory(client) { - maxWorkflowThreadCount = 800 -} +val factory = KWorkerFactory(client) val worker: KWorker = factory.newWorker("task-queue") { maxConcurrentActivityExecutionSize = 100 @@ -134,38 +132,6 @@ worker.registerWorkflowImplementationTypes( factory.start() ``` -## Complete Worker Example - -```kotlin -fun main() = runBlocking { - // Initialize services (could use DI framework) - val inventoryService = InventoryServiceImpl() - val paymentService = PaymentServiceImpl() - val shippingService = ShippingServiceImpl() - val notificationService = NotificationServiceImpl() - - val service = WorkflowServiceStubs.newLocalServiceStubs() - - // Create KWorkflowClient for Kotlin-specific APIs - val client = KWorkflowClient(service) - - // KWorkerFactory automatically enables Kotlin coroutine support - val factory = KWorkerFactory(client) - val worker: KWorker = factory.newWorker("orders") - - // Plugin handles suspend functions automatically - worker.registerWorkflowImplementationTypes(OrderWorkflowImpl::class) - worker.registerActivitiesImplementations( - OrderActivitiesImpl(inventoryService, paymentService, shippingService, notificationService) - ) - - factory.start() - - // Keep running until shutdown signal - // ... -} -``` - ## Related - [Interceptors](../configuration/interceptors.md) - Adding cross-cutting concerns From 2a727c8dd8c1d9d48d114fea6080cf16239fddc9 Mon Sep 17 00:00:00 2001 From: Maxim Fateev Date: Tue, 6 Jan 2026 21:42:12 -0800 Subject: [PATCH 58/83] Use idiomatic Kotlin naming in API parity doc MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Use property syntax instead of get* methods for KWorkflow APIs: - getTypedSearchAttributes() → typedSearchAttributes - getMemo() → memo - getLastCompletionResult() → lastCompletionResult() - getVersion() → version() - Add KActivityInfo properties (previously listed as gaps): - workflowType - currentAttemptScheduledTimestamp - retryOptions --- kotlin/api-parity.md | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/kotlin/api-parity.md b/kotlin/api-parity.md index 4994a8b..d93e215 100644 --- a/kotlin/api-parity.md +++ b/kotlin/api-parity.md @@ -67,16 +67,16 @@ The following Java SDK workflow APIs have Kotlin equivalents in `KWorkflow`: | Java SDK API | Kotlin SDK | |--------------|------------| -| `Workflow.getTypedSearchAttributes()` | `KWorkflow.getTypedSearchAttributes()` | +| `Workflow.getTypedSearchAttributes()` | `KWorkflow.typedSearchAttributes` | | `Workflow.upsertTypedSearchAttributes(...)` | `KWorkflow.upsertTypedSearchAttributes()` | -| `Workflow.getMemo(key, class)` | `KWorkflow.getMemo()` | +| `Workflow.getMemo(key, class)` | `KWorkflow.memo` | | `Workflow.upsertMemo(...)` | `KWorkflow.upsertMemo()` | ### Workflow State & Context | Java SDK API | Kotlin SDK | |--------------|------------| -| `Workflow.getLastCompletionResult(class)` | `KWorkflow.getLastCompletionResult()` | +| `Workflow.getLastCompletionResult(class)` | `KWorkflow.lastCompletionResult()` | | `Workflow.getPreviousRunFailure()` | `KWorkflow.previousRunFailure` | | `Workflow.isReplaying()` | `KWorkflow.isReplaying` | | `Workflow.getCurrentUpdateInfo()` | `KWorkflow.currentUpdateInfo` | @@ -91,7 +91,7 @@ The following Java SDK workflow APIs have Kotlin equivalents in `KWorkflow`: |--------------|------------| | `Workflow.sideEffect(...)` | `KWorkflow.sideEffect()` | | `Workflow.mutableSideEffect(...)` | `KWorkflow.mutableSideEffect()` | -| `Workflow.getVersion(...)` | `KWorkflow.getVersion()` | +| `Workflow.getVersion(...)` | `KWorkflow.version()` | | `Workflow.retry(...)` | `KWorkflow.retry()` | | `Workflow.randomUUID()` | `KWorkflow.randomUUID()` | | `Workflow.newRandom()` | `KWorkflow.newRandom()` | @@ -116,13 +116,13 @@ The following Java SDK workflow APIs have Kotlin equivalents in `KWorkflow`: | `Workflow.startNexusOperation(...)` | Nexus support - deferred to separate project | | `Workflow.getInstance()` | Advanced use case - low priority | -## KActivityInfo Gaps +## KActivityInfo -| Java ActivityInfo Field | Status | -|------------------------|--------| -| `workflowType` | Missing - workflow type that called the activity | -| `currentAttemptScheduledTimestamp` | Missing - current attempt schedule time | -| `retryOptions` | Missing - activity retry options | +| Java ActivityInfo | Kotlin SDK | +|-------------------|------------| +| `getWorkflowType()` | `KActivityInfo.workflowType` | +| `getCurrentAttemptScheduledTimestamp()` | `KActivityInfo.currentAttemptScheduledTimestamp` | +| `getRetryOptions()` | `KActivityInfo.retryOptions` | ## Related From 3a7ac102960d5386b4d16c3963d1a6b56e6d80c5 Mon Sep 17 00:00:00 2001 From: Maxim Fateev Date: Wed, 7 Jan 2026 09:15:51 -0800 Subject: [PATCH 59/83] Update Kotlin SDK spec based on gap validation analysis MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Update dynamic handler signatures to use KEncodedValues - Change query methods to suspend functions - Update signal types to KFunction - Add resultClass param to getUpdateHandle - Document additional properties and constraints - Note architectural change: handle interfaces → classes --- kotlin/activities/implementation.md | 22 +++- kotlin/client/workflow-client.md | 25 +---- kotlin/client/workflow-handle.md | 150 +++++++++++++++++++++------- kotlin/configuration/koptions.md | 86 ++++++++++++++-- kotlin/workflows/signals-queries.md | 91 ++++++++++++++--- 5 files changed, 289 insertions(+), 85 deletions(-) diff --git a/kotlin/activities/implementation.md b/kotlin/activities/implementation.md index 9582b68..566f307 100644 --- a/kotlin/activities/implementation.md +++ b/kotlin/activities/implementation.md @@ -160,12 +160,30 @@ inline fun KActivityContext.heartbeatDetails(): T? ```kotlin interface KActivityInfo { + // Core identifiers val activityId: String val activityType: String val workflowId: String + val runId: String + val namespace: String + + // Execution context + val taskQueue: String val attempt: Int - val isLocal: Boolean // True for local activities - // ... other properties + val isLocal: Boolean + + // Timing information + val scheduledTime: Instant? + val startedTime: Instant? + val scheduleToCloseTimeout: Duration? + val startToCloseTimeout: Duration? + val heartbeatTimeout: Duration? + + // Heartbeat state + val heartbeatDetails: Any? + + // Task token for async completion (throws for local activities) + val taskToken: ByteArray } ``` diff --git a/kotlin/client/workflow-client.md b/kotlin/client/workflow-client.md index a04280b..9258c5b 100644 --- a/kotlin/client/workflow-client.md +++ b/kotlin/client/workflow-client.md @@ -113,30 +113,7 @@ val result = handle.result() // Type inferred as String from method reference ## KWorkflowOptions -```kotlin -/** - * Kotlin-native workflow options for client execution. - * workflowId and taskQueue are optional - if not provided, will use defaults or generate UUID. - */ -data class KWorkflowOptions( - val workflowId: String? = null, - val workflowIdReusePolicy: WorkflowIdReusePolicy? = null, - val workflowIdConflictPolicy: WorkflowIdConflictPolicy? = null, - val workflowRunTimeout: Duration? = null, - val workflowExecutionTimeout: Duration? = null, - val workflowTaskTimeout: Duration? = null, - val taskQueue: String? = null, - val retryOptions: KRetryOptions? = null, - val cronSchedule: String? = null, - val memo: Map? = null, - val typedSearchAttributes: SearchAttributes? = null, - val disableEagerExecution: Boolean = true, - val startDelay: Duration? = null, - val staticSummary: String? = null, - val staticDetails: String? = null, - val priority: Priority? = null -) -``` +See [KOptions](../configuration/koptions.md#kworkflowoptions) for the full `KWorkflowOptions` reference. ## Related diff --git a/kotlin/client/workflow-handle.md b/kotlin/client/workflow-handle.md index 82aa420..2b0b024 100644 --- a/kotlin/client/workflow-handle.md +++ b/kotlin/client/workflow-handle.md @@ -21,66 +21,84 @@ val result = handle.result() // Updates - execute and wait for result val updateResult = handle.executeUpdate(OrderWorkflow::addItem, newItem) -// Or start update async and get handle -val updateHandle = handle.startUpdate(OrderWorkflow::addItem, newItem) -val asyncResult = updateHandle.result() - // Cancel or terminate handle.cancel() handle.terminate("No longer needed") // Workflow metadata -val info = handle.describe() +val description = handle.describe() println("Workflow ID: ${handle.workflowId}, Run ID: ${handle.runId}") +println("Status: ${description.status}") ``` +## Architectural Note: Classes vs Interfaces + +All handle types (`KWorkflowHandle`, `KTypedWorkflowHandle`, `KUpdateHandle`, `WorkflowHandle`, `KChildWorkflowHandle`) are **classes** rather than interfaces. This design choice enables: + +1. **Reified type parameters** - `inline fun ` methods directly on handle types +2. **Better IDE discoverability** - Methods appear directly in autocomplete +3. **No extension function workarounds** - No need for separate extension functions for reified generics +4. **Full testing support** - mockk and other frameworks can mock classes + ## KWorkflowHandle API ```kotlin // Base handle - returned by getWorkflowHandle(id) // Result type is unknown, must specify when calling result() -interface KWorkflowHandle { - val workflowId: String - val runId: String? - +open class KWorkflowHandle( + val workflowId: String, + val runId: String?, + val execution: WorkflowExecution, + // ... internal state +) { // Result - requires explicit type since we don't know it - suspend fun result(): R + suspend fun result(resultClass: Class): R + inline suspend fun result(): R // Reified version - // Signals - type-safe method references (suspend functions) - suspend fun signal(method: KSuspendFunction1) - suspend fun signal(method: KSuspendFunction2, arg: A1) - suspend fun signal(method: KSuspendFunction3, arg1: A1, arg2: A2) + // Signals - type-safe method references + // Note: Signal handlers can be either suspend or non-suspend functions + suspend fun signal(method: KFunction1) + suspend fun signal(method: KFunction2, arg: A1) + suspend fun signal(method: KFunction3, arg1: A1, arg2: A2) - // Queries - type-safe method references (NOT suspend) - fun query(method: KFunction1): R - fun query(method: KFunction2, arg: A1): R + // Queries - type-safe method references (suspend for network I/O) + suspend fun query(method: KFunction1): R + suspend fun query(method: KFunction2, arg: A1): R // Updates - execute and wait for result (suspend functions) suspend fun executeUpdate(method: KSuspendFunction1): R suspend fun executeUpdate(method: KSuspendFunction2, arg: A1): R - // Updates - start and get handle for async result (suspend functions) - suspend fun startUpdate(method: KSuspendFunction1): KUpdateHandle - suspend fun startUpdate(method: KSuspendFunction2, arg: A1): KUpdateHandle - // Get handle for existing update by ID - fun getKUpdateHandle(updateId: String): KUpdateHandle + fun getUpdateHandle(updateId: String, resultClass: Class): KUpdateHandle + inline fun getUpdateHandle(updateId: String): KUpdateHandle // Reified version // Lifecycle suspend fun cancel() suspend fun terminate(reason: String? = null) - fun describe(): WorkflowExecutionInfo + suspend fun describe(): KWorkflowExecutionDescription + + // Java SDK interop + fun toStub(): WorkflowStub } ``` +**Note on query methods:** Although queries are synchronous within the workflow, client-side query calls involve network I/O to the Temporal service, which is why they are `suspend` functions following idiomatic Kotlin patterns. + ## KTypedWorkflowHandle Extended handle returned by `startWorkflow()` - result type R is captured from the workflow method reference: ```kotlin -interface KTypedWorkflowHandle : KWorkflowHandle { +class KTypedWorkflowHandle( + workflowId: String, + runId: String?, + execution: WorkflowExecution, + // ... internal state +) : KWorkflowHandle(...) { // Result type is known from method reference - no type parameter needed suspend fun result(): R + suspend fun result(timeout: java.time.Duration): R } ``` @@ -110,12 +128,64 @@ val result = existingHandle.result() // Must specify type ## KUpdateHandle ```kotlin -interface KUpdateHandle { - val updateId: String +class KUpdateHandle( + val updateId: String, + val execution: WorkflowExecution, + // ... internal state +) { suspend fun result(): R + suspend fun result(timeout: java.time.Duration): R } ``` +## KWorkflowExecutionDescription + +Kotlin wrapper for workflow execution description with idiomatic API: + +```kotlin +class KWorkflowExecutionDescription( + private val delegate: WorkflowExecutionDescription +) { + // Properties from WorkflowExecutionMetadata + val execution: WorkflowExecution + val workflowType: String + val taskQueue: String + val startTime: Instant + val executionTime: Instant + val closeTime: Instant? + val status: WorkflowExecutionStatus + val historyLength: Long + val parentNamespace: String? + val parentExecution: WorkflowExecution? + val rootExecution: WorkflowExecution? + val firstRunId: String? + val executionDuration: Duration? + val typedSearchAttributes: SearchAttributes + + // Reified memo access + inline fun memo(key: String): T? + inline fun memo(key: String, genericType: Type): T? + + // Experimental properties + @Experimental val staticSummary: String? + @Experimental val staticDetails: String? + + // Raw response for advanced use cases + val rawDescription: DescribeWorkflowExecutionResponse + + // Java interop + fun toWorkflowExecutionDescription(): WorkflowExecutionDescription +} +``` + +**Usage:** +```kotlin +val description = handle.describe() +println("Type: ${description.workflowType}") +println("Status: ${description.status}") +val config: MyConfig? = description.memo("config") +``` + ## Untyped Handles For cases where you don't know the workflow type at compile time: @@ -134,21 +204,33 @@ untypedHandle.cancel() ``` ```kotlin -interface WorkflowHandle { - val workflowId: String - val runId: String? +class WorkflowHandle( + val workflowId: String, + val runId: String?, + val execution: WorkflowExecution, + // ... internal state +) { + suspend fun result(resultClass: Class): R + inline suspend fun result(): R // Reified version - suspend fun result(): R suspend fun signal(signalName: String, vararg args: Any?) - fun query(queryName: String, vararg args: Any?): R - suspend fun executeUpdate(updateName: String, vararg args: Any?): Any? + + suspend fun query(queryName: String, resultClass: Class, vararg args: Any?): R + inline suspend fun query(queryName: String, vararg args: Any?): R // Reified version + + suspend fun executeUpdate(updateName: String, resultClass: Class, vararg args: Any?): R + inline suspend fun executeUpdate(updateName: String, vararg args: Any?): R // Reified version + suspend fun cancel() suspend fun terminate(reason: String? = null) - fun describe(): WorkflowExecutionInfo + suspend fun describe(): KWorkflowExecutionDescription + + // Java SDK interop + fun toStub(): WorkflowStub } ``` -This pattern matches Python SDK's `WorkflowHandle` with the same method names (`signal`, `query`, `result`, `cancel`, `terminate`, `execute_update`, `start_update`). +This pattern matches Python SDK's `WorkflowHandle` with the same method names (`signal`, `query`, `result`, `cancel`, `terminate`, `execute_update`). ## Related diff --git a/kotlin/configuration/koptions.md b/kotlin/configuration/koptions.md index 706d94f..a01de7a 100644 --- a/kotlin/configuration/koptions.md +++ b/kotlin/configuration/koptions.md @@ -2,12 +2,16 @@ For a fully idiomatic Kotlin experience, the SDK provides dedicated `KOptions` data classes that accept `kotlin.time.Duration` directly, use named parameters with default values, and follow immutable data class patterns. +> **Note on Experimental Features:** Properties marked with `@Experimental` mirror experimental features in the Java SDK. These are subject to change or removal without notice. Use with caution in production code. + ## KActivityOptions ```kotlin /** * Kotlin-native activity options with Duration support. * All timeout properties accept kotlin.time.Duration directly. + * + * IMPORTANT: At least one of startToCloseTimeout or scheduleToCloseTimeout must be specified. */ data class KActivityOptions( val startToCloseTimeout: Duration? = null, @@ -17,8 +21,17 @@ data class KActivityOptions( val taskQueue: String? = null, val retryOptions: KRetryOptions? = null, val cancellationType: ActivityCancellationType? = null, // Java default: TRY_CANCEL - val disableEagerExecution: Boolean = false -) + val disableEagerExecution: Boolean = false, + // Experimental + @Experimental val summary: String? = null, + @Experimental val priority: Priority? = null +) { + init { + require(startToCloseTimeout != null || scheduleToCloseTimeout != null) { + "At least one of startToCloseTimeout or scheduleToCloseTimeout must be specified" + } + } +} ``` **Usage:** @@ -49,7 +62,9 @@ data class KLocalActivityOptions( val startToCloseTimeout: Duration? = null, val scheduleToCloseTimeout: Duration? = null, val localRetryThreshold: Duration? = null, - val retryOptions: KRetryOptions? = null + val retryOptions: KRetryOptions? = null, + // Experimental + @Experimental val summary: String? = null ) ``` @@ -102,9 +117,10 @@ data class KChildWorkflowOptions( val memo: Map? = null, val typedSearchAttributes: SearchAttributes? = null, val cancellationType: ChildWorkflowCancellationType? = null, - val staticSummary: String? = null, - val staticDetails: String? = null, - val priority: Priority? = null + // Experimental + @Experimental val staticSummary: String? = null, + @Experimental val staticDetails: String? = null, + @Experimental val priority: Priority? = null ) ``` @@ -143,9 +159,16 @@ data class KWorkflowOptions( val typedSearchAttributes: SearchAttributes? = null, val disableEagerExecution: Boolean = true, val startDelay: Duration? = null, - val staticSummary: String? = null, - val staticDetails: String? = null, - val priority: Priority? = null + val contextPropagators: List? = null, + // Experimental + @Experimental val staticSummary: String? = null, + @Experimental val staticDetails: String? = null, + @Experimental val priority: Priority? = null, + @Experimental val requestId: String? = null, + @Experimental val completionCallbacks: List? = null, + @Experimental val links: List? = null, + @Experimental val onConflictOptions: KOnConflictOptions? = null, + @Experimental val versioningOverride: VersioningOverride? = null ) ``` @@ -164,6 +187,48 @@ val result = client.executeWorkflow( ) ``` +## KOnConflictOptions + +```kotlin +/** + * Options for handling workflow ID conflicts when using USE_EXISTING conflict policy. + * These options control what gets attached to an existing workflow when a start request + * conflicts with it. + * + * @Experimental This API is experimental and may change. + */ +@Experimental +data class KOnConflictOptions( + val attachRequestId: Boolean = false, + val attachCompletionCallbacks: Boolean = false, + val attachLinks: Boolean = false +) { + init { + if (attachCompletionCallbacks) { + require(attachRequestId) { + "attachRequestId must be true if attachCompletionCallbacks is true" + } + } + } + + fun toJavaOptions(): OnConflictOptions +} +``` + +**Usage:** + +```kotlin +val options = KWorkflowOptions( + workflowId = "my-workflow", + taskQueue = "my-queue", + workflowIdConflictPolicy = WorkflowIdConflictPolicy.WORKFLOW_ID_CONFLICT_POLICY_USE_EXISTING, + onConflictOptions = KOnConflictOptions( + attachRequestId = true, + attachCompletionCallbacks = true + ) +) +``` + ## KContinueAsNewOptions ```kotlin @@ -177,7 +242,8 @@ data class KContinueAsNewOptions( val retryOptions: KRetryOptions? = null, val workflowTaskTimeout: Duration? = null, val memo: Map? = null, - val typedSearchAttributes: SearchAttributes? = null + val typedSearchAttributes: SearchAttributes? = null, + val contextPropagators: List? = null ) ``` diff --git a/kotlin/workflows/signals-queries.md b/kotlin/workflows/signals-queries.md index 6335160..68c9c1d 100644 --- a/kotlin/workflows/signals-queries.md +++ b/kotlin/workflows/signals-queries.md @@ -30,7 +30,35 @@ interface OrderWorkflow { ## Dynamic Handler Registration -For workflows that need to handle signals, queries, or updates dynamically (without annotations), use the registration APIs: +For workflows that need to handle signals, queries, or updates dynamically (without annotations), use the registration APIs. + +### KEncodedValues + +All dynamic handlers receive arguments as `KEncodedValues`, a Kotlin-idiomatic wrapper around the Java SDK's `EncodedValues`: + +```kotlin +class KEncodedValues(private val delegate: EncodedValues) { + val size: Int + fun isEmpty(): Boolean + + // Primary API - reified generics + inline fun get(index: Int = 0): T + inline fun get(index: Int, genericType: Type): T + + // KClass-based access + fun get(index: Int, type: KClass): T + + // Destructuring support + inline operator fun component1(): T + inline operator fun component2(): T + inline operator fun component3(): T + + // Java interop + fun toEncodedValues(): EncodedValues +} +``` + +### Dynamic Handler Examples ```kotlin class DynamicWorkflowImpl : DynamicWorkflow { @@ -40,27 +68,27 @@ class DynamicWorkflowImpl : DynamicWorkflow { // Register a named update handler with validator KWorkflow.registerUpdateHandler( "updateState", - validator = { args -> - val key = args.get(0, String::class.java) + validator = { args: KEncodedValues -> + val key: String = args.get(0) require(key.isNotBlank()) { "Key cannot be blank" } }, - handler = { args -> - val key = args.get(0, String::class.java) - val value = args.get(1, Any::class.java) + handler = { args: KEncodedValues -> + val key: String = args.get(0) + val value: Any = args.get(1) state[key] = value "Updated $key" } ) // Register a named signal handler - KWorkflow.registerSignalHandler("notify") { args -> - val message = args.get(0, String::class.java) + KWorkflow.registerSignalHandler("notify") { args: KEncodedValues -> + val message: String = args.get() // index defaults to 0 println("Received: $message") } // Register a named query handler - KWorkflow.registerQueryHandler("getState") { args -> - val key = args.get(0, String::class.java) + KWorkflow.registerQueryHandler("getState") { args: KEncodedValues -> + val key: String = args.get() state[key] } @@ -73,10 +101,16 @@ class DynamicWorkflowImpl : DynamicWorkflow { println("Unknown signal: $signalName") } - KWorkflow.registerDynamicQueryHandler { queryName, args, resultClass -> + // Dynamic query handler - 2 parameters (name and args) + KWorkflow.registerDynamicQueryHandler { queryName, args -> "Unknown query: $queryName" } + // Validate all unknown updates + KWorkflow.registerDynamicUpdateValidator { updateName, args -> + require(updateName.startsWith("custom_")) { "Unknown update: $updateName" } + } + // Wait for completion signal KWorkflow.awaitCondition { state["done"] == true } return "Completed" @@ -84,6 +118,37 @@ class DynamicWorkflowImpl : DynamicWorkflow { } ``` +### Using Destructuring + +```kotlin +KWorkflow.registerDynamicUpdateHandler { updateName, args -> + // Destructuring for multiple arguments + val (name: String, value: Int) = args + update(name, value) + "Updated" +} +``` + +### Dynamic Handler API Reference + +```kotlin +// Named handlers with KEncodedValues +fun registerSignalHandler(signalName: String, handler: suspend (KEncodedValues) -> Unit) +fun registerQueryHandler(queryName: String, handler: (KEncodedValues) -> Any?) +fun registerUpdateHandler(updateName: String, handler: suspend (KEncodedValues) -> Any?) +fun registerUpdateHandler( + updateName: String, + validator: (KEncodedValues) -> Unit, + handler: suspend (KEncodedValues) -> Any? +) + +// Catch-all dynamic handlers +fun registerDynamicSignalHandler(handler: suspend (signalName: String, args: KEncodedValues) -> Unit) +fun registerDynamicQueryHandler(handler: (queryName: String, args: KEncodedValues) -> Any?) +fun registerDynamicUpdateHandler(handler: suspend (updateName: String, args: KEncodedValues) -> Any?) +fun registerDynamicUpdateValidator(validator: (updateName: String, args: KEncodedValues) -> Unit) +``` + ## Client-Side Interaction ### Sending Signals @@ -114,10 +179,6 @@ val handle = client.getWorkflowHandle("order-123") // Execute update and wait for result val added = handle.executeUpdate(OrderWorkflow::addItem, newItem) - -// Or start update async and get handle -val updateHandle = handle.startUpdate(OrderWorkflow::addItem, newItem) -val result = updateHandle.result() ``` ## Update Validation From eaaec83c2a7c793213a8173b46eb5a0f5c8104b7 Mon Sep 17 00:00:00 2001 From: Maxim Fateev <1463622+mfateev@users.noreply.github.com> Date: Sun, 11 Jan 2026 16:59:45 -0800 Subject: [PATCH 60/83] Add testing documentation and navigation fixes - Add testing.md with KTestWorkflowEnvironment, time skipping, mocking - Add external-workflows.md for signal/cancel external workflows - Fix navigation links throughout documentation - Update various docs based on gap analysis --- kotlin/README.md | 1 + kotlin/activities/local-activities.md | 5 +- kotlin/api-parity.md | 33 ++ kotlin/client/workflow-handle.md | 50 +++ kotlin/configuration/interceptors.md | 24 +- kotlin/configuration/koptions.md | 7 +- kotlin/implementation/implementation-plan.md | 26 +- kotlin/testing.md | 356 +++++++++++++++++++ kotlin/worker/setup.md | 2 +- kotlin/workflows/README.md | 1 + kotlin/workflows/child-workflows.md | 8 +- kotlin/workflows/external-workflows.md | 115 ++++++ 12 files changed, 608 insertions(+), 20 deletions(-) create mode 100644 kotlin/testing.md create mode 100644 kotlin/workflows/external-workflows.md diff --git a/kotlin/README.md b/kotlin/README.md index a01c38b..e1b289c 100644 --- a/kotlin/README.md +++ b/kotlin/README.md @@ -72,6 +72,7 @@ This approach provides: ### Reference +- **[Testing](./testing.md)** - Unit testing, mocking activities, time skipping - **[Migration Guide](./migration.md)** - Migrating from Java SDK - **[API Parity](./api-parity.md)** - Java SDK comparison, gaps, not-needed APIs diff --git a/kotlin/activities/local-activities.md b/kotlin/activities/local-activities.md index 3a4f513..d2b2e39 100644 --- a/kotlin/activities/local-activities.md +++ b/kotlin/activities/local-activities.md @@ -56,7 +56,10 @@ data class KLocalActivityOptions( val startToCloseTimeout: Duration? = null, val scheduleToCloseTimeout: Duration? = null, val localRetryThreshold: Duration? = null, - val retryOptions: KRetryOptions? = null + val retryOptions: KRetryOptions? = null, + // Experimental + @Experimental val summary: String? = null, + @Experimental val priority: Priority? = null ) ``` diff --git a/kotlin/api-parity.md b/kotlin/api-parity.md index d93e215..0d545e2 100644 --- a/kotlin/api-parity.md +++ b/kotlin/api-parity.md @@ -108,6 +108,15 @@ The following Java SDK workflow APIs have Kotlin equivalents in `KWorkflow`: | N/A (new in Kotlin SDK) | `KWorkflow.registerUpdateHandler()` | | N/A (new in Kotlin SDK) | `KWorkflow.registerDynamicUpdateValidator()` | +## APIs Identical to Java SDK + +The following areas use the same API and behavior as the Java SDK: + +| Area | Notes | +|------|-------| +| Error Handling | `ApplicationFailure`, exception types (`ActivityFailure`, `ChildWorkflowFailure`, `TimeoutFailure`, etc.), retry behavior | +| Workflow Info | `KWorkflow.info` provides property-style access to Java `WorkflowInfo` (same properties: `workflowId`, `runId`, `parentWorkflowId`, `attempt`, `taskQueue`, etc.) | + ## Remaining Gaps | Java SDK API | Status | @@ -116,6 +125,30 @@ The following Java SDK workflow APIs have Kotlin equivalents in `KWorkflow`: | `Workflow.startNexusOperation(...)` | Nexus support - deferred to separate project | | `Workflow.getInstance()` | Advanced use case - low priority | +## KWorkflowInfo + +`KWorkflow.info` returns `KWorkflowInfo`, a property-style wrapper around Java's `WorkflowInfo`: + +| Java WorkflowInfo | Kotlin SDK | +|-------------------|------------| +| `getWorkflowId()` | `KWorkflowInfo.workflowId` | +| `getRunId()` | `KWorkflowInfo.runId` | +| `getWorkflowType()` | `KWorkflowInfo.workflowType` | +| `getTaskQueue()` | `KWorkflowInfo.taskQueue` | +| `getNamespace()` | `KWorkflowInfo.namespace` | +| `getAttempt()` | `KWorkflowInfo.attempt` | +| `getParentWorkflowId()` | `KWorkflowInfo.parentWorkflowId: String?` | +| `getParentRunId()` | `KWorkflowInfo.parentRunId: String?` | +| `getContinuedExecutionRunId()` | `KWorkflowInfo.continuedExecutionRunId: String?` | +| `getCronSchedule()` | `KWorkflowInfo.cronSchedule: String?` | +| `getSearchAttributes()` | `KWorkflowInfo.typedSearchAttributes` | +| `getHistoryLength()` | `KWorkflowInfo.historyLength` | +| `isContinueAsNewSuggested()` | `KWorkflowInfo.isContinueAsNewSuggested` | +| `getFirstExecutionRunId()` | `KWorkflowInfo.firstExecutionRunId` | +| `getOriginalExecutionRunId()` | `KWorkflowInfo.originalExecutionRunId` | +| `getCurrentBuildId()` | `KWorkflowInfo.currentBuildId: String?` | +| `getPriority()` | `@Experimental KWorkflowInfo.priority` | + ## KActivityInfo | Java ActivityInfo | Kotlin SDK | diff --git a/kotlin/client/workflow-handle.md b/kotlin/client/workflow-handle.md index 2b0b024..ca60529 100644 --- a/kotlin/client/workflow-handle.md +++ b/kotlin/client/workflow-handle.md @@ -21,6 +21,15 @@ val result = handle.result() // Updates - execute and wait for result val updateResult = handle.executeUpdate(OrderWorkflow::addItem, newItem) +// Updates - start and get handle (don't wait for completion) +val updateHandle = handle.startUpdate( + OrderWorkflow::addItem, + waitForStage = WorkflowUpdateStage.ACCEPTED, + arg = newItem +) +// ... do other work ... +val result = updateHandle.result() // Wait for result when needed + // Cancel or terminate handle.cancel() handle.terminate("No longer needed") @@ -68,6 +77,29 @@ open class KWorkflowHandle( // Updates - execute and wait for result (suspend functions) suspend fun executeUpdate(method: KSuspendFunction1): R suspend fun executeUpdate(method: KSuspendFunction2, arg: A1): R + suspend fun executeUpdate(method: KSuspendFunction3, arg1: A1, arg2: A2): R + // ... up to 6 arguments + + // Updates - start and return handle without waiting for completion + suspend fun startUpdate( + method: KSuspendFunction1, + waitForStage: WorkflowUpdateStage = WorkflowUpdateStage.ACCEPTED, + updateId: String? = null + ): KUpdateHandle + suspend fun startUpdate( + method: KSuspendFunction2, + waitForStage: WorkflowUpdateStage = WorkflowUpdateStage.ACCEPTED, + updateId: String? = null, + arg: A1 + ): KUpdateHandle + suspend fun startUpdate( + method: KSuspendFunction3, + waitForStage: WorkflowUpdateStage = WorkflowUpdateStage.ACCEPTED, + updateId: String? = null, + arg1: A1, + arg2: A2 + ): KUpdateHandle + // ... up to 6 arguments // Get handle for existing update by ID fun getUpdateHandle(updateId: String, resultClass: Class): KUpdateHandle @@ -199,6 +231,10 @@ untypedHandle.signal("updatePriority", Priority.HIGH) val status = untypedHandle.query("status") val result = untypedHandle.result() +// Updates by name +val updateResult = untypedHandle.executeUpdate("addItem", newItem) +val updateHandle = untypedHandle.startUpdate("addItem", arg = newItem) + // Cancel/terminate work the same untypedHandle.cancel() ``` @@ -221,6 +257,20 @@ class WorkflowHandle( suspend fun executeUpdate(updateName: String, resultClass: Class, vararg args: Any?): R inline suspend fun executeUpdate(updateName: String, vararg args: Any?): R // Reified version + suspend fun startUpdate( + updateName: String, + resultClass: Class, + waitForStage: WorkflowUpdateStage = WorkflowUpdateStage.ACCEPTED, + updateId: String? = null, + vararg args: Any? + ): KUpdateHandle + inline suspend fun startUpdate( + updateName: String, + waitForStage: WorkflowUpdateStage = WorkflowUpdateStage.ACCEPTED, + updateId: String? = null, + vararg args: Any? + ): KUpdateHandle // Reified version + suspend fun cancel() suspend fun terminate(reason: String? = null) suspend fun describe(): KWorkflowExecutionDescription diff --git a/kotlin/configuration/interceptors.md b/kotlin/configuration/interceptors.md index dac8af1..ba082b6 100644 --- a/kotlin/configuration/interceptors.md +++ b/kotlin/configuration/interceptors.md @@ -96,10 +96,28 @@ open class KWorkflowInboundCallsInterceptorBase( ```kotlin data class KWorkflowInput(val header: Header, val arguments: Array) data class KWorkflowOutput(val result: Any?) -data class KSignalInput(val signalName: String, val arguments: Array, val eventId: Long, val header: Header) -data class KQueryInput(val queryName: String, val arguments: Array, val header: Header) + +// Dynamic handlers can use encodedValues to decode raw payloads +data class KSignalInput( + val signalName: String, + val arguments: Array, + val encodedValues: KEncodedValues, + val eventId: Long, + val header: Header +) +data class KQueryInput( + val queryName: String, + val arguments: Array, + val encodedValues: KEncodedValues, + val header: Header +) data class KQueryOutput(val result: Any?) -data class KUpdateInput(val updateName: String, val arguments: Array, val header: Header) +data class KUpdateInput( + val updateName: String, + val arguments: Array, + val encodedValues: KEncodedValues, + val header: Header +) data class KUpdateOutput(val result: Any?) ``` diff --git a/kotlin/configuration/koptions.md b/kotlin/configuration/koptions.md index a01de7a..54f7747 100644 --- a/kotlin/configuration/koptions.md +++ b/kotlin/configuration/koptions.md @@ -64,7 +64,8 @@ data class KLocalActivityOptions( val localRetryThreshold: Duration? = null, val retryOptions: KRetryOptions? = null, // Experimental - @Experimental val summary: String? = null + @Experimental val summary: String? = null, + @Experimental val priority: Priority? = null ) ``` @@ -92,10 +93,12 @@ data class KRetryOptions( val backoffCoefficient: Double = 2.0, val maximumInterval: Duration? = null, val maximumAttempts: Int = 0, // 0 = unlimited - val doNotRetry: List = emptyList() + val doNotRetry: List = emptyList() // Exception type names ) ``` +> **Note:** `doNotRetry` contains fully-qualified exception class names (e.g., `"java.lang.IllegalArgumentException"`, `"com.example.BusinessException"`). Activities throwing these exceptions will not be retried. + ## KChildWorkflowOptions ```kotlin diff --git a/kotlin/implementation/implementation-plan.md b/kotlin/implementation/implementation-plan.md index b94ee5e..7ee1e79 100644 --- a/kotlin/implementation/implementation-plan.md +++ b/kotlin/implementation/implementation-plan.md @@ -49,9 +49,10 @@ - ✅ `KChildWorkflowHandle` interface (signal, cancel, result) - ✅ Optional `KChildWorkflowOptions` (uses defaults when omitted) -### 2.3 Update Enhancements -- Dynamic update handler registration (`registerUpdateHandler`, `registerDynamicUpdateHandler`) - TODO +### 2.3 Update Enhancements ✅ +- ✅ Dynamic update handler registration (`registerUpdateHandler`, `registerDynamicUpdateHandler`) - ✅ Update validator support (`@UpdateValidatorMethod`) - Uses Java SDK annotations +- ✅ `KEncodedValues` for dynamic handlers to access raw payloads ### 2.4 Client API ✅ - ✅ `KWorkflowClient` - Kotlin client with suspend functions and DSL constructor @@ -89,7 +90,7 @@ --- -## Phase 3: Testing Framework & Interceptors +## Phase 3: Testing Framework & Interceptors ✅ COMPLETE ### 3.1 Test Environment ✅ COMPLETE - ✅ `KTestActivityEnvironment` - typed executeActivity/executeLocalActivity, suspend activity support, heartbeat/cancellation testing @@ -107,13 +108,18 @@ - ✅ Support for both regular and suspend activity mocks - ✅ Integration with `KTestWorkflowExtension` for automatic mock registration -### 3.3 Interceptors (Design Complete - see [interceptors.md](../configuration/interceptors.md)) -- `KWorkerInterceptor` interface with `KWorkerInterceptorBase` -- `KWorkflowInboundCallsInterceptor` with suspend functions and input/output data classes -- `KWorkflowOutboundCallsInterceptor` with full API (activities, child workflows, timers, side effects, etc.) -- `KActivityInboundCallsInterceptor` with suspend support -- Base classes for convenience (`*Base` classes) -- Example: logging interceptor +### 3.3 Interceptors ✅ COMPLETE (see [interceptors.md](../configuration/interceptors.md)) +- ✅ `KWorkerInterceptor` interface with `KWorkerInterceptorBase` +- ✅ `KWorkflowInboundCallsInterceptor` with suspend functions and input/output data classes +- ✅ `KWorkflowOutboundCallsInterceptor` with full API (activities, child workflows, timers, side effects, etc.) +- ✅ `KActivityInboundCallsInterceptor` with suspend support +- ✅ Base classes for convenience (`*Base` classes) +- ✅ `RootWorkflowOutboundCallsInterceptor` - terminal outbound interceptor implementation +- ✅ `WorkflowContextElement` - ThreadContextElement for workflow context propagation to interceptors +- ✅ `KEncodedValues` for dynamic handlers to access raw payloads +- ✅ Interceptor chain integration in `KotlinReplayWorkflow` +- ✅ KWorkflow static methods routed through outbound interceptor (newRandom, randomUUID, currentTimeMillis) +- ✅ Integration test: `TracingInterceptorIntegrationTest` --- diff --git a/kotlin/testing.md b/kotlin/testing.md new file mode 100644 index 0000000..7eb5f97 --- /dev/null +++ b/kotlin/testing.md @@ -0,0 +1,356 @@ +# Testing + +The Kotlin SDK provides testing utilities that integrate with `kotlinx.coroutines.test` for testing workflows and activities. + +## Test Environment + +Use `KTestWorkflowEnvironment` to create an in-memory Temporal environment for fast, deterministic tests: + +```kotlin +class OrderWorkflowTest { + private lateinit var testEnv: KTestWorkflowEnvironment + private lateinit var worker: KWorker + private lateinit var client: KWorkflowClient + + @BeforeEach + fun setup() { + testEnv = KTestWorkflowEnvironment.newInstance() + worker = testEnv.newWorker("test-queue") + client = testEnv.workflowClient + } + + @AfterEach + fun teardown() { + testEnv.close() + } + + @Test + fun `test order workflow completes successfully`() = runTest { + // Register workflow and mock activities + worker.registerWorkflowImplementationTypes(OrderWorkflowImpl::class) + worker.registerActivitiesImplementations(MockOrderActivities()) + testEnv.start() + + // Execute workflow + val result = client.executeWorkflow( + OrderWorkflow::processOrder, + KWorkflowOptions( + workflowId = "test-order-123", + taskQueue = "test-queue" + ), + testOrder + ) + + assertEquals(OrderStatus.COMPLETED, result.status) + } +} +``` + +## KTestWorkflowEnvironment API + +```kotlin +/** + * Kotlin test environment wrapping TestWorkflowEnvironment. + * Provides coroutine-friendly APIs for testing workflows. + */ +class KTestWorkflowEnvironment private constructor( + private val delegate: TestWorkflowEnvironment +) : AutoCloseable { + companion object { + fun newInstance(): KTestWorkflowEnvironment + fun newInstance(options: TestEnvironmentOptions): KTestWorkflowEnvironment + fun newInstance(options: TestEnvironmentOptions.Builder.() -> Unit): KTestWorkflowEnvironment + } + + /** The workflow client for starting and interacting with workflows */ + val workflowClient: KWorkflowClient + + /** Create a new worker for the given task queue */ + fun newWorker(taskQueue: String): KWorker + fun newWorker(taskQueue: String, options: WorkerOptions.Builder.() -> Unit): KWorker + + /** Start the test environment (workers begin processing) */ + fun start() + + /** Shutdown the test environment */ + override fun close() + + // Time control + /** Current test time */ + val currentTimeMillis: Long + + /** Skip time forward, triggering any timers that fire */ + suspend fun skipTime(duration: Duration) + + /** Sleep real time (for integration-style tests) */ + suspend fun sleep(duration: Duration) + + /** Register a callback for when workflow reaches a timer */ + fun registerDelayCallback(duration: Duration, callback: () -> Unit) + + // Advanced + /** Access the underlying TestWorkflowEnvironment */ + val testEnvironment: TestWorkflowEnvironment +} +``` + +## Time Skipping + +Test long-running workflows without waiting for real time to pass: + +```kotlin +@Test +fun `test workflow with timers`() = runTest { + worker.registerWorkflowImplementationTypes(ReminderWorkflowImpl::class) + testEnv.start() + + // Start workflow that has a 24-hour delay + val handle = client.startWorkflow( + ReminderWorkflow::scheduleReminder, + KWorkflowOptions( + workflowId = "reminder-123", + taskQueue = "test-queue" + ), + reminder + ) + + // Skip 24 hours instantly + testEnv.skipTime(24.hours) + + // Workflow should now be complete + val result = handle.result() + assertTrue(result.reminderSent) +} +``` + +## Mocking Activities + +### Simple Mock Implementation + +```kotlin +class MockOrderActivities : OrderActivities { + var chargePaymentCalled = false + var lastOrder: Order? = null + + override suspend fun chargePayment(order: Order): PaymentResult { + chargePaymentCalled = true + lastOrder = order + return PaymentResult(success = true, transactionId = "mock-tx-123") + } + + override suspend fun shipOrder(order: Order): ShipmentResult { + return ShipmentResult(trackingNumber = "MOCK-TRACK-123") + } +} + +@Test +fun `test payment is charged`() = runTest { + val mockActivities = MockOrderActivities() + worker.registerWorkflowImplementationTypes(OrderWorkflowImpl::class) + worker.registerActivitiesImplementations(mockActivities) + testEnv.start() + + client.executeWorkflow( + OrderWorkflow::processOrder, + options, + testOrder + ) + + assertTrue(mockActivities.chargePaymentCalled) + assertEquals(testOrder, mockActivities.lastOrder) +} +``` + +### Using Mocking Frameworks + +```kotlin +@Test +fun `test with mockk`() = runTest { + val mockActivities = mockk() + coEvery { mockActivities.chargePayment(any()) } returns PaymentResult(success = true, transactionId = "tx-123") + coEvery { mockActivities.shipOrder(any()) } returns ShipmentResult(trackingNumber = "TRACK-123") + + worker.registerWorkflowImplementationTypes(OrderWorkflowImpl::class) + worker.registerActivitiesImplementations(mockActivities) + testEnv.start() + + val result = client.executeWorkflow(OrderWorkflow::processOrder, options, testOrder) + + coVerify { mockActivities.chargePayment(testOrder) } + assertTrue(result.success) +} +``` + +## Testing Signals and Queries + +```kotlin +@Test +fun `test signal updates workflow state`() = runTest { + worker.registerWorkflowImplementationTypes(OrderWorkflowImpl::class) + worker.registerActivitiesImplementations(mockActivities) + testEnv.start() + + val handle = client.startWorkflow( + OrderWorkflow::processOrder, + options, + testOrder + ) + + // Query initial state + val initialStatus = handle.query(OrderWorkflow::status) + assertEquals(OrderStatus.PENDING, initialStatus) + + // Send signal + handle.signal(OrderWorkflow::updatePriority, Priority.HIGH) + + // Query updated state + val priority = handle.query(OrderWorkflow::priority) + assertEquals(Priority.HIGH, priority) +} +``` + +## Testing Updates + +```kotlin +@Test +fun `test update modifies order`() = runTest { + worker.registerWorkflowImplementationTypes(OrderWorkflowImpl::class) + worker.registerActivitiesImplementations(mockActivities) + testEnv.start() + + val handle = client.startWorkflow( + OrderWorkflow::processOrder, + options, + testOrder + ) + + // Execute update + val added = handle.executeUpdate(OrderWorkflow::addItem, newItem) + assertTrue(added) + + // Verify via query + val itemCount = handle.query(OrderWorkflow::getItemCount) + assertEquals(2, itemCount) // Original item + new item +} +``` + +## Testing Cancellation + +```kotlin +@Test +fun `test workflow handles cancellation gracefully`() = runTest { + worker.registerWorkflowImplementationTypes(OrderWorkflowImpl::class) + worker.registerActivitiesImplementations(mockActivities) + testEnv.start() + + val handle = client.startWorkflow( + OrderWorkflow::processOrder, + options, + testOrder + ) + + // Cancel the workflow + handle.cancel() + + // Verify cleanup activities were called + val description = handle.describe() + assertEquals(WorkflowExecutionStatus.WORKFLOW_EXECUTION_STATUS_CANCELED, description.status) +} +``` + +## Testing Child Workflows + +```kotlin +@Test +fun `test parent orchestrates child workflows`() = runTest { + worker.registerWorkflowImplementationTypes( + ParentWorkflowImpl::class, + ChildWorkflowImpl::class + ) + worker.registerActivitiesImplementations(mockActivities) + testEnv.start() + + val result = client.executeWorkflow( + ParentWorkflow::orchestrate, + options, + parentInput + ) + + assertEquals(3, result.childResults.size) +} +``` + +## Integration Testing + +For tests that need a real Temporal server: + +```kotlin +@TestInstance(TestInstance.Lifecycle.PER_CLASS) +class OrderWorkflowIntegrationTest { + private lateinit var service: WorkflowServiceStubs + private lateinit var client: KWorkflowClient + private lateinit var factory: KWorkerFactory + + @BeforeAll + fun setup() { + // Connect to local Temporal server + service = WorkflowServiceStubs.newLocalServiceStubs() + client = KWorkflowClient(service) + factory = KWorkerFactory(client) + + val worker = factory.newWorker("integration-test-queue") + worker.registerWorkflowImplementationTypes(OrderWorkflowImpl::class) + worker.registerActivitiesImplementations(RealOrderActivities()) + factory.start() + } + + @AfterAll + fun teardown() { + factory.shutdown() + service.shutdown() + } + + @Test + fun `integration test with real server`() = runBlocking { + val result = client.executeWorkflow( + OrderWorkflow::processOrder, + KWorkflowOptions( + workflowId = "integration-test-${UUID.randomUUID()}", + taskQueue = "integration-test-queue" + ), + testOrder + ) + + assertTrue(result.success) + } +} +``` + +## Best Practices + +1. **Use `runTest`** from `kotlinx.coroutines.test` for suspend function tests +2. **Prefer unit tests** with `KTestWorkflowEnvironment` for fast feedback +3. **Use time skipping** for workflows with delays - don't wait for real time +4. **Mock activities** to isolate workflow logic and control external dependencies +5. **Test edge cases**: cancellation, timeouts, failures, retries +6. **Use unique workflow IDs** in integration tests to avoid conflicts + +## Dependencies + +Add the testing dependency to your project: + +```kotlin +// build.gradle.kts +testImplementation("io.temporal:temporal-testing:$temporalVersion") +testImplementation("org.jetbrains.kotlinx:kotlinx-coroutines-test:$coroutinesVersion") +``` + +## Related + +- [Workflow Definition](./workflows/definition.md) - Defining testable workflows +- [Activities](./activities/README.md) - Defining testable activities +- [Worker Setup](./worker/setup.md) - Production worker configuration + +--- + +**Next:** [Migration Guide](./migration.md) diff --git a/kotlin/worker/setup.md b/kotlin/worker/setup.md index e67fedc..29afafb 100644 --- a/kotlin/worker/setup.md +++ b/kotlin/worker/setup.md @@ -140,4 +140,4 @@ factory.start() --- -**Next:** [Migration Guide](../migration.md) +**Next:** [Testing](../testing.md) diff --git a/kotlin/workflows/README.md b/kotlin/workflows/README.md index 37cf645..68b32a8 100644 --- a/kotlin/workflows/README.md +++ b/kotlin/workflows/README.md @@ -13,6 +13,7 @@ Kotlin workflows use coroutines and suspend functions for an idiomatic async exp | [Definition](./definition.md) | Workflow interfaces, suspend methods, Java interop patterns | | [Signals, Queries & Updates](./signals-queries.md) | Communication with running workflows | | [Child Workflows](./child-workflows.md) | Orchestrating child workflow execution | +| [External Workflows](./external-workflows.md) | Signal or cancel workflows in other executions | | [Timers & Parallel Execution](./timers-parallel.md) | Delays, async patterns, await conditions | | [Cancellation](./cancellation.md) | Handling cancellation, cleanup with NonCancellable | | [Continue-As-New](./continue-as-new.md) | Long-running workflow patterns | diff --git a/kotlin/workflows/child-workflows.md b/kotlin/workflows/child-workflows.md index 9c7067e..2b07d7a 100644 --- a/kotlin/workflows/child-workflows.md +++ b/kotlin/workflows/child-workflows.md @@ -199,10 +199,11 @@ KChildWorkflowOptions( cancellationType = ChildWorkflowCancellationType.WAIT_CANCELLATION_COMPLETED // Optional ) -// Options are optional - use default KChildWorkflowOptions() when not specified +// Minimal options - just use defaults val result = KWorkflow.executeChildWorkflow( ChildWorkflow::processData, - inputData // No options needed for simple cases + KChildWorkflowOptions(), + inputData ) ``` @@ -210,9 +211,10 @@ val result = KWorkflow.executeChildWorkflow( ## Related +- [External Workflows](./external-workflows.md) - Signal/cancel workflows in other executions - [Cancellation](./cancellation.md) - How cancellation propagates to child workflows - [Workflow Definition](./definition.md) - Basic workflow patterns --- -**Next:** [Timers & Parallel Execution](./timers-parallel.md) +**Next:** [External Workflows](./external-workflows.md) diff --git a/kotlin/workflows/external-workflows.md b/kotlin/workflows/external-workflows.md new file mode 100644 index 0000000..6923d0b --- /dev/null +++ b/kotlin/workflows/external-workflows.md @@ -0,0 +1,115 @@ +# External Workflows + +External workflows allow you to signal or cancel workflows running in separate executions. Use `KWorkflow.getExternalWorkflowHandle()` to get a handle for interaction. + +## Typed Handle + +```kotlin +// Get typed handle for external workflow +val handle = KWorkflow.getExternalWorkflowHandle("order-123") + +// Signal using method reference (type-safe) +handle.signal(OrderWorkflow::updatePriority, Priority.HIGH) + +// Cancel the external workflow +handle.cancel() +``` + +## With Run ID + +```kotlin +// Target a specific execution +val handle = KWorkflow.getExternalWorkflowHandle( + workflowId = "order-123", + runId = "abc-run-456" +) + +handle.signal(OrderWorkflow::cancelOrder, "Duplicate order") +``` + +## Untyped Handle + +When the workflow type is unknown at compile time: + +```kotlin +// Get untyped handle +val handle = KWorkflow.getExternalWorkflowHandle("order-123") + +// Signal by name +handle.signal("updatePriority", Priority.HIGH) + +// Cancel +handle.cancel() +``` + +## KExternalWorkflowHandle API + +```kotlin +/** + * Handle for interacting with an external workflow (workflow in a different execution). + * Obtained via KWorkflow.getExternalWorkflowHandle(). + * + * @param T The external workflow interface type (for type-safe signals) + */ +class KExternalWorkflowHandle( + val workflowId: String, + val runId: String? +) { + // Signals - type-safe method references + suspend fun signal(method: KFunction1) + suspend fun signal(method: KFunction2, arg: A1) + suspend fun signal(method: KFunction3, arg1: A1, arg2: A2) + // ... up to 6 arguments + + /** Request cancellation of the external workflow. */ + suspend fun cancel() +} +``` + +## ExternalWorkflowHandle (Untyped) + +```kotlin +/** + * Untyped handle for external workflows. + * Use when workflow type is unknown at compile time. + */ +class ExternalWorkflowHandle( + val workflowId: String, + val runId: String? +) { + suspend fun signal(signalName: String, vararg args: Any?) + suspend fun cancel() +} +``` + +## KWorkflow API + +```kotlin +object KWorkflow { + /** Get typed handle for external workflow */ + inline fun getExternalWorkflowHandle(workflowId: String): KExternalWorkflowHandle + inline fun getExternalWorkflowHandle(workflowId: String, runId: String): KExternalWorkflowHandle + + /** Get untyped handle for external workflow */ + fun getExternalWorkflowHandle(workflowId: String): ExternalWorkflowHandle + fun getExternalWorkflowHandle(workflowId: String, runId: String): ExternalWorkflowHandle +} +``` + +## Limitations + +| Operation | Supported | +|-----------|-----------| +| Signal | ✓ | +| Cancel | ✓ | +| Query | ✗ (queries are synchronous, only available via client) | +| Get Result | ✗ (cannot await external workflow results from within a workflow) | + +## Related + +- [Child Workflows](./child-workflows.md) - Workflows started by the current workflow +- [Signals, Queries & Updates](./signals-queries.md) - Signal handler definitions + +--- + +**Next:** [Timers & Parallel Execution](./timers-parallel.md) From f95b7bffd17e707d2316ab622ebecc52b3a83ba6 Mon Sep 17 00:00:00 2001 From: Maxim Fateev <1463622+mfateev@users.noreply.github.com> Date: Sun, 11 Jan 2026 17:07:28 -0800 Subject: [PATCH 61/83] Kotlin SDK API Proposal This proposal defines the public API for the Temporal Kotlin SDK, providing an idiomatic Kotlin experience using coroutines and suspend functions. Key features: - Coroutine-based workflows with suspend functions - Type-safe activity and workflow execution via method references - Native Kotlin Duration support - KOptions data classes for configuration - Full interoperability with Java SDK Documentation includes: - Workflow definition, signals, queries, updates - Activity execution (regular and local) - Child and external workflow patterns - Client API with typed handles - Worker setup and interceptors - Testing utilities - Migration guide from Java SDK --- kotlin/README.md | 131 ++++++++ kotlin/activities/README.md | 71 +++++ kotlin/activities/definition.md | 171 +++++++++++ kotlin/activities/implementation.md | 199 ++++++++++++ kotlin/activities/local-activities.md | 86 ++++++ kotlin/api-parity.md | 164 ++++++++++ kotlin/client/README.md | 90 ++++++ kotlin/client/advanced.md | 178 +++++++++++ kotlin/client/workflow-client.md | 125 ++++++++ kotlin/client/workflow-handle.md | 292 ++++++++++++++++++ kotlin/configuration/README.md | 73 +++++ kotlin/configuration/data-conversion.md | 128 ++++++++ kotlin/configuration/interceptors.md | 389 ++++++++++++++++++++++++ kotlin/configuration/koptions.md | 296 ++++++++++++++++++ kotlin/kotlin-idioms.md | 197 ++++++++++++ kotlin/migration.md | 129 ++++++++ kotlin/testing.md | 356 ++++++++++++++++++++++ kotlin/worker/README.md | 51 ++++ kotlin/worker/setup.md | 143 +++++++++ kotlin/workflows/README.md | 61 ++++ kotlin/workflows/cancellation.md | 151 +++++++++ kotlin/workflows/child-workflows.md | 220 ++++++++++++++ kotlin/workflows/continue-as-new.md | 150 +++++++++ kotlin/workflows/definition.md | 131 ++++++++ kotlin/workflows/external-workflows.md | 115 +++++++ kotlin/workflows/signals-queries.md | 211 +++++++++++++ kotlin/workflows/timers-parallel.md | 125 ++++++++ 27 files changed, 4433 insertions(+) create mode 100644 kotlin/README.md create mode 100644 kotlin/activities/README.md create mode 100644 kotlin/activities/definition.md create mode 100644 kotlin/activities/implementation.md create mode 100644 kotlin/activities/local-activities.md create mode 100644 kotlin/api-parity.md create mode 100644 kotlin/client/README.md create mode 100644 kotlin/client/advanced.md create mode 100644 kotlin/client/workflow-client.md create mode 100644 kotlin/client/workflow-handle.md create mode 100644 kotlin/configuration/README.md create mode 100644 kotlin/configuration/data-conversion.md create mode 100644 kotlin/configuration/interceptors.md create mode 100644 kotlin/configuration/koptions.md create mode 100644 kotlin/kotlin-idioms.md create mode 100644 kotlin/migration.md create mode 100644 kotlin/testing.md create mode 100644 kotlin/worker/README.md create mode 100644 kotlin/worker/setup.md create mode 100644 kotlin/workflows/README.md create mode 100644 kotlin/workflows/cancellation.md create mode 100644 kotlin/workflows/child-workflows.md create mode 100644 kotlin/workflows/continue-as-new.md create mode 100644 kotlin/workflows/definition.md create mode 100644 kotlin/workflows/external-workflows.md create mode 100644 kotlin/workflows/signals-queries.md create mode 100644 kotlin/workflows/timers-parallel.md diff --git a/kotlin/README.md b/kotlin/README.md new file mode 100644 index 0000000..e1b289c --- /dev/null +++ b/kotlin/README.md @@ -0,0 +1,131 @@ +# Kotlin SDK API Proposal + +This document describes the public API and developer experience for the Temporal Kotlin SDK. + +## Overview + +The Kotlin SDK provides an idiomatic Kotlin experience for building Temporal workflows using coroutines and suspend functions. + +**Key Features:** + +* Coroutine-based workflows with `suspend fun` +* Full interoperability with Java SDK +* Kotlin Duration support (`30.seconds`) +* DSL builders for configuration +* Null safety (no `Optional`) + +**Requirements:** + +* Minimum Kotlin version: 1.8.x +* Coroutines library: kotlinx-coroutines-core 1.7.x+ + +## Design Principle + +**Use idiomatic Kotlin language patterns wherever possible instead of custom APIs.** + +The Kotlin SDK should feel natural to Kotlin developers by leveraging standard `kotlinx.coroutines` primitives. Custom APIs should only be introduced when Temporal-specific semantics cannot be achieved through standard patterns. + +| Pattern | Standard Kotlin | Temporal Integration | +|---------|-----------------|----------------------| +| Parallel execution | `coroutineScope { async { ... } }` | Works via deterministic dispatcher | +| Await multiple | `awaitAll(d1, d2)` | Standard kotlinx.coroutines | +| Sleep/delay | `delay(duration)` | Intercepted via `Delay` interface | +| Deferred results | `Deferred` | Standard + `Promise.toDeferred()` | + +This approach provides: +- **Familiar patterns**: Kotlin developers use patterns they already know +- **IDE support**: Full autocomplete and documentation for standard APIs +- **Ecosystem compatibility**: Works with existing coroutine libraries and utilities +- **Smaller API surface**: Less custom code to learn and maintain + +## Documentation Structure + +### Core Concepts + +- **[Kotlin Idioms](./kotlin-idioms.md)** - Duration, null safety, property syntax for queries +- **[Configuration](./configuration/README.md)** - KOptions classes, data conversion, interceptors + +### Building Blocks + +- **[Workflows](./workflows/README.md)** - Defining and implementing workflows + - [Definition](./workflows/definition.md) - Interfaces, suspend methods, Java interop + - [Signals, Queries & Updates](./workflows/signals-queries.md) - Communication patterns + - [Child Workflows](./workflows/child-workflows.md) - Orchestrating child workflows + - [Timers & Parallel Execution](./workflows/timers-parallel.md) - Delays, async patterns + - [Cancellation](./workflows/cancellation.md) - Handling cancellation, cleanup + - [Continue-As-New](./workflows/continue-as-new.md) - Long-running workflow patterns + +- **[Activities](./activities/README.md)** - Defining and implementing activities + - [Definition](./activities/definition.md) - Interfaces, typed/string-based execution + - [Implementation](./activities/implementation.md) - Suspend activities, heartbeating + - [Local Activities](./activities/local-activities.md) - Short-lived local activities + +### Infrastructure + +- **[Client](./client/README.md)** - Interacting with workflows + - [Workflow Client](./client/workflow-client.md) - KWorkflowClient, starting workflows + - [Workflow Handles](./client/workflow-handle.md) - Signals, queries, results + - [Advanced Operations](./client/advanced.md) - SignalWithStart, UpdateWithStart + +- **[Worker](./worker/README.md)** - Running workflows and activities + - [Setup](./worker/setup.md) - KWorkerFactory, KWorker, registration + +### Reference + +- **[Testing](./testing.md)** - Unit testing, mocking activities, time skipping +- **[Migration Guide](./migration.md)** - Migrating from Java SDK +- **[API Parity](./api-parity.md)** - Java SDK comparison, gaps, not-needed APIs + +## Quick Start + +```kotlin +// Define activity interface +@ActivityInterface +interface GreetingActivities { + @ActivityMethod + suspend fun composeGreeting(greeting: String, name: String): String +} + +// Implement activity +class GreetingActivitiesImpl : GreetingActivities { + override suspend fun composeGreeting(greeting: String, name: String): String { + return "$greeting, $name!" + } +} + +// Define workflow interface +@WorkflowInterface +interface GreetingWorkflow { + @WorkflowMethod + suspend fun getGreeting(name: String): String +} + +// Implement workflow +class GreetingWorkflowImpl : GreetingWorkflow { + override suspend fun getGreeting(name: String): String { + return KWorkflow.executeActivity( + GreetingActivities::composeGreeting, + KActivityOptions(startToCloseTimeout = 10.seconds), + "Hello", name + ) + } +} + +// Start worker +val factory = KWorkerFactory(client) +val worker = factory.newWorker("greetings") +worker.registerWorkflowImplementationTypes(GreetingWorkflowImpl::class) +worker.registerActivitiesImplementations(GreetingActivitiesImpl()) +factory.start() + +// Execute workflow +val result = client.executeWorkflow( + GreetingWorkflow::getGreeting, + KWorkflowOptions(workflowId = "greeting-123", taskQueue = "greetings"), + "Temporal" +) +``` + +--- + +**[Start Review →](./kotlin-idioms.md)** diff --git a/kotlin/activities/README.md b/kotlin/activities/README.md new file mode 100644 index 0000000..08da097 --- /dev/null +++ b/kotlin/activities/README.md @@ -0,0 +1,71 @@ +# Activities + +This section covers defining and implementing Temporal activities in Kotlin. + +## Overview + +Activities are the building blocks for interacting with external systems. The Kotlin SDK provides type-safe activity execution with suspend function support. + +## Documents + +| Document | Description | +|----------|-------------| +| [Definition](./definition.md) | Activity interfaces, typed and string-based execution | +| [Implementation](./implementation.md) | Implementing activities, KActivity API, heartbeating | +| [Local Activities](./local-activities.md) | Short-lived local activities | + +## Quick Reference + +### Basic Activity + +```kotlin +// Define activity interface +@ActivityInterface +interface GreetingActivities { + @ActivityMethod + suspend fun composeGreeting(greeting: String, name: String): String +} + +// Implement activity +class GreetingActivitiesImpl : GreetingActivities { + override suspend fun composeGreeting(greeting: String, name: String): String { + return "$greeting, $name!" + } +} +``` + +### Calling Activities from Workflows + +```kotlin +// Type-safe method reference +val greeting = KWorkflow.executeActivity( + GreetingActivities::composeGreeting, + KActivityOptions(startToCloseTimeout = 30.seconds), + "Hello", "World" +) + +// String-based (for cross-language interop) +val result = KWorkflow.executeActivity( + "composeGreeting", + KActivityOptions(startToCloseTimeout = 30.seconds), + "Hello", "World" +) +``` + +### Key Patterns + +| Pattern | API | +|---------|-----| +| Execute activity | `KWorkflow.executeActivity(Interface::method, options, args)` | +| Execute by name | `KWorkflow.executeActivity("name", options, args)` | +| Local activity | `KWorkflow.executeLocalActivity(Interface::method, options, args)` | +| Heartbeat | `KActivity.context.heartbeat(details)` | + +## Related + +- [Implementation](./implementation.md) - Suspend activity patterns +- [Local Activities](./local-activities.md) - Short-lived activities + +--- + +**Next:** [Activity Definition](./definition.md) diff --git a/kotlin/activities/definition.md b/kotlin/activities/definition.md new file mode 100644 index 0000000..36b036f --- /dev/null +++ b/kotlin/activities/definition.md @@ -0,0 +1,171 @@ +# Activity Definition + +## String-based Activity Execution + +For calling activities by name (useful for cross-language interop or dynamic activity names): + +```kotlin +// Execute activity by string name - suspend function, awaits result +val result = KWorkflow.executeActivity( + "activityName", + KActivityOptions( + startToCloseTimeout = 30.seconds, + retryOptions = KRetryOptions( + initialInterval = 1.seconds, + maximumAttempts = 3 + ) + ), + arg1, arg2 +) + +// Parallel execution - use standard coroutineScope { async {} } +val results = coroutineScope { + val d1 = async { KWorkflow.executeActivity("activity1", options, arg1) } + val d2 = async { KWorkflow.executeActivity("activity2", options, arg2) } + awaitAll(d1, d2) // Returns List +} +``` + +## Typed Activities + +The typed activity API uses direct method references - no stub creation needed. This approach: +- Provides full compile-time type safety for arguments and return types +- Allows different options (timeouts, retry policies) per activity call +- Works with both Kotlin `suspend` and Java non-suspend activity interfaces +- Similar to TypeScript and Python SDK patterns + +```kotlin +// Define activity interface +@ActivityInterface +interface GreetingActivities { + @ActivityMethod + suspend fun composeGreeting(greeting: String, name: String): String + + @ActivityMethod + suspend fun sendEmail(email: Email): SendResult + + @ActivityMethod + suspend fun log(message: String) +} + +// In workflow - direct method reference, no stub needed +val greeting = KWorkflow.executeActivity( + GreetingActivities::composeGreeting, // Direct reference to interface method + KActivityOptions(startToCloseTimeout = 30.seconds), + "Hello", "World" +) + +// Different activity, different options +val result = KWorkflow.executeActivity( + GreetingActivities::sendEmail, + KActivityOptions( + startToCloseTimeout = 2.minutes, + retryOptions = KRetryOptions(maximumAttempts = 5) + ), + email +) + +// Void activities work too +KWorkflow.executeActivity( + GreetingActivities::log, + KActivityOptions(startToCloseTimeout = 5.seconds), + "Processing started" +) +``` + +## Type Safety + +The API uses `KFunction` reflection to extract method metadata and provides compile-time type checking: + +```kotlin +// Compile error! Wrong argument types +KWorkflow.executeActivity( + GreetingActivities::composeGreeting, + options, + 123, true // ✗ Type mismatch: expected String, String +) +``` + +## Parallel Execution + +Use standard `coroutineScope { async { } }` for concurrent execution: + +```kotlin +override suspend fun parallelGreetings(names: List): List = coroutineScope { + names.map { name -> + async { + KWorkflow.executeActivity( + GreetingActivities::composeGreeting, + KActivityOptions(startToCloseTimeout = 10.seconds), + "Hello", name + ) + } + }.awaitAll() // Standard kotlinx.coroutines.awaitAll +} + +// Multiple different activities in parallel +val (result1, result2) = coroutineScope { + val d1 = async { KWorkflow.executeActivity(Activities::operation1, options, arg1) } + val d2 = async { KWorkflow.executeActivity(Activities::operation2, options, arg2) } + awaitAll(d1, d2) +} +``` + +> **Note:** We use standard Kotlin `async` instead of a custom `startActivity` method. The workflow's deterministic dispatcher ensures correct replay behavior. + +## Java Activity Interoperability + +Method references work regardless of whether the activity is defined in Kotlin or Java: + +```kotlin +// Java activity interface works seamlessly +// public interface JavaPaymentActivities { +// PaymentResult processPayment(String orderId, BigDecimal amount); +// } + +val result: PaymentResult = KWorkflow.executeActivity( + JavaPaymentActivities::processPayment, + KActivityOptions(startToCloseTimeout = 2.minutes), + orderId, amount +) +``` + +## Activity Execution API + +The `KWorkflow` object provides type-safe overloads using `KFunction` types: + +```kotlin +object KWorkflow { + // 1 argument + suspend fun executeActivity( + activity: KFunction2, + options: KActivityOptions, + arg1: A1 + ): R + + // 2 arguments + suspend fun executeActivity( + activity: KFunction3, + options: KActivityOptions, + arg1: A1, arg2: A2 + ): R + + // ... up to 6 arguments + + // String-based overloads + suspend inline fun executeActivity( + activityName: String, + options: KActivityOptions, + vararg args: Any? + ): R +} +``` + +## Related + +- [Local Activities](./local-activities.md) - Short-lived local activities +- [Workflows](../workflows/README.md) - Calling activities from workflows + +--- + +**Next:** [Activity Implementation](./implementation.md) diff --git a/kotlin/activities/implementation.md b/kotlin/activities/implementation.md new file mode 100644 index 0000000..566f307 --- /dev/null +++ b/kotlin/activities/implementation.md @@ -0,0 +1,199 @@ +# Activity Implementation + +Activity interfaces can have both suspend and non-suspend methods. The worker handles both automatically. + +## Defining Activities + +```kotlin +@ActivityInterface +interface OrderActivities { + // Suspend method - uses coroutines + @ActivityMethod + suspend fun chargePayment(order: Order): PaymentResult + + // Non-suspend method - runs on thread pool + @ActivityMethod + fun validateOrder(order: Order): Boolean +} + +class OrderActivitiesImpl( + private val paymentService: PaymentService +) : OrderActivities { + + override suspend fun chargePayment(order: Order): PaymentResult { + // Full coroutine support - use withContext, async I/O, etc. + return paymentService.charge(order) + } + + override fun validateOrder(order: Order): Boolean { + return order.items.isNotEmpty() && order.total > 0 + } +} +``` + +## Calling Activities from Workflows + +```kotlin +// Kotlin workflows use method references +val isValid = KWorkflow.executeActivity( + OrderActivities::validateOrder, + KActivityOptions(startToCloseTimeout = 10.seconds), + order +) + +val payment = KWorkflow.executeActivity( + OrderActivities::chargePayment, + KActivityOptions(startToCloseTimeout = 30.seconds), + order +) +``` + +## Java Interoperability + +Java workflows call Kotlin activities using string-based activity names: + +```java +// Java workflow calling Kotlin activity +String result = Workflow.newActivityStub(ActivityOptions.newBuilder() + .setStartToCloseTimeout(Duration.ofSeconds(30)) + .build()) + .execute("chargePayment", PaymentResult.class, order); +``` + +This mirrors how Java workflows call Kotlin workflows - using string names for cross-language interop. + +## Registering Activities + +```kotlin +worker.registerActivitiesImplementations(OrderActivitiesImpl(paymentService)) +``` + +## Heartbeating + +For long-running activities, use heartbeating to report progress and detect cancellation: + +```kotlin +class LongRunningActivitiesImpl : LongRunningActivities { + override suspend fun processLargeFile(filePath: String): ProcessResult { + val context = KActivity.context + val lines = File(filePath).readLines() + + lines.forEachIndexed { index, line -> + context.heartbeat(index) + + // Process line... + processLine(line) + } + + return ProcessResult(linesProcessed = lines.size) + } +} +``` + +## Heartbeat Details Recovery + +Retrieve heartbeat details from a previous failed attempt: + +```kotlin +override suspend fun resumableProcess(data: List): ProcessResult { + val context = KActivity.context + + // Get progress from previous attempt if available + val startIndex = context.heartbeatDetails() ?: 0 + + for (i in startIndex until data.size) { + context.heartbeat(i) + processItem(data[i]) + } + + return ProcessResult(success = true) +} +``` + +## KActivity API + +`KActivity.context` provides access to the activity execution context for both regular and local activities: + +```kotlin +// In activity implementation +val context = KActivity.context + +// Get activity info (works for both regular and local activities) +val info = context.info +println("Activity ${info.activityType}, attempt ${info.attempt}") +println("Is local: ${info.isLocal}") + +// Heartbeat for long-running activities (no-op for local activities) +context.heartbeat(progressDetails) + +// Get heartbeat details from previous attempt (empty for local activities) +val previousProgress = context.heartbeatDetails() + +// Logging (works for both) +val log = context.logger() + +// Regular activities only - throws UnsupportedOperationException for local activities +context.doNotCompleteOnReturn() +val taskToken = context.taskToken +``` + +## KActivityContext Interface + +```kotlin +interface KActivityContext { + val info: KActivityInfo + fun heartbeat(details: Any? = null) // No-op for local activities + fun heartbeatDetails(detailsClass: Class): T? // Empty for local activities + val taskToken: ByteArray // Throws for local activities + fun doNotCompleteOnReturn() // Throws for local activities + val isDoNotCompleteOnReturn: Boolean + fun logger(): Logger + fun logger(name: String): Logger + fun logger(clazz: Class<*>): Logger +} + +// Reified extension for easier Kotlin usage +inline fun KActivityContext.heartbeatDetails(): T? +``` + +## KActivityInfo Interface + +```kotlin +interface KActivityInfo { + // Core identifiers + val activityId: String + val activityType: String + val workflowId: String + val runId: String + val namespace: String + + // Execution context + val taskQueue: String + val attempt: Int + val isLocal: Boolean + + // Timing information + val scheduledTime: Instant? + val startedTime: Instant? + val scheduleToCloseTimeout: Duration? + val startToCloseTimeout: Duration? + val heartbeatTimeout: Duration? + + // Heartbeat state + val heartbeatDetails: Any? + + // Task token for async completion (throws for local activities) + val taskToken: ByteArray +} +``` + +> **Note:** `ActivityCompletionClient` for async activity completion uses the same API as the Java SDK. Async completion is not supported for local activities. + +## Related + +- [Activity Definition](./definition.md) - Interface patterns +- [Worker Setup](../worker/setup.md) - Registering activities + +--- + +**Next:** [Local Activities](./local-activities.md) diff --git a/kotlin/activities/local-activities.md b/kotlin/activities/local-activities.md new file mode 100644 index 0000000..d2b2e39 --- /dev/null +++ b/kotlin/activities/local-activities.md @@ -0,0 +1,86 @@ +# Local Activities + +Local activities use the same stub-less pattern as regular activities. + +## Local Activity Execution + +```kotlin +@ActivityInterface +interface ValidationActivities { + suspend fun validate(input: String): Boolean + fun sanitize(input: String): String // Non-suspend also supported +} + +val isValid = KWorkflow.executeLocalActivity( + ValidationActivities::validate, + KLocalActivityOptions(startToCloseTimeout = 5.seconds), + input +) + +val sanitized = KWorkflow.executeLocalActivity( + ValidationActivities::sanitize, + KLocalActivityOptions(startToCloseTimeout = 1.seconds), + input +) +``` + +## KWorkflow Local Activity Methods + +```kotlin +object KWorkflow { + /** + * Execute a local activity with type-safe method reference. + */ + suspend fun executeLocalActivity( + activity: KFunction2, + options: KLocalActivityOptions, + arg1: A1 + ): R + + suspend fun executeLocalActivity( + activity: KFunction1, + options: KLocalActivityOptions + ): R + + // ... up to 6 arguments +} +``` + +## KLocalActivityOptions + +```kotlin +/** + * Kotlin-native local activity options. + */ +data class KLocalActivityOptions( + val startToCloseTimeout: Duration? = null, + val scheduleToCloseTimeout: Duration? = null, + val localRetryThreshold: Duration? = null, + val retryOptions: KRetryOptions? = null, + // Experimental + @Experimental val summary: String? = null, + @Experimental val priority: Priority? = null +) +``` + +## KActivity.context in Local Activities + +`KActivity.context` is available in local activities with limited functionality: + +| Feature | Local Activity Behavior | +|---------|------------------------| +| `context.info` | Works (`info.isLocal` returns `true`) | +| `context.logger()` | Works | +| `context.heartbeat()` | No-op (ignored) | +| `context.heartbeatDetails()` | Returns `null` | +| `context.taskToken` | Throws `UnsupportedOperationException` | +| `context.doNotCompleteOnReturn()` | Throws `UnsupportedOperationException` | + +## Related + +- [Activity Definition](./definition.md) - Interface patterns +- [Activity Implementation](./implementation.md) - Full implementation details + +--- + +**Next:** [Client](../client/README.md) diff --git a/kotlin/api-parity.md b/kotlin/api-parity.md new file mode 100644 index 0000000..0d545e2 --- /dev/null +++ b/kotlin/api-parity.md @@ -0,0 +1,164 @@ +# Java SDK API Parity + +This document describes the intentional differences between Java and Kotlin SDK APIs, and identifies remaining gaps. + +## APIs Not Needed in Kotlin + +The following Java SDK APIs are **not needed** in the Kotlin SDK due to language differences: + +| Java SDK API | Reason Not Needed | +|--------------|-------------------| +| `Workflow.sleep(Duration)` | Use `kotlinx.coroutines.delay()` - intercepted by dispatcher | +| `Workflow.newTimer(Duration)` | Use `async { delay(duration) }` for racing timers | +| `Workflow.wrap(Exception)` | Kotlin has no checked exceptions - not needed | +| `Activity.wrap(Throwable)` | Kotlin has no checked exceptions - not needed | +| `Workflow.newCancellationScope(...)` | Use Kotlin's `coroutineScope { }` with structured concurrency | +| `Workflow.newDetachedCancellationScope(...)` | Use `supervisorScope { }` or launch in parent scope | +| `Workflow.newWorkflowLock()` | Not needed - cooperative coroutines don't have true concurrency | +| `Workflow.newWorkflowSemaphore(...)` | Use structured concurrency patterns (e.g., `chunked().map { async }.awaitAll()`) | +| `Workflow.newQueue(...)` / `newWorkflowQueue(...)` | Use `mutableListOf` + `awaitCondition` - no concurrent access in suspend model | +| `Workflow.newPromise()` / `newFailedPromise(...)` | Use `CompletableDeferred` from kotlinx.coroutines | +| `Workflow.setDefaultActivityOptions(...)` | Pass `KActivityOptions` per call, or define shared `val defaultOptions` | +| `Workflow.setActivityOptions(...)` | Pass options per call | +| `Workflow.applyActivityOptions(...)` | Pass options per call | +| `Workflow.setDefaultLocalActivityOptions(...)` | Pass `KLocalActivityOptions` per call | +| `Workflow.applyLocalActivityOptions(...)` | Pass options per call | + +## Activity API Design Decisions + +### Single heartbeat() API + +The Kotlin SDK provides a single `heartbeat()` method on `KActivityContext` for both sync and suspend activities: + +```kotlin +context.heartbeat(progressDetails) +``` + +**Rationale:** Heartbeat is a short, non-blocking operation that records progress locally. The actual network call happens asynchronously in the background. A separate `suspendHeartbeat()` API is unnecessary. + +### Both Sync and Suspend Activities Supported + +The Kotlin SDK supports both regular and suspend activity methods: + +```kotlin +@ActivityInterface +interface OrderActivities { + fun validate(order: Order): Boolean // Sync - runs on thread pool + suspend fun fetchExternal(id: String): Data // Suspend - runs on coroutine +} +``` + +**Rationale:** This matches other Kotlin frameworks (Spring, Micronaut, gRPC-Kotlin) that detect method signatures and handle them appropriately. It reduces migration friction from Java SDK and gives developers choice. + +## Cancellation Support + +Kotlin coroutine cancellation is fully integrated with Temporal workflow cancellation: + +- Workflow cancellation triggers `coroutineScope.cancel(CancellationException(...))` +- Cancellation propagates to all child coroutines (activities, timers, child workflows) +- Activities use `suspendCancellableCoroutine` with `invokeOnCancellation` to cancel via Temporal +- Use standard Kotlin patterns: `try/finally`, `use { }`, `invokeOnCompletion` + +## Implemented APIs (Java SDK Parity) + +The following Java SDK workflow APIs have Kotlin equivalents in `KWorkflow`: + +### Search Attributes & Memo + +| Java SDK API | Kotlin SDK | +|--------------|------------| +| `Workflow.getTypedSearchAttributes()` | `KWorkflow.typedSearchAttributes` | +| `Workflow.upsertTypedSearchAttributes(...)` | `KWorkflow.upsertTypedSearchAttributes()` | +| `Workflow.getMemo(key, class)` | `KWorkflow.memo` | +| `Workflow.upsertMemo(...)` | `KWorkflow.upsertMemo()` | + +### Workflow State & Context + +| Java SDK API | Kotlin SDK | +|--------------|------------| +| `Workflow.getLastCompletionResult(class)` | `KWorkflow.lastCompletionResult()` | +| `Workflow.getPreviousRunFailure()` | `KWorkflow.previousRunFailure` | +| `Workflow.isReplaying()` | `KWorkflow.isReplaying` | +| `Workflow.getCurrentUpdateInfo()` | `KWorkflow.currentUpdateInfo` | +| `Workflow.isEveryHandlerFinished()` | `KWorkflow.isEveryHandlerFinished` | +| `Workflow.setCurrentDetails(...)` | `KWorkflow.currentDetails = ...` | +| `Workflow.getCurrentDetails()` | `KWorkflow.currentDetails` | +| `Workflow.getMetricsScope()` | `KWorkflow.metricsScope` | + +### Side Effects & Utilities + +| Java SDK API | Kotlin SDK | +|--------------|------------| +| `Workflow.sideEffect(...)` | `KWorkflow.sideEffect()` | +| `Workflow.mutableSideEffect(...)` | `KWorkflow.mutableSideEffect()` | +| `Workflow.getVersion(...)` | `KWorkflow.version()` | +| `Workflow.retry(...)` | `KWorkflow.retry()` | +| `Workflow.randomUUID()` | `KWorkflow.randomUUID()` | +| `Workflow.newRandom()` | `KWorkflow.newRandom()` | + +### Dynamic Handler Registration + +| Java SDK API | Kotlin SDK | +|--------------|------------| +| `Workflow.registerListener(DynamicSignalHandler)` | `KWorkflow.registerDynamicSignalHandler()` | +| `Workflow.registerListener(DynamicQueryHandler)` | `KWorkflow.registerDynamicQueryHandler()` | +| `Workflow.registerListener(DynamicUpdateHandler)` | `KWorkflow.registerDynamicUpdateHandler()` | +| N/A (new in Kotlin SDK) | `KWorkflow.registerSignalHandler()` | +| N/A (new in Kotlin SDK) | `KWorkflow.registerQueryHandler()` | +| N/A (new in Kotlin SDK) | `KWorkflow.registerUpdateHandler()` | +| N/A (new in Kotlin SDK) | `KWorkflow.registerDynamicUpdateValidator()` | + +## APIs Identical to Java SDK + +The following areas use the same API and behavior as the Java SDK: + +| Area | Notes | +|------|-------| +| Error Handling | `ApplicationFailure`, exception types (`ActivityFailure`, `ChildWorkflowFailure`, `TimeoutFailure`, etc.), retry behavior | +| Workflow Info | `KWorkflow.info` provides property-style access to Java `WorkflowInfo` (same properties: `workflowId`, `runId`, `parentWorkflowId`, `attempt`, `taskQueue`, etc.) | + +## Remaining Gaps + +| Java SDK API | Status | +|--------------|--------| +| `Workflow.newNexusServiceStub(...)` | Nexus support - deferred to separate project | +| `Workflow.startNexusOperation(...)` | Nexus support - deferred to separate project | +| `Workflow.getInstance()` | Advanced use case - low priority | + +## KWorkflowInfo + +`KWorkflow.info` returns `KWorkflowInfo`, a property-style wrapper around Java's `WorkflowInfo`: + +| Java WorkflowInfo | Kotlin SDK | +|-------------------|------------| +| `getWorkflowId()` | `KWorkflowInfo.workflowId` | +| `getRunId()` | `KWorkflowInfo.runId` | +| `getWorkflowType()` | `KWorkflowInfo.workflowType` | +| `getTaskQueue()` | `KWorkflowInfo.taskQueue` | +| `getNamespace()` | `KWorkflowInfo.namespace` | +| `getAttempt()` | `KWorkflowInfo.attempt` | +| `getParentWorkflowId()` | `KWorkflowInfo.parentWorkflowId: String?` | +| `getParentRunId()` | `KWorkflowInfo.parentRunId: String?` | +| `getContinuedExecutionRunId()` | `KWorkflowInfo.continuedExecutionRunId: String?` | +| `getCronSchedule()` | `KWorkflowInfo.cronSchedule: String?` | +| `getSearchAttributes()` | `KWorkflowInfo.typedSearchAttributes` | +| `getHistoryLength()` | `KWorkflowInfo.historyLength` | +| `isContinueAsNewSuggested()` | `KWorkflowInfo.isContinueAsNewSuggested` | +| `getFirstExecutionRunId()` | `KWorkflowInfo.firstExecutionRunId` | +| `getOriginalExecutionRunId()` | `KWorkflowInfo.originalExecutionRunId` | +| `getCurrentBuildId()` | `KWorkflowInfo.currentBuildId: String?` | +| `getPriority()` | `@Experimental KWorkflowInfo.priority` | + +## KActivityInfo + +| Java ActivityInfo | Kotlin SDK | +|-------------------|------------| +| `getWorkflowType()` | `KActivityInfo.workflowType` | +| `getCurrentAttemptScheduledTimestamp()` | `KActivityInfo.currentAttemptScheduledTimestamp` | +| `getRetryOptions()` | `KActivityInfo.retryOptions` | + +## Related + +- [Migration Guide](./migration.md) - Practical migration steps +- [Kotlin Idioms](./kotlin-idioms.md) - Kotlin-specific patterns +- [README](./README.md) - Back to documentation home diff --git a/kotlin/client/README.md b/kotlin/client/README.md new file mode 100644 index 0000000..cf0b076 --- /dev/null +++ b/kotlin/client/README.md @@ -0,0 +1,90 @@ +# Client API + +This section covers the Kotlin client API for interacting with Temporal workflows. + +## Overview + +The Kotlin SDK provides `KWorkflowClient` with suspend functions and type-safe workflow APIs for starting and interacting with workflows. + +## Documents + +| Document | Description | +|----------|-------------| +| [Workflow Client](./workflow-client.md) | KWorkflowClient, starting workflows | +| [Workflow Handles](./workflow-handle.md) | Typed/Untyped handles, signals, queries, results | +| [Advanced Operations](./advanced.md) | SignalWithStart, UpdateWithStart | + +## Quick Reference + +### Creating a Client + +```kotlin +val service = WorkflowServiceStubs.newLocalServiceStubs() + +val client = KWorkflowClient(service) { + setNamespace("default") + setDataConverter(myConverter) +} +``` + +### Starting Workflows + +```kotlin +// Execute and wait for result +val result = client.executeWorkflow( + GreetingWorkflow::getGreeting, + KWorkflowOptions( + workflowId = "greeting-123", + taskQueue = "greetings" + ), + "Temporal" +) + +// Start async and get handle +val handle = client.startWorkflow( + GreetingWorkflow::getGreeting, + KWorkflowOptions( + workflowId = "greeting-123", + taskQueue = "greetings" + ), + "Temporal" +) +val result = handle.result() +``` + +### Interacting with Workflows + +```kotlin +val handle = client.getWorkflowHandle("order-123") + +// Signal +handle.signal(OrderWorkflow::updatePriority, Priority.HIGH) + +// Query +val status = handle.query(OrderWorkflow::status) + +// Update +val result = handle.executeUpdate(OrderWorkflow::addItem, newItem) + +// Cancel +handle.cancel() +``` + +### Key Patterns + +| Pattern | API | +|---------|-----| +| Execute workflow | `client.executeWorkflow(Interface::method, options, args)` | +| Start workflow | `client.startWorkflow(Interface::method, options, args)` | +| Get handle by ID | `client.getWorkflowHandle(workflowId)` | +| Signal with start | `client.signalWithStart(...)` | +| Update with start | `client.executeUpdateWithStart(...)` | + +## Related + +- [Workflow Handles](./workflow-handle.md) - Interacting with running workflows +- [Advanced Operations](./advanced.md) - Atomic operations + +--- + +**Next:** [Workflow Client](./workflow-client.md) diff --git a/kotlin/client/advanced.md b/kotlin/client/advanced.md new file mode 100644 index 0000000..135923a --- /dev/null +++ b/kotlin/client/advanced.md @@ -0,0 +1,178 @@ +# Advanced Client Operations + +Both SignalWithStart and UpdateWithStart use `withStartWorkflowOperation` to create a handle that becomes usable after the atomic operation completes. + +## SignalWithStart + +Atomically start a workflow and send a signal. If the workflow already exists, only the signal is sent: + +```kotlin +// Create handle (not yet started) +val handle = client.withStartWorkflowOperation( + OrderWorkflow::processOrder, + KWorkflowOptions( + workflowId = "order-123", + taskQueue = "orders" + ), + order +) + +// Atomically start workflow and send signal +client.signalWithStart(handle, OrderWorkflow::updatePriority, Priority.HIGH) + +// Handle is now usable for queries/signals/result +val status = handle.query(OrderWorkflow::status) +val result = handle.result() // Type inferred as OrderResult +``` + +## UpdateWithStart + +Atomically start a workflow and send an update. Behavior depends on `workflowIdConflictPolicy`: +- `USE_EXISTING`: sends update to existing workflow +- `FAIL`: throws exception if workflow already exists + +### KUpdateWithStartOptions + +```kotlin +/** + * Options for update-with-start operations. + */ +data class KUpdateWithStartOptions( + /** Stage to wait for before returning (required for startUpdateWithStart) */ + val waitForStage: WorkflowUpdateStage = WorkflowUpdateStage.ACCEPTED, + + /** Optional update ID for idempotency */ + val updateId: String? = null +) +``` + +### Execute and Wait for Completion + +```kotlin +// Create handle (not yet started) +val handle = client.withStartWorkflowOperation( + OrderWorkflow::processOrder, + KWorkflowOptions( + workflowId = "order-123", + taskQueue = "orders", + workflowIdConflictPolicy = WorkflowIdConflictPolicy.USE_EXISTING + ), + order +) + +// Execute update with start (waits for update completion) +val updateResult: Boolean = client.executeUpdateWithStart( + handle, + OrderWorkflow::addItem, + KUpdateWithStartOptions(), + newItem +) + +// Handle is now usable +val workflowResult: OrderResult = handle.result() +``` + +### Start Async (Don't Wait for Completion) + +```kotlin +// Create handle (not yet started) +val handle = client.withStartWorkflowOperation( + OrderWorkflow::processOrder, + KWorkflowOptions( + workflowId = "order-456", + taskQueue = "orders", + workflowIdConflictPolicy = WorkflowIdConflictPolicy.FAIL + ), + order +) + +// Start update and return immediately after it's accepted +val updateHandle: KUpdateHandle = client.startUpdateWithStart( + handle, + OrderWorkflow::addItem, + KUpdateWithStartOptions(waitForStage = WorkflowUpdateStage.ACCEPTED), + newItem +) + +// Handle is now usable, get workflow result +val workflowResult = handle.result() + +// Get update result when needed +val updateResult = updateHandle.result() +``` + +## KWorkflowClient API for Advanced Operations + +```kotlin +class KWorkflowClient { + /** + * Create a workflow handle for use with signal-with-start or update-with-start. + * The handle is not usable until signalWithStart or an update-with-start method is called. + */ + fun withStartWorkflowOperation( + workflow: KSuspendFunction1, + options: KWorkflowOptions + ): KTypedWorkflowHandle + + fun withStartWorkflowOperation( + workflow: KSuspendFunction2, + options: KWorkflowOptions, + arg1: A1 + ): KTypedWorkflowHandle + + /** + * Atomically start a workflow and send a signal. + * If the workflow already exists, only the signal is sent. + * After this call, the handle becomes usable. + */ + suspend fun signalWithStart( + handle: KTypedWorkflowHandle, + signal: KSuspendFunction2, + signalArg: SA1 + ) + + /** + * Atomically start a workflow and execute an update, waiting for completion. + * After this call, the handle becomes usable. + */ + suspend fun executeUpdateWithStart( + handle: KTypedWorkflowHandle, + update: KSuspendFunction1, + options: KUpdateWithStartOptions = KUpdateWithStartOptions() + ): UR + + suspend fun executeUpdateWithStart( + handle: KTypedWorkflowHandle, + update: KSuspendFunction2, + options: KUpdateWithStartOptions = KUpdateWithStartOptions(), + updateArg: UA1 + ): UR + + /** + * Atomically start a workflow and send an update, returning immediately after + * the update reaches the specified wait stage. + * After this call, the handle becomes usable. + */ + suspend fun startUpdateWithStart( + handle: KTypedWorkflowHandle, + update: KSuspendFunction1, + options: KUpdateWithStartOptions = KUpdateWithStartOptions() + ): KUpdateHandle + + suspend fun startUpdateWithStart( + handle: KTypedWorkflowHandle, + update: KSuspendFunction2, + options: KUpdateWithStartOptions = KUpdateWithStartOptions(), + updateArg: UA1 + ): KUpdateHandle +} +``` + +## Related + +- [Workflow Client](./workflow-client.md) - Basic client operations +- [Workflow Handles](./workflow-handle.md) - Interacting with workflows + +--- + +**Next:** [Worker](../worker/README.md) diff --git a/kotlin/client/workflow-client.md b/kotlin/client/workflow-client.md new file mode 100644 index 0000000..9258c5b --- /dev/null +++ b/kotlin/client/workflow-client.md @@ -0,0 +1,125 @@ +# Workflow Client + +## Creating a Client + +```kotlin +val service = WorkflowServiceStubs.newLocalServiceStubs() + +// Create KWorkflowClient with DSL configuration +val client = KWorkflowClient(service) { + setNamespace("default") + setDataConverter(myConverter) +} + +// For blocking calls from non-suspend contexts, use runBlocking +val result = runBlocking { + client.executeWorkflow(MyWorkflow::process, options, input) +} +``` + +## KWorkflowClient + +`KWorkflowClient` provides Kotlin-specific APIs with suspend functions for starting and interacting with workflows: + +```kotlin +/** + * Kotlin workflow client providing suspend functions and type-safe workflow APIs. + * + * @param service The WorkflowServiceStubs to connect to + * @param options DSL builder for WorkflowClientOptions + */ +class KWorkflowClient( + service: WorkflowServiceStubs, + options: WorkflowClientOptions.Builder.() -> Unit = {} +) { + /** The underlying WorkflowClient for advanced use cases */ + val workflowClient: WorkflowClient + + /** + * Start a workflow and return a handle for interaction. + * Does not wait for the workflow to complete. + */ + suspend fun startWorkflow( + workflow: KSuspendFunction1, + options: KWorkflowOptions + ): KTypedWorkflowHandle + + suspend fun startWorkflow( + workflow: KSuspendFunction2, + options: KWorkflowOptions, + arg: A1 + ): KTypedWorkflowHandle + + // Overloads for 2-6 arguments... + + /** + * Start a workflow and wait for its result. + * Suspends until the workflow completes. + */ + suspend fun executeWorkflow( + workflow: KSuspendFunction1, + options: KWorkflowOptions + ): R + + suspend fun executeWorkflow( + workflow: KSuspendFunction2, + options: KWorkflowOptions, + arg: A1 + ): R + + // Overloads for 2-6 arguments... + + /** + * Get a typed handle for an existing workflow by ID. + * Use this to signal, query, or get results from a workflow started elsewhere. + */ + inline fun getWorkflowHandle(workflowId: String): KWorkflowHandle + inline fun getWorkflowHandle(workflowId: String, runId: String): KWorkflowHandle + + /** + * Get an untyped handle for an existing workflow by ID. + * Use when you don't know the workflow type at compile time. + */ + fun getUntypedWorkflowHandle(workflowId: String): WorkflowHandle + fun getUntypedWorkflowHandle(workflowId: String, runId: String): WorkflowHandle +} +``` + +## Starting Workflows + +```kotlin +// Execute workflow and wait for result (suspend function) +val result = client.executeWorkflow( + GreetingWorkflow::getGreeting, + KWorkflowOptions( + workflowId = "greeting-123", + taskQueue = "greeting-queue", + workflowExecutionTimeout = 1.hours + ), + "Temporal" +) + +// Or start async and get handle +val handle = client.startWorkflow( + GreetingWorkflow::getGreeting, + KWorkflowOptions( + workflowId = "greeting-123", + taskQueue = "greeting-queue" + ), + "Temporal" +) +val result = handle.result() // Type inferred as String from method reference +``` + +## KWorkflowOptions + +See [KOptions](../configuration/koptions.md#kworkflowoptions) for the full `KWorkflowOptions` reference. + +## Related + +- [Advanced Operations](./advanced.md) - SignalWithStart, UpdateWithStart +- [KOptions](../configuration/koptions.md) - KWorkflowOptions reference + +--- + +**Next:** [Workflow Handles](./workflow-handle.md) diff --git a/kotlin/client/workflow-handle.md b/kotlin/client/workflow-handle.md new file mode 100644 index 0000000..ca60529 --- /dev/null +++ b/kotlin/client/workflow-handle.md @@ -0,0 +1,292 @@ +# Workflow Handles + +For interacting with existing workflows (signals, queries, results, cancellation), use a typed or untyped handle. + +## Typed Handles + +```kotlin +// Get typed handle for existing workflow by ID +val handle = client.getWorkflowHandle("order-123") + +// Send signal - method reference provides type safety +handle.signal(OrderWorkflow::updatePriority, Priority.HIGH) + +// Query - method reference with compile-time type checking +val status = handle.query(OrderWorkflow::status) +val count = handle.query(OrderWorkflow::getItemCount) + +// Get result (suspends until workflow completes) +val result = handle.result() + +// Updates - execute and wait for result +val updateResult = handle.executeUpdate(OrderWorkflow::addItem, newItem) + +// Updates - start and get handle (don't wait for completion) +val updateHandle = handle.startUpdate( + OrderWorkflow::addItem, + waitForStage = WorkflowUpdateStage.ACCEPTED, + arg = newItem +) +// ... do other work ... +val result = updateHandle.result() // Wait for result when needed + +// Cancel or terminate +handle.cancel() +handle.terminate("No longer needed") + +// Workflow metadata +val description = handle.describe() +println("Workflow ID: ${handle.workflowId}, Run ID: ${handle.runId}") +println("Status: ${description.status}") +``` + +## Architectural Note: Classes vs Interfaces + +All handle types (`KWorkflowHandle`, `KTypedWorkflowHandle`, `KUpdateHandle`, `WorkflowHandle`, `KChildWorkflowHandle`) are **classes** rather than interfaces. This design choice enables: + +1. **Reified type parameters** - `inline fun ` methods directly on handle types +2. **Better IDE discoverability** - Methods appear directly in autocomplete +3. **No extension function workarounds** - No need for separate extension functions for reified generics +4. **Full testing support** - mockk and other frameworks can mock classes + +## KWorkflowHandle API + +```kotlin +// Base handle - returned by getWorkflowHandle(id) +// Result type is unknown, must specify when calling result() +open class KWorkflowHandle( + val workflowId: String, + val runId: String?, + val execution: WorkflowExecution, + // ... internal state +) { + // Result - requires explicit type since we don't know it + suspend fun result(resultClass: Class): R + inline suspend fun result(): R // Reified version + + // Signals - type-safe method references + // Note: Signal handlers can be either suspend or non-suspend functions + suspend fun signal(method: KFunction1) + suspend fun signal(method: KFunction2, arg: A1) + suspend fun signal(method: KFunction3, arg1: A1, arg2: A2) + + // Queries - type-safe method references (suspend for network I/O) + suspend fun query(method: KFunction1): R + suspend fun query(method: KFunction2, arg: A1): R + + // Updates - execute and wait for result (suspend functions) + suspend fun executeUpdate(method: KSuspendFunction1): R + suspend fun executeUpdate(method: KSuspendFunction2, arg: A1): R + suspend fun executeUpdate(method: KSuspendFunction3, arg1: A1, arg2: A2): R + // ... up to 6 arguments + + // Updates - start and return handle without waiting for completion + suspend fun startUpdate( + method: KSuspendFunction1, + waitForStage: WorkflowUpdateStage = WorkflowUpdateStage.ACCEPTED, + updateId: String? = null + ): KUpdateHandle + suspend fun startUpdate( + method: KSuspendFunction2, + waitForStage: WorkflowUpdateStage = WorkflowUpdateStage.ACCEPTED, + updateId: String? = null, + arg: A1 + ): KUpdateHandle + suspend fun startUpdate( + method: KSuspendFunction3, + waitForStage: WorkflowUpdateStage = WorkflowUpdateStage.ACCEPTED, + updateId: String? = null, + arg1: A1, + arg2: A2 + ): KUpdateHandle + // ... up to 6 arguments + + // Get handle for existing update by ID + fun getUpdateHandle(updateId: String, resultClass: Class): KUpdateHandle + inline fun getUpdateHandle(updateId: String): KUpdateHandle // Reified version + + // Lifecycle + suspend fun cancel() + suspend fun terminate(reason: String? = null) + suspend fun describe(): KWorkflowExecutionDescription + + // Java SDK interop + fun toStub(): WorkflowStub +} +``` + +**Note on query methods:** Although queries are synchronous within the workflow, client-side query calls involve network I/O to the Temporal service, which is why they are `suspend` functions following idiomatic Kotlin patterns. + +## KTypedWorkflowHandle + +Extended handle returned by `startWorkflow()` - result type R is captured from the workflow method reference: + +```kotlin +class KTypedWorkflowHandle( + workflowId: String, + runId: String?, + execution: WorkflowExecution, + // ... internal state +) : KWorkflowHandle(...) { + // Result type is known from method reference - no type parameter needed + suspend fun result(): R + suspend fun result(timeout: java.time.Duration): R +} +``` + +**How result type is captured:** + +```kotlin +// startWorkflow captures result type from method reference +suspend fun startWorkflow( + workflow: KSuspendFunction2, // R is captured here + options: KWorkflowOptions, + arg: A1 +): KTypedWorkflowHandle // R is preserved in return type + +// Usage - result type is inferred +val handle = client.startWorkflow( + OrderWorkflow::processOrder, // KSuspendFunction2 + options, + order +) +val result: OrderResult = handle.result() // No type parameter needed! + +// getWorkflowHandle doesn't know result type +val existingHandle = client.getWorkflowHandle(workflowId) +val result = existingHandle.result() // Must specify type +``` + +## KUpdateHandle + +```kotlin +class KUpdateHandle( + val updateId: String, + val execution: WorkflowExecution, + // ... internal state +) { + suspend fun result(): R + suspend fun result(timeout: java.time.Duration): R +} +``` + +## KWorkflowExecutionDescription + +Kotlin wrapper for workflow execution description with idiomatic API: + +```kotlin +class KWorkflowExecutionDescription( + private val delegate: WorkflowExecutionDescription +) { + // Properties from WorkflowExecutionMetadata + val execution: WorkflowExecution + val workflowType: String + val taskQueue: String + val startTime: Instant + val executionTime: Instant + val closeTime: Instant? + val status: WorkflowExecutionStatus + val historyLength: Long + val parentNamespace: String? + val parentExecution: WorkflowExecution? + val rootExecution: WorkflowExecution? + val firstRunId: String? + val executionDuration: Duration? + val typedSearchAttributes: SearchAttributes + + // Reified memo access + inline fun memo(key: String): T? + inline fun memo(key: String, genericType: Type): T? + + // Experimental properties + @Experimental val staticSummary: String? + @Experimental val staticDetails: String? + + // Raw response for advanced use cases + val rawDescription: DescribeWorkflowExecutionResponse + + // Java interop + fun toWorkflowExecutionDescription(): WorkflowExecutionDescription +} +``` + +**Usage:** +```kotlin +val description = handle.describe() +println("Type: ${description.workflowType}") +println("Status: ${description.status}") +val config: MyConfig? = description.memo("config") +``` + +## Untyped Handles + +For cases where you don't know the workflow type at compile time: + +```kotlin +// Untyped handle - signal/query by string name +val untypedHandle = client.getUntypedWorkflowHandle("order-123") + +// Operations use string names instead of method references +untypedHandle.signal("updatePriority", Priority.HIGH) +val status = untypedHandle.query("status") +val result = untypedHandle.result() + +// Updates by name +val updateResult = untypedHandle.executeUpdate("addItem", newItem) +val updateHandle = untypedHandle.startUpdate("addItem", arg = newItem) + +// Cancel/terminate work the same +untypedHandle.cancel() +``` + +```kotlin +class WorkflowHandle( + val workflowId: String, + val runId: String?, + val execution: WorkflowExecution, + // ... internal state +) { + suspend fun result(resultClass: Class): R + inline suspend fun result(): R // Reified version + + suspend fun signal(signalName: String, vararg args: Any?) + + suspend fun query(queryName: String, resultClass: Class, vararg args: Any?): R + inline suspend fun query(queryName: String, vararg args: Any?): R // Reified version + + suspend fun executeUpdate(updateName: String, resultClass: Class, vararg args: Any?): R + inline suspend fun executeUpdate(updateName: String, vararg args: Any?): R // Reified version + + suspend fun startUpdate( + updateName: String, + resultClass: Class, + waitForStage: WorkflowUpdateStage = WorkflowUpdateStage.ACCEPTED, + updateId: String? = null, + vararg args: Any? + ): KUpdateHandle + inline suspend fun startUpdate( + updateName: String, + waitForStage: WorkflowUpdateStage = WorkflowUpdateStage.ACCEPTED, + updateId: String? = null, + vararg args: Any? + ): KUpdateHandle // Reified version + + suspend fun cancel() + suspend fun terminate(reason: String? = null) + suspend fun describe(): KWorkflowExecutionDescription + + // Java SDK interop + fun toStub(): WorkflowStub +} +``` + +This pattern matches Python SDK's `WorkflowHandle` with the same method names (`signal`, `query`, `result`, `cancel`, `terminate`, `execute_update`). + +## Related + +- [Workflow Client](./workflow-client.md) - Creating clients +- [Signals, Queries & Updates](../workflows/signals-queries.md) - Handler definitions + +--- + +**Next:** [Advanced Operations](./advanced.md) diff --git a/kotlin/configuration/README.md b/kotlin/configuration/README.md new file mode 100644 index 0000000..d2bd78f --- /dev/null +++ b/kotlin/configuration/README.md @@ -0,0 +1,73 @@ +# Configuration + +This section covers configuration options for the Kotlin SDK. + +## Documents + +| Document | Description | +|----------|-------------| +| [KOptions](./koptions.md) | Kotlin-native option classes (Activity, Workflow, Retry, etc.) | +| [Data Conversion](./data-conversion.md) | kotlinx.serialization, Jackson configuration | +| [Interceptors](./interceptors.md) | Worker, Workflow, Activity interceptors | + +## Overview + +The Kotlin SDK provides native Kotlin configuration classes that: +- Accept `kotlin.time.Duration` directly +- Use named parameters with default values +- Follow immutable data class patterns +- Support `copy()` for creating variants + +## Quick Reference + +### KOptions Classes + +```kotlin +// Activity options +KActivityOptions( + startToCloseTimeout = 30.seconds, + retryOptions = KRetryOptions(maximumAttempts = 3) +) + +// Local activity options +KLocalActivityOptions( + startToCloseTimeout = 5.seconds, + localRetryThreshold = 10.seconds +) + +// Child workflow options +KChildWorkflowOptions( + workflowId = "child-123", + workflowExecutionTimeout = 1.hours +) + +// Workflow options (for client) +KWorkflowOptions( + workflowId = "order-123", + taskQueue = "orders", + workflowExecutionTimeout = 24.hours +) +``` + +### Data Conversion + +```kotlin +// kotlinx.serialization (default) +@Serializable +data class Order(val id: String, val items: List) + +// Jackson (for Java interop) +val converter = DefaultDataConverter.newDefaultInstance().withPayloadConverterOverrides( + JacksonJsonPayloadConverter(KotlinObjectMapperFactory.new()) +) +``` + +## Related + +- [KOptions](./koptions.md) - Full options reference +- [Data Conversion](./data-conversion.md) - Serialization configuration +- [Interceptors](./interceptors.md) - Cross-cutting concerns + +--- + +**Next:** [KOptions](./koptions.md) diff --git a/kotlin/configuration/data-conversion.md b/kotlin/configuration/data-conversion.md new file mode 100644 index 0000000..cf4d874 --- /dev/null +++ b/kotlin/configuration/data-conversion.md @@ -0,0 +1,128 @@ +# Data Conversion + +The Kotlin SDK uses `kotlinx.serialization` by default for JSON serialization. It provides compile-time safety, no reflection overhead, and native Kotlin support. + +## kotlinx.serialization (Default) + +Annotate data classes with `@Serializable`: + +```kotlin +@Serializable +data class Order( + val id: String, + val items: List, + val status: OrderStatus +) + +@Serializable +data class OrderItem( + val productId: String, + val quantity: Int +) + +@Serializable +enum class OrderStatus { PENDING, PROCESSING, COMPLETED } +``` + +No additional configuration needed—the SDK automatically uses `kotlinx.serialization` for classes annotated with `@Serializable`. + +### Custom JSON Configuration + +```kotlin +val client = WorkflowClient(service) { + dataConverter = KotlinxSerializationDataConverter { + ignoreUnknownKeys = true + prettyPrint = false // default + encodeDefaults = true + } +} +``` + +### Polymorphic Types + +For sealed classes and interfaces: + +```kotlin +@Serializable +sealed class PaymentMethod { + @Serializable + @SerialName("credit_card") + data class CreditCard(val number: String, val expiry: String) : PaymentMethod() + + @Serializable + @SerialName("bank_transfer") + data class BankTransfer(val accountNumber: String) : PaymentMethod() +} +``` + +### Custom Serializers + +For types that need custom serialization: + +```kotlin +@Serializable(with = BigDecimalSerializer::class) +data class Money(val amount: BigDecimal, val currency: String) + +object BigDecimalSerializer : KSerializer { + override val descriptor = PrimitiveSerialDescriptor("BigDecimal", PrimitiveKind.STRING) + override fun serialize(encoder: Encoder, value: BigDecimal) = encoder.encodeString(value.toPlainString()) + override fun deserialize(decoder: Decoder) = BigDecimal(decoder.decodeString()) +} +``` + +## Jackson (Optional, for Java Interop) + +For mixed Java/Kotlin codebases or when integrating with existing Jackson-based infrastructure: + +```kotlin +val converter = DefaultDataConverter.newDefaultInstance().withPayloadConverterOverrides( + JacksonJsonPayloadConverter( + KotlinObjectMapperFactory.new() + ) +) + +val client = WorkflowClient(service) { + dataConverter = converter +} +``` + +> **Note:** Jackson requires the `jackson-module-kotlin` dependency and uses runtime reflection. Prefer `kotlinx.serialization` for pure Kotlin projects. + +### Custom Jackson Configuration + +```kotlin +val objectMapper = KotlinObjectMapperFactory.new().apply { + configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false) + registerModule(JavaTimeModule()) +} + +val converter = DefaultDataConverter.newDefaultInstance().withPayloadConverterOverrides( + JacksonJsonPayloadConverter(objectMapper) +) +``` + +## Comparison + +| Feature | kotlinx.serialization | Jackson | +|---------|----------------------|---------| +| Reflection | None (compile-time) | Required | +| Performance | Faster | Slower | +| Kotlin support | Native | Via module | +| Java interop | Limited | Excellent | +| Setup | `@Serializable` annotation | Automatic | +| Bundle size | Smaller | Larger | + +## Recommendations + +- **Pure Kotlin projects**: Use `kotlinx.serialization` +- **Mixed Java/Kotlin**: Consider Jackson for shared data types +- **Existing Jackson infrastructure**: Use Jackson for consistency + +## Related + +- [KOptions](./koptions.md) - Configuration options +- [Interceptors](./interceptors.md) - Cross-cutting concerns + +--- + +**Next:** [Interceptors](./interceptors.md) diff --git a/kotlin/configuration/interceptors.md b/kotlin/configuration/interceptors.md new file mode 100644 index 0000000..ba082b6 --- /dev/null +++ b/kotlin/configuration/interceptors.md @@ -0,0 +1,389 @@ +# Interceptors + +Interceptors allow you to intercept workflow and activity executions to add cross-cutting concerns like logging, metrics, tracing, and custom error handling. The Kotlin SDK provides suspend-function-aware interceptors that integrate naturally with coroutines. + +## KWorkerInterceptor + +The main entry point for interceptors. Registered with `KWorkerFactory` and called when workflows or activities are instantiated. + +```kotlin +/** + * Intercepts workflow and activity executions. + * + * Prefer extending [KWorkerInterceptorBase] and overriding only the methods you need. + */ +interface KWorkerInterceptor { + /** + * Called when a workflow is instantiated. May create a [KWorkflowInboundCallsInterceptor]. + * The returned interceptor must forward all calls to [next]. + */ + fun interceptWorkflow(next: KWorkflowInboundCallsInterceptor): KWorkflowInboundCallsInterceptor + + /** + * Called when an activity task is received. May create a [KActivityInboundCallsInterceptor]. + * The returned interceptor must forward all calls to [next]. + */ + fun interceptActivity(next: KActivityInboundCallsInterceptor): KActivityInboundCallsInterceptor +} + +/** + * Base implementation that passes through all calls. Extend this class and override only needed methods. + */ +open class KWorkerInterceptorBase : KWorkerInterceptor { + override fun interceptWorkflow(next: KWorkflowInboundCallsInterceptor) = next + override fun interceptActivity(next: KActivityInboundCallsInterceptor) = next +} +``` + +## Registering Interceptors + +Interceptors are registered via `KWorkerFactory`: + +```kotlin +val factory = KWorkerFactory(client) { + workerInterceptors = listOf( + LoggingInterceptor() + ) +} + +val worker = factory.newWorker("task-queue") +worker.registerWorkflowImplementationTypes() +worker.registerActivitiesImplementations(MyActivitiesImpl()) + +factory.start() +``` + +## KWorkflowInboundCallsInterceptor + +Intercepts inbound calls to workflow execution (workflow method, signals, queries, updates). + +```kotlin +interface KWorkflowInboundCallsInterceptor { + /** Called when the workflow is instantiated. Use this to wrap the outbound interceptor. */ + suspend fun init(outboundCalls: KWorkflowOutboundCallsInterceptor) + + /** Called when the workflow main method is invoked. */ + suspend fun execute(input: KWorkflowInput): KWorkflowOutput + + /** Called when a signal is delivered to the workflow. */ + suspend fun handleSignal(input: KSignalInput) + + /** Called when a query is made to the workflow. Note: Queries must be synchronous. */ + fun handleQuery(input: KQueryInput): KQueryOutput + + /** Called to validate an update before execution. Throw an exception to reject. */ + fun validateUpdate(input: KUpdateInput) + + /** Called to execute an update after validation passes. */ + suspend fun executeUpdate(input: KUpdateInput): KUpdateOutput +} + +/** Base implementation that forwards all calls to the next interceptor. */ +open class KWorkflowInboundCallsInterceptorBase( + protected val next: KWorkflowInboundCallsInterceptor +) : KWorkflowInboundCallsInterceptor { + override suspend fun init(outboundCalls: KWorkflowOutboundCallsInterceptor) = next.init(outboundCalls) + override suspend fun execute(input: KWorkflowInput) = next.execute(input) + override suspend fun handleSignal(input: KSignalInput) = next.handleSignal(input) + override fun handleQuery(input: KQueryInput) = next.handleQuery(input) + override fun validateUpdate(input: KUpdateInput) = next.validateUpdate(input) + override suspend fun executeUpdate(input: KUpdateInput) = next.executeUpdate(input) +} +``` + +### Input/Output Classes + +```kotlin +data class KWorkflowInput(val header: Header, val arguments: Array) +data class KWorkflowOutput(val result: Any?) + +// Dynamic handlers can use encodedValues to decode raw payloads +data class KSignalInput( + val signalName: String, + val arguments: Array, + val encodedValues: KEncodedValues, + val eventId: Long, + val header: Header +) +data class KQueryInput( + val queryName: String, + val arguments: Array, + val encodedValues: KEncodedValues, + val header: Header +) +data class KQueryOutput(val result: Any?) +data class KUpdateInput( + val updateName: String, + val arguments: Array, + val encodedValues: KEncodedValues, + val header: Header +) +data class KUpdateOutput(val result: Any?) +``` + +## KWorkflowOutboundCallsInterceptor + +Intercepts outbound calls from workflow code to Temporal APIs (activities, child workflows, timers, etc.). + +All async operations are `suspend` functions. For parallel execution, use standard `async { }` pattern. + +```kotlin +interface KWorkflowOutboundCallsInterceptor { + // Activities - suspend, use async {} for parallel execution + suspend fun executeActivity(input: KActivityInvocationInput): R + suspend fun executeLocalActivity(input: KLocalActivityInvocationInput): R + + // Child Workflows - returns handle with Deferred result + suspend fun startChildWorkflow(input: KChildWorkflowInvocationInput): KChildWorkflowHandle + + // Timers + suspend fun delay(duration: Duration) + + // Await Conditions + suspend fun awaitCondition(timeout: Duration, reason: String, condition: () -> Boolean): Boolean + suspend fun awaitCondition(reason: String, condition: () -> Boolean) + + // Side Effects + fun sideEffect(resultClass: Class, func: () -> R): R + fun mutableSideEffect(id: String, resultClass: Class, updated: (R?, R?) -> Boolean, func: () -> R): R + + // Versioning + fun getVersion(changeId: String, minSupported: Int, maxSupported: Int): Int + + // Continue As New + fun continueAsNew(input: KContinueAsNewInput): Nothing + + // External Workflow Communication + suspend fun signalExternalWorkflow(input: KSignalExternalInput) + suspend fun cancelWorkflow(input: KCancelWorkflowInput) + + // Search Attributes and Memo + fun upsertTypedSearchAttributes(vararg updates: SearchAttributeUpdate<*>) + fun upsertMemo(memo: Map) + + // Utilities + fun newRandom(): Random + fun randomUUID(): UUID + fun currentTimeMillis(): Long +} + +/** + * Base implementation that forwards all calls to the next interceptor. + */ +open class KWorkflowOutboundCallsInterceptorBase( + protected val next: KWorkflowOutboundCallsInterceptor +) : KWorkflowOutboundCallsInterceptor { + override suspend fun executeActivity(input: KActivityInvocationInput) = next.executeActivity(input) + override suspend fun executeLocalActivity(input: KLocalActivityInvocationInput) = next.executeLocalActivity(input) + override suspend fun startChildWorkflow(input: KChildWorkflowInvocationInput) = next.startChildWorkflow(input) + override suspend fun delay(duration: Duration) = next.delay(duration) + override suspend fun awaitCondition(timeout: Duration, reason: String, condition: () -> Boolean) = + next.awaitCondition(timeout, reason, condition) + override suspend fun awaitCondition(reason: String, condition: () -> Boolean) = + next.awaitCondition(reason, condition) + override fun sideEffect(resultClass: Class, func: () -> R) = next.sideEffect(resultClass, func) + override fun mutableSideEffect(id: String, resultClass: Class, updated: (R?, R?) -> Boolean, func: () -> R) = + next.mutableSideEffect(id, resultClass, updated, func) + override fun getVersion(changeId: String, minSupported: Int, maxSupported: Int) = + next.getVersion(changeId, minSupported, maxSupported) + override fun continueAsNew(input: KContinueAsNewInput) = next.continueAsNew(input) + override suspend fun signalExternalWorkflow(input: KSignalExternalInput) = next.signalExternalWorkflow(input) + override suspend fun cancelWorkflow(input: KCancelWorkflowInput) = next.cancelWorkflow(input) + override fun upsertTypedSearchAttributes(vararg updates: SearchAttributeUpdate<*>) = + next.upsertTypedSearchAttributes(*updates) + override fun upsertMemo(memo: Map) = next.upsertMemo(memo) + override fun newRandom() = next.newRandom() + override fun randomUUID() = next.randomUUID() + override fun currentTimeMillis() = next.currentTimeMillis() +} +``` + +### Outbound Input/Output Classes + +```kotlin +/** + * Input for activity invocation. + */ +data class KActivityInvocationInput( + val activityName: String, + val resultClass: Class, + val resultType: Type, + val arguments: Array, + val options: KActivityOptions, + val header: Header +) + +/** + * Input for local activity invocation. + */ +data class KLocalActivityInvocationInput( + val activityName: String, + val resultClass: Class, + val resultType: Type, + val arguments: Array, + val options: KLocalActivityOptions, + val header: Header +) + +/** + * Input for child workflow invocation. + */ +data class KChildWorkflowInvocationInput( + val workflowId: String, + val workflowType: String, + val resultClass: Class, + val resultType: Type, + val arguments: Array, + val options: KChildWorkflowOptions, + val header: Header +) + +/** + * Input for continue-as-new. + */ +data class KContinueAsNewInput( + val workflowType: String?, + val options: KContinueAsNewOptions?, + val arguments: Array, + val header: Header +) + +/** + * Input for signaling external workflow. + */ +data class KSignalExternalInput( + val execution: WorkflowExecution, + val signalName: String, + val arguments: Array, + val header: Header +) + +/** + * Input for canceling external workflow. + */ +data class KCancelWorkflowInput( + val execution: WorkflowExecution, + val reason: String? +) +``` + +## KActivityInboundCallsInterceptor + +Intercepts inbound calls to activity execution. + +```kotlin +interface KActivityInboundCallsInterceptor { + /** Called when activity is initialized. Provides access to the activity execution context. */ + fun init(context: ActivityExecutionContext) + + /** Called when activity method is invoked. This is a suspend function to support suspend activities. */ + suspend fun execute(input: KActivityExecutionInput): KActivityExecutionOutput +} + +open class KActivityInboundCallsInterceptorBase( + protected val next: KActivityInboundCallsInterceptor +) : KActivityInboundCallsInterceptor { + override fun init(context: ActivityExecutionContext) = next.init(context) + override suspend fun execute(input: KActivityExecutionInput) = next.execute(input) +} + +data class KActivityExecutionInput(val header: Header, val arguments: Array) +data class KActivityExecutionOutput(val result: Any?) +``` + +## Example: Logging Interceptor + +```kotlin +class LoggingInterceptor : KWorkerInterceptorBase() { + + override fun interceptWorkflow( + next: KWorkflowInboundCallsInterceptor + ): KWorkflowInboundCallsInterceptor { + return LoggingWorkflowInterceptor(next) + } + + override fun interceptActivity( + next: KActivityInboundCallsInterceptor + ): KActivityInboundCallsInterceptor { + return LoggingActivityInterceptor(next) + } +} + +private class LoggingWorkflowInterceptor( + next: KWorkflowInboundCallsInterceptor +) : KWorkflowInboundCallsInterceptorBase(next) { + + private val log = KWorkflow.logger() + + override suspend fun execute(input: KWorkflowInput): KWorkflowOutput { + log.info("Workflow started with ${input.arguments.size} arguments") + val startTime = KWorkflow.currentTimeMillis() + + return try { + val result = next.execute(input) + val duration = KWorkflow.currentTimeMillis() - startTime + log.info("Workflow completed in ${duration}ms") + result + } catch (e: Exception) { + val duration = KWorkflow.currentTimeMillis() - startTime + log.error("Workflow failed after ${duration}ms", e) + throw e + } + } + + override suspend fun handleSignal(input: KSignalInput) { + log.info("Signal received: ${input.signalName}") + next.handleSignal(input) + } + + override fun handleQuery(input: KQueryInput): KQueryOutput { + log.debug("Query received: ${input.queryName}") + return next.handleQuery(input) + } + + override suspend fun executeUpdate(input: KUpdateInput): KUpdateOutput { + log.info("Update received: ${input.updateName}") + return try { + val result = next.executeUpdate(input) + log.info("Update ${input.updateName} completed") + result + } catch (e: Exception) { + log.error("Update ${input.updateName} failed", e) + throw e + } + } +} + +private class LoggingActivityInterceptor( + next: KActivityInboundCallsInterceptor +) : KActivityInboundCallsInterceptorBase(next) { + + override suspend fun execute(input: KActivityExecutionInput): KActivityExecutionOutput { + val info = KActivity.info + val log = KActivity.logger() + + log.info("Activity ${info.activityType} started") + val startTime = System.currentTimeMillis() + + return try { + val result = next.execute(input) + val duration = System.currentTimeMillis() - startTime + log.info("Activity ${info.activityType} completed in ${duration}ms") + result + } catch (e: Exception) { + val duration = System.currentTimeMillis() - startTime + log.error("Activity ${info.activityType} failed after ${duration}ms", e) + throw e + } + } +} +``` + +## Related + +- [KOptions](./koptions.md) - Configuration options +- [Worker Setup](../worker/setup.md) - Registering interceptors + +--- + +**Next:** [Workflows](../workflows/README.md) diff --git a/kotlin/configuration/koptions.md b/kotlin/configuration/koptions.md new file mode 100644 index 0000000..54f7747 --- /dev/null +++ b/kotlin/configuration/koptions.md @@ -0,0 +1,296 @@ +# KOptions Classes + +For a fully idiomatic Kotlin experience, the SDK provides dedicated `KOptions` data classes that accept `kotlin.time.Duration` directly, use named parameters with default values, and follow immutable data class patterns. + +> **Note on Experimental Features:** Properties marked with `@Experimental` mirror experimental features in the Java SDK. These are subject to change or removal without notice. Use with caution in production code. + +## KActivityOptions + +```kotlin +/** + * Kotlin-native activity options with Duration support. + * All timeout properties accept kotlin.time.Duration directly. + * + * IMPORTANT: At least one of startToCloseTimeout or scheduleToCloseTimeout must be specified. + */ +data class KActivityOptions( + val startToCloseTimeout: Duration? = null, + val scheduleToCloseTimeout: Duration? = null, + val scheduleToStartTimeout: Duration? = null, + val heartbeatTimeout: Duration? = null, + val taskQueue: String? = null, + val retryOptions: KRetryOptions? = null, + val cancellationType: ActivityCancellationType? = null, // Java default: TRY_CANCEL + val disableEagerExecution: Boolean = false, + // Experimental + @Experimental val summary: String? = null, + @Experimental val priority: Priority? = null +) { + init { + require(startToCloseTimeout != null || scheduleToCloseTimeout != null) { + "At least one of startToCloseTimeout or scheduleToCloseTimeout must be specified" + } + } +} +``` + +**Usage:** + +```kotlin +val result = KWorkflow.executeActivity( + GreetingActivities::composeGreeting, + KActivityOptions( + startToCloseTimeout = 30.seconds, + scheduleToCloseTimeout = 5.minutes, + heartbeatTimeout = 10.seconds, + retryOptions = KRetryOptions( + initialInterval = 1.seconds, + maximumAttempts = 3 + ) + ), + "Hello", "World" +) +``` + +## KLocalActivityOptions + +```kotlin +/** + * Kotlin-native local activity options. + */ +data class KLocalActivityOptions( + val startToCloseTimeout: Duration? = null, + val scheduleToCloseTimeout: Duration? = null, + val localRetryThreshold: Duration? = null, + val retryOptions: KRetryOptions? = null, + // Experimental + @Experimental val summary: String? = null, + @Experimental val priority: Priority? = null +) +``` + +**Usage:** + +```kotlin +val validated = KWorkflow.executeLocalActivity( + ValidationActivities::validate, + KLocalActivityOptions( + startToCloseTimeout = 5.seconds, + localRetryThreshold = 10.seconds + ), + input +) +``` + +## KRetryOptions + +```kotlin +/** + * Kotlin-native retry options. + */ +data class KRetryOptions( + val initialInterval: Duration = 1.seconds, + val backoffCoefficient: Double = 2.0, + val maximumInterval: Duration? = null, + val maximumAttempts: Int = 0, // 0 = unlimited + val doNotRetry: List = emptyList() // Exception type names +) +``` + +> **Note:** `doNotRetry` contains fully-qualified exception class names (e.g., `"java.lang.IllegalArgumentException"`, `"com.example.BusinessException"`). Activities throwing these exceptions will not be retried. + +## KChildWorkflowOptions + +```kotlin +/** + * Kotlin-native child workflow options. + * All fields are optional - null values inherit from parent workflow or use Java SDK defaults. + */ +data class KChildWorkflowOptions( + val namespace: String? = null, + val workflowId: String? = null, + val workflowIdReusePolicy: WorkflowIdReusePolicy? = null, + val workflowRunTimeout: Duration? = null, + val workflowExecutionTimeout: Duration? = null, + val workflowTaskTimeout: Duration? = null, + val taskQueue: String? = null, + val retryOptions: KRetryOptions? = null, + val cronSchedule: String? = null, + val parentClosePolicy: ParentClosePolicy? = null, + val memo: Map? = null, + val typedSearchAttributes: SearchAttributes? = null, + val cancellationType: ChildWorkflowCancellationType? = null, + // Experimental + @Experimental val staticSummary: String? = null, + @Experimental val staticDetails: String? = null, + @Experimental val priority: Priority? = null +) +``` + +**Usage:** + +```kotlin +val childResult = KWorkflow.executeChildWorkflow( + ChildWorkflow::process, + KChildWorkflowOptions( + workflowId = "child-123", + workflowExecutionTimeout = 1.hours, + retryOptions = KRetryOptions(maximumAttempts = 3) + ), + data +) +``` + +## KWorkflowOptions + +```kotlin +/** + * Kotlin-native workflow options for client execution. + * workflowId and taskQueue are optional - if not provided, will use defaults or generate UUID. + */ +data class KWorkflowOptions( + val workflowId: String? = null, + val workflowIdReusePolicy: WorkflowIdReusePolicy? = null, + val workflowIdConflictPolicy: WorkflowIdConflictPolicy? = null, + val workflowRunTimeout: Duration? = null, + val workflowExecutionTimeout: Duration? = null, + val workflowTaskTimeout: Duration? = null, + val taskQueue: String? = null, + val retryOptions: KRetryOptions? = null, + val cronSchedule: String? = null, + val memo: Map? = null, + val typedSearchAttributes: SearchAttributes? = null, + val disableEagerExecution: Boolean = true, + val startDelay: Duration? = null, + val contextPropagators: List? = null, + // Experimental + @Experimental val staticSummary: String? = null, + @Experimental val staticDetails: String? = null, + @Experimental val priority: Priority? = null, + @Experimental val requestId: String? = null, + @Experimental val completionCallbacks: List? = null, + @Experimental val links: List? = null, + @Experimental val onConflictOptions: KOnConflictOptions? = null, + @Experimental val versioningOverride: VersioningOverride? = null +) +``` + +**Usage:** + +```kotlin +val result = client.executeWorkflow( + OrderWorkflow::processOrder, + KWorkflowOptions( + workflowId = "order-123", + taskQueue = "orders", + workflowExecutionTimeout = 24.hours, + workflowRunTimeout = 1.hours + ), + order +) +``` + +## KOnConflictOptions + +```kotlin +/** + * Options for handling workflow ID conflicts when using USE_EXISTING conflict policy. + * These options control what gets attached to an existing workflow when a start request + * conflicts with it. + * + * @Experimental This API is experimental and may change. + */ +@Experimental +data class KOnConflictOptions( + val attachRequestId: Boolean = false, + val attachCompletionCallbacks: Boolean = false, + val attachLinks: Boolean = false +) { + init { + if (attachCompletionCallbacks) { + require(attachRequestId) { + "attachRequestId must be true if attachCompletionCallbacks is true" + } + } + } + + fun toJavaOptions(): OnConflictOptions +} +``` + +**Usage:** + +```kotlin +val options = KWorkflowOptions( + workflowId = "my-workflow", + taskQueue = "my-queue", + workflowIdConflictPolicy = WorkflowIdConflictPolicy.WORKFLOW_ID_CONFLICT_POLICY_USE_EXISTING, + onConflictOptions = KOnConflictOptions( + attachRequestId = true, + attachCompletionCallbacks = true + ) +) +``` + +## KContinueAsNewOptions + +```kotlin +/** + * Options for continuing a workflow as a new execution. + * All fields are optional - null values inherit from the current workflow. + */ +data class KContinueAsNewOptions( + val workflowRunTimeout: Duration? = null, + val taskQueue: String? = null, + val retryOptions: KRetryOptions? = null, + val workflowTaskTimeout: Duration? = null, + val memo: Map? = null, + val typedSearchAttributes: SearchAttributes? = null, + val contextPropagators: List? = null +) +``` + +## Copying with Modifications + +Data classes support the `copy()` function for creating variants: + +```kotlin +val baseOptions = KActivityOptions( + startToCloseTimeout = 30.seconds, + retryOptions = KRetryOptions(maximumAttempts = 3) +) + +// Create variant with different timeout +val longRunningOptions = baseOptions.copy( + startToCloseTimeout = 5.minutes, + heartbeatTimeout = 30.seconds +) +``` + +## KOptions vs DSL Builders + +The Kotlin SDK provides two approaches for configuring options: + +1. **KOptions (Recommended)** - Native Kotlin data classes designed for the Kotlin SDK +2. **DSL Builders** - Extension functions on Java SDK builders, provided as a stopgap for using Kotlin with the Java SDK + +> **Important:** When using the Kotlin SDK (`KWorkflow`, `KWorkflowClient`, etc.), always use KOptions classes. The DSL builders (`ActivityOptions { }`, `WorkflowOptions { }`, etc.) exist only for compatibility when using Kotlin with the Java SDK directly and should not be used with the Kotlin SDK APIs. + +| Aspect | DSL Builder (Java SDK interop) | KOptions (Kotlin SDK) | +|--------|--------------------------------|------------------------| +| Duration | Requires `.toJava()` conversion | Native `kotlin.time.Duration` | +| Syntax | `setStartToCloseTimeout(...)` | `startToCloseTimeout = ...` | +| Defaults | Must check Java defaults | Visible in constructor | +| Immutability | Mutable builder | Immutable data class | +| Copy | Manual rebuild | `copy()` function | +| IDE | Limited autocomplete | Full parameter hints | +| Usage | Java SDK only | Kotlin SDK | + +## Related + +- [Data Conversion](./data-conversion.md) - Serialization configuration +- [Interceptors](./interceptors.md) - Cross-cutting concerns + +--- + +**Next:** [Data Conversion](./data-conversion.md) diff --git a/kotlin/kotlin-idioms.md b/kotlin/kotlin-idioms.md new file mode 100644 index 0000000..179440a --- /dev/null +++ b/kotlin/kotlin-idioms.md @@ -0,0 +1,197 @@ +# Kotlin Idioms + +The Kotlin SDK uses **standard Kotlin patterns** wherever possible instead of custom APIs. + +| Java SDK | Kotlin SDK | +|----------|------------| +| `void method()` | `suspend fun method()` | +| `Async.function(() -> ...)` | `async { ... }` | +| `Promise.allOf(...).get()` | `awaitAll(d1, d2)` | +| `Workflow.sleep(duration)` | `delay(duration)` | +| `Workflow.newDetachedCancellationScope()` | `withContext(NonCancellable)` | +| `Duration.ofSeconds(30)` | `30.seconds` | +| `Optional` | `T?` | + +## Suspend Functions + +Workflows and activities use `suspend fun` for natural coroutine integration: + +```kotlin +@WorkflowInterface +interface OrderWorkflow { + @WorkflowMethod + suspend fun processOrder(order: Order): OrderResult + + @SignalMethod + suspend fun updatePriority(priority: Priority) + + @UpdateMethod + suspend fun addItem(item: OrderItem): Boolean + + @QueryMethod // Queries are NOT suspend - must be synchronous + val status: OrderStatus +} + +@ActivityInterface +interface OrderActivities { + @ActivityMethod + suspend fun validateOrder(order: Order): Boolean + + @ActivityMethod + suspend fun chargePayment(order: Order): PaymentResult +} +``` + +> **Note:** Query methods are never `suspend` because queries must return immediately without blocking. + +## Coroutines and Concurrency + +Use standard `kotlinx.coroutines` patterns for parallel execution: + +```kotlin +override suspend fun processOrder(order: Order): OrderResult = coroutineScope { + // Parallel execution using standard async + val validation = async { KWorkflow.executeActivity(OrderActivities::validateOrder, options, order) } + val inventory = async { KWorkflow.executeActivity(OrderActivities::checkInventory, options, order) } + + // Wait for all - standard awaitAll + val (isValid, hasInventory) = awaitAll(validation, inventory) + + if (!isValid || !hasInventory) { + return@coroutineScope OrderResult(success = false) + } + + // Sequential execution + val charged = KWorkflow.executeActivity(OrderActivities::chargePayment, options, order) + val shipped = KWorkflow.executeActivity(OrderActivities::shipOrder, options, order) + + OrderResult(success = true, trackingNumber = shipped) +} +``` + +| Kotlin Pattern | Purpose | +|----------------|---------| +| `coroutineScope { }` | Structured concurrency - if one fails, all cancel | +| `async { }` | Start parallel operation, returns `Deferred` | +| `awaitAll(d1, d2)` | Wait for multiple deferreds | +| `delay(duration)` | Temporal timer (deterministic) | + +## Cancellation + +Use standard Kotlin cancellation patterns: + +```kotlin +override suspend fun processOrder(order: Order): OrderResult { + return try { + doProcessOrder(order) + } catch (e: CancellationException) { + // Workflow was cancelled - run cleanup in non-cancellable context + withContext(NonCancellable) { + KWorkflow.executeActivity(OrderActivities::releaseInventory, options, order) + KWorkflow.executeActivity(OrderActivities::refundPayment, options, order) + } + throw e // Re-throw to propagate cancellation + } +} +``` + +| Kotlin Pattern | Java SDK Equivalent | +|----------------|---------------------| +| `catch (e: CancellationException)` | `CancellationScope` failure callback | +| `withContext(NonCancellable) { }` | `Workflow.newDetachedCancellationScope()` | +| `coroutineScope { }` | `Workflow.newCancellationScope()` | +| `isActive` / `ensureActive()` | `CancellationScope.isCancelRequested()` | + +## Timeouts + +Use `withTimeout` for deadline-based cancellation: + +```kotlin +override suspend fun processWithDeadline(order: Order): OrderResult { + return withTimeout(1.hours) { + // Everything here cancels if it takes > 1 hour + KWorkflow.executeActivity(OrderActivities::validateOrder, options, order) + KWorkflow.executeActivity(OrderActivities::chargePayment, options, order) + OrderResult(success = true) + } +} + +// Or get null instead of exception +val result = withTimeoutOrNull(30.minutes) { + KWorkflow.executeActivity(OrderActivities::slowOperation, options, data) +} +``` + +## Kotlin Duration + +Use `kotlin.time.Duration` for readable time expressions: + +```kotlin +import kotlin.time.Duration.Companion.seconds +import kotlin.time.Duration.Companion.minutes +import kotlin.time.Duration.Companion.hours + +val options = KActivityOptions( + startToCloseTimeout = 30.seconds, + scheduleToCloseTimeout = 5.minutes, + heartbeatTimeout = 10.seconds +) + +delay(1.hours) +withTimeout(30.minutes) { ... } +``` + +## Null Safety + +Nullable types replace `Optional`: + +```kotlin +// KWorkflowInfo uses nullable instead of Optional +val info = KWorkflow.info +val parentId: String? = info.parentWorkflowId // null if no parent + +// Activity heartbeat details +val progress = KActivity.context.heartbeatDetails() +val startIndex = progress ?: 0 // Elvis operator for default +``` + +This eliminates `.orElse(null)`, `.isPresent`, and other Optional ceremony. + +## Data Classes + +Use Kotlin data classes with `@Serializable` for workflow inputs/outputs: + +```kotlin +@Serializable +data class Order( + val id: String, + val customerId: String, + val items: List, + val priority: Priority = Priority.NORMAL +) + +@Serializable +data class OrderResult( + val success: Boolean, + val trackingNumber: String? = null, + val errorMessage: String? = null +) + +@Serializable +enum class Priority { LOW, NORMAL, HIGH } +``` + +Data classes provide: +- Automatic `equals()`, `hashCode()`, `toString()` +- `copy()` for creating modified instances +- Destructuring: `val (id, customerId) = order` + +## Related + +- [KOptions Classes](./configuration/koptions.md) - Kotlin-native configuration +- [Workflow Definition](./workflows/definition.md) - Using these idioms in workflows +- [Activity Definition](./activities/definition.md) - Using these idioms in activities + +--- + +**Next:** [Configuration](./configuration/README.md) diff --git a/kotlin/migration.md b/kotlin/migration.md new file mode 100644 index 0000000..d3d1a84 --- /dev/null +++ b/kotlin/migration.md @@ -0,0 +1,129 @@ +# Migration from Java SDK + +## API Mapping + +| Java SDK | Kotlin SDK | +|----------|------------| +| **Activities** | | +| `Workflow.newUntypedActivityStub(opts)` | *(not needed - options passed per call)* | +| `activities.execute("name", Cls, arg)` | `KWorkflow.executeActivity("name", options, arg)` | +| `stub.method(arg)` (typed activity) | `KWorkflow.executeActivity(Interface::method, options, arg)` | +| `Async.function(stub::method, arg)` | `coroutineScope { async { KWorkflow.executeActivity(...) } }` | +| **Workflows** | | +| `Workflow.newChildWorkflowStub(...)` | `KWorkflow.executeChildWorkflow(Interface::method, options, ...)` | +| `Async.function(childStub::method, arg)` | `KWorkflow.startChildWorkflow(Interface::method, options, ...)` → `KChildWorkflowHandle` | +| `Workflow.getWorkflowExecution(childStub)` | `childHandle.workflowId` / `childHandle.firstExecutionRunId` | +| `client.newWorkflowStub(...)` | `client.startWorkflow(Interface::method, ...)` → `KTypedWorkflowHandle` | +| `client.newWorkflowStub(Cls, id)` | `client.getWorkflowHandle(id)` → `KWorkflowHandle` | +| `stub.signal(arg)` | `handle.signal(T::method, arg)` | +| `stub.query()` | `handle.query(T::method)` | +| `handle.getResult()` | `handle.result()` (type inferred) or `handle.result()` | +| **Primitives** | | +| `Workflow.sleep(duration)` | `delay(duration)` - standard kotlinx.coroutines | +| `Workflow.await(() -> cond)` | `KWorkflow.awaitCondition { cond }` | +| `Promise` | Standard `Deferred` via `Promise.toDeferred()` | +| `Optional` | `T?` | +| `Duration.ofSeconds(30)` | `30.seconds` | +| **Parallel Execution** | | +| `Async.function(...)` + `Promise.get()` | `coroutineScope { async { ... } }` + `awaitAll()` | +| **Options** | | +| `ActivityOptions.newBuilder()...build()` | `KActivityOptions(...)` | +| `LocalActivityOptions.newBuilder()...build()` | `KLocalActivityOptions(...)` | +| `ChildWorkflowOptions.newBuilder()...build()` | `KChildWorkflowOptions(...)` | +| `WorkflowOptions.newBuilder()...build()` | `KWorkflowOptions(...)` | +| `RetryOptions.newBuilder()...build()` | `KRetryOptions(...)` | + +## Before (Java) + +```java +@WorkflowInterface +public interface GreetingWorkflow { + @WorkflowMethod + String getGreeting(String name); +} + +public class GreetingWorkflowImpl implements GreetingWorkflow { + @Override + public String getGreeting(String name) { + ActivityStub activities = Workflow.newUntypedActivityStub( + ActivityOptions.newBuilder() + .setStartToCloseTimeout(Duration.ofSeconds(30)) + .build() + ); + return activities.execute("greet", String.class, name); + } +} +``` + +## After (Kotlin) + +```kotlin +@WorkflowInterface +interface GreetingWorkflow { + @WorkflowMethod + suspend fun getGreeting(name: String): String +} + +class GreetingWorkflowImpl : GreetingWorkflow { + override suspend fun getGreeting(name: String): String { + return KWorkflow.executeActivity( + "greet", + KActivityOptions(startToCloseTimeout = 30.seconds), + name + ) + } +} +``` + +## Interoperability + +### Kotlin Workflows Calling Java Activities + +```kotlin +override suspend fun processOrder(order: Order): String { + // JavaActivities is a Java @ActivityInterface - no stub needed + return KWorkflow.executeActivity( + JavaActivities::process, + KActivityOptions(startToCloseTimeout = 30.seconds), + order + ) +} +``` + +### Java Clients Calling Kotlin Workflows + +```kotlin +val result = client.executeWorkflow( + JavaWorkflowInterface::execute, + KWorkflowOptions(workflowId = "java-workflow", taskQueue = "java-queue"), + input +) +``` + +### Mixed Workers + +A single worker can host both Java and Kotlin workflows: + +```kotlin +// Java workflows (thread-based) +worker.registerWorkflowImplementationTypes( + OrderWorkflowJavaImpl::class.java +) + +// Kotlin workflows (coroutine-based) +worker.registerWorkflowImplementationTypes( + GreetingWorkflowImpl::class +) + +// Both run on the same worker - execution model is per-workflow-instance +factory.start() +``` + +## Related + +- [Kotlin Idioms](./kotlin-idioms.md) - Kotlin-specific patterns +- [Worker Setup](./worker/setup.md) - Mixed Java/Kotlin workers + +--- + +**Next:** [API Parity](./api-parity.md) diff --git a/kotlin/testing.md b/kotlin/testing.md new file mode 100644 index 0000000..7eb5f97 --- /dev/null +++ b/kotlin/testing.md @@ -0,0 +1,356 @@ +# Testing + +The Kotlin SDK provides testing utilities that integrate with `kotlinx.coroutines.test` for testing workflows and activities. + +## Test Environment + +Use `KTestWorkflowEnvironment` to create an in-memory Temporal environment for fast, deterministic tests: + +```kotlin +class OrderWorkflowTest { + private lateinit var testEnv: KTestWorkflowEnvironment + private lateinit var worker: KWorker + private lateinit var client: KWorkflowClient + + @BeforeEach + fun setup() { + testEnv = KTestWorkflowEnvironment.newInstance() + worker = testEnv.newWorker("test-queue") + client = testEnv.workflowClient + } + + @AfterEach + fun teardown() { + testEnv.close() + } + + @Test + fun `test order workflow completes successfully`() = runTest { + // Register workflow and mock activities + worker.registerWorkflowImplementationTypes(OrderWorkflowImpl::class) + worker.registerActivitiesImplementations(MockOrderActivities()) + testEnv.start() + + // Execute workflow + val result = client.executeWorkflow( + OrderWorkflow::processOrder, + KWorkflowOptions( + workflowId = "test-order-123", + taskQueue = "test-queue" + ), + testOrder + ) + + assertEquals(OrderStatus.COMPLETED, result.status) + } +} +``` + +## KTestWorkflowEnvironment API + +```kotlin +/** + * Kotlin test environment wrapping TestWorkflowEnvironment. + * Provides coroutine-friendly APIs for testing workflows. + */ +class KTestWorkflowEnvironment private constructor( + private val delegate: TestWorkflowEnvironment +) : AutoCloseable { + companion object { + fun newInstance(): KTestWorkflowEnvironment + fun newInstance(options: TestEnvironmentOptions): KTestWorkflowEnvironment + fun newInstance(options: TestEnvironmentOptions.Builder.() -> Unit): KTestWorkflowEnvironment + } + + /** The workflow client for starting and interacting with workflows */ + val workflowClient: KWorkflowClient + + /** Create a new worker for the given task queue */ + fun newWorker(taskQueue: String): KWorker + fun newWorker(taskQueue: String, options: WorkerOptions.Builder.() -> Unit): KWorker + + /** Start the test environment (workers begin processing) */ + fun start() + + /** Shutdown the test environment */ + override fun close() + + // Time control + /** Current test time */ + val currentTimeMillis: Long + + /** Skip time forward, triggering any timers that fire */ + suspend fun skipTime(duration: Duration) + + /** Sleep real time (for integration-style tests) */ + suspend fun sleep(duration: Duration) + + /** Register a callback for when workflow reaches a timer */ + fun registerDelayCallback(duration: Duration, callback: () -> Unit) + + // Advanced + /** Access the underlying TestWorkflowEnvironment */ + val testEnvironment: TestWorkflowEnvironment +} +``` + +## Time Skipping + +Test long-running workflows without waiting for real time to pass: + +```kotlin +@Test +fun `test workflow with timers`() = runTest { + worker.registerWorkflowImplementationTypes(ReminderWorkflowImpl::class) + testEnv.start() + + // Start workflow that has a 24-hour delay + val handle = client.startWorkflow( + ReminderWorkflow::scheduleReminder, + KWorkflowOptions( + workflowId = "reminder-123", + taskQueue = "test-queue" + ), + reminder + ) + + // Skip 24 hours instantly + testEnv.skipTime(24.hours) + + // Workflow should now be complete + val result = handle.result() + assertTrue(result.reminderSent) +} +``` + +## Mocking Activities + +### Simple Mock Implementation + +```kotlin +class MockOrderActivities : OrderActivities { + var chargePaymentCalled = false + var lastOrder: Order? = null + + override suspend fun chargePayment(order: Order): PaymentResult { + chargePaymentCalled = true + lastOrder = order + return PaymentResult(success = true, transactionId = "mock-tx-123") + } + + override suspend fun shipOrder(order: Order): ShipmentResult { + return ShipmentResult(trackingNumber = "MOCK-TRACK-123") + } +} + +@Test +fun `test payment is charged`() = runTest { + val mockActivities = MockOrderActivities() + worker.registerWorkflowImplementationTypes(OrderWorkflowImpl::class) + worker.registerActivitiesImplementations(mockActivities) + testEnv.start() + + client.executeWorkflow( + OrderWorkflow::processOrder, + options, + testOrder + ) + + assertTrue(mockActivities.chargePaymentCalled) + assertEquals(testOrder, mockActivities.lastOrder) +} +``` + +### Using Mocking Frameworks + +```kotlin +@Test +fun `test with mockk`() = runTest { + val mockActivities = mockk() + coEvery { mockActivities.chargePayment(any()) } returns PaymentResult(success = true, transactionId = "tx-123") + coEvery { mockActivities.shipOrder(any()) } returns ShipmentResult(trackingNumber = "TRACK-123") + + worker.registerWorkflowImplementationTypes(OrderWorkflowImpl::class) + worker.registerActivitiesImplementations(mockActivities) + testEnv.start() + + val result = client.executeWorkflow(OrderWorkflow::processOrder, options, testOrder) + + coVerify { mockActivities.chargePayment(testOrder) } + assertTrue(result.success) +} +``` + +## Testing Signals and Queries + +```kotlin +@Test +fun `test signal updates workflow state`() = runTest { + worker.registerWorkflowImplementationTypes(OrderWorkflowImpl::class) + worker.registerActivitiesImplementations(mockActivities) + testEnv.start() + + val handle = client.startWorkflow( + OrderWorkflow::processOrder, + options, + testOrder + ) + + // Query initial state + val initialStatus = handle.query(OrderWorkflow::status) + assertEquals(OrderStatus.PENDING, initialStatus) + + // Send signal + handle.signal(OrderWorkflow::updatePriority, Priority.HIGH) + + // Query updated state + val priority = handle.query(OrderWorkflow::priority) + assertEquals(Priority.HIGH, priority) +} +``` + +## Testing Updates + +```kotlin +@Test +fun `test update modifies order`() = runTest { + worker.registerWorkflowImplementationTypes(OrderWorkflowImpl::class) + worker.registerActivitiesImplementations(mockActivities) + testEnv.start() + + val handle = client.startWorkflow( + OrderWorkflow::processOrder, + options, + testOrder + ) + + // Execute update + val added = handle.executeUpdate(OrderWorkflow::addItem, newItem) + assertTrue(added) + + // Verify via query + val itemCount = handle.query(OrderWorkflow::getItemCount) + assertEquals(2, itemCount) // Original item + new item +} +``` + +## Testing Cancellation + +```kotlin +@Test +fun `test workflow handles cancellation gracefully`() = runTest { + worker.registerWorkflowImplementationTypes(OrderWorkflowImpl::class) + worker.registerActivitiesImplementations(mockActivities) + testEnv.start() + + val handle = client.startWorkflow( + OrderWorkflow::processOrder, + options, + testOrder + ) + + // Cancel the workflow + handle.cancel() + + // Verify cleanup activities were called + val description = handle.describe() + assertEquals(WorkflowExecutionStatus.WORKFLOW_EXECUTION_STATUS_CANCELED, description.status) +} +``` + +## Testing Child Workflows + +```kotlin +@Test +fun `test parent orchestrates child workflows`() = runTest { + worker.registerWorkflowImplementationTypes( + ParentWorkflowImpl::class, + ChildWorkflowImpl::class + ) + worker.registerActivitiesImplementations(mockActivities) + testEnv.start() + + val result = client.executeWorkflow( + ParentWorkflow::orchestrate, + options, + parentInput + ) + + assertEquals(3, result.childResults.size) +} +``` + +## Integration Testing + +For tests that need a real Temporal server: + +```kotlin +@TestInstance(TestInstance.Lifecycle.PER_CLASS) +class OrderWorkflowIntegrationTest { + private lateinit var service: WorkflowServiceStubs + private lateinit var client: KWorkflowClient + private lateinit var factory: KWorkerFactory + + @BeforeAll + fun setup() { + // Connect to local Temporal server + service = WorkflowServiceStubs.newLocalServiceStubs() + client = KWorkflowClient(service) + factory = KWorkerFactory(client) + + val worker = factory.newWorker("integration-test-queue") + worker.registerWorkflowImplementationTypes(OrderWorkflowImpl::class) + worker.registerActivitiesImplementations(RealOrderActivities()) + factory.start() + } + + @AfterAll + fun teardown() { + factory.shutdown() + service.shutdown() + } + + @Test + fun `integration test with real server`() = runBlocking { + val result = client.executeWorkflow( + OrderWorkflow::processOrder, + KWorkflowOptions( + workflowId = "integration-test-${UUID.randomUUID()}", + taskQueue = "integration-test-queue" + ), + testOrder + ) + + assertTrue(result.success) + } +} +``` + +## Best Practices + +1. **Use `runTest`** from `kotlinx.coroutines.test` for suspend function tests +2. **Prefer unit tests** with `KTestWorkflowEnvironment` for fast feedback +3. **Use time skipping** for workflows with delays - don't wait for real time +4. **Mock activities** to isolate workflow logic and control external dependencies +5. **Test edge cases**: cancellation, timeouts, failures, retries +6. **Use unique workflow IDs** in integration tests to avoid conflicts + +## Dependencies + +Add the testing dependency to your project: + +```kotlin +// build.gradle.kts +testImplementation("io.temporal:temporal-testing:$temporalVersion") +testImplementation("org.jetbrains.kotlinx:kotlinx-coroutines-test:$coroutinesVersion") +``` + +## Related + +- [Workflow Definition](./workflows/definition.md) - Defining testable workflows +- [Activities](./activities/README.md) - Defining testable activities +- [Worker Setup](./worker/setup.md) - Production worker configuration + +--- + +**Next:** [Migration Guide](./migration.md) diff --git a/kotlin/worker/README.md b/kotlin/worker/README.md new file mode 100644 index 0000000..4672fe9 --- /dev/null +++ b/kotlin/worker/README.md @@ -0,0 +1,51 @@ +# Worker API + +This section covers setting up workers to execute Temporal workflows and activities in Kotlin. + +## Overview + +The Kotlin SDK provides `KWorkerFactory` and `KWorker` which automatically enable coroutine support for Kotlin workflows and suspend activities. + +## Documents + +| Document | Description | +|----------|-------------| +| [Setup](./setup.md) | KWorkerFactory, KWorker, KotlinPlugin, registration | + +## Quick Reference + +### Basic Worker Setup + +```kotlin +val service = WorkflowServiceStubs.newLocalServiceStubs() +val client = KWorkflowClient(service) + +// KWorkerFactory automatically enables Kotlin coroutine support +val factory = KWorkerFactory(client) +val worker = factory.newWorker("task-queue") + +// Register workflows and activities +worker.registerWorkflowImplementationTypes(MyWorkflowImpl::class) +worker.registerActivitiesImplementations(MyActivitiesImpl()) + +// Start the worker +factory.start() +``` + +### Key Components + +| Component | Purpose | +|-----------|---------| +| `KWorkerFactory` | Creates workers with automatic coroutine support | +| `KWorker` | Registers Kotlin workflows and suspend activities | +| `KotlinPlugin` | Plugin for Java main apps (used automatically by KWorkerFactory) | + +## Related + +- [Interceptors](../configuration/interceptors.md) - Cross-cutting concerns +- [Workflows](../workflows/README.md) - Defining workflows to register +- [Activities](../activities/README.md) - Defining activities to register + +--- + +**Next:** [Worker Setup](./setup.md) diff --git a/kotlin/worker/setup.md b/kotlin/worker/setup.md new file mode 100644 index 0000000..29afafb --- /dev/null +++ b/kotlin/worker/setup.md @@ -0,0 +1,143 @@ +# Worker Setup + +## KWorkerFactory (Recommended) + +For pure Kotlin applications, use `KWorkerFactory` which automatically enables coroutine support: + +```kotlin +val service = WorkflowServiceStubs.newLocalServiceStubs() +val client = KWorkflowClient(service) { ... } + +// KWorkerFactory automatically enables Kotlin coroutine support +val factory = KWorkerFactory(client) + +val worker: KWorker = factory.newWorker("task-queue") { + maxConcurrentActivityExecutionSize = 100 +} + +// Register Kotlin coroutine workflows +worker.registerWorkflowImplementationTypes( + GreetingWorkflowImpl::class, + OrderWorkflowImpl::class +) + +// Register activities - suspend functions handled automatically +worker.registerActivitiesImplementations( + GreetingActivitiesImpl(), // Kotlin suspend activities + JavaActivitiesImpl() // Java activities work too +) + +// Start the worker +factory.start() +``` + +## KWorkerFactory API + +```kotlin +/** + * Kotlin worker factory that automatically enables coroutine support. + * Wraps WorkerFactory with KotlinPlugin pre-configured. + */ +class KWorkerFactory( + client: KWorkflowClient, + options: WorkerFactoryOptions.Builder.() -> Unit = {} +) { + /** The underlying WorkerFactory for advanced use cases */ + val workerFactory: WorkerFactory + + fun newWorker(taskQueue: String, options: WorkerOptions.Builder.() -> Unit = {}): KWorker + fun start() + fun shutdown() + fun shutdownNow() + suspend fun awaitTermination(timeout: Duration) +} +``` + +## KWorker API + +```kotlin +/** + * Kotlin worker that provides idiomatic APIs for registering + * Kotlin workflows and suspend activities. + * + * Use KWorker for pure Kotlin implementations. For mixed Java/Kotlin + * scenarios, access the underlying Worker via the [worker] property. + */ +class KWorker { + /** The underlying Java Worker for interop scenarios */ + val worker: Worker + + /** Register Kotlin workflow implementation types using reified generics */ + inline fun registerWorkflowImplementationTypes() + + /** Register Kotlin workflow implementation types using KClass */ + fun registerWorkflowImplementationTypes(vararg workflowClasses: KClass<*>) + + /** Register Kotlin workflow implementation types with options */ + fun registerWorkflowImplementationTypes( + options: WorkflowImplementationOptions, + vararg workflowClasses: KClass<*> + ) + + /** Register activity implementations (automatically detects suspend functions) */ + fun registerActivitiesImplementations(vararg activities: Any) + + /** Register suspend activity implementations explicitly */ + fun registerSuspendActivities(vararg activities: Any) + + /** Register Nexus service implementations */ + fun registerNexusServiceImplementations(vararg services: Any) +} +``` + +**When to use KWorker vs Worker:** +- Use `KWorker` for pure Kotlin implementations (recommended) +- Use `Worker` (via `kworker.worker`) when mixing Java and Kotlin workflows/activities on the same worker + +## KotlinPlugin (For Java Main) + +When your main application is written in Java and you need to register Kotlin workflows, use `KotlinPlugin` explicitly: + +```kotlin +// Java main or mixed Java/Kotlin setup +val service = WorkflowServiceStubs.newLocalServiceStubs() +val client = WorkflowClient.newInstance(service) + +val factory = WorkerFactory.newInstance(client, WorkerFactoryOptions.newBuilder() + .addPlugin(KotlinPlugin()) + .build()) + +val worker = factory.newWorker("task-queue") + +// Register Kotlin workflows - plugin handles suspend functions +worker.registerWorkflowImplementationTypes(KotlinWorkflowImpl::class.java) +``` + +## Mixed Java and Kotlin + +A single worker supports both Java and Kotlin workflows on the same task queue: + +```kotlin +// Java workflows (thread-based) +worker.registerWorkflowImplementationTypes( + OrderWorkflowJavaImpl::class.java +) + +// Kotlin workflows (coroutine-based) - same method, plugin handles execution +worker.registerWorkflowImplementationTypes( + GreetingWorkflowImpl::class +) + +// Both run on the same worker - execution model is per-workflow-instance +factory.start() +``` + +## Related + +- [Interceptors](../configuration/interceptors.md) - Adding cross-cutting concerns +- [Workflows](../workflows/README.md) - Defining workflows to register +- [Activities](../activities/README.md) - Defining activities to register + +--- + +**Next:** [Testing](../testing.md) diff --git a/kotlin/workflows/README.md b/kotlin/workflows/README.md new file mode 100644 index 0000000..68b32a8 --- /dev/null +++ b/kotlin/workflows/README.md @@ -0,0 +1,61 @@ +# Workflows + +This section covers defining and implementing Temporal workflows in Kotlin. + +## Overview + +Kotlin workflows use coroutines and suspend functions for an idiomatic async experience while maintaining Temporal's determinism guarantees. + +## Documents + +| Document | Description | +|----------|-------------| +| [Definition](./definition.md) | Workflow interfaces, suspend methods, Java interop patterns | +| [Signals, Queries & Updates](./signals-queries.md) | Communication with running workflows | +| [Child Workflows](./child-workflows.md) | Orchestrating child workflow execution | +| [External Workflows](./external-workflows.md) | Signal or cancel workflows in other executions | +| [Timers & Parallel Execution](./timers-parallel.md) | Delays, async patterns, await conditions | +| [Cancellation](./cancellation.md) | Handling cancellation, cleanup with NonCancellable | +| [Continue-As-New](./continue-as-new.md) | Long-running workflow patterns | + +## Quick Reference + +### Basic Workflow + +```kotlin +@WorkflowInterface +interface GreetingWorkflow { + @WorkflowMethod + suspend fun getGreeting(name: String): String +} + +class GreetingWorkflowImpl : GreetingWorkflow { + override suspend fun getGreeting(name: String): String { + return KWorkflow.executeActivity( + GreetingActivities::composeGreeting, + KActivityOptions(startToCloseTimeout = 10.seconds), + "Hello", name + ) + } +} +``` + +### Key Patterns + +| Pattern | Kotlin SDK | +|---------|------------| +| Execute activity | `KWorkflow.executeActivity(Interface::method, options, args)` | +| Execute child workflow | `KWorkflow.executeChildWorkflow(Interface::method, options, args)` | +| Timer/delay | `delay(duration)` - standard kotlinx.coroutines | +| Wait for condition | `KWorkflow.awaitCondition { condition }` | +| Parallel execution | `coroutineScope { async { ... } }.awaitAll()` | +| Cancellation cleanup | `withContext(NonCancellable) { ... }` | + +## Related + +- [Activities](../activities/README.md) - What workflows orchestrate +- [Client](../client/README.md) - Starting and interacting with workflows + +--- + +**Next:** [Workflow Definition](./definition.md) diff --git a/kotlin/workflows/cancellation.md b/kotlin/workflows/cancellation.md new file mode 100644 index 0000000..3e5aa95 --- /dev/null +++ b/kotlin/workflows/cancellation.md @@ -0,0 +1,151 @@ +# Cancellation + +Kotlin's built-in coroutine cancellation replaces Java SDK's `CancellationScope`. This provides a more idiomatic experience while maintaining full Temporal semantics. + +## How Cancellation Works + +When a workflow is cancelled (from the server or programmatically), the Kotlin SDK translates this into coroutine cancellation. Cancellation is **cooperative**—it's checked at suspension points (`delay`, activity execution, child workflow execution, etc.): + +```kotlin +override suspend fun processOrder(order: Order): OrderResult = coroutineScope { + // Cancellation is checked at each suspension point + val validated = KWorkflow.executeActivity(...) // ← cancellation checked here + delay(5.minutes) // ← cancellation checked here + val result = KWorkflow.executeActivity(...) // ← cancellation checked here + result +} +``` + +## Explicit Cancellation Checks + +In rare cases where workflow code doesn't have suspension points (e.g., tight loops), you can check cancellation explicitly using `isActive` or `ensureActive()`. However, CPU-bound work should typically be delegated to activities. + +## Parallel Execution and Cancellation + +`coroutineScope` provides structured concurrency—if one child fails or the scope is cancelled, all children are automatically cancelled: + +```kotlin +override suspend fun parallelWorkflow(): String = coroutineScope { + val a = async { KWorkflow.executeActivity(...) } + val b = async { KWorkflow.executeActivity(...) } + + // If either activity fails, the other is cancelled + // If workflow is cancelled, both activities are cancelled + "${a.await()} - ${b.await()}" +} +``` + +## Detached Scopes (Cleanup Logic) + +Use `withContext(NonCancellable)` for cleanup code that must run even when the workflow is cancelled: + +```kotlin +override suspend fun processOrder(order: Order): OrderResult { + try { + return doProcessOrder(order) + } catch (e: CancellationException) { + // Cleanup runs even though workflow is cancelled + withContext(NonCancellable) { + KWorkflow.executeActivity( + OrderActivities::releaseReservation, + KActivityOptions(startToCloseTimeout = 30.seconds), + order + ) + } + throw e // Re-throw to propagate cancellation + } +} +``` + +This is equivalent to Java's `Workflow.newDetachedCancellationScope()`. + +## Cancellation with Timeout + +Use `withTimeout` to cancel a block after a duration: + +```kotlin +override suspend fun processWithDeadline(order: Order): OrderResult { + return withTimeout(1.hours) { + // Everything in this block is cancelled if it takes > 1 hour + val validated = KWorkflow.executeActivity(...) + val charged = KWorkflow.executeActivity(...) + OrderResult(success = true) + } +} + +// Or use withTimeoutOrNull to get null instead of exception +override suspend fun tryProcess(order: Order): OrderResult? { + return withTimeoutOrNull(30.minutes) { + KWorkflow.executeActivity(...) + } +} +``` + +## Complete Cleanup Example + +```kotlin +class OrderWorkflowImpl : OrderWorkflow { + private var paymentCharged = false + private var reservedItems = mutableListOf() + + override suspend fun processOrder(order: Order): OrderResult { + return try { + doProcessOrder(order) + } catch (e: CancellationException) { + // Workflow was cancelled - run cleanup in detached scope + withContext(NonCancellable) { + cleanup(order) + } + throw e // Re-throw to complete workflow as cancelled + } + } + + private suspend fun cleanup(order: Order) { + // Release any reserved inventory + for (item in reservedItems) { + try { + KWorkflow.executeActivity( + OrderActivities::releaseInventory, + KActivityOptions(startToCloseTimeout = 30.seconds), + item + ) + } catch (e: Exception) { + // Log but continue cleanup + } + } + + // Refund payment if it was charged + if (paymentCharged) { + try { + KWorkflow.executeActivity( + OrderActivities::refundPayment, + KActivityOptions(startToCloseTimeout = 30.seconds), + order + ) + } catch (e: Exception) { + // Log but continue cleanup + } + } + } +} +``` + +## Comparison with Java SDK + +| Java SDK | Kotlin SDK | +|----------|------------| +| `Workflow.newCancellationScope(() -> { ... })` | `coroutineScope { ... }` | +| `Workflow.newDetachedCancellationScope(() -> { ... })` | `withContext(NonCancellable) { ... }` | +| `CancellationScope.cancel()` | `job.cancel()` | +| `CancellationScope.isCancelRequested()` | `!isActive` | +| `CancellationScope.throwCanceled()` | `ensureActive()` | +| `scope.run()` with timeout | `withTimeout(duration) { ... }` | + +## Related + +- [Timers & Parallel](./timers-parallel.md) - Timeout patterns +- [Child Workflows](./child-workflows.md) - Cancellation propagation to children + +--- + +**Next:** [Continue-As-New](./continue-as-new.md) diff --git a/kotlin/workflows/child-workflows.md b/kotlin/workflows/child-workflows.md new file mode 100644 index 0000000..2b07d7a --- /dev/null +++ b/kotlin/workflows/child-workflows.md @@ -0,0 +1,220 @@ +# Child Workflows + +Child workflows are invoked using direct method references - no stub creation needed. + +## Execute and Wait + +```kotlin +// Simple case - execute child workflow and wait for result +override suspend fun parentWorkflow(): String { + return KWorkflow.executeChildWorkflow( + ChildWorkflow::doWork, + KChildWorkflowOptions(workflowId = "child-workflow-id"), + "input" + ) +} + +// With retry options +override suspend fun parentWorkflowWithRetry(): String { + return KWorkflow.executeChildWorkflow( + ChildWorkflow::doWork, + KChildWorkflowOptions( + workflowId = "child-workflow-id", + workflowExecutionTimeout = 1.hours, + retryOptions = KRetryOptions(maximumAttempts = 3) + ), + "input" + ) +} +``` + +## Parallel Execution + +Use standard `coroutineScope { async {} }` for parallel child workflows: + +```kotlin +override suspend fun parentWorkflowParallel(): String = coroutineScope { + // Start child and activity in parallel using standard Kotlin async + val childDeferred = async { + KWorkflow.executeChildWorkflow( + ChildWorkflow::doWork, + KChildWorkflowOptions(workflowId = "child-workflow-id"), + "input" + ) + } + val activityDeferred = async { + KWorkflow.executeActivity( + SomeActivities::doSomething, + KActivityOptions(startToCloseTimeout = 30.seconds) + ) + } + + // Wait for both using standard awaitAll + val (childResult, activityResult) = awaitAll(childDeferred, activityDeferred) + "$childResult - $activityResult" +} +``` + +## Child Workflow Handles + +For cases where you need to interact with a child workflow (signal, query, cancel) rather than just wait for its result, use `startChildWorkflow` to get a handle: + +```kotlin +// Start child workflow and get handle for interaction +override suspend fun parentWorkflowWithHandle(): String { + val handle = KWorkflow.startChildWorkflow( + ChildWorkflow::doWork, + KChildWorkflowOptions(workflowId = "child-workflow-id"), + "input" + ) + + // Can signal the child workflow + handle.signal(ChildWorkflow::updateProgress, 50) + + // Wait for result when ready + return handle.result() +} + +// Parallel child workflows with handles for interaction +override suspend fun parallelChildrenWithHandles(): List = coroutineScope { + val handles = listOf("child-1", "child-2", "child-3").map { id -> + KWorkflow.startChildWorkflow( + ChildWorkflow::doWork, + KChildWorkflowOptions(workflowId = id), + "input" + ) + } + + // Can interact with any child while they're running + handles.forEach { handle -> + handle.signal(ChildWorkflow::updatePriority, Priority.HIGH) + } + + // Wait for all results + handles.map { async { it.result() } }.awaitAll() +} +``` + +## KWorkflow Child Workflow Methods + +```kotlin +object KWorkflow { + /** + * Execute a child workflow and wait for its result. + * For fire-and-wait cases where you don't need to interact with the child. + */ + suspend fun executeChildWorkflow( + workflow: KFunction2, + options: KChildWorkflowOptions, + arg: A1 + ): R + + suspend fun executeChildWorkflow( + workflow: KFunction1, + options: KChildWorkflowOptions + ): R + + // ... up to 6 arguments + + /** + * Start a child workflow and return a handle for interaction. + * Use this when you need to signal, query, or cancel the child workflow. + * For simple fire-and-wait cases, prefer executeChildWorkflow() instead. + */ + suspend fun startChildWorkflow( + workflow: KFunction2, + options: KChildWorkflowOptions, + arg: A1 + ): KChildWorkflowHandle + + suspend fun startChildWorkflow( + workflow: KFunction1, + options: KChildWorkflowOptions + ): KChildWorkflowHandle + + suspend fun startChildWorkflow( + workflow: KFunction3, + options: KChildWorkflowOptions, + arg1: A1, + arg2: A2 + ): KChildWorkflowHandle + + // ... up to 6 arguments +} +``` + +## KChildWorkflowHandle API + +```kotlin +/** + * Handle for interacting with a started child workflow. + * Returned by startChildWorkflow() with result type captured from method reference. + * + * @param T The child workflow interface type + * @param R The result type of the child workflow method + */ +interface KChildWorkflowHandle { + /** The child workflow's workflow ID (available immediately) */ + val workflowId: String + + /** + * Get the child workflow's run ID. + * Suspends until the child workflow starts. + */ + suspend fun runId(): String + + /** + * Wait for the child workflow to complete and return its result. + * Suspends until the child workflow finishes. + */ + suspend fun result(): R + + // Signals - type-safe method references + suspend fun signal(method: KFunction1) + suspend fun signal(method: KFunction2, arg: A1) + suspend fun signal(method: KFunction3, arg1: A1, arg2: A2) + + /** + * Request cancellation of the child workflow. + * The child workflow will receive a CancellationException at its next suspension point. + */ + suspend fun cancel() +} +``` + +## KChildWorkflowOptions + +```kotlin +// All fields are optional - null values inherit from parent workflow or use Java SDK defaults +KChildWorkflowOptions( + workflowId = "child-workflow-id", + taskQueue = "child-queue", // Optional: defaults to parent's task queue + workflowExecutionTimeout = 1.hours, + workflowRunTimeout = 30.minutes, + retryOptions = KRetryOptions( + initialInterval = 1.seconds, + maximumAttempts = 3 + ), + parentClosePolicy = ParentClosePolicy.PARENT_CLOSE_POLICY_TERMINATE, // Optional + cancellationType = ChildWorkflowCancellationType.WAIT_CANCELLATION_COMPLETED // Optional +) + +// Minimal options - just use defaults +val result = KWorkflow.executeChildWorkflow( + ChildWorkflow::processData, + KChildWorkflowOptions(), + inputData +) +``` + +> **Note:** For simple parallel execution where you only need the result, use standard `coroutineScope { async { executeChildWorkflow(...) } }`. Use `startChildWorkflow` only when you need to interact with the child workflow while it's running. + +## Related + +- [External Workflows](./external-workflows.md) - Signal/cancel workflows in other executions +- [Cancellation](./cancellation.md) - How cancellation propagates to child workflows +- [Workflow Definition](./definition.md) - Basic workflow patterns + +--- + +**Next:** [External Workflows](./external-workflows.md) diff --git a/kotlin/workflows/continue-as-new.md b/kotlin/workflows/continue-as-new.md new file mode 100644 index 0000000..557c731 --- /dev/null +++ b/kotlin/workflows/continue-as-new.md @@ -0,0 +1,150 @@ +# Continue-As-New + +Continue-as-new completes the current workflow execution and immediately starts a new execution with fresh event history. This is essential for: + +- **Preventing history growth**: Long-running workflows accumulate event history which can impact performance. Continue-as-new resets the history. +- **Batch processing**: Process a batch of items, then continue-as-new with the next batch offset. +- **Implementing loops**: Instead of infinite loops that accumulate history, use continue-as-new. + +## Basic Usage + +```kotlin +// Basic continue-as-new - same workflow type, inherit all options +KWorkflow.continueAsNew(nextBatchId, newOffset) + +// With modified options +KWorkflow.continueAsNew( + KContinueAsNewOptions( + taskQueue = "high-priority-queue", + workflowRunTimeout = 2.hours + ), + nextBatchId, newOffset +) + +// Continue as different workflow type (for versioning/migration) +KWorkflow.continueAsNew( + "OrderProcessorV2", + KContinueAsNewOptions(taskQueue = "orders-v2"), + migratedState +) + +// Type-safe continue as different workflow using method reference +KWorkflow.continueAsNew( + OrderProcessorV2::process, + KContinueAsNewOptions(), + migratedState +) +``` + +## KContinueAsNewOptions + +```kotlin +/** + * Options for continuing a workflow as a new execution. + * All fields are optional - null values inherit from the current workflow. + */ +data class KContinueAsNewOptions( + val workflowRunTimeout: Duration? = null, + val taskQueue: String? = null, + val retryOptions: KRetryOptions? = null, + val workflowTaskTimeout: Duration? = null, + val memo: Map? = null, + val typedSearchAttributes: SearchAttributes? = null +) +``` + +## Batch Processing Pattern + +```kotlin +class BatchProcessorImpl : BatchProcessor { + override suspend fun processBatches(startOffset: Int) { + val batchSize = 100 + val items = KWorkflow.executeActivity( + DataActivities::fetchBatch, + KActivityOptions(startToCloseTimeout = 1.minutes), + startOffset, batchSize + ) + + if (items.isEmpty()) { + return // All done, workflow completes normally + } + + // Process items... + for (item in items) { + KWorkflow.executeActivity( + DataActivities::processItem, + KActivityOptions(startToCloseTimeout = 30.seconds), + item + ) + } + + // Continue with the next batch + KWorkflow.continueAsNew(startOffset + batchSize) + } +} +``` + +## History Size Check Pattern + +```kotlin +override suspend fun execute(state: WorkflowState) { + while (true) { + // Check if history is getting too large + if (KWorkflow.info.isContinueAsNewSuggested) { + KWorkflow.continueAsNew(state) + } + + // Wait for signals and process... + KWorkflow.awaitCondition { hasNewWork } + processWork() + } +} +``` + +## KWorkflow.continueAsNew API + +```kotlin +object KWorkflow { + /** + * Continue workflow as new with the same workflow type. + * This function never returns - it terminates the current execution. + */ + fun continueAsNew(vararg args: Any?): Nothing + + /** + * Continue workflow as new with modified options. + * Null option values inherit from the current workflow. + */ + fun continueAsNew(options: KContinueAsNewOptions, vararg args: Any?): Nothing + + /** + * Continue as a different workflow type. + * Useful for workflow versioning or migration. + */ + fun continueAsNew( + workflowType: String, + options: KContinueAsNewOptions, + vararg args: Any? + ): Nothing + + /** + * Type-safe continue as different workflow using method reference. + */ + fun continueAsNew( + workflow: KFunction<*>, + options: KContinueAsNewOptions, + vararg args: Any? + ): Nothing +} +``` + +> **Important:** `continueAsNew` never returns normally. It terminates the current workflow execution and signals Temporal to start a new execution. The return type `Nothing` indicates this in Kotlin's type system. + +## Related + +- [Workflow Definition](./definition.md) - Basic workflow patterns +- [Migration Guide](../migration.md) - Java SDK comparison + +--- + +**Next:** [Activities](../activities/README.md) diff --git a/kotlin/workflows/definition.md b/kotlin/workflows/definition.md new file mode 100644 index 0000000..195af7d --- /dev/null +++ b/kotlin/workflows/definition.md @@ -0,0 +1,131 @@ +# Workflow Definition + +Define workflow interfaces with `suspend` methods for full Kotlin coroutine support: + +```kotlin +@WorkflowInterface +interface GreetingWorkflow { + @WorkflowMethod + suspend fun getGreeting(name: String): String +} + +class GreetingWorkflowImpl : GreetingWorkflow { + override suspend fun getGreeting(name: String): String { + return KWorkflow.executeActivity( + GreetingActivities::composeGreeting, + KActivityOptions(startToCloseTimeout = 10.seconds), + "Hello", name + ) + } +} + +// Client call using KWorkflowClient - same pattern as activities, no stub needed +val result = client.executeWorkflow( + GreetingWorkflow::getGreeting, + KWorkflowOptions( + workflowId = "greeting-123", + taskQueue = "greetings" + ), + "Temporal" +) +``` + +## Java Interoperability + +### Kotlin Calling Java Workflows + +Kotlin clients can call Java workflows using typed method references: + +```kotlin +// Java workflow interface (defined in Java) +// @WorkflowInterface +// public interface OrderWorkflow { +// @WorkflowMethod +// OrderResult processOrder(Order order); +// } + +// Kotlin client calling Java workflow - works seamlessly +val result: OrderResult = client.executeWorkflow( + OrderWorkflow::processOrder, + KWorkflowOptions( + workflowId = "order-123", + taskQueue = "orders" + ), + order +) +``` + +### Java Calling Kotlin Workflows + +Java clients can invoke Kotlin suspend workflows using **untyped workflow stubs**. The workflow type name defaults to the interface name. + +```java +// Java client calling a Kotlin suspend workflow +WorkflowStub stub = client.newUntypedWorkflowStub( + "GreetingWorkflow", // Workflow type = interface name + WorkflowOptions.newBuilder() + .setTaskQueue("greetings") + .setWorkflowId("greeting-123") + .build() +); + +stub.start("Temporal"); +String result = stub.getResult(String.class); +``` + +Alternatively, Java clients can define their own Java interface with the **same name** as the Kotlin interface to use typed stubs: + +```java +// Java interface matching the Kotlin workflow type +@WorkflowInterface +public interface GreetingWorkflow { + @WorkflowMethod + String getGreeting(String name); +} + +// Java client using typed stub +GreetingWorkflow workflow = client.newWorkflowStub( + GreetingWorkflow.class, + WorkflowOptions.newBuilder() + .setTaskQueue("greetings") + .setWorkflowId("greeting-123") + .build() +); +String result = workflow.getGreeting("Temporal"); +``` + +> **Note:** Java cannot directly use Kotlin suspend interfaces because suspend functions compile to methods with an extra `Continuation` parameter. The untyped stub approach is recommended for Java clients. + +## Key Characteristics + +* Use `coroutineScope`, `async`, `launch` for concurrent execution +* Use `delay()` for timers (maps to Temporal timers, not `Thread.sleep`) +* Reuses `@WorkflowInterface` and `@WorkflowMethod` annotations from Java SDK +* Data classes work naturally for parameters and results + +## Logging + +Use `KWorkflow.logger()` for workflow-safe logging: + +```kotlin +// Get logger using workflow type as name +val log = KWorkflow.logger() +log.info("Processing order") + +// Or with custom logger name +val customLog = KWorkflow.logger("my.custom.logger") + +// Or with class +val classLog = KWorkflow.logger(MyWorkflowImpl::class.java) +``` + +> **Note:** Versioning (`KWorkflow.getVersion`), side effects (`KWorkflow.sideEffect`), and search attributes (`KWorkflow.upsertSearchAttributes`) use the same patterns as the Java SDK. + +## Related + +- [Child Workflows](./child-workflows.md) - Orchestrating child workflows +- [Timers & Parallel Execution](./timers-parallel.md) - Delays and async patterns + +--- + +**Next:** [Signals, Queries & Updates](./signals-queries.md) diff --git a/kotlin/workflows/external-workflows.md b/kotlin/workflows/external-workflows.md new file mode 100644 index 0000000..6923d0b --- /dev/null +++ b/kotlin/workflows/external-workflows.md @@ -0,0 +1,115 @@ +# External Workflows + +External workflows allow you to signal or cancel workflows running in separate executions. Use `KWorkflow.getExternalWorkflowHandle()` to get a handle for interaction. + +## Typed Handle + +```kotlin +// Get typed handle for external workflow +val handle = KWorkflow.getExternalWorkflowHandle("order-123") + +// Signal using method reference (type-safe) +handle.signal(OrderWorkflow::updatePriority, Priority.HIGH) + +// Cancel the external workflow +handle.cancel() +``` + +## With Run ID + +```kotlin +// Target a specific execution +val handle = KWorkflow.getExternalWorkflowHandle( + workflowId = "order-123", + runId = "abc-run-456" +) + +handle.signal(OrderWorkflow::cancelOrder, "Duplicate order") +``` + +## Untyped Handle + +When the workflow type is unknown at compile time: + +```kotlin +// Get untyped handle +val handle = KWorkflow.getExternalWorkflowHandle("order-123") + +// Signal by name +handle.signal("updatePriority", Priority.HIGH) + +// Cancel +handle.cancel() +``` + +## KExternalWorkflowHandle API + +```kotlin +/** + * Handle for interacting with an external workflow (workflow in a different execution). + * Obtained via KWorkflow.getExternalWorkflowHandle(). + * + * @param T The external workflow interface type (for type-safe signals) + */ +class KExternalWorkflowHandle( + val workflowId: String, + val runId: String? +) { + // Signals - type-safe method references + suspend fun signal(method: KFunction1) + suspend fun signal(method: KFunction2, arg: A1) + suspend fun signal(method: KFunction3, arg1: A1, arg2: A2) + // ... up to 6 arguments + + /** Request cancellation of the external workflow. */ + suspend fun cancel() +} +``` + +## ExternalWorkflowHandle (Untyped) + +```kotlin +/** + * Untyped handle for external workflows. + * Use when workflow type is unknown at compile time. + */ +class ExternalWorkflowHandle( + val workflowId: String, + val runId: String? +) { + suspend fun signal(signalName: String, vararg args: Any?) + suspend fun cancel() +} +``` + +## KWorkflow API + +```kotlin +object KWorkflow { + /** Get typed handle for external workflow */ + inline fun getExternalWorkflowHandle(workflowId: String): KExternalWorkflowHandle + inline fun getExternalWorkflowHandle(workflowId: String, runId: String): KExternalWorkflowHandle + + /** Get untyped handle for external workflow */ + fun getExternalWorkflowHandle(workflowId: String): ExternalWorkflowHandle + fun getExternalWorkflowHandle(workflowId: String, runId: String): ExternalWorkflowHandle +} +``` + +## Limitations + +| Operation | Supported | +|-----------|-----------| +| Signal | ✓ | +| Cancel | ✓ | +| Query | ✗ (queries are synchronous, only available via client) | +| Get Result | ✗ (cannot await external workflow results from within a workflow) | + +## Related + +- [Child Workflows](./child-workflows.md) - Workflows started by the current workflow +- [Signals, Queries & Updates](./signals-queries.md) - Signal handler definitions + +--- + +**Next:** [Timers & Parallel Execution](./timers-parallel.md) diff --git a/kotlin/workflows/signals-queries.md b/kotlin/workflows/signals-queries.md new file mode 100644 index 0000000..68c9c1d --- /dev/null +++ b/kotlin/workflows/signals-queries.md @@ -0,0 +1,211 @@ +# Signals, Queries, and Updates + +Signals and updates follow the same suspend/non-suspend pattern as workflow methods. Queries are always synchronous (never suspend) and can be defined as properties. + +## Defining Handlers + +```kotlin +@WorkflowInterface +interface OrderWorkflow { + @WorkflowMethod + suspend fun processOrder(order: Order): OrderResult + + @SignalMethod + suspend fun cancelOrder(reason: String) + + @UpdateMethod + suspend fun addItem(item: OrderItem): Boolean + + @UpdateValidatorMethod(updateMethod = "addItem") + fun validateAddItem(item: OrderItem) + + // Queries - always synchronous, can use property syntax + @QueryMethod + val status: OrderStatus + + @QueryMethod + fun getItemCount(): Int +} +``` + +## Dynamic Handler Registration + +For workflows that need to handle signals, queries, or updates dynamically (without annotations), use the registration APIs. + +### KEncodedValues + +All dynamic handlers receive arguments as `KEncodedValues`, a Kotlin-idiomatic wrapper around the Java SDK's `EncodedValues`: + +```kotlin +class KEncodedValues(private val delegate: EncodedValues) { + val size: Int + fun isEmpty(): Boolean + + // Primary API - reified generics + inline fun get(index: Int = 0): T + inline fun get(index: Int, genericType: Type): T + + // KClass-based access + fun get(index: Int, type: KClass): T + + // Destructuring support + inline operator fun component1(): T + inline operator fun component2(): T + inline operator fun component3(): T + + // Java interop + fun toEncodedValues(): EncodedValues +} +``` + +### Dynamic Handler Examples + +```kotlin +class DynamicWorkflowImpl : DynamicWorkflow { + private var state = mutableMapOf() + + override suspend fun execute(input: String): String { + // Register a named update handler with validator + KWorkflow.registerUpdateHandler( + "updateState", + validator = { args: KEncodedValues -> + val key: String = args.get(0) + require(key.isNotBlank()) { "Key cannot be blank" } + }, + handler = { args: KEncodedValues -> + val key: String = args.get(0) + val value: Any = args.get(1) + state[key] = value + "Updated $key" + } + ) + + // Register a named signal handler + KWorkflow.registerSignalHandler("notify") { args: KEncodedValues -> + val message: String = args.get() // index defaults to 0 + println("Received: $message") + } + + // Register a named query handler + KWorkflow.registerQueryHandler("getState") { args: KEncodedValues -> + val key: String = args.get() + state[key] + } + + // Register dynamic handlers for unknown names (catch-all) + KWorkflow.registerDynamicUpdateHandler { updateName, args -> + "Handled unknown update: $updateName" + } + + KWorkflow.registerDynamicSignalHandler { signalName, args -> + println("Unknown signal: $signalName") + } + + // Dynamic query handler - 2 parameters (name and args) + KWorkflow.registerDynamicQueryHandler { queryName, args -> + "Unknown query: $queryName" + } + + // Validate all unknown updates + KWorkflow.registerDynamicUpdateValidator { updateName, args -> + require(updateName.startsWith("custom_")) { "Unknown update: $updateName" } + } + + // Wait for completion signal + KWorkflow.awaitCondition { state["done"] == true } + return "Completed" + } +} +``` + +### Using Destructuring + +```kotlin +KWorkflow.registerDynamicUpdateHandler { updateName, args -> + // Destructuring for multiple arguments + val (name: String, value: Int) = args + update(name, value) + "Updated" +} +``` + +### Dynamic Handler API Reference + +```kotlin +// Named handlers with KEncodedValues +fun registerSignalHandler(signalName: String, handler: suspend (KEncodedValues) -> Unit) +fun registerQueryHandler(queryName: String, handler: (KEncodedValues) -> Any?) +fun registerUpdateHandler(updateName: String, handler: suspend (KEncodedValues) -> Any?) +fun registerUpdateHandler( + updateName: String, + validator: (KEncodedValues) -> Unit, + handler: suspend (KEncodedValues) -> Any? +) + +// Catch-all dynamic handlers +fun registerDynamicSignalHandler(handler: suspend (signalName: String, args: KEncodedValues) -> Unit) +fun registerDynamicQueryHandler(handler: (queryName: String, args: KEncodedValues) -> Any?) +fun registerDynamicUpdateHandler(handler: suspend (updateName: String, args: KEncodedValues) -> Any?) +fun registerDynamicUpdateValidator(validator: (updateName: String, args: KEncodedValues) -> Unit) +``` + +## Client-Side Interaction + +### Sending Signals + +```kotlin +val handle = client.getWorkflowHandle("order-123") + +// Type-safe signal using method reference +handle.signal(OrderWorkflow::cancelOrder, "Customer request") +``` + +### Querying Workflows + +```kotlin +val handle = client.getWorkflowHandle("order-123") + +// Query using property reference +val status = handle.query(OrderWorkflow::status) + +// Query using method reference +val count = handle.query(OrderWorkflow::getItemCount) +``` + +### Executing Updates + +```kotlin +val handle = client.getWorkflowHandle("order-123") + +// Execute update and wait for result +val added = handle.executeUpdate(OrderWorkflow::addItem, newItem) +``` + +## Update Validation + +Update validators run synchronously before the update handler. They can reject updates by throwing exceptions: + +```kotlin +@UpdateValidatorMethod(updateMethod = "addItem") +fun validateAddItem(item: OrderItem) { + require(item.quantity > 0) { "Quantity must be positive" } + require(item.price >= BigDecimal.ZERO) { "Price cannot be negative" } + require(_status == OrderStatus.PENDING) { "Cannot add items after processing started" } +} + +@UpdateMethod +suspend fun addItem(item: OrderItem): Boolean { + // Validator already passed - safe to proceed + _items.add(item) + return true +} +``` + +## Related + +- [Client API](../client/workflow-handle.md) - More on workflow handles +- [Workflow Definition](./definition.md) - Basic workflow patterns + +--- + +**Next:** [Child Workflows](./child-workflows.md) diff --git a/kotlin/workflows/timers-parallel.md b/kotlin/workflows/timers-parallel.md new file mode 100644 index 0000000..b81812d --- /dev/null +++ b/kotlin/workflows/timers-parallel.md @@ -0,0 +1,125 @@ +# Timers and Parallel Execution + +## Timers and Delays + +```kotlin +override suspend fun workflowWithTimer(): String { + // Simple delay using Kotlin Duration + delay(5.minutes) + + return "completed" +} +``` + +The standard `kotlinx.coroutines.delay()` is intercepted by Temporal's deterministic dispatcher and creates durable timers. + +## Parallel Execution + +Use standard `coroutineScope { async { } }` for parallel execution: + +```kotlin +val options = KActivityOptions(startToCloseTimeout = 30.seconds) + +override suspend fun parallelWorkflow(items: List): List = coroutineScope { + // Process all items in parallel using standard Kotlin patterns + items.map { item -> + async { + KWorkflow.executeActivity(ProcessingActivities::process, options, item) + } + }.awaitAll() // Standard kotlinx.coroutines.awaitAll +} + +// Another example: parallel activities with different results +override suspend fun getGreetings(name: String): String = coroutineScope { + val hello = async { KWorkflow.executeActivity(GreetingActivities::greet, options, "Hello", name) } + val goodbye = async { KWorkflow.executeActivity(GreetingActivities::greet, options, "Goodbye", name) } + + // Standard awaitAll works with any Deferred + val (helloResult, goodbyeResult) = awaitAll(hello, goodbye) + "$helloResult\n$goodbyeResult" +} +``` + +**Why this works deterministically:** +- All coroutines inherit the workflow's deterministic dispatcher +- The dispatcher executes tasks in a FIFO queue ensuring consistent ordering +- Same execution order during replay + +> **Note:** `coroutineScope` provides structured concurrency—if one `async` fails, all others are automatically cancelled. This maps naturally to Temporal's cancellation semantics. + +## Await Condition + +Wait for a condition to become true (equivalent to Java's `Workflow.await()`): + +```kotlin +override suspend fun workflowWithCondition(): String { + var approved = false + + // Signal handler sets approved = true + // ... + + // Wait until approved (blocks workflow until condition is true) + KWorkflow.awaitCondition { approved } + + return "Approved" +} + +// With timeout - returns false if timed out +override suspend fun workflowWithTimeout(): String { + var approved = false + + val wasApproved = KWorkflow.awaitCondition(timeout = 24.hours) { approved } + + return if (wasApproved) "Approved" else "Timed out" +} +``` + +## Timeout Patterns + +Use `withTimeout` to cancel a block after a duration: + +```kotlin +override suspend fun processWithDeadline(order: Order): OrderResult { + return withTimeout(1.hours) { + // Everything in this block is cancelled if it takes > 1 hour + val validated = KWorkflow.executeActivity(...) + val charged = KWorkflow.executeActivity(...) + OrderResult(success = true) + } +} + +// Or use withTimeoutOrNull to get null instead of exception +override suspend fun tryProcess(order: Order): OrderResult? { + return withTimeoutOrNull(30.minutes) { + KWorkflow.executeActivity(...) + } +} +``` + +## Racing Patterns + +Race multiple operations and take the first result: + +```kotlin +override suspend fun raceOperations(): String = coroutineScope { + // Start multiple operations + val fast = async { KWorkflow.executeActivity(Activities::fastOperation, options) } + val slow = async { KWorkflow.executeActivity(Activities::slowOperation, options) } + + // Use select to get first result + select { + fast.onAwait { "Fast: $it" } + slow.onAwait { "Slow: $it" } + } + // Note: The other coroutine is still running but will be cancelled when scope exits +} +``` + +## Related + +- [Child Workflows](./child-workflows.md) - Parallel child workflow patterns +- [Workflow Definition](./definition.md) - Basic workflow patterns + +--- + +**Next:** [Cancellation](./cancellation.md) From 96036911eeaa09b900a2bab496e0787bf5cdf26e Mon Sep 17 00:00:00 2001 From: Maxim Fateev <1463622+mfateev@users.noreply.github.com> Date: Sun, 11 Jan 2026 19:07:07 -0800 Subject: [PATCH 62/83] Expand migration guide with comprehensive API mapping --- kotlin/migration.md | 79 ++++++++++++++++++++++++++++++++++++--------- 1 file changed, 63 insertions(+), 16 deletions(-) diff --git a/kotlin/migration.md b/kotlin/migration.md index d3d1a84..ec8ffb0 100644 --- a/kotlin/migration.md +++ b/kotlin/migration.md @@ -4,34 +4,81 @@ | Java SDK | Kotlin SDK | |----------|------------| -| **Activities** | | -| `Workflow.newUntypedActivityStub(opts)` | *(not needed - options passed per call)* | -| `activities.execute("name", Cls, arg)` | `KWorkflow.executeActivity("name", options, arg)` | -| `stub.method(arg)` (typed activity) | `KWorkflow.executeActivity(Interface::method, options, arg)` | -| `Async.function(stub::method, arg)` | `coroutineScope { async { KWorkflow.executeActivity(...) } }` | -| **Workflows** | | -| `Workflow.newChildWorkflowStub(...)` | `KWorkflow.executeChildWorkflow(Interface::method, options, ...)` | -| `Async.function(childStub::method, arg)` | `KWorkflow.startChildWorkflow(Interface::method, options, ...)` → `KChildWorkflowHandle` | -| `Workflow.getWorkflowExecution(childStub)` | `childHandle.workflowId` / `childHandle.firstExecutionRunId` | -| `client.newWorkflowStub(...)` | `client.startWorkflow(Interface::method, ...)` → `KTypedWorkflowHandle` | -| `client.newWorkflowStub(Cls, id)` | `client.getWorkflowHandle(id)` → `KWorkflowHandle` | +| **Client** | | +| `WorkflowClient.newInstance(service)` | `KWorkflowClient(service)` | +| `client.newWorkflowStub(Cls, opts)` | `client.startWorkflow(Interface::method, options, ...)` | +| `client.newWorkflowStub(Cls, id)` | `client.getWorkflowHandle(id)` | +| `stub.method(arg)` | `client.executeWorkflow(Interface::method, options, arg)` | | `stub.signal(arg)` | `handle.signal(T::method, arg)` | | `stub.query()` | `handle.query(T::method)` | -| `handle.getResult()` | `handle.result()` (type inferred) or `handle.result()` | -| **Primitives** | | +| `handle.getResult()` | `handle.result()` or `handle.result()` | +| **Worker** | | +| `WorkerFactory.newInstance(client)` | `KWorkerFactory(client)` | +| `factory.newWorker(taskQueue)` | `factory.newWorker(taskQueue)` → `KWorker` | +| `worker.registerWorkflowImplementationTypes(Cls)` | `worker.registerWorkflowImplementationTypes(Cls::class)` | +| `worker.registerActivitiesImplementations(impl)` | `worker.registerActivitiesImplementations(impl)` | +| **KWorkflow Object** | | +| `Workflow.getInfo()` | `KWorkflow.info` | +| `Workflow.getLogger()` | `KWorkflow.logger()` | | `Workflow.sleep(duration)` | `delay(duration)` - standard kotlinx.coroutines | | `Workflow.await(() -> cond)` | `KWorkflow.awaitCondition { cond }` | -| `Promise` | Standard `Deferred` via `Promise.toDeferred()` | +| `Workflow.sideEffect(cls, func)` | `KWorkflow.sideEffect { func }` | +| `Workflow.getVersion(id, min, max)` | `KWorkflow.version(id, min, max)` | +| `Workflow.continueAsNew(args)` | `KWorkflow.continueAsNew(args)` | +| `Workflow.randomUUID()` | `KWorkflow.randomUUID()` | +| `Workflow.newRandom()` | `KWorkflow.newRandom()` | +| `Workflow.currentTimeMillis()` | `KWorkflow.currentTimeMillis()` | +| `Workflow.getTypedSearchAttributes()` | `KWorkflow.typedSearchAttributes` | +| `Workflow.upsertTypedSearchAttributes(...)` | `KWorkflow.upsertTypedSearchAttributes(...)` | +| `Workflow.getMemo(key, cls)` | `KWorkflow.memo(key)` | +| `Workflow.upsertMemo(map)` | `KWorkflow.upsertMemo(map)` | +| `Workflow.getMetricsScope()` | `KWorkflow.metricsScope` | +| `Workflow.isReplaying()` | `KWorkflow.isReplaying` | +| `Workflow.getCurrentUpdateInfo()` | `KWorkflow.currentUpdateInfo` | +| **Activities (from workflow)** | | +| `Workflow.newActivityStub(Cls, opts)` | *(not needed - options passed per call)* | +| `stub.method(arg)` | `KWorkflow.executeActivity(Interface::method, options, arg)` | +| `Workflow.newLocalActivityStub(Cls, opts)` | *(not needed - options passed per call)* | +| `localStub.method(arg)` | `KWorkflow.executeLocalActivity(Interface::method, options, arg)` | +| `Async.function(stub::method, arg)` | `coroutineScope { async { KWorkflow.executeActivity(...) } }` | +| **Child Workflows** | | +| `Workflow.newChildWorkflowStub(Cls, opts)` | *(not needed - options passed per call)* | +| `childStub.method(arg)` | `KWorkflow.executeChildWorkflow(Interface::method, options, arg)` | +| `Async.function(childStub::method, arg)` | `KWorkflow.startChildWorkflow(...)` → `KChildWorkflowHandle` | +| `Workflow.getWorkflowExecution(childStub)` | `childHandle.workflowId` / `childHandle.runId()` | +| **External Workflows** | | +| `Workflow.newExternalWorkflowStub(Cls, id)` | `KWorkflow.getExternalWorkflowHandle(id)` | +| `externalStub.signal(arg)` | `externalHandle.signal(T::method, arg)` | +| `Workflow.newUntypedExternalWorkflowStub(id)` | `KWorkflow.getExternalWorkflowHandle(id)` | +| **Cancellation** | | +| `Workflow.newCancellationScope(...)` | `coroutineScope { ... }` | +| `Workflow.newDetachedCancellationScope(...)` | `withContext(NonCancellable) { ... }` | +| `scope.cancel()` | `job.cancel()` | +| `CancellationScope.isCancelRequested()` | `!isActive` | +| **KActivity Object** | | +| `Activity.getExecutionContext()` | `KActivity.context` | +| `context.getInfo()` | `KActivity.context.info` or `KActivity.info` | +| `context.heartbeat(details)` | `KActivity.context.heartbeat(details)` | +| `context.getHeartbeatDetails(cls)` | `KActivity.context.heartbeatDetails()` | +| `Activity.getLogger()` | `KActivity.logger()` | +| `context.doNotCompleteOnReturn()` | `KActivity.context.doNotCompleteOnReturn()` | +| **Testing** | | +| `TestWorkflowEnvironment.newInstance()` | `KTestWorkflowEnvironment.newInstance()` | +| `testEnv.newWorker(taskQueue)` | `testEnv.newWorker(taskQueue)` → `KWorker` | +| `testEnv.getWorkflowClient()` | `testEnv.workflowClient` → `KWorkflowClient` | +| `testEnv.sleep(duration)` | `testEnv.sleep(duration)` | +| **Primitives** | | +| `Promise` | `Deferred` via `async { }` | +| `Async.function(...)` + `Promise.get()` | `coroutineScope { async { ... } }` + `awaitAll()` | | `Optional` | `T?` | | `Duration.ofSeconds(30)` | `30.seconds` | -| **Parallel Execution** | | -| `Async.function(...)` + `Promise.get()` | `coroutineScope { async { ... } }` + `awaitAll()` | | **Options** | | | `ActivityOptions.newBuilder()...build()` | `KActivityOptions(...)` | | `LocalActivityOptions.newBuilder()...build()` | `KLocalActivityOptions(...)` | | `ChildWorkflowOptions.newBuilder()...build()` | `KChildWorkflowOptions(...)` | | `WorkflowOptions.newBuilder()...build()` | `KWorkflowOptions(...)` | | `RetryOptions.newBuilder()...build()` | `KRetryOptions(...)` | +| `ContinueAsNewOptions.newBuilder()...build()` | `KContinueAsNewOptions(...)` | ## Before (Java) From dfb633e5a74ae33769a8022b0162a1644215441c Mon Sep 17 00:00:00 2001 From: Maxim Fateev <1463622+mfateev@users.noreply.github.com> Date: Sun, 11 Jan 2026 19:07:07 -0800 Subject: [PATCH 63/83] Expand migration guide with comprehensive API mapping --- kotlin/migration.md | 79 ++++++++++++++++++++++++++++++++++++--------- 1 file changed, 63 insertions(+), 16 deletions(-) diff --git a/kotlin/migration.md b/kotlin/migration.md index d3d1a84..ec8ffb0 100644 --- a/kotlin/migration.md +++ b/kotlin/migration.md @@ -4,34 +4,81 @@ | Java SDK | Kotlin SDK | |----------|------------| -| **Activities** | | -| `Workflow.newUntypedActivityStub(opts)` | *(not needed - options passed per call)* | -| `activities.execute("name", Cls, arg)` | `KWorkflow.executeActivity("name", options, arg)` | -| `stub.method(arg)` (typed activity) | `KWorkflow.executeActivity(Interface::method, options, arg)` | -| `Async.function(stub::method, arg)` | `coroutineScope { async { KWorkflow.executeActivity(...) } }` | -| **Workflows** | | -| `Workflow.newChildWorkflowStub(...)` | `KWorkflow.executeChildWorkflow(Interface::method, options, ...)` | -| `Async.function(childStub::method, arg)` | `KWorkflow.startChildWorkflow(Interface::method, options, ...)` → `KChildWorkflowHandle` | -| `Workflow.getWorkflowExecution(childStub)` | `childHandle.workflowId` / `childHandle.firstExecutionRunId` | -| `client.newWorkflowStub(...)` | `client.startWorkflow(Interface::method, ...)` → `KTypedWorkflowHandle` | -| `client.newWorkflowStub(Cls, id)` | `client.getWorkflowHandle(id)` → `KWorkflowHandle` | +| **Client** | | +| `WorkflowClient.newInstance(service)` | `KWorkflowClient(service)` | +| `client.newWorkflowStub(Cls, opts)` | `client.startWorkflow(Interface::method, options, ...)` | +| `client.newWorkflowStub(Cls, id)` | `client.getWorkflowHandle(id)` | +| `stub.method(arg)` | `client.executeWorkflow(Interface::method, options, arg)` | | `stub.signal(arg)` | `handle.signal(T::method, arg)` | | `stub.query()` | `handle.query(T::method)` | -| `handle.getResult()` | `handle.result()` (type inferred) or `handle.result()` | -| **Primitives** | | +| `handle.getResult()` | `handle.result()` or `handle.result()` | +| **Worker** | | +| `WorkerFactory.newInstance(client)` | `KWorkerFactory(client)` | +| `factory.newWorker(taskQueue)` | `factory.newWorker(taskQueue)` → `KWorker` | +| `worker.registerWorkflowImplementationTypes(Cls)` | `worker.registerWorkflowImplementationTypes(Cls::class)` | +| `worker.registerActivitiesImplementations(impl)` | `worker.registerActivitiesImplementations(impl)` | +| **KWorkflow Object** | | +| `Workflow.getInfo()` | `KWorkflow.info` | +| `Workflow.getLogger()` | `KWorkflow.logger()` | | `Workflow.sleep(duration)` | `delay(duration)` - standard kotlinx.coroutines | | `Workflow.await(() -> cond)` | `KWorkflow.awaitCondition { cond }` | -| `Promise` | Standard `Deferred` via `Promise.toDeferred()` | +| `Workflow.sideEffect(cls, func)` | `KWorkflow.sideEffect { func }` | +| `Workflow.getVersion(id, min, max)` | `KWorkflow.version(id, min, max)` | +| `Workflow.continueAsNew(args)` | `KWorkflow.continueAsNew(args)` | +| `Workflow.randomUUID()` | `KWorkflow.randomUUID()` | +| `Workflow.newRandom()` | `KWorkflow.newRandom()` | +| `Workflow.currentTimeMillis()` | `KWorkflow.currentTimeMillis()` | +| `Workflow.getTypedSearchAttributes()` | `KWorkflow.typedSearchAttributes` | +| `Workflow.upsertTypedSearchAttributes(...)` | `KWorkflow.upsertTypedSearchAttributes(...)` | +| `Workflow.getMemo(key, cls)` | `KWorkflow.memo(key)` | +| `Workflow.upsertMemo(map)` | `KWorkflow.upsertMemo(map)` | +| `Workflow.getMetricsScope()` | `KWorkflow.metricsScope` | +| `Workflow.isReplaying()` | `KWorkflow.isReplaying` | +| `Workflow.getCurrentUpdateInfo()` | `KWorkflow.currentUpdateInfo` | +| **Activities (from workflow)** | | +| `Workflow.newActivityStub(Cls, opts)` | *(not needed - options passed per call)* | +| `stub.method(arg)` | `KWorkflow.executeActivity(Interface::method, options, arg)` | +| `Workflow.newLocalActivityStub(Cls, opts)` | *(not needed - options passed per call)* | +| `localStub.method(arg)` | `KWorkflow.executeLocalActivity(Interface::method, options, arg)` | +| `Async.function(stub::method, arg)` | `coroutineScope { async { KWorkflow.executeActivity(...) } }` | +| **Child Workflows** | | +| `Workflow.newChildWorkflowStub(Cls, opts)` | *(not needed - options passed per call)* | +| `childStub.method(arg)` | `KWorkflow.executeChildWorkflow(Interface::method, options, arg)` | +| `Async.function(childStub::method, arg)` | `KWorkflow.startChildWorkflow(...)` → `KChildWorkflowHandle` | +| `Workflow.getWorkflowExecution(childStub)` | `childHandle.workflowId` / `childHandle.runId()` | +| **External Workflows** | | +| `Workflow.newExternalWorkflowStub(Cls, id)` | `KWorkflow.getExternalWorkflowHandle(id)` | +| `externalStub.signal(arg)` | `externalHandle.signal(T::method, arg)` | +| `Workflow.newUntypedExternalWorkflowStub(id)` | `KWorkflow.getExternalWorkflowHandle(id)` | +| **Cancellation** | | +| `Workflow.newCancellationScope(...)` | `coroutineScope { ... }` | +| `Workflow.newDetachedCancellationScope(...)` | `withContext(NonCancellable) { ... }` | +| `scope.cancel()` | `job.cancel()` | +| `CancellationScope.isCancelRequested()` | `!isActive` | +| **KActivity Object** | | +| `Activity.getExecutionContext()` | `KActivity.context` | +| `context.getInfo()` | `KActivity.context.info` or `KActivity.info` | +| `context.heartbeat(details)` | `KActivity.context.heartbeat(details)` | +| `context.getHeartbeatDetails(cls)` | `KActivity.context.heartbeatDetails()` | +| `Activity.getLogger()` | `KActivity.logger()` | +| `context.doNotCompleteOnReturn()` | `KActivity.context.doNotCompleteOnReturn()` | +| **Testing** | | +| `TestWorkflowEnvironment.newInstance()` | `KTestWorkflowEnvironment.newInstance()` | +| `testEnv.newWorker(taskQueue)` | `testEnv.newWorker(taskQueue)` → `KWorker` | +| `testEnv.getWorkflowClient()` | `testEnv.workflowClient` → `KWorkflowClient` | +| `testEnv.sleep(duration)` | `testEnv.sleep(duration)` | +| **Primitives** | | +| `Promise` | `Deferred` via `async { }` | +| `Async.function(...)` + `Promise.get()` | `coroutineScope { async { ... } }` + `awaitAll()` | | `Optional` | `T?` | | `Duration.ofSeconds(30)` | `30.seconds` | -| **Parallel Execution** | | -| `Async.function(...)` + `Promise.get()` | `coroutineScope { async { ... } }` + `awaitAll()` | | **Options** | | | `ActivityOptions.newBuilder()...build()` | `KActivityOptions(...)` | | `LocalActivityOptions.newBuilder()...build()` | `KLocalActivityOptions(...)` | | `ChildWorkflowOptions.newBuilder()...build()` | `KChildWorkflowOptions(...)` | | `WorkflowOptions.newBuilder()...build()` | `KWorkflowOptions(...)` | | `RetryOptions.newBuilder()...build()` | `KRetryOptions(...)` | +| `ContinueAsNewOptions.newBuilder()...build()` | `KContinueAsNewOptions(...)` | ## Before (Java) From 8273a67a0a0380fbc793102c1475eb09665b1dbb Mon Sep 17 00:00:00 2001 From: Maxim Fateev <1463622+mfateev@users.noreply.github.com> Date: Mon, 12 Jan 2026 15:44:20 -0600 Subject: [PATCH 64/83] Add interfaceless workflow/activity proposal as open question Add "Decision Needed" sections to activity and workflow definition docs proposing Python SDK-style interfaceless definitions where activities and workflows can be defined directly on implementation classes without requiring separate interfaces. --- kotlin/activities/definition.md | 48 +++++++++++++++++++++++++++ kotlin/workflows/definition.md | 57 +++++++++++++++++++++++++++++++++ 2 files changed, 105 insertions(+) diff --git a/kotlin/activities/definition.md b/kotlin/activities/definition.md index 36b036f..76a7def 100644 --- a/kotlin/activities/definition.md +++ b/kotlin/activities/definition.md @@ -168,4 +168,52 @@ object KWorkflow { --- +## Open Questions (Decision Needed) + +### Interfaceless Activity Definition + +**Status:** Decision needed + +Currently, activities require interface definitions: + +```kotlin +// Current approach - requires interface +@ActivityInterface +interface GreetingActivities { + suspend fun composeGreeting(greeting: String, name: String): String +} + +class GreetingActivitiesImpl : GreetingActivities { + override suspend fun composeGreeting(greeting: String, name: String) = "$greeting, $name!" +} +``` + +**Proposal:** Allow defining activities directly on implementation classes without interfaces, similar to Python SDK: + +```kotlin +// Proposed approach - no interface required +class GreetingActivities { + @ActivityMethod + suspend fun composeGreeting(greeting: String, name: String) = "$greeting, $name!" +} + +// In workflow - call using method reference to impl class +val result = KWorkflow.executeActivity( + GreetingActivities::composeGreeting, + KActivityOptions(startToCloseTimeout = 30.seconds), + "Hello", "World" +) +``` + +**Benefits:** +- Reduces boilerplate (no separate interface file) +- More similar to Python SDK experience +- Kotlin-only feature, no Java SDK changes required + +**Trade-offs:** +- Different from Java SDK convention +- Activity name derived from method name (convention-based, respects `@ActivityMethod(name = "...")`) + +--- + **Next:** [Activity Implementation](./implementation.md) diff --git a/kotlin/workflows/definition.md b/kotlin/workflows/definition.md index 195af7d..cfb8662 100644 --- a/kotlin/workflows/definition.md +++ b/kotlin/workflows/definition.md @@ -128,4 +128,61 @@ val classLog = KWorkflow.logger(MyWorkflowImpl::class.java) --- +## Open Questions (Decision Needed) + +### Interfaceless Workflow Definition + +**Status:** Decision needed + +Currently, workflows require interface definitions: + +```kotlin +// Current approach - requires interface +@WorkflowInterface +interface GreetingWorkflow { + @WorkflowMethod + suspend fun getGreeting(name: String): String +} + +class GreetingWorkflowImpl : GreetingWorkflow { + override suspend fun getGreeting(name: String): String { + // implementation + } +} +``` + +**Proposal:** Allow defining workflows directly on implementation classes without interfaces, similar to Python SDK: + +```kotlin +// Proposed approach - no interface required +class GreetingWorkflow { + @WorkflowMethod + suspend fun getGreeting(name: String): String { + return KWorkflow.executeActivity( + GreetingActivities::composeGreeting, + KActivityOptions(startToCloseTimeout = 10.seconds), + "Hello", name + ) + } +} + +// Client call using method reference to impl class +val result = client.executeWorkflow( + GreetingWorkflow::getGreeting, + KWorkflowOptions(workflowId = "greeting-123", taskQueue = "greetings"), + "World" +) +``` + +**Benefits:** +- Reduces boilerplate (no separate interface file) +- More similar to Python SDK experience +- Kotlin-only feature, no Java SDK changes required + +**Trade-offs:** +- Different from Java SDK convention +- Workflow type name derived from class name (convention-based, respects `@WorkflowMethod(name = "...")`) + +--- + **Next:** [Signals, Queries & Updates](./signals-queries.md) From c86195b6ab4057682187e04a0eef36ad6ba8638f Mon Sep 17 00:00:00 2001 From: Maxim Fateev <1463622+mfateev@users.noreply.github.com> Date: Mon, 12 Jan 2026 15:47:44 -0600 Subject: [PATCH 65/83] Add open questions document and cross-link discussions - Create open-questions.md to centralize API design decisions - Add link to open questions in main README reference section - Link activity and workflow definition docs to full discussion --- kotlin/README.md | 1 + kotlin/activities/definition.md | 2 +- kotlin/open-questions.md | 96 +++++++++++++++++++++++++++++++++ kotlin/workflows/definition.md | 2 +- 4 files changed, 99 insertions(+), 2 deletions(-) create mode 100644 kotlin/open-questions.md diff --git a/kotlin/README.md b/kotlin/README.md index e1b289c..8280276 100644 --- a/kotlin/README.md +++ b/kotlin/README.md @@ -75,6 +75,7 @@ This approach provides: - **[Testing](./testing.md)** - Unit testing, mocking activities, time skipping - **[Migration Guide](./migration.md)** - Migrating from Java SDK - **[API Parity](./api-parity.md)** - Java SDK comparison, gaps, not-needed APIs +- **[Open Questions](./open-questions.md)** - API design decisions pending discussion ## Quick Start diff --git a/kotlin/activities/definition.md b/kotlin/activities/definition.md index 76a7def..33addb0 100644 --- a/kotlin/activities/definition.md +++ b/kotlin/activities/definition.md @@ -172,7 +172,7 @@ object KWorkflow { ### Interfaceless Activity Definition -**Status:** Decision needed +**Status:** Decision needed | [Full discussion](../open-questions.md#interfaceless-workflows-and-activities) Currently, activities require interface definitions: diff --git a/kotlin/open-questions.md b/kotlin/open-questions.md new file mode 100644 index 0000000..a95c995 --- /dev/null +++ b/kotlin/open-questions.md @@ -0,0 +1,96 @@ +# Open Questions + +This document tracks API design questions that need discussion and decisions before implementation. + +## Interfaceless Workflows and Activities + +**Status:** Decision needed + +### Problem Statement + +Currently, workflows and activities require interface definitions with annotations, which adds boilerplate: + +```kotlin +// Current approach - requires interface +@ActivityInterface +interface GreetingActivities { + suspend fun composeGreeting(greeting: String, name: String): String +} + +class GreetingActivitiesImpl : GreetingActivities { + override suspend fun composeGreeting(greeting: String, name: String) = "$greeting, $name!" +} + +@WorkflowInterface +interface GreetingWorkflow { + @WorkflowMethod + suspend fun getGreeting(name: String): String +} + +class GreetingWorkflowImpl : GreetingWorkflow { + override suspend fun getGreeting(name: String): String { + // implementation + } +} +``` + +### Proposal + +Allow defining activities and workflows directly on implementation classes without interfaces, similar to Python SDK: + +**Activities:** + +```kotlin +// Proposed approach - no interface required +class GreetingActivities { + @ActivityMethod + suspend fun composeGreeting(greeting: String, name: String) = "$greeting, $name!" +} + +// In workflow - call using method reference to impl class +val result = KWorkflow.executeActivity( + GreetingActivities::composeGreeting, + KActivityOptions(startToCloseTimeout = 30.seconds), + "Hello", "World" +) +``` + +**Workflows:** + +```kotlin +// Proposed approach - no interface required +class GreetingWorkflow { + @WorkflowMethod + suspend fun getGreeting(name: String): String { + return KWorkflow.executeActivity( + GreetingActivities::composeGreeting, + KActivityOptions(startToCloseTimeout = 10.seconds), + "Hello", name + ) + } +} + +// Client call using method reference to impl class +val result = client.executeWorkflow( + GreetingWorkflow::getGreeting, + KWorkflowOptions(workflowId = "greeting-123", taskQueue = "greetings"), + "World" +) +``` + +### Benefits + +- Reduces boilerplate (no separate interface file) +- More similar to Python SDK experience +- Kotlin-only feature, no Java SDK changes required + +### Trade-offs + +- Different from Java SDK convention +- Activity/workflow type names derived from method/class names (convention-based) +- Respects `@ActivityMethod(name = "...")` and `@WorkflowMethod(name = "...")` annotations if present + +### Related Sections + +- [Activity Definition](./activities/definition.md#interfaceless-activity-definition) +- [Workflow Definition](./workflows/definition.md#interfaceless-workflow-definition) diff --git a/kotlin/workflows/definition.md b/kotlin/workflows/definition.md index cfb8662..afe5ffd 100644 --- a/kotlin/workflows/definition.md +++ b/kotlin/workflows/definition.md @@ -132,7 +132,7 @@ val classLog = KWorkflow.logger(MyWorkflowImpl::class.java) ### Interfaceless Workflow Definition -**Status:** Decision needed +**Status:** Decision needed | [Full discussion](../open-questions.md#interfaceless-workflows-and-activities) Currently, workflows require interface definitions: From c5c1e1e984539e5331294e5eebcd25fed0b079d3 Mon Sep 17 00:00:00 2001 From: Maxim Fateev <1463622+mfateev@users.noreply.github.com> Date: Mon, 12 Jan 2026 15:50:04 -0600 Subject: [PATCH 66/83] Add Claude instructions with open questions guidance Document the process for adding API design questions that need discussion before implementation, including format for the central open-questions.md and inline sections in proposal docs. --- CLAUDE.md | 62 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 62 insertions(+) create mode 100644 CLAUDE.md diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 0000000..fca656e --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1,62 @@ +# Claude Instructions for Kotlin SDK Proposals + +## Repository Purpose + +This repository contains API proposals and design documentation for the Temporal Kotlin SDK. + +## Adding Open Questions + +When proposing new API features that need discussion before implementation: + +1. **Add to the central document**: Add a new section to `kotlin/open-questions.md` with: + - Clear problem statement + - Proposed solution with code examples + - Benefits and trade-offs + - Status marked as "Decision needed" + +2. **Add inline sections**: In the relevant proposal document (e.g., `activities/definition.md`), add an "Open Questions (Decision Needed)" section at the end with: + - Brief summary of the proposal + - Link to full discussion: `[Full discussion](../open-questions.md#section-anchor)` + +3. **Update the README**: Ensure `kotlin/open-questions.md` is linked in the Reference section of `kotlin/README.md` + +### Example Format + +In `open-questions.md`: +```markdown +## Feature Name + +**Status:** Decision needed + +### Problem Statement +[Describe what problem this solves] + +### Proposal +[Code examples showing proposed API] + +### Benefits +- [List benefits] + +### Trade-offs +- [List trade-offs] + +### Related Sections +- [Link to relevant proposal docs] +``` + +In the relevant proposal doc: +```markdown +## Open Questions (Decision Needed) + +### Feature Name + +**Status:** Decision needed | [Full discussion](../open-questions.md#feature-name) + +[Brief summary and code example] +``` + +## Commit Style + +Follow concise commit message style: +- First line: Brief summary of change +- Body: Additional context if needed From e1c6c05f58127a5b59630c931dec2dd8e9ccbace Mon Sep 17 00:00:00 2001 From: Maxim Fateev <1463622+mfateev@users.noreply.github.com> Date: Mon, 12 Jan 2026 16:20:06 -0600 Subject: [PATCH 67/83] Add KArgs type-safe activity arguments as open question Propose using typed KArgs classes instead of varargs for compile-time type safety when invoking activities with multiple arguments. --- kotlin/activities/definition.md | 31 ++++++++ kotlin/open-questions.md | 127 ++++++++++++++++++++++++++++++++ 2 files changed, 158 insertions(+) diff --git a/kotlin/activities/definition.md b/kotlin/activities/definition.md index 33addb0..b9690fb 100644 --- a/kotlin/activities/definition.md +++ b/kotlin/activities/definition.md @@ -216,4 +216,35 @@ val result = KWorkflow.executeActivity( --- +### Type-Safe Activity Arguments with KArgs + +**Status:** Decision needed | [Full discussion](../open-questions.md#type-safe-activity-arguments-with-kargs) + +Use typed `KArgs` classes instead of varargs for compile-time type safety: + +```kotlin +// Single argument - passed directly +KWorkflow.executeActivity( + GreetingActivities::greet, + "World", + KActivityOptions(startToCloseTimeout = 30.seconds) +) + +// Multiple arguments - use kargs() +KWorkflow.executeActivity( + GreetingActivities::composeGreeting, + kargs("Hello", "World"), // KArgs2 - compile-time checked + KActivityOptions(startToCloseTimeout = 30.seconds) +) +``` + +**Benefits:** +- Full compile-time type safety +- Wrong argument types or arity caught at compile time + +**Trade-offs:** +- More verbose for multi-argument activities + +--- + **Next:** [Activity Implementation](./implementation.md) diff --git a/kotlin/open-questions.md b/kotlin/open-questions.md index a95c995..f025a19 100644 --- a/kotlin/open-questions.md +++ b/kotlin/open-questions.md @@ -94,3 +94,130 @@ val result = client.executeWorkflow( - [Activity Definition](./activities/definition.md#interfaceless-activity-definition) - [Workflow Definition](./workflows/definition.md#interfaceless-workflow-definition) + +--- + +## Type-Safe Activity Arguments with KArgs + +**Status:** Decision needed + +### Problem Statement + +The current activity execution API uses varargs which are not compile-time type-safe: + +```kotlin +// Current approach - vararg, no compile-time type checking +KWorkflow.executeActivity( + GreetingActivities::composeGreeting, + KActivityOptions(startToCloseTimeout = 30.seconds), + "Hello", "World" // vararg Any? - wrong types only caught at runtime +) +``` + +### Proposal + +Use typed argument classes (`KArgs`) to provide compile-time type safety: + +**Single argument - passed directly:** +```kotlin +KWorkflow.executeActivity( + GreetingActivities::greet, + "World", + KActivityOptions(startToCloseTimeout = 30.seconds) +) +``` + +**Multiple arguments - use KArgs:** +```kotlin +KWorkflow.executeActivity( + GreetingActivities::composeGreeting, + kargs("Hello", "World"), + KActivityOptions(startToCloseTimeout = 30.seconds) +) +``` + +**KArgs classes:** +```kotlin +sealed interface KArgs + +data class KArgs2(val a1: A1, val a2: A2) : KArgs +data class KArgs3(val a1: A1, val a2: A2, val a3: A3) : KArgs +data class KArgs4(val a1: A1, val a2: A2, val a3: A3, val a4: A4) : KArgs +data class KArgs5(val a1: A1, val a2: A2, val a3: A3, val a4: A4, val a5: A5) : KArgs +data class KArgs6(val a1: A1, val a2: A2, val a3: A3, val a4: A4, val a5: A5, val a6: A6) : KArgs +data class KArgs7(val a1: A1, val a2: A2, val a3: A3, val a4: A4, val a5: A5, val a6: A6, val a7: A7) : KArgs + +// Factory functions +fun kargs(a1: A1, a2: A2) = KArgs2(a1, a2) +fun kargs(a1: A1, a2: A2, a3: A3) = KArgs3(a1, a2, a3) +// ... up to 7 +``` + +**Execute overloads:** +```kotlin +object KWorkflow { + // 1 argument - direct + suspend fun executeActivity( + activity: KFunction2, + arg: A, + options: KActivityOptions + ): R + + // 2 arguments - KArgs2 types must match KFunction3 params + suspend fun executeActivity( + activity: KFunction3, + args: KArgs2, + options: KActivityOptions + ): R + + // 3 arguments + suspend fun executeActivity( + activity: KFunction4, + args: KArgs3, + options: KActivityOptions + ): R + + // ... up to 7 +} +``` + +**Compile-time safety examples:** +```kotlin +interface MyActivities { + suspend fun greet(name: String): String + suspend fun compose(greeting: String, name: String): String +} + +// COMPILES - types match +KWorkflow.executeActivity(MyActivities::greet, "World", options) +KWorkflow.executeActivity(MyActivities::compose, kargs("Hello", "World"), options) + +// COMPILE ERROR - wrong type +KWorkflow.executeActivity(MyActivities::greet, 123, options) +// Error: Type mismatch. Required: String, Found: Int + +// COMPILE ERROR - wrong arg types in kargs +KWorkflow.executeActivity(MyActivities::compose, kargs("Hello", 123), options) +// Error: Type mismatch. Required: KArgs2, Found: KArgs2 + +// COMPILE ERROR - wrong arity +KWorkflow.executeActivity(MyActivities::compose, kargs("Hello", "World", "Extra"), options) +// Error: Type mismatch. Required: KArgs2, Found: KArgs3 +``` + +### Benefits + +- Full compile-time type safety for activity arguments +- Errors caught at compile time, not runtime +- Natural reading order: "execute activity X with args Y using options Z" +- Works with Kotlin's type inference + +### Trade-offs + +- More verbose for multi-argument activities (`kargs(...)` wrapper) +- Multiple overloads needed (7 for each arity) +- Different from current vararg approach + +### Related Sections + +- [Activity Definition](./activities/definition.md#type-safe-activity-arguments-with-kargs) From fcaaa016dd7d4b31b0792d99b48b469f5d7aa856 Mon Sep 17 00:00:00 2001 From: Maxim Fateev <1463622+mfateev@users.noreply.github.com> Date: Mon, 12 Jan 2026 17:37:43 -0600 Subject: [PATCH 68/83] Add 0-arg versions and child workflow/client examples to KArgs proposal Include examples for activities, child workflows, and client workflow execution with 0, 1, and multiple arguments. --- kotlin/open-questions.md | 128 +++++++++++++++++++++++++++++++++++---- 1 file changed, 116 insertions(+), 12 deletions(-) diff --git a/kotlin/open-questions.md b/kotlin/open-questions.md index f025a19..6f97f92 100644 --- a/kotlin/open-questions.md +++ b/kotlin/open-questions.md @@ -118,17 +118,22 @@ KWorkflow.executeActivity( Use typed argument classes (`KArgs`) to provide compile-time type safety: -**Single argument - passed directly:** +**Activities:** ```kotlin +// 0 arguments - just method reference and options +KWorkflow.executeActivity( + GreetingActivities::getDefaultGreeting, + KActivityOptions(startToCloseTimeout = 30.seconds) +) + +// 1 argument - passed directly KWorkflow.executeActivity( GreetingActivities::greet, "World", KActivityOptions(startToCloseTimeout = 30.seconds) ) -``` -**Multiple arguments - use KArgs:** -```kotlin +// Multiple arguments - use kargs() KWorkflow.executeActivity( GreetingActivities::composeGreeting, kargs("Hello", "World"), @@ -136,6 +141,52 @@ KWorkflow.executeActivity( ) ``` +**Child Workflows:** +```kotlin +// 0 arguments +KWorkflow.executeChildWorkflow( + ChildWorkflow::run, + KChildWorkflowOptions(workflowId = "child-1") +) + +// 1 argument +KWorkflow.executeChildWorkflow( + ChildWorkflow::process, + order, + KChildWorkflowOptions(workflowId = "child-1") +) + +// Multiple arguments +KWorkflow.executeChildWorkflow( + ChildWorkflow::processWithConfig, + kargs(order, config), + KChildWorkflowOptions(workflowId = "child-1") +) +``` + +**Client Workflow Execution:** +```kotlin +// 0 arguments +client.executeWorkflow( + MyWorkflow::run, + KWorkflowOptions(workflowId = "wf-1", taskQueue = "main") +) + +// 1 argument +client.executeWorkflow( + MyWorkflow::process, + input, + KWorkflowOptions(workflowId = "wf-1", taskQueue = "main") +) + +// Multiple arguments +client.executeWorkflow( + MyWorkflow::processWithConfig, + kargs(input, config), + KWorkflowOptions(workflowId = "wf-1", taskQueue = "main") +) +``` + **KArgs classes:** ```kotlin sealed interface KArgs @@ -153,9 +204,15 @@ fun kargs(a1: A1, a2: A2, a3: A3) = KArgs3(a1, a2, a3) // ... up to 7 ``` -**Execute overloads:** +**Execute overloads (activities):** ```kotlin object KWorkflow { + // 0 arguments + suspend fun executeActivity( + activity: KFunction1, + options: KActivityOptions + ): R + // 1 argument - direct suspend fun executeActivity( activity: KFunction2, @@ -170,11 +227,58 @@ object KWorkflow { options: KActivityOptions ): R - // 3 arguments - suspend fun executeActivity( - activity: KFunction4, - args: KArgs3, - options: KActivityOptions + // ... up to 7 +} +``` + +**Execute overloads (child workflows):** +```kotlin +object KWorkflow { + // 0 arguments + suspend fun executeChildWorkflow( + workflow: KFunction1, + options: KChildWorkflowOptions + ): R + + // 1 argument + suspend fun executeChildWorkflow( + workflow: KFunction2, + arg: A, + options: KChildWorkflowOptions + ): R + + // 2 arguments + suspend fun executeChildWorkflow( + workflow: KFunction3, + args: KArgs2, + options: KChildWorkflowOptions + ): R + + // ... up to 7 +} +``` + +**Execute overloads (client):** +```kotlin +class KWorkflowClient { + // 0 arguments + suspend fun executeWorkflow( + workflow: KFunction1, + options: KWorkflowOptions + ): R + + // 1 argument + suspend fun executeWorkflow( + workflow: KFunction2, + arg: A, + options: KWorkflowOptions + ): R + + // 2 arguments + suspend fun executeWorkflow( + workflow: KFunction3, + args: KArgs2, + options: KWorkflowOptions ): R // ... up to 7 @@ -214,8 +318,8 @@ KWorkflow.executeActivity(MyActivities::compose, kargs("Hello", "World", "Extra" ### Trade-offs -- More verbose for multi-argument activities (`kargs(...)` wrapper) -- Multiple overloads needed (7 for each arity) +- More verbose for multi-argument calls (`kargs(...)` wrapper) +- Multiple overloads needed (8 for each: 0-7 arguments) - Different from current vararg approach ### Related Sections From de7cbadbb7fb35a32e823f4a7673112b6c410456 Mon Sep 17 00:00:00 2001 From: Maxim Fateev <1463622+mfateev@users.noreply.github.com> Date: Mon, 12 Jan 2026 17:40:38 -0600 Subject: [PATCH 69/83] Add three options for type-safe activity/workflow arguments - Option A: Keep current varargs (no type safety) - Option B: Direct overloads (0-7 arguments, no wrapper) - Option C: KArgs wrapper classes (fewer overloads) Include comparison table showing trade-offs between options. --- kotlin/activities/definition.md | 27 +++--- kotlin/open-questions.md | 158 ++++++++++++++++++++++++-------- 2 files changed, 135 insertions(+), 50 deletions(-) diff --git a/kotlin/activities/definition.md b/kotlin/activities/definition.md index b9690fb..465ca54 100644 --- a/kotlin/activities/definition.md +++ b/kotlin/activities/definition.md @@ -216,34 +216,33 @@ val result = KWorkflow.executeActivity( --- -### Type-Safe Activity Arguments with KArgs +### Type-Safe Activity Arguments -**Status:** Decision needed | [Full discussion](../open-questions.md#type-safe-activity-arguments-with-kargs) +**Status:** Decision needed | [Full discussion](../open-questions.md#type-safe-activityworkflow-arguments) -Use typed `KArgs` classes instead of varargs for compile-time type safety: +Three options for compile-time type-safe activity arguments: +**Option A:** Keep current varargs (no type safety) + +**Option B:** Direct overloads (0-7 arguments each) ```kotlin -// Single argument - passed directly KWorkflow.executeActivity( - GreetingActivities::greet, - "World", + GreetingActivities::composeGreeting, + "Hello", "World", // Direct args - type checked KActivityOptions(startToCloseTimeout = 30.seconds) ) +``` -// Multiple arguments - use kargs() +**Option C:** KArgs wrapper classes +```kotlin KWorkflow.executeActivity( GreetingActivities::composeGreeting, - kargs("Hello", "World"), // KArgs2 - compile-time checked + kargs("Hello", "World"), // KArgs2 KActivityOptions(startToCloseTimeout = 30.seconds) ) ``` -**Benefits:** -- Full compile-time type safety -- Wrong argument types or arity caught at compile time - -**Trade-offs:** -- More verbose for multi-argument activities +See [full discussion](../open-questions.md#type-safe-activityworkflow-arguments) for comparison. --- diff --git a/kotlin/open-questions.md b/kotlin/open-questions.md index 6f97f92..6339f9f 100644 --- a/kotlin/open-questions.md +++ b/kotlin/open-questions.md @@ -97,7 +97,7 @@ val result = client.executeWorkflow( --- -## Type-Safe Activity Arguments with KArgs +## Type-Safe Activity/Workflow Arguments **Status:** Decision needed @@ -114,9 +114,111 @@ KWorkflow.executeActivity( ) ``` -### Proposal +### Options + +Three options are being considered: + +--- + +### Option A: Keep Current Varargs (No Change) + +Keep the current vararg approach: + +```kotlin +KWorkflow.executeActivity( + GreetingActivities::composeGreeting, + KActivityOptions(startToCloseTimeout = 30.seconds), + "Hello", "World" // vararg Any? +) +``` + +**Pros:** +- Simple API, no wrapper classes +- Familiar pattern + +**Cons:** +- No compile-time type safety +- Wrong argument types/count only caught at runtime + +--- + +### Option B: Direct Overloads (0-7 Arguments) + +Provide separate overloads for each arity with direct arguments: + +```kotlin +// 0 arguments +KWorkflow.executeActivity( + GreetingActivities::getDefault, + KActivityOptions(startToCloseTimeout = 30.seconds) +) -Use typed argument classes (`KArgs`) to provide compile-time type safety: +// 1 argument +KWorkflow.executeActivity( + GreetingActivities::greet, + "World", + KActivityOptions(startToCloseTimeout = 30.seconds) +) + +// 2 arguments +KWorkflow.executeActivity( + GreetingActivities::composeGreeting, + "Hello", "World", + KActivityOptions(startToCloseTimeout = 30.seconds) +) + +// 3 arguments +KWorkflow.executeActivity( + OrderActivities::process, + orderId, customer, items, + KActivityOptions(startToCloseTimeout = 30.seconds) +) +``` + +**Overloads:** +```kotlin +object KWorkflow { + suspend fun executeActivity( + activity: KFunction1, + options: KActivityOptions + ): R + + suspend fun executeActivity( + activity: KFunction2, + arg: A, + options: KActivityOptions + ): R + + suspend fun executeActivity( + activity: KFunction3, + arg1: A1, arg2: A2, + options: KActivityOptions + ): R + + suspend fun executeActivity( + activity: KFunction4, + arg1: A1, arg2: A2, arg3: A3, + options: KActivityOptions + ): R + + // ... up to 7 arguments +} +``` + +**Pros:** +- Full compile-time type safety +- Clean call syntax, no wrapper classes +- Natural reading order + +**Cons:** +- Many overloads (8 per method × 3 call types = 24 overloads) +- Options always last (can't use trailing lambda syntax if options were a builder) + +--- + +### Option C: KArgs Wrapper Classes + +Use typed `KArgs` classes for multiple arguments: **Activities:** ```kotlin @@ -285,43 +387,27 @@ class KWorkflowClient { } ``` -**Compile-time safety examples:** -```kotlin -interface MyActivities { - suspend fun greet(name: String): String - suspend fun compose(greeting: String, name: String): String -} - -// COMPILES - types match -KWorkflow.executeActivity(MyActivities::greet, "World", options) -KWorkflow.executeActivity(MyActivities::compose, kargs("Hello", "World"), options) - -// COMPILE ERROR - wrong type -KWorkflow.executeActivity(MyActivities::greet, 123, options) -// Error: Type mismatch. Required: String, Found: Int - -// COMPILE ERROR - wrong arg types in kargs -KWorkflow.executeActivity(MyActivities::compose, kargs("Hello", 123), options) -// Error: Type mismatch. Required: KArgs2, Found: KArgs2 - -// COMPILE ERROR - wrong arity -KWorkflow.executeActivity(MyActivities::compose, kargs("Hello", "World", "Extra"), options) -// Error: Type mismatch. Required: KArgs2, Found: KArgs3 -``` +**Pros:** +- Full compile-time type safety +- Fewer overloads than Option B (only 0, 1, and KArgs variants = 3 per method) +- Works with Kotlin's type inference -### Benefits +**Cons:** +- Requires `kargs(...)` wrapper for 2+ arguments +- Additional classes in the API -- Full compile-time type safety for activity arguments -- Errors caught at compile time, not runtime -- Natural reading order: "execute activity X with args Y using options Z" -- Works with Kotlin's type inference +--- -### Trade-offs +### Comparison -- More verbose for multi-argument calls (`kargs(...)` wrapper) -- Multiple overloads needed (8 for each: 0-7 arguments) -- Different from current vararg approach +| Aspect | Option A (Varargs) | Option B (Direct) | Option C (KArgs) | +|--------|-------------------|-------------------|------------------| +| Type safety | Runtime only | Compile-time | Compile-time | +| Call syntax (2+ args) | `"a", "b"` | `"a", "b"` | `kargs("a", "b")` | +| Overloads per method | 1 | 8 | 3 | +| Total overloads | 3 | 24 | 9 | +| Additional classes | None | None | KArgs2-7 | ### Related Sections -- [Activity Definition](./activities/definition.md#type-safe-activity-arguments-with-kargs) +- [Activity Definition](./activities/definition.md#type-safe-activity-arguments) From a45e8de948d567b7e25d45cd99354586d466ac87 Mon Sep 17 00:00:00 2001 From: Maxim Fateev <1463622+mfateev@users.noreply.github.com> Date: Mon, 12 Jan 2026 17:57:58 -0600 Subject: [PATCH 70/83] Add data classes vs builder+DSL open question --- kotlin/open-questions.md | 93 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 93 insertions(+) diff --git a/kotlin/open-questions.md b/kotlin/open-questions.md index 6339f9f..1977ae6 100644 --- a/kotlin/open-questions.md +++ b/kotlin/open-questions.md @@ -411,3 +411,96 @@ class KWorkflowClient { ### Related Sections - [Activity Definition](./activities/definition.md#type-safe-activity-arguments) + +--- + +## Data Classes vs Builder+DSL for Options/Config Classes + +**Status:** Decision needed + +### Problem Statement + +Data classes are convenient for options/config classes due to named parameters and `copy()`: + +```kotlin +data class KActivityOptions( + val startToCloseTimeout: Duration? = null, + val scheduleToCloseTimeout: Duration? = null, + // ... +) + +val options = KActivityOptions(startToCloseTimeout = 10.minutes) +``` + +However, the Kotlin team doesn't recommend using data classes as part of library APIs because adding a new field (even an optional one) is always a **binary breaking change**. This happens because: + +- Adding a new property requires a new constructor parameter +- Even with default values, this changes the constructor signature +- Existing compiled code calling the old constructor breaks at runtime +- The auto-generated `copy()` method has the same issue + +Libraries like Jackson started with constructors with named parameters and later deprecated them in favor of a DSL/builder combo. + +### Proposed Alternative: Builder + DSL Pattern + +```kotlin +class KActivityOptions +private constructor(builder: Builder) { + val startToCloseTimeout: Duration? + val scheduleToCloseTimeout: Duration? + // ... + + init { + startToCloseTimeout = builder.startToCloseTimeout + scheduleToCloseTimeout = builder.scheduleToCloseTimeout + // ... + } + + class Builder { + var startToCloseTimeout: Duration? = null + var scheduleToCloseTimeout: Duration? = null + // ... + + fun build(): KActivityOptions { + require(startToCloseTimeout != null || scheduleToCloseTimeout != null) { + "At least one of startToCloseTimeout or scheduleToCloseTimeout must be specified" + } + return KActivityOptions(this) + } + } +} + +inline fun KActivityOptions(init: KActivityOptions.Builder.() -> Unit): KActivityOptions { + return KActivityOptions.Builder().apply(init).build() +} +``` + +This gives an ABI-safe equivalent for data class named constructor parameters: + +```kotlin +val options = KActivityOptions { + startToCloseTimeout = 10.minutes + // ... +} +``` + +### Benefits of Builder+DSL + +- Adding new optional properties to the `Builder` class is binary compatible +- The inline factory function provides the same ergonomic DSL syntax as data class constructors +- Validation can happen in `build()` before object construction +- `copy` can be implemented safely if needed (via a `toBuilder()` method) + +### Trade-offs + +- More boilerplate code to write and maintain +- Loses data class conveniences (`equals`, `hashCode`, `toString`, `componentN`) + - Though these can be manually implemented or generated +- Slightly more complex internal implementation + +### Precedent + +This pattern is used by: +- kotlinx.serialization +- Ktor +- Jackson (migrated from named parameters to this pattern) From d63d70277bf7e53da3c198ba4fc9ff28637dbd74 Mon Sep 17 00:00:00 2001 From: Maxim Fateev <1463622+mfateev@users.noreply.github.com> Date: Mon, 12 Jan 2026 17:59:57 -0600 Subject: [PATCH 71/83] Update Kotlin SDK proposal with API refinements and open questions - Add open questions document covering interfaceless workflows/activities, type-safe activity arguments, and data classes vs builder+DSL pattern - Refine API documentation with Kotlin idioms (suspend functions, properties) - Add testing documentation and implementation design docs - Expand migration guide with comprehensive API mapping - Various documentation fixes and improvements --- .claude/skills/implement-change.md | 42 + .claude/skills/implement.md | 62 + .claude/skills/plan-change.md | 40 + .claude/skills/plan-phase.md | 63 + .idea/.gitignore | 8 + .idea/google-java-format.xml | 6 + .idea/misc.xml | 6 + .idea/modules.xml | 8 + .idea/proposals:kotlin-sdk.iml | 9 + .idea/vcs.xml | 6 + CLAUDE.md | 62 + kotlin/README.md | 1 + kotlin/activities/definition.md | 78 + kotlin/implementation/README.md | 19 + kotlin/implementation/implementation-plan.md | 131 ++ kotlin/implementation/sdk-implementation.md | 1016 ++++++++++ kotlin/implementation/sdk-proposal.md | 53 + .../suspend-activities-design.md | 996 ++++++++++ .../implementation/test-framework-design.md | 970 +++++++++ .../test-framework-implementation-design.md | 1756 +++++++++++++++++ .../test-framework-implementation-plan.md | 835 ++++++++ kotlin/open-questions.md | 506 +++++ kotlin/workflows/definition.md | 57 + nexus/images/behavior.png | Bin 192047 -> 131 bytes nexus/images/full-client-id-flow.png | Bin 402976 -> 131 bytes nexus/images/lost-client-id.png | Bin 231111 -> 131 bytes nexus/images/nexus-flow.png | Bin 218330 -> 131 bytes 27 files changed, 6730 insertions(+) create mode 100644 .claude/skills/implement-change.md create mode 100644 .claude/skills/implement.md create mode 100644 .claude/skills/plan-change.md create mode 100644 .claude/skills/plan-phase.md create mode 100644 .idea/.gitignore create mode 100644 .idea/google-java-format.xml create mode 100644 .idea/misc.xml create mode 100644 .idea/modules.xml create mode 100644 .idea/proposals:kotlin-sdk.iml create mode 100644 .idea/vcs.xml create mode 100644 CLAUDE.md create mode 100644 kotlin/implementation/README.md create mode 100644 kotlin/implementation/implementation-plan.md create mode 100644 kotlin/implementation/sdk-implementation.md create mode 100644 kotlin/implementation/sdk-proposal.md create mode 100644 kotlin/implementation/suspend-activities-design.md create mode 100644 kotlin/implementation/test-framework-design.md create mode 100644 kotlin/implementation/test-framework-implementation-design.md create mode 100644 kotlin/implementation/test-framework-implementation-plan.md create mode 100644 kotlin/open-questions.md diff --git a/.claude/skills/implement-change.md b/.claude/skills/implement-change.md new file mode 100644 index 0000000..57e06e0 --- /dev/null +++ b/.claude/skills/implement-change.md @@ -0,0 +1,42 @@ +# Implement Change Skill + +Implement a change based on a detailed plan from the change-planner agent. + +## Usage + +``` +/implement-change . +``` + +Examples: +- `/implement-change 1.1.1` - Implement Phase 1.1, Change 1 +- `/implement-change 2.3.5` - Implement Phase 2.3, Change 5 + +## Behavior + +1. Read the change plan from `kotlin/phases/changes/phase-X.Y-change-N.md` +2. Verify dependencies are met +3. Create new files as specified +4. Modify existing files as specified +5. Implement all unit tests +6. Verify code compiles and tests pass +7. Report summary of changes + +## Prerequisites + +The change plan must exist. Run `/plan-change X.Y.N` first if needed. + +## Output + +- Production code files (created/modified) +- Test files +- Summary of what was done +- Any deviations from plan with justification + +## Quality Gates + +The agent will verify: +- Code compiles without warnings +- All new tests pass +- Existing tests still pass +- Acceptance criteria from plan are met diff --git a/.claude/skills/implement.md b/.claude/skills/implement.md new file mode 100644 index 0000000..7d28f82 --- /dev/null +++ b/.claude/skills/implement.md @@ -0,0 +1,62 @@ +# Implement Skill + +Fully implement a change from a phase plan, orchestrating planning and implementation. + +## Usage + +``` +/implement . +``` + +Examples: +- `/implement 1.1.1` - Implement Phase 1.1, Change 1 +- `/implement 2.3.5` - Implement Phase 2.3, Change 5 + +## Behavior + +This skill orchestrates the full implementation workflow: + +1. **Validate** - Check dependencies are met +2. **Plan** - Generate detailed implementation plan (change-planner) +3. **Review** - Verify plan is feasible and complete +4. **Implement** - Write code and tests (change-implementer) +5. **Verify** - Ensure compilation and tests pass +6. **Retry** - Replan or fix if issues encountered + +## Automatic Recovery + +The orchestrator handles failures automatically: + +| Issue | Action | +|-------|--------| +| Plan incomplete | Replan with feedback (up to 2x) | +| Compilation error | Fix and retry (up to 3x) | +| Test failure | Diagnose and fix or replan | +| Blocking issue | Stop and report | + +## Output + +- Completed implementation with passing tests +- Change plan document at `kotlin/phases/changes/phase-X.Y-change-N.md` +- Summary of files created/modified +- Verification report + +## Prerequisites + +- Phase plan must exist at `kotlin/phases/phase-X.Y-detailed.md` +- Prior changes in the phase should be complete + +## Workflow Visualization + +``` +/implement 1.1.3 + │ + ▼ +┌─────────────┐ ┌─────────────┐ ┌─────────────┐ +│ Plan Change │ ──▶ │ Implement │ ──▶ │ Verify │ +│ │ │ │ │ │ +└─────────────┘ └─────────────┘ └─────────────┘ + ▲ │ │ + │ │ │ + └────── replan ──────┴─────── fix ────────┘ +``` diff --git a/.claude/skills/plan-change.md b/.claude/skills/plan-change.md new file mode 100644 index 0000000..661e979 --- /dev/null +++ b/.claude/skills/plan-change.md @@ -0,0 +1,40 @@ +# Plan Change Skill + +Create a detailed implementation plan for a single change from a phase plan. + +## Usage + +``` +/plan-change . +``` + +Examples: +- `/plan-change 1.1.1` - Plan Phase 1.1, Change 1 +- `/plan-change 2.3.5` - Plan Phase 2.3, Change 5 + +## Behavior + +1. Read the phase plan from `kotlin/phases/phase-X.Y-detailed.md` +2. Read SDK proposal docs and relevant source code +3. Validate the change is feasible +4. Design the simplest, most readable implementation +5. Specify complete test coverage +6. Write output to `kotlin/phases/changes/phase-X.Y-change-N.md` + +## Output Contents + +The implementation plan includes: + +- **Feasibility validation** - Confirm change is possible +- **File changes** - Exact files to create/modify with code skeletons +- **Test specifications** - Complete unit test definitions +- **Acceptance criteria** - Checklist for completion +- **Implementation notes** - Gotchas, patterns, references + +## Requirements + +The output must be **self-contained**. A developer should be able to implement the change using only: +- This document +- The SDK source code + +No need to consult proposal documents or other planning materials. diff --git a/.claude/skills/plan-phase.md b/.claude/skills/plan-phase.md new file mode 100644 index 0000000..2d25005 --- /dev/null +++ b/.claude/skills/plan-phase.md @@ -0,0 +1,63 @@ +# Plan Phase Skill + +Break down a phase of the Kotlin SDK implementation into detailed, self-contained changes. + +## Usage + +``` +/plan-phase +``` + +Examples: +- `/plan-phase 1.1` - Plan "Java SDK Refactoring" +- `/plan-phase 2.1` - Plan "Typed Activity Stubs" + +## Behavior + +1. Read the implementation plan from `kotlin/implementation-plan.md` +2. Read context from `kotlin/sdk-api.md` and `kotlin/sdk-implementation.md` +3. For the specified phase, produce a detailed breakdown where each item: + - Is a self-contained, submittable PR + - Includes full test coverage + - Has clear dependencies on prior items + - Can be reviewed and merged independently + +## Output Format + +Write the detailed plan to `kotlin/phases/phase-X.Y-detailed.md` with this structure: + +```markdown +# Phase X.Y: [Name] - Detailed Plan + +## Overview +Brief description of what this phase accomplishes. + +## Prerequisites +Any required setup or prior phases. + +## Changes + +### Change 1: [Title] +**Summary:** One-line description + +**Scope:** +- Files to add/modify +- Public API changes (if any) + +**Tests:** +- Unit tests required +- Integration tests required + +**Dependencies:** None | Change N + +--- +(repeat for each change) +``` + +## Guidelines + +- **Atomic**: Each change does ONE thing +- **Testable**: Every change includes tests +- **Ordered**: Dependencies flow forward only +- **Compilable**: Code compiles after each change +- **No Design**: Don't go into implementation details - just scope and structure diff --git a/.idea/.gitignore b/.idea/.gitignore new file mode 100644 index 0000000..13566b8 --- /dev/null +++ b/.idea/.gitignore @@ -0,0 +1,8 @@ +# Default ignored files +/shelf/ +/workspace.xml +# Editor-based HTTP Client requests +/httpRequests/ +# Datasource local storage ignored files +/dataSources/ +/dataSources.local.xml diff --git a/.idea/google-java-format.xml b/.idea/google-java-format.xml new file mode 100644 index 0000000..2aa056d --- /dev/null +++ b/.idea/google-java-format.xml @@ -0,0 +1,6 @@ + + + + + \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml new file mode 100644 index 0000000..eeb80f7 --- /dev/null +++ b/.idea/misc.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/.idea/modules.xml b/.idea/modules.xml new file mode 100644 index 0000000..03a1c1e --- /dev/null +++ b/.idea/modules.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/.idea/proposals:kotlin-sdk.iml b/.idea/proposals:kotlin-sdk.iml new file mode 100644 index 0000000..d6ebd48 --- /dev/null +++ b/.idea/proposals:kotlin-sdk.iml @@ -0,0 +1,9 @@ + + + + + + + + + \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml new file mode 100644 index 0000000..35eb1dd --- /dev/null +++ b/.idea/vcs.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 0000000..fca656e --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1,62 @@ +# Claude Instructions for Kotlin SDK Proposals + +## Repository Purpose + +This repository contains API proposals and design documentation for the Temporal Kotlin SDK. + +## Adding Open Questions + +When proposing new API features that need discussion before implementation: + +1. **Add to the central document**: Add a new section to `kotlin/open-questions.md` with: + - Clear problem statement + - Proposed solution with code examples + - Benefits and trade-offs + - Status marked as "Decision needed" + +2. **Add inline sections**: In the relevant proposal document (e.g., `activities/definition.md`), add an "Open Questions (Decision Needed)" section at the end with: + - Brief summary of the proposal + - Link to full discussion: `[Full discussion](../open-questions.md#section-anchor)` + +3. **Update the README**: Ensure `kotlin/open-questions.md` is linked in the Reference section of `kotlin/README.md` + +### Example Format + +In `open-questions.md`: +```markdown +## Feature Name + +**Status:** Decision needed + +### Problem Statement +[Describe what problem this solves] + +### Proposal +[Code examples showing proposed API] + +### Benefits +- [List benefits] + +### Trade-offs +- [List trade-offs] + +### Related Sections +- [Link to relevant proposal docs] +``` + +In the relevant proposal doc: +```markdown +## Open Questions (Decision Needed) + +### Feature Name + +**Status:** Decision needed | [Full discussion](../open-questions.md#feature-name) + +[Brief summary and code example] +``` + +## Commit Style + +Follow concise commit message style: +- First line: Brief summary of change +- Body: Additional context if needed diff --git a/kotlin/README.md b/kotlin/README.md index e1b289c..8280276 100644 --- a/kotlin/README.md +++ b/kotlin/README.md @@ -75,6 +75,7 @@ This approach provides: - **[Testing](./testing.md)** - Unit testing, mocking activities, time skipping - **[Migration Guide](./migration.md)** - Migrating from Java SDK - **[API Parity](./api-parity.md)** - Java SDK comparison, gaps, not-needed APIs +- **[Open Questions](./open-questions.md)** - API design decisions pending discussion ## Quick Start diff --git a/kotlin/activities/definition.md b/kotlin/activities/definition.md index 36b036f..465ca54 100644 --- a/kotlin/activities/definition.md +++ b/kotlin/activities/definition.md @@ -168,4 +168,82 @@ object KWorkflow { --- +## Open Questions (Decision Needed) + +### Interfaceless Activity Definition + +**Status:** Decision needed | [Full discussion](../open-questions.md#interfaceless-workflows-and-activities) + +Currently, activities require interface definitions: + +```kotlin +// Current approach - requires interface +@ActivityInterface +interface GreetingActivities { + suspend fun composeGreeting(greeting: String, name: String): String +} + +class GreetingActivitiesImpl : GreetingActivities { + override suspend fun composeGreeting(greeting: String, name: String) = "$greeting, $name!" +} +``` + +**Proposal:** Allow defining activities directly on implementation classes without interfaces, similar to Python SDK: + +```kotlin +// Proposed approach - no interface required +class GreetingActivities { + @ActivityMethod + suspend fun composeGreeting(greeting: String, name: String) = "$greeting, $name!" +} + +// In workflow - call using method reference to impl class +val result = KWorkflow.executeActivity( + GreetingActivities::composeGreeting, + KActivityOptions(startToCloseTimeout = 30.seconds), + "Hello", "World" +) +``` + +**Benefits:** +- Reduces boilerplate (no separate interface file) +- More similar to Python SDK experience +- Kotlin-only feature, no Java SDK changes required + +**Trade-offs:** +- Different from Java SDK convention +- Activity name derived from method name (convention-based, respects `@ActivityMethod(name = "...")`) + +--- + +### Type-Safe Activity Arguments + +**Status:** Decision needed | [Full discussion](../open-questions.md#type-safe-activityworkflow-arguments) + +Three options for compile-time type-safe activity arguments: + +**Option A:** Keep current varargs (no type safety) + +**Option B:** Direct overloads (0-7 arguments each) +```kotlin +KWorkflow.executeActivity( + GreetingActivities::composeGreeting, + "Hello", "World", // Direct args - type checked + KActivityOptions(startToCloseTimeout = 30.seconds) +) +``` + +**Option C:** KArgs wrapper classes +```kotlin +KWorkflow.executeActivity( + GreetingActivities::composeGreeting, + kargs("Hello", "World"), // KArgs2 + KActivityOptions(startToCloseTimeout = 30.seconds) +) +``` + +See [full discussion](../open-questions.md#type-safe-activityworkflow-arguments) for comparison. + +--- + **Next:** [Activity Implementation](./implementation.md) diff --git a/kotlin/implementation/README.md b/kotlin/implementation/README.md new file mode 100644 index 0000000..cac02c5 --- /dev/null +++ b/kotlin/implementation/README.md @@ -0,0 +1,19 @@ +# Implementation Details + +This folder contains internal design documents and implementation plans for the Kotlin SDK. + +## Documents + +| Document | Description | +|----------|-------------| +| [sdk-proposal.md](./sdk-proposal.md) | Original SDK proposal | +| [sdk-implementation.md](./sdk-implementation.md) | Implementation details and architecture | +| [implementation-plan.md](./implementation-plan.md) | Implementation phases and milestones | +| [suspend-activities-design.md](./suspend-activities-design.md) | Design for suspend activity support | +| [test-framework-design.md](./test-framework-design.md) | Testing framework design | +| [test-framework-implementation-design.md](./test-framework-implementation-design.md) | Testing framework implementation details | +| [test-framework-implementation-plan.md](./test-framework-implementation-plan.md) | Testing framework implementation plan | + +## Related + +For public API documentation, see the [parent folder](../). diff --git a/kotlin/implementation/implementation-plan.md b/kotlin/implementation/implementation-plan.md new file mode 100644 index 0000000..7ee1e79 --- /dev/null +++ b/kotlin/implementation/implementation-plan.md @@ -0,0 +1,131 @@ +# Kotlin SDK Implementation Plan + +## Phase 1: Core Coroutine Infrastructure ✅ COMPLETE + +### 1.1 Java SDK Refactoring +- ✅ Add `WorkflowImplementationFactory` interface +- ✅ Update `Worker` to support multiple factories +- ✅ Update `SyncWorkflowWorker` with composite factory +- ✅ Add `Async.await()` methods returning `Promise` +- ✅ Expose `ReplayWorkflow` and `ReplayWorkflowContext` for SPI + +### 1.2 Kotlin Coroutine Runtime +- ✅ Implement `KotlinCoroutineDispatcher` (deterministic execution with `Delay`) +- ✅ Implement `KotlinReplayWorkflow` +- ✅ Implement `KotlinWorkflowContext` +- ✅ Implement `KotlinWorkflowImplementationFactory` + +### 1.3 Core Kotlin APIs +- ✅ `KWorkflow` object with string-based activity/child workflow execution +- ✅ `KWorkflowInfo` wrapper with null safety +- ✅ Duration extensions (`kotlin.time.Duration` ↔ `java.time.Duration`) +- ✅ `KWorkflow.awaitCondition()` suspend function +- ✅ Standard `delay()` support via `Delay` interface +- ✅ Standard `coroutineScope { async { } }` for parallel execution + +### 1.4 Worker Integration +- ✅ `KotlinPlugin` for enabling coroutine support +- ✅ Worker extension for registering Kotlin workflows +- ✅ Suspend function detection in registration + +### 1.5 Signals, Queries, Updates +- ✅ Signal handler registration (annotation-based and dynamic) with suspend support +- ✅ Query methods (annotation-based and dynamic) +- ✅ Update methods (annotation-based only) + +--- + +## Phase 2: Typed APIs & Full Feature Set ✅ COMPLETE + +### 2.1 Typed Activity Execution ✅ +- ✅ `KFunction`-based `executeActivity()` overloads (0-6 args) +- ✅ Type extraction from method references +- ✅ Local activity support with `executeLocalActivity()` +- ✅ Suspend activity support with `registerSuspendActivities()` + +### 2.2 Typed Child Workflow Execution ✅ +- ✅ Typed `executeChildWorkflow()` with method references +- ✅ `startChildWorkflow()` returning `KChildWorkflowHandle` +- ✅ `KChildWorkflowHandle` interface (signal, cancel, result) +- ✅ Optional `KChildWorkflowOptions` (uses defaults when omitted) + +### 2.3 Update Enhancements ✅ +- ✅ Dynamic update handler registration (`registerUpdateHandler`, `registerDynamicUpdateHandler`) +- ✅ Update validator support (`@UpdateValidatorMethod`) - Uses Java SDK annotations +- ✅ `KEncodedValues` for dynamic handlers to access raw payloads + +### 2.4 Client API ✅ +- ✅ `KWorkflowClient` - Kotlin client with suspend functions and DSL constructor +- ✅ `KWorkflowHandle` - typed handle for signals/queries/updates +- ✅ `KTypedWorkflowHandle` - extends KWorkflowHandle with typed result +- ✅ `WorkflowHandle` - untyped handle (string-based operations) +- ✅ `KUpdateHandle` - handle for async update execution +- ✅ `startWorkflow()`, `executeWorkflow()` suspend functions (0-6 args) +- ✅ `signalWithStart()` +- ✅ `updateWithStart()` - Atomically start workflow + send update + - ✅ `withStartWorkflowOperation()` factory methods (0-6 workflow args, regular and suspend) + - ✅ `startUpdateWithStart()` / `executeUpdateWithStart()` with `KUpdateWithStartOptions` (0-6 update args) + - ✅ `KWithStartWorkflowOperation` to capture workflow start metadata + - ✅ `KUpdateWithStartOptions` with waitForStage and updateId options +- ✅ `getWorkflowHandle()` and `getUntypedWorkflowHandle()` + +### 2.5 Worker API ✅ +- ✅ `KWorkerFactory` - Kotlin worker factory with KotlinPlugin pre-configured +- ✅ `KWorker` - Kotlin worker wrapper with reified generics, KClass support, DSL options for workflow/activity/Nexus registration +- ✅ `KWorkerFactory.newWorker()` returns `KWorker` for idiomatic Kotlin usage +- ✅ DSL builders for worker options + +### 2.6 Kotlin Activity API ✅ +- ✅ `KActivity` object (entry point for activity APIs) +- ✅ `KActivity.info`, `context`, `heartbeat()` properties/methods +- ✅ `KActivity.logger()` for idiomatic logging +- ✅ `KActivityInfo` with null safety +- ✅ Suspend activity support via `SuspendActivityWrapper` + +### 2.7 Kotlin Workflow API ✅ +- ✅ `KWorkflow.logger()` for idiomatic logging +- ✅ `KWorkflow.async {}` for eager parallel execution +- ✅ `KWorkflow.continueAsNew()` with `KContinueAsNewOptions` +- ✅ `KWorkflow.retry()` for workflow-level retry with exponential backoff + +--- + +## Phase 3: Testing Framework & Interceptors ✅ COMPLETE + +### 3.1 Test Environment ✅ COMPLETE +- ✅ `KTestActivityEnvironment` - typed executeActivity/executeLocalActivity, suspend activity support, heartbeat/cancellation testing +- ✅ `KTestWorkflowEnvironment` - worker creation (returns `KWorker`), client access, time manipulation, delayed callbacks, lifecycle management +- ✅ `KTestEnvironmentOptions` and `KTestEnvironmentOptionsBuilder` - DSL configuration +- ✅ `KTestActivityExtension` - JUnit 5 extension with parameter resolution and lifecycle management +- ✅ `KTestWorkflowExtension` - JUnit 5 extension with workflow stub injection, diagnostics on failure +- ✅ `@WorkflowInitialTime` annotation for test-specific initial time +- ✅ Time skipping utilities (`sleep()`, `registerDelayedCallback()`) +- ✅ Search attribute registration in test environment + +### 3.2 Mocking Support ✅ COMPLETE +- ✅ `KActivityMockRegistry` - thread-safe registry for activity mocks +- ✅ `KMockDynamicActivityHandler` - dynamic activity handler routing calls to mocks +- ✅ Support for both regular and suspend activity mocks +- ✅ Integration with `KTestWorkflowExtension` for automatic mock registration + +### 3.3 Interceptors ✅ COMPLETE (see [interceptors.md](../configuration/interceptors.md)) +- ✅ `KWorkerInterceptor` interface with `KWorkerInterceptorBase` +- ✅ `KWorkflowInboundCallsInterceptor` with suspend functions and input/output data classes +- ✅ `KWorkflowOutboundCallsInterceptor` with full API (activities, child workflows, timers, side effects, etc.) +- ✅ `KActivityInboundCallsInterceptor` with suspend support +- ✅ Base classes for convenience (`*Base` classes) +- ✅ `RootWorkflowOutboundCallsInterceptor` - terminal outbound interceptor implementation +- ✅ `WorkflowContextElement` - ThreadContextElement for workflow context propagation to interceptors +- ✅ `KEncodedValues` for dynamic handlers to access raw payloads +- ✅ Interceptor chain integration in `KotlinReplayWorkflow` +- ✅ KWorkflow static methods routed through outbound interceptor (newRandom, randomUUID, currentTimeMillis) +- ✅ Integration test: `TracingInterceptorIntegrationTest` + +--- + +## Cross-Cutting Concerns (All Phases) + +- Documentation and examples +- Migration guide from Java SDK +- Integration tests +- Compatibility testing (Java ↔ Kotlin interop) diff --git a/kotlin/implementation/sdk-implementation.md b/kotlin/implementation/sdk-implementation.md new file mode 100644 index 0000000..1a52400 --- /dev/null +++ b/kotlin/implementation/sdk-implementation.md @@ -0,0 +1,1016 @@ +# Kotlin SDK Implementation Details + +This document describes the internal architecture and implementation details for the Temporal Kotlin SDK. + +For public API and developer experience, see the [SDK API documentation](../README.md). + +## Phases + +* **Phase 1 (COMPLETE)** - Coroutine-based workflows, untyped activity/child workflow execution, pluggable WorkflowImplementationFactory, core Kotlin idioms (Duration, null safety, KWorkflowInfo), signals/queries (annotation + dynamic handlers), updates (annotation-based), standard `delay()` and `coroutineScope { async { } }` support +* **Phase 2** - Typed activity execution, typed child workflow execution, KChildWorkflowHandle, dynamic update handlers, update validators, KActivity/KActivityInfo/KActivityContext wrappers, client & worker API (KWorkflowClient, KWorkerFactory, KWorkflowHandle, startWorkflow, signalWithStart, etc.) +* **Phase 3** - Interceptor interfaces, testing framework + +> **Note:** Nexus support is a separate project and will be addressed independently. + +## Relationship to Java SDK + +The Kotlin SDK builds on top of the Java SDK rather than replacing it: + +* **Shared infrastructure**: Uses the same gRPC client, data conversion, and service client +* **Interoperability**: Kotlin workflows can call Java activities and vice versa +* **Gradual adoption**: Teams can mix Java and Kotlin workflows in the same worker +* **Existing module**: Extends the existing `temporal-kotlin` module which already provides DSL extensions + +## Repository/Package Strategy + +### Repository + +The Kotlin SDK will continue to live in the `temporal-kotlin` module within the `sdk-java` repository: + +* Pros: + * Shared build infrastructure + * Easier to maintain version compatibility + * Single release process +* Cons: + * Ties Kotlin SDK releases to Java SDK releases + +### Package Naming + +* Core workflow APIs: `io.temporal.kotlin.workflow` +* Worker APIs: `io.temporal.kotlin.worker` +* Interceptors: `io.temporal.kotlin.interceptors` +* Existing extensions remain in their current packages (e.g., `io.temporal.client`) + +### Maven Artifacts + +```xml + + io.temporal + temporal-kotlin + N.N.N + +``` + +Additional dependency on kotlinx-coroutines: +```xml + + org.jetbrains.kotlinx + kotlinx-coroutines-core + 1.7.x + +``` + +## Existing Kotlin Classes (Retained) + +The `temporal-kotlin` module already provides Kotlin extensions for the Java SDK: + +| File | Purpose | Status | +|------|---------|--------| +| `TemporalDsl.kt` | `@DslMarker` for type-safe builders | **Keep as-is** | +| `*OptionsExt.kt` (15 files) | DSL builders for all Options classes | **Keep as-is** | +| `WorkflowClientExt.kt` | Reified `newWorkflowStub()`, DSL extensions | **Keep as-is** | +| `WorkflowStubExt.kt` | Reified `getResult()` | **Keep as-is** | +| `WorkerFactoryExt.kt` | `WorkerFactory()` constructor-like DSL | **Keep as-is** | +| `WorkerExt.kt` | Reified `registerWorkflowImplementationType()` | **Keep as-is** | +| `WorkflowMetadata.kt` | `workflowName()`, `workflowSignalName()` | **Keep as-is** | +| `ActivityMetadata.kt` | `activityName(Interface::method)` | **Keep as-is** | +| `KotlinObjectMapperFactory.kt` | Jackson ObjectMapper for Kotlin | **Keep as-is** | +| `KotlinMethodReferenceDisassemblyService.kt` | Kotlin method references in `Async` | **Keep as-is** | + +## New Classes (Kotlin SDK) + +| Class | Purpose | Status | +|-------|---------|--------| +| **Core Workflow** | | | +| `KWorkflow` | Entry point for workflow APIs (like `Workflow` in Java) | ✅ Done | +| `KotlinWorkflowContext` | Internal workflow execution context | ✅ Done | +| `KotlinCoroutineDispatcher` | Deterministic coroutine dispatcher with `Delay` implementation | ✅ Done | +| `KWorkerInterceptor` | Interceptor interface with suspend functions | Phase 3 | +| **Factory/Registration** | | | +| `KotlinPlugin` | Plugin for enabling coroutine support and registering interceptors | ✅ Done | +| `KotlinWorkflowImplementationFactory` | Implements `WorkflowImplementationFactory` for coroutine workflows | ✅ Done | +| `KotlinWorkflowDefinition` | Metadata about a Kotlin workflow type | ✅ Done | +| `KotlinReplayWorkflow` | Implements `ReplayWorkflow` using coroutines | ✅ Done | +| `WorkerExt.kt` (additions) | Extension `registerKotlinWorkflowImplementationTypes()` | Phase 2 | +| **Kotlin Wrappers** | | | +| `KWorkflowInfo` | Kotlin wrapper for WorkflowInfo with nullable types | ✅ Done | +| `KActivity` | Entry point for activity APIs (like `Activity` in Java) | Phase 2 | +| `KActivityInfo` | Kotlin wrapper for ActivityInfo with nullable types | Phase 2 | +| `KActivityContext` | Kotlin wrapper for ActivityExecutionContext | Phase 2 | +| **Client & Worker API** | | | +| `KWorkflowClient` | Kotlin client with suspend functions for starting/executing workflows | Phase 2 | +| `KWorkerFactory` | Kotlin worker factory with KotlinPlugin pre-configured | Phase 2 | +| `WorkflowHandle` | Untyped workflow handle (string-based signals/queries) | Phase 2 | +| `KWorkflowHandle` | Typed workflow handle for signals/queries/updates | Phase 2 | +| `KTypedWorkflowHandle` | Extends KWorkflowHandle with typed result (returned by startWorkflow) | Phase 2 | +| `KUpdateHandle` | Handle for async update execution | Phase 2 | +| `KChildWorkflowHandle` | Handle for interacting with started child workflows (signal, cancel, result) | Phase 2 | +| **Extensions** | | | +| `DurationExt.kt` | Conversions between `kotlin.time.Duration` and `java.time.Duration` | ✅ Done | +| `PromiseExt.kt` | `Promise.toDeferred()` to bridge Java SDK to standard coroutines | ✅ Done | + +> **Note:** We deliberately **do not** have `KActivityHandle`. Instead, users use standard `coroutineScope { async { } }` with `Deferred` for parallel activity execution. This follows the design principle of using idiomatic Kotlin patterns instead of custom APIs. However, `KChildWorkflowHandle` is provided because child workflows support signaling, which requires a handle. + +## Unified Worker Architecture + +The SDK uses a **single unified `WorkerFactory`** that supports both Java and Kotlin workflows. The execution model (thread-based vs coroutine-based) is determined **per workflow instance**, not per worker. This is achieved through a pluggable `WorkflowImplementationFactory` interface. + +``` +┌─────────────────────────────────────────────────────────┐ +│ WorkerFactory │ +│ │ +│ registerWorkflowImplementationTypes(...) ─────────┐ │ +│ registerWorkflowImplementationFactory(...) │ │ +│ ▼ │ +│ ┌─────────────────────────────────────────────────┐ │ +│ │ WorkflowImplementationFactory Registry │ │ +│ │ │ │ +│ │ ┌─────────────────┐ ┌──────────────────────┐ │ │ +│ │ │ POJOWorkflow │ │ KotlinWorkflow │ │ │ +│ │ │ Implementation │ │ Implementation │ │ │ +│ │ │ Factory │ │ Factory │ │ │ +│ │ │ (Java SDK) │ │ (temporal-kotlin) │ │ │ +│ │ └────────┬────────┘ └──────────┬───────────┘ │ │ +│ └───────────┼──────────────────────┼─────────────┘ │ +└──────────────┼──────────────────────┼─────────────────┘ + │ │ + ▼ ▼ +┌──────────────────────┐ ┌──────────────────────────────┐ +│ Java workflow task │ │ Kotlin workflow task │ +│ │ │ │ +│ → DeterministicRunner│ │ → KotlinCoroutineDispatcher│ +│ → Thread-based exec │ │ → Coroutine-based exec │ +└──────────────────────┘ └──────────────────────────────┘ +``` + +**Key benefits:** + +| Benefit | Description | +|---------|-------------| +| Single worker type | No need for separate worker infrastructure | +| Mixed workflows | Java and Kotlin workflows on the same task queue | +| Gradual migration | Convert workflows one at a time | +| No Kotlin in Java SDK | All coroutine code stays in `temporal-kotlin` | +| Per-instance execution | Dispatcher is per workflow instance, not per worker | + +### KWorkerFactory vs Unified Architecture + +The unified worker architecture describes the **internal implementation**—how Java and Kotlin workflows coexist in the same worker. The **API surface** provides two ways to configure this: + +| API | Use Case | What It Does | +|-----|----------|--------------| +| `KWorkerFactory` | Pure Kotlin applications | Convenience wrapper that creates `WorkerFactory` with `KotlinPlugin` pre-configured | +| `WorkerFactory` + `KotlinPlugin` | Mixed Java/Kotlin or Java-main apps | Explicit plugin registration for more control | + +Both approaches produce the same result—a worker that can handle both Java and Kotlin workflows on the same task queue: + +```kotlin +// Option 1: KWorkerFactory (recommended for Kotlin apps) +val factory = KWorkerFactory(client) { + maxWorkflowThreadCount = 800 +} + +// Option 2: Explicit plugin (for Java-main or mixed scenarios) +val factory = WorkerFactory.newInstance(client, WorkerFactoryOptions.newBuilder() + .addPlugin(KotlinPlugin()) + .build()) +``` + +`KWorkerFactory` is not a separate worker type—it's a Kotlin-idiomatic API that wraps the standard `WorkerFactory`. Under the hood, both options use the same unified architecture with pluggable `WorkflowImplementationFactory`. + +## Java SDK Refactoring for Pluggability + +To support the Kotlin coroutine-based execution model, the Java SDK requires refactoring to make workflow execution pluggable. The key principle is that the Java SDK remains **completely Kotlin-agnostic**—it only provides extension points that the `temporal-kotlin` module can use. + +### Required Changes to `temporal-sdk` + +#### 1. New `WorkflowImplementationFactory` Interface + +**File:** `io.temporal.worker.WorkflowImplementationFactory` (new) + +A pluggable interface for creating workflow instances with different execution models: + +```java +/** + * Factory for creating workflow implementations. Different implementations + * can provide different execution models (e.g., thread-based, coroutine-based). + */ +public interface WorkflowImplementationFactory { + /** + * Returns true if this factory handles the given workflow implementation type. + * Used to route workflow registration to the appropriate factory. + */ + boolean supportsType(Class workflowImplementationType); + + /** + * Register workflow implementation types with this factory. + */ + void registerWorkflowImplementationTypes( + WorkflowImplementationOptions options, + Class... workflowImplementationTypes + ); + + /** + * Register a workflow implementation factory function. + */ + void addWorkflowImplementationFactory( + WorkflowImplementationOptions options, + Class workflowInterface, + Functions.Func factory + ); + + /** + * Create a ReplayWorkflow instance for the given workflow type. + * Returns null if this factory doesn't handle the given type. + */ + @Nullable + ReplayWorkflow createWorkflow( + WorkflowType workflowType, + WorkflowExecution workflowExecution + ); + + /** + * Returns the set of workflow types registered with this factory. + */ + Set getRegisteredWorkflowTypes(); +} +``` + +#### 2. Update `Worker` to Support Multiple Factories + +**File:** `io.temporal.worker.Worker` + +```java +public final class Worker { + // Default factory for Java workflows (existing behavior) + private final POJOWorkflowImplementationFactory defaultFactory; + + // Additional factories (e.g., for Kotlin coroutine workflows) + private final List additionalFactories = new ArrayList<>(); + + /** + * Register a custom workflow implementation factory. + * The factory will be consulted for workflow types it supports. + */ + public void registerWorkflowImplementationFactory(WorkflowImplementationFactory factory) { + additionalFactories.add(factory); + } + + // Existing method unchanged - uses default POJO factory + public void registerWorkflowImplementationTypes(Class... workflowImplementationTypes) { + defaultFactory.registerWorkflowImplementationTypes(options, workflowImplementationTypes); + } +} +``` + +#### 3. Update `SyncWorkflowWorker` to Use Factory Registry + +**File:** `io.temporal.internal.worker.SyncWorkflowWorker` + +Modify to consult all registered factories when creating workflow instances: + +```java +class CompositeReplayWorkflowFactory implements ReplayWorkflowFactory { + private final POJOWorkflowImplementationFactory defaultFactory; + private final List additionalFactories; + + @Override + public ReplayWorkflow getWorkflow(WorkflowType workflowType, WorkflowExecution execution) { + // First, check additional factories + for (WorkflowImplementationFactory factory : additionalFactories) { + ReplayWorkflow workflow = factory.createWorkflow(workflowType, execution); + if (workflow != null) { + return workflow; + } + } + // Fall back to default POJO factory + return defaultFactory.getWorkflow(workflowType, execution); + } + + @Override + public boolean isAnyTypeSupported() { + return defaultFactory.isAnyTypeSupported() || + additionalFactories.stream().anyMatch(f -> !f.getRegisteredWorkflowTypes().isEmpty()); + } +} +``` + +#### 4. Expose Required Internal Classes + +Some internal classes need to be accessible for custom `WorkflowImplementationFactory` implementations: + +**File:** `io.temporal.internal.replay.ReplayWorkflow` → Move to SPI package or make accessible + +```java +// Either move to io.temporal.worker.spi or document as semi-public API +public interface ReplayWorkflow { + void start(HistoryEvent event, ReplayWorkflowContext context); + boolean eventLoop() throws Throwable; + WorkflowExecutionResult getOutput(); + void cancel(String reason); + void close(); + // ... other methods +} +``` + +**File:** `io.temporal.internal.replay.ReplayWorkflowContext` → Similar treatment + +The exact approach (SPI package vs `@InternalApi` annotation) is discussed in [Open Question #6: SPI Stability](#6-spi-stability). + +#### 5. Add Async Version of `Workflow.await()` + +**File:** `io.temporal.workflow.Async` + +The existing `Workflow.await()` is blocking with no `Promise`-based equivalent. To support Kotlin coroutines, we need an async version in the `Async` class (consistent with `Async.function()`, `Async.procedure()`, etc.): + +```java +public final class Async { + // Existing methods + public static Promise function(Functions.Func func); + public static Promise procedure(Functions.Proc proc); + // ... + + // New async await methods for coroutine support + public static Promise await(Supplier condition); + public static Promise await(Duration timeout, Supplier condition); +} +``` + +This allows Kotlin to wrap it as a suspend function in `KWorkflow`: + +```kotlin +/** + * Suspends until the condition becomes true. + * + * This is the Kotlin equivalent of Java's [Workflow.await]. + * The condition is re-evaluated after each workflow event. + */ +suspend fun awaitCondition(condition: () -> Boolean) { + Async.await { condition() }.await() // Promise.await() suspends +} + +/** + * Suspends until the condition becomes true or timeout expires. + * + * This is the Kotlin equivalent of Java's [Workflow.await] with timeout. + * + * @return true if condition became true, false if timed out + */ +suspend fun awaitCondition(timeout: Duration, condition: () -> Boolean): Boolean { + return Async.await(timeout.toJava()) { condition() }.await() +} +``` + +**Implementation notes:** +- **Prerequisite:** `Async.await()` does not currently exist in the Java SDK and must be added before `KWorkflow.awaitCondition()` can be implemented. +- Under the hood, this uses `Async.await()` which returns a `Promise` that completes when the condition becomes true. The condition is re-evaluated after each workflow event (signals, activity completions, timers, etc.). +- The async version should integrate with the same condition-tracking mechanism used by the blocking `Workflow.await()`, completing the Promise when the condition becomes true or timeout expires. + +### Files Changed Summary + +| File | Change Type | Description | +|------|-------------|-------------| +| `WorkflowImplementationFactory.java` | **New** | Pluggable factory interface | +| `Worker.java` | **Modified** | Add `registerWorkflowImplementationFactory()` method | +| `SyncWorkflowWorker.java` | **Modified** | Use composite factory for workflow creation | +| `ReplayWorkflow.java` | **Modified** | Make accessible for SPI implementations | +| `ReplayWorkflowContext.java` | **Modified** | Make accessible for SPI implementations | +| `POJOWorkflowImplementationFactory.java` | **Modified** | Implement new interface | +| `Async.java` | **Modified** | Add `await()` methods returning `Promise` | + +### Backward Compatibility + +These changes maintain full backward compatibility: + +* `Worker.registerWorkflowImplementationTypes()` unchanged for existing users +* Existing Java workflows continue to work without modification +* New `registerWorkflowImplementationFactory()` is purely additive +* No Kotlin dependencies in `temporal-sdk` module +* No breaking changes to public APIs + +## Kotlin Module Implementation + +The `temporal-kotlin` module provides the coroutine-based implementation: + +```kotlin +// In temporal-kotlin module +class KotlinWorkflowImplementationFactory( + private val clientOptions: WorkflowClientOptions, + private val workerOptions: WorkerOptions, + private val cache: WorkflowExecutorCache +) : WorkflowImplementationFactory { + + private val registeredTypes = mutableMapOf() + + override fun supportsType(type: Class<*>): Boolean { + // Check if workflow methods are suspend functions (have Continuation parameter) + return type.methods.any { method -> + method.isAnnotationPresent(WorkflowMethod::class.java) && + method.parameterTypes.any { it.name == "kotlin.coroutines.Continuation" } + } + } + + override fun registerWorkflowImplementationTypes( + options: WorkflowImplementationOptions, + vararg types: Class<*> + ) { + for (type in types) { + require(supportsType(type)) { + "Class ${type.name} does not have suspend workflow methods" + } + val definition = KotlinWorkflowDefinition(type, options) + registeredTypes[definition.workflowType] = definition + } + } + + override fun createWorkflow( + workflowType: WorkflowType, + workflowExecution: WorkflowExecution + ): ReplayWorkflow? { + val definition = registeredTypes[workflowType.name] ?: return null + return KotlinReplayWorkflow( + definition, + workflowExecution, + KotlinCoroutineDispatcher() + ) + } + + override fun getRegisteredWorkflowTypes(): Set = registeredTypes.keys +} +``` + +```kotlin +// Kotlin extension for ergonomic registration +fun Worker.registerKotlinWorkflowImplementationTypes(vararg types: KClass<*>) { + val factory = KotlinWorkflowImplementationFactory(/* obtain from worker */) + for (type in types) { + factory.registerWorkflowImplementationTypes( + WorkflowImplementationOptions.getDefaultInstance(), + type.java + ) + } + registerWorkflowImplementationFactory(factory) +} +``` + +## KotlinCoroutineDispatcher + +The custom dispatcher ensures deterministic execution of coroutines and implements the `Delay` interface to intercept standard `kotlinx.coroutines.delay()` calls: + +* Executes coroutines in a controlled, deterministic order (FIFO queue) +* Integrates with Temporal's replay mechanism +* **Implements `Delay` interface** - standard `delay()` is automatically routed through Temporal timers +* Handles cancellation scopes properly +* All coroutines launched with this dispatcher inherit deterministic behavior + +```kotlin +internal class KotlinCoroutineDispatcher( + private val workflowContext: KotlinWorkflowContext +) : CoroutineDispatcher(), Delay { + + private val readyQueue = ArrayDeque() + private var inEventLoop = false + + override fun dispatch(context: CoroutineContext, block: Runnable) { + // Queue for deterministic execution + readyQueue.addLast(block) + + // If we're already in the event loop, the block will be picked up + // Otherwise, we need to signal the workflow to continue + if (!inEventLoop) { + workflowContext.signalReady() + } + } + + /** + * Intercept standard delay() calls and route through Temporal timer. + * This allows users to write `delay(5.seconds)` and have it work deterministically. + */ + override fun scheduleResumeAfterDelay( + timeMillis: Long, + continuation: CancellableContinuation + ) { + workflowContext.scheduleTimer(timeMillis) { + continuation.resume(Unit) + } + } + + /** + * Process queued coroutines deterministically. + * Called by the replay machinery during workflow task processing. + */ + fun eventLoop(deadlockDetectionTimeout: Long): Boolean { + inEventLoop = true + try { + val deadline = System.currentTimeMillis() + deadlockDetectionTimeout + + while (readyQueue.isNotEmpty()) { + if (System.currentTimeMillis() > deadline) { + throw PotentialDeadlockException("Workflow thread stuck") + } + + val runnable = readyQueue.removeFirst() + runnable.run() + } + + return workflowContext.hasMoreWork() + } finally { + inEventLoop = false + } + } +} +``` + +### Delay Implementation + +The `Delay` interface implementation is integrated directly into `KotlinCoroutineDispatcher` (shown above). This allows standard `kotlinx.coroutines.delay()` to work deterministically: + +```kotlin +// Users write standard Kotlin: +delay(5.seconds) + +// The dispatcher's scheduleResumeAfterDelay is called automatically, +// which routes through Temporal's deterministic timer mechanism +``` + +**Why this works:** +- When a coroutine calls `delay()`, kotlinx.coroutines checks if the dispatcher implements `Delay` +- If it does, `scheduleResumeAfterDelay` is called instead of blocking +- Our implementation schedules a Temporal timer that resumes the continuation when fired +- This is completely transparent to user code - no custom `KWorkflow.delay()` needed + +## KotlinReplayWorkflow + +Implements the `ReplayWorkflow` interface using coroutines: + +```kotlin +internal class KotlinReplayWorkflow( + private val definition: KotlinWorkflowDefinition, + private val workflowExecution: WorkflowExecution, + private val dispatcher: KotlinCoroutineDispatcher +) : ReplayWorkflow { + + private var workflowJob: Job? = null + private var result: WorkflowExecutionResult? = null + private lateinit var context: KotlinWorkflowContext + + override fun start(event: HistoryEvent, replayContext: ReplayWorkflowContext) { + context = KotlinWorkflowContext(replayContext) + + // Create coroutine scope with our deterministic dispatcher + val scope = CoroutineScope( + dispatcher + + KotlinDelay(context) + + SupervisorJob() + ) + + // Start the workflow coroutine + workflowJob = scope.launch { + try { + val instance = definition.createInstance() + val output = definition.invokeWorkflowMethod(instance, event.arguments) + result = WorkflowExecutionResult.completed(output) + } catch (e: CancellationException) { + result = WorkflowExecutionResult.canceled(e.message) + } catch (e: Throwable) { + result = WorkflowExecutionResult.failed(e) + } + } + } + + override fun eventLoop(): Boolean { + return dispatcher.eventLoop(deadlockDetectionTimeout) + } + + override fun getOutput(): WorkflowExecutionResult? = result + + override fun cancel(reason: String) { + workflowJob?.cancel(CancellationException(reason)) + } + + override fun close() { + workflowJob?.cancel() + } +} +``` + +## KotlinWorkflowContext + +Provides workflow APIs to the coroutine-based workflow: + +```kotlin +internal class KotlinWorkflowContext( + private val replayContext: ReplayWorkflowContext +) { + fun createTimer(duration: Duration): CompletableFuture { + return replayContext.createTimer(duration) + } + + fun executeActivity( + name: String, + options: ActivityOptions, + args: Array + ): CompletableFuture { + return replayContext.executeActivity(name, options, args) + } + + fun executeChildWorkflow( + type: String, + options: ChildWorkflowOptions, + args: Array + ): CompletableFuture { + return replayContext.executeChildWorkflow(type, options, args) + } + + fun sideEffect(func: () -> Any?): Any? { + return replayContext.sideEffect(func) + } + + fun getVersion(changeId: String, minSupported: Int, maxSupported: Int): Int { + return replayContext.getVersion(changeId, minSupported, maxSupported) + } + + fun currentTime(): Instant = replayContext.currentTimeMillis().let { + Instant.ofEpochMilli(it) + } + + // ... other workflow APIs +} +``` + +## Activity Execution Implementation + +### String-based Activity Execution + +String-based activity execution is implemented directly in `KWorkflow` using **reified generics** to provide a clean API without requiring explicit `Class<*>` parameters or casts: + +```kotlin +object KWorkflow { + /** + * Execute an activity by name with reified return type. + * + * Usage: KWorkflow.executeActivity("activityName", options, arg1, arg2) + * + * The `inline` + `reified` combination allows access to R::class.java at runtime. + * At each call site, the compiler substitutes the actual type. + */ + inline suspend fun executeActivity( + activityName: String, + options: ActivityOptions, + vararg args: Any? + ): R { + return executeActivityInternal(activityName, R::class.java, options, args) as R + } + + /** + * Internal implementation that receives the actual Class for DataConverter. + */ + @PublishedApi + internal suspend fun executeActivityInternal( + activityName: String, + resultType: Class<*>, + options: ActivityOptions, + args: Array + ): Any? { + val context = currentContext() + val future = context.executeActivity(activityName, resultType, options, args) + return future.await() // Suspends until activity completes + } +} +``` + +**Parallel Execution:** Instead of a custom `startActivity` method returning a handle, users use standard Kotlin patterns: + +```kotlin +// Parallel activities using standard coroutineScope { async { } } +val results = coroutineScope { + val d1 = async { KWorkflow.executeActivity("add", options, 1, 2) } + val d2 = async { KWorkflow.executeActivity("add", options, 3, 4) } + awaitAll(d1, d2) // Standard Deferred instances +} +``` + +This approach uses standard `Deferred` instead of a custom `KActivityHandle`, following the design principle of using idiomatic Kotlin patterns. + +**Why `inline` + `reified`?** + +Kotlin generics are erased at runtime (like Java). Without `reified`, we cannot access `R::class.java`: + +```kotlin +// Does NOT work - R is erased at runtime +suspend fun executeActivity(activityName: String, options: ActivityOptions, vararg args: Any?): R { + val resultType = R::class.java // Compile error: Cannot use 'R' as reified type parameter +} + +// WORKS - inline + reified captures type at compile time +inline suspend fun executeActivity(activityName: String, options: ActivityOptions, vararg args: Any?): R { + val resultType = R::class.java // OK: compiler substitutes actual type at call site +} +``` + +**How it works at the call site:** + +```kotlin +// User writes: +val result = KWorkflow.executeActivity("greet", options, name) + +// Compiler inlines to (conceptually): +val result = KWorkflow.executeActivityInternal("greet", String::class.java, options, arrayOf(name)) as String +``` + +**Notes:** +- `@PublishedApi` is required for `internal` functions called from `inline` functions +- `inline suspend fun` is supported since Kotlin 1.3.70 +- Cannot be overridden (inline functions are not virtual) +- Cannot be called from Java (inlined at Kotlin call sites only) + +### Return Type for DataConverter + +The DataConverter needs the return type to deserialize the activity result. With `reified` generics, we can capture the type at compile time: + +```kotlin +inline suspend fun executeActivity(...): R { + // Simple approach - works for non-generic types + val resultType = R::class.java // String::class.java, Int::class.java, etc. + + // Full generic type info using typeOf (Kotlin 1.6+) + val kType: KType = typeOf() // Preserves List, Map, etc. +} +``` + +**Limitation with `R::class.java`:** + +```kotlin +// Works fine for simple types +executeActivity("greet", options, name) // R::class.java = String::class.java + +// Loses type parameter for generic types +executeActivity>("getNames", options) // R::class.java = List::class.java (loses String) +``` + +**Solution using `typeOf()`:** + +Kotlin's `typeOf()` (introduced in Kotlin 1.6) preserves full generic type information: + +```kotlin +inline suspend fun executeActivity( + activityName: String, + options: ActivityOptions, + vararg args: Any? +): R { + val kType: KType = typeOf() // Preserves full generic info + val javaType = kType.javaType // Converts to java.lang.reflect.Type + + return executeActivityInternal(activityName, javaType, options, args) as R +} + +// Internal method accepts java.lang.reflect.Type (handles ParameterizedType) +@PublishedApi +internal suspend fun executeActivityInternal( + activityName: String, + resultType: java.lang.reflect.Type, // Can be Class or ParameterizedType + options: ActivityOptions, + args: Array +): Any? { + val context = currentContext() + val future = context.executeActivity(activityName, resultType, options, args) + return future.await() +} +``` + +This allows the DataConverter to properly deserialize generic types: + +```kotlin +// Now works correctly +val names: List = KWorkflow.executeActivity>("getNames", options) +val mapping: Map = KWorkflow.executeActivity>("getMapping", options) +``` + +### CompletableFuture to Suspend Extension + +```kotlin +// Extension to convert CompletableFuture to suspend +private suspend fun CompletableFuture.await(): T { + return suspendCancellableCoroutine { cont -> + this.whenComplete { result, error -> + if (error != null) { + cont.resumeWithException(error) + } else { + cont.resume(result) + } + } + cont.invokeOnCancellation { + this.cancel(true) + } + } +} +``` + +### Method Reference-based Activity Execution (Phase 2) + +The typed activity API uses `KFunction` references to extract metadata (interface class, method name) without requiring proxy creation: + +```kotlin +object KWorkflow { + /** + * Execute activity using method reference. + * Uses KFunction to extract activity name and return type. + */ + suspend fun executeActivity( + activity: KFunction2, + options: ActivityOptions, + arg1: A1 + ): R { + val activityName = extractActivityName(activity) + val resultType = extractReturnType(activity) + val context = currentContext() + val future = context.executeActivity(activityName, resultType, options, arrayOf(arg1)) + return future.await() as R + } + + /** + * Extract activity name from KFunction. + * Uses @ActivityMethod annotation name if present, otherwise method name. + */ + private fun extractActivityName(function: KFunction<*>): String { + val method = function.javaMethod + ?: throw IllegalArgumentException("Cannot get Java method from function") + + val annotation = method.getAnnotation(ActivityMethod::class.java) + return if (annotation != null && annotation.name.isNotEmpty()) { + annotation.name + } else { + method.name + } + } + + /** + * Extract return type from KFunction for DataConverter. + */ + private fun extractReturnType(function: KFunction<*>): Class<*> { + val method = function.javaMethod + ?: throw IllegalArgumentException("Cannot get Java method from function") + return method.returnType + } +} +``` + +**How `KFunction` works:** + +```kotlin +// User writes: +val result = KWorkflow.executeActivity( + GreetingActivities::composeGreeting, // KFunction2 + options, + name +) + +// At runtime: +// - activity.javaMethod gives us the Method object +// - Method.name gives us "composeGreeting" +// - Method.declaringClass gives us GreetingActivities +// - Method.returnType gives us String.class +``` + +## Duration Extensions + +```kotlin +// DurationExt.kt +package io.temporal.kotlin + +import java.time.Duration as JavaDuration +import kotlin.time.Duration as KotlinDuration +import kotlin.time.toJavaDuration +import kotlin.time.toKotlinDuration + +/** + * Convert Kotlin Duration to Java Duration for interop with Java SDK. + */ +fun KotlinDuration.toJava(): JavaDuration = this.toJavaDuration() + +/** + * Convert Java Duration to Kotlin Duration. + */ +fun JavaDuration.toKotlin(): KotlinDuration = this.toKotlinDuration() + +/** + * DSL property that accepts Kotlin Duration and converts to Java Duration. + */ +var ActivityOptions.Builder.startToCloseTimeout: KotlinDuration + @Deprecated("Write-only property", level = DeprecationLevel.HIDDEN) + get() = throw UnsupportedOperationException() + set(value) { setStartToCloseTimeout(value.toJava()) } + +// Similar extensions for other timeout properties... +``` + +## Decision Justifications + +### Why Coroutines Instead of Extending Java Thread Model? + +* **Idiomatic Kotlin**: Coroutines are the standard concurrency model in Kotlin +* **Structured concurrency**: `coroutineScope`, `async`, `launch` provide clear parent-child relationships +* **Cancellation**: Coroutine cancellation maps naturally to Temporal's cancellation scopes +* **Performance**: Lightweight compared to threads, though this is less relevant in workflow context +* **Ecosystem**: Integrates with Kotlin ecosystem (Flow, channels, etc.) + +### Why Suspend Functions for Workflows? + +* **Natural async**: Workflow operations (activities, timers) are inherently async +* **Sequential code**: Suspend functions allow sequential-looking code for async operations +* **Error handling**: Standard try-catch works with suspend functions +* **Testing**: Coroutine test utilities work well with suspend functions + +### Why Keep Compatibility with Java SDK? + +* **Gradual migration**: Teams can adopt Kotlin incrementally +* **Ecosystem leverage**: Benefits from Java SDK's stability and features +* **Reduced maintenance**: Shared infrastructure reduces duplication +* **Activity reuse**: Existing Java activities work without modification + +### Why Untyped Stubs First in Phase 1? + +* **Complexity management**: Typed stub generation requires more infrastructure +* **Proof of concept**: Validates coroutine-based execution model first +* **Faster iteration**: Can release usable SDK sooner +* **Java interop**: Untyped stubs work with any activity implementation + +### Why Custom CoroutineDispatcher? + +* **Determinism**: Standard dispatchers are non-deterministic +* **Replay support**: Must execute coroutines in same order during replay +* **Timer integration**: `delay()` must map to Temporal timers, not actual time +* **Deadlock detection**: Can implement workflow-aware deadlock detection + +### Why Unified Worker Instead of Separate KotlinWorkerFactory? + +* **Per-instance execution**: The dispatcher is per workflow instance, not per worker—there's no technical reason to separate workers +* **Simpler API**: One worker type is easier to understand and use +* **Mixed task queues**: Java and Kotlin workflows can share the same task queue +* **Easier migration**: Convert workflows one at a time without changing infrastructure +* **No Kotlin in Java SDK**: The pluggable `WorkflowImplementationFactory` keeps all Kotlin code in `temporal-kotlin` + +### Why Invest in Kotlin Idioms? + +* **Developer expectations**: Kotlin developers expect null safety and kotlin.time.Duration +* **Null safety**: Nullable types replace `Optional` throughout the API +* **Readability**: `30.seconds` is more readable than `Duration.ofSeconds(30)` +* **Coroutines**: `suspend fun` for natural async workflows and activities +* **IDE support**: Kotlin idioms provide better autocomplete and error detection +* **Differentiation**: Makes the Kotlin SDK feel native, not just a wrapper around Java + +## Open Questions + +### Workflow Versioning with Coroutines + +How do `Workflow.getVersion()` semantics work with coroutines? + +- **Version marker ordering**: Version checks record markers in the event history. With coroutines suspending and resuming, need to ensure markers are recorded in deterministic order during replay. +- **Suspension points**: If `getVersion()` is called before a suspension point, does the version marker position remain stable across code changes? +- **Expected behavior**: Likely works the same as Java since version markers are recorded synchronously before any suspension. Needs validation during implementation. + +## Resolved Decisions + +### CancellationScope Mapping + +Kotlin's built-in coroutine cancellation replaces Java SDK's `CancellationScope`. The `KotlinCoroutineDispatcher` already provides deterministic execution, so standard Kotlin cancellation patterns work correctly with Temporal's replay mechanism. + +| Java SDK | Kotlin SDK | Notes | +|----------|------------|-------| +| `Workflow.newCancellationScope(() -> {...})` | `coroutineScope { ... }` | Parent cancellation propagates to children | +| `Workflow.newDetachedCancellationScope(() -> {...})` | `withContext(NonCancellable) { ... }` | For cleanup code that must run | +| `CancellationScope.cancel()` | `job.cancel()` | Explicit cancellation | +| `CancellationScope.isCancelRequested()` | `!isActive` | Check without throwing | +| `CancellationScope.throwCanceled()` | `ensureActive()` | Throws if cancelled | +| `scope.run()` with timeout | `withTimeout(duration) { ... }` | Built-in timeout support | + +**Why this works:** +- Server cancellation triggers coroutine cancellation via root `Job` +- Cancellation state is recorded in workflow history for deterministic replay +- `withContext(NonCancellable)` maps directly to detached scope semantics +- No custom API needed—Kotlin's structured concurrency matches Temporal's model + +### DSL vs Annotations + +**Decision**: Keep annotations (`@WorkflowMethod`, `@SignalMethod`) as the primary approach for consistency with Java SDK. Consider DSL as optional alternative in future phases if there's demand. + +### SPI Stability + +**Decision**: Start with `@InternalApi` annotation for Phase 1. The affected interfaces (`WorkflowImplementationFactory`, `ReplayWorkflow`, `ReplayWorkflowContext`) will be promoted to a stable SPI package once they stabilize after real-world usage (likely post-Phase 3). + +## Future Considerations + +### Kotlin Flow Support + +Streaming results via Kotlin Flow is deferred to post-Phase 3. Existing patterns (heartbeat details, signals) cover most use cases. Flow integration requires careful handling of backpressure, cancellation, and replay determinism. + +### Kotlin Multiplatform + +Not a near-term priority. The SDK is JVM-only due to Java SDK dependency. May revisit if sdk-core provides C bindings that Kotlin/Native could use. + +## References + +* [Kotlin Coroutines Prototype PR #1792](https://github.com/temporalio/sdk-java/pull/1792) - Early prototype; this proposal uses a different architecture (pluggable factory vs separate worker factory) +* [Existing temporal-kotlin Module](https://github.com/temporalio/sdk-java/tree/master/temporal-kotlin) +* [.NET SDK Proposal - Phase 1](../dotnet/sdk-phase-1.md) +* [.NET SDK Proposal - Phase 2](../dotnet/sdk-phase-2.md) diff --git a/kotlin/implementation/sdk-proposal.md b/kotlin/implementation/sdk-proposal.md new file mode 100644 index 0000000..95e61a2 --- /dev/null +++ b/kotlin/implementation/sdk-proposal.md @@ -0,0 +1,53 @@ +# Kotlin SDK Proposal + +The Kotlin SDK proposal is split into two documents: + +## Design Principle + +**Use idiomatic Kotlin language patterns wherever possible instead of custom APIs.** + +The Kotlin SDK should feel natural to Kotlin developers by leveraging standard `kotlinx.coroutines` primitives. Custom APIs should only be introduced when Temporal-specific semantics cannot be achieved through standard patterns. + +| Pattern | Standard Kotlin | Temporal Integration | +|---------|-----------------|----------------------| +| Parallel execution | `coroutineScope { async { ... } }` | Works via deterministic dispatcher | +| Await multiple | `awaitAll(d1, d2)` | Standard kotlinx.coroutines | +| Sleep/delay | `delay(duration)` | Intercepted via `Delay` interface | +| Deferred results | `Deferred` | Standard + `Promise.toDeferred()` | + +## [SDK API](../README.md) + +Public API and developer experience documentation including: + +- Design principle: idiomatic Kotlin patterns +- Kotlin idioms (Duration, null safety, standard coroutines) +- Workflow definition with suspend functions +- Activity definition (no stubs - options per call) +- Client API (leverages existing DSL extensions) +- Worker API +- Data conversion +- Interceptors +- Testing +- Migration guide from Java SDK +- Complete examples + +## [SDK Implementation](./sdk-implementation.md) + +Internal architecture and implementation details including: + +- Relationship to Java SDK +- Repository and package strategy +- Existing DSL extensions (ActivityOptions, WorkflowOptions, etc.) +- New classes (KWorkflow, KotlinCoroutineDispatcher, etc.) +- Unified worker architecture +- Java SDK refactoring for pluggability +- `WorkflowImplementationFactory` interface +- `KotlinCoroutineDispatcher` with `Delay` implementation +- `KotlinReplayWorkflow` implementation +- Decision justifications +- Open questions + +## Quick Links + +- [Kotlin Coroutines Prototype PR #1792](https://github.com/temporalio/sdk-java/pull/1792) +- [Existing temporal-kotlin Module](https://github.com/temporalio/sdk-java/tree/master/temporal-kotlin) diff --git a/kotlin/implementation/suspend-activities-design.md b/kotlin/implementation/suspend-activities-design.md new file mode 100644 index 0000000..8d3850a --- /dev/null +++ b/kotlin/implementation/suspend-activities-design.md @@ -0,0 +1,996 @@ +# Design: Suspend Function Support for Kotlin Activities (Revised) + +## Overview + +This document proposes adding support for Kotlin suspend functions as activity implementations. This enables activities to use Kotlin's coroutine-based async I/O libraries (Ktor, R2DBC, etc.) without blocking threads. + +## Current State + +### Activity Execution Model + +Activities in the Java SDK are **thread-based**: + +``` +Worker Thread Pool + ↓ +ActivityTaskHandlerImpl.handle() + ↓ +POJOActivityTaskExecutor.execute() + ↓ +Method.invoke() [blocking] + ↓ +Return result → sendReply() [blocking gRPC] + ↓ +Release slot permit +``` + +- Activities run on dedicated threads from a thread pool +- Context is stored in thread-local storage (`ActivityInternal.currentActivityExecutionContext`) +- Activities can block the thread (e.g., HTTP calls, database queries) +- Result is sent to server via **blocking** gRPC call + +### Existing Async Pattern: Local Manual Completion + +The Java SDK already supports async activity completion via `useLocalManualCompletion()`: + +```java +public int execute() { + ActivityExecutionContext context = Activity.getExecutionContext(); + ManualActivityCompletionClient client = context.useLocalManualCompletion(); + + // Offload to thread pool - activity method returns immediately + ForkJoinPool.commonPool().execute(() -> { + try { + // Async work happens here + Object result = doAsyncWork(); + client.complete(result); // Complete when done + } catch (Exception e) { + client.fail(e); // Or fail on error + } + }); + + return 0; // Return immediately, thread is freed +} +``` + +**Key properties:** +- `useLocalManualCompletion()` marks activity for manual completion +- Returns `ManualActivityCompletionClient` for completing later +- Respects `maxConcurrentActivityExecutionSize` limit (slot stays reserved) +- Releases slot permit when `complete()`, `fail()`, or `reportCancellation()` is called + +### Problem + +Using `runBlocking` in a suspend activity wrapper defeats the purpose - it still ties up the thread pool thread during the entire coroutine execution. We need truly non-blocking execution. + +## Proposed Design + +### Key Insight + +Leverage the existing `useLocalManualCompletion()` pattern to achieve truly async suspend activities without modifying the core Java SDK. + +### Architecture Overview + +``` +┌─────────────────────────────────────────────────────────────────────┐ +│ Activity Task Arrives │ +└───────────────────────────────┬─────────────────────────────────────┘ + │ + ▼ +┌─────────────────────────────────────────────────────────────────────┐ +│ SuspendActivityTaskExecutor │ +│ │ +│ 1. Get ManualActivityCompletionClient via useLocalManualCompletion()│ +│ 2. Launch coroutine on CoroutineDispatcher │ +│ 3. Return immediately (thread freed) │ +└───────────────────────────────┬─────────────────────────────────────┘ + │ + ┌───────────────────────┴───────────────────────┐ + │ │ + ▼ ▼ +┌─────────────────────┐ ┌─────────────────────────┐ +│ Thread Pool Thread │ │ Coroutine Dispatcher │ +│ (freed immediately) │ │ │ +└─────────────────────┘ │ Execute suspend fun │ + │ │ │ + │ ▼ │ + │ On I/O suspend: │ + │ Thread released! │ + │ │ │ + │ ▼ │ + │ Resume on I/O complete │ + │ │ │ + │ ▼ │ + │ Complete via client │ + └─────────────────────────┘ + │ + ▼ + ┌─────────────────────────┐ + │ ManualActivityCompletion│ + │ Client.complete(result) │ + │ │ + │ - Sends result to server│ + │ - Releases slot permit │ + └─────────────────────────┘ +``` + +### Component 1: SuspendActivityInvoker + +A wrapper that intercepts suspend activity method invocations: + +```kotlin +// io.temporal.kotlin.internal.SuspendActivityInvoker + +internal class SuspendActivityInvoker( + private val activityInstance: Any, + private val method: KFunction<*>, + private val dispatcher: CoroutineDispatcher, + private val heartbeatInterval: Duration? = null // Optional auto-heartbeat +) { + /** + * Called by the activity framework. Returns immediately after launching coroutine. + */ + fun invoke( + context: ActivityExecutionContext, + args: Array + ): Any? { + // Get manual completion client - marks activity for async completion + val completionClient = context.useLocalManualCompletion() + + // Create cancellation-aware scope + val job = SupervisorJob() + val scope = CoroutineScope(dispatcher + job) + + // Launch coroutine - this returns immediately! + scope.launch { + // Create activity context element for coroutine + val kContext = KActivityContext(context, completionClient, job) + + withContext(KActivityContextElement(kContext)) { + try { + // Start auto-heartbeat if configured + val heartbeatJob = heartbeatInterval?.let { interval -> + launch { autoHeartbeat(completionClient, interval, job) } + } + + // Execute the suspend function + val result = method.callSuspend(activityInstance, *args) + + // Cancel heartbeat job + heartbeatJob?.cancel() + + // Complete successfully (uses Dispatchers.IO for blocking gRPC) + withContext(Dispatchers.IO) { + completionClient.complete(result) + } + } catch (e: CancellationException) { + // Coroutine was cancelled (activity cancellation or timeout) + withContext(Dispatchers.IO + NonCancellable) { + completionClient.reportCancellation(null) + } + } catch (e: Throwable) { + // Activity failed + withContext(Dispatchers.IO + NonCancellable) { + completionClient.fail(e) + } + } + } + } + + // Return immediately - actual result sent via completion client + return null + } + + /** + * Background job that sends heartbeats and monitors for cancellation. + */ + private suspend fun autoHeartbeat( + client: ManualActivityCompletionClient, + interval: Duration, + parentJob: Job + ) { + while (isActive) { + delay(interval) + try { + // recordHeartbeat throws CanceledFailure if activity is cancelled + withContext(Dispatchers.IO) { + client.recordHeartbeat(null) + } + } catch (e: CanceledFailure) { + // Activity was cancelled - cancel the parent job + parentJob.cancel(CancellationException("Activity cancelled", e)) + break + } + } + } +} +``` + +### Component 2: KActivityContext (Coroutine-aware) + +Activity context accessible from within coroutines: + +```kotlin +// io.temporal.kotlin.internal.KActivityContext + +internal class KActivityContext( + private val javaContext: ActivityExecutionContext, + private val completionClient: ManualActivityCompletionClient, + private val parentJob: Job +) { + val info: ActivityInfo get() = javaContext.info + val taskToken: ByteArray get() = javaContext.taskToken + + /** + * Sends a heartbeat. This is a suspend function that doesn't block. + * Throws CancellationException if the activity has been cancelled. + */ + suspend fun heartbeat(details: Any? = null) { + try { + withContext(Dispatchers.IO) { + completionClient.recordHeartbeat(details) + } + } catch (e: CanceledFailure) { + // Convert to coroutine cancellation + parentJob.cancel(CancellationException("Activity cancelled", e)) + throw CancellationException("Activity cancelled", e) + } + } + + /** + * Gets heartbeat details from a previous attempt. + */ + inline fun getHeartbeatDetails(): T? { + return javaContext.getHeartbeatDetails(T::class.java).orElse(null) + } +} + +// Coroutine context element +internal class KActivityContextElement( + val context: KActivityContext +) : AbstractCoroutineContextElement(Key) { + companion object Key : CoroutineContext.Key +} +``` + +### Component 3: KActivity Public API + +```kotlin +// io.temporal.kotlin.activity.KActivity + +public object KActivity { + + /** + * Gets activity info for the current execution. + * Works in both suspend and regular activities. + */ + public fun getInfo(): KActivityInfo { + // Try coroutine context first (suspend activities) + val kContext = currentCoroutineContextOrNull()?.get(KActivityContextElement)?.context + if (kContext != null) { + return KActivityInfo(kContext.info) + } + // Fall back to thread-local (regular activities) + return KActivityInfo(Activity.getExecutionContext().info) + } + + /** + * Sends a heartbeat with optional details. + * In suspend activities, this is non-blocking. + * + * @throws CancellationException if the activity has been cancelled + */ + public suspend fun heartbeat(details: T? = null) { + val kContext = coroutineContext[KActivityContextElement]?.context + ?: throw IllegalStateException("heartbeat() must be called from within an activity") + kContext.heartbeat(details) + } + + /** + * Gets heartbeat details from a previous attempt. + */ + public inline fun getHeartbeatDetails(): T? { + val kContext = currentCoroutineContextOrNull()?.get(KActivityContextElement)?.context + if (kContext != null) { + return kContext.getHeartbeatDetails() + } + return Activity.getExecutionContext() + .getHeartbeatDetails(T::class.java) + .orElse(null) + } + + /** + * Gets the task token for external completion scenarios. + */ + public fun getTaskToken(): ByteArray { + val kContext = currentCoroutineContextOrNull()?.get(KActivityContextElement)?.context + if (kContext != null) { + return kContext.taskToken + } + return Activity.getExecutionContext().taskToken + } +} + +// Helper to get coroutine context without throwing +private fun currentCoroutineContextOrNull(): CoroutineContext? { + return try { + // This works if we're in a coroutine + runBlocking { coroutineContext } + } catch (e: Exception) { + null + } +} +``` + +### Component 4: Activity Registration + +Detect suspend functions at registration and create appropriate invokers: + +```kotlin +// io.temporal.kotlin.worker.KWorkerFactory (enhanced) + +public class KWorkerFactory(client: KWorkflowClient) { + + private val activityDispatcher: CoroutineDispatcher = Dispatchers.Default + private val defaultHeartbeatInterval: Duration? = null // Optional + + /** + * Configure the dispatcher for suspend activities. + */ + public fun setActivityDispatcher(dispatcher: CoroutineDispatcher) { + this.activityDispatcher = dispatcher + } + + /** + * Register activity implementations. + * Automatically detects suspend functions and creates appropriate handlers. + */ + public fun registerActivitiesImplementations(vararg activityImplementations: Any) { + for (impl in activityImplementations) { + val activityInterfaces = findActivityInterfaces(impl::class) + + for (iface in activityInterfaces) { + for (method in iface.memberFunctions) { + if (isActivityMethod(method)) { + if (method.isSuspend) { + // Create suspend-aware invoker + val invoker = SuspendActivityInvoker( + activityInstance = impl, + method = method, + dispatcher = activityDispatcher, + heartbeatInterval = defaultHeartbeatInterval + ) + registerSuspendActivity(method, invoker) + } else { + // Use existing POJO registration + registerPOJOActivity(method, impl) + } + } + } + } + } + } + + private fun registerSuspendActivity(method: KFunction<*>, invoker: SuspendActivityInvoker) { + // Create a wrapper that the Java SDK can invoke + val wrapper = object : ActivityMethod { + fun execute(context: ActivityExecutionContext, args: Array): Any? { + return invoker.invoke(context, args) + } + } + // Register with Java SDK's activity registry + // ... registration logic + } +} +``` + +## Thread/Coroutine Flow Diagram + +``` +Timeline: +─────────────────────────────────────────────────────────────────────────────► + +Thread Pool Thread #1: +│ +├─ Poll for task ───────────────────────────────────────────────────────────── +│ │ +│ ├─ Receive activity task +│ │ │ +│ │ ├─ Call SuspendActivityInvoker.invoke() +│ │ │ │ +│ │ │ ├─ Get ManualActivityCompletionClient +│ │ │ ├─ Launch coroutine (async!) +│ │ │ └─ Return immediately +│ │ │ +│ │ └─ Thread freed! ◄───────────────────── +│ │ +│ └─ Poll for next task ──────────────────────────────────────────────────── + +Coroutine on Dispatcher: + │ + ├─ Start executing suspend fun + │ │ + │ ├─ Await HTTP call (suspend) + │ │ │ + │ │ └─ Thread released while waiting + │ │ + │ ├─ Resume on HTTP response + │ │ │ + │ │ └─ Continue processing + │ │ + │ └─ Return result + │ + ├─ Call client.complete(result) + │ │ + │ └─ gRPC to server (on Dispatchers.IO) + │ + └─ Slot permit released +``` + +## Usage Examples + +### Example 1: Basic Suspend Activity + +```kotlin +@ActivityInterface +interface HttpActivities { + suspend fun fetchUser(userId: String): User +} + +class HttpActivitiesImpl( + private val client: HttpClient // Ktor client +) : HttpActivities { + + override suspend fun fetchUser(userId: String): User { + // Non-blocking HTTP call - thread is released during I/O wait + return client.get("https://api.example.com/users/$userId").body() + } +} +``` + +### Example 2: Activity with Heartbeat + +```kotlin +@ActivityInterface +interface BatchActivities { + suspend fun processBatch(items: List): BatchResult +} + +class BatchActivitiesImpl : BatchActivities { + + override suspend fun processBatch(items: List): BatchResult { + val results = mutableListOf() + + for ((index, item) in items.withIndex()) { + // Process item asynchronously + val result = processItem(item) + results.add(result) + + // Non-blocking heartbeat - throws CancellationException if cancelled + KActivity.heartbeat(Progress(index + 1, items.size)) + } + + return BatchResult(results) + } + + private suspend fun processItem(item: Item): ItemResult { + // Async processing using Ktor, R2DBC, etc. + delay(100) // Simulated async work + return ItemResult(item.id, "processed") + } +} +``` + +### Example 3: Mixed Activities (Suspend and Regular) + +```kotlin +@ActivityInterface +interface MixedActivities { + // Regular activity - runs on thread pool, blocks during execution + fun computeHash(data: ByteArray): String + + // Suspend activity - thread freed during I/O + suspend fun fetchRemoteData(url: String): ByteArray +} + +class MixedActivitiesImpl : MixedActivities { + + // CPU-bound work - regular function is fine (uses thread pool) + override fun computeHash(data: ByteArray): String { + return MessageDigest.getInstance("SHA-256") + .digest(data) + .toHexString() + } + + // I/O-bound work - suspend function for efficiency + override suspend fun fetchRemoteData(url: String): ByteArray { + return httpClient.get(url).body() + } +} +``` + +### Example 4: Database Activity with R2DBC + +```kotlin +@ActivityInterface +interface DatabaseActivities { + suspend fun findUser(id: Long): User? + suspend fun saveUser(user: User): Long +} + +class DatabaseActivitiesImpl( + private val connectionFactory: ConnectionFactory // R2DBC +) : DatabaseActivities { + + override suspend fun findUser(id: Long): User? { + return connectionFactory.create().awaitSingle().use { conn -> + conn.createStatement("SELECT * FROM users WHERE id = $1") + .bind("$1", id) + .execute() + .awaitFirst() + .map { row -> row.toUser() } + .awaitFirstOrNull() + } + } + + override suspend fun saveUser(user: User): Long { + return connectionFactory.create().awaitSingle().use { conn -> + conn.createStatement( + "INSERT INTO users (name, email) VALUES ($1, $2) RETURNING id" + ) + .bind("$1", user.name) + .bind("$2", user.email) + .execute() + .awaitFirst() + .map { row -> row.get("id", Long::class.java)!! } + .awaitFirst() + } + } +} +``` + +### Example 5: Parallel Async Operations + +```kotlin +@ActivityInterface +interface AggregatorActivities { + suspend fun aggregateData(sources: List): AggregatedResult +} + +class AggregatorActivitiesImpl : AggregatorActivities { + + override suspend fun aggregateData(sources: List): AggregatedResult { + // Fetch from all sources in parallel - truly concurrent! + val results = coroutineScope { + sources.map { source -> + async { + fetchFromSource(source) + } + }.awaitAll() + } + + return AggregatedResult(results) + } + + private suspend fun fetchFromSource(source: String): SourceData { + return httpClient.get(source).body() + } +} +``` + +## Cancellation Handling + +### Cancellation Flow + +``` +Server sends cancellation + │ + ▼ +┌─────────────────────────────────────┐ +│ Heartbeat throws CanceledFailure │ +│ (either auto-heartbeat or manual) │ +└───────────────────┬─────────────────┘ + │ + ▼ +┌─────────────────────────────────────┐ +│ parentJob.cancel() called │ +│ - Cancels main coroutine │ +│ - Cancels all child coroutines │ +└───────────────────┬─────────────────┘ + │ + ▼ +┌─────────────────────────────────────┐ +│ CancellationException propagates │ +│ through coroutine hierarchy │ +└───────────────────┬─────────────────┘ + │ + ▼ +┌─────────────────────────────────────┐ +│ Caught in SuspendActivityInvoker │ +│ - Reports cancellation to server │ +│ - Releases slot permit │ +└─────────────────────────────────────┘ +``` + +### Cancellation Detection Options + +1. **Auto-heartbeat** (recommended for long-running activities): + - Configure heartbeat interval at registration + - Background job periodically heartbeats + - Cancellation detected within heartbeat interval + +2. **Manual heartbeat**: + - Activity explicitly calls `KActivity.heartbeat()` + - Cancellation detected at heartbeat points + +3. **Polling check** (for tight loops): + ```kotlin + suspend fun processItems(items: List) { + for (item in items) { + ensureActive() // Check for cancellation + process(item) + } + } + ``` + +## Coroutine Dispatcher: Thread Model + +### Where Do Coroutine Threads Come From? + +When a suspend activity is invoked, the coroutine runs on threads provided by a `CoroutineDispatcher`. +This is a **separate thread pool** from the Java SDK's activity executor thread pool. + +``` +┌─────────────────────────────────────────────────────────────────────────────┐ +│ JAVA SDK THREAD POOL │ +│ (ActivityWorker's executor) │ +│ │ +│ ┌─────────┐ ┌─────────┐ ┌─────────┐ ┌─────────┐ │ +│ │Thread-1 │ │Thread-2 │ │Thread-3 │ │Thread-4 │ ... │ +│ └────┬────┘ └────┬────┘ └────┬────┘ └────┬────┘ │ +│ │ │ │ │ │ +│ │ poll() │ poll() │ poll() │ poll() │ +│ │ │ │ │ │ +│ ▼ │ │ │ │ +│ invoke() ─────────┼────────────┼────────────┼──────────────────────┐ │ +│ │ │ │ │ │ │ +│ │ launch() │ │ │ │ │ +│ │ │ │ │ │ │ +│ ▼ │ │ │ │ │ +│ return (FREE!) │ │ │ │ │ +│ │ │ │ │ │ │ +│ ▼ │ │ │ │ │ +│ poll() again │ │ │ │ │ +│ │ │ │ │ │ +└─────────────────────┼────────────┼────────────┼──────────────────────┼──────┘ + │ │ │ │ + │ │ │ │ +┌─────────────────────┼────────────┼────────────┼──────────────────────┼──────┐ +│ │ │ │ │ │ +│ COROUTINE DISPATCHER THREAD POOL │ │ +│ (Kotlin coroutine threads) │ │ +│ │ │ +│ ┌─────────┐ ┌─────────┐ ┌─────────┐ ┌─────────┐ │ │ +│ │ Coro-1 │ │ Coro-2 │ │ Coro-3 │ │ Coro-4 │ ... │ │ +│ └────┬────┘ └────┬────┘ └────┬────┘ └────┬────┘ │ │ +│ │ │ │ │ │ │ +│ ◄────────────┼────────────┼────────────┼──────────────────────┘ │ +│ │ │ │ │ │ +│ ▼ │ │ │ │ +│ Execute suspend │ │ │ │ +│ function │ │ │ │ +│ │ │ │ │ │ +│ (suspend on I/O) │ │ │ │ +│ │ │ │ │ │ +│ ▼ │ │ │ │ +│ Thread released │ │ │ │ +│ . │ │ │ │ +│ . │ │ │ │ +│ . (waiting) │ │ │ │ +│ . │ │ │ │ +│ ▼ │ │ │ │ +│ Resume on Coro-3 ─┼────────────► │ │ +│ │ │ │ │ +│ │ ▼ │ │ +│ │ Continue execution │ │ +│ │ │ │ │ +│ │ ▼ │ │ +│ │ complete(result) │ │ +│ │ │ │ │ +└─────────────────────┴────────────┴────────────┴──────────────────────────────┘ +``` + +**Key point**: The coroutine can start on one thread (Coro-1), suspend, and resume on a completely +different thread (Coro-3). This is normal Kotlin coroutine behavior. + +### Built-in Dispatchers + +Kotlin provides several built-in dispatchers: + +| Dispatcher | Thread Pool | Size | Best For | +|------------|-------------|------|----------| +| `Dispatchers.Default` | Shared, fixed | CPU cores (min 2) | CPU-bound + async I/O | +| `Dispatchers.IO` | Shared, elastic | Up to 64+ threads | Blocking I/O calls | +| `Dispatchers.Unconfined` | Caller's thread | N/A | Testing only | + +```kotlin +// Dispatchers.Default - recommended for most cases +// Backed by a shared ForkJoinPool +val dispatcher = Dispatchers.Default + +// Dispatchers.IO - for blocking calls within suspend functions +// Shares threads with Default but can grow larger +val dispatcher = Dispatchers.IO + +// Limited parallelism - caps concurrent coroutines +val dispatcher = Dispatchers.Default.limitedParallelism(100) +``` + +### Custom Dispatcher from Java Executor + +You can create a dispatcher from any Java `Executor` or `ExecutorService`: + +```kotlin +import kotlinx.coroutines.asCoroutineDispatcher +import java.util.concurrent.Executors + +// From a fixed thread pool +val executor = Executors.newFixedThreadPool(50) +val dispatcher = executor.asCoroutineDispatcher() + +// From a cached thread pool (elastic) +val executor = Executors.newCachedThreadPool() +val dispatcher = executor.asCoroutineDispatcher() + +// From a custom ThreadPoolExecutor +val executor = ThreadPoolExecutor( + 10, // core pool size + 100, // max pool size + 60L, // keep-alive time + TimeUnit.SECONDS, + LinkedBlockingQueue() +) +val dispatcher = executor.asCoroutineDispatcher() + +// IMPORTANT: Remember to close the dispatcher when done +dispatcher.close() // Also shuts down the executor +``` + +### Configuration API + +```kotlin +// Option 1: Use built-in dispatcher (simplest) +val worker = factory.newWorker("my-task-queue") { + suspendActivityDispatcher = Dispatchers.Default +} + +// Option 2: Limited parallelism for backpressure +val worker = factory.newWorker("my-task-queue") { + // Max 100 concurrent suspend activities + suspendActivityDispatcher = Dispatchers.Default.limitedParallelism(100) +} + +// Option 3: Custom executor for full control +val activityExecutor = Executors.newFixedThreadPool(200) +val worker = factory.newWorker("my-task-queue") { + suspendActivityDispatcher = activityExecutor.asCoroutineDispatcher() +} + +// Option 4: Match Java SDK's activity thread pool size +val worker = factory.newWorker("my-task-queue") { + val maxConcurrent = workerOptions.maxConcurrentActivityExecutionSize + suspendActivityDispatcher = Dispatchers.Default.limitedParallelism(maxConcurrent) +} +``` + +### Dispatcher Lifecycle Management + +```kotlin +class KWorkerFactory(client: KWorkflowClient) : Closeable { + + private var customDispatcher: CloseableCoroutineDispatcher? = null + + fun newWorker(taskQueue: String, configure: WorkerConfig.() -> Unit): KWorker { + val config = WorkerConfig().apply(configure) + + // Track custom dispatchers for cleanup + if (config.suspendActivityDispatcher is CloseableCoroutineDispatcher) { + customDispatcher = config.suspendActivityDispatcher + } + + // ... + } + + override fun close() { + // Clean up custom dispatchers + customDispatcher?.close() + // ... + } +} +``` + +### Thread Flow Example + +``` +Time ──────────────────────────────────────────────────────────────────────────► + +Java SDK Thread Pool (4 threads): +┌──────────────────────────────────────────────────────────────────────────────┐ +│ T1: [poll]──[invoke]──[return]──[poll]──[invoke]──[return]──[poll]──... │ +│ T2: [poll]──────────────────────[invoke]──[return]──[poll]──... │ +│ T3: [poll]──[invoke]──[return]──[poll]──... │ +│ T4: [poll]──────────────────────────────[invoke]──[return]──[poll]──... │ +└──────────────────────────────────────────────────────────────────────────────┘ + │ │ │ │ + │ launch │ launch │ launch │ launch + ▼ ▼ ▼ ▼ + +Coroutine Dispatcher (separate pool): +┌──────────────────────────────────────────────────────────────────────────────┐ +│ C1: [run]─[suspend]─────────────[resume]─[complete] │ +│ C2: [run]─[suspend]───────────────────────[resume]─[suspend]─[resume] │ +│ C3: [run]─[complete] │ +│ C4: [run]─[suspend]─────────[resume]─[complete] │ +└──────────────────────────────────────────────────────────────────────────────┘ + +Legend: +- [poll] = Waiting for activity task from server +- [invoke] = Calling SuspendActivityInvoker.invoke() +- [return] = Method returns, thread freed +- [launch] = Coroutine scheduled on dispatcher +- [run] = Coroutine executing code +- [suspend] = Coroutine suspended (waiting for I/O) +- [resume] = Coroutine resumed after I/O +- [complete]= Activity completed via ManualActivityCompletionClient +``` + +### Recommendations + +1. **Start with `Dispatchers.Default`** - It's well-tuned for most workloads + +2. **Use `limitedParallelism()` for backpressure** - Prevents unbounded coroutine growth: + ```kotlin + Dispatchers.Default.limitedParallelism(maxConcurrentActivities) + ``` + +3. **Use `Dispatchers.IO` for blocking calls** - If your suspend activity must call blocking APIs: + ```kotlin + suspend fun myActivity() { + withContext(Dispatchers.IO) { + // Blocking call here won't block Default dispatcher + legacyBlockingApi.call() + } + } + ``` + +4. **Custom executor for isolation** - If you need suspend activities isolated from other coroutines: + ```kotlin + val dedicatedExecutor = Executors.newFixedThreadPool(50) + val dedicatedDispatcher = dedicatedExecutor.asCoroutineDispatcher() + ``` + +## Heartbeat Configuration + +```kotlin +val worker = factory.newWorker("my-task-queue") { + // Dispatcher for suspend activities + suspendActivityDispatcher = Dispatchers.Default.limitedParallelism(100) + + // Auto-heartbeat interval for suspend activities (optional) + // If set, a background coroutine sends heartbeats automatically + suspendActivityHeartbeatInterval = 30.seconds +} + +// Per-activity configuration via annotation (future enhancement) +@SuspendActivityOptions( + heartbeatInterval = "10s" +) +suspend fun myActivity(): Result +``` + +## Implementation Plan + +### Phase 1: Core Infrastructure +1. Create `SuspendActivityInvoker` class +2. Create `KActivityContext` with coroutine support +3. Create `KActivityContextElement` for coroutine context +4. Implement basic suspend activity execution + +### Phase 2: Cancellation Handling +1. Implement auto-heartbeat background job +2. Map `CanceledFailure` to coroutine cancellation +3. Ensure proper cleanup on cancellation + +### Phase 3: Activity Registration +1. Detect suspend functions at registration time +2. Create `SuspendActivityInvoker` for suspend methods +3. Register with Java SDK's activity framework + +### Phase 4: API and Configuration +1. Update `KActivity` API for coroutine support +2. Add dispatcher configuration options +3. Add heartbeat interval configuration + +### Phase 5: Testing +1. Unit tests for suspend activity execution +2. Integration tests with real async I/O (Ktor) +3. Cancellation handling tests +4. Concurrent execution tests + +## Comparison: Old Design vs New Design + +| Aspect | Old Design (runBlocking) | New Design (useLocalManualCompletion) | +|--------|--------------------------|---------------------------------------| +| Thread during suspend | **Blocked** | **Free** | +| I/O efficiency | Low | High | +| Concurrent activities | Limited by threads | Limited by coroutines (much higher) | +| Java SDK changes | None needed | None needed | +| Complexity | Lower | Moderate | +| Cancellation | Immediate | Within heartbeat interval | + +## Considerations + +### Dispatcher Selection + +**Dispatchers.Default** (recommended): +- Shared thread pool sized to CPU cores +- Good for mixed CPU/IO workloads +- Can be limited with `limitedParallelism(n)` + +**Dispatchers.IO**: +- Elastic thread pool for blocking I/O +- Overhead of thread creation +- Use if calling blocking APIs from suspend context + +**Custom dispatcher**: +- Full control over thread pool +- Can match `maxConcurrentActivityExecutionSize` for parity + +### Heartbeat Timing + +The cancellation detection latency equals the heartbeat interval. For activities that need fast cancellation response: +- Use shorter heartbeat intervals (e.g., 5 seconds) +- Or call `KActivity.heartbeat()` explicitly at key points + +### Error Handling + +| Exception Type | Handling | +|----------------|----------| +| Regular exception | `client.fail(e)` - Activity fails, may retry | +| `CancellationException` | `client.reportCancellation(null)` - Clean cancellation | +| Error during completion | Logged, slot released, activity marked failed | + +### Structured Concurrency + +Activities can use `coroutineScope` for structured concurrency: +```kotlin +suspend fun processInParallel(items: List): List { + return coroutineScope { + items.map { async { process(it) } }.awaitAll() + } +} +``` + +All child coroutines are cancelled if the activity is cancelled. + +## Open Questions + +1. **Default heartbeat interval**: Should we have a default auto-heartbeat interval for suspend activities, or require explicit configuration? + +2. **Exception translation**: Should we map specific Kotlin exceptions (e.g., `TimeoutCancellationException`) to specific Temporal failures? + +3. **Context propagation**: How to propagate MDC/tracing context through coroutines? + +4. **Metrics**: Should we expose coroutine-specific metrics (active coroutines, suspension points)? + +## Conclusion + +This design leverages the existing `useLocalManualCompletion()` pattern in the Java SDK to achieve truly non-blocking suspend activities. Key benefits: + +- **No thread blocking**: Executor thread is freed immediately after launching coroutine +- **High concurrency**: Can run many more concurrent I/O-bound activities than threads +- **No Java SDK changes**: Works with existing infrastructure +- **Kotlin-idiomatic**: Uses standard coroutine patterns and context +- **Backward compatible**: Regular (non-suspend) activities continue to work unchanged + +The approach provides efficient async I/O support while maintaining Temporal's reliability guarantees for activity execution, retries, and timeouts. diff --git a/kotlin/implementation/test-framework-design.md b/kotlin/implementation/test-framework-design.md new file mode 100644 index 0000000..843c2c5 --- /dev/null +++ b/kotlin/implementation/test-framework-design.md @@ -0,0 +1,970 @@ +# Kotlin SDK Test Framework Design + +## Overview + +This document describes the user experience for testing Temporal workflows and activities in the Kotlin SDK. The test framework provides Kotlin-idiomatic APIs while maintaining consistency with the Java SDK where appropriate. + +## Design Principles + +1. **Kotlin-Idiomatic**: DSL builders, suspend functions, extension functions, null safety +2. **Coroutine-Native**: Full support for suspend functions and coroutine-based testing +3. **Minimal Boilerplate**: Sensible defaults, concise APIs +4. **Java Interoperability**: Can test Kotlin workflows calling Java activities and vice versa +5. **Familiar to Java Users**: Similar structure and concepts as Java SDK test framework + +--- + +## Package Structure + +Following the Java SDK pattern: +``` +io.temporal.kotlin.testing +├── KTestWorkflowEnvironment +├── KTestWorkflowExtension +├── KTestActivityEnvironment +├── KTestActivityExtension +└── KTestEnvironmentOptions +``` + +--- + +## 1. KTestWorkflowEnvironment + +The core interface for workflow testing, providing an in-memory Temporal service with automatic time skipping. + +### Creating a Test Environment + +```kotlin +// Simple creation with defaults +val testEnv = KTestWorkflowEnvironment.newInstance() + +// With configuration DSL +val testEnv = KTestWorkflowEnvironment.newInstance { + namespace = "test-namespace" + initialTime = Instant.parse("2024-01-01T00:00:00Z") + useTimeskipping = true + + workerFactoryOptions { + // WorkerFactoryOptions configuration + } + + workflowClientOptions { + // WorkflowClientOptions configuration + } + + searchAttributes { + register("CustomKeyword", IndexedValueType.INDEXED_VALUE_TYPE_KEYWORD) + register("CustomInt", IndexedValueType.INDEXED_VALUE_TYPE_INT) + } +} +``` + +### Worker Management + +```kotlin +// Create a KWorker for a task queue (returns KWorker for pure Kotlin) +val worker: KWorker = testEnv.newWorker("my-task-queue") + +// With worker options DSL +val worker: KWorker = testEnv.newWorker("my-task-queue") { + maxConcurrentActivityExecutionSize = 100 + maxConcurrentWorkflowTaskExecutionSize = 50 +} + +// Register workflow and activity implementations +worker.registerWorkflowImplementationTypes() +worker.registerActivitiesImplementations(MyActivitiesImpl()) + +// Register Kotlin suspend activities +worker.registerSuspendActivities(MySuspendActivitiesImpl()) +``` + +### KWorker API + +`KWorker` is the Kotlin-idiomatic worker for pure Kotlin implementations: + +```kotlin +/** + * Kotlin worker that provides idiomatic APIs for registering + * Kotlin workflows and suspend activities. + */ +class KWorker { + /** The underlying Java Worker for interop scenarios */ + val worker: Worker + + /** Register Kotlin workflow implementation types */ + inline fun registerWorkflowImplementationTypes() + fun registerWorkflowImplementationTypes(vararg workflowClasses: KClass<*>) + fun registerWorkflowImplementationTypes( + options: WorkflowImplementationOptions, + vararg workflowClasses: KClass<*> + ) + + /** Register activity implementations (both regular and suspend) */ + fun registerActivitiesImplementations(vararg activities: Any) + + /** Register suspend activity implementations */ + fun registerSuspendActivities(vararg activities: Any) + + /** Register Nexus service implementations */ + fun registerNexusServiceImplementations(vararg services: Any) +} +``` + +**When to use `KWorker` vs `Worker`:** +- Use `KWorker` for pure Kotlin implementations (workflows and activities) +- Use `Worker` (via `worker` property) when mixing Java and Kotlin workflows/activities + +### Client Access + +```kotlin +// Get the workflow client (KWorkflowClient) +val client: KWorkflowClient = testEnv.workflowClient + +// Start and execute workflows +val result = client.executeWorkflow( + MyWorkflow::execute, + KWorkflowOptions(taskQueue = "my-task-queue"), + "input" +) +``` + +### Time Manipulation + +```kotlin +// Get current test time +val currentTime: Instant = testEnv.currentTime +val currentTimeMillis: Long = testEnv.currentTimeMillis + +// Sleep with time skipping (suspend function) +testEnv.sleep(5.minutes) +testEnv.sleep(Duration.ofMinutes(5)) // Java Duration also supported + +// Register delayed callbacks +testEnv.registerDelayedCallback(10.seconds) { + // This executes when test time reaches 10 seconds + println("10 seconds have passed in test time") +} +``` + +### Lifecycle Management + +```kotlin +// Start all registered workers +testEnv.start() + +// Shutdown +testEnv.shutdown() // Graceful shutdown +testEnv.shutdownNow() // Immediate shutdown + +// Await termination (suspend function) +testEnv.awaitTermination(30.seconds) + +// Check status +val isStarted: Boolean = testEnv.isStarted +val isShutdown: Boolean = testEnv.isShutdown +val isTerminated: Boolean = testEnv.isTerminated + +// Auto-close with use {} +KTestWorkflowEnvironment.newInstance().use { testEnv -> + // Test code here + // Automatically closed when block exits +} +``` + +### Diagnostics + +```kotlin +// Get diagnostic information (workflow histories) +val diagnostics: String = testEnv.getDiagnostics() +``` + +### Service Access (Advanced) + +```kotlin +// Direct access to gRPC stubs +val workflowStubs: WorkflowServiceStubs = testEnv.workflowServiceStubs +val operatorStubs: OperatorServiceStubs = testEnv.operatorServiceStubs +val namespace: String = testEnv.namespace +``` + +--- + +## 2. KTestWorkflowExtension (JUnit 5) + +A JUnit 5 extension that simplifies workflow testing with automatic lifecycle management and parameter injection. + +### Basic Usage + +```kotlin +class MyWorkflowTest { + companion object { + @JvmField + @RegisterExtension + val testWorkflow = kTestWorkflowExtension { + registerWorkflowImplementationTypes() + setActivityImplementations(MyActivitiesImpl()) + } + } + + @Test + fun `test workflow execution`(workflow: MyWorkflow) { + val result = workflow.execute("input") + assertEquals("expected", result) + } + + @Test + fun `test with environment access`( + workflow: MyWorkflow, + testEnv: KTestWorkflowEnvironment, + client: KWorkflowClient + ) { + // Access to all test components via parameter injection + testEnv.sleep(5.minutes) + val result = workflow.execute("input") + assertEquals("expected", result) + } +} +``` + +### Configuration DSL + +```kotlin +val testWorkflow = kTestWorkflowExtension { + // Workflow Registration + registerWorkflowImplementationTypes() + registerWorkflowImplementationTypes() + + // With workflow implementation options + registerWorkflowImplementationTypes { + failWorkflowExceptionTypes = listOf(MyBusinessException::class.java) + } + + // Activity Registration + setActivityImplementations(MyActivitiesImpl()) + + // Suspend Activity Registration + setSuspendActivityImplementations(MySuspendActivitiesImpl()) + + // Nexus Service Registration + setNexusServiceImplementations(MyNexusServiceImpl()) + + // Worker Configuration + workerOptions { + maxConcurrentActivityExecutionSize = 100 + } + + workerFactoryOptions { + // WorkerFactoryOptions configuration + } + + // Client Configuration + workflowClientOptions { + // WorkflowClientOptions configuration + } + + namespace = "TestNamespace" // Default: "UnitTest" + + // Service Selection + useInternalService() // Default - in-memory service + // OR + useExternalService() // Uses ClientConfigProfile.load() - reads from env vars (TEMPORAL_ADDRESS, etc.) + // OR + useExternalService("custom-host:7233") // Explicit address override + + // Time Configuration + initialTime = Instant.parse("2024-01-01T00:00:00Z") + useTimeskipping = true // Default: true + + // Search Attributes + searchAttributes { + register("CustomAttr", IndexedValueType.INDEXED_VALUE_TYPE_KEYWORD) + } + + // Defer worker startup (for mock registration) + doNotStart = true +} +``` + +### Parameter Injection + +The extension supports injecting the following types into test methods: + +| Type | Description | +|------|-------------| +| `KTestWorkflowEnvironment` | The test environment | +| `KWorkflowClient` | The workflow client | +| `KWorkflowOptions` | Default workflow options | +| `KWorker` | The Kotlin worker instance | +| `Worker` | The Java worker instance (for mixed Java/Kotlin) | +| `` (workflow interface) | A workflow stub ready to execute | + +**Note:** Use `KWorker` for pure Kotlin implementations. Use `Worker` only when mixing Java and Kotlin workflows/activities in the same test. + +```kotlin +@Test +fun `test with all injected parameters`( + testEnv: KTestWorkflowEnvironment, + client: KWorkflowClient, + worker: KWorker, + options: KWorkflowOptions, + workflow: MyWorkflow // Automatically creates stub +) { + // All parameters automatically injected +} +``` + +### Initial Time Annotation + +Override the initial time for specific tests: + +```kotlin +@Test +@WorkflowInitialTime("2024-06-15T12:00:00Z") +fun `test with specific initial time`(workflow: MyWorkflow) { + // Test runs with specified initial time +} +``` + +### Testing with Mocks + +```kotlin +class MockedActivityTest { + companion object { + @JvmField + @RegisterExtension + val testWorkflow = kTestWorkflowExtension { + registerWorkflowImplementationTypes() + doNotStart = true // Important: defer startup for mock registration + } + } + + @Test + fun `test with mocked activities`( + testEnv: KTestWorkflowEnvironment, + worker: KWorker, + workflow: MyWorkflow + ) { + // Create mock + val mockActivity = mock { + on { doSomething(any()) } doReturn "mocked result" + } + + // Register mock and start + worker.registerActivitiesImplementations(mockActivity) + testEnv.start() + + // Execute workflow + val result = workflow.execute("input") + assertEquals("mocked result", result) + + // Verify + verify(mockActivity).doSomething(any()) + } +} +``` + +--- + +## 3. KTestActivityEnvironment + +For unit testing activity implementations in isolation. + +### Creating an Activity Test Environment + +```kotlin +// Simple creation +val activityEnv = KTestActivityEnvironment.newInstance() + +// With configuration +val activityEnv = KTestActivityEnvironment.newInstance { + workflowClientOptions { + // Configuration + } +} +``` + +### Registering and Testing Activities + +```kotlin +// Register activity implementations +activityEnv.registerActivitiesImplementations(MyActivitiesImpl()) + +// Register suspend activities +activityEnv.registerSuspendActivities(MySuspendActivitiesImpl()) + +// Execute activity using method reference (same pattern as KWorkflow.executeActivity) +val result = activityEnv.executeActivity( + MyActivity::doSomething, + KActivityOptions(startToCloseTimeout = 30.seconds), + "input" +) +assertEquals("expected", result) + +// With retry options +val result = activityEnv.executeActivity( + MyActivity::processOrder, + KActivityOptions( + startToCloseTimeout = 30.seconds, + retryOptions = KRetryOptions(maximumAttempts = 3) + ), + orderData +) +``` + +### Testing Local Activities + +```kotlin +val result = activityEnv.executeLocalActivity( + MyActivity::validate, + KLocalActivityOptions(startToCloseTimeout = 10.seconds), + input +) +assertEquals("valid", result) +``` + +### Heartbeat Testing + +```kotlin +// Set heartbeat details for the next activity call (simulates retry with heartbeat) +activityEnv.setHeartbeatDetails("checkpoint-data") + +// Listen for heartbeats during activity execution +activityEnv.setActivityHeartbeatListener { details -> + println("Activity heartbeat: $details") +} + +// With complex types +activityEnv.setActivityHeartbeatListener { progress -> + println("Progress: ${progress.percentage}%") +} +``` + +### Cancellation Testing + +```kotlin +@Test +fun `test activity cancellation handling`() { + val activityEnv = KTestActivityEnvironment.newInstance() + activityEnv.registerActivitiesImplementations(MyActivitiesImpl()) + + // Request cancellation during activity execution + activityEnv.requestCancelActivity() + + assertThrows { + activityEnv.executeActivity( + MyActivity::longRunningTask, + KActivityOptions(startToCloseTimeout = 1.minutes) + ) + } +} +``` + +### Lifecycle + +```kotlin +// Auto-close with use {} +KTestActivityEnvironment.newInstance().use { activityEnv -> + // Test code +} + +// Manual close +activityEnv.close() +``` + +--- + +## 4. KTestActivityExtension (JUnit 5) + +A JUnit 5 extension for simplified activity testing. + +### Basic Usage + +```kotlin +class MyActivityTest { + companion object { + @JvmField + @RegisterExtension + val testActivity = kTestActivityExtension { + setActivityImplementations(MyActivitiesImpl()) + } + } + + @Test + fun `test activity`(activityEnv: KTestActivityEnvironment) { + val result = activityEnv.executeActivity( + MyActivity::doSomething, + KActivityOptions(startToCloseTimeout = 30.seconds), + "input" + ) + assertEquals("expected", result) + } + + @Test + fun `test with heartbeat`(activityEnv: KTestActivityEnvironment) { + activityEnv.setHeartbeatDetails("checkpoint") + val result = activityEnv.executeActivity( + MyActivity::resumableTask, + KActivityOptions( + startToCloseTimeout = 1.minutes, + heartbeatTimeout = 10.seconds + ) + ) + assertEquals("completed", result) + } +} +``` + +### Configuration + +```kotlin +val testActivity = kTestActivityExtension { + setActivityImplementations(MyActivitiesImpl()) + setSuspendActivityImplementations(MySuspendActivitiesImpl()) + + testEnvironmentOptions { + workflowClientOptions { + // Configuration + } + } +} +``` + +--- + +## 5. Testing Suspend Activities + +The Kotlin SDK provides first-class support for testing suspend activities. + +### Unit Testing Suspend Activities + +```kotlin +class SuspendActivityTest { + companion object { + @JvmField + @RegisterExtension + val testActivity = kTestActivityExtension { + setSuspendActivityImplementations(MySuspendActivitiesImpl()) + } + } + + @Test + fun `test suspend activity`(activityEnv: KTestActivityEnvironment) = runTest { + val result = activityEnv.executeActivity( + MySuspendActivities::fetchDataAsync, + KActivityOptions(startToCloseTimeout = 30.seconds), + "key" + ) + assertEquals("value", result) + } +} +``` + +### Integration Testing with Workflows + +```kotlin +class WorkflowWithSuspendActivitiesTest { + companion object { + @JvmField + @RegisterExtension + val testWorkflow = kTestWorkflowExtension { + registerWorkflowImplementationTypes() + setSuspendActivityImplementations(MySuspendActivitiesImpl()) + } + } + + @Test + fun `workflow calls suspend activities`(workflow: MyWorkflow) { + val result = workflow.processData("input") + assertEquals("processed", result) + } +} +``` + +--- + +## 6. Common Testing Patterns + +### Pattern 1: Basic Workflow Test + +```kotlin +class BasicWorkflowTest { + companion object { + @JvmField + @RegisterExtension + val testWorkflow = kTestWorkflowExtension { + registerWorkflowImplementationTypes() + setActivityImplementations(GreetingActivitiesImpl()) + } + } + + @Test + fun `greets user correctly`(workflow: GreetingWorkflow) { + val result = workflow.greet("World") + assertEquals("Hello, World!", result) + } +} +``` + +### Pattern 2: Testing Signals and Queries + +```kotlin +@Test +fun `handles signals correctly`( + workflow: OrderWorkflow, + testEnv: KTestWorkflowEnvironment, + client: KWorkflowClient +) = runBlocking { + // Start workflow asynchronously + val handle = client.startWorkflow( + OrderWorkflow::processOrder, + KWorkflowOptions( + taskQueue = "orders", + workflowId = "order-workflow-1" + ), + "order-123" + ) + + // Let workflow start + testEnv.sleep(1.seconds) + + // Query current state + val status = handle.query(OrderWorkflow::getStatus) + assertEquals("PENDING", status) + + // Send signal + handle.signal(OrderWorkflow::approve, "manager-1") + + // Wait for completion + val result = handle.result() + assertEquals("COMPLETED", result) +} +``` + +### Pattern 3: Testing Updates + +```kotlin +@Test +fun `handles updates correctly`( + workflow: CounterWorkflow, + testEnv: KTestWorkflowEnvironment, + client: KWorkflowClient +) = runBlocking { + val handle = client.startWorkflow( + CounterWorkflow::run, + KWorkflowOptions( + taskQueue = "counters", + workflowId = "counter-1" + ) + ) + + testEnv.sleep(1.seconds) + + // Execute update and get result + val newValue = handle.executeUpdate(CounterWorkflow::increment, 5) + assertEquals(5, newValue) + + val finalValue = handle.executeUpdate(CounterWorkflow::increment, 3) + assertEquals(8, finalValue) +} +``` + +### Pattern 4: Testing Time-Dependent Workflows + +```kotlin +@Test +@WorkflowInitialTime("2024-01-01T00:00:00Z") +fun `processes scheduled tasks`( + workflow: ScheduledWorkflow, + testEnv: KTestWorkflowEnvironment +) { + // Start workflow that waits for specific time + val future = async { workflow.waitUntilTime() } + + // Advance time by 1 hour (time skipping makes this instant) + testEnv.sleep(1.hours) + + val result = future.await() + assertEquals("executed at scheduled time", result) +} +``` + +### Pattern 5: Testing Retries and Failures + +```kotlin +@Test +fun `retries failed activities`( + testEnv: KTestWorkflowEnvironment, + worker: KWorker, + workflow: RetryWorkflow +) { + var attempts = 0 + val mockActivity = mock { + on { unreliableCall() } doAnswer { + attempts++ + if (attempts < 3) { + throw RuntimeException("Temporary failure") + } + "success" + } + } + + worker.registerActivitiesImplementations(mockActivity) + testEnv.start() + + val result = workflow.executeWithRetry() + + assertEquals("success", result) + assertEquals(3, attempts) +} +``` + +### Pattern 6: Testing Child Workflows + +```kotlin +@Test +fun `executes child workflows`(workflow: ParentWorkflow) { + val result = workflow.processWithChildren(listOf("a", "b", "c")) + assertEquals(listOf("processed-a", "processed-b", "processed-c"), result) +} +``` + +### Pattern 7: Testing Continue-As-New + +```kotlin +@Test +fun `continues as new after threshold`( + workflow: BatchWorkflow, + testEnv: KTestWorkflowEnvironment +) { + // Workflow that continues-as-new every 100 items + val result = workflow.processBatch((1..250).toList()) + + assertEquals(250, result.processedCount) + assertEquals(3, result.continuations) // 100 + 100 + 50 +} +``` + +### Pattern 8: Async Activity Completion + +```kotlin +@Test +fun `handles async activity completion`(activityEnv: KTestActivityEnvironment) { + activityEnv.registerActivitiesImplementations(AsyncActivityImpl()) + + // Activity signals async completion + assertThrows { + activityEnv.executeActivity( + AsyncActivity::startAsyncOperation, + KActivityOptions(startToCloseTimeout = 1.minutes) + ) + } +} +``` + +### Pattern 9: Testing with External Service + +```kotlin +class ExternalServiceTest { + companion object { + @JvmField + @RegisterExtension + val testWorkflow = kTestWorkflowExtension { + registerWorkflowImplementationTypes() + setActivityImplementations(MyActivitiesImpl()) + // Uses ClientConfigProfile.load() which reads from: + // - TEMPORAL_ADDRESS (service address) + // - TEMPORAL_NAMESPACE (namespace) + // - TEMPORAL_API_KEY (optional API key) + // - TEMPORAL_TLS_* (TLS configuration) + useExternalService() + } + } + + @Test + fun `works with real temporal service`(workflow: MyWorkflow) { + val result = workflow.execute("input") + assertEquals("expected", result) + } +} + +// Alternative: explicit address override +class ExplicitAddressTest { + companion object { + @JvmField + @RegisterExtension + val testWorkflow = kTestWorkflowExtension { + registerWorkflowImplementationTypes() + setActivityImplementations(MyActivitiesImpl()) + useExternalService("localhost:7233") // Explicit address + namespace = "test-namespace" + } + } +} +``` + +### Pattern 10: Diagnostics on Failure + +```kotlin +class DiagnosticTest { + companion object { + @JvmField + @RegisterExtension + val testWorkflow = kTestWorkflowExtension { + registerWorkflowImplementationTypes() + setActivityImplementations(MyActivitiesImpl()) + } + } + + @Test + fun `test with diagnostics on failure`( + workflow: MyWorkflow, + testEnv: KTestWorkflowEnvironment + ) { + try { + workflow.execute("input") + } catch (e: Exception) { + // Print workflow histories for debugging + println(testEnv.getDiagnostics()) + throw e + } + } +} +``` + +--- + +## 7. Builder Functions + +For cases where the DSL is not preferred, traditional builders are available: + +```kotlin +// Test environment +val testEnv = KTestWorkflowEnvironment.newInstance( + KTestEnvironmentOptions.newBuilder() + .setNamespace("test") + .setInitialTime(Instant.now()) + .setUseTimeskipping(true) + .build() +) + +// Test extension +val testWorkflow = KTestWorkflowExtension.newBuilder() + .registerWorkflowImplementationTypes(MyWorkflowImpl::class.java) + .setActivityImplementations(MyActivitiesImpl()) + .build() +``` + +--- + +## 8. Integration with kotlinx-coroutines-test + +The test framework integrates with `kotlinx-coroutines-test` for coroutine testing: + +```kotlin +@Test +fun `test with coroutine test utilities`( + workflow: MyWorkflow, + testEnv: KTestWorkflowEnvironment +) = runTest { + // Use kotlinx-coroutines-test utilities + val result = workflow.execute("input") + assertEquals("expected", result) +} +``` + +--- + +## 9. Summary: Java vs Kotlin Comparison + +| Java SDK | Kotlin SDK | Notes | +|----------|------------|-------| +| `TestWorkflowEnvironment.newInstance()` | `KTestWorkflowEnvironment.newInstance()` | Same pattern | +| `TestWorkflowEnvironment.newInstance(options)` | `KTestWorkflowEnvironment.newInstance { }` | DSL builder | +| `TestWorkflowExtension.newBuilder()...build()` | `kTestWorkflowExtension { }` | DSL builder | +| `testEnv.newWorker()` returns `Worker` | `testEnv.newWorker()` returns `KWorker` | Kotlin wrapper | +| `Worker` | `KWorker` (with `worker` property for interop) | Kotlin-idiomatic registration | +| `testEnv.sleep(Duration)` | `testEnv.sleep(Duration)` | Suspend function, supports kotlin.time | +| `testEnv.currentTimeMillis()` | `testEnv.currentTimeMillis` / `testEnv.currentTime` | Property access + Instant | +| `TestActivityEnvironment` | `KTestActivityEnvironment` | Same pattern | +| `activityEnv.newActivityStub(T.class, options)` | `activityEnv.executeActivity(T::method, options, args)` | Handle approach (consistent with SDK) | +| `testEnv.getWorkflowClient()` | `testEnv.workflowClient` | Property access | +| N/A | `setSuspendActivityImplementations()` | Kotlin-specific | +| Builder pattern | DSL + Builder pattern | Both available | + +--- + +## 10. External Service Configuration + +When using `useExternalService()` without an explicit address, the test framework uses `ClientConfigProfile.load()` to load configuration from environment variables and config files. + +### Environment Variables + +| Variable | Description | +|----------|-------------| +| `TEMPORAL_ADDRESS` | Service address (e.g., `localhost:7233` or `myns.tmprl.cloud:7233`) | +| `TEMPORAL_NAMESPACE` | Namespace to use | +| `TEMPORAL_API_KEY` | API key for authentication (Temporal Cloud) | +| `TEMPORAL_TLS` | Enable TLS (`true`/`false`) | +| `TEMPORAL_TLS_CLIENT_CERT_PATH` | Path to client certificate file | +| `TEMPORAL_TLS_CLIENT_KEY_PATH` | Path to client key file | +| `TEMPORAL_TLS_SERVER_CA_CERT_PATH` | Path to CA certificate file | +| `TEMPORAL_TLS_SERVER_NAME` | Server name for TLS verification | +| `TEMPORAL_TLS_DISABLE_HOST_VERIFICATION` | Disable host verification (`true`/`false`) | +| `TEMPORAL_PROFILE` | Config file profile to use (default: `default`) | +| `TEMPORAL_GRPC_META_*` | Custom gRPC metadata (e.g., `TEMPORAL_GRPC_META_MY_HEADER=value`) | + +### Example: Running Tests Against Temporal Cloud + +```bash +export TEMPORAL_ADDRESS="myns.tmprl.cloud:7233" +export TEMPORAL_NAMESPACE="myns" +export TEMPORAL_API_KEY="my-api-key" + +./gradlew test +``` + +### Example: Running Tests Against Local Server with mTLS + +```bash +export TEMPORAL_ADDRESS="localhost:7233" +export TEMPORAL_NAMESPACE="default" +export TEMPORAL_TLS=true +export TEMPORAL_TLS_CLIENT_CERT_PATH="/path/to/client.pem" +export TEMPORAL_TLS_CLIENT_KEY_PATH="/path/to/client.key" +export TEMPORAL_TLS_SERVER_CA_CERT_PATH="/path/to/ca.pem" + +./gradlew test +``` + +--- + +## 11. Dependencies + +```kotlin +// build.gradle.kts +dependencies { + testImplementation("io.temporal:temporal-kotlin-testing:VERSION") + + // JUnit 5 + testImplementation("org.junit.jupiter:junit-jupiter:5.10.0") + + // Coroutine testing (optional) + testImplementation("org.jetbrains.kotlinx:kotlinx-coroutines-test:1.7.3") + + // Mocking (optional) + testImplementation("org.mockito.kotlin:mockito-kotlin:5.1.0") +} +``` + +--- + +## 12. Implementation Notes + +1. **KTestWorkflowEnvironment** wraps `TestWorkflowEnvironment` and returns `KWorkflowClient` +2. **KWorker** wraps `Worker` with Kotlin-idiomatic registration APIs; exposes underlying `Worker` for interop +3. **Time functions** use `kotlin.time.Duration` with `java.time.Duration` overloads +4. **Suspend functions** for `sleep()` and `awaitTermination()` +5. **Extension functions** provide Kotlin-idiomatic APIs on Java types +6. **DSL builders** internally use Java builders for configuration +7. **Parameter injection** in extensions works via JUnit 5's `ParameterResolver` diff --git a/kotlin/implementation/test-framework-implementation-design.md b/kotlin/implementation/test-framework-implementation-design.md new file mode 100644 index 0000000..ebed3c6 --- /dev/null +++ b/kotlin/implementation/test-framework-implementation-design.md @@ -0,0 +1,1756 @@ +# Kotlin SDK Test Framework Implementation Design + +## Overview + +This document describes the implementation design for the Kotlin SDK test framework based on the [Test Framework Design API](./test-framework-design.md). The implementation wraps the Java SDK's test framework while providing Kotlin-idiomatic APIs. + +## Package Structure + +``` +io.temporal.kotlin.testing/ +├── KTestWorkflowEnvironment.kt # Main test environment +├── KTestWorkflowExtension.kt # JUnit 5 workflow extension +├── KTestActivityEnvironment.kt # Activity test environment +├── KTestActivityExtension.kt # JUnit 5 activity extension +├── KTestEnvironmentOptions.kt # Configuration options +├── KWorker.kt # Kotlin worker wrapper +├── WorkflowInitialTime.kt # Annotation for test initial time +└── internal/ + └── KTestActivityEnvironmentInternal.kt # Internal implementation +``` + +--- + +## 1. KTestEnvironmentOptions + +**Purpose:** Configuration options for test environments with DSL support. + +### Implementation + +```kotlin +package io.temporal.kotlin.testing + +import com.uber.m3.tally.Scope +import io.temporal.api.enums.v1.IndexedValueType +import io.temporal.client.WorkflowClientOptions +import io.temporal.kotlin.TemporalDsl +import io.temporal.serviceclient.WorkflowServiceStubsOptions +import io.temporal.testing.TestEnvironmentOptions +import io.temporal.worker.WorkerFactoryOptions +import java.time.Instant + +/** + * Kotlin DSL builder for test environment options. + */ +@TemporalDsl +public class KTestEnvironmentOptionsBuilder internal constructor() { + + /** Namespace to use for testing. Default: "UnitTest" */ + public var namespace: String = "UnitTest" + + /** Initial time for the workflow virtual clock. Default: current time */ + public var initialTime: Instant? = null + + /** Whether to enable time skipping. Default: true */ + public var useTimeskipping: Boolean = true + + /** Whether to use external Temporal service. Default: false (in-memory) */ + public var useExternalService: Boolean = false + + /** Target endpoint for external service. */ + public var target: String? = null + + /** Metrics scope for reporting. */ + public var metricsScope: Scope? = null + + private var workerFactoryOptionsBuilder: (WorkerFactoryOptions.Builder.() -> Unit)? = null + private var workflowClientOptionsBuilder: (WorkflowClientOptions.Builder.() -> Unit)? = null + private var workflowServiceStubsOptionsBuilder: (WorkflowServiceStubsOptions.Builder.() -> Unit)? = null + private val searchAttributes: MutableMap = mutableMapOf() + + /** + * Configure WorkerFactoryOptions. + */ + public fun workerFactoryOptions(block: WorkerFactoryOptions.Builder.() -> Unit) { + workerFactoryOptionsBuilder = block + } + + /** + * Configure WorkflowClientOptions. + */ + public fun workflowClientOptions(block: WorkflowClientOptions.Builder.() -> Unit) { + workflowClientOptionsBuilder = block + } + + /** + * Configure WorkflowServiceStubsOptions. + */ + public fun workflowServiceStubsOptions(block: WorkflowServiceStubsOptions.Builder.() -> Unit) { + workflowServiceStubsOptionsBuilder = block + } + + /** + * Register search attributes. + */ + public fun searchAttributes(block: SearchAttributesBuilder.() -> Unit) { + SearchAttributesBuilder(searchAttributes).apply(block) + } + + @TemporalDsl + public class SearchAttributesBuilder internal constructor( + private val attributes: MutableMap + ) { + public fun register(name: String, type: IndexedValueType) { + attributes[name] = type + } + } + + internal fun build(): TestEnvironmentOptions { + val builder = TestEnvironmentOptions.newBuilder() + + // Apply namespace via WorkflowClientOptions + val clientOptions = WorkflowClientOptions.newBuilder().apply { + setNamespace(namespace) + workflowClientOptionsBuilder?.invoke(this) + }.build() + builder.setWorkflowClientOptions(clientOptions) + + // Apply other options + workerFactoryOptionsBuilder?.let { block -> + builder.setWorkerFactoryOptions( + WorkerFactoryOptions.newBuilder().apply(block).build() + ) + } + + workflowServiceStubsOptionsBuilder?.let { block -> + builder.setWorkflowServiceStubsOptions( + WorkflowServiceStubsOptions.newBuilder().apply(block).build() + ) + } + + initialTime?.let { builder.setInitialTime(it) } + builder.setUseTimeskipping(useTimeskipping) + builder.setUseExternalService(useExternalService) + target?.let { builder.setTarget(it) } + metricsScope?.let { builder.setMetricsScope(it) } + + searchAttributes.forEach { (name, type) -> + builder.registerSearchAttribute(name, type) + } + + return builder.build() + } +} + +/** + * Immutable configuration for test environments. + */ +public class KTestEnvironmentOptions private constructor( + internal val javaOptions: TestEnvironmentOptions +) { + + public companion object { + /** + * Create options using DSL builder. + */ + public fun newBuilder(block: KTestEnvironmentOptionsBuilder.() -> Unit = {}): KTestEnvironmentOptions { + return KTestEnvironmentOptions( + KTestEnvironmentOptionsBuilder().apply(block).build() + ) + } + + /** + * Get default options. + */ + public fun getDefaultInstance(): KTestEnvironmentOptions { + return KTestEnvironmentOptions(TestEnvironmentOptions.getDefaultInstance()) + } + } +} +``` + +### Key Design Decisions + +1. **DSL Builder Pattern**: Uses `@TemporalDsl` annotation for type-safe DSL +2. **Wraps Java Options**: Internally converts to `TestEnvironmentOptions` +3. **Nested DSL Builders**: Supports nested configuration for search attributes + +--- + +## 2. KWorker + +**Purpose:** Kotlin wrapper for Worker with idiomatic registration APIs. + +### Implementation + +```kotlin +package io.temporal.kotlin.testing + +import io.temporal.kotlin.TemporalDsl +import io.temporal.kotlin.activity.SuspendActivityWrapper +import io.temporal.worker.Worker +import io.temporal.worker.WorkerOptions +import io.temporal.worker.WorkflowImplementationOptions +import kotlin.reflect.KClass + +/** + * Kotlin worker that provides idiomatic APIs for registering + * Kotlin workflows and suspend activities. + * + * Use [worker] property for direct access to the underlying Java Worker + * when interoperating with Java workflows/activities. + */ +public class KWorker internal constructor( + /** The underlying Java Worker for interop scenarios */ + public val worker: Worker +) { + + /** + * Register Kotlin workflow implementation types using reified generics. + */ + public inline fun registerWorkflowImplementationTypes() { + worker.registerWorkflowImplementationTypes(T::class.java) + } + + /** + * Register Kotlin workflow implementation types using KClass. + */ + public fun registerWorkflowImplementationTypes(vararg workflowClasses: KClass<*>) { + worker.registerWorkflowImplementationTypes( + *workflowClasses.map { it.java }.toTypedArray() + ) + } + + /** + * Register Kotlin workflow implementation types with options using KClass. + */ + public fun registerWorkflowImplementationTypes( + options: WorkflowImplementationOptions, + vararg workflowClasses: KClass<*> + ) { + worker.registerWorkflowImplementationTypes( + options, + *workflowClasses.map { it.java }.toTypedArray() + ) + } + + /** + * Register Kotlin workflow implementation types with options DSL. + */ + public inline fun registerWorkflowImplementationTypes( + options: WorkflowImplementationOptions.Builder.() -> Unit + ) { + val opts = WorkflowImplementationOptions.newBuilder().apply(options).build() + worker.registerWorkflowImplementationTypes(opts, T::class.java) + } + + /** + * Register activity implementations. + * Works with both regular and suspend activity implementations. + */ + public fun registerActivitiesImplementations(vararg activities: Any) { + worker.registerActivitiesImplementations(*activities) + } + + /** + * Register suspend activity implementations. + * Wraps suspend functions for execution in the Temporal activity context. + * + * @param activities Activity implementation objects containing suspend functions + */ + public fun registerSuspendActivities(vararg activities: Any) { + activities.forEach { activity -> + val wrapper = SuspendActivityWrapper.wrap(activity) + worker.registerActivitiesImplementations(wrapper) + } + } + + /** + * Register Nexus service implementations. + */ + public fun registerNexusServiceImplementations(vararg services: Any) { + worker.registerNexusServiceImplementation(*services) + } +} +``` + +### Key Design Decisions + +1. **Exposes Underlying Worker**: Via `worker` property for interop +2. **Reified Generics**: For type-safe registration without reflection +3. **KClass Support**: For vararg registration patterns +4. **Suspend Activity Support**: Via `SuspendActivityWrapper` integration + +--- + +## 3. KTestWorkflowEnvironment + +**Purpose:** Main test environment providing in-memory Temporal service with time skipping. + +### Implementation + +```kotlin +package io.temporal.kotlin.testing + +import io.temporal.api.enums.v1.IndexedValueType +import io.temporal.api.nexus.v1.Endpoint +import io.temporal.kotlin.client.KWorkflowClient +import io.temporal.kotlin.toJavaDuration +import io.temporal.kotlin.worker.KotlinPlugin +import io.temporal.serviceclient.OperatorServiceStubs +import io.temporal.serviceclient.WorkflowServiceStubs +import io.temporal.testing.TestWorkflowEnvironment +import io.temporal.worker.Worker +import io.temporal.worker.WorkerOptions +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.withContext +import java.io.Closeable +import java.time.Duration +import java.time.Instant +import java.util.concurrent.TimeUnit +import kotlin.time.Duration as KDuration +import kotlin.time.toJavaDuration as kotlinToJava + +/** + * Kotlin test environment for workflow unit testing. + * + * Provides an in-memory Temporal service with automatic time skipping, + * allowing workflows that run for hours/days to be tested in milliseconds. + * + * Example: + * ```kotlin + * val testEnv = KTestWorkflowEnvironment.newInstance { + * namespace = "test-namespace" + * initialTime = Instant.parse("2024-01-01T00:00:00Z") + * } + * + * val worker = testEnv.newWorker("task-queue") + * worker.registerWorkflowImplementationTypes() + * worker.registerActivitiesImplementations(MyActivitiesImpl()) + * + * testEnv.start() + * + * val result = testEnv.workflowClient.executeWorkflow( + * MyWorkflow::execute, + * KWorkflowOptions(taskQueue = "task-queue"), + * "input" + * ) + * + * testEnv.close() + * ``` + */ +public class KTestWorkflowEnvironment private constructor( + private val testEnvironment: TestWorkflowEnvironment +) : Closeable { + + /** + * The Kotlin workflow client for interacting with workflows. + */ + public val workflowClient: KWorkflowClient by lazy { + KWorkflowClient(testEnvironment.workflowClient) + } + + /** + * Current test time in milliseconds since epoch. + * May differ from system time due to time skipping. + */ + public val currentTimeMillis: Long + get() = testEnvironment.currentTimeMillis() + + /** + * Current test time as an Instant. + */ + public val currentTime: Instant + get() = Instant.ofEpochMilli(currentTimeMillis) + + /** + * The namespace used by this test environment. + */ + public val namespace: String + get() = testEnvironment.namespace + + /** + * Whether the workers have been started. + */ + public val isStarted: Boolean + get() = testEnvironment.isStarted + + /** + * Whether shutdown has been initiated. + */ + public val isShutdown: Boolean + get() = testEnvironment.isShutdown + + /** + * Whether all workers have terminated. + */ + public val isTerminated: Boolean + get() = testEnvironment.isTerminated + + /** + * Access to WorkflowServiceStubs for advanced scenarios. + */ + public val workflowServiceStubs: WorkflowServiceStubs + get() = testEnvironment.workflowServiceStubs + + /** + * Access to OperatorServiceStubs for advanced scenarios. + */ + public val operatorServiceStubs: OperatorServiceStubs + get() = testEnvironment.operatorServiceStubs + + /** + * Create a new Kotlin worker for the specified task queue. + * + * @param taskQueue The task queue name + * @return A KWorker instance + */ + public fun newWorker(taskQueue: String): KWorker { + return KWorker(testEnvironment.newWorker(taskQueue)) + } + + /** + * Create a new Kotlin worker with options. + * + * @param taskQueue The task queue name + * @param options DSL builder for WorkerOptions + * @return A KWorker instance + */ + public fun newWorker( + taskQueue: String, + options: WorkerOptions.Builder.() -> Unit + ): KWorker { + val workerOptions = WorkerOptions.newBuilder().apply(options).build() + return KWorker(testEnvironment.newWorker(taskQueue, workerOptions)) + } + + /** + * Create a new Kotlin worker with WorkerOptions. + * + * @param taskQueue The task queue name + * @param options WorkerOptions instance + * @return A KWorker instance + */ + public fun newWorker(taskQueue: String, options: WorkerOptions): KWorker { + return KWorker(testEnvironment.newWorker(taskQueue, options)) + } + + /** + * Start all registered workers. + */ + public fun start() { + testEnvironment.start() + } + + /** + * Sleep for the specified duration with time skipping. + * This is a suspend function that advances test time without blocking. + * + * @param duration Kotlin Duration to sleep + */ + public suspend fun sleep(duration: KDuration) { + withContext(Dispatchers.IO) { + testEnvironment.sleep(duration.kotlinToJava()) + } + } + + /** + * Sleep for the specified duration with time skipping. + * + * @param duration Java Duration to sleep + */ + public suspend fun sleep(duration: Duration) { + withContext(Dispatchers.IO) { + testEnvironment.sleep(duration) + } + } + + /** + * Register a callback to execute after the specified delay in test time. + * + * @param delay Kotlin Duration delay + * @param callback The callback to execute + */ + public fun registerDelayedCallback(delay: KDuration, callback: () -> Unit) { + testEnvironment.registerDelayedCallback(delay.kotlinToJava()) { callback() } + } + + /** + * Register a callback to execute after the specified delay in test time. + * + * @param delay Java Duration delay + * @param callback The callback to execute + */ + public fun registerDelayedCallback(delay: Duration, callback: () -> Unit) { + testEnvironment.registerDelayedCallback(delay) { callback() } + } + + /** + * Register a search attribute with the test server. + * + * @param name Search attribute name + * @param type Search attribute type + * @return true if registered, false if already exists + */ + public fun registerSearchAttribute(name: String, type: IndexedValueType): Boolean { + return testEnvironment.registerSearchAttribute(name, type) + } + + /** + * Create a Nexus endpoint for testing. + * + * @param name Endpoint name + * @param taskQueue Task queue for the endpoint + * @return The created Endpoint + */ + public fun createNexusEndpoint(name: String, taskQueue: String): Endpoint { + return testEnvironment.createNexusEndpoint(name, taskQueue) + } + + /** + * Delete a Nexus endpoint. + * + * @param endpoint The endpoint to delete + */ + public fun deleteNexusEndpoint(endpoint: Endpoint) { + testEnvironment.deleteNexusEndpoint(endpoint) + } + + /** + * Get diagnostic information including workflow histories. + * Useful for debugging test failures. + */ + public fun getDiagnostics(): String { + return testEnvironment.diagnostics + } + + /** + * Initiate graceful shutdown. + * Workers stop accepting new tasks but complete in-progress work. + */ + public fun shutdown() { + testEnvironment.shutdown() + } + + /** + * Initiate immediate shutdown. + * Attempts to stop all processing immediately. + */ + public fun shutdownNow() { + testEnvironment.shutdownNow() + } + + /** + * Wait for all workers to terminate. + * + * @param timeout Maximum time to wait + */ + public suspend fun awaitTermination(timeout: KDuration) { + withContext(Dispatchers.IO) { + testEnvironment.awaitTermination( + timeout.inWholeMilliseconds, + TimeUnit.MILLISECONDS + ) + } + } + + /** + * Wait for all workers to terminate. + * + * @param timeout Maximum time to wait (Java Duration) + */ + public suspend fun awaitTermination(timeout: Duration) { + withContext(Dispatchers.IO) { + testEnvironment.awaitTermination( + timeout.toMillis(), + TimeUnit.MILLISECONDS + ) + } + } + + /** + * Close the test environment. + * Calls shutdownNow() and awaitTermination(). + */ + override fun close() { + testEnvironment.close() + } + + public companion object { + /** + * Create a new test environment with default options. + */ + public fun newInstance(): KTestWorkflowEnvironment { + return KTestWorkflowEnvironment(TestWorkflowEnvironment.newInstance()) + } + + /** + * Create a new test environment with DSL configuration. + * + * Example: + * ```kotlin + * val testEnv = KTestWorkflowEnvironment.newInstance { + * namespace = "test-namespace" + * initialTime = Instant.parse("2024-01-01T00:00:00Z") + * useTimeskipping = true + * + * workerFactoryOptions { + * maxWorkflowThreadCount = 800 + * } + * + * searchAttributes { + * register("CustomKeyword", IndexedValueType.INDEXED_VALUE_TYPE_KEYWORD) + * } + * } + * ``` + */ + public fun newInstance( + options: KTestEnvironmentOptionsBuilder.() -> Unit + ): KTestWorkflowEnvironment { + val javaOptions = KTestEnvironmentOptionsBuilder().apply(options).build() + return KTestWorkflowEnvironment(TestWorkflowEnvironment.newInstance(javaOptions)) + } + + /** + * Create a new test environment with pre-built options. + */ + public fun newInstance(options: KTestEnvironmentOptions): KTestWorkflowEnvironment { + return KTestWorkflowEnvironment( + TestWorkflowEnvironment.newInstance(options.javaOptions) + ) + } + } +} + +/** + * Use the test environment with automatic cleanup. + * + * Example: + * ```kotlin + * KTestWorkflowEnvironment.newInstance().use { testEnv -> + * // Test code here + * } // Automatically closed + * ``` + */ +public inline fun KTestWorkflowEnvironment.use(block: (KTestWorkflowEnvironment) -> T): T { + return try { + block(this) + } finally { + close() + } +} +``` + +### Key Design Decisions + +1. **Wraps TestWorkflowEnvironment**: Provides Kotlin-idiomatic API over Java implementation +2. **Suspend Functions**: `sleep()` and `awaitTermination()` are suspend functions +3. **Dual Duration Support**: Accepts both `kotlin.time.Duration` and `java.time.Duration` +4. **Property Access**: Uses Kotlin properties instead of getter methods +5. **DSL Builder**: Static `newInstance { }` DSL for configuration +6. **KWorkflowClient Integration**: Returns `KWorkflowClient` for workflow operations +7. **KWorker Return**: `newWorker()` returns `KWorker` instead of Java `Worker` + +--- + +## 4. WorkflowInitialTime Annotation + +**Purpose:** Override initial time for specific test methods. + +### Implementation + +```kotlin +package io.temporal.kotlin.testing + +/** + * Annotation to specify the initial time for a workflow test. + * Overrides the initial time configured in the extension. + * + * Example: + * ```kotlin + * @Test + * @WorkflowInitialTime("2024-06-15T12:00:00Z") + * fun `test with specific initial time`(workflow: MyWorkflow) { + * // Test runs with June 15, 2024 as initial time + * } + * ``` + */ +@Target(AnnotationTarget.FUNCTION) +@Retention(AnnotationRetention.RUNTIME) +public annotation class WorkflowInitialTime( + /** + * ISO-8601 formatted timestamp for the initial time. + * Example: "2024-01-01T00:00:00Z" + */ + val value: String +) +``` + +--- + +## 5. KTestWorkflowExtension + +**Purpose:** JUnit 5 extension for simplified workflow testing. + +### Implementation + +```kotlin +package io.temporal.kotlin.testing + +import io.temporal.api.enums.v1.IndexedValueType +import io.temporal.client.WorkflowClientOptions +import io.temporal.client.WorkflowOptions +import io.temporal.common.metadata.POJOWorkflowImplMetadata +import io.temporal.common.metadata.POJOWorkflowInterfaceMetadata +import io.temporal.kotlin.TemporalDsl +import io.temporal.kotlin.client.KWorkflowClient +import io.temporal.kotlin.client.KWorkflowOptions +import io.temporal.testing.TestWorkflowEnvironment +import io.temporal.worker.Worker +import io.temporal.worker.WorkerFactoryOptions +import io.temporal.worker.WorkerOptions +import io.temporal.worker.WorkflowImplementationOptions +import io.temporal.workflow.DynamicWorkflow +import org.junit.jupiter.api.extension.* +import org.junit.platform.commons.support.AnnotationSupport +import java.lang.reflect.Constructor +import java.lang.reflect.Parameter +import java.time.Instant +import java.util.* +import kotlin.reflect.KClass + +/** + * JUnit 5 extension for testing Temporal workflows with Kotlin-idiomatic APIs. + * + * Example: + * ```kotlin + * class MyWorkflowTest { + * companion object { + * @JvmField + * @RegisterExtension + * val testWorkflow = kTestWorkflowExtension { + * registerWorkflowImplementationTypes() + * setActivityImplementations(MyActivitiesImpl()) + * } + * } + * + * @Test + * fun `test workflow execution`(workflow: MyWorkflow) { + * val result = workflow.execute("input") + * assertEquals("expected", result) + * } + * } + * ``` + */ +public class KTestWorkflowExtension private constructor( + private val config: ExtensionConfig +) : ParameterResolver, TestWatcher, BeforeEachCallback, AfterEachCallback { + + private data class ExtensionConfig( + val namespace: String, + val workflowTypes: Map, WorkflowImplementationOptions>, + val activityImplementations: Array, + val suspendActivityImplementations: Array, + val nexusServiceImplementations: Array, + val workerOptions: WorkerOptions, + val workerFactoryOptions: WorkerFactoryOptions?, + val workflowClientOptions: WorkflowClientOptions?, + val useExternalService: Boolean, + val target: String?, + val doNotStart: Boolean, + val initialTimeMillis: Long, + val useTimeskipping: Boolean, + val searchAttributes: Map + ) + + private val supportedParameterTypes = mutableSetOf>().apply { + add(KTestWorkflowEnvironment::class.java) + add(KWorkflowClient::class.java) + add(KWorkflowOptions::class.java) + add(KWorker::class.java) + add(Worker::class.java) + // Add workflow interface types + config.workflowTypes.keys.forEach { workflowType -> + if (!DynamicWorkflow::class.java.isAssignableFrom(workflowType)) { + val metadata = POJOWorkflowImplMetadata.newInstance(workflowType) + metadata.workflowInterfaces.forEach { add(it.interfaceClass) } + } + } + } + + private val includesDynamicWorkflow = config.workflowTypes.keys.any { + DynamicWorkflow::class.java.isAssignableFrom(it) + } + + override fun supportsParameter( + parameterContext: ParameterContext, + extensionContext: ExtensionContext + ): Boolean { + val parameter = parameterContext.parameter + if (parameter.declaringExecutable is Constructor<*>) return false + + val parameterType = parameter.type + if (supportedParameterTypes.contains(parameterType)) return true + + if (!includesDynamicWorkflow) return false + + return try { + POJOWorkflowInterfaceMetadata.newInstance(parameterType) + true + } catch (e: Exception) { + false + } + } + + override fun resolveParameter( + parameterContext: ParameterContext, + extensionContext: ExtensionContext + ): Any { + val parameterType = parameterContext.parameter.type + val store = getStore(extensionContext) + + return when (parameterType) { + KTestWorkflowEnvironment::class.java -> getTestEnvironment(store) + KWorkflowClient::class.java -> getTestEnvironment(store).workflowClient + KWorkflowOptions::class.java -> getWorkflowOptions(store) + KWorker::class.java -> getKWorker(store) + Worker::class.java -> getKWorker(store).worker + else -> { + // Create workflow stub + val testEnv = getTestEnvironment(store) + val options = getWorkflowOptions(store) + testEnv.workflowClient.workflowClient.newWorkflowStub( + parameterType, + options.toJavaOptions() + ) + } + } + } + + override fun beforeEach(context: ExtensionContext) { + val currentInitialTimeMillis = AnnotationSupport.findAnnotation( + context.element, + WorkflowInitialTime::class.java + ).map { Instant.parse(it.value).toEpochMilli() } + .orElse(config.initialTimeMillis) + + val testEnvOptions = io.temporal.testing.TestEnvironmentOptions.newBuilder().apply { + setUseExternalService(config.useExternalService) + setUseTimeskipping(config.useTimeskipping) + config.target?.let { setTarget(it) } + if (currentInitialTimeMillis > 0) setInitialTimeMillis(currentInitialTimeMillis) + + val clientOptions = (config.workflowClientOptions?.let { + WorkflowClientOptions.newBuilder(it) + } ?: WorkflowClientOptions.newBuilder()) + .setNamespace(config.namespace) + .build() + setWorkflowClientOptions(clientOptions) + + config.workerFactoryOptions?.let { setWorkerFactoryOptions(it) } + config.searchAttributes.forEach { (name, type) -> + registerSearchAttribute(name, type) + } + }.build() + + val javaTestEnv = TestWorkflowEnvironment.newInstance(testEnvOptions) + val testEnvironment = KTestWorkflowEnvironment.newInstance { + // Options already applied to javaTestEnv + } + + // Create task queue + val taskQueue = "WorkflowTest-${context.displayName}-${context.uniqueId}" + val worker = javaTestEnv.newWorker(taskQueue, config.workerOptions) + val kWorker = KWorker(worker) + + // Register workflows + config.workflowTypes.forEach { (wfType, options) -> + worker.registerWorkflowImplementationTypes(options, wfType) + } + + // Register activities + worker.registerActivitiesImplementations(*config.activityImplementations) + + // Register suspend activities + config.suspendActivityImplementations.forEach { activity -> + kWorker.registerSuspendActivities(activity) + } + + // Register Nexus services + if (config.nexusServiceImplementations.isNotEmpty()) { + worker.registerNexusServiceImplementation(*config.nexusServiceImplementations) + } + + if (!config.doNotStart) { + javaTestEnv.start() + } + + // Store in extension context + val store = getStore(context) + store.put(TEST_ENVIRONMENT_KEY, KTestWorkflowEnvironment.Companion::class.java.getDeclaredMethod( + "newInstance" + ).let { + // Wrap the Java test environment + val field = KTestWorkflowEnvironment::class.java.getDeclaredField("testEnvironment") + field.isAccessible = true + KTestWorkflowEnvironment::class.java.getDeclaredConstructor( + TestWorkflowEnvironment::class.java + ).apply { isAccessible = true }.newInstance(javaTestEnv) + }) + store.put(KWORKER_KEY, kWorker) + store.put(WORKFLOW_OPTIONS_KEY, KWorkflowOptions(taskQueue = taskQueue)) + } + + override fun afterEach(context: ExtensionContext) { + val testEnv = getStore(context).get(TEST_ENVIRONMENT_KEY, KTestWorkflowEnvironment::class.java) + testEnv?.close() + } + + override fun testFailed(context: ExtensionContext, cause: Throwable) { + val testEnv = getStore(context).get(TEST_ENVIRONMENT_KEY, KTestWorkflowEnvironment::class.java) + testEnv?.let { + System.err.println("Workflow execution histories:\n${it.getDiagnostics()}") + } + } + + private fun getStore(context: ExtensionContext): ExtensionContext.Store { + val namespace = ExtensionContext.Namespace.create( + KTestWorkflowExtension::class.java, + context.requiredTestMethod + ) + return context.getStore(namespace) + } + + private fun getTestEnvironment(store: ExtensionContext.Store): KTestWorkflowEnvironment { + return store.get(TEST_ENVIRONMENT_KEY, KTestWorkflowEnvironment::class.java) + ?: throw IllegalStateException("Test environment not initialized") + } + + private fun getKWorker(store: ExtensionContext.Store): KWorker { + return store.get(KWORKER_KEY, KWorker::class.java) + ?: throw IllegalStateException("Worker not initialized") + } + + private fun getWorkflowOptions(store: ExtensionContext.Store): KWorkflowOptions { + return store.get(WORKFLOW_OPTIONS_KEY, KWorkflowOptions::class.java) + ?: throw IllegalStateException("Workflow options not initialized") + } + + public companion object { + private const val TEST_ENVIRONMENT_KEY = "testEnvironment" + private const val KWORKER_KEY = "kWorker" + private const val WORKFLOW_OPTIONS_KEY = "workflowOptions" + + /** + * Create a new extension builder. + */ + public fun newBuilder(): Builder = Builder() + } + + /** + * Builder for KTestWorkflowExtension. + */ + @TemporalDsl + public class Builder internal constructor() { + private var namespace: String = "UnitTest" + private val workflowTypes = mutableMapOf, WorkflowImplementationOptions>() + private var activityImplementations: Array = emptyArray() + private var suspendActivityImplementations: Array = emptyArray() + private var nexusServiceImplementations: Array = emptyArray() + private var workerOptions: WorkerOptions = WorkerOptions.getDefaultInstance() + private var workerFactoryOptions: WorkerFactoryOptions? = null + private var workflowClientOptions: WorkflowClientOptions? = null + private var useExternalService: Boolean = false + private var target: String? = null + private var doNotStart: Boolean = false + private var initialTimeMillis: Long = 0 + private var useTimeskipping: Boolean = true + private val searchAttributes = mutableMapOf() + + /** Set the namespace. Default: "UnitTest" */ + public var namespace_: String + get() = namespace + set(value) { namespace = value } + + /** Initial time for the test. */ + public var initialTime: Instant? = null + set(value) { + field = value + initialTimeMillis = value?.toEpochMilli() ?: 0 + } + + /** Whether to enable time skipping. Default: true */ + public var useTimeskipping_: Boolean + get() = useTimeskipping + set(value) { useTimeskipping = value } + + /** Whether to defer worker startup. Default: false */ + public var doNotStart_: Boolean + get() = doNotStart + set(value) { doNotStart = value } + + /** + * Register workflow implementation types using reified generics. + */ + public inline fun registerWorkflowImplementationTypes() { + workflowTypes[T::class.java] = WorkflowImplementationOptions.newBuilder().build() + } + + /** + * Register workflow implementation types with options DSL. + */ + public inline fun registerWorkflowImplementationTypes( + options: WorkflowImplementationOptions.Builder.() -> Unit + ) { + workflowTypes[T::class.java] = WorkflowImplementationOptions.newBuilder().apply(options).build() + } + + /** + * Register workflow implementation types. + */ + public fun registerWorkflowImplementationTypes(vararg classes: Class<*>) { + val defaultOptions = WorkflowImplementationOptions.newBuilder().build() + classes.forEach { workflowTypes[it] = defaultOptions } + } + + /** + * Register workflow implementation types with options. + */ + public fun registerWorkflowImplementationTypes( + options: WorkflowImplementationOptions, + vararg classes: Class<*> + ) { + classes.forEach { workflowTypes[it] = options } + } + + /** + * Set activity implementations. + */ + public fun setActivityImplementations(vararg activities: Any) { + activityImplementations = arrayOf(*activities) + } + + /** + * Set suspend activity implementations. + */ + public fun setSuspendActivityImplementations(vararg activities: Any) { + suspendActivityImplementations = arrayOf(*activities) + } + + /** + * Set Nexus service implementations. + */ + public fun setNexusServiceImplementations(vararg services: Any) { + nexusServiceImplementations = arrayOf(*services) + } + + /** + * Configure worker options. + */ + public fun workerOptions(block: WorkerOptions.Builder.() -> Unit) { + workerOptions = WorkerOptions.newBuilder().apply(block).build() + } + + /** + * Configure worker factory options. + */ + public fun workerFactoryOptions(block: WorkerFactoryOptions.Builder.() -> Unit) { + workerFactoryOptions = WorkerFactoryOptions.newBuilder().apply(block).build() + } + + /** + * Configure workflow client options. + */ + public fun workflowClientOptions(block: WorkflowClientOptions.Builder.() -> Unit) { + workflowClientOptions = WorkflowClientOptions.newBuilder().apply(block).build() + } + + /** + * Use internal in-memory service (default). + */ + public fun useInternalService() { + useExternalService = false + target = null + } + + /** + * Use external Temporal service with ClientConfigProfile. + */ + public fun useExternalService() { + useExternalService = true + target = null + } + + /** + * Use external Temporal service with explicit address. + */ + public fun useExternalService(address: String) { + useExternalService = true + target = address + } + + /** + * Configure search attributes. + */ + public fun searchAttributes(block: SearchAttributesBuilder.() -> Unit) { + SearchAttributesBuilder(searchAttributes).apply(block) + } + + @TemporalDsl + public class SearchAttributesBuilder internal constructor( + private val attributes: MutableMap + ) { + public fun register(name: String, type: IndexedValueType) { + attributes[name] = type + } + } + + public fun build(): KTestWorkflowExtension { + return KTestWorkflowExtension( + ExtensionConfig( + namespace = namespace, + workflowTypes = workflowTypes.toMap(), + activityImplementations = activityImplementations, + suspendActivityImplementations = suspendActivityImplementations, + nexusServiceImplementations = nexusServiceImplementations, + workerOptions = workerOptions, + workerFactoryOptions = workerFactoryOptions, + workflowClientOptions = workflowClientOptions, + useExternalService = useExternalService, + target = target, + doNotStart = doNotStart, + initialTimeMillis = initialTimeMillis, + useTimeskipping = useTimeskipping, + searchAttributes = searchAttributes.toMap() + ) + ) + } + } +} + +/** + * DSL function to create a KTestWorkflowExtension. + * + * Example: + * ```kotlin + * companion object { + * @JvmField + * @RegisterExtension + * val testWorkflow = kTestWorkflowExtension { + * registerWorkflowImplementationTypes() + * setActivityImplementations(MyActivitiesImpl()) + * } + * } + * ``` + */ +public fun kTestWorkflowExtension( + block: KTestWorkflowExtension.Builder.() -> Unit +): KTestWorkflowExtension { + return KTestWorkflowExtension.newBuilder().apply(block).build() +} +``` + +### Key Design Decisions + +1. **DSL Function**: `kTestWorkflowExtension { }` top-level function +2. **Reified Generics**: `registerWorkflowImplementationTypes()` for type-safe registration +3. **Parameter Injection**: Supports `KTestWorkflowEnvironment`, `KWorkflowClient`, `KWorkflowOptions`, `KWorker`, `Worker`, and workflow stubs +4. **Initial Time Annotation**: Supports `@WorkflowInitialTime` per-test override +5. **Test Failure Diagnostics**: Automatically prints workflow histories on failure +6. **External Service Support**: `useExternalService()` and `useExternalService(address)` + +--- + +## 6. KTestActivityEnvironment + +**Purpose:** Environment for unit testing activity implementations in isolation. + +### Implementation + +```kotlin +package io.temporal.kotlin.testing + +import io.temporal.activity.ActivityOptions +import io.temporal.activity.LocalActivityOptions +import io.temporal.kotlin.TemporalDsl +import io.temporal.kotlin.activity.KActivityOptions +import io.temporal.kotlin.activity.KLocalActivityOptions +import io.temporal.kotlin.activity.SuspendActivityWrapper +import io.temporal.testing.TestActivityEnvironment +import io.temporal.workflow.Functions +import java.io.Closeable +import java.lang.reflect.Type +import kotlin.reflect.KFunction1 +import kotlin.reflect.KFunction2 +import kotlin.reflect.KFunction3 +import kotlin.reflect.KFunction4 +import kotlin.reflect.KSuspendFunction1 +import kotlin.reflect.KSuspendFunction2 +import kotlin.reflect.KSuspendFunction3 +import kotlin.reflect.KSuspendFunction4 +import kotlin.reflect.jvm.javaMethod + +/** + * Kotlin test environment for activity unit testing. + * + * Supports testing both regular and suspend activity implementations + * in isolation without needing workflows. + * + * Example: + * ```kotlin + * val activityEnv = KTestActivityEnvironment.newInstance() + * activityEnv.registerActivitiesImplementations(MyActivitiesImpl()) + * + * val result = activityEnv.executeActivity( + * MyActivities::doSomething, + * KActivityOptions(startToCloseTimeout = 30.seconds), + * "input" + * ) + * + * assertEquals("expected", result) + * activityEnv.close() + * ``` + */ +public class KTestActivityEnvironment private constructor( + private val testEnvironment: TestActivityEnvironment +) : Closeable { + + /** + * Register activity implementations. + */ + public fun registerActivitiesImplementations(vararg activities: Any) { + testEnvironment.registerActivitiesImplementations(*activities) + } + + /** + * Register suspend activity implementations. + */ + public fun registerSuspendActivities(vararg activities: Any) { + activities.forEach { activity -> + val wrapper = SuspendActivityWrapper.wrap(activity) + testEnvironment.registerActivitiesImplementations(wrapper) + } + } + + // ========== Execute Activity (1-4 args) ========== + + /** + * Execute an activity with no arguments. + */ + public fun executeActivity( + activity: KFunction1, + options: KActivityOptions + ): R { + val stub = createActivityStub(activity, options.toJavaOptions()) + @Suppress("UNCHECKED_CAST") + return activity.call(stub) as R + } + + /** + * Execute an activity with one argument. + */ + public fun executeActivity( + activity: KFunction2, + options: KActivityOptions, + arg1: A1 + ): R { + val stub = createActivityStub(activity, options.toJavaOptions()) + @Suppress("UNCHECKED_CAST") + return activity.call(stub, arg1) as R + } + + /** + * Execute an activity with two arguments. + */ + public fun executeActivity( + activity: KFunction3, + options: KActivityOptions, + arg1: A1, + arg2: A2 + ): R { + val stub = createActivityStub(activity, options.toJavaOptions()) + @Suppress("UNCHECKED_CAST") + return activity.call(stub, arg1, arg2) as R + } + + /** + * Execute an activity with three arguments. + */ + public fun executeActivity( + activity: KFunction4, + options: KActivityOptions, + arg1: A1, + arg2: A2, + arg3: A3 + ): R { + val stub = createActivityStub(activity, options.toJavaOptions()) + @Suppress("UNCHECKED_CAST") + return activity.call(stub, arg1, arg2, arg3) as R + } + + // ========== Execute Suspend Activity (1-4 args) ========== + + /** + * Execute a suspend activity with no arguments. + */ + @JvmName("executeSuspendActivity0") + public suspend fun executeActivity( + activity: KSuspendFunction1, + options: KActivityOptions + ): R { + val stub = createActivityStub(activity, options.toJavaOptions()) + @Suppress("UNCHECKED_CAST") + return activity.call(stub) as R + } + + /** + * Execute a suspend activity with one argument. + */ + @JvmName("executeSuspendActivity1") + public suspend fun executeActivity( + activity: KSuspendFunction2, + options: KActivityOptions, + arg1: A1 + ): R { + val stub = createActivityStub(activity, options.toJavaOptions()) + @Suppress("UNCHECKED_CAST") + return activity.call(stub, arg1) as R + } + + /** + * Execute a suspend activity with two arguments. + */ + @JvmName("executeSuspendActivity2") + public suspend fun executeActivity( + activity: KSuspendFunction3, + options: KActivityOptions, + arg1: A1, + arg2: A2 + ): R { + val stub = createActivityStub(activity, options.toJavaOptions()) + @Suppress("UNCHECKED_CAST") + return activity.call(stub, arg1, arg2) as R + } + + /** + * Execute a suspend activity with three arguments. + */ + @JvmName("executeSuspendActivity3") + public suspend fun executeActivity( + activity: KSuspendFunction4, + options: KActivityOptions, + arg1: A1, + arg2: A2, + arg3: A3 + ): R { + val stub = createActivityStub(activity, options.toJavaOptions()) + @Suppress("UNCHECKED_CAST") + return activity.call(stub, arg1, arg2, arg3) as R + } + + // ========== Execute Local Activity (1-4 args) ========== + + /** + * Execute a local activity with no arguments. + */ + public fun executeLocalActivity( + activity: KFunction1, + options: KLocalActivityOptions + ): R { + val stub = createLocalActivityStub(activity, options.toJavaOptions()) + @Suppress("UNCHECKED_CAST") + return activity.call(stub) as R + } + + /** + * Execute a local activity with one argument. + */ + public fun executeLocalActivity( + activity: KFunction2, + options: KLocalActivityOptions, + arg1: A1 + ): R { + val stub = createLocalActivityStub(activity, options.toJavaOptions()) + @Suppress("UNCHECKED_CAST") + return activity.call(stub, arg1) as R + } + + /** + * Execute a local activity with two arguments. + */ + public fun executeLocalActivity( + activity: KFunction3, + options: KLocalActivityOptions, + arg1: A1, + arg2: A2 + ): R { + val stub = createLocalActivityStub(activity, options.toJavaOptions()) + @Suppress("UNCHECKED_CAST") + return activity.call(stub, arg1, arg2) as R + } + + /** + * Execute a local activity with three arguments. + */ + public fun executeLocalActivity( + activity: KFunction4, + options: KLocalActivityOptions, + arg1: A1, + arg2: A2, + arg3: A3 + ): R { + val stub = createLocalActivityStub(activity, options.toJavaOptions()) + @Suppress("UNCHECKED_CAST") + return activity.call(stub, arg1, arg2, arg3) as R + } + + // ========== Heartbeat Testing ========== + + /** + * Set heartbeat details for the next activity execution. + * Simulates activity retry with heartbeat checkpoint. + */ + public fun setHeartbeatDetails(details: T) { + testEnvironment.setHeartbeatDetails(details) + } + + /** + * Set a listener for activity heartbeats. + */ + public inline fun setActivityHeartbeatListener( + noinline listener: (T) -> Unit + ) { + testEnvironment.setActivityHeartbeatListener( + T::class.java, + Functions.Proc1 { listener(it) } + ) + } + + /** + * Set a listener for activity heartbeats with type information. + */ + public fun setActivityHeartbeatListener( + detailsClass: Class, + detailsType: Type, + listener: (T) -> Unit + ) { + testEnvironment.setActivityHeartbeatListener( + detailsClass, + detailsType, + Functions.Proc1 { listener(it) } + ) + } + + // ========== Cancellation Testing ========== + + /** + * Request cancellation of the currently executing activity. + * Cancellation is delivered on the next heartbeat. + */ + public fun requestCancelActivity() { + testEnvironment.requestCancelActivity() + } + + // ========== Lifecycle ========== + + override fun close() { + testEnvironment.close() + } + + // ========== Internal Helpers ========== + + @Suppress("UNCHECKED_CAST") + private fun createActivityStub( + activity: kotlin.reflect.KFunction<*>, + options: ActivityOptions + ): T { + val activityClass = activity.javaMethod?.declaringClass + ?: throw IllegalArgumentException("Cannot determine activity interface") + return testEnvironment.newActivityStub(activityClass, options) as T + } + + @Suppress("UNCHECKED_CAST") + private fun createLocalActivityStub( + activity: kotlin.reflect.KFunction<*>, + options: LocalActivityOptions + ): T { + val activityClass = activity.javaMethod?.declaringClass + ?: throw IllegalArgumentException("Cannot determine activity interface") + return testEnvironment.newLocalActivityStub(activityClass, options, emptyMap()) as T + } + + public companion object { + /** + * Create a new activity test environment with default options. + */ + public fun newInstance(): KTestActivityEnvironment { + return KTestActivityEnvironment(TestActivityEnvironment.newInstance()) + } + + /** + * Create a new activity test environment with DSL configuration. + */ + public fun newInstance( + options: KTestEnvironmentOptionsBuilder.() -> Unit + ): KTestActivityEnvironment { + val javaOptions = KTestEnvironmentOptionsBuilder().apply(options).build() + return KTestActivityEnvironment(TestActivityEnvironment.newInstance(javaOptions)) + } + + /** + * Create a new activity test environment with pre-built options. + */ + public fun newInstance(options: KTestEnvironmentOptions): KTestActivityEnvironment { + return KTestActivityEnvironment( + TestActivityEnvironment.newInstance(options.javaOptions) + ) + } + } +} +``` + +### Key Design Decisions + +1. **Method Reference API**: `executeActivity(MyActivity::method, options, args)` +2. **Suspend Function Support**: Separate overloads for suspend activities +3. **Local Activity Support**: `executeLocalActivity` methods +4. **Heartbeat Testing**: `setHeartbeatDetails` and `setActivityHeartbeatListener` +5. **Cancellation Testing**: `requestCancelActivity()` + +--- + +## 7. KTestActivityExtension + +**Purpose:** JUnit 5 extension for simplified activity testing. + +### Implementation + +```kotlin +package io.temporal.kotlin.testing + +import io.temporal.kotlin.TemporalDsl +import io.temporal.kotlin.activity.SuspendActivityWrapper +import io.temporal.testing.TestActivityEnvironment +import io.temporal.testing.TestEnvironmentOptions +import org.junit.jupiter.api.extension.* +import java.lang.reflect.Constructor + +/** + * JUnit 5 extension for testing Temporal activities. + * + * Example: + * ```kotlin + * class MyActivityTest { + * companion object { + * @JvmField + * @RegisterExtension + * val testActivity = kTestActivityExtension { + * setActivityImplementations(MyActivitiesImpl()) + * } + * } + * + * @Test + * fun `test activity`(activityEnv: KTestActivityEnvironment) { + * val result = activityEnv.executeActivity( + * MyActivities::doSomething, + * KActivityOptions(startToCloseTimeout = 30.seconds), + * "input" + * ) + * assertEquals("expected", result) + * } + * } + * ``` + */ +public class KTestActivityExtension private constructor( + private val config: ExtensionConfig +) : ParameterResolver, BeforeEachCallback, AfterEachCallback { + + private data class ExtensionConfig( + val testEnvironmentOptions: TestEnvironmentOptions, + val activityImplementations: Array, + val suspendActivityImplementations: Array + ) + + override fun supportsParameter( + parameterContext: ParameterContext, + extensionContext: ExtensionContext + ): Boolean { + val parameter = parameterContext.parameter + if (parameter.declaringExecutable is Constructor<*>) return false + return parameter.type == KTestActivityEnvironment::class.java + } + + override fun resolveParameter( + parameterContext: ParameterContext, + extensionContext: ExtensionContext + ): Any { + return getStore(extensionContext).get( + TEST_ENVIRONMENT_KEY, + KTestActivityEnvironment::class.java + ) ?: throw IllegalStateException("Activity environment not initialized") + } + + override fun beforeEach(context: ExtensionContext) { + val javaEnv = TestActivityEnvironment.newInstance(config.testEnvironmentOptions) + + // Register regular activities + javaEnv.registerActivitiesImplementations(*config.activityImplementations) + + // Register suspend activities + config.suspendActivityImplementations.forEach { activity -> + val wrapper = SuspendActivityWrapper.wrap(activity) + javaEnv.registerActivitiesImplementations(wrapper) + } + + val kEnv = KTestActivityEnvironment::class.java + .getDeclaredConstructor(TestActivityEnvironment::class.java) + .apply { isAccessible = true } + .newInstance(javaEnv) + + getStore(context).put(TEST_ENVIRONMENT_KEY, kEnv) + } + + override fun afterEach(context: ExtensionContext) { + getStore(context).get(TEST_ENVIRONMENT_KEY, KTestActivityEnvironment::class.java)?.close() + } + + private fun getStore(context: ExtensionContext): ExtensionContext.Store { + val namespace = ExtensionContext.Namespace.create( + KTestActivityExtension::class.java, + context.requiredTestMethod + ) + return context.getStore(namespace) + } + + public companion object { + private const val TEST_ENVIRONMENT_KEY = "testEnvironment" + + public fun newBuilder(): Builder = Builder() + } + + @TemporalDsl + public class Builder internal constructor() { + private var testEnvironmentOptions: TestEnvironmentOptions = + TestEnvironmentOptions.getDefaultInstance() + private var activityImplementations: Array = emptyArray() + private var suspendActivityImplementations: Array = emptyArray() + + /** + * Configure test environment options. + */ + public fun testEnvironmentOptions(block: KTestEnvironmentOptionsBuilder.() -> Unit) { + testEnvironmentOptions = KTestEnvironmentOptionsBuilder().apply(block).build() + } + + /** + * Set activity implementations. + */ + public fun setActivityImplementations(vararg activities: Any) { + activityImplementations = arrayOf(*activities) + } + + /** + * Set suspend activity implementations. + */ + public fun setSuspendActivityImplementations(vararg activities: Any) { + suspendActivityImplementations = arrayOf(*activities) + } + + public fun build(): KTestActivityExtension { + return KTestActivityExtension( + ExtensionConfig( + testEnvironmentOptions = testEnvironmentOptions, + activityImplementations = activityImplementations, + suspendActivityImplementations = suspendActivityImplementations + ) + ) + } + } +} + +/** + * DSL function to create a KTestActivityExtension. + */ +public fun kTestActivityExtension( + block: KTestActivityExtension.Builder.() -> Unit +): KTestActivityExtension { + return KTestActivityExtension.newBuilder().apply(block).build() +} +``` + +--- + +## 8. Implementation Notes + +### Dependencies + +The test framework module will need the following dependencies: + +```kotlin +// build.gradle.kts +dependencies { + api(project(":temporal-kotlin")) + api("io.temporal:temporal-testing:VERSION") + + implementation("org.junit.jupiter:junit-jupiter-api:5.10.0") + implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.7.3") + + testImplementation("org.junit.jupiter:junit-jupiter:5.10.0") + testImplementation("org.jetbrains.kotlinx:kotlinx-coroutines-test:1.7.3") +} +``` + +### Module Structure + +``` +temporal-kotlin-testing/ +├── src/main/kotlin/io/temporal/kotlin/testing/ +│ ├── KTestWorkflowEnvironment.kt +│ ├── KTestWorkflowExtension.kt +│ ├── KTestActivityEnvironment.kt +│ ├── KTestActivityExtension.kt +│ ├── KTestEnvironmentOptions.kt +│ ├── KWorker.kt +│ └── WorkflowInitialTime.kt +└── src/test/kotlin/io/temporal/kotlin/testing/ + ├── KTestWorkflowEnvironmentTest.kt + ├── KTestWorkflowExtensionTest.kt + ├── KTestActivityEnvironmentTest.kt + └── KTestActivityExtensionTest.kt +``` + +### Key Integration Points + +1. **SuspendActivityWrapper**: Reuse existing implementation for suspend activity support +2. **KotlinPlugin**: Ensure Kotlin workflow support via existing plugin +3. **KWorkflowClient**: Return Kotlin client from test environment +4. **KWorkflowOptions**: Use existing options classes + +### Thread Safety + +- Test environments are designed for single-threaded test execution +- Each test method gets its own environment instance (via JUnit extension lifecycle) +- Suspend functions use `Dispatchers.IO` for blocking operations + +--- + +## 9. Summary + +This implementation design provides: + +| Component | Purpose | +|-----------|---------| +| `KTestEnvironmentOptions` | Configuration with DSL builder | +| `KWorker` | Kotlin wrapper for Worker with suspend activity support | +| `KTestWorkflowEnvironment` | Main test environment with time skipping | +| `KTestWorkflowExtension` | JUnit 5 extension for workflow tests | +| `KTestActivityEnvironment` | Activity unit testing environment | +| `KTestActivityExtension` | JUnit 5 extension for activity tests | +| `WorkflowInitialTime` | Annotation for per-test initial time | + +The design: +- Wraps Java SDK test framework for full compatibility +- Provides Kotlin-idiomatic DSL builders +- Supports suspend functions throughout +- Integrates with existing Kotlin SDK components +- Follows established Kotlin SDK patterns diff --git a/kotlin/implementation/test-framework-implementation-plan.md b/kotlin/implementation/test-framework-implementation-plan.md new file mode 100644 index 0000000..3173177 --- /dev/null +++ b/kotlin/implementation/test-framework-implementation-plan.md @@ -0,0 +1,835 @@ +# Kotlin SDK Test Framework Implementation Plan + +## Overview + +This document outlines the implementation plan for the Kotlin SDK test framework, broken down into small, incremental commits. Each commit is designed to: + +- Be as small as possible while adding testable functionality +- Make sense in isolation +- Build on previous commits without breaking them +- Have clear test scenarios + +**Total commits: 26** + +--- + +## Phase 1: Foundation (Commits 1-3) + +### Commit 1: Add temporal-kotlin-testing module structure + +**Description:** Set up the new Gradle module with dependencies. + +**Files:** +``` +temporal-kotlin-testing/ +├── build.gradle.kts +└── src/ + ├── main/kotlin/io/temporal/kotlin/testing/ + │ └── .gitkeep + └── test/kotlin/io/temporal/kotlin/testing/ + └── .gitkeep +``` + +**Changes:** +- Create `temporal-kotlin-testing` directory structure +- Add `build.gradle.kts` with dependencies: + - `api(project(":temporal-kotlin"))` + - `api("io.temporal:temporal-testing")` + - `implementation("org.junit.jupiter:junit-jupiter-api")` + - `implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core")` +- Add module to root `settings.gradle.kts` + +**Test scenarios:** +- Module compiles successfully +- Dependencies resolve correctly + +--- + +### Commit 2: Add WorkflowInitialTime annotation + +**Description:** Add annotation for per-test initial time override. + +**Files:** +``` +temporal-kotlin-testing/src/main/kotlin/io/temporal/kotlin/testing/ +└── WorkflowInitialTime.kt +``` + +**Content:** +```kotlin +@Target(AnnotationTarget.FUNCTION) +@Retention(AnnotationRetention.RUNTIME) +annotation class WorkflowInitialTime(val value: String) +``` + +**Test scenarios:** +- Annotation can be applied to functions +- Value is accessible via reflection +- Invalid ISO-8601 strings detected at parse time (not annotation time) + +--- + +### Commit 3: Add KTestEnvironmentOptions with DSL builder + +**Description:** Add configuration options with Kotlin DSL support. + +**Files:** +``` +temporal-kotlin-testing/src/main/kotlin/io/temporal/kotlin/testing/ +└── KTestEnvironmentOptions.kt +``` + +**Content:** +- `KTestEnvironmentOptionsBuilder` class with: + - `namespace: String` (default: "UnitTest") + - `initialTime: Instant?` + - `useTimeskipping: Boolean` (default: true) + - `useExternalService: Boolean` (default: false) + - `target: String?` + - `metricsScope: Scope?` + - `workerFactoryOptions {}` DSL + - `workflowClientOptions {}` DSL + - `workflowServiceStubsOptions {}` DSL + - `searchAttributes {}` nested DSL with `register(name, type)` + - `build()` method returning `TestEnvironmentOptions` +- `KTestEnvironmentOptions` class with: + - `internal val javaOptions: TestEnvironmentOptions` + - Companion: `newBuilder {}`, `getDefaultInstance()` + +**Test scenarios:** +- Builder with defaults creates valid TestEnvironmentOptions +- Each property correctly maps to Java options +- Nested DSLs (workerFactoryOptions, searchAttributes) work correctly +- `@TemporalDsl` annotation prevents DSL leakage + +--- + +## Phase 2: KWorker (Commits 4-6) ✅ COMPLETE + +> **Note:** KWorker was initially implemented in `temporal-kotlin-testing` but has since been moved to `temporal-kotlin/src/main/kotlin/io/temporal/kotlin/worker/KWorker.kt` to make it available for production use. `KWorkerFactory.newWorker()` now returns `KWorker`. + +### Commit 4: Add KWorker with workflow registration ✅ + +**Description:** Add Kotlin worker wrapper with workflow registration methods. + +**Files:** +``` +temporal-kotlin/src/main/kotlin/io/temporal/kotlin/worker/ +└── KWorker.kt +``` + +**Content:** +```kotlin +class KWorker internal constructor(val worker: Worker) { + inline fun registerWorkflowImplementationTypes() + fun registerWorkflowImplementationTypes(vararg workflowClasses: KClass<*>) + fun registerWorkflowImplementationTypes( + options: WorkflowImplementationOptions, + vararg workflowClasses: KClass<*> + ) + inline fun registerWorkflowImplementationTypes( + options: WorkflowImplementationOptions.Builder.() -> Unit + ) +} +``` + +**Test scenarios:** +- Reified generic registration works: `registerWorkflowImplementationTypes()` +- KClass vararg registration works +- Options DSL correctly applies WorkflowImplementationOptions +- Underlying Worker receives correct registrations + +--- + +### Commit 5: Add KWorker activity registration methods ✅ + +**Description:** Add activity registration including suspend activity support. + +**Files:** +- `KWorker.kt` (modify) + +**Content:** +```kotlin +// Add to KWorker class: +fun registerActivitiesImplementations(vararg activities: Any) +fun registerSuspendActivities(vararg activities: Any) +``` + +**Test scenarios:** +- Regular activities registered and callable +- Suspend activities wrapped via `SuspendActivityWrapper` +- Mixed registration (regular + suspend) works + +--- + +### Commit 6: Add KWorker Nexus service registration ✅ + +**Description:** Add Nexus service registration method. + +**Files:** +- `KWorker.kt` (modify) + +**Content:** +```kotlin +// Add to KWorker class: +fun registerNexusServiceImplementations(vararg services: Any) +``` + +**Test scenarios:** +- Nexus services registered on underlying Worker +- Services callable via Nexus operations + +--- + +## Phase 3: KTestWorkflowEnvironment (Commits 7-12) + +### Commit 7: Add KTestWorkflowEnvironment basic structure + +**Description:** Add core test environment with worker creation. + +**Files:** +``` +temporal-kotlin-testing/src/main/kotlin/io/temporal/kotlin/testing/ +└── KTestWorkflowEnvironment.kt +``` + +**Content:** +```kotlin +class KTestWorkflowEnvironment private constructor( + private val testEnvironment: TestWorkflowEnvironment +) : Closeable { + val namespace: String + + fun newWorker(taskQueue: String): KWorker + fun newWorker(taskQueue: String, options: WorkerOptions.Builder.() -> Unit): KWorker + fun newWorker(taskQueue: String, options: WorkerOptions): KWorker + + companion object { + fun newInstance(): KTestWorkflowEnvironment + fun newInstance(options: KTestEnvironmentOptionsBuilder.() -> Unit): KTestWorkflowEnvironment + fun newInstance(options: KTestEnvironmentOptions): KTestWorkflowEnvironment + } +} +``` + +**Test scenarios:** +- Environment created with defaults +- Environment created with DSL options +- Workers created with correct task queue +- Workers return KWorker instances + +--- + +### Commit 8: Add KTestWorkflowEnvironment client access + +**Description:** Add workflow client and service stub access. + +**Files:** +- `KTestWorkflowEnvironment.kt` (modify) + +**Content:** +```kotlin +// Add to KTestWorkflowEnvironment: +val workflowClient: KWorkflowClient // lazy +val workflowServiceStubs: WorkflowServiceStubs +val operatorServiceStubs: OperatorServiceStubs +``` + +**Test scenarios:** +- `workflowClient` returns valid KWorkflowClient +- Client can create workflow stubs +- Service stubs accessible for advanced operations + +--- + +### Commit 9: Add KTestWorkflowEnvironment time manipulation + +**Description:** Add time-related properties and sleep function. + +**Files:** +- `KTestWorkflowEnvironment.kt` (modify) + +**Content:** +```kotlin +// Add to KTestWorkflowEnvironment: +val currentTimeMillis: Long +val currentTime: Instant + +suspend fun sleep(duration: kotlin.time.Duration) +suspend fun sleep(duration: java.time.Duration) +``` + +**Test scenarios:** +- `currentTime` returns test environment time (not system time) +- `sleep()` advances test time without real delay +- Both Kotlin and Java Duration overloads work +- Workflow timers fire after sleep advances time + +--- + +### Commit 10: Add KTestWorkflowEnvironment delayed callbacks + +**Description:** Add delayed callback registration. + +**Files:** +- `KTestWorkflowEnvironment.kt` (modify) + +**Content:** +```kotlin +// Add to KTestWorkflowEnvironment: +fun registerDelayedCallback(delay: kotlin.time.Duration, callback: () -> Unit) +fun registerDelayedCallback(delay: java.time.Duration, callback: () -> Unit) +``` + +**Test scenarios:** +- Callback executes when test time reaches delay +- Multiple callbacks execute in correct order +- Both Duration types work + +--- + +### Commit 11: Add KTestWorkflowEnvironment lifecycle management + +**Description:** Add start, shutdown, and termination methods. + +**Files:** +- `KTestWorkflowEnvironment.kt` (modify) + +**Content:** +```kotlin +// Add to KTestWorkflowEnvironment: +val isStarted: Boolean +val isShutdown: Boolean +val isTerminated: Boolean + +fun start() +fun shutdown() +fun shutdownNow() +suspend fun awaitTermination(timeout: kotlin.time.Duration) +suspend fun awaitTermination(timeout: java.time.Duration) +override fun close() + +// Extension function: +inline fun KTestWorkflowEnvironment.use(block: (KTestWorkflowEnvironment) -> T): T +``` + +**Test scenarios:** +- `start()` enables workflow execution +- State properties reflect correct lifecycle state +- `shutdown()` stops accepting new work +- `shutdownNow()` interrupts in-progress work +- `awaitTermination()` suspends until terminated +- `close()` combines shutdownNow + awaitTermination +- `use {}` auto-closes on block exit + +--- + +### Commit 12: Add KTestWorkflowEnvironment advanced features + +**Description:** Add search attributes, Nexus endpoints, and diagnostics. + +**Files:** +- `KTestWorkflowEnvironment.kt` (modify) + +**Content:** +```kotlin +// Add to KTestWorkflowEnvironment: +fun registerSearchAttribute(name: String, type: IndexedValueType): Boolean +fun createNexusEndpoint(name: String, taskQueue: String): Endpoint +fun deleteNexusEndpoint(endpoint: Endpoint) +fun getDiagnostics(): String +``` + +**Test scenarios:** +- Search attributes registered and queryable +- Nexus endpoints created and usable +- Nexus endpoints deleted successfully +- Diagnostics returns workflow histories + +--- + +## Phase 4: KTestWorkflowExtension (Commits 13-18) + +### Commit 13: Add KTestWorkflowExtension builder basics + +**Description:** Add extension builder with workflow/activity registration. + +**Files:** +``` +temporal-kotlin-testing/src/main/kotlin/io/temporal/kotlin/testing/ +└── KTestWorkflowExtension.kt +``` + +**Content:** +```kotlin +class KTestWorkflowExtension private constructor(config: ExtensionConfig) { + private data class ExtensionConfig(...) + + class Builder { + var namespace: String + var initialTime: Instant? + var useTimeskipping: Boolean + var doNotStart: Boolean + + inline fun registerWorkflowImplementationTypes() + inline fun registerWorkflowImplementationTypes( + options: WorkflowImplementationOptions.Builder.() -> Unit + ) + fun registerWorkflowImplementationTypes(vararg classes: Class<*>) + fun setActivityImplementations(vararg activities: Any) + fun setSuspendActivityImplementations(vararg activities: Any) + fun setNexusServiceImplementations(vararg services: Any) + fun build(): KTestWorkflowExtension + } + + companion object { + fun newBuilder(): Builder + } +} + +fun kTestWorkflowExtension(block: KTestWorkflowExtension.Builder.() -> Unit): KTestWorkflowExtension +``` + +**Test scenarios:** +- Builder creates extension with correct config +- DSL function `kTestWorkflowExtension {}` works +- Workflow types stored correctly +- Activity implementations stored correctly + +--- + +### Commit 14: Add KTestWorkflowExtension service configuration + +**Description:** Add worker, client, and service configuration DSLs. + +**Files:** +- `KTestWorkflowExtension.kt` (modify) + +**Content:** +```kotlin +// Add to Builder: +fun workerOptions(block: WorkerOptions.Builder.() -> Unit) +fun workerFactoryOptions(block: WorkerFactoryOptions.Builder.() -> Unit) +fun workflowClientOptions(block: WorkflowClientOptions.Builder.() -> Unit) +fun useInternalService() +fun useExternalService() +fun useExternalService(address: String) +fun searchAttributes(block: SearchAttributesBuilder.() -> Unit) +``` + +**Test scenarios:** +- Worker options flow through to worker creation +- Factory options configure WorkerFactory +- Client options configure WorkflowClient +- `useExternalService()` connects to real Temporal +- Search attributes registered on test server + +--- + +### Commit 15: Add KTestWorkflowExtension lifecycle callbacks + +**Description:** Implement JUnit 5 BeforeEach/AfterEach callbacks. + +**Files:** +- `KTestWorkflowExtension.kt` (modify) + +**Content:** +```kotlin +// Implement interfaces: +class KTestWorkflowExtension : BeforeEachCallback, AfterEachCallback { + override fun beforeEach(context: ExtensionContext) { + // Create test environment + // Create worker with unique task queue + // Register workflows and activities + // Start if not doNotStart + // Store in ExtensionContext.Store + } + + override fun afterEach(context: ExtensionContext) { + // Close test environment + } +} +``` + +**Test scenarios:** +- Each test gets isolated environment +- Workers registered before test runs +- Environment closed after test completes +- Test isolation verified (no cross-test contamination) + +--- + +### Commit 16: Add KTestWorkflowExtension basic parameter injection + +**Description:** Implement ParameterResolver for core types. + +**Files:** +- `KTestWorkflowExtension.kt` (modify) + +**Content:** +```kotlin +// Implement ParameterResolver: +class KTestWorkflowExtension : ParameterResolver { + override fun supportsParameter(parameterContext, extensionContext): Boolean { + // Support: KTestWorkflowEnvironment, KWorkflowClient, + // KWorkflowOptions, KWorker, Worker + } + + override fun resolveParameter(parameterContext, extensionContext): Any { + // Return appropriate instance from store + } +} +``` + +**Test scenarios:** +- `KTestWorkflowEnvironment` injected correctly +- `KWorkflowClient` injected correctly +- `KWorkflowOptions` injected with correct task queue +- `KWorker` injected correctly +- `Worker` (Java) injected correctly + +--- + +### Commit 17: Add KTestWorkflowExtension workflow stub injection + +**Description:** Add workflow interface stub injection. + +**Files:** +- `KTestWorkflowExtension.kt` (modify) + +**Content:** +```kotlin +// Enhance parameter resolution: +private val supportedParameterTypes: MutableSet> + +// In constructor: populate from registered workflow types +// In supportsParameter: check workflow interfaces +// In resolveParameter: create workflow stub +``` + +**Test scenarios:** +- Workflow interface parameters injected as stubs +- Stubs have correct task queue +- Stubs can execute workflows +- Dynamic workflow support works + +--- + +### Commit 18: Add KTestWorkflowExtension annotations and diagnostics + +**Description:** Add WorkflowInitialTime support and test failure diagnostics. + +**Files:** +- `KTestWorkflowExtension.kt` (modify) + +**Content:** +```kotlin +// Implement TestWatcher: +class KTestWorkflowExtension : TestWatcher { + override fun testFailed(context: ExtensionContext, cause: Throwable) { + // Print diagnostics to stderr + } +} + +// In beforeEach: +// Check for @WorkflowInitialTime annotation +// Override initial time if present +``` + +**Test scenarios:** +- `@WorkflowInitialTime` overrides initial time for specific test +- Test failure prints workflow histories to stderr +- Diagnostics help debug failing tests + +--- + +## Phase 5: KTestActivityEnvironment (Commits 19-24) + +### Commit 19: Add KTestActivityEnvironment basic structure + +**Description:** Add core activity test environment with registration. + +**Files:** +``` +temporal-kotlin-testing/src/main/kotlin/io/temporal/kotlin/testing/ +└── KTestActivityEnvironment.kt +``` + +**Content:** +```kotlin +class KTestActivityEnvironment private constructor( + private val testEnvironment: TestActivityEnvironment +) : Closeable { + fun registerActivitiesImplementations(vararg activities: Any) + fun registerSuspendActivities(vararg activities: Any) + override fun close() + + companion object { + fun newInstance(): KTestActivityEnvironment + fun newInstance(options: KTestEnvironmentOptionsBuilder.() -> Unit): KTestActivityEnvironment + fun newInstance(options: KTestEnvironmentOptions): KTestActivityEnvironment + } +} +``` + +**Test scenarios:** +- Environment created with defaults +- Activities registered successfully +- Suspend activities wrapped and registered +- Environment closes cleanly + +--- + +### Commit 20: Add KTestActivityEnvironment executeActivity (0-2 args) + +**Description:** Add activity execution via method references. + +**Files:** +- `KTestActivityEnvironment.kt` (modify) + +**Content:** +```kotlin +// Add to KTestActivityEnvironment: +private fun createActivityStub(activity: KFunction<*>, options: ActivityOptions): T + +fun executeActivity(activity: KFunction1, options: KActivityOptions): R +fun executeActivity(activity: KFunction2, options: KActivityOptions, arg1: A1): R +fun executeActivity(activity: KFunction3, options: KActivityOptions, arg1: A1, arg2: A2): R +``` + +**Test scenarios:** +- Activity with no args executes correctly +- Activity with 1 arg executes correctly +- Activity with 2 args executes correctly +- Method reference extracts correct interface + +--- + +### Commit 21: Add KTestActivityEnvironment executeActivity (3+ args) + +**Description:** Add activity execution for higher arities. + +**Files:** +- `KTestActivityEnvironment.kt` (modify) + +**Content:** +```kotlin +// Add to KTestActivityEnvironment: +fun executeActivity( + activity: KFunction4, + options: KActivityOptions, + arg1: A1, arg2: A2, arg3: A3 +): R + +// Additional arities as needed (4, 5, 6 args) +``` + +**Test scenarios:** +- Activity with 3 args executes correctly +- Activity with 4+ args executes correctly + +--- + +### Commit 22: Add KTestActivityEnvironment suspend activity support + +**Description:** Add suspend function overloads for activity execution. + +**Files:** +- `KTestActivityEnvironment.kt` (modify) + +**Content:** +```kotlin +// Add suspend overloads: +@JvmName("executeSuspendActivity0") +suspend fun executeActivity(activity: KSuspendFunction1, options: KActivityOptions): R + +@JvmName("executeSuspendActivity1") +suspend fun executeActivity(activity: KSuspendFunction2, options: KActivityOptions, arg1: A1): R + +// Continue for 2, 3+ args +``` + +**Test scenarios:** +- Suspend activity with no args executes +- Suspend activity with args executes +- Coroutine context preserved correctly + +--- + +### Commit 23: Add KTestActivityEnvironment local activity support + +**Description:** Add local activity execution methods. + +**Files:** +- `KTestActivityEnvironment.kt` (modify) + +**Content:** +```kotlin +// Add to KTestActivityEnvironment: +private fun createLocalActivityStub(activity: KFunction<*>, options: LocalActivityOptions): T + +fun executeLocalActivity(activity: KFunction1, options: KLocalActivityOptions): R +fun executeLocalActivity(activity: KFunction2, options: KLocalActivityOptions, arg1: A1): R +fun executeLocalActivity(activity: KFunction3, options: KLocalActivityOptions, arg1: A1, arg2: A2): R +fun executeLocalActivity(activity: KFunction4, options: KLocalActivityOptions, arg1: A1, arg2: A2, arg3: A3): R +``` + +**Test scenarios:** +- Local activity executes with correct options +- Local activity timeout behavior works +- Local activity retry behavior works + +--- + +### Commit 24: Add KTestActivityEnvironment heartbeat and cancellation + +**Description:** Add heartbeat testing and cancellation support. + +**Files:** +- `KTestActivityEnvironment.kt` (modify) + +**Content:** +```kotlin +// Add to KTestActivityEnvironment: +fun setHeartbeatDetails(details: T) + +inline fun setActivityHeartbeatListener(noinline listener: (T) -> Unit) +fun setActivityHeartbeatListener(detailsClass: Class, detailsType: Type, listener: (T) -> Unit) + +fun requestCancelActivity() +``` + +**Test scenarios:** +- `setHeartbeatDetails` provides checkpoint to activity +- Heartbeat listener receives heartbeat calls +- `requestCancelActivity` triggers cancellation on next heartbeat +- `ActivityCanceledException` thrown after cancellation + +--- + +## Phase 6: KTestActivityExtension (Commits 25-26) + +### Commit 25: Add KTestActivityExtension builder + +**Description:** Add extension builder with DSL. + +**Files:** +``` +temporal-kotlin-testing/src/main/kotlin/io/temporal/kotlin/testing/ +└── KTestActivityExtension.kt +``` + +**Content:** +```kotlin +class KTestActivityExtension private constructor(config: ExtensionConfig) { + private data class ExtensionConfig( + val testEnvironmentOptions: TestEnvironmentOptions, + val activityImplementations: Array, + val suspendActivityImplementations: Array + ) + + class Builder { + fun testEnvironmentOptions(block: KTestEnvironmentOptionsBuilder.() -> Unit) + fun setActivityImplementations(vararg activities: Any) + fun setSuspendActivityImplementations(vararg activities: Any) + fun build(): KTestActivityExtension + } + + companion object { + fun newBuilder(): Builder + } +} + +fun kTestActivityExtension(block: KTestActivityExtension.Builder.() -> Unit): KTestActivityExtension +``` + +**Test scenarios:** +- Builder creates extension with correct config +- DSL function works +- Activity implementations stored correctly + +--- + +### Commit 26: Add KTestActivityExtension lifecycle and injection + +**Description:** Implement JUnit 5 callbacks and parameter injection. + +**Files:** +- `KTestActivityExtension.kt` (modify) + +**Content:** +```kotlin +class KTestActivityExtension : ParameterResolver, BeforeEachCallback, AfterEachCallback { + override fun supportsParameter(parameterContext, extensionContext): Boolean { + // Support KTestActivityEnvironment + } + + override fun resolveParameter(parameterContext, extensionContext): Any { + // Return KTestActivityEnvironment from store + } + + override fun beforeEach(context: ExtensionContext) { + // Create environment + // Register activities + // Store in context + } + + override fun afterEach(context: ExtensionContext) { + // Close environment + } +} +``` + +**Test scenarios:** +- `KTestActivityEnvironment` injected into test methods +- Each test gets isolated environment +- Activities callable via injected environment +- Environment closed after test + +--- + +## Summary + +| Phase | Commits | Description | +|-------|---------|-------------| +| 1. Foundation | 1-3 | Module setup, annotation, options | +| 2. KWorker | 4-6 | Worker wrapper with registrations | +| 3. KTestWorkflowEnvironment | 7-12 | Main test environment | +| 4. KTestWorkflowExtension | 13-18 | JUnit 5 workflow extension | +| 5. KTestActivityEnvironment | 19-24 | Activity test environment | +| 6. KTestActivityExtension | 25-26 | JUnit 5 activity extension | + +**Total: 26 commits** + +## Dependencies Graph + +``` +Commit 1 (module) + └── Commit 2 (annotation) + └── Commit 3 (options) + └── Commits 4-6 (KWorker) + └── Commits 7-12 (KTestWorkflowEnvironment) + └── Commits 13-18 (KTestWorkflowExtension) + └── Commits 19-24 (KTestActivityEnvironment) + └── Commits 25-26 (KTestActivityExtension) +``` + +## Testing Strategy + +Each commit should include: + +1. **Unit tests** for the new functionality +2. **Integration tests** where applicable (especially for environment/extension commits) +3. **Example usage** in test code demonstrating the API + +Test files follow the pattern: +``` +temporal-kotlin-testing/src/test/kotlin/io/temporal/kotlin/testing/ +├── WorkflowInitialTimeTest.kt +├── KTestEnvironmentOptionsTest.kt +├── KWorkerTest.kt +├── KTestWorkflowEnvironmentTest.kt +├── KTestWorkflowExtensionTest.kt +├── KTestActivityEnvironmentTest.kt +└── KTestActivityExtensionTest.kt +``` diff --git a/kotlin/open-questions.md b/kotlin/open-questions.md new file mode 100644 index 0000000..1977ae6 --- /dev/null +++ b/kotlin/open-questions.md @@ -0,0 +1,506 @@ +# Open Questions + +This document tracks API design questions that need discussion and decisions before implementation. + +## Interfaceless Workflows and Activities + +**Status:** Decision needed + +### Problem Statement + +Currently, workflows and activities require interface definitions with annotations, which adds boilerplate: + +```kotlin +// Current approach - requires interface +@ActivityInterface +interface GreetingActivities { + suspend fun composeGreeting(greeting: String, name: String): String +} + +class GreetingActivitiesImpl : GreetingActivities { + override suspend fun composeGreeting(greeting: String, name: String) = "$greeting, $name!" +} + +@WorkflowInterface +interface GreetingWorkflow { + @WorkflowMethod + suspend fun getGreeting(name: String): String +} + +class GreetingWorkflowImpl : GreetingWorkflow { + override suspend fun getGreeting(name: String): String { + // implementation + } +} +``` + +### Proposal + +Allow defining activities and workflows directly on implementation classes without interfaces, similar to Python SDK: + +**Activities:** + +```kotlin +// Proposed approach - no interface required +class GreetingActivities { + @ActivityMethod + suspend fun composeGreeting(greeting: String, name: String) = "$greeting, $name!" +} + +// In workflow - call using method reference to impl class +val result = KWorkflow.executeActivity( + GreetingActivities::composeGreeting, + KActivityOptions(startToCloseTimeout = 30.seconds), + "Hello", "World" +) +``` + +**Workflows:** + +```kotlin +// Proposed approach - no interface required +class GreetingWorkflow { + @WorkflowMethod + suspend fun getGreeting(name: String): String { + return KWorkflow.executeActivity( + GreetingActivities::composeGreeting, + KActivityOptions(startToCloseTimeout = 10.seconds), + "Hello", name + ) + } +} + +// Client call using method reference to impl class +val result = client.executeWorkflow( + GreetingWorkflow::getGreeting, + KWorkflowOptions(workflowId = "greeting-123", taskQueue = "greetings"), + "World" +) +``` + +### Benefits + +- Reduces boilerplate (no separate interface file) +- More similar to Python SDK experience +- Kotlin-only feature, no Java SDK changes required + +### Trade-offs + +- Different from Java SDK convention +- Activity/workflow type names derived from method/class names (convention-based) +- Respects `@ActivityMethod(name = "...")` and `@WorkflowMethod(name = "...")` annotations if present + +### Related Sections + +- [Activity Definition](./activities/definition.md#interfaceless-activity-definition) +- [Workflow Definition](./workflows/definition.md#interfaceless-workflow-definition) + +--- + +## Type-Safe Activity/Workflow Arguments + +**Status:** Decision needed + +### Problem Statement + +The current activity execution API uses varargs which are not compile-time type-safe: + +```kotlin +// Current approach - vararg, no compile-time type checking +KWorkflow.executeActivity( + GreetingActivities::composeGreeting, + KActivityOptions(startToCloseTimeout = 30.seconds), + "Hello", "World" // vararg Any? - wrong types only caught at runtime +) +``` + +### Options + +Three options are being considered: + +--- + +### Option A: Keep Current Varargs (No Change) + +Keep the current vararg approach: + +```kotlin +KWorkflow.executeActivity( + GreetingActivities::composeGreeting, + KActivityOptions(startToCloseTimeout = 30.seconds), + "Hello", "World" // vararg Any? +) +``` + +**Pros:** +- Simple API, no wrapper classes +- Familiar pattern + +**Cons:** +- No compile-time type safety +- Wrong argument types/count only caught at runtime + +--- + +### Option B: Direct Overloads (0-7 Arguments) + +Provide separate overloads for each arity with direct arguments: + +```kotlin +// 0 arguments +KWorkflow.executeActivity( + GreetingActivities::getDefault, + KActivityOptions(startToCloseTimeout = 30.seconds) +) + +// 1 argument +KWorkflow.executeActivity( + GreetingActivities::greet, + "World", + KActivityOptions(startToCloseTimeout = 30.seconds) +) + +// 2 arguments +KWorkflow.executeActivity( + GreetingActivities::composeGreeting, + "Hello", "World", + KActivityOptions(startToCloseTimeout = 30.seconds) +) + +// 3 arguments +KWorkflow.executeActivity( + OrderActivities::process, + orderId, customer, items, + KActivityOptions(startToCloseTimeout = 30.seconds) +) +``` + +**Overloads:** +```kotlin +object KWorkflow { + suspend fun executeActivity( + activity: KFunction1, + options: KActivityOptions + ): R + + suspend fun executeActivity( + activity: KFunction2, + arg: A, + options: KActivityOptions + ): R + + suspend fun executeActivity( + activity: KFunction3, + arg1: A1, arg2: A2, + options: KActivityOptions + ): R + + suspend fun executeActivity( + activity: KFunction4, + arg1: A1, arg2: A2, arg3: A3, + options: KActivityOptions + ): R + + // ... up to 7 arguments +} +``` + +**Pros:** +- Full compile-time type safety +- Clean call syntax, no wrapper classes +- Natural reading order + +**Cons:** +- Many overloads (8 per method × 3 call types = 24 overloads) +- Options always last (can't use trailing lambda syntax if options were a builder) + +--- + +### Option C: KArgs Wrapper Classes + +Use typed `KArgs` classes for multiple arguments: + +**Activities:** +```kotlin +// 0 arguments - just method reference and options +KWorkflow.executeActivity( + GreetingActivities::getDefaultGreeting, + KActivityOptions(startToCloseTimeout = 30.seconds) +) + +// 1 argument - passed directly +KWorkflow.executeActivity( + GreetingActivities::greet, + "World", + KActivityOptions(startToCloseTimeout = 30.seconds) +) + +// Multiple arguments - use kargs() +KWorkflow.executeActivity( + GreetingActivities::composeGreeting, + kargs("Hello", "World"), + KActivityOptions(startToCloseTimeout = 30.seconds) +) +``` + +**Child Workflows:** +```kotlin +// 0 arguments +KWorkflow.executeChildWorkflow( + ChildWorkflow::run, + KChildWorkflowOptions(workflowId = "child-1") +) + +// 1 argument +KWorkflow.executeChildWorkflow( + ChildWorkflow::process, + order, + KChildWorkflowOptions(workflowId = "child-1") +) + +// Multiple arguments +KWorkflow.executeChildWorkflow( + ChildWorkflow::processWithConfig, + kargs(order, config), + KChildWorkflowOptions(workflowId = "child-1") +) +``` + +**Client Workflow Execution:** +```kotlin +// 0 arguments +client.executeWorkflow( + MyWorkflow::run, + KWorkflowOptions(workflowId = "wf-1", taskQueue = "main") +) + +// 1 argument +client.executeWorkflow( + MyWorkflow::process, + input, + KWorkflowOptions(workflowId = "wf-1", taskQueue = "main") +) + +// Multiple arguments +client.executeWorkflow( + MyWorkflow::processWithConfig, + kargs(input, config), + KWorkflowOptions(workflowId = "wf-1", taskQueue = "main") +) +``` + +**KArgs classes:** +```kotlin +sealed interface KArgs + +data class KArgs2(val a1: A1, val a2: A2) : KArgs +data class KArgs3(val a1: A1, val a2: A2, val a3: A3) : KArgs +data class KArgs4(val a1: A1, val a2: A2, val a3: A3, val a4: A4) : KArgs +data class KArgs5(val a1: A1, val a2: A2, val a3: A3, val a4: A4, val a5: A5) : KArgs +data class KArgs6(val a1: A1, val a2: A2, val a3: A3, val a4: A4, val a5: A5, val a6: A6) : KArgs +data class KArgs7(val a1: A1, val a2: A2, val a3: A3, val a4: A4, val a5: A5, val a6: A6, val a7: A7) : KArgs + +// Factory functions +fun kargs(a1: A1, a2: A2) = KArgs2(a1, a2) +fun kargs(a1: A1, a2: A2, a3: A3) = KArgs3(a1, a2, a3) +// ... up to 7 +``` + +**Execute overloads (activities):** +```kotlin +object KWorkflow { + // 0 arguments + suspend fun executeActivity( + activity: KFunction1, + options: KActivityOptions + ): R + + // 1 argument - direct + suspend fun executeActivity( + activity: KFunction2, + arg: A, + options: KActivityOptions + ): R + + // 2 arguments - KArgs2 types must match KFunction3 params + suspend fun executeActivity( + activity: KFunction3, + args: KArgs2, + options: KActivityOptions + ): R + + // ... up to 7 +} +``` + +**Execute overloads (child workflows):** +```kotlin +object KWorkflow { + // 0 arguments + suspend fun executeChildWorkflow( + workflow: KFunction1, + options: KChildWorkflowOptions + ): R + + // 1 argument + suspend fun executeChildWorkflow( + workflow: KFunction2, + arg: A, + options: KChildWorkflowOptions + ): R + + // 2 arguments + suspend fun executeChildWorkflow( + workflow: KFunction3, + args: KArgs2, + options: KChildWorkflowOptions + ): R + + // ... up to 7 +} +``` + +**Execute overloads (client):** +```kotlin +class KWorkflowClient { + // 0 arguments + suspend fun executeWorkflow( + workflow: KFunction1, + options: KWorkflowOptions + ): R + + // 1 argument + suspend fun executeWorkflow( + workflow: KFunction2, + arg: A, + options: KWorkflowOptions + ): R + + // 2 arguments + suspend fun executeWorkflow( + workflow: KFunction3, + args: KArgs2, + options: KWorkflowOptions + ): R + + // ... up to 7 +} +``` + +**Pros:** +- Full compile-time type safety +- Fewer overloads than Option B (only 0, 1, and KArgs variants = 3 per method) +- Works with Kotlin's type inference + +**Cons:** +- Requires `kargs(...)` wrapper for 2+ arguments +- Additional classes in the API + +--- + +### Comparison + +| Aspect | Option A (Varargs) | Option B (Direct) | Option C (KArgs) | +|--------|-------------------|-------------------|------------------| +| Type safety | Runtime only | Compile-time | Compile-time | +| Call syntax (2+ args) | `"a", "b"` | `"a", "b"` | `kargs("a", "b")` | +| Overloads per method | 1 | 8 | 3 | +| Total overloads | 3 | 24 | 9 | +| Additional classes | None | None | KArgs2-7 | + +### Related Sections + +- [Activity Definition](./activities/definition.md#type-safe-activity-arguments) + +--- + +## Data Classes vs Builder+DSL for Options/Config Classes + +**Status:** Decision needed + +### Problem Statement + +Data classes are convenient for options/config classes due to named parameters and `copy()`: + +```kotlin +data class KActivityOptions( + val startToCloseTimeout: Duration? = null, + val scheduleToCloseTimeout: Duration? = null, + // ... +) + +val options = KActivityOptions(startToCloseTimeout = 10.minutes) +``` + +However, the Kotlin team doesn't recommend using data classes as part of library APIs because adding a new field (even an optional one) is always a **binary breaking change**. This happens because: + +- Adding a new property requires a new constructor parameter +- Even with default values, this changes the constructor signature +- Existing compiled code calling the old constructor breaks at runtime +- The auto-generated `copy()` method has the same issue + +Libraries like Jackson started with constructors with named parameters and later deprecated them in favor of a DSL/builder combo. + +### Proposed Alternative: Builder + DSL Pattern + +```kotlin +class KActivityOptions +private constructor(builder: Builder) { + val startToCloseTimeout: Duration? + val scheduleToCloseTimeout: Duration? + // ... + + init { + startToCloseTimeout = builder.startToCloseTimeout + scheduleToCloseTimeout = builder.scheduleToCloseTimeout + // ... + } + + class Builder { + var startToCloseTimeout: Duration? = null + var scheduleToCloseTimeout: Duration? = null + // ... + + fun build(): KActivityOptions { + require(startToCloseTimeout != null || scheduleToCloseTimeout != null) { + "At least one of startToCloseTimeout or scheduleToCloseTimeout must be specified" + } + return KActivityOptions(this) + } + } +} + +inline fun KActivityOptions(init: KActivityOptions.Builder.() -> Unit): KActivityOptions { + return KActivityOptions.Builder().apply(init).build() +} +``` + +This gives an ABI-safe equivalent for data class named constructor parameters: + +```kotlin +val options = KActivityOptions { + startToCloseTimeout = 10.minutes + // ... +} +``` + +### Benefits of Builder+DSL + +- Adding new optional properties to the `Builder` class is binary compatible +- The inline factory function provides the same ergonomic DSL syntax as data class constructors +- Validation can happen in `build()` before object construction +- `copy` can be implemented safely if needed (via a `toBuilder()` method) + +### Trade-offs + +- More boilerplate code to write and maintain +- Loses data class conveniences (`equals`, `hashCode`, `toString`, `componentN`) + - Though these can be manually implemented or generated +- Slightly more complex internal implementation + +### Precedent + +This pattern is used by: +- kotlinx.serialization +- Ktor +- Jackson (migrated from named parameters to this pattern) diff --git a/kotlin/workflows/definition.md b/kotlin/workflows/definition.md index 195af7d..afe5ffd 100644 --- a/kotlin/workflows/definition.md +++ b/kotlin/workflows/definition.md @@ -128,4 +128,61 @@ val classLog = KWorkflow.logger(MyWorkflowImpl::class.java) --- +## Open Questions (Decision Needed) + +### Interfaceless Workflow Definition + +**Status:** Decision needed | [Full discussion](../open-questions.md#interfaceless-workflows-and-activities) + +Currently, workflows require interface definitions: + +```kotlin +// Current approach - requires interface +@WorkflowInterface +interface GreetingWorkflow { + @WorkflowMethod + suspend fun getGreeting(name: String): String +} + +class GreetingWorkflowImpl : GreetingWorkflow { + override suspend fun getGreeting(name: String): String { + // implementation + } +} +``` + +**Proposal:** Allow defining workflows directly on implementation classes without interfaces, similar to Python SDK: + +```kotlin +// Proposed approach - no interface required +class GreetingWorkflow { + @WorkflowMethod + suspend fun getGreeting(name: String): String { + return KWorkflow.executeActivity( + GreetingActivities::composeGreeting, + KActivityOptions(startToCloseTimeout = 10.seconds), + "Hello", name + ) + } +} + +// Client call using method reference to impl class +val result = client.executeWorkflow( + GreetingWorkflow::getGreeting, + KWorkflowOptions(workflowId = "greeting-123", taskQueue = "greetings"), + "World" +) +``` + +**Benefits:** +- Reduces boilerplate (no separate interface file) +- More similar to Python SDK experience +- Kotlin-only feature, no Java SDK changes required + +**Trade-offs:** +- Different from Java SDK convention +- Workflow type name derived from class name (convention-based, respects `@WorkflowMethod(name = "...")`) + +--- + **Next:** [Signals, Queries & Updates](./signals-queries.md) diff --git a/nexus/images/behavior.png b/nexus/images/behavior.png index 901f73d641e1440d8d53702c067cc804327f2c8d..2bd1679c3f73c08e6dfcce513c34c7a223350617 100644 GIT binary patch literal 131 zcmW;CI}*Ym5CG7gQ*Z&ppOD;UcUgoPm5h)So?gf5HSgm0X#FMYoX6Oey0>|Il(GD* zCoa^VY8*`JGNQLEM+F<=9w-1Jtr-E+#@cBMg@93;v_<1fV((laoikGi+5L+rZ~z!% N5~F=sn$2pziZ4tAD3<^L literal 192047 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 F{s$Ndder~` diff --git a/nexus/images/full-client-id-flow.png b/nexus/images/full-client-id-flow.png index 33e3b270a78d7dffa426e3a50fb75eb0abe0e8da..3200e809c1af71cb2e4e453d5e46a2a5ec8dd821 100644 GIT binary patch literal 131 zcmWN?%MrpL5CG6SRnUOp&$8)e1FSHkk`c_o>h)dTRo}CZmuzdDb64u#*X_Y```IJOc%WoRSFNdXa>+W+MjJ9j MV)k#9UR#7Heht$nqyPW_ literal 402976 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%0AL{uU>*CDO;7XwywQcE_nYY5K_j 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 M7?iXhY3pF5{yvK+ZU6uP literal 231111 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 diff --git a/nexus/images/nexus-flow.png b/nexus/images/nexus-flow.png index 938a198069a3088e5b1470c75a4c355b923ab7a0..fb3e5ad9700727559499976dd2c5fa94b4af99fb 100644 GIT binary patch literal 131 zcmWN?OA^8$3;@tQr{DsXwBZwa8$LmpQR$f4g{RlIc^AKB%$M%xdB|?eeVn&PljVQ^ z^ literal 218330 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%0AYu7m7pLzhIOkdl(mg73#u3<>DJB<_R67P}x`I+W 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

H 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 Date: Mon, 12 Jan 2026 18:14:48 -0600 Subject: [PATCH 72/83] Document that default parameter values are not allowed --- kotlin/activities/definition.md | 21 +++++++++ kotlin/open-questions.md | 71 +++++++++++++++++++++++++++++ kotlin/workflows/definition.md | 21 +++++++++ kotlin/workflows/signals-queries.md | 2 + 4 files changed, 115 insertions(+) diff --git a/kotlin/activities/definition.md b/kotlin/activities/definition.md index 465ca54..c1bb3a9 100644 --- a/kotlin/activities/definition.md +++ b/kotlin/activities/definition.md @@ -73,6 +73,27 @@ KWorkflow.executeActivity( ) ``` +## Parameter Restrictions + +**Default parameter values are not allowed** in activity methods. This is validated at worker registration time. + +```kotlin +// ✗ NOT ALLOWED - will fail at registration +@ActivityMethod +suspend fun processOrder(orderId: String, priority: Int = 0) // Error! + +// ✓ CORRECT - use a parameter object with optional fields +data class ProcessOrderParams( + val orderId: String, + val priority: Int? = null +) + +@ActivityMethod +suspend fun processOrder(params: ProcessOrderParams) +``` + +**Rationale:** Default values create replay safety issues (changing defaults breaks determinism), serialization ambiguity, and cross-language compatibility problems. See [full discussion](../open-questions.md#default-parameter-values-not-allowed). + ## Type Safety The API uses `KFunction` reflection to extract method metadata and provides compile-time type checking: diff --git a/kotlin/open-questions.md b/kotlin/open-questions.md index 1977ae6..a3bf89f 100644 --- a/kotlin/open-questions.md +++ b/kotlin/open-questions.md @@ -2,6 +2,77 @@ This document tracks API design questions that need discussion and decisions before implementation. +--- + +## Default Parameter Values Not Allowed + +**Status:** Decided + +### Decision + +**Default parameter values are not allowed** in workflow methods, activity methods, signal handlers, update handlers, and query handlers. This restriction is enforced at worker registration time via reflection (`KParameter.isOptional`). + +```kotlin +// ✗ NOT ALLOWED - will fail at registration +@ActivityMethod +suspend fun processOrder(orderId: String, priority: Int = 0) // Error! + +// ✓ CORRECT - use a parameter object with optional fields +data class ProcessOrderParams( + val orderId: String, + val priority: Int? = null +) + +@ActivityMethod +suspend fun processOrder(params: ProcessOrderParams) +``` + +### Rationale + +1. **Replay Safety** - If default values change between deployments, replayed workflows behave differently, violating determinism +2. **Serialization Ambiguity** - Unclear whether the caller serializes defaults or the worker applies them +3. **Cross-Language Compatibility** - Other languages calling the activity/workflow don't know about Kotlin defaults +4. **SDK Consistency** - Python SDK explicitly disallows default parameters; Go/Java don't have them + +### Validation + +The SDK validates at registration time using Kotlin reflection: + +```kotlin +fun validateNoDefaultParameters(function: KFunction<*>) { + val paramsWithDefaults = function.parameters + .filter { it.kind == KParameter.Kind.VALUE } + .filter { it.isOptional } + + if (paramsWithDefaults.isNotEmpty()) { + throw IllegalArgumentException( + "Default parameter values are not allowed. " + + "Use a parameter object with optional fields instead." + ) + } +} +``` + +### Recommended Pattern + +Use a single parameter object with nullable/optional fields: + +```kotlin +data class OrderParams( + val orderId: String, + val priority: Int? = null, // Optional via nullability + val retryCount: Int? = null +) + +@WorkflowMethod +suspend fun processOrder(params: OrderParams): OrderResult +``` + +This pattern: +- Allows adding new optional fields without breaking existing callers +- Makes all parameters explicit in serialization +- Works consistently across all Temporal SDKs + ## Interfaceless Workflows and Activities **Status:** Decision needed diff --git a/kotlin/workflows/definition.md b/kotlin/workflows/definition.md index afe5ffd..1c29adc 100644 --- a/kotlin/workflows/definition.md +++ b/kotlin/workflows/definition.md @@ -96,6 +96,27 @@ String result = workflow.getGreeting("Temporal"); > **Note:** Java cannot directly use Kotlin suspend interfaces because suspend functions compile to methods with an extra `Continuation` parameter. The untyped stub approach is recommended for Java clients. +## Parameter Restrictions + +**Default parameter values are not allowed** in workflow methods. This is validated at worker registration time. + +```kotlin +// ✗ NOT ALLOWED - will fail at registration +@WorkflowMethod +suspend fun processOrder(orderId: String, priority: Int = 0) // Error! + +// ✓ CORRECT - use a parameter object with optional fields +data class ProcessOrderParams( + val orderId: String, + val priority: Int? = null +) + +@WorkflowMethod +suspend fun processOrder(params: ProcessOrderParams): OrderResult +``` + +**Rationale:** Default values create replay safety issues (changing defaults breaks determinism), serialization ambiguity, and cross-language compatibility problems. See [full discussion](../open-questions.md#default-parameter-values-not-allowed). + ## Key Characteristics * Use `coroutineScope`, `async`, `launch` for concurrent execution diff --git a/kotlin/workflows/signals-queries.md b/kotlin/workflows/signals-queries.md index 68c9c1d..a61b1d0 100644 --- a/kotlin/workflows/signals-queries.md +++ b/kotlin/workflows/signals-queries.md @@ -2,6 +2,8 @@ Signals and updates follow the same suspend/non-suspend pattern as workflow methods. Queries are always synchronous (never suspend) and can be defined as properties. +> **Note:** Default parameter values are not allowed in signal, query, or update handlers. Use parameter objects with optional fields instead. See [Parameter Restrictions](../open-questions.md#default-parameter-values-not-allowed). + ## Defining Handlers ```kotlin From a98bf12e58b6658e362ad540c5db13149b8c2e0a Mon Sep 17 00:00:00 2001 From: Maxim Fateev <1463622+mfateev@users.noreply.github.com> Date: Mon, 12 Jan 2026 20:27:04 -0600 Subject: [PATCH 73/83] Rename KActivityContext to KActivityExecutionContext, remove logger --- kotlin/activities/README.md | 2 +- kotlin/activities/implementation.md | 20 +++++-------- kotlin/activities/local-activities.md | 5 ++-- kotlin/api-parity.md | 2 +- kotlin/implementation/sdk-implementation.md | 4 +-- .../suspend-activities-design.md | 28 +++++++++---------- kotlin/kotlin-idioms.md | 2 +- kotlin/migration.md | 11 ++++---- 8 files changed, 33 insertions(+), 41 deletions(-) diff --git a/kotlin/activities/README.md b/kotlin/activities/README.md index 08da097..b306a15 100644 --- a/kotlin/activities/README.md +++ b/kotlin/activities/README.md @@ -59,7 +59,7 @@ val result = KWorkflow.executeActivity( | Execute activity | `KWorkflow.executeActivity(Interface::method, options, args)` | | Execute by name | `KWorkflow.executeActivity("name", options, args)` | | Local activity | `KWorkflow.executeLocalActivity(Interface::method, options, args)` | -| Heartbeat | `KActivity.context.heartbeat(details)` | +| Heartbeat | `KActivity.executionContext.heartbeat(details)` | ## Related diff --git a/kotlin/activities/implementation.md b/kotlin/activities/implementation.md index 566f307..9abf8ea 100644 --- a/kotlin/activities/implementation.md +++ b/kotlin/activities/implementation.md @@ -75,7 +75,7 @@ For long-running activities, use heartbeating to report progress and detect canc ```kotlin class LongRunningActivitiesImpl : LongRunningActivities { override suspend fun processLargeFile(filePath: String): ProcessResult { - val context = KActivity.context + val context = KActivity.executionContext val lines = File(filePath).readLines() lines.forEachIndexed { index, line -> @@ -96,7 +96,7 @@ Retrieve heartbeat details from a previous failed attempt: ```kotlin override suspend fun resumableProcess(data: List): ProcessResult { - val context = KActivity.context + val context = KActivity.executionContext // Get progress from previous attempt if available val startIndex = context.heartbeatDetails() ?: 0 @@ -112,11 +112,11 @@ override suspend fun resumableProcess(data: List): ProcessResult { ## KActivity API -`KActivity.context` provides access to the activity execution context for both regular and local activities: +`KActivity.executionContext` provides access to the activity execution context for both regular and local activities: ```kotlin // In activity implementation -val context = KActivity.context +val context = KActivity.executionContext // Get activity info (works for both regular and local activities) val info = context.info @@ -129,31 +129,25 @@ context.heartbeat(progressDetails) // Get heartbeat details from previous attempt (empty for local activities) val previousProgress = context.heartbeatDetails() -// Logging (works for both) -val log = context.logger() - // Regular activities only - throws UnsupportedOperationException for local activities context.doNotCompleteOnReturn() val taskToken = context.taskToken ``` -## KActivityContext Interface +## KActivityExecutionContext Interface ```kotlin -interface KActivityContext { +interface KActivityExecutionContext { val info: KActivityInfo fun heartbeat(details: Any? = null) // No-op for local activities fun heartbeatDetails(detailsClass: Class): T? // Empty for local activities val taskToken: ByteArray // Throws for local activities fun doNotCompleteOnReturn() // Throws for local activities val isDoNotCompleteOnReturn: Boolean - fun logger(): Logger - fun logger(name: String): Logger - fun logger(clazz: Class<*>): Logger } // Reified extension for easier Kotlin usage -inline fun KActivityContext.heartbeatDetails(): T? +inline fun KActivityExecutionContext.heartbeatDetails(): T? ``` ## KActivityInfo Interface diff --git a/kotlin/activities/local-activities.md b/kotlin/activities/local-activities.md index d2b2e39..a384ba0 100644 --- a/kotlin/activities/local-activities.md +++ b/kotlin/activities/local-activities.md @@ -63,14 +63,13 @@ data class KLocalActivityOptions( ) ``` -## KActivity.context in Local Activities +## KActivity.executionContext in Local Activities -`KActivity.context` is available in local activities with limited functionality: +`KActivity.executionContext` is available in local activities with limited functionality: | Feature | Local Activity Behavior | |---------|------------------------| | `context.info` | Works (`info.isLocal` returns `true`) | -| `context.logger()` | Works | | `context.heartbeat()` | No-op (ignored) | | `context.heartbeatDetails()` | Returns `null` | | `context.taskToken` | Throws `UnsupportedOperationException` | diff --git a/kotlin/api-parity.md b/kotlin/api-parity.md index 0d545e2..faa0f7e 100644 --- a/kotlin/api-parity.md +++ b/kotlin/api-parity.md @@ -28,7 +28,7 @@ The following Java SDK APIs are **not needed** in the Kotlin SDK due to language ### Single heartbeat() API -The Kotlin SDK provides a single `heartbeat()` method on `KActivityContext` for both sync and suspend activities: +The Kotlin SDK provides a single `heartbeat()` method on `KActivityExecutionContext` for both sync and suspend activities: ```kotlin context.heartbeat(progressDetails) diff --git a/kotlin/implementation/sdk-implementation.md b/kotlin/implementation/sdk-implementation.md index 1a52400..7ee8672 100644 --- a/kotlin/implementation/sdk-implementation.md +++ b/kotlin/implementation/sdk-implementation.md @@ -7,7 +7,7 @@ For public API and developer experience, see the [SDK API documentation](../READ ## Phases * **Phase 1 (COMPLETE)** - Coroutine-based workflows, untyped activity/child workflow execution, pluggable WorkflowImplementationFactory, core Kotlin idioms (Duration, null safety, KWorkflowInfo), signals/queries (annotation + dynamic handlers), updates (annotation-based), standard `delay()` and `coroutineScope { async { } }` support -* **Phase 2** - Typed activity execution, typed child workflow execution, KChildWorkflowHandle, dynamic update handlers, update validators, KActivity/KActivityInfo/KActivityContext wrappers, client & worker API (KWorkflowClient, KWorkerFactory, KWorkflowHandle, startWorkflow, signalWithStart, etc.) +* **Phase 2** - Typed activity execution, typed child workflow execution, KChildWorkflowHandle, dynamic update handlers, update validators, KActivity/KActivityInfo/KActivityExecutionContext wrappers, client & worker API (KWorkflowClient, KWorkerFactory, KWorkflowHandle, startWorkflow, signalWithStart, etc.) * **Phase 3** - Interceptor interfaces, testing framework > **Note:** Nexus support is a separate project and will be addressed independently. @@ -96,7 +96,7 @@ The `temporal-kotlin` module already provides Kotlin extensions for the Java SDK | `KWorkflowInfo` | Kotlin wrapper for WorkflowInfo with nullable types | ✅ Done | | `KActivity` | Entry point for activity APIs (like `Activity` in Java) | Phase 2 | | `KActivityInfo` | Kotlin wrapper for ActivityInfo with nullable types | Phase 2 | -| `KActivityContext` | Kotlin wrapper for ActivityExecutionContext | Phase 2 | +| `KActivityExecutionContext` | Kotlin wrapper for ActivityExecutionContext | Phase 2 | | **Client & Worker API** | | | | `KWorkflowClient` | Kotlin client with suspend functions for starting/executing workflows | Phase 2 | | `KWorkerFactory` | Kotlin worker factory with KotlinPlugin pre-configured | Phase 2 | diff --git a/kotlin/implementation/suspend-activities-design.md b/kotlin/implementation/suspend-activities-design.md index 8d3850a..5edd82d 100644 --- a/kotlin/implementation/suspend-activities-design.md +++ b/kotlin/implementation/suspend-activities-design.md @@ -144,9 +144,9 @@ internal class SuspendActivityInvoker( // Launch coroutine - this returns immediately! scope.launch { // Create activity context element for coroutine - val kContext = KActivityContext(context, completionClient, job) + val kContext = KActivityExecutionContext(context, completionClient, job) - withContext(KActivityContextElement(kContext)) { + withContext(KActivityExecutionContextElement(kContext)) { try { // Start auto-heartbeat if configured val heartbeatJob = heartbeatInterval?.let { interval -> @@ -206,14 +206,14 @@ internal class SuspendActivityInvoker( } ``` -### Component 2: KActivityContext (Coroutine-aware) +### Component 2: KActivityExecutionContext (Coroutine-aware) Activity context accessible from within coroutines: ```kotlin -// io.temporal.kotlin.internal.KActivityContext +// io.temporal.kotlin.internal.KActivityExecutionContext -internal class KActivityContext( +internal class KActivityExecutionContext( private val javaContext: ActivityExecutionContext, private val completionClient: ManualActivityCompletionClient, private val parentJob: Job @@ -246,10 +246,10 @@ internal class KActivityContext( } // Coroutine context element -internal class KActivityContextElement( - val context: KActivityContext +internal class KActivityExecutionContextElement( + val context: KActivityExecutionContext ) : AbstractCoroutineContextElement(Key) { - companion object Key : CoroutineContext.Key + companion object Key : CoroutineContext.Key } ``` @@ -266,7 +266,7 @@ public object KActivity { */ public fun getInfo(): KActivityInfo { // Try coroutine context first (suspend activities) - val kContext = currentCoroutineContextOrNull()?.get(KActivityContextElement)?.context + val kContext = currentCoroutineContextOrNull()?.get(KActivityExecutionContextElement)?.context if (kContext != null) { return KActivityInfo(kContext.info) } @@ -281,7 +281,7 @@ public object KActivity { * @throws CancellationException if the activity has been cancelled */ public suspend fun heartbeat(details: T? = null) { - val kContext = coroutineContext[KActivityContextElement]?.context + val kContext = coroutineContext[KActivityExecutionContextElement]?.context ?: throw IllegalStateException("heartbeat() must be called from within an activity") kContext.heartbeat(details) } @@ -290,7 +290,7 @@ public object KActivity { * Gets heartbeat details from a previous attempt. */ public inline fun getHeartbeatDetails(): T? { - val kContext = currentCoroutineContextOrNull()?.get(KActivityContextElement)?.context + val kContext = currentCoroutineContextOrNull()?.get(KActivityExecutionContextElement)?.context if (kContext != null) { return kContext.getHeartbeatDetails() } @@ -303,7 +303,7 @@ public object KActivity { * Gets the task token for external completion scenarios. */ public fun getTaskToken(): ByteArray { - val kContext = currentCoroutineContextOrNull()?.get(KActivityContextElement)?.context + val kContext = currentCoroutineContextOrNull()?.get(KActivityExecutionContextElement)?.context if (kContext != null) { return kContext.taskToken } @@ -892,8 +892,8 @@ suspend fun myActivity(): Result ### Phase 1: Core Infrastructure 1. Create `SuspendActivityInvoker` class -2. Create `KActivityContext` with coroutine support -3. Create `KActivityContextElement` for coroutine context +2. Create `KActivityExecutionContext` with coroutine support +3. Create `KActivityExecutionContextElement` for coroutine context 4. Implement basic suspend activity execution ### Phase 2: Cancellation Handling diff --git a/kotlin/kotlin-idioms.md b/kotlin/kotlin-idioms.md index 179440a..be0e010 100644 --- a/kotlin/kotlin-idioms.md +++ b/kotlin/kotlin-idioms.md @@ -151,7 +151,7 @@ val info = KWorkflow.info val parentId: String? = info.parentWorkflowId // null if no parent // Activity heartbeat details -val progress = KActivity.context.heartbeatDetails() +val progress = KActivity.executionContext.heartbeatDetails() val startIndex = progress ?: 0 // Elvis operator for default ``` diff --git a/kotlin/migration.md b/kotlin/migration.md index ec8ffb0..5123917 100644 --- a/kotlin/migration.md +++ b/kotlin/migration.md @@ -56,12 +56,11 @@ | `scope.cancel()` | `job.cancel()` | | `CancellationScope.isCancelRequested()` | `!isActive` | | **KActivity Object** | | -| `Activity.getExecutionContext()` | `KActivity.context` | -| `context.getInfo()` | `KActivity.context.info` or `KActivity.info` | -| `context.heartbeat(details)` | `KActivity.context.heartbeat(details)` | -| `context.getHeartbeatDetails(cls)` | `KActivity.context.heartbeatDetails()` | -| `Activity.getLogger()` | `KActivity.logger()` | -| `context.doNotCompleteOnReturn()` | `KActivity.context.doNotCompleteOnReturn()` | +| `Activity.getExecutionContext()` | `KActivity.executionContext` | +| `context.getInfo()` | `KActivity.executionContext.info` or `KActivity.info` | +| `context.heartbeat(details)` | `KActivity.executionContext.heartbeat(details)` | +| `context.getHeartbeatDetails(cls)` | `KActivity.executionContext.heartbeatDetails()` | +| `context.doNotCompleteOnReturn()` | `KActivity.executionContext.doNotCompleteOnReturn()` | | **Testing** | | | `TestWorkflowEnvironment.newInstance()` | `KTestWorkflowEnvironment.newInstance()` | | `testEnv.newWorker(taskQueue)` | `testEnv.newWorker(taskQueue)` → `KWorker` | From 4306ffa0108daa6dfb7dca175c774a8f861b4cd3 Mon Sep 17 00:00:00 2001 From: Maxim Fateev <1463622+mfateev@users.noreply.github.com> Date: Mon, 12 Jan 2026 20:33:54 -0600 Subject: [PATCH 74/83] Remove heartbeatDetails from KActivityInfo (use KActivityExecutionContext instead) --- kotlin/activities/implementation.md | 3 --- 1 file changed, 3 deletions(-) diff --git a/kotlin/activities/implementation.md b/kotlin/activities/implementation.md index 9abf8ea..a939a82 100644 --- a/kotlin/activities/implementation.md +++ b/kotlin/activities/implementation.md @@ -173,9 +173,6 @@ interface KActivityInfo { val startToCloseTimeout: Duration? val heartbeatTimeout: Duration? - // Heartbeat state - val heartbeatDetails: Any? - // Task token for async completion (throws for local activities) val taskToken: ByteArray } From fb99a7683817651bb0662488a1a94ee9880eb503 Mon Sep 17 00:00:00 2001 From: Maxim Fateev <1463622+mfateev@users.noreply.github.com> Date: Mon, 12 Jan 2026 20:40:29 -0600 Subject: [PATCH 75/83] Fix KLocalActivityOptions to match Java LocalActivityOptions - Remove priority (not in Java) - Add scheduleToStartTimeout - Add doNotIncludeArgumentsIntoMarker --- kotlin/activities/local-activities.md | 5 +++-- kotlin/configuration/koptions.md | 5 +++-- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/kotlin/activities/local-activities.md b/kotlin/activities/local-activities.md index a384ba0..9bde98a 100644 --- a/kotlin/activities/local-activities.md +++ b/kotlin/activities/local-activities.md @@ -55,11 +55,12 @@ object KWorkflow { data class KLocalActivityOptions( val startToCloseTimeout: Duration? = null, val scheduleToCloseTimeout: Duration? = null, + val scheduleToStartTimeout: Duration? = null, val localRetryThreshold: Duration? = null, val retryOptions: KRetryOptions? = null, + val doNotIncludeArgumentsIntoMarker: Boolean = false, // Experimental - @Experimental val summary: String? = null, - @Experimental val priority: Priority? = null + @Experimental val summary: String? = null ) ``` diff --git a/kotlin/configuration/koptions.md b/kotlin/configuration/koptions.md index 54f7747..5d1f718 100644 --- a/kotlin/configuration/koptions.md +++ b/kotlin/configuration/koptions.md @@ -61,11 +61,12 @@ val result = KWorkflow.executeActivity( data class KLocalActivityOptions( val startToCloseTimeout: Duration? = null, val scheduleToCloseTimeout: Duration? = null, + val scheduleToStartTimeout: Duration? = null, val localRetryThreshold: Duration? = null, val retryOptions: KRetryOptions? = null, + val doNotIncludeArgumentsIntoMarker: Boolean = false, // Experimental - @Experimental val summary: String? = null, - @Experimental val priority: Priority? = null + @Experimental val summary: String? = null ) ``` From 99b668e1ae70044c2d45037c52b5ac54ac98146d Mon Sep 17 00:00:00 2001 From: Maxim Fateev <1463622+mfateev@users.noreply.github.com> Date: Mon, 12 Jan 2026 22:19:45 -0600 Subject: [PATCH 76/83] Update Kotlin SDK API docs based on PR review decisions Key changes: - Add KWorkflow.delay() with optional KTimerOptions - Rename typedSearchAttributes to searchAttributes throughout - Rename getWorkflowHandle() to workflowHandle() with result type overload - Add KWorkflowClient.connect(options) pattern - Update handle type hierarchy: KWorkflowHandleUntyped -> KWorkflowHandle -> KWorkflowHandleWithResult - Add KStartUpdateOptions/KUpdateOptions with waitForStage required - Add KWorkflowExecutionInfo base class for listWorkflows() - Add KWorkflowClientInterceptor for client-side interception - Clarify cancellation docs re: standard Kotlin behavior - Recommend factory.run() pattern over start() --- kotlin/README.md | 9 +- kotlin/api-parity.md | 18 +- kotlin/client/README.md | 16 +- kotlin/client/advanced.md | 39 ++-- kotlin/client/workflow-client.md | 62 ++++-- kotlin/client/workflow-handle.md | 291 ++++++++++++++++----------- kotlin/configuration/interceptors.md | 129 +++++++++++- kotlin/configuration/koptions.md | 6 +- kotlin/migration.md | 10 +- kotlin/workflows/cancellation.md | 8 +- kotlin/workflows/continue-as-new.md | 2 +- kotlin/workflows/signals-queries.md | 6 +- 12 files changed, 415 insertions(+), 181 deletions(-) diff --git a/kotlin/README.md b/kotlin/README.md index 8280276..887ae06 100644 --- a/kotlin/README.md +++ b/kotlin/README.md @@ -112,12 +112,17 @@ class GreetingWorkflowImpl : GreetingWorkflow { } } -// Start worker +// Start worker (recommended: use run() which blocks and propagates fatal errors) val factory = KWorkerFactory(client) val worker = factory.newWorker("greetings") worker.registerWorkflowImplementationTypes(GreetingWorkflowImpl::class) worker.registerActivitiesImplementations(GreetingActivitiesImpl()) -factory.start() +factory.run() // Blocks until shutdown, propagates fatal errors + +// Alternative for advanced use cases: +// factory.start() // Returns immediately +// ... do other work ... +// factory.shutdown() // Execute workflow val result = client.executeWorkflow( diff --git a/kotlin/api-parity.md b/kotlin/api-parity.md index faa0f7e..f897bb4 100644 --- a/kotlin/api-parity.md +++ b/kotlin/api-parity.md @@ -8,8 +8,8 @@ The following Java SDK APIs are **not needed** in the Kotlin SDK due to language | Java SDK API | Reason Not Needed | |--------------|-------------------| -| `Workflow.sleep(Duration)` | Use `kotlinx.coroutines.delay()` - intercepted by dispatcher | -| `Workflow.newTimer(Duration)` | Use `async { delay(duration) }` for racing timers | +| `Workflow.sleep(Duration)` | Use `kotlinx.coroutines.delay()` or `KWorkflow.delay()` with options | +| `Workflow.newTimer(Duration)` | Use `async { delay(duration) }` or `async { KWorkflow.delay(duration, options) }` for racing timers | | `Workflow.wrap(Exception)` | Kotlin has no checked exceptions - not needed | | `Activity.wrap(Throwable)` | Kotlin has no checked exceptions - not needed | | `Workflow.newCancellationScope(...)` | Use Kotlin's `coroutineScope { }` with structured concurrency | @@ -67,8 +67,8 @@ The following Java SDK workflow APIs have Kotlin equivalents in `KWorkflow`: | Java SDK API | Kotlin SDK | |--------------|------------| -| `Workflow.getTypedSearchAttributes()` | `KWorkflow.typedSearchAttributes` | -| `Workflow.upsertTypedSearchAttributes(...)` | `KWorkflow.upsertTypedSearchAttributes()` | +| `Workflow.getTypedSearchAttributes()` | `KWorkflow.searchAttributes` | +| `Workflow.upsertTypedSearchAttributes(...)` | `KWorkflow.upsertSearchAttributes()` | | `Workflow.getMemo(key, class)` | `KWorkflow.memo` | | `Workflow.upsertMemo(...)` | `KWorkflow.upsertMemo()` | @@ -85,6 +85,14 @@ The following Java SDK workflow APIs have Kotlin equivalents in `KWorkflow`: | `Workflow.getCurrentDetails()` | `KWorkflow.currentDetails` | | `Workflow.getMetricsScope()` | `KWorkflow.metricsScope` | +### Timers + +| Java SDK API | Kotlin SDK | +|--------------|------------| +| `Workflow.sleep(...)` | `kotlinx.coroutines.delay()` - standard Kotlin, or `KWorkflow.delay()` | +| N/A | `KWorkflow.delay(duration)` - simple delay | +| N/A | `KWorkflow.delay(duration, options)` - with `KTimerOptions` for summary | + ### Side Effects & Utilities | Java SDK API | Kotlin SDK | @@ -141,7 +149,7 @@ The following areas use the same API and behavior as the Java SDK: | `getParentRunId()` | `KWorkflowInfo.parentRunId: String?` | | `getContinuedExecutionRunId()` | `KWorkflowInfo.continuedExecutionRunId: String?` | | `getCronSchedule()` | `KWorkflowInfo.cronSchedule: String?` | -| `getSearchAttributes()` | `KWorkflowInfo.typedSearchAttributes` | +| `getSearchAttributes()` | `KWorkflowInfo.searchAttributes` | | `getHistoryLength()` | `KWorkflowInfo.historyLength` | | `isContinueAsNewSuggested()` | `KWorkflowInfo.isContinueAsNewSuggested` | | `getFirstExecutionRunId()` | `KWorkflowInfo.firstExecutionRunId` | diff --git a/kotlin/client/README.md b/kotlin/client/README.md index cf0b076..3548883 100644 --- a/kotlin/client/README.md +++ b/kotlin/client/README.md @@ -19,12 +19,12 @@ The Kotlin SDK provides `KWorkflowClient` with suspend functions and type-safe w ### Creating a Client ```kotlin -val service = WorkflowServiceStubs.newLocalServiceStubs() - -val client = KWorkflowClient(service) { - setNamespace("default") - setDataConverter(myConverter) -} +val client = KWorkflowClient.connect( + KWorkflowClientOptions( + target = "localhost:7233", + namespace = "default" + ) +) ``` ### Starting Workflows @@ -55,7 +55,7 @@ val result = handle.result() ### Interacting with Workflows ```kotlin -val handle = client.getWorkflowHandle("order-123") +val handle = client.workflowHandle("order-123") // Signal handle.signal(OrderWorkflow::updatePriority, Priority.HIGH) @@ -76,7 +76,7 @@ handle.cancel() |---------|-----| | Execute workflow | `client.executeWorkflow(Interface::method, options, args)` | | Start workflow | `client.startWorkflow(Interface::method, options, args)` | -| Get handle by ID | `client.getWorkflowHandle(workflowId)` | +| Get handle by ID | `client.workflowHandle(workflowId)` | | Signal with start | `client.signalWithStart(...)` | | Update with start | `client.executeUpdateWithStart(...)` | diff --git a/kotlin/client/advanced.md b/kotlin/client/advanced.md index 135923a..458ff62 100644 --- a/kotlin/client/advanced.md +++ b/kotlin/client/advanced.md @@ -36,10 +36,11 @@ Atomically start a workflow and send an update. Behavior depends on `workflowIdC ```kotlin /** * Options for update-with-start operations. + * Note: waitForStage is required for startUpdateWithStart (no default value). */ data class KUpdateWithStartOptions( - /** Stage to wait for before returning (required for startUpdateWithStart) */ - val waitForStage: WorkflowUpdateStage = WorkflowUpdateStage.ACCEPTED, + /** Stage to wait for before returning (required - no default) */ + val waitForStage: WorkflowUpdateStage, /** Optional update ID for idempotency */ val updateId: String? = null @@ -61,11 +62,12 @@ val handle = client.withStartWorkflowOperation( ) // Execute update with start (waits for update completion) +// Args before options val updateResult: Boolean = client.executeUpdateWithStart( handle, OrderWorkflow::addItem, - KUpdateWithStartOptions(), - newItem + newItem, // arg before options + KUpdateWithStartOptions(waitForStage = WorkflowUpdateStage.COMPLETED) ) // Handle is now usable @@ -87,11 +89,12 @@ val handle = client.withStartWorkflowOperation( ) // Start update and return immediately after it's accepted +// waitForStage is required - no default val updateHandle: KUpdateHandle = client.startUpdateWithStart( handle, OrderWorkflow::addItem, - KUpdateWithStartOptions(waitForStage = WorkflowUpdateStage.ACCEPTED), - newItem + newItem, // arg before options + KUpdateWithStartOptions(waitForStage = WorkflowUpdateStage.ACCEPTED) ) // Handle is now usable, get workflow result @@ -112,13 +115,13 @@ class KWorkflowClient { fun withStartWorkflowOperation( workflow: KSuspendFunction1, options: KWorkflowOptions - ): KTypedWorkflowHandle + ): KWorkflowHandleWithResult fun withStartWorkflowOperation( workflow: KSuspendFunction2, options: KWorkflowOptions, arg1: A1 - ): KTypedWorkflowHandle + ): KWorkflowHandleWithResult /** * Atomically start a workflow and send a signal. @@ -126,7 +129,7 @@ class KWorkflowClient { * After this call, the handle becomes usable. */ suspend fun signalWithStart( - handle: KTypedWorkflowHandle, + handle: KWorkflowHandleWithResult, signal: KSuspendFunction2, signalArg: SA1 ) @@ -136,16 +139,16 @@ class KWorkflowClient { * After this call, the handle becomes usable. */ suspend fun executeUpdateWithStart( - handle: KTypedWorkflowHandle, + handle: KWorkflowHandleWithResult, update: KSuspendFunction1, options: KUpdateWithStartOptions = KUpdateWithStartOptions() ): UR suspend fun executeUpdateWithStart( - handle: KTypedWorkflowHandle, + handle: KWorkflowHandleWithResult, update: KSuspendFunction2, - options: KUpdateWithStartOptions = KUpdateWithStartOptions(), - updateArg: UA1 + updateArg: UA1, + options: KUpdateWithStartOptions = KUpdateWithStartOptions() ): UR /** @@ -154,16 +157,16 @@ class KWorkflowClient { * After this call, the handle becomes usable. */ suspend fun startUpdateWithStart( - handle: KTypedWorkflowHandle, + handle: KWorkflowHandleWithResult, update: KSuspendFunction1, - options: KUpdateWithStartOptions = KUpdateWithStartOptions() + options: KUpdateWithStartOptions // waitForStage required - no default ): KUpdateHandle suspend fun startUpdateWithStart( - handle: KTypedWorkflowHandle, + handle: KWorkflowHandleWithResult, update: KSuspendFunction2, - options: KUpdateWithStartOptions = KUpdateWithStartOptions(), - updateArg: UA1 + updateArg: UA1, + options: KUpdateWithStartOptions ): KUpdateHandle } ``` diff --git a/kotlin/client/workflow-client.md b/kotlin/client/workflow-client.md index 9258c5b..8497896 100644 --- a/kotlin/client/workflow-client.md +++ b/kotlin/client/workflow-client.md @@ -2,14 +2,19 @@ ## Creating a Client +Use `KWorkflowClient.connect()` to create a client: + ```kotlin -val service = WorkflowServiceStubs.newLocalServiceStubs() +// Connect to Temporal (like newer SDKs) +val client = KWorkflowClient.connect( + KWorkflowClientOptions( + target = "localhost:7233", + namespace = "default" + ) +) -// Create KWorkflowClient with DSL configuration -val client = KWorkflowClient(service) { - setNamespace("default") - setDataConverter(myConverter) -} +// Access raw gRPC services when needed +val workflowService = client.workflowService // For blocking calls from non-suspend contexts, use runBlocking val result = runBlocking { @@ -24,14 +29,18 @@ val result = runBlocking { ```kotlin /** * Kotlin workflow client providing suspend functions and type-safe workflow APIs. - * - * @param service The WorkflowServiceStubs to connect to - * @param options DSL builder for WorkflowClientOptions */ -class KWorkflowClient( - service: WorkflowServiceStubs, - options: WorkflowClientOptions.Builder.() -> Unit = {} -) { +class KWorkflowClient private constructor(...) { + companion object { + /** + * Connect to Temporal service and create a client. + */ + suspend fun connect(options: KWorkflowClientOptions): KWorkflowClient + } + + /** The underlying WorkflowServiceStubs for advanced use cases */ + val workflowService: WorkflowServiceStubs + /** The underlying WorkflowClient for advanced use cases */ val workflowClient: WorkflowClient @@ -42,13 +51,13 @@ class KWorkflowClient( suspend fun startWorkflow( workflow: KSuspendFunction1, options: KWorkflowOptions - ): KTypedWorkflowHandle + ): KWorkflowHandleWithResult suspend fun startWorkflow( workflow: KSuspendFunction2, options: KWorkflowOptions, arg: A1 - ): KTypedWorkflowHandle + ): KWorkflowHandleWithResult // Overloads for 2-6 arguments... @@ -73,15 +82,22 @@ class KWorkflowClient( * Get a typed handle for an existing workflow by ID. * Use this to signal, query, or get results from a workflow started elsewhere. */ - inline fun getWorkflowHandle(workflowId: String): KWorkflowHandle - inline fun getWorkflowHandle(workflowId: String, runId: String): KWorkflowHandle + inline fun workflowHandle(workflowId: String): KWorkflowHandle + inline fun workflowHandle(workflowId: String, runId: String): KWorkflowHandle + + /** + * Get a typed handle with known result type for an existing workflow by ID. + * Use when you know both the workflow type and result type at compile time. + */ + inline fun workflowHandle(workflowId: String): KWorkflowHandleWithResult + inline fun workflowHandle(workflowId: String, runId: String): KWorkflowHandleWithResult /** * Get an untyped handle for an existing workflow by ID. * Use when you don't know the workflow type at compile time. */ - fun getUntypedWorkflowHandle(workflowId: String): WorkflowHandle - fun getUntypedWorkflowHandle(workflowId: String, runId: String): WorkflowHandle + fun untypedWorkflowHandle(workflowId: String): KWorkflowHandleUntyped + fun untypedWorkflowHandle(workflowId: String, runId: String): KWorkflowHandleUntyped } ``` @@ -109,6 +125,14 @@ val handle = client.startWorkflow( "Temporal" ) val result = handle.result() // Type inferred as String from method reference + +// Get handle for existing workflow +val existingHandle = client.workflowHandle("greeting-123") +val result = existingHandle.result() // Must specify result type + +// Or with result type known +val typedHandle = client.workflowHandle("greeting-123") +val result = typedHandle.result() // Result type already known ``` ## KWorkflowOptions diff --git a/kotlin/client/workflow-handle.md b/kotlin/client/workflow-handle.md index ca60529..7cd333a 100644 --- a/kotlin/client/workflow-handle.md +++ b/kotlin/client/workflow-handle.md @@ -2,11 +2,43 @@ For interacting with existing workflows (signals, queries, results, cancellation), use a typed or untyped handle. +## Handle Type Hierarchy + +```kotlin +KWorkflowHandleUntyped // Untyped base +KWorkflowHandle : KWorkflowHandleUntyped // Typed workflow, untyped result +KWorkflowHandleWithResult : KWorkflowHandle // Fully typed +``` + +## Update Options + +```kotlin +/** + * Options for executing updates. + */ +data class KUpdateOptions( + /** Optional update ID for idempotency */ + val updateId: String? = null +) + +/** + * Options for starting updates (not waiting for completion). + * Note: waitForStage is required with no default value. + */ +data class KStartUpdateOptions( + /** Stage to wait for before returning (required) */ + val waitForStage: WorkflowUpdateStage, // No default - must be specified + + /** Optional update ID for idempotency */ + val updateId: String? = null +) +``` + ## Typed Handles ```kotlin // Get typed handle for existing workflow by ID -val handle = client.getWorkflowHandle("order-123") +val handle = client.workflowHandle("order-123") // Send signal - method reference provides type safety handle.signal(OrderWorkflow::updatePriority, Priority.HIGH) @@ -15,17 +47,25 @@ handle.signal(OrderWorkflow::updatePriority, Priority.HIGH) val status = handle.query(OrderWorkflow::status) val count = handle.query(OrderWorkflow::getItemCount) -// Get result (suspends until workflow completes) +// Get result (suspends until workflow completes) - must specify type val result = handle.result() +// Or get handle with known result type +val typedHandle = client.workflowHandle("order-123") +val result = typedHandle.result() // Type already known + // Updates - execute and wait for result -val updateResult = handle.executeUpdate(OrderWorkflow::addItem, newItem) +val updateResult = handle.executeUpdate( + OrderWorkflow::addItem, + newItem, + KUpdateOptions(updateId = "add-item-1") // Options always last +) // Updates - start and get handle (don't wait for completion) val updateHandle = handle.startUpdate( OrderWorkflow::addItem, - waitForStage = WorkflowUpdateStage.ACCEPTED, - arg = newItem + newItem, + KStartUpdateOptions(waitForStage = WorkflowUpdateStage.ACCEPTED) // waitForStage required ) // ... do other work ... val result = updateHandle.result() // Wait for result when needed @@ -42,19 +82,18 @@ println("Status: ${description.status}") ## Architectural Note: Classes vs Interfaces -All handle types (`KWorkflowHandle`, `KTypedWorkflowHandle`, `KUpdateHandle`, `WorkflowHandle`, `KChildWorkflowHandle`) are **classes** rather than interfaces. This design choice enables: +All handle types (`KWorkflowHandleUntyped`, `KWorkflowHandle`, `KWorkflowHandleWithResult`, `KUpdateHandle`, `KChildWorkflowHandle`) are **classes** rather than interfaces. This design choice enables: 1. **Reified type parameters** - `inline fun ` methods directly on handle types 2. **Better IDE discoverability** - Methods appear directly in autocomplete 3. **No extension function workarounds** - No need for separate extension functions for reified generics 4. **Full testing support** - mockk and other frameworks can mock classes -## KWorkflowHandle API +## KWorkflowHandleUntyped API ```kotlin -// Base handle - returned by getWorkflowHandle(id) -// Result type is unknown, must specify when calling result() -open class KWorkflowHandle( +// Base untyped handle - returned by untypedWorkflowHandle(id) +open class KWorkflowHandleUntyped( val workflowId: String, val runId: String?, val execution: WorkflowExecution, @@ -64,6 +103,64 @@ open class KWorkflowHandle( suspend fun result(resultClass: Class): R inline suspend fun result(): R // Reified version + // Signals by name + suspend fun signal(signalName: String, vararg args: Any?) + + // Queries by name (suspend for network I/O) + suspend fun query(queryName: String, resultClass: Class, vararg args: Any?): R + inline suspend fun query(queryName: String, vararg args: Any?): R // Reified version + + // Updates by name + suspend fun executeUpdate( + updateName: String, + resultClass: Class, + vararg args: Any?, + options: KUpdateOptions = KUpdateOptions() + ): R + inline suspend fun executeUpdate( + updateName: String, + vararg args: Any?, + options: KUpdateOptions = KUpdateOptions() + ): R + + suspend fun startUpdate( + updateName: String, + resultClass: Class, + vararg args: Any?, + options: KStartUpdateOptions // waitForStage is required - no default + ): KUpdateHandle + inline suspend fun startUpdate( + updateName: String, + vararg args: Any?, + options: KStartUpdateOptions + ): KUpdateHandle + + // Get handle for existing update by ID + fun updateHandle(updateId: String, resultClass: Class): KUpdateHandle + inline fun updateHandle(updateId: String): KUpdateHandle // Reified version + + // Lifecycle + suspend fun cancel() + suspend fun terminate(reason: String? = null) + suspend fun describe(): KWorkflowExecutionDescription + + // Java SDK interop + fun toStub(): WorkflowStub +} +``` + +## KWorkflowHandle API + +```kotlin +// Typed handle - returned by workflowHandle(id) +// Result type is unknown, must specify when calling result() +open class KWorkflowHandle( + workflowId: String, + runId: String?, + execution: WorkflowExecution, + // ... internal state +) : KWorkflowHandleUntyped(workflowId, runId, execution) { + // Signals - type-safe method references // Note: Signal handlers can be either suspend or non-suspend functions suspend fun signal(method: KFunction1) @@ -74,61 +171,46 @@ open class KWorkflowHandle( suspend fun query(method: KFunction1): R suspend fun query(method: KFunction2, arg: A1): R - // Updates - execute and wait for result (suspend functions) - suspend fun executeUpdate(method: KSuspendFunction1): R - suspend fun executeUpdate(method: KSuspendFunction2, arg: A1): R - suspend fun executeUpdate(method: KSuspendFunction3, arg1: A1, arg2: A2): R + // Updates - execute and wait for result (options always last) + suspend fun executeUpdate( + method: KSuspendFunction1, + options: KUpdateOptions = KUpdateOptions() + ): R + suspend fun executeUpdate( + method: KSuspendFunction2, + arg: A1, + options: KUpdateOptions = KUpdateOptions() + ): R // ... up to 6 arguments - // Updates - start and return handle without waiting for completion + // Updates - start and return handle (waitForStage required in options) suspend fun startUpdate( method: KSuspendFunction1, - waitForStage: WorkflowUpdateStage = WorkflowUpdateStage.ACCEPTED, - updateId: String? = null + options: KStartUpdateOptions // waitForStage required - no default ): KUpdateHandle suspend fun startUpdate( method: KSuspendFunction2, - waitForStage: WorkflowUpdateStage = WorkflowUpdateStage.ACCEPTED, - updateId: String? = null, - arg: A1 - ): KUpdateHandle - suspend fun startUpdate( - method: KSuspendFunction3, - waitForStage: WorkflowUpdateStage = WorkflowUpdateStage.ACCEPTED, - updateId: String? = null, - arg1: A1, - arg2: A2 + arg: A1, + options: KStartUpdateOptions ): KUpdateHandle // ... up to 6 arguments - - // Get handle for existing update by ID - fun getUpdateHandle(updateId: String, resultClass: Class): KUpdateHandle - inline fun getUpdateHandle(updateId: String): KUpdateHandle // Reified version - - // Lifecycle - suspend fun cancel() - suspend fun terminate(reason: String? = null) - suspend fun describe(): KWorkflowExecutionDescription - - // Java SDK interop - fun toStub(): WorkflowStub } ``` **Note on query methods:** Although queries are synchronous within the workflow, client-side query calls involve network I/O to the Temporal service, which is why they are `suspend` functions following idiomatic Kotlin patterns. -## KTypedWorkflowHandle +## KWorkflowHandleWithResult -Extended handle returned by `startWorkflow()` - result type R is captured from the workflow method reference: +Extended handle returned by `startWorkflow()` or `workflowHandle()` - result type R is known: ```kotlin -class KTypedWorkflowHandle( +class KWorkflowHandleWithResult( workflowId: String, runId: String?, execution: WorkflowExecution, // ... internal state -) : KWorkflowHandle(...) { - // Result type is known from method reference - no type parameter needed +) : KWorkflowHandle(workflowId, runId, execution) { + // Result type is known - no type parameter needed suspend fun result(): R suspend fun result(timeout: java.time.Duration): R } @@ -142,7 +224,7 @@ suspend fun startWorkflow( workflow: KSuspendFunction2, // R is captured here options: KWorkflowOptions, arg: A1 -): KTypedWorkflowHandle // R is preserved in return type +): KWorkflowHandleWithResult // R is preserved in return type // Usage - result type is inferred val handle = client.startWorkflow( @@ -152,9 +234,13 @@ val handle = client.startWorkflow( ) val result: OrderResult = handle.result() // No type parameter needed! -// getWorkflowHandle doesn't know result type -val existingHandle = client.getWorkflowHandle(workflowId) +// workflowHandle with one type param doesn't know result type +val existingHandle = client.workflowHandle(workflowId) val result = existingHandle.result() // Must specify type + +// workflowHandle with two type params knows result type +val typedHandle = client.workflowHandle(workflowId) +val result = typedHandle.result() // Type already known ``` ## KUpdateHandle @@ -170,29 +256,40 @@ class KUpdateHandle( } ``` -## KWorkflowExecutionDescription +## KWorkflowExecutionInfo and KWorkflowExecutionDescription -Kotlin wrapper for workflow execution description with idiomatic API: +Base class `KWorkflowExecutionInfo` is returned by `listWorkflows()`. Extended class `KWorkflowExecutionDescription` is returned by `describe()`: ```kotlin -class KWorkflowExecutionDescription( - private val delegate: WorkflowExecutionDescription -) { - // Properties from WorkflowExecutionMetadata - val execution: WorkflowExecution - val workflowType: String - val taskQueue: String - val startTime: Instant - val executionTime: Instant - val closeTime: Instant? +// Base class returned by listWorkflows() +open class KWorkflowExecutionInfo( + val execution: WorkflowExecution, + val workflowType: String, + val taskQueue: String, + val startTime: Instant, val status: WorkflowExecutionStatus - val historyLength: Long - val parentNamespace: String? - val parentExecution: WorkflowExecution? - val rootExecution: WorkflowExecution? - val firstRunId: String? - val executionDuration: Duration? - val typedSearchAttributes: SearchAttributes + // ... lighter set of fields +) + +// Extended class returned by describe() +class KWorkflowExecutionDescription( + execution: WorkflowExecution, + workflowType: String, + taskQueue: String, + startTime: Instant, + status: WorkflowExecutionStatus, + // Additional fields from describe() + val executionTime: Instant, + val closeTime: Instant?, + val historyLength: Long, + val parentNamespace: String?, + val parentExecution: WorkflowExecution?, + val rootExecution: WorkflowExecution?, + val firstRunId: String?, + val executionDuration: Duration?, + val searchAttributes: SearchAttributes, + // ... additional detail fields +) : KWorkflowExecutionInfo(execution, workflowType, taskQueue, startTime, status) { // Reified memo access inline fun memo(key: String): T? @@ -204,82 +301,48 @@ class KWorkflowExecutionDescription( // Raw response for advanced use cases val rawDescription: DescribeWorkflowExecutionResponse - - // Java interop - fun toWorkflowExecutionDescription(): WorkflowExecutionDescription } ``` **Usage:** ```kotlin +// From describe() - full details val description = handle.describe() println("Type: ${description.workflowType}") println("Status: ${description.status}") val config: MyConfig? = description.memo("config") + +// From listWorkflows() - lighter weight +client.listWorkflows("WorkflowType = 'OrderWorkflow'").collect { info -> + println("${info.workflowType}: ${info.status}") +} ``` ## Untyped Handles -For cases where you don't know the workflow type at compile time: +For cases where you don't know the workflow type at compile time, use `KWorkflowHandleUntyped` (see API definition above): ```kotlin // Untyped handle - signal/query by string name -val untypedHandle = client.getUntypedWorkflowHandle("order-123") +val untypedHandle = client.untypedWorkflowHandle("order-123") // Operations use string names instead of method references untypedHandle.signal("updatePriority", Priority.HIGH) val status = untypedHandle.query("status") val result = untypedHandle.result() -// Updates by name +// Updates by name - options always last, waitForStage required for startUpdate val updateResult = untypedHandle.executeUpdate("addItem", newItem) -val updateHandle = untypedHandle.startUpdate("addItem", arg = newItem) +val updateHandle = untypedHandle.startUpdate( + "addItem", + newItem, + options = KStartUpdateOptions(waitForStage = WorkflowUpdateStage.ACCEPTED) +) // Cancel/terminate work the same untypedHandle.cancel() ``` -```kotlin -class WorkflowHandle( - val workflowId: String, - val runId: String?, - val execution: WorkflowExecution, - // ... internal state -) { - suspend fun result(resultClass: Class): R - inline suspend fun result(): R // Reified version - - suspend fun signal(signalName: String, vararg args: Any?) - - suspend fun query(queryName: String, resultClass: Class, vararg args: Any?): R - inline suspend fun query(queryName: String, vararg args: Any?): R // Reified version - - suspend fun executeUpdate(updateName: String, resultClass: Class, vararg args: Any?): R - inline suspend fun executeUpdate(updateName: String, vararg args: Any?): R // Reified version - - suspend fun startUpdate( - updateName: String, - resultClass: Class, - waitForStage: WorkflowUpdateStage = WorkflowUpdateStage.ACCEPTED, - updateId: String? = null, - vararg args: Any? - ): KUpdateHandle - inline suspend fun startUpdate( - updateName: String, - waitForStage: WorkflowUpdateStage = WorkflowUpdateStage.ACCEPTED, - updateId: String? = null, - vararg args: Any? - ): KUpdateHandle // Reified version - - suspend fun cancel() - suspend fun terminate(reason: String? = null) - suspend fun describe(): KWorkflowExecutionDescription - - // Java SDK interop - fun toStub(): WorkflowStub -} -``` - This pattern matches Python SDK's `WorkflowHandle` with the same method names (`signal`, `query`, `result`, `cancel`, `terminate`, `execute_update`). ## Related diff --git a/kotlin/configuration/interceptors.md b/kotlin/configuration/interceptors.md index ba082b6..78de057 100644 --- a/kotlin/configuration/interceptors.md +++ b/kotlin/configuration/interceptors.md @@ -1,6 +1,11 @@ # Interceptors -Interceptors allow you to intercept workflow and activity executions to add cross-cutting concerns like logging, metrics, tracing, and custom error handling. The Kotlin SDK provides suspend-function-aware interceptors that integrate naturally with coroutines. +Interceptors allow you to intercept workflow, activity, and client operations to add cross-cutting concerns like logging, metrics, tracing, and custom error handling. The Kotlin SDK provides: + +- **KWorkerInterceptor** - Intercepts workflow and activity executions on the worker side +- **KWorkflowClientInterceptor** - Intercepts client-side operations (start, signal, query, update) + +All interceptors are suspend-function-aware and integrate naturally with coroutines. ## KWorkerInterceptor @@ -379,6 +384,128 @@ private class LoggingActivityInterceptor( } ``` +## KWorkflowClientInterceptor + +Client interceptors intercept client-side operations such as starting workflows, sending signals, executing queries, and updates. + +```kotlin +/** + * Intercepts client-side workflow operations. + * + * Prefer extending [KWorkflowClientInterceptorBase] and overriding only the methods you need. + */ +interface KWorkflowClientInterceptor { + /** Called when starting a workflow */ + suspend fun startWorkflow(input: KStartWorkflowInput, next: suspend (KStartWorkflowInput) -> KWorkflowHandleWithResult<*, R>): KWorkflowHandleWithResult<*, R> + + /** Called when signaling a workflow */ + suspend fun signalWorkflow(input: KSignalWorkflowInput, next: suspend (KSignalWorkflowInput) -> Unit) + + /** Called when querying a workflow */ + suspend fun queryWorkflow(input: KQueryWorkflowInput, next: suspend (KQueryWorkflowInput) -> R): R + + /** Called when executing an update on a workflow */ + suspend fun executeUpdate(input: KExecuteUpdateInput, next: suspend (KExecuteUpdateInput) -> R): R + + /** Called when starting an update on a workflow */ + suspend fun startUpdate(input: KStartUpdateInput, next: suspend (KStartUpdateInput) -> KUpdateHandle): KUpdateHandle + + /** Called when canceling a workflow */ + suspend fun cancelWorkflow(input: KCancelWorkflowInput, next: suspend (KCancelWorkflowInput) -> Unit) + + /** Called when terminating a workflow */ + suspend fun terminateWorkflow(input: KTerminateWorkflowInput, next: suspend (KTerminateWorkflowInput) -> Unit) +} + +/** + * Base implementation that passes through all calls. + */ +open class KWorkflowClientInterceptorBase : KWorkflowClientInterceptor { + override suspend fun startWorkflow(input: KStartWorkflowInput, next: suspend (KStartWorkflowInput) -> KWorkflowHandleWithResult<*, R>) = next(input) + override suspend fun signalWorkflow(input: KSignalWorkflowInput, next: suspend (KSignalWorkflowInput) -> Unit) = next(input) + override suspend fun queryWorkflow(input: KQueryWorkflowInput, next: suspend (KQueryWorkflowInput) -> R) = next(input) + override suspend fun executeUpdate(input: KExecuteUpdateInput, next: suspend (KExecuteUpdateInput) -> R) = next(input) + override suspend fun startUpdate(input: KStartUpdateInput, next: suspend (KStartUpdateInput) -> KUpdateHandle) = next(input) + override suspend fun cancelWorkflow(input: KCancelWorkflowInput, next: suspend (KCancelWorkflowInput) -> Unit) = next(input) + override suspend fun terminateWorkflow(input: KTerminateWorkflowInput, next: suspend (KTerminateWorkflowInput) -> Unit) = next(input) +} +``` + +### Client Interceptor Input Classes + +```kotlin +data class KStartWorkflowInput( + val workflowId: String, + val workflowType: String, + val taskQueue: String, + val arguments: Array, + val options: KWorkflowOptions, + val header: Header +) + +data class KSignalWorkflowInput( + val workflowId: String, + val runId: String?, + val signalName: String, + val arguments: Array, + val header: Header +) + +data class KQueryWorkflowInput( + val workflowId: String, + val runId: String?, + val queryName: String, + val arguments: Array, + val resultClass: Class, + val header: Header +) + +data class KExecuteUpdateInput( + val workflowId: String, + val runId: String?, + val updateName: String, + val arguments: Array, + val options: KUpdateOptions, + val resultClass: Class, + val header: Header +) + +data class KStartUpdateInput( + val workflowId: String, + val runId: String?, + val updateName: String, + val arguments: Array, + val options: KStartUpdateOptions, + val resultClass: Class, + val header: Header +) + +data class KCancelWorkflowInput( + val workflowId: String, + val runId: String? +) + +data class KTerminateWorkflowInput( + val workflowId: String, + val runId: String?, + val reason: String? +) +``` + +### Registering Client Interceptors + +```kotlin +val client = KWorkflowClient.connect( + KWorkflowClientOptions( + target = "localhost:7233", + namespace = "default", + interceptors = listOf( + TracingClientInterceptor() + ) + ) +) +``` + ## Related - [KOptions](./koptions.md) - Configuration options diff --git a/kotlin/configuration/koptions.md b/kotlin/configuration/koptions.md index 5d1f718..15378bd 100644 --- a/kotlin/configuration/koptions.md +++ b/kotlin/configuration/koptions.md @@ -119,7 +119,7 @@ data class KChildWorkflowOptions( val cronSchedule: String? = null, val parentClosePolicy: ParentClosePolicy? = null, val memo: Map? = null, - val typedSearchAttributes: SearchAttributes? = null, + val searchAttributes: SearchAttributes? = null, val cancellationType: ChildWorkflowCancellationType? = null, // Experimental @Experimental val staticSummary: String? = null, @@ -160,7 +160,7 @@ data class KWorkflowOptions( val retryOptions: KRetryOptions? = null, val cronSchedule: String? = null, val memo: Map? = null, - val typedSearchAttributes: SearchAttributes? = null, + val searchAttributes: SearchAttributes? = null, val disableEagerExecution: Boolean = true, val startDelay: Duration? = null, val contextPropagators: List? = null, @@ -246,7 +246,7 @@ data class KContinueAsNewOptions( val retryOptions: KRetryOptions? = null, val workflowTaskTimeout: Duration? = null, val memo: Map? = null, - val typedSearchAttributes: SearchAttributes? = null, + val searchAttributes: SearchAttributes? = null, val contextPropagators: List? = null ) ``` diff --git a/kotlin/migration.md b/kotlin/migration.md index 5123917..f5b261d 100644 --- a/kotlin/migration.md +++ b/kotlin/migration.md @@ -5,9 +5,9 @@ | Java SDK | Kotlin SDK | |----------|------------| | **Client** | | -| `WorkflowClient.newInstance(service)` | `KWorkflowClient(service)` | +| `WorkflowClient.newInstance(service)` | `KWorkflowClient.connect(options)` | | `client.newWorkflowStub(Cls, opts)` | `client.startWorkflow(Interface::method, options, ...)` | -| `client.newWorkflowStub(Cls, id)` | `client.getWorkflowHandle(id)` | +| `client.newWorkflowStub(Cls, id)` | `client.workflowHandle(id)` | | `stub.method(arg)` | `client.executeWorkflow(Interface::method, options, arg)` | | `stub.signal(arg)` | `handle.signal(T::method, arg)` | | `stub.query()` | `handle.query(T::method)` | @@ -20,7 +20,7 @@ | **KWorkflow Object** | | | `Workflow.getInfo()` | `KWorkflow.info` | | `Workflow.getLogger()` | `KWorkflow.logger()` | -| `Workflow.sleep(duration)` | `delay(duration)` - standard kotlinx.coroutines | +| `Workflow.sleep(duration)` | `delay(duration)` or `KWorkflow.delay(duration, options)` | | `Workflow.await(() -> cond)` | `KWorkflow.awaitCondition { cond }` | | `Workflow.sideEffect(cls, func)` | `KWorkflow.sideEffect { func }` | | `Workflow.getVersion(id, min, max)` | `KWorkflow.version(id, min, max)` | @@ -28,8 +28,8 @@ | `Workflow.randomUUID()` | `KWorkflow.randomUUID()` | | `Workflow.newRandom()` | `KWorkflow.newRandom()` | | `Workflow.currentTimeMillis()` | `KWorkflow.currentTimeMillis()` | -| `Workflow.getTypedSearchAttributes()` | `KWorkflow.typedSearchAttributes` | -| `Workflow.upsertTypedSearchAttributes(...)` | `KWorkflow.upsertTypedSearchAttributes(...)` | +| `Workflow.getTypedSearchAttributes()` | `KWorkflow.searchAttributes` | +| `Workflow.upsertTypedSearchAttributes(...)` | `KWorkflow.upsertSearchAttributes(...)` | | `Workflow.getMemo(key, cls)` | `KWorkflow.memo(key)` | | `Workflow.upsertMemo(map)` | `KWorkflow.upsertMemo(map)` | | `Workflow.getMetricsScope()` | `KWorkflow.metricsScope` | diff --git a/kotlin/workflows/cancellation.md b/kotlin/workflows/cancellation.md index 3e5aa95..baba224 100644 --- a/kotlin/workflows/cancellation.md +++ b/kotlin/workflows/cancellation.md @@ -22,14 +22,18 @@ In rare cases where workflow code doesn't have suspension points (e.g., tight lo ## Parallel Execution and Cancellation -`coroutineScope` provides structured concurrency—if one child fails or the scope is cancelled, all children are automatically cancelled: +`coroutineScope` provides structured concurrency—if one child fails or the scope is cancelled, all children are automatically cancelled. + +**Note:** This is standard Kotlin `coroutineScope` exception propagation behavior, not Temporal-specific: +- When one child fails with an exception, `coroutineScope` cancels all other children +- This is automatic Kotlin structured concurrency behavior ```kotlin override suspend fun parallelWorkflow(): String = coroutineScope { val a = async { KWorkflow.executeActivity(...) } val b = async { KWorkflow.executeActivity(...) } - // If either activity fails, the other is cancelled + // If either activity fails, the other is cancelled (standard Kotlin behavior) // If workflow is cancelled, both activities are cancelled "${a.await()} - ${b.await()}" } diff --git a/kotlin/workflows/continue-as-new.md b/kotlin/workflows/continue-as-new.md index 557c731..c90d7ab 100644 --- a/kotlin/workflows/continue-as-new.md +++ b/kotlin/workflows/continue-as-new.md @@ -49,7 +49,7 @@ data class KContinueAsNewOptions( val retryOptions: KRetryOptions? = null, val workflowTaskTimeout: Duration? = null, val memo: Map? = null, - val typedSearchAttributes: SearchAttributes? = null + val searchAttributes: SearchAttributes? = null ) ``` diff --git a/kotlin/workflows/signals-queries.md b/kotlin/workflows/signals-queries.md index a61b1d0..cbdca07 100644 --- a/kotlin/workflows/signals-queries.md +++ b/kotlin/workflows/signals-queries.md @@ -156,7 +156,7 @@ fun registerDynamicUpdateValidator(validator: (updateName: String, args: KEncode ### Sending Signals ```kotlin -val handle = client.getWorkflowHandle("order-123") +val handle = client.workflowHandle("order-123") // Type-safe signal using method reference handle.signal(OrderWorkflow::cancelOrder, "Customer request") @@ -165,7 +165,7 @@ handle.signal(OrderWorkflow::cancelOrder, "Customer request") ### Querying Workflows ```kotlin -val handle = client.getWorkflowHandle("order-123") +val handle = client.workflowHandle("order-123") // Query using property reference val status = handle.query(OrderWorkflow::status) @@ -177,7 +177,7 @@ val count = handle.query(OrderWorkflow::getItemCount) ### Executing Updates ```kotlin -val handle = client.getWorkflowHandle("order-123") +val handle = client.workflowHandle("order-123") // Execute update and wait for result val added = handle.executeUpdate(OrderWorkflow::addItem, newItem) From 8ae3077e3e2acdd390bd2af02550a3c641589fe1 Mon Sep 17 00:00:00 2001 From: Maxim Fateev <1463622+mfateev@users.noreply.github.com> Date: Tue, 13 Jan 2026 07:46:32 -0600 Subject: [PATCH 77/83] Remove .idea and .claude directories from tracking Add .gitignore to prevent IDE and tool-specific files from being committed. --- .claude/skills/implement-change.md | 42 -------------------- .claude/skills/implement.md | 62 ----------------------------- .claude/skills/plan-change.md | 40 ------------------- .claude/skills/plan-phase.md | 63 ------------------------------ .gitignore | 3 ++ .idea/.gitignore | 8 ---- .idea/google-java-format.xml | 6 --- .idea/misc.xml | 6 --- .idea/modules.xml | 8 ---- .idea/proposals:kotlin-sdk.iml | 9 ----- .idea/vcs.xml | 6 --- 11 files changed, 3 insertions(+), 250 deletions(-) delete mode 100644 .claude/skills/implement-change.md delete mode 100644 .claude/skills/implement.md delete mode 100644 .claude/skills/plan-change.md delete mode 100644 .claude/skills/plan-phase.md create mode 100644 .gitignore delete mode 100644 .idea/.gitignore delete mode 100644 .idea/google-java-format.xml delete mode 100644 .idea/misc.xml delete mode 100644 .idea/modules.xml delete mode 100644 .idea/proposals:kotlin-sdk.iml delete mode 100644 .idea/vcs.xml diff --git a/.claude/skills/implement-change.md b/.claude/skills/implement-change.md deleted file mode 100644 index 57e06e0..0000000 --- a/.claude/skills/implement-change.md +++ /dev/null @@ -1,42 +0,0 @@ -# Implement Change Skill - -Implement a change based on a detailed plan from the change-planner agent. - -## Usage - -``` -/implement-change . -``` - -Examples: -- `/implement-change 1.1.1` - Implement Phase 1.1, Change 1 -- `/implement-change 2.3.5` - Implement Phase 2.3, Change 5 - -## Behavior - -1. Read the change plan from `kotlin/phases/changes/phase-X.Y-change-N.md` -2. Verify dependencies are met -3. Create new files as specified -4. Modify existing files as specified -5. Implement all unit tests -6. Verify code compiles and tests pass -7. Report summary of changes - -## Prerequisites - -The change plan must exist. Run `/plan-change X.Y.N` first if needed. - -## Output - -- Production code files (created/modified) -- Test files -- Summary of what was done -- Any deviations from plan with justification - -## Quality Gates - -The agent will verify: -- Code compiles without warnings -- All new tests pass -- Existing tests still pass -- Acceptance criteria from plan are met diff --git a/.claude/skills/implement.md b/.claude/skills/implement.md deleted file mode 100644 index 7d28f82..0000000 --- a/.claude/skills/implement.md +++ /dev/null @@ -1,62 +0,0 @@ -# Implement Skill - -Fully implement a change from a phase plan, orchestrating planning and implementation. - -## Usage - -``` -/implement . -``` - -Examples: -- `/implement 1.1.1` - Implement Phase 1.1, Change 1 -- `/implement 2.3.5` - Implement Phase 2.3, Change 5 - -## Behavior - -This skill orchestrates the full implementation workflow: - -1. **Validate** - Check dependencies are met -2. **Plan** - Generate detailed implementation plan (change-planner) -3. **Review** - Verify plan is feasible and complete -4. **Implement** - Write code and tests (change-implementer) -5. **Verify** - Ensure compilation and tests pass -6. **Retry** - Replan or fix if issues encountered - -## Automatic Recovery - -The orchestrator handles failures automatically: - -| Issue | Action | -|-------|--------| -| Plan incomplete | Replan with feedback (up to 2x) | -| Compilation error | Fix and retry (up to 3x) | -| Test failure | Diagnose and fix or replan | -| Blocking issue | Stop and report | - -## Output - -- Completed implementation with passing tests -- Change plan document at `kotlin/phases/changes/phase-X.Y-change-N.md` -- Summary of files created/modified -- Verification report - -## Prerequisites - -- Phase plan must exist at `kotlin/phases/phase-X.Y-detailed.md` -- Prior changes in the phase should be complete - -## Workflow Visualization - -``` -/implement 1.1.3 - │ - ▼ -┌─────────────┐ ┌─────────────┐ ┌─────────────┐ -│ Plan Change │ ──▶ │ Implement │ ──▶ │ Verify │ -│ │ │ │ │ │ -└─────────────┘ └─────────────┘ └─────────────┘ - ▲ │ │ - │ │ │ - └────── replan ──────┴─────── fix ────────┘ -``` diff --git a/.claude/skills/plan-change.md b/.claude/skills/plan-change.md deleted file mode 100644 index 661e979..0000000 --- a/.claude/skills/plan-change.md +++ /dev/null @@ -1,40 +0,0 @@ -# Plan Change Skill - -Create a detailed implementation plan for a single change from a phase plan. - -## Usage - -``` -/plan-change . -``` - -Examples: -- `/plan-change 1.1.1` - Plan Phase 1.1, Change 1 -- `/plan-change 2.3.5` - Plan Phase 2.3, Change 5 - -## Behavior - -1. Read the phase plan from `kotlin/phases/phase-X.Y-detailed.md` -2. Read SDK proposal docs and relevant source code -3. Validate the change is feasible -4. Design the simplest, most readable implementation -5. Specify complete test coverage -6. Write output to `kotlin/phases/changes/phase-X.Y-change-N.md` - -## Output Contents - -The implementation plan includes: - -- **Feasibility validation** - Confirm change is possible -- **File changes** - Exact files to create/modify with code skeletons -- **Test specifications** - Complete unit test definitions -- **Acceptance criteria** - Checklist for completion -- **Implementation notes** - Gotchas, patterns, references - -## Requirements - -The output must be **self-contained**. A developer should be able to implement the change using only: -- This document -- The SDK source code - -No need to consult proposal documents or other planning materials. diff --git a/.claude/skills/plan-phase.md b/.claude/skills/plan-phase.md deleted file mode 100644 index 2d25005..0000000 --- a/.claude/skills/plan-phase.md +++ /dev/null @@ -1,63 +0,0 @@ -# Plan Phase Skill - -Break down a phase of the Kotlin SDK implementation into detailed, self-contained changes. - -## Usage - -``` -/plan-phase -``` - -Examples: -- `/plan-phase 1.1` - Plan "Java SDK Refactoring" -- `/plan-phase 2.1` - Plan "Typed Activity Stubs" - -## Behavior - -1. Read the implementation plan from `kotlin/implementation-plan.md` -2. Read context from `kotlin/sdk-api.md` and `kotlin/sdk-implementation.md` -3. For the specified phase, produce a detailed breakdown where each item: - - Is a self-contained, submittable PR - - Includes full test coverage - - Has clear dependencies on prior items - - Can be reviewed and merged independently - -## Output Format - -Write the detailed plan to `kotlin/phases/phase-X.Y-detailed.md` with this structure: - -```markdown -# Phase X.Y: [Name] - Detailed Plan - -## Overview -Brief description of what this phase accomplishes. - -## Prerequisites -Any required setup or prior phases. - -## Changes - -### Change 1: [Title] -**Summary:** One-line description - -**Scope:** -- Files to add/modify -- Public API changes (if any) - -**Tests:** -- Unit tests required -- Integration tests required - -**Dependencies:** None | Change N - ---- -(repeat for each change) -``` - -## Guidelines - -- **Atomic**: Each change does ONE thing -- **Testable**: Every change includes tests -- **Ordered**: Dependencies flow forward only -- **Compilable**: Code compiles after each change -- **No Design**: Don't go into implementation details - just scope and structure diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..7b203e1 --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +.idea/ +.claude/ +*.iml diff --git a/.idea/.gitignore b/.idea/.gitignore deleted file mode 100644 index 13566b8..0000000 --- a/.idea/.gitignore +++ /dev/null @@ -1,8 +0,0 @@ -# Default ignored files -/shelf/ -/workspace.xml -# Editor-based HTTP Client requests -/httpRequests/ -# Datasource local storage ignored files -/dataSources/ -/dataSources.local.xml diff --git a/.idea/google-java-format.xml b/.idea/google-java-format.xml deleted file mode 100644 index 2aa056d..0000000 --- a/.idea/google-java-format.xml +++ /dev/null @@ -1,6 +0,0 @@ - - - - - \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml deleted file mode 100644 index eeb80f7..0000000 --- a/.idea/misc.xml +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - \ No newline at end of file diff --git a/.idea/modules.xml b/.idea/modules.xml deleted file mode 100644 index 03a1c1e..0000000 --- a/.idea/modules.xml +++ /dev/null @@ -1,8 +0,0 @@ - - - - - - - - \ No newline at end of file diff --git a/.idea/proposals:kotlin-sdk.iml b/.idea/proposals:kotlin-sdk.iml deleted file mode 100644 index d6ebd48..0000000 --- a/.idea/proposals:kotlin-sdk.iml +++ /dev/null @@ -1,9 +0,0 @@ - - - - - - - - - \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml deleted file mode 100644 index 35eb1dd..0000000 --- a/.idea/vcs.xml +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - \ No newline at end of file From 38289959b48fda0ae5bcefa30e143d72b1573c92 Mon Sep 17 00:00:00 2001 From: Maxim Fateev <1463622+mfateev@users.noreply.github.com> Date: Tue, 13 Jan 2026 07:49:53 -0600 Subject: [PATCH 78/83] Remove CLAUDE.md from tracking --- .gitignore | 1 + CLAUDE.md | 62 ------------------------------------------------------ 2 files changed, 1 insertion(+), 62 deletions(-) delete mode 100644 CLAUDE.md diff --git a/.gitignore b/.gitignore index 7b203e1..1affe11 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ .idea/ .claude/ *.iml +CLAUDE.md diff --git a/CLAUDE.md b/CLAUDE.md deleted file mode 100644 index fca656e..0000000 --- a/CLAUDE.md +++ /dev/null @@ -1,62 +0,0 @@ -# Claude Instructions for Kotlin SDK Proposals - -## Repository Purpose - -This repository contains API proposals and design documentation for the Temporal Kotlin SDK. - -## Adding Open Questions - -When proposing new API features that need discussion before implementation: - -1. **Add to the central document**: Add a new section to `kotlin/open-questions.md` with: - - Clear problem statement - - Proposed solution with code examples - - Benefits and trade-offs - - Status marked as "Decision needed" - -2. **Add inline sections**: In the relevant proposal document (e.g., `activities/definition.md`), add an "Open Questions (Decision Needed)" section at the end with: - - Brief summary of the proposal - - Link to full discussion: `[Full discussion](../open-questions.md#section-anchor)` - -3. **Update the README**: Ensure `kotlin/open-questions.md` is linked in the Reference section of `kotlin/README.md` - -### Example Format - -In `open-questions.md`: -```markdown -## Feature Name - -**Status:** Decision needed - -### Problem Statement -[Describe what problem this solves] - -### Proposal -[Code examples showing proposed API] - -### Benefits -- [List benefits] - -### Trade-offs -- [List trade-offs] - -### Related Sections -- [Link to relevant proposal docs] -``` - -In the relevant proposal doc: -```markdown -## Open Questions (Decision Needed) - -### Feature Name - -**Status:** Decision needed | [Full discussion](../open-questions.md#feature-name) - -[Brief summary and code example] -``` - -## Commit Style - -Follow concise commit message style: -- First line: Brief summary of change -- Body: Additional context if needed From c6370b1de216c11e249a8d07342cc49f7ad168e6 Mon Sep 17 00:00:00 2001 From: Maxim Fateev <1463622+mfateev@users.noreply.github.com> Date: Tue, 13 Jan 2026 07:54:59 -0600 Subject: [PATCH 79/83] Add enhanced nested DSL ergonomics suggestion to open-questions --- kotlin/open-questions.md | 34 ++++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/kotlin/open-questions.md b/kotlin/open-questions.md index a3bf89f..3fbadc2 100644 --- a/kotlin/open-questions.md +++ b/kotlin/open-questions.md @@ -562,6 +562,40 @@ val options = KActivityOptions { - Validation can happen in `build()` before object construction - `copy` can be implemented safely if needed (via a `toBuilder()` method) +### Enhanced Nested DSL Ergonomics + +With the Builder+DSL pattern, extension methods can provide cleaner syntax for nested options: + +```kotlin +// Without extension method - requires assignment +val options = KActivityOptions { + startToCloseTimeout = 10.minutes + retryOptions = KRetryOptions { + initialInterval = 10.seconds + backoffCoefficient = 1.5 + } +} + +// With extension method - no assignment needed +val options = KActivityOptions { + startToCloseTimeout = 10.minutes + retryOptions { + initialInterval = 10.seconds + backoffCoefficient = 1.5 + } +} +``` + +The extension method: + +```kotlin +inline fun KActivityOptions.Builder.retryOptions(init: KRetryOptions.Builder.() -> Unit) { + this.retryOptions = KRetryOptions(init) +} +``` + +This provides slightly better ergonomics for nested configuration while maintaining full type safety. + ### Trade-offs - More boilerplate code to write and maintain From 43c7014397e6f5b2f18335e4cc0d89f582607d5c Mon Sep 17 00:00:00 2001 From: Maxim Fateev <1463622+mfateev@users.noreply.github.com> Date: Tue, 13 Jan 2026 18:27:38 -0600 Subject: [PATCH 80/83] Update API proposal based on PR review round 2 - Q1: Allow default parameters for 0-1 argument case (align with Python/.NET/Ruby) - Q5: Follow Python/.NET pattern for worker options (pass workflows/activities at construction) - Q6: Heartbeat infallible, cancellation via coroutine or CompletableFuture - Q7: Only show annotations when customizing names (consistency cleanup) - Q8: Add KWorkflow.delay() with and without summary parameter - Q9: Rename KotlinPlugin to KotlinJavaWorkerPlugin --- kotlin/README.md | 27 +++-- kotlin/activities/README.md | 10 +- kotlin/activities/definition.md | 24 +++-- kotlin/activities/implementation.md | 110 ++++++++++++++++++-- kotlin/api-parity.md | 58 ++++++++++- kotlin/kotlin-idioms.md | 23 +++-- kotlin/open-questions.md | 41 ++++---- kotlin/worker/setup.md | 149 +++++++++++++++------------- kotlin/workflows/README.md | 8 +- kotlin/workflows/definition.md | 27 +++-- kotlin/workflows/signals-queries.md | 32 ++++-- 11 files changed, 357 insertions(+), 152 deletions(-) diff --git a/kotlin/README.md b/kotlin/README.md index 887ae06..5ba4fa2 100644 --- a/kotlin/README.md +++ b/kotlin/README.md @@ -80,10 +80,9 @@ This approach provides: ## Quick Start ```kotlin -// Define activity interface +// Define activity interface - @ActivityMethod is optional @ActivityInterface interface GreetingActivities { - @ActivityMethod suspend fun composeGreeting(greeting: String, name: String): String } @@ -94,10 +93,9 @@ class GreetingActivitiesImpl : GreetingActivities { } } -// Define workflow interface +// Define workflow interface - @WorkflowMethod is optional @WorkflowInterface interface GreetingWorkflow { - @WorkflowMethod suspend fun getGreeting(name: String): String } @@ -112,17 +110,16 @@ class GreetingWorkflowImpl : GreetingWorkflow { } } -// Start worker (recommended: use run() which blocks and propagates fatal errors) -val factory = KWorkerFactory(client) -val worker = factory.newWorker("greetings") -worker.registerWorkflowImplementationTypes(GreetingWorkflowImpl::class) -worker.registerActivitiesImplementations(GreetingActivitiesImpl()) -factory.run() // Blocks until shutdown, propagates fatal errors - -// Alternative for advanced use cases: -// factory.start() // Returns immediately -// ... do other work ... -// factory.shutdown() +// Start worker - workflows and activities specified in options +val worker = KWorker( + client, + KWorkerOptions( + taskQueue = "greetings", + workflows = listOf(GreetingWorkflowImpl::class), + activities = listOf(GreetingActivitiesImpl()) + ) +) +worker.start() // Execute workflow val result = client.executeWorkflow( diff --git a/kotlin/activities/README.md b/kotlin/activities/README.md index b306a15..1d9bd67 100644 --- a/kotlin/activities/README.md +++ b/kotlin/activities/README.md @@ -19,10 +19,16 @@ Activities are the building blocks for interacting with external systems. The Ko ### Basic Activity ```kotlin -// Define activity interface +// Define activity interface - @ActivityMethod is optional @ActivityInterface interface GreetingActivities { - @ActivityMethod + suspend fun composeGreeting(greeting: String, name: String): String +} + +// Use @ActivityMethod only when customizing the activity name +@ActivityInterface +interface CustomNameActivities { + @ActivityMethod(name = "compose-greeting") suspend fun composeGreeting(greeting: String, name: String): String } diff --git a/kotlin/activities/definition.md b/kotlin/activities/definition.md index c1bb3a9..60a2ce8 100644 --- a/kotlin/activities/definition.md +++ b/kotlin/activities/definition.md @@ -38,16 +38,20 @@ The typed activity API uses direct method references - no stub creation needed. // Define activity interface @ActivityInterface interface GreetingActivities { - @ActivityMethod suspend fun composeGreeting(greeting: String, name: String): String - @ActivityMethod suspend fun sendEmail(email: Email): SendResult - @ActivityMethod suspend fun log(message: String) } +// Use @ActivityMethod only when customizing the activity name +@ActivityInterface +interface CustomNameActivities { + @ActivityMethod(name = "compose-greeting") + suspend fun composeGreeting(greeting: String, name: String): String +} + // In workflow - direct method reference, no stub needed val greeting = KWorkflow.executeActivity( GreetingActivities::composeGreeting, // Direct reference to interface method @@ -75,24 +79,25 @@ KWorkflow.executeActivity( ## Parameter Restrictions -**Default parameter values are not allowed** in activity methods. This is validated at worker registration time. +**Default parameter values are allowed for methods with 0 or 1 arguments.** For methods with 2+ arguments, defaults are not allowed. This is validated at worker registration time. ```kotlin -// ✗ NOT ALLOWED - will fail at registration -@ActivityMethod +// ✓ ALLOWED - 1 argument with default +suspend fun processOrder(priority: Int = 0): OrderResult + +// ✗ NOT ALLOWED - 2+ arguments with defaults suspend fun processOrder(orderId: String, priority: Int = 0) // Error! -// ✓ CORRECT - use a parameter object with optional fields +// ✓ CORRECT for 2+ arguments - use a parameter object with optional fields data class ProcessOrderParams( val orderId: String, val priority: Int? = null ) -@ActivityMethod suspend fun processOrder(params: ProcessOrderParams) ``` -**Rationale:** Default values create replay safety issues (changing defaults breaks determinism), serialization ambiguity, and cross-language compatibility problems. See [full discussion](../open-questions.md#default-parameter-values-not-allowed). +**Rationale:** This aligns with Python, .NET, and Ruby SDKs which support defaults. For complex inputs with multiple parameters, the parameter object pattern avoids serialization ambiguity and cross-language issues. See [full discussion](../open-questions.md#default-parameter-values). ## Type Safety @@ -214,7 +219,6 @@ class GreetingActivitiesImpl : GreetingActivities { ```kotlin // Proposed approach - no interface required class GreetingActivities { - @ActivityMethod suspend fun composeGreeting(greeting: String, name: String) = "$greeting, $name!" } diff --git a/kotlin/activities/implementation.md b/kotlin/activities/implementation.md index a939a82..6338d9e 100644 --- a/kotlin/activities/implementation.md +++ b/kotlin/activities/implementation.md @@ -8,14 +8,19 @@ Activity interfaces can have both suspend and non-suspend methods. The worker ha @ActivityInterface interface OrderActivities { // Suspend method - uses coroutines - @ActivityMethod suspend fun chargePayment(order: Order): PaymentResult // Non-suspend method - runs on thread pool - @ActivityMethod fun validateOrder(order: Order): Boolean } +// Use @ActivityMethod only when customizing the activity name +@ActivityInterface +interface CustomOrderActivities { + @ActivityMethod(name = "charge-payment") + suspend fun chargePayment(order: Order): PaymentResult +} + class OrderActivitiesImpl( private val paymentService: PaymentService ) : OrderActivities { @@ -70,7 +75,7 @@ worker.registerActivitiesImplementations(OrderActivitiesImpl(paymentService)) ## Heartbeating -For long-running activities, use heartbeating to report progress and detect cancellation: +For long-running activities, use heartbeating to report progress. **Heartbeat is infallible** - it never throws exceptions. Cancellation is detected separately (see Cancellation section below). ```kotlin class LongRunningActivitiesImpl : LongRunningActivities { @@ -79,6 +84,7 @@ class LongRunningActivitiesImpl : LongRunningActivities { val lines = File(filePath).readLines() lines.forEachIndexed { index, line -> + // Heartbeat is infallible - never throws context.heartbeat(index) // Process line... @@ -90,6 +96,8 @@ class LongRunningActivitiesImpl : LongRunningActivities { } ``` +**Rationale:** Heartbeat is a local operation that records progress. The actual network communication happens asynchronously in the background. Making heartbeat infallible simplifies activity code - cancellation is handled through dedicated cancellation mechanisms. + ## Heartbeat Details Recovery Retrieve heartbeat details from a previous failed attempt: @@ -110,6 +118,65 @@ override suspend fun resumableProcess(data: List): ProcessResult { } ``` +## Activity Cancellation + +Activity cancellation works differently for suspend and non-suspend activities: + +### Suspend Activity Cancellation + +Suspend activities use **standard Kotlin coroutine cancellation**. When an activity is cancelled, a `CancellationException` is thrown at suspension points: + +```kotlin +override suspend fun processItems(items: List): ProcessResult { + for (item in items) { + // CancellationException thrown here if activity is cancelled + processItem(item) + } + return ProcessResult(success = true) +} + +// With cleanup on cancellation +override suspend fun processWithCleanup(items: List): ProcessResult { + return try { + processItems(items) + } catch (e: CancellationException) { + // Cleanup on cancellation + cleanup() + throw e // Re-throw to complete as cancelled + } +} +``` + +### Non-Suspend Activity Cancellation + +Non-suspend activities use `KActivity.cancellationFuture()` which returns a `CompletableFuture`: + +```kotlin +override fun processItemsBlocking(items: List): ProcessResult { + val cancellationFuture = KActivity.cancellationFuture() + + for (item in items) { + // Check if cancelled + if (cancellationFuture.isDone) { + val details = cancellationFuture.get() + cleanup() + throw CancellationException("Activity cancelled: ${details.message}") + } + processItem(item) + } + return ProcessResult(success = true) +} +``` + +The `CancellationDetails` provides information about why the activity was cancelled: + +```kotlin +data class CancellationDetails( + val message: String?, + val cause: CancellationType // WORKFLOW_CANCELLED, ACTIVITY_TIMEOUT, etc. +) +``` + ## KActivity API `KActivity.executionContext` provides access to the activity execution context for both regular and local activities: @@ -124,6 +191,7 @@ println("Activity ${info.activityType}, attempt ${info.attempt}") println("Is local: ${info.isLocal}") // Heartbeat for long-running activities (no-op for local activities) +// Heartbeat is infallible - never throws context.heartbeat(progressDetails) // Get heartbeat details from previous attempt (empty for local activities) @@ -134,15 +202,43 @@ context.doNotCompleteOnReturn() val taskToken = context.taskToken ``` +### KActivity Static Methods + +```kotlin +object KActivity { + /** Get the execution context for the current activity */ + val executionContext: KActivityExecutionContext + + /** + * Get a future that completes when the activity is cancelled. + * For non-suspend activities to detect cancellation. + */ + fun cancellationFuture(): CompletableFuture +} +``` + ## KActivityExecutionContext Interface ```kotlin interface KActivityExecutionContext { val info: KActivityInfo - fun heartbeat(details: Any? = null) // No-op for local activities - fun heartbeatDetails(detailsClass: Class): T? // Empty for local activities - val taskToken: ByteArray // Throws for local activities - fun doNotCompleteOnReturn() // Throws for local activities + + /** + * Send a heartbeat with optional progress details. + * Heartbeat is infallible - never throws. Cancellation is detected separately. + * No-op for local activities. + */ + fun heartbeat(details: Any? = null) + + /** Get heartbeat details from previous attempt. Empty for local activities. */ + fun heartbeatDetails(detailsClass: Class): T? + + /** Task token for async completion. Throws for local activities. */ + val taskToken: ByteArray + + /** Mark activity for manual completion. Throws for local activities. */ + fun doNotCompleteOnReturn() + val isDoNotCompleteOnReturn: Boolean } diff --git a/kotlin/api-parity.md b/kotlin/api-parity.md index f897bb4..bf39627 100644 --- a/kotlin/api-parity.md +++ b/kotlin/api-parity.md @@ -26,15 +26,47 @@ The following Java SDK APIs are **not needed** in the Kotlin SDK due to language ## Activity API Design Decisions -### Single heartbeat() API +### Infallible heartbeat() API -The Kotlin SDK provides a single `heartbeat()` method on `KActivityExecutionContext` for both sync and suspend activities: +The Kotlin SDK provides a single `heartbeat()` method on `KActivityExecutionContext` that is **infallible** - it never throws exceptions: ```kotlin -context.heartbeat(progressDetails) +context.heartbeat(progressDetails) // Never throws ``` -**Rationale:** Heartbeat is a short, non-blocking operation that records progress locally. The actual network call happens asynchronously in the background. A separate `suspendHeartbeat()` API is unnecessary. +**Rationale:** Heartbeat is a local operation that records progress. The actual network communication happens asynchronously in the background. Making heartbeat infallible simplifies activity code - cancellation is handled through dedicated cancellation mechanisms (see below). + +### Activity Cancellation + +Cancellation works differently for suspend and non-suspend activities: + +**Suspend activities:** Use standard Kotlin coroutine cancellation. `CancellationException` is thrown at suspension points when the activity is cancelled. + +```kotlin +override suspend fun processItems(items: List): ProcessResult { + for (item in items) { + processItem(item) // CancellationException thrown here if cancelled + } + return ProcessResult(success = true) +} +``` + +**Non-suspend activities:** Use `KActivity.cancellationFuture()` which returns a `CompletableFuture`: + +```kotlin +override fun processItemsBlocking(items: List): ProcessResult { + val cancellationFuture = KActivity.cancellationFuture() + + for (item in items) { + if (cancellationFuture.isDone) { + val details = cancellationFuture.get() + throw CancellationException("Cancelled: ${details.message}") + } + processItem(item) + } + return ProcessResult(success = true) +} +``` ### Both Sync and Suspend Activities Supported @@ -91,7 +123,23 @@ The following Java SDK workflow APIs have Kotlin equivalents in `KWorkflow`: |--------------|------------| | `Workflow.sleep(...)` | `kotlinx.coroutines.delay()` - standard Kotlin, or `KWorkflow.delay()` | | N/A | `KWorkflow.delay(duration)` - simple delay | -| N/A | `KWorkflow.delay(duration, options)` - with `KTimerOptions` for summary | +| N/A | `KWorkflow.delay(duration, summary)` - with summary string | + +**KWorkflow.delay overloads:** + +```kotlin +object KWorkflow { + /** Simple delay - equivalent to kotlinx.coroutines.delay() */ + suspend fun delay(duration: Duration) + + /** Delay with summary for observability */ + suspend fun delay(duration: Duration, summary: String) +} + +// Usage examples +KWorkflow.delay(5.minutes) +KWorkflow.delay(1.hours, "Waiting for approval timeout") +``` ### Side Effects & Utilities diff --git a/kotlin/kotlin-idioms.md b/kotlin/kotlin-idioms.md index be0e010..dc02c38 100644 --- a/kotlin/kotlin-idioms.md +++ b/kotlin/kotlin-idioms.md @@ -17,27 +17,38 @@ The Kotlin SDK uses **standard Kotlin patterns** wherever possible instead of cu Workflows and activities use `suspend fun` for natural coroutine integration: ```kotlin +// Method annotations are optional - use only when customizing names @WorkflowInterface interface OrderWorkflow { - @WorkflowMethod suspend fun processOrder(order: Order): OrderResult - @SignalMethod + suspend fun updatePriority(priority: Priority) // Signal handler + + suspend fun addItem(item: OrderItem): Boolean // Update handler + + val status: OrderStatus // Query handler - queries are NOT suspend (synchronous) +} + +// Use annotations only when customizing names +@WorkflowInterface +interface CustomNameWorkflow { + @WorkflowMethod(name = "ProcessOrder") + suspend fun processOrder(order: Order): OrderResult + + @SignalMethod(name = "update-priority") suspend fun updatePriority(priority: Priority) - @UpdateMethod + @UpdateMethod(name = "add-item") suspend fun addItem(item: OrderItem): Boolean - @QueryMethod // Queries are NOT suspend - must be synchronous + @QueryMethod(name = "get-status") val status: OrderStatus } @ActivityInterface interface OrderActivities { - @ActivityMethod suspend fun validateOrder(order: Order): Boolean - @ActivityMethod suspend fun chargePayment(order: Order): PaymentResult } ``` diff --git a/kotlin/open-questions.md b/kotlin/open-questions.md index 3fbadc2..b9d5bd4 100644 --- a/kotlin/open-questions.md +++ b/kotlin/open-questions.md @@ -4,56 +4,62 @@ This document tracks API design questions that need discussion and decisions bef --- -## Default Parameter Values Not Allowed +## Default Parameter Values **Status:** Decided ### Decision -**Default parameter values are not allowed** in workflow methods, activity methods, signal handlers, update handlers, and query handlers. This restriction is enforced at worker registration time via reflection (`KParameter.isOptional`). +**Default parameter values are allowed for methods with 0 or 1 arguments** in workflow methods, activity methods, signal handlers, update handlers, and query handlers. This aligns with Python, .NET, and Ruby SDKs which support defaults. ```kotlin -// ✗ NOT ALLOWED - will fail at registration -@ActivityMethod +// ✓ ALLOWED - 0 arguments with defaults (effectively optional call) +suspend fun getStatus(includeDetails: Boolean = false): Status + +// ✓ ALLOWED - 1 argument with default +suspend fun processOrder(priority: Int = 0): OrderResult + +// ✗ NOT ALLOWED - 2+ arguments, any with defaults suspend fun processOrder(orderId: String, priority: Int = 0) // Error! -// ✓ CORRECT - use a parameter object with optional fields +// ✓ CORRECT for 2+ arguments - use a parameter object with optional fields data class ProcessOrderParams( val orderId: String, val priority: Int? = null ) -@ActivityMethod suspend fun processOrder(params: ProcessOrderParams) ``` ### Rationale -1. **Replay Safety** - If default values change between deployments, replayed workflows behave differently, violating determinism -2. **Serialization Ambiguity** - Unclear whether the caller serializes defaults or the worker applies them -3. **Cross-Language Compatibility** - Other languages calling the activity/workflow don't know about Kotlin defaults -4. **SDK Consistency** - Python SDK explicitly disallows default parameters; Go/Java don't have them +1. **SDK Alignment** - Python, .NET, and Ruby SDKs allow default parameters; aligning with them provides consistency +2. **0-1 Argument Simplicity** - For simple methods, defaults are unambiguous and convenient +3. **2+ Arguments Complexity** - With multiple arguments, defaults create serialization and cross-language issues +4. **Parameter Object Pattern** - For complex inputs, parameter objects remain the recommended approach ### Validation The SDK validates at registration time using Kotlin reflection: ```kotlin -fun validateNoDefaultParameters(function: KFunction<*>) { - val paramsWithDefaults = function.parameters +fun validateDefaultParameters(function: KFunction<*>) { + val valueParams = function.parameters .filter { it.kind == KParameter.Kind.VALUE } - .filter { it.isOptional } - if (paramsWithDefaults.isNotEmpty()) { + val paramsWithDefaults = valueParams.filter { it.isOptional } + + // Allow defaults only for 0-1 argument methods + if (paramsWithDefaults.isNotEmpty() && valueParams.size > 1) { throw IllegalArgumentException( - "Default parameter values are not allowed. " + + "Default parameter values are only allowed for methods with 0 or 1 arguments. " + "Use a parameter object with optional fields instead." ) } } ``` -### Recommended Pattern +### Recommended Pattern for Complex Inputs Use a single parameter object with nullable/optional fields: @@ -64,7 +70,6 @@ data class OrderParams( val retryCount: Int? = null ) -@WorkflowMethod suspend fun processOrder(params: OrderParams): OrderResult ``` @@ -114,7 +119,6 @@ Allow defining activities and workflows directly on implementation classes witho ```kotlin // Proposed approach - no interface required class GreetingActivities { - @ActivityMethod suspend fun composeGreeting(greeting: String, name: String) = "$greeting, $name!" } @@ -131,7 +135,6 @@ val result = KWorkflow.executeActivity( ```kotlin // Proposed approach - no interface required class GreetingWorkflow { - @WorkflowMethod suspend fun getGreeting(name: String): String { return KWorkflow.executeActivity( GreetingActivities::composeGreeting, diff --git a/kotlin/worker/setup.md b/kotlin/worker/setup.md index 29afafb..8e6d591 100644 --- a/kotlin/worker/setup.md +++ b/kotlin/worker/setup.md @@ -1,51 +1,51 @@ # Worker Setup -## KWorkerFactory (Recommended) +## KWorker (Recommended) -For pure Kotlin applications, use `KWorkerFactory` which automatically enables coroutine support: +For pure Kotlin applications, use `KWorker` which automatically enables coroutine support. Following the Python/.NET pattern, workflows and activities are passed at construction time via options: ```kotlin val service = WorkflowServiceStubs.newLocalServiceStubs() val client = KWorkflowClient(service) { ... } -// KWorkerFactory automatically enables Kotlin coroutine support -val factory = KWorkerFactory(client) - -val worker: KWorker = factory.newWorker("task-queue") { - maxConcurrentActivityExecutionSize = 100 -} - -// Register Kotlin coroutine workflows -worker.registerWorkflowImplementationTypes( - GreetingWorkflowImpl::class, - OrderWorkflowImpl::class -) - -// Register activities - suspend functions handled automatically -worker.registerActivitiesImplementations( - GreetingActivitiesImpl(), // Kotlin suspend activities - JavaActivitiesImpl() // Java activities work too +// Create worker with workflows and activities specified in options +val worker = KWorker( + client, + KWorkerOptions( + taskQueue = "task-queue", + workflows = listOf( + GreetingWorkflowImpl::class, + OrderWorkflowImpl::class + ), + activities = listOf( + GreetingActivitiesImpl(), // Kotlin suspend activities + JavaActivitiesImpl() // Java activities work too + ), + maxConcurrentActivityExecutionSize = 100 + ) ) // Start the worker -factory.start() +worker.start() ``` -## KWorkerFactory API +## KWorker API ```kotlin /** - * Kotlin worker factory that automatically enables coroutine support. - * Wraps WorkerFactory with KotlinPlugin pre-configured. + * Kotlin worker that provides idiomatic APIs for running + * Kotlin workflows and suspend activities. + * + * Workflows and activities are passed at construction time via KWorkerOptions, + * following the Python/.NET SDK pattern. */ -class KWorkerFactory( +class KWorker( client: KWorkflowClient, - options: WorkerFactoryOptions.Builder.() -> Unit = {} + options: KWorkerOptions ) { - /** The underlying WorkerFactory for advanced use cases */ - val workerFactory: WorkerFactory + /** The underlying Java Worker for interop scenarios */ + val worker: Worker - fun newWorker(taskQueue: String, options: WorkerOptions.Builder.() -> Unit = {}): KWorker fun start() fun shutdown() fun shutdownNow() @@ -53,50 +53,56 @@ class KWorkerFactory( } ``` -## KWorker API +## KWorkerOptions ```kotlin /** - * Kotlin worker that provides idiomatic APIs for registering - * Kotlin workflows and suspend activities. - * - * Use KWorker for pure Kotlin implementations. For mixed Java/Kotlin - * scenarios, access the underlying Worker via the [worker] property. + * Options for configuring a KWorker. + * Workflows and activities are specified at construction time. */ -class KWorker { - /** The underlying Java Worker for interop scenarios */ - val worker: Worker +data class KWorkerOptions( + val taskQueue: String, + val workflows: List> = emptyList(), + val activities: List = emptyList(), + val workflowImplementationOptions: WorkflowImplementationOptions? = null, + val maxConcurrentActivityExecutionSize: Int? = null, + val maxConcurrentWorkflowTaskExecutionSize: Int? = null, + val maxConcurrentLocalActivityExecutionSize: Int? = null, + // ... other worker options +) +``` - /** Register Kotlin workflow implementation types using reified generics */ - inline fun registerWorkflowImplementationTypes() +## Multiple Workers - /** Register Kotlin workflow implementation types using KClass */ - fun registerWorkflowImplementationTypes(vararg workflowClasses: KClass<*>) +For multiple task queues, create multiple workers: - /** Register Kotlin workflow implementation types with options */ - fun registerWorkflowImplementationTypes( - options: WorkflowImplementationOptions, - vararg workflowClasses: KClass<*> +```kotlin +val orderWorker = KWorker( + client, + KWorkerOptions( + taskQueue = "orders", + workflows = listOf(OrderWorkflowImpl::class), + activities = listOf(OrderActivitiesImpl()) ) +) - /** Register activity implementations (automatically detects suspend functions) */ - fun registerActivitiesImplementations(vararg activities: Any) - - /** Register suspend activity implementations explicitly */ - fun registerSuspendActivities(vararg activities: Any) +val notificationWorker = KWorker( + client, + KWorkerOptions( + taskQueue = "notifications", + workflows = listOf(NotificationWorkflowImpl::class), + activities = listOf(NotificationActivitiesImpl()) + ) +) - /** Register Nexus service implementations */ - fun registerNexusServiceImplementations(vararg services: Any) -} +// Start all workers +orderWorker.start() +notificationWorker.start() ``` -**When to use KWorker vs Worker:** -- Use `KWorker` for pure Kotlin implementations (recommended) -- Use `Worker` (via `kworker.worker`) when mixing Java and Kotlin workflows/activities on the same worker - -## KotlinPlugin (For Java Main) +## KotlinJavaWorkerPlugin (For Java Main) -When your main application is written in Java and you need to register Kotlin workflows, use `KotlinPlugin` explicitly: +When your main application is written in Java and you need to register Kotlin workflows, use `KotlinJavaWorkerPlugin` explicitly: ```kotlin // Java main or mixed Java/Kotlin setup @@ -104,7 +110,7 @@ val service = WorkflowServiceStubs.newLocalServiceStubs() val client = WorkflowClient.newInstance(service) val factory = WorkerFactory.newInstance(client, WorkerFactoryOptions.newBuilder() - .addPlugin(KotlinPlugin()) + .addPlugin(KotlinJavaWorkerPlugin()) .build()) val worker = factory.newWorker("task-queue") @@ -118,18 +124,23 @@ worker.registerWorkflowImplementationTypes(KotlinWorkflowImpl::class.java) A single worker supports both Java and Kotlin workflows on the same task queue: ```kotlin -// Java workflows (thread-based) -worker.registerWorkflowImplementationTypes( - OrderWorkflowJavaImpl::class.java -) - -// Kotlin workflows (coroutine-based) - same method, plugin handles execution -worker.registerWorkflowImplementationTypes( - GreetingWorkflowImpl::class +val worker = KWorker( + client, + KWorkerOptions( + taskQueue = "mixed-queue", + workflows = listOf( + GreetingWorkflowImpl::class, // Kotlin workflow (coroutine-based) + OrderWorkflowJavaImpl::class // Java workflow (thread-based) + ), + activities = listOf( + KotlinActivitiesImpl(), // Kotlin suspend activities + JavaActivitiesImpl() // Java blocking activities + ) + ) ) // Both run on the same worker - execution model is per-workflow-instance -factory.start() +worker.start() ``` ## Related diff --git a/kotlin/workflows/README.md b/kotlin/workflows/README.md index 68b32a8..fd3e2ed 100644 --- a/kotlin/workflows/README.md +++ b/kotlin/workflows/README.md @@ -25,7 +25,13 @@ Kotlin workflows use coroutines and suspend functions for an idiomatic async exp ```kotlin @WorkflowInterface interface GreetingWorkflow { - @WorkflowMethod + suspend fun getGreeting(name: String): String +} + +// Use @WorkflowMethod only when customizing the workflow type name +@WorkflowInterface +interface CustomNameWorkflow { + @WorkflowMethod(name = "CustomGreeting") suspend fun getGreeting(name: String): String } diff --git a/kotlin/workflows/definition.md b/kotlin/workflows/definition.md index 1c29adc..4d14ebf 100644 --- a/kotlin/workflows/definition.md +++ b/kotlin/workflows/definition.md @@ -5,7 +5,13 @@ Define workflow interfaces with `suspend` methods for full Kotlin coroutine supp ```kotlin @WorkflowInterface interface GreetingWorkflow { - @WorkflowMethod + suspend fun getGreeting(name: String): String +} + +// Use @WorkflowMethod only when customizing the workflow type name +@WorkflowInterface +interface CustomNameWorkflow { + @WorkflowMethod(name = "CustomGreeting") suspend fun getGreeting(name: String): String } @@ -79,8 +85,7 @@ Alternatively, Java clients can define their own Java interface with the **same // Java interface matching the Kotlin workflow type @WorkflowInterface public interface GreetingWorkflow { - @WorkflowMethod - String getGreeting(String name); + String getGreeting(String name); // @WorkflowMethod optional in Java too } // Java client using typed stub @@ -98,14 +103,18 @@ String result = workflow.getGreeting("Temporal"); ## Parameter Restrictions -**Default parameter values are not allowed** in workflow methods. This is validated at worker registration time. +**Default parameter values are allowed for methods with 0 or 1 arguments.** For methods with 2+ arguments, defaults are not allowed. This is validated at worker registration time. ```kotlin -// ✗ NOT ALLOWED - will fail at registration +// ✓ ALLOWED - 1 argument with default +@WorkflowMethod +suspend fun processOrder(priority: Int = 0): OrderResult + +// ✗ NOT ALLOWED - 2+ arguments with defaults @WorkflowMethod suspend fun processOrder(orderId: String, priority: Int = 0) // Error! -// ✓ CORRECT - use a parameter object with optional fields +// ✓ CORRECT for 2+ arguments - use a parameter object with optional fields data class ProcessOrderParams( val orderId: String, val priority: Int? = null @@ -115,13 +124,14 @@ data class ProcessOrderParams( suspend fun processOrder(params: ProcessOrderParams): OrderResult ``` -**Rationale:** Default values create replay safety issues (changing defaults breaks determinism), serialization ambiguity, and cross-language compatibility problems. See [full discussion](../open-questions.md#default-parameter-values-not-allowed). +**Rationale:** This aligns with Python, .NET, and Ruby SDKs which support defaults. For complex inputs with multiple parameters, the parameter object pattern avoids serialization ambiguity and cross-language issues. See [full discussion](../open-questions.md#default-parameter-values). ## Key Characteristics * Use `coroutineScope`, `async`, `launch` for concurrent execution * Use `delay()` for timers (maps to Temporal timers, not `Thread.sleep`) -* Reuses `@WorkflowInterface` and `@WorkflowMethod` annotations from Java SDK +* Reuses `@WorkflowInterface` annotation from Java SDK +* Method annotations (`@WorkflowMethod`, `@SignalMethod`, etc.) are optional - use only when customizing names * Data classes work naturally for parameters and results ## Logging @@ -177,7 +187,6 @@ class GreetingWorkflowImpl : GreetingWorkflow { ```kotlin // Proposed approach - no interface required class GreetingWorkflow { - @WorkflowMethod suspend fun getGreeting(name: String): String { return KWorkflow.executeActivity( GreetingActivities::composeGreeting, diff --git a/kotlin/workflows/signals-queries.md b/kotlin/workflows/signals-queries.md index cbdca07..a3ce44d 100644 --- a/kotlin/workflows/signals-queries.md +++ b/kotlin/workflows/signals-queries.md @@ -2,32 +2,45 @@ Signals and updates follow the same suspend/non-suspend pattern as workflow methods. Queries are always synchronous (never suspend) and can be defined as properties. -> **Note:** Default parameter values are not allowed in signal, query, or update handlers. Use parameter objects with optional fields instead. See [Parameter Restrictions](../open-questions.md#default-parameter-values-not-allowed). +> **Note:** Default parameter values are allowed for handlers with 0 or 1 arguments. For handlers with 2+ arguments, use parameter objects with optional fields instead. See [Parameter Restrictions](../open-questions.md#default-parameter-values). ## Defining Handlers +Method annotations are optional - use only when customizing handler names: + ```kotlin @WorkflowInterface interface OrderWorkflow { - @WorkflowMethod suspend fun processOrder(order: Order): OrderResult - @SignalMethod - suspend fun cancelOrder(reason: String) + suspend fun cancelOrder(reason: String) // Signal handler - @UpdateMethod - suspend fun addItem(item: OrderItem): Boolean + suspend fun addItem(item: OrderItem): Boolean // Update handler @UpdateValidatorMethod(updateMethod = "addItem") fun validateAddItem(item: OrderItem) // Queries - always synchronous, can use property syntax - @QueryMethod val status: OrderStatus - @QueryMethod fun getItemCount(): Int } + +// Use annotations only when customizing handler names +@WorkflowInterface +interface CustomNameOrderWorkflow { + @WorkflowMethod(name = "ProcessOrder") + suspend fun processOrder(order: Order): OrderResult + + @SignalMethod(name = "cancel-order") + suspend fun cancelOrder(reason: String) + + @UpdateMethod(name = "add-item") + suspend fun addItem(item: OrderItem): Boolean + + @QueryMethod(name = "get-status") + val status: OrderStatus +} ``` ## Dynamic Handler Registration @@ -188,6 +201,7 @@ val added = handle.executeUpdate(OrderWorkflow::addItem, newItem) Update validators run synchronously before the update handler. They can reject updates by throwing exceptions: ```kotlin +// Validator annotation specifies which update method it validates @UpdateValidatorMethod(updateMethod = "addItem") fun validateAddItem(item: OrderItem) { require(item.quantity > 0) { "Quantity must be positive" } @@ -195,7 +209,7 @@ fun validateAddItem(item: OrderItem) { require(_status == OrderStatus.PENDING) { "Cannot add items after processing started" } } -@UpdateMethod +// The update handler - @UpdateMethod is optional unless customizing the name suspend fun addItem(item: OrderItem): Boolean { // Validator already passed - safe to proceed _items.add(item) From dbfc51242db56d15529c3711c9938c8210ed244e Mon Sep 17 00:00:00 2001 From: Maxim Fateev <1463622+mfateev@users.noreply.github.com> Date: Wed, 14 Jan 2026 16:03:42 -0600 Subject: [PATCH 81/83] Address PR review round 2 comments - Q3: Use KArgs approach for type-safe activity/workflow arguments - Q11: Heartbeat throws CancellationException on cancellation (prevents slot eating) - Q12: Unified KClient matching Python/.NET pattern (workflows, schedules, async completion) - R1-Q5: Use KWorkflow.version() method instead of getter-style Key changes: - Rename KWorkflowClient to KClient throughout - Add schedule and async activity completion APIs to KClient - Update heartbeat to throw on cancellation, add TODO for cancellationFuture - Mark KArgs (Option C) as decided in open-questions.md --- kotlin/README.md | 2 +- kotlin/activities/implementation.md | 55 +++--- kotlin/client/README.md | 10 +- kotlin/client/workflow-client.md | 73 ++++++-- kotlin/configuration/interceptors.md | 4 +- kotlin/configuration/koptions.md | 2 +- kotlin/migration.md | 4 +- kotlin/open-questions.md | 6 +- kotlin/testing.md | 8 +- kotlin/worker/setup.md | 5 +- kotlin/workflows/definition.md | 2 +- tasks/pr-review-answers.md | 242 +++++++++++++++++++++++++++ tasks/pr-review-questions.md | 156 +++++++++++++++++ tasks/pr-review-round2.md | 197 ++++++++++++++++++++++ 14 files changed, 695 insertions(+), 71 deletions(-) create mode 100644 tasks/pr-review-answers.md create mode 100644 tasks/pr-review-questions.md create mode 100644 tasks/pr-review-round2.md diff --git a/kotlin/README.md b/kotlin/README.md index 5ba4fa2..ac715be 100644 --- a/kotlin/README.md +++ b/kotlin/README.md @@ -63,7 +63,7 @@ This approach provides: ### Infrastructure - **[Client](./client/README.md)** - Interacting with workflows - - [Workflow Client](./client/workflow-client.md) - KWorkflowClient, starting workflows + - [Workflow Client](./client/workflow-client.md) - KClient, starting workflows - [Workflow Handles](./client/workflow-handle.md) - Signals, queries, results - [Advanced Operations](./client/advanced.md) - SignalWithStart, UpdateWithStart diff --git a/kotlin/activities/implementation.md b/kotlin/activities/implementation.md index 6338d9e..97c856a 100644 --- a/kotlin/activities/implementation.md +++ b/kotlin/activities/implementation.md @@ -75,7 +75,7 @@ worker.registerActivitiesImplementations(OrderActivitiesImpl(paymentService)) ## Heartbeating -For long-running activities, use heartbeating to report progress. **Heartbeat is infallible** - it never throws exceptions. Cancellation is detected separately (see Cancellation section below). +For long-running activities, use heartbeating to report progress. **Heartbeat throws `CancellationException`** when the activity is cancelled, ensuring activities don't continue running and consuming worker slots. ```kotlin class LongRunningActivitiesImpl : LongRunningActivities { @@ -84,7 +84,7 @@ class LongRunningActivitiesImpl : LongRunningActivities { val lines = File(filePath).readLines() lines.forEachIndexed { index, line -> - // Heartbeat is infallible - never throws + // Heartbeat throws CancellationException if activity is cancelled context.heartbeat(index) // Process line... @@ -96,7 +96,7 @@ class LongRunningActivitiesImpl : LongRunningActivities { } ``` -**Rationale:** Heartbeat is a local operation that records progress. The actual network communication happens asynchronously in the background. Making heartbeat infallible simplifies activity code - cancellation is handled through dedicated cancellation mechanisms. +**Rationale:** Throwing on cancellation prevents activities from eating worker slots when developers forget to check cancellation. This aligns with Java SDK behavior and ensures consistent cancellation handling. ## Heartbeat Details Recovery @@ -120,16 +120,16 @@ override suspend fun resumableProcess(data: List): ProcessResult { ## Activity Cancellation -Activity cancellation works differently for suspend and non-suspend activities: +Activity cancellation is delivered via `heartbeat()` - when an activity is cancelled, the next heartbeat call throws `CancellationException`. This works consistently for both suspend and non-suspend activities. -### Suspend Activity Cancellation - -Suspend activities use **standard Kotlin coroutine cancellation**. When an activity is cancelled, a `CancellationException` is thrown at suspension points: +### Cancellation via Heartbeat ```kotlin override suspend fun processItems(items: List): ProcessResult { + val context = KActivity.executionContext for (item in items) { // CancellationException thrown here if activity is cancelled + context.heartbeat() processItem(item) } return ProcessResult(success = true) @@ -137,8 +137,13 @@ override suspend fun processItems(items: List): ProcessResult { // With cleanup on cancellation override suspend fun processWithCleanup(items: List): ProcessResult { + val context = KActivity.executionContext return try { - processItems(items) + for (item in items) { + context.heartbeat() + processItem(item) + } + ProcessResult(success = true) } catch (e: CancellationException) { // Cleanup on cancellation cleanup() @@ -147,35 +152,23 @@ override suspend fun processWithCleanup(items: List): ProcessResult { } ``` -### Non-Suspend Activity Cancellation +### Non-Suspend Activities -Non-suspend activities use `KActivity.cancellationFuture()` which returns a `CompletableFuture`: +Non-suspend activities also receive cancellation via heartbeat: ```kotlin override fun processItemsBlocking(items: List): ProcessResult { - val cancellationFuture = KActivity.cancellationFuture() - + val context = KActivity.executionContext for (item in items) { - // Check if cancelled - if (cancellationFuture.isDone) { - val details = cancellationFuture.get() - cleanup() - throw CancellationException("Activity cancelled: ${details.message}") - } + // CancellationException thrown here if activity is cancelled + context.heartbeat() processItem(item) } return ProcessResult(success = true) } ``` -The `CancellationDetails` provides information about why the activity was cancelled: - -```kotlin -data class CancellationDetails( - val message: String?, - val cause: CancellationType // WORKFLOW_CANCELLED, ACTIVITY_TIMEOUT, etc. -) -``` +> **TODO:** `KActivity.cancellationFuture()` will be added when cancellation can be delivered without requiring heartbeat calls (e.g., server-push cancellation). This will return a `CompletableFuture` for activities that need cancellation notification without heartbeating. ## KActivity API @@ -191,7 +184,7 @@ println("Activity ${info.activityType}, attempt ${info.attempt}") println("Is local: ${info.isLocal}") // Heartbeat for long-running activities (no-op for local activities) -// Heartbeat is infallible - never throws +// Throws CancellationException if activity is cancelled context.heartbeat(progressDetails) // Get heartbeat details from previous attempt (empty for local activities) @@ -208,12 +201,6 @@ val taskToken = context.taskToken object KActivity { /** Get the execution context for the current activity */ val executionContext: KActivityExecutionContext - - /** - * Get a future that completes when the activity is cancelled. - * For non-suspend activities to detect cancellation. - */ - fun cancellationFuture(): CompletableFuture } ``` @@ -225,7 +212,7 @@ interface KActivityExecutionContext { /** * Send a heartbeat with optional progress details. - * Heartbeat is infallible - never throws. Cancellation is detected separately. + * Throws CancellationException if the activity has been cancelled. * No-op for local activities. */ fun heartbeat(details: Any? = null) diff --git a/kotlin/client/README.md b/kotlin/client/README.md index 3548883..1a4608c 100644 --- a/kotlin/client/README.md +++ b/kotlin/client/README.md @@ -1,16 +1,16 @@ # Client API -This section covers the Kotlin client API for interacting with Temporal workflows. +This section covers the Kotlin client API for interacting with Temporal. ## Overview -The Kotlin SDK provides `KWorkflowClient` with suspend functions and type-safe workflow APIs for starting and interacting with workflows. +The Kotlin SDK provides `KClient`, a unified client (like Python's `Client` and .NET's `TemporalClient`) with suspend functions and type-safe APIs for workflows, schedules, and async activity completion. ## Documents | Document | Description | |----------|-------------| -| [Workflow Client](./workflow-client.md) | KWorkflowClient, starting workflows | +| [Client](./workflow-client.md) | KClient, starting workflows, schedules | | [Workflow Handles](./workflow-handle.md) | Typed/Untyped handles, signals, queries, results | | [Advanced Operations](./advanced.md) | SignalWithStart, UpdateWithStart | @@ -19,8 +19,8 @@ The Kotlin SDK provides `KWorkflowClient` with suspend functions and type-safe w ### Creating a Client ```kotlin -val client = KWorkflowClient.connect( - KWorkflowClientOptions( +val client = KClient.connect( + KClientOptions( target = "localhost:7233", namespace = "default" ) diff --git a/kotlin/client/workflow-client.md b/kotlin/client/workflow-client.md index 8497896..b928ba4 100644 --- a/kotlin/client/workflow-client.md +++ b/kotlin/client/workflow-client.md @@ -1,13 +1,13 @@ -# Workflow Client +# Client ## Creating a Client -Use `KWorkflowClient.connect()` to create a client: +Use `KClient.connect()` to create a client (unified client like Python/.NET SDKs): ```kotlin -// Connect to Temporal (like newer SDKs) -val client = KWorkflowClient.connect( - KWorkflowClientOptions( +// Connect to Temporal (like Python/.NET SDKs) +val client = KClient.connect( + KClientOptions( target = "localhost:7233", namespace = "default" ) @@ -22,27 +22,27 @@ val result = runBlocking { } ``` -## KWorkflowClient +## KClient -`KWorkflowClient` provides Kotlin-specific APIs with suspend functions for starting and interacting with workflows: +`KClient` is a unified client (like Python's `Client` and .NET's `TemporalClient`) providing Kotlin-specific APIs with suspend functions for workflows, schedules, and async activity completion: ```kotlin /** - * Kotlin workflow client providing suspend functions and type-safe workflow APIs. + * Unified Kotlin client providing suspend functions and type-safe APIs + * for workflows, schedules, and async activity completion. */ -class KWorkflowClient private constructor(...) { +class KClient private constructor(...) { companion object { /** * Connect to Temporal service and create a client. */ - suspend fun connect(options: KWorkflowClientOptions): KWorkflowClient + suspend fun connect(options: KClientOptions): KClient } /** The underlying WorkflowServiceStubs for advanced use cases */ val workflowService: WorkflowServiceStubs - /** The underlying WorkflowClient for advanced use cases */ - val workflowClient: WorkflowClient + // ==================== Workflow Operations ==================== /** * Start a workflow and return a handle for interaction. @@ -59,7 +59,7 @@ class KWorkflowClient private constructor(...) { arg: A1 ): KWorkflowHandleWithResult - // Overloads for 2-6 arguments... + // Overloads for 2+ arguments using kargs()... /** * Start a workflow and wait for its result. @@ -76,28 +76,53 @@ class KWorkflowClient private constructor(...) { arg: A1 ): R - // Overloads for 2-6 arguments... + // Overloads for 2+ arguments using kargs()... /** * Get a typed handle for an existing workflow by ID. - * Use this to signal, query, or get results from a workflow started elsewhere. */ inline fun workflowHandle(workflowId: String): KWorkflowHandle inline fun workflowHandle(workflowId: String, runId: String): KWorkflowHandle /** * Get a typed handle with known result type for an existing workflow by ID. - * Use when you know both the workflow type and result type at compile time. */ inline fun workflowHandle(workflowId: String): KWorkflowHandleWithResult inline fun workflowHandle(workflowId: String, runId: String): KWorkflowHandleWithResult /** * Get an untyped handle for an existing workflow by ID. - * Use when you don't know the workflow type at compile time. */ fun untypedWorkflowHandle(workflowId: String): KWorkflowHandleUntyped fun untypedWorkflowHandle(workflowId: String, runId: String): KWorkflowHandleUntyped + + // ==================== Schedule Operations ==================== + + /** + * Create a new schedule. + */ + suspend fun createSchedule( + scheduleId: String, + schedule: KSchedule, + options: KScheduleOptions = KScheduleOptions() + ): KScheduleHandle + + /** + * Get a handle to an existing schedule. + */ + fun scheduleHandle(scheduleId: String): KScheduleHandle + + /** + * List all schedules. + */ + fun listSchedules(): Flow + + // ==================== Async Activity Completion ==================== + + /** + * Get a handle for async activity completion using task token. + */ + fun activityCompletionHandle(taskToken: ByteArray): KActivityCompletionHandle } ``` @@ -135,6 +160,19 @@ val typedHandle = client.workflowHandle("greeting-123" val result = typedHandle.result() // Result type already known ``` +## KClientOptions + +```kotlin +data class KClientOptions( + val target: String = "localhost:7233", + val namespace: String = "default", + val identity: String? = null, + val dataConverter: DataConverter? = null, + val interceptors: List = emptyList(), + // ... other options +) +``` + ## KWorkflowOptions See [KOptions](../configuration/koptions.md#kworkflowoptions) for the full `KWorkflowOptions` reference. @@ -143,6 +181,7 @@ See [KOptions](../configuration/koptions.md#kworkflowoptions) for the full `KWor - [Advanced Operations](./advanced.md) - SignalWithStart, UpdateWithStart - [KOptions](../configuration/koptions.md) - KWorkflowOptions reference +- [Schedules](./schedules.md) - Schedule operations --- diff --git a/kotlin/configuration/interceptors.md b/kotlin/configuration/interceptors.md index 78de057..dfd7f8c 100644 --- a/kotlin/configuration/interceptors.md +++ b/kotlin/configuration/interceptors.md @@ -495,8 +495,8 @@ data class KTerminateWorkflowInput( ### Registering Client Interceptors ```kotlin -val client = KWorkflowClient.connect( - KWorkflowClientOptions( +val client = KClient.connect( + KClientOptions( target = "localhost:7233", namespace = "default", interceptors = listOf( diff --git a/kotlin/configuration/koptions.md b/kotlin/configuration/koptions.md index 15378bd..92a8120 100644 --- a/kotlin/configuration/koptions.md +++ b/kotlin/configuration/koptions.md @@ -275,7 +275,7 @@ The Kotlin SDK provides two approaches for configuring options: 1. **KOptions (Recommended)** - Native Kotlin data classes designed for the Kotlin SDK 2. **DSL Builders** - Extension functions on Java SDK builders, provided as a stopgap for using Kotlin with the Java SDK -> **Important:** When using the Kotlin SDK (`KWorkflow`, `KWorkflowClient`, etc.), always use KOptions classes. The DSL builders (`ActivityOptions { }`, `WorkflowOptions { }`, etc.) exist only for compatibility when using Kotlin with the Java SDK directly and should not be used with the Kotlin SDK APIs. +> **Important:** When using the Kotlin SDK (`KWorkflow`, `KClient`, etc.), always use KOptions classes. The DSL builders (`ActivityOptions { }`, `WorkflowOptions { }`, etc.) exist only for compatibility when using Kotlin with the Java SDK directly and should not be used with the Kotlin SDK APIs. | Aspect | DSL Builder (Java SDK interop) | KOptions (Kotlin SDK) | |--------|--------------------------------|------------------------| diff --git a/kotlin/migration.md b/kotlin/migration.md index f5b261d..f26edb6 100644 --- a/kotlin/migration.md +++ b/kotlin/migration.md @@ -5,7 +5,7 @@ | Java SDK | Kotlin SDK | |----------|------------| | **Client** | | -| `WorkflowClient.newInstance(service)` | `KWorkflowClient.connect(options)` | +| `WorkflowClient.newInstance(service)` | `KClient.connect(options)` | | `client.newWorkflowStub(Cls, opts)` | `client.startWorkflow(Interface::method, options, ...)` | | `client.newWorkflowStub(Cls, id)` | `client.workflowHandle(id)` | | `stub.method(arg)` | `client.executeWorkflow(Interface::method, options, arg)` | @@ -64,7 +64,7 @@ | **Testing** | | | `TestWorkflowEnvironment.newInstance()` | `KTestWorkflowEnvironment.newInstance()` | | `testEnv.newWorker(taskQueue)` | `testEnv.newWorker(taskQueue)` → `KWorker` | -| `testEnv.getWorkflowClient()` | `testEnv.workflowClient` → `KWorkflowClient` | +| `testEnv.getWorkflowClient()` | `testEnv.workflowClient` → `KClient` | | `testEnv.sleep(duration)` | `testEnv.sleep(duration)` | | **Primitives** | | | `Promise` | `Deferred` via `async { }` | diff --git a/kotlin/open-questions.md b/kotlin/open-questions.md index b9d5bd4..f856f61 100644 --- a/kotlin/open-questions.md +++ b/kotlin/open-questions.md @@ -173,7 +173,11 @@ val result = client.executeWorkflow( ## Type-Safe Activity/Workflow Arguments -**Status:** Decision needed +**Status:** Decided + +### Decision + +**Option C: KArgs Wrapper Classes** - Use typed `KArgs` classes for 2+ arguments, with simpler direct forms for 0-1 arguments. This provides full compile-time type safety while keeping common cases simple. ### Problem Statement diff --git a/kotlin/testing.md b/kotlin/testing.md index 7eb5f97..27e07d1 100644 --- a/kotlin/testing.md +++ b/kotlin/testing.md @@ -10,7 +10,7 @@ Use `KTestWorkflowEnvironment` to create an in-memory Temporal environment for f class OrderWorkflowTest { private lateinit var testEnv: KTestWorkflowEnvironment private lateinit var worker: KWorker - private lateinit var client: KWorkflowClient + private lateinit var client: KClient @BeforeEach fun setup() { @@ -63,7 +63,7 @@ class KTestWorkflowEnvironment private constructor( } /** The workflow client for starting and interacting with workflows */ - val workflowClient: KWorkflowClient + val workflowClient: KClient /** Create a new worker for the given task queue */ fun newWorker(taskQueue: String): KWorker @@ -288,14 +288,14 @@ For tests that need a real Temporal server: @TestInstance(TestInstance.Lifecycle.PER_CLASS) class OrderWorkflowIntegrationTest { private lateinit var service: WorkflowServiceStubs - private lateinit var client: KWorkflowClient + private lateinit var client: KClient private lateinit var factory: KWorkerFactory @BeforeAll fun setup() { // Connect to local Temporal server service = WorkflowServiceStubs.newLocalServiceStubs() - client = KWorkflowClient(service) + client = KClient(service) factory = KWorkerFactory(client) val worker = factory.newWorker("integration-test-queue") diff --git a/kotlin/worker/setup.md b/kotlin/worker/setup.md index 8e6d591..dc471e5 100644 --- a/kotlin/worker/setup.md +++ b/kotlin/worker/setup.md @@ -5,8 +5,7 @@ For pure Kotlin applications, use `KWorker` which automatically enables coroutine support. Following the Python/.NET pattern, workflows and activities are passed at construction time via options: ```kotlin -val service = WorkflowServiceStubs.newLocalServiceStubs() -val client = KWorkflowClient(service) { ... } +val client = KClient.connect(KClientOptions(target = "localhost:7233")) // Create worker with workflows and activities specified in options val worker = KWorker( @@ -40,7 +39,7 @@ worker.start() * following the Python/.NET SDK pattern. */ class KWorker( - client: KWorkflowClient, + client: KClient, options: KWorkerOptions ) { /** The underlying Java Worker for interop scenarios */ diff --git a/kotlin/workflows/definition.md b/kotlin/workflows/definition.md index 4d14ebf..ca93302 100644 --- a/kotlin/workflows/definition.md +++ b/kotlin/workflows/definition.md @@ -25,7 +25,7 @@ class GreetingWorkflowImpl : GreetingWorkflow { } } -// Client call using KWorkflowClient - same pattern as activities, no stub needed +// Client call using KClient - same pattern as activities, no stub needed val result = client.executeWorkflow( GreetingWorkflow::getGreeting, KWorkflowOptions( diff --git a/tasks/pr-review-answers.md b/tasks/pr-review-answers.md new file mode 100644 index 0000000..9da5dc9 --- /dev/null +++ b/tasks/pr-review-answers.md @@ -0,0 +1,242 @@ +# PR Review Answers + +Decisions on unanswered comments from @cretz on PR #104. + +--- + +## 1. Workflow timer/sleep (api-parity.md:11) + +**Decision:** Add `KWorkflow.delay()` with an overload for options + +```kotlin +// Simple case +KWorkflow.delay(10.seconds) + +// With options +KWorkflow.delay(10.seconds, KTimerOptions(summary = "Waiting for retry")) +``` + +**Open question:** Should stdlib `delay()` also work (mapping internally), or require explicit `KWorkflow.delay()`? + +--- + +## 2. Async await helpers (api-parity.md:20) + +**Decision:** Use standard Kotlin coroutines (Option A) - no need for `KWorkflow.awaitAll()` etc. + +```kotlin +// Use standard coroutines +coroutineScope { + val results = listOf( + async { activity1() }, + async { activity2() } + ).awaitAll() +} +``` + +--- + +## 3. Default activity options (api-parity.md:21) + +**Decision:** Per-call only (Option A) - like newer SDKs, no shared/default activity options mechanism + +--- + +## 4. Rename typedSearchAttributes (api-parity.md:70) + +**Decision:** Rename to `searchAttributes` (Option A) - no legacy to disambiguate from in Kotlin SDK + +--- + +## 5. getVersion side-effecting (api-parity.md:94) + +**Decision:** PENDING - Ask cretz if full patching API (Option D) is preferred + +--- + +## 6. WithStartWorkflowOperation design (advanced.md:21) + +**Decision:** PENDING - Present Option B and ask cretz if this is what he meant: + +```kotlin +val result = client.updateWithStart( + KWithStartWorkflowOperation(MyWorkflow::run, arg, options), // arg before options + MyWorkflow::myUpdate, + updateArg +) +``` + +--- + +## 7. Handle naming (workflow-client.md:77) + +**Decision:** Rename to `workflowHandle()` (Option C) - more Kotlin idiomatic, no get/new prefix + +--- + +## 8. Reified R for optional approach (workflow-client.md:77) + +**Decision:** Add overload to specify `R` when getting handle (Option B): + +```kotlin +val handle = client.workflowHandle(workflowId) +val result = handle.result() // R already known +``` + +--- + +## 9. Options after args (workflow-client.md:31) + +**Decision:** Already updated - args before options (Option B) in open-questions.md + +--- + +## 10. Client connect static call (workflow-client.md:12) + +**Decision:** Single `KWorkflowClient.connect(options)` like newer SDKs (Option A) + +```kotlin +val client = KWorkflowClient.connect( + KWorkflowClientOptions( + target = "localhost:7233", + namespace = "default" + ) +) + +// Access raw gRPC services when needed +val workflowService = client.workflowService +``` + +--- + +## 11. Handle type hierarchy (workflow-handle.md:45) + +**Decision:** Use cretz's recommended naming (Option B) + +```kotlin +KWorkflowHandleUntyped // Untyped base +KWorkflowHandle : KWorkflowHandleUntyped // Typed workflow, untyped result +KWorkflowHandleWithResult : KWorkflowHandle // Fully typed +``` + +--- + +## 12. Options class for handle methods (workflow-handle.md:87) + +**Decision:** Use options class with convenience overloads (Option B). Options always last. + +```kotlin +// Primary API with options +suspend fun startUpdate( + method: KSuspendFunction1, + options: KStartUpdateOptions +): KUpdateHandle + +// Convenience overload with arg before options +suspend fun startUpdate( + method: KSuspendFunction2, + arg: A1, + options: KStartUpdateOptions +): KUpdateHandle +``` + +--- + +## 13. List super class (workflow-handle.md:178) + +**Decision:** Add hierarchy (Option A) + +```kotlin +// Base class returned by listWorkflows() +open class KWorkflowExecutionInfo( + val execution: WorkflowExecution, + val workflowType: String, + val taskQueue: String, + val startTime: Instant, + val status: WorkflowExecutionStatus + // ... lighter set of fields +) + +// Extended class returned by describe() +class KWorkflowExecutionDescription( + // ... all fields from info plus additional details +) : KWorkflowExecutionInfo(...) +``` + +--- + +## 14. Wait for stage required (workflow-handle.md:86) + +**Decision:** Make waitForStage required, no default (Option B) + +```kotlin +// waitForStage is required - no default value +suspend fun startUpdate( + method: KSuspendFunction1, + options: KStartUpdateOptions // waitForStage required in options +): KUpdateHandle +``` + +Note: This aligns with the decision in Q12 to use options classes. `KStartUpdateOptions.waitForStage` will be required. + +--- + +## 15. Data converter design (README.md:52) + +**Decision:** TBD - Mark for later detailed design + +Options to consider: +- A) Newer SDK pattern with composable components (payloadConverter, failureConverter, payloadCodec) +- B) Keep current Java-style DataConverter interface + +--- + +## 16. Client interceptors (interceptors.md:1) + +**Decision:** Add client interceptors (Option A) + +Add `KWorkflowClientInterceptor` for intercepting client-side operations: +- startWorkflow +- signalWorkflow +- queryWorkflow +- executeUpdate +- etc. + +--- + +## 17. Remove required interfaces (kotlin-idioms.md:21) + +**Decision:** Already addressed - interfaces are optional (Option B already in API spec) + +Plain classes with annotated methods are supported; @WorkflowInterface/@ActivityInterface are optional. + +--- + +## 18. Cancellation statement unclear (cancellation.md:32) + +**Decision:** Clarify wording (Option A) + +Update docs to clarify this is standard Kotlin `coroutineScope` exception propagation behavior: +- When one child fails with exception, coroutineScope cancels all other children +- This is automatic Kotlin structured concurrency, not Temporal-specific behavior + +--- + +## 19. Discourage start pattern (README.md:32) + +**Decision:** Recommend run() pattern (Option A) + +Update examples to use `factory.run()` which blocks and propagates fatal errors. +Mention `start()`/`shutdown()` as alternative for advanced use cases. + +```kotlin +// Recommended pattern +factory.run() // Blocks until shutdown, propagates fatal errors + +// Alternative for advanced use cases +factory.start() // Returns immediately +// ... +factory.shutdown() +``` + +--- diff --git a/tasks/pr-review-questions.md b/tasks/pr-review-questions.md new file mode 100644 index 0000000..178a6c5 --- /dev/null +++ b/tasks/pr-review-questions.md @@ -0,0 +1,156 @@ +# PR Review Questions to Address + +Unanswered comments from @cretz on PR #104. + +--- + +## API Parity + +### 1. Workflow timer/sleep (api-parity.md:11) +> We thought this way too about async helpers when we were developing Python, .NET, and Ruby. But there are two reasons why you may want to have a workflow version: 1) because you have more options (e.g. timer summary), and 2) because internal lang runtime developers don't know it's harmful to add an extra async. + +**Status:** [ ] TODO + +--- + +### 2. Async await helpers (api-parity.md:20) +> Technically none of these are needed in Java either and can be written with wait conditions, but we have them anyways in Java (but not usually in other languages) + +**Status:** [ ] TODO + +--- + +### 3. Default activity options (api-parity.md:21) +> Technically we could have made everyone define a shared default options in Java too, but we chose not to (for other languages we don't have this usually) + +**Status:** [ ] TODO + +--- + +### 4. Rename typedSearchAttributes (api-parity.md:70) +> I would not call this `typedSearchAttributes`, just `searchAttributes` is fine, we only used the `typed` prefix to disambiguate from the existing, deprecated form in Java. + +**Status:** [ ] TODO + +--- + +### 5. getVersion side-effecting (api-parity.md:94) +> Arguably this should have never been a getter since it is side-effecting. But I understand if we don't want to change to full-blown patching API. + +**Status:** [ ] TODO + +--- + +## Client + +### 6. WithStartWorkflowOperation design (advanced.md:21) +> Why do I need a client to create a with start workflow operation? And why does that return a "handle"? I think after lots of time spent designing this for update with start in Java, we concluded that the with-start-workflow-operation "promise" was just something passed in to `updateWithStart` (and `signalWithStart` if/when modernized). + +**Status:** [ ] TODO + +--- + +### 7. Handle naming (workflow-client.md:77) +> Would totally be ok calling these `newWorkflowHandle` or just `workflowHandle` + +**Status:** [ ] TODO + +--- + +### 8. Reified R for optional approach (workflow-client.md:77) +> Arguably one should be able to provide reified `R` too if such an optional approach is allowed, but meh + +**Status:** [ ] TODO + +--- + +### 9. Options after args (workflow-client.md:31) +> Same comments as activity concerning options after args, 0 or 1 arg overload only (maybe), etc + +**Status:** [ ] TODO + +--- + +### 10. Client connect static call (workflow-client.md:12) +> In newer SDKs, we found _requiring_ these separate steps unnecessary. Would recommend a static call on `KWorkflowClient` called `connect` that accepts options with target, namespace, etc. + +**Status:** [ ] TODO + +--- + +### 11. Handle type hierarchy (workflow-handle.md:45) +> Mentioned before, but it is confusing to have: +> * `KWorkflowHandle` - half-typed (just not result) +> * `KTypedWorkflowHandle` - typed with result +> * `WorkflowHandle` - untyped, named as if it'll be used from Java +> +> I recommend: +> * `KWorkflowHandle : KWorkflowHandleUntyped` (extends only needed if reasonable) +> * `KWorkflowHandleWithResult : KWorkflowHandle` (extends only needed if reasonable) +> * `KWorkflowHandleUntyped` + +**Status:** [ ] TODO + +--- + +### 12. Options class for handle methods (workflow-handle.md:87) +> These should be in an options class if we want to be consistent (we can sugar out to these if needed) + +**Status:** [ ] TODO + +--- + +### 13. List super class (workflow-handle.md:178) +> I assume there's a super class for this that list returns? + +**Status:** [ ] TODO + +--- + +### 14. Wait for stage required (workflow-handle.md:86) +> We very intentionally decided to _require_ wait for stage at this time because we expect to support "admitted" as a wait for stage one day and we're not sure if that will become the default or if we'll ever have a default. + +**Status:** [ ] TODO + +--- + +## Configuration + +### 15. Data converter design (README.md:52) +> Kotlin has an opportunity to improve on Java here if we want to do what newer SDKs do and make data converter a class that just accepts payload converter, failure converter, and codec instead of having it be something users implement as a whole. But ok if we don't want to have that design here. + +**Status:** [ ] TODO + +--- + +### 16. Client interceptors (interceptors.md:1) +> No client interceptors? + +**Status:** [ ] TODO + +--- + +## Workflows + +### 17. Remove required interfaces (kotlin-idioms.md:21) +> Same as activity, I think we can discuss removing the _required_ interfaces altogether (but still supporting them) + +**Status:** [ ] TODO + +--- + +### 18. Cancellation statement unclear (cancellation.md:32) +> I don't understand this statement exactly. Does it mean "if either activity fails, the workflow fails which implicitly cancels any activities" or is there something more explicit here? + +**Status:** [ ] TODO + +--- + +## Worker + +### 19. Discourage start pattern (README.md:32) +> We should discourage the `start` pattern IMO in favor of a `run` one. We have learned that `start`+`shutdown` can swallow fatal worker-runtime errors that we'd rather propagate. + +**Status:** [ ] TODO + +--- diff --git a/tasks/pr-review-round2.md b/tasks/pr-review-round2.md new file mode 100644 index 0000000..96f15fe --- /dev/null +++ b/tasks/pr-review-round2.md @@ -0,0 +1,197 @@ +# PR Review Round 2 - cretz Comments + +## Q1: Default Parameters - Other SDKs Support Them +**Comment ID:** 2687176966 +**File:** kotlin/open-questions.md:35 +**Status:** DECIDED + +> While I agree with the decision to disallow because it can be easily walked back if we change our minds, it may be worth noting that IIRC, Python, .NET, and Ruby all support default parameters. Here is a Python test confirming it: https://github.com/temporalio/sdk-python/blob/993de6d0e9b42bb01f24edfdb46e0795b00debcf/tests/worker/test_workflow.py#L3502-L3545. + +**Decision:** Allow default parameters for the 0-1 argument case, aligning with Python/.NET/Ruby. With the 0-1 parameter model (>1 falls back to untyped), Kotlin's out-of-order named parameter complexity becomes moot. + +**Response:** Good point about other SDKs supporting defaults. Given we're moving to 0-1 parameters (with >1 falling back to untyped), supporting a default for a single parameter is straightforward and aligns with Python/.NET/Ruby. Will update the docs to allow defaults for the single-parameter case. + +--- + +## Q2: Interfaceless - Require Matching Annotations on Overrides +**Comment ID:** 2687205136 +**File:** kotlin/open-questions.md:162 +**Status:** DECIDED + +> Note, in order to support this in some newer SDKs and not hit diamond problems or other inheritance confusion for when they _do_ want to have interfaces/abstracts, we _require_ every overridden method to have the same matching annotation/attribute/decorator. This prevents ambiguity, is easy to understand, and clarifies author intent clearly to reader without relying on inheritance (at the small cost of having to write duplicate annotations on overrides). + +**Decision:** Keep current Java SDK behavior (annotation only on interface, not required on implementation). Requiring duplicate annotations would break backward compatibility with existing Java activities/workflows and feel unnatural to Java SDK users. + +**Response:** While this approach makes sense for newer SDKs starting fresh, requiring duplicate annotations would break backward compatibility with existing Java activities/workflows and feel unnatural to Java SDK users. Since Kotlin SDK builds on Java SDK, we'll maintain the current convention where annotations on the interface are sufficient. + +--- + +## Q3: Type-Safe Args - 0-1 Arguments Pattern +**Comment ID:** 2687220447 +**File:** kotlin/open-questions.md:216 +**Status:** DECIDED + +> Or "0-1 Argument(s)", meaning any > 1 argument, which Temporal discourages, can fall back to untyped. This is what we do in some newer SDKs. + +**Our Follow-up:** Why do you prefer the type-unsafe fallback for >1 args instead of the KArgs approach which maintains type safety? Is API simplicity the main driver, or are there other considerations? + +**cretz Response (2690497106):** +> API simplicity (and overload count) was my main driver in preferring just falling back to untyped, but in some SDKs (namely Python), we actually do support arbitrary multi-param typed as a different overload. Would be fine if we did that here too. And really, with how .NET is lambda expressions, it supports multi-param typed as well. So overall, yeah, ok w/ a typed multi-param, and having 0 or 1 be even simpler forms of that. + +**Decision:** Use KArgs approach (Option C) - type-safe for all arities with `kargs()` wrapper for 2+ arguments. 0-1 args have simpler direct forms. + +**Response:** Thanks for confirming. We'll go with the KArgs approach (Option C) - provides full type safety for all arities while keeping 0-1 argument cases simple. The `kargs()` wrapper for 2+ args is a small price for compile-time type checking. + +--- + +## Q4: Data Classes - Binary Breaking Not Considered Breaking +**Comment ID:** 2687231550 +**File:** kotlin/open-questions.md:506 +**Status:** DEFERRED - Need to think + +> Hrmm, not sure we have ever considered binary breaking changes to be breaking changes from our POV (i.e. we expect you to compile against the same JAR you'll run with). Having said that, definitely not against better options patterns. But I don't think we want to discourage e.g. users from using `data` classes for their need-backwards-compatibility models. + +**Decision:** Deferred + +**Response:** + +--- + +## Q5: Pure Kotlin Worker Options +**Comment ID:** 2687245857 +**File:** kotlin/activities/implementation.md:68 +**In Reply To:** 2682769025 +**Status:** DECIDED + +> I saw later there is a concept of a "plugin" that you register with a worker if you're using Java interop. Note, for a pure Kotlin experience, we don't have to be subject to Java worker approaches even if it wraps a Java worker. For instance, activities, workflows, Nexus services, etc can be worker options. But I understand not wanting to deviate too far from Java. + +**Decision:** Follow Python/.NET pattern - pass workflows/activities at worker construction time via options rather than separate registration methods. + +**Response:** Agreed. Will follow the Python/.NET pattern where workflows and activities are passed at worker construction time via options. This aligns with newer SDKs and provides immutable, all-in-one-place configuration. + +--- + +## Q6: Heartbeat Infallible + Coroutine Cancellation +**Comment ID:** 2687258492 +**File:** kotlin/activities/implementation.md:1 +**In Reply To:** 2682800514 +**Status:** DECIDED + +> Not understanding "push" cancellation, but yeah so long as I can have heartbeat infallible and cancellation represented as traditional Kotlin coroutine cancellation, I think that is ideal. Arguably both of those could be the default, but I understand if it is confusing to have `suspend fun` do something completely different than non-suspend `fun` (this is a struggle we run into w/ Python where cancellation is represented quite differently w/ `async def` vs just `def`, but not this differently, heartbeat remained infallible on both). + +**Decision:** +- Heartbeat infallible (never throws) +- Suspend activities: Standard coroutine cancellation (CancellationException at suspension points) +- Non-suspend activities: `CompletableFuture` via `KActivity.cancellationFuture()` - supports polling (isDone), blocking (get), or callbacks (thenAccept) + +**Response:** Agreed on heartbeat infallible. For cancellation: +- Suspend activities: Standard Kotlin coroutine cancellation +- Non-suspend activities: `KActivity.cancellationFuture()` returns `CompletableFuture` which supports polling (`isDone`), blocking (`get`), or callback notification (`thenAccept`) - similar flexibility to Python's approach. + +--- + +## Q7: Inconsistent Annotation Usage +**Comment ID:** 2687264865 +**File:** kotlin/activities/local-activities.md:10 +**In Reply To:** 2682894879 +**Status:** DECIDED + +> Makes sense, was just a bit strange to see it present inconsistently sometimes even w/out name customization in this proposal + +**Decision:** Only show annotations in examples when customizing name (minimal approach). Will clean up docs for consistency. + +**Response:** Good catch. Will clean up the docs to be consistent - only showing annotations like `@ActivityMethod` when customizing the name. + +--- + +## Q8: Workflow-Specific Alternatives for Sleep +**Comment ID:** 2687272025 +**File:** kotlin/api-parity.md:11 +**In Reply To:** 2682956017 +**Status:** DECIDED + +> Using existing async utilities makes sense, but still may need some workflow-specific alternatives for advanced users, such as being able to provide a timer summary for `sleep`. + +**Decision:** Provide both overloads: +- `KWorkflow.delay(duration)` - simple alternative to stdlib `delay()` +- `KWorkflow.delay(duration, summary)` - for timer summaries + +**Response:** Agreed. Will provide `KWorkflow.delay(duration)` as a simple alternative to stdlib `delay()`, plus `KWorkflow.delay(duration, summary)` for advanced users who need timer summaries. + +--- + +## Q9: KotlinPlugin Naming Confusion +**Comment ID:** 2687294728 +**File:** kotlin/worker/setup.md:97 +**In Reply To:** 2683181166 +**Status:** DECIDED + +> Makes sense to implement Kotlin support on Java workers as a plugin, though may get a bit confusing to call it KotlinPlugin, assuming there will _also_ be KPlugin for users to implement plugins in Kotlin (granted I can't think of a much better name, maybe KotlinToJavaWorkerPlugin or something) + +**Decision:** Rename to `KotlinJavaWorkerPlugin` - clearly indicates it's for integrating Kotlin with Java workers. + +**Response:** Good point about naming confusion. Renamed to `KotlinJavaWorkerPlugin` to make it clear this is for integrating Kotlin coroutine support with Java workers. + +--- + +## Q10: coroutineScope vs supervisorScope +**Comment ID:** 2687311387 +**File:** kotlin/workflows/cancellation.md:32 +**In Reply To:** 2683186177 +**Status:** RESOLVED + +> Ah, I see this is the difference between `coroutineScope` and `supervisorScope` + +**Decision:** No action needed - cretz's comment is an acknowledgment that he understood the distinction. + +**Response:** (No response needed - resolved by acknowledgment) + +--- + +## Q11: Heartbeat Infallible - Slot Eating Concern +**Comment ID:** 2690451590 +**File:** kotlin/activities/implementation.md +**In Reply To:** 2682800514 (Q6) +**Status:** DECIDED + +> Note, I was a bit on the fence here of whether heartbeat being infallible should be the default. There are pros/cons to the default of heartbeat still throwing. Granted this is one of those defaults people will probably never change via worker options, so maybe deviating from Java is accepted here. But we've found if we don't interrupt these activities by default and require people to opt-in to checking cancellation (i.e. the non-suspend ones), they will eat slots because people will forget. + +**Concern:** Non-suspend activities may eat worker slots if developers forget to check cancellation. + +**Decision:** Heartbeat throws `CancellationException` on cancellation for both suspend and non-suspend activities. This prevents slot eating and provides consistent behavior. Add TODO for `cancellationFuture()` which will be needed when cancellation can be delivered without heartbeat. + +**Response:** Good point about slot eating. Revised approach: heartbeat throws `CancellationException` on cancellation for both suspend and non-suspend activities. This prevents slot eating and aligns with Java behavior. We'll document `cancellationFuture()` as a TODO for future use when cancellation can be delivered without requiring heartbeat calls. + +--- + +## Q12: Unified Client Suggestion +**Comment ID:** 2690483803 +**File:** kotlin/client/workflow-client.md +**Status:** DECIDED + +> Forgot to mention this, but in newer SDKs, we found just having one big client that is a workflow client + schedule client (w/ features to make an async activity completion handle) is a bit cleaner from an options POV. This will also help when standalone activity client and Nexus operation client come about. No need to change though if we don't want, there is also value in matching what Java does (though you will duplicate a lot of these client options each time). + +**Suggestion:** Consider unified `KClient` instead of separate `KWorkflowClient`, `KScheduleClient`, etc. + +**Decision:** Use unified `KClient` matching Python/.NET style. Single client with workflow, schedule, async activity completion, and future Nexus support. + +**Response:** Agreed. We'll use a unified `KClient` matching the Python/.NET pattern - single client covering workflows, schedules, async activity completion, and ready for future Nexus support. Cleaner options and less duplication. + +--- + +## Previous Pending Questions + +### R1-Q5: getVersion API +**Comment ID:** 2682977990 +**Status:** DECIDED + +**cretz Response (2690460783):** +> I have no strong preference, but as a past Kotlin developer, I always thought in terms of Java, so I think our default stance of "be like Java" is a good one for developer understanding. (I'm not even sure the newer patching approaches for Core-based SDKs can be done in Java user-land today without Java SDK updates) + +**Decision:** Use `KWorkflow.version(changeId, minVersion, maxVersion)` method instead of getter-style. Better indicates side-effect nature. + +**Response:** Since there's no strong preference, we'll use `KWorkflow.version()` as a method rather than getter-style. This better indicates the side-effecting nature of the call, which feels more idiomatic for Kotlin. + +### R1-Q6: WithStartWorkflowOperation Design +**Comment ID:** 2683006111 +**Status:** PENDING - Awaiting cretz clarification From edf8cbf4df6f5fea583a76906c1367b8d61fd421 Mon Sep 17 00:00:00 2001 From: Maxim Fateev <1463622+mfateev@users.noreply.github.com> Date: Wed, 14 Jan 2026 21:26:41 -0600 Subject: [PATCH 82/83] Address PR review comments Q20-Q24 - Use KActivityContext.current() pattern instead of KActivity (Q20) - Remove KActivity.logger()/KWorkflow.logger(), use MDC with standard logging (Q21) - Keep single-argument heartbeat, rename to lastHeartbeatDetails() (Q22) - Document event loop implementation on top of Java SDK (Q24) Updated files: - activities/implementation.md - activities/local-activities.md - workflows/definition.md - configuration/interceptors.md - migration.md - implementation/implementation-plan.md --- kotlin/activities/implementation.md | 56 +++++++------- kotlin/activities/local-activities.md | 14 ++-- kotlin/configuration/interceptors.md | 17 +++-- kotlin/implementation/implementation-plan.md | 8 +- kotlin/migration.md | 14 ++-- kotlin/workflows/definition.md | 18 +---- tasks/pr-review-answers.md | 79 ++++++++++++++++++++ tasks/pr-review-questions.md | 46 ++++++++++++ 8 files changed, 181 insertions(+), 71 deletions(-) diff --git a/kotlin/activities/implementation.md b/kotlin/activities/implementation.md index 97c856a..77b1b93 100644 --- a/kotlin/activities/implementation.md +++ b/kotlin/activities/implementation.md @@ -80,7 +80,7 @@ For long-running activities, use heartbeating to report progress. **Heartbeat th ```kotlin class LongRunningActivitiesImpl : LongRunningActivities { override suspend fun processLargeFile(filePath: String): ProcessResult { - val context = KActivity.executionContext + val context = KActivityContext.current() val lines = File(filePath).readLines() lines.forEachIndexed { index, line -> @@ -104,13 +104,13 @@ Retrieve heartbeat details from a previous failed attempt: ```kotlin override suspend fun resumableProcess(data: List): ProcessResult { - val context = KActivity.executionContext + val ctx = KActivityContext.current() // Get progress from previous attempt if available - val startIndex = context.heartbeatDetails() ?: 0 + val startIndex = ctx.lastHeartbeatDetails() ?: 0 for (i in startIndex until data.size) { - context.heartbeat(i) + ctx.heartbeat(i) processItem(data[i]) } @@ -126,7 +126,7 @@ Activity cancellation is delivered via `heartbeat()` - when an activity is cance ```kotlin override suspend fun processItems(items: List): ProcessResult { - val context = KActivity.executionContext + val context = KActivityContext.current() for (item in items) { // CancellationException thrown here if activity is cancelled context.heartbeat() @@ -137,7 +137,7 @@ override suspend fun processItems(items: List): ProcessResult { // With cleanup on cancellation override suspend fun processWithCleanup(items: List): ProcessResult { - val context = KActivity.executionContext + val context = KActivityContext.current() return try { for (item in items) { context.heartbeat() @@ -158,7 +158,7 @@ Non-suspend activities also receive cancellation via heartbeat: ```kotlin override fun processItemsBlocking(items: List): ProcessResult { - val context = KActivity.executionContext + val context = KActivityContext.current() for (item in items) { // CancellationException thrown here if activity is cancelled context.heartbeat() @@ -168,46 +168,42 @@ override fun processItemsBlocking(items: List): ProcessResult { } ``` -> **TODO:** `KActivity.cancellationFuture()` will be added when cancellation can be delivered without requiring heartbeat calls (e.g., server-push cancellation). This will return a `CompletableFuture` for activities that need cancellation notification without heartbeating. +> **TODO:** `KActivityContext.current().cancellationFuture()` will be added when cancellation can be delivered without requiring heartbeat calls (e.g., server-push cancellation). This will return a `CompletableFuture` for activities that need cancellation notification without heartbeating. -## KActivity API +## KActivityContext API -`KActivity.executionContext` provides access to the activity execution context for both regular and local activities: +`KActivityContext.current()` provides access to the activity execution context for both regular and local activities: ```kotlin // In activity implementation -val context = KActivity.executionContext +val ctx = KActivityContext.current() // Get activity info (works for both regular and local activities) -val info = context.info +val info = ctx.info println("Activity ${info.activityType}, attempt ${info.attempt}") println("Is local: ${info.isLocal}") // Heartbeat for long-running activities (no-op for local activities) // Throws CancellationException if activity is cancelled -context.heartbeat(progressDetails) +ctx.heartbeat(progressDetails) -// Get heartbeat details from previous attempt (empty for local activities) -val previousProgress = context.heartbeatDetails() +// Get last heartbeat details from previous attempt (null for local activities) +val previousProgress = ctx.lastHeartbeatDetails() // Regular activities only - throws UnsupportedOperationException for local activities -context.doNotCompleteOnReturn() -val taskToken = context.taskToken +ctx.doNotCompleteOnReturn() +val taskToken = ctx.taskToken ``` -### KActivity Static Methods +## KActivityContext Interface ```kotlin -object KActivity { - /** Get the execution context for the current activity */ - val executionContext: KActivityExecutionContext -} -``` - -## KActivityExecutionContext Interface +interface KActivityContext { + /** Get the current activity context. */ + companion object { + fun current(): KActivityContext + } -```kotlin -interface KActivityExecutionContext { val info: KActivityInfo /** @@ -217,8 +213,8 @@ interface KActivityExecutionContext { */ fun heartbeat(details: Any? = null) - /** Get heartbeat details from previous attempt. Empty for local activities. */ - fun heartbeatDetails(detailsClass: Class): T? + /** Get last heartbeat details from previous attempt. Null for local activities. */ + fun lastHeartbeatDetails(detailsClass: Class): T? /** Task token for async completion. Throws for local activities. */ val taskToken: ByteArray @@ -230,7 +226,7 @@ interface KActivityExecutionContext { } // Reified extension for easier Kotlin usage -inline fun KActivityExecutionContext.heartbeatDetails(): T? +inline fun KActivityContext.lastHeartbeatDetails(): T? ``` ## KActivityInfo Interface diff --git a/kotlin/activities/local-activities.md b/kotlin/activities/local-activities.md index 9bde98a..0dce121 100644 --- a/kotlin/activities/local-activities.md +++ b/kotlin/activities/local-activities.md @@ -64,17 +64,17 @@ data class KLocalActivityOptions( ) ``` -## KActivity.executionContext in Local Activities +## KActivityContext in Local Activities -`KActivity.executionContext` is available in local activities with limited functionality: +`KActivityContext.current()` is available in local activities with limited functionality: | Feature | Local Activity Behavior | |---------|------------------------| -| `context.info` | Works (`info.isLocal` returns `true`) | -| `context.heartbeat()` | No-op (ignored) | -| `context.heartbeatDetails()` | Returns `null` | -| `context.taskToken` | Throws `UnsupportedOperationException` | -| `context.doNotCompleteOnReturn()` | Throws `UnsupportedOperationException` | +| `ctx.info` | Works (`info.isLocal` returns `true`) | +| `ctx.heartbeat()` | No-op (ignored) | +| `ctx.lastHeartbeatDetails()` | Returns `null` | +| `ctx.taskToken` | Throws `UnsupportedOperationException` | +| `ctx.doNotCompleteOnReturn()` | Throws `UnsupportedOperationException` | ## Related diff --git a/kotlin/configuration/interceptors.md b/kotlin/configuration/interceptors.md index dfd7f8c..9dfaed5 100644 --- a/kotlin/configuration/interceptors.md +++ b/kotlin/configuration/interceptors.md @@ -298,6 +298,8 @@ data class KActivityExecutionOutput(val result: Any?) ## Example: Logging Interceptor +Uses standard logging with MDC. The SDK automatically populates MDC with context (workflowId, runId, activityType, etc.). + ```kotlin class LoggingInterceptor : KWorkerInterceptorBase() { @@ -318,7 +320,8 @@ private class LoggingWorkflowInterceptor( next: KWorkflowInboundCallsInterceptor ) : KWorkflowInboundCallsInterceptorBase(next) { - private val log = KWorkflow.logger() + // Standard SLF4J logger - MDC is populated automatically by the SDK + private val log = LoggerFactory.getLogger(LoggingWorkflowInterceptor::class.java) override suspend fun execute(input: KWorkflowInput): KWorkflowOutput { log.info("Workflow started with ${input.arguments.size} arguments") @@ -363,21 +366,23 @@ private class LoggingActivityInterceptor( next: KActivityInboundCallsInterceptor ) : KActivityInboundCallsInterceptorBase(next) { + // Standard SLF4J logger - MDC is populated automatically by the SDK + private val log = LoggerFactory.getLogger(LoggingActivityInterceptor::class.java) + override suspend fun execute(input: KActivityExecutionInput): KActivityExecutionOutput { - val info = KActivity.info - val log = KActivity.logger() + val ctx = KActivityContext.current() - log.info("Activity ${info.activityType} started") + log.info("Activity ${ctx.info.activityType} started") val startTime = System.currentTimeMillis() return try { val result = next.execute(input) val duration = System.currentTimeMillis() - startTime - log.info("Activity ${info.activityType} completed in ${duration}ms") + log.info("Activity ${ctx.info.activityType} completed in ${duration}ms") result } catch (e: Exception) { val duration = System.currentTimeMillis() - startTime - log.error("Activity ${info.activityType} failed after ${duration}ms", e) + log.error("Activity ${ctx.info.activityType} failed after ${duration}ms", e) throw e } } diff --git a/kotlin/implementation/implementation-plan.md b/kotlin/implementation/implementation-plan.md index 7ee1e79..28db030 100644 --- a/kotlin/implementation/implementation-plan.md +++ b/kotlin/implementation/implementation-plan.md @@ -76,14 +76,14 @@ - ✅ DSL builders for worker options ### 2.6 Kotlin Activity API ✅ -- ✅ `KActivity` object (entry point for activity APIs) -- ✅ `KActivity.info`, `context`, `heartbeat()` properties/methods -- ✅ `KActivity.logger()` for idiomatic logging +- ✅ `KActivityContext.current()` (entry point for activity APIs) +- ✅ `ctx.info`, `ctx.heartbeat()`, `ctx.lastHeartbeatDetails()` methods +- ✅ MDC populated automatically with activity context for standard logging - ✅ `KActivityInfo` with null safety - ✅ Suspend activity support via `SuspendActivityWrapper` ### 2.7 Kotlin Workflow API ✅ -- ✅ `KWorkflow.logger()` for idiomatic logging +- ✅ MDC populated automatically with workflow context for standard logging - ✅ `KWorkflow.async {}` for eager parallel execution - ✅ `KWorkflow.continueAsNew()` with `KContinueAsNewOptions` - ✅ `KWorkflow.retry()` for workflow-level retry with exponential backoff diff --git a/kotlin/migration.md b/kotlin/migration.md index f26edb6..df7c61e 100644 --- a/kotlin/migration.md +++ b/kotlin/migration.md @@ -19,7 +19,7 @@ | `worker.registerActivitiesImplementations(impl)` | `worker.registerActivitiesImplementations(impl)` | | **KWorkflow Object** | | | `Workflow.getInfo()` | `KWorkflow.info` | -| `Workflow.getLogger()` | `KWorkflow.logger()` | +| `Workflow.getLogger()` | Standard SLF4J logger (MDC populated by SDK) | | `Workflow.sleep(duration)` | `delay(duration)` or `KWorkflow.delay(duration, options)` | | `Workflow.await(() -> cond)` | `KWorkflow.awaitCondition { cond }` | | `Workflow.sideEffect(cls, func)` | `KWorkflow.sideEffect { func }` | @@ -55,12 +55,12 @@ | `Workflow.newDetachedCancellationScope(...)` | `withContext(NonCancellable) { ... }` | | `scope.cancel()` | `job.cancel()` | | `CancellationScope.isCancelRequested()` | `!isActive` | -| **KActivity Object** | | -| `Activity.getExecutionContext()` | `KActivity.executionContext` | -| `context.getInfo()` | `KActivity.executionContext.info` or `KActivity.info` | -| `context.heartbeat(details)` | `KActivity.executionContext.heartbeat(details)` | -| `context.getHeartbeatDetails(cls)` | `KActivity.executionContext.heartbeatDetails()` | -| `context.doNotCompleteOnReturn()` | `KActivity.executionContext.doNotCompleteOnReturn()` | +| **KActivityContext** | | +| `Activity.getExecutionContext()` | `KActivityContext.current()` | +| `context.getInfo()` | `KActivityContext.current().info` | +| `context.heartbeat(details)` | `KActivityContext.current().heartbeat(details)` | +| `context.getHeartbeatDetails(cls)` | `KActivityContext.current().lastHeartbeatDetails()` | +| `context.doNotCompleteOnReturn()` | `KActivityContext.current().doNotCompleteOnReturn()` | | **Testing** | | | `TestWorkflowEnvironment.newInstance()` | `KTestWorkflowEnvironment.newInstance()` | | `testEnv.newWorker(taskQueue)` | `testEnv.newWorker(taskQueue)` → `KWorker` | diff --git a/kotlin/workflows/definition.md b/kotlin/workflows/definition.md index ca93302..c61f5e1 100644 --- a/kotlin/workflows/definition.md +++ b/kotlin/workflows/definition.md @@ -134,23 +134,7 @@ suspend fun processOrder(params: ProcessOrderParams): OrderResult * Method annotations (`@WorkflowMethod`, `@SignalMethod`, etc.) are optional - use only when customizing names * Data classes work naturally for parameters and results -## Logging - -Use `KWorkflow.logger()` for workflow-safe logging: - -```kotlin -// Get logger using workflow type as name -val log = KWorkflow.logger() -log.info("Processing order") - -// Or with custom logger name -val customLog = KWorkflow.logger("my.custom.logger") - -// Or with class -val classLog = KWorkflow.logger(MyWorkflowImpl::class.java) -``` - -> **Note:** Versioning (`KWorkflow.getVersion`), side effects (`KWorkflow.sideEffect`), and search attributes (`KWorkflow.upsertSearchAttributes`) use the same patterns as the Java SDK. +> **Note:** Versioning (`KWorkflow.version()`), side effects (`KWorkflow.sideEffect`), and search attributes (`KWorkflow.upsertSearchAttributes`) use the same patterns as the Java SDK. Logging uses standard logging frameworks with MDC - the SDK automatically populates MDC with workflow context (workflowId, runId, taskQueue, etc.). ## Related diff --git a/tasks/pr-review-answers.md b/tasks/pr-review-answers.md index 9da5dc9..5f89bce 100644 --- a/tasks/pr-review-answers.md +++ b/tasks/pr-review-answers.md @@ -240,3 +240,82 @@ factory.shutdown() ``` --- + +## 24. Event loop control (kotlin-idioms.md:58) + +**Decision:** Kotlin wraps Java SDK, `awaitCondition` built on `Async.await()` + +Kotlin SDK does not have its own event loop - it's implemented on top of Java SDK. The `awaitCondition` functionality will be built on top of the new `Async.await()` method being added to Java SDK (PR #2751), which returns a `Promise` for non-blocking condition waiting. + +```kotlin +// Kotlin awaitCondition suspends the coroutine +KWorkflow.awaitCondition { condition } + +// Implemented using Java's Async.await() under the hood +// Async.await(supplier) -> Promise +``` + +--- + +## 23. Priority for local activities (local-activities.md) + +**Decision:** Already addressed - priority removed from KLocalActivityOptions + +The `KLocalActivityOptions` class doesn't include `priority` or other non-applicable fields (like task queue). Local activities run in-process, so task queue routing options don't apply. + +--- + +## 22. Heartbeat details collection (implementation.md) + +**Decision:** Support single argument only, like Java SDK + +Keep it simple - heartbeat accepts a single detail value. If users need multiple values, they wrap them in a data class. + +```kotlin +// Heartbeat with single value +ctx.heartbeat(progressPercent) + +// Or wrap multiple values in a data class +data class MyProgress(val percent: Int, val checkpoint: String) +ctx.heartbeat(MyProgress(50, "step-3")) + +// Retrieve on retry +val progress: MyProgress? = ctx.lastHeartbeatDetails() +``` + +Use `lastHeartbeatDetails` naming to clarify it's from the last heartbeat before retry. + +--- + +## 21. Logger on context (implementation.md) + +**Decision:** Remove logger from context, use MDC only (Option A) + +Use MDC/coroutine context for activity and workflow metadata. Users use standard logging frameworks. Temporal SDK populates MDC automatically with workflowId, activityId, etc. + +```kotlin +// SDK sets up MDC with activity/workflow info automatically +val logger = LoggerFactory.getLogger(MyActivity::class.java) +logger.info("Processing...") // MDC includes activityId, workflowId, taskQueue, etc. +``` + +Remove `KActivity.logger()` and `KWorkflow.logger()` from the API. This matches Java SDK approach. + +--- + +## 20. Context data type (implementation.md) + +**Decision:** Use `KActivityContext.current()` pattern, remove KActivity class + +Move context access to the context class itself, matching .NET pattern. Since Kotlin doesn't have checked exceptions, `Activity.wrap()` is not needed, so `KActivity` class can be removed entirely. + +```kotlin +val ctx = KActivityContext.current() +ctx.info // ActivityInfo +ctx.heartbeat(details) +ctx.heartbeatDetails() +ctx.doNotCompleteOnReturn() +ctx.cancellationFuture() // for non-suspend activities +``` + +--- diff --git a/tasks/pr-review-questions.md b/tasks/pr-review-questions.md index 178a6c5..43bae29 100644 --- a/tasks/pr-review-questions.md +++ b/tasks/pr-review-questions.md @@ -154,3 +154,49 @@ Unanswered comments from @cretz on PR #104. **Status:** [ ] TODO --- + +## Activities - Additional + +### 20. Context data type (implementation.md) +**Comment ID:** 2682791531 +> What is the data type of this context? Arguably for OO languages where the context is a type one can pass around and reference, obtaining the current context is a static method on the type itself, but if it is the Java SDK context, makes sense (static extension methods are not a thing). EDIT: I see later this is a Kotlin context class, I would recommend moving this call to that class, though it can be here too, meh. + +**Status:** [ ] TODO + +--- + +### 21. Logger on context (implementation.md) +**Comment ID:** 2682832357 +> Can I get more detail on why a logger would be on the context. Is there an expected Kotlin logging solution that expects situationally stateful loggers instead of existing MDC/NDC thread/coroutine-local types of approaches? + +**Status:** [ ] TODO + +--- + +### 22. Heartbeat details collection (implementation.md) +**Comment ID:** 2682834623 +> Heartbeat details is a collection, this will either have to accept an index and have a total-count method, or maybe accept some kind of reified tuple type or something. I guess there can be a helper for just the first detail item. Same for the actual `heartbeat` call. +> +> Also, will there be a `lastHeartbeatDetails`? + +**Status:** [ ] TODO + +--- + +### 23. Priority for local activities (local-activities.md) +**Comment ID:** 2682941947 +> Priority doesn't make sense here + +**Status:** [ ] TODO + +--- + +## Workflows - Additional + +### 24. Event loop control (kotlin-idioms.md:58) +**Comment ID:** 2683150870 +> Do we have full control over the event loop including running the `awaitCondition` stuff when/how we want in that loop, or is this somehow just sugar on top of the Java one (sorry, could dig into the PoC, but being lazy) + +**Status:** [ ] TODO + +--- From 8d3d46fc5288f341379086fcc50bfc711e3a908e Mon Sep 17 00:00:00 2001 From: Maxim Fateev <1463622+mfateev@users.noreply.github.com> Date: Fri, 16 Jan 2026 13:14:49 -0800 Subject: [PATCH 83/83] Add run() method to KWorker API Following Python SDK pattern, KWorker now has: - run() - recommended, blocks until shutdown, propagates fatal errors - start()/shutdown() - for advanced manual control scenarios Updated examples to use run() as the primary pattern. --- kotlin/worker/README.md | 33 +++++++++++++-------------- kotlin/worker/setup.md | 49 ++++++++++++++++++++++++++++++++++++----- 2 files changed, 61 insertions(+), 21 deletions(-) diff --git a/kotlin/worker/README.md b/kotlin/worker/README.md index 4672fe9..23b6e29 100644 --- a/kotlin/worker/README.md +++ b/kotlin/worker/README.md @@ -17,28 +17,29 @@ The Kotlin SDK provides `KWorkerFactory` and `KWorker` which automatically enabl ### Basic Worker Setup ```kotlin -val service = WorkflowServiceStubs.newLocalServiceStubs() -val client = KWorkflowClient(service) - -// KWorkerFactory automatically enables Kotlin coroutine support -val factory = KWorkerFactory(client) -val worker = factory.newWorker("task-queue") - -// Register workflows and activities -worker.registerWorkflowImplementationTypes(MyWorkflowImpl::class) -worker.registerActivitiesImplementations(MyActivitiesImpl()) - -// Start the worker -factory.start() +val client = KClient.connect(KClientOptions(target = "localhost:7233")) + +// Create worker with workflows and activities at construction time +val worker = KWorker( + client, + KWorkerOptions( + taskQueue = "task-queue", + workflows = listOf(MyWorkflowImpl::class), + activities = listOf(MyActivitiesImpl()) + ) +) + +// Run the worker (blocks until shutdown or fatal error) +worker.run() ``` ### Key Components | Component | Purpose | |-----------|---------| -| `KWorkerFactory` | Creates workers with automatic coroutine support | -| `KWorker` | Registers Kotlin workflows and suspend activities | -| `KotlinPlugin` | Plugin for Java main apps (used automatically by KWorkerFactory) | +| `KWorker` | Creates and runs workers with automatic coroutine support | +| `KWorkerOptions` | Configuration including workflows and activities | +| `KotlinJavaWorkerPlugin` | Plugin for Java main apps using Kotlin workflows | ## Related diff --git a/kotlin/worker/setup.md b/kotlin/worker/setup.md index dc471e5..4c6d9c6 100644 --- a/kotlin/worker/setup.md +++ b/kotlin/worker/setup.md @@ -24,8 +24,8 @@ val worker = KWorker( ) ) -// Start the worker -worker.start() +// Run the worker (blocks until shutdown signal or fatal error) +worker.run() ``` ## KWorker API @@ -45,9 +45,30 @@ class KWorker( /** The underlying Java Worker for interop scenarios */ val worker: Worker + /** + * Run the worker, blocking until a shutdown signal is received or a fatal error occurs. + * This is the recommended way to run a worker. + * + * Unlike start()/shutdown(), this method propagates fatal worker-runtime errors + * rather than silently swallowing them. + * + * @throws WorkerException if a fatal error occurs during worker execution + */ + suspend fun run() + + /** + * Start the worker without blocking. Use shutdown() to stop. + * Prefer run() for most use cases as it properly propagates fatal errors. + */ fun start() + + /** Gracefully shut down the worker, allowing in-flight tasks to complete. */ fun shutdown() + + /** Immediately shut down the worker, cancelling in-flight tasks. */ fun shutdownNow() + + /** Wait for the worker to terminate after shutdown is called. */ suspend fun awaitTermination(timeout: Duration) } ``` @@ -73,7 +94,7 @@ data class KWorkerOptions( ## Multiple Workers -For multiple task queues, create multiple workers: +For multiple task queues, create multiple workers and run them concurrently: ```kotlin val orderWorker = KWorker( @@ -94,9 +115,27 @@ val notificationWorker = KWorker( ) ) -// Start all workers +// Run all workers concurrently - blocks until shutdown or fatal error +coroutineScope { + launch { orderWorker.run() } + launch { notificationWorker.run() } +} +``` + +For advanced scenarios where you need manual control: + +```kotlin +// Start workers without blocking orderWorker.start() notificationWorker.start() + +// ... do other work ... + +// Graceful shutdown +orderWorker.shutdown() +notificationWorker.shutdown() +orderWorker.awaitTermination(30.seconds) +notificationWorker.awaitTermination(30.seconds) ``` ## KotlinJavaWorkerPlugin (For Java Main) @@ -139,7 +178,7 @@ val worker = KWorker( ) // Both run on the same worker - execution model is per-workflow-instance -worker.start() +worker.run() // Blocks until shutdown or fatal error ``` ## Related