runtara_sdk/
lib.rs

1// Copyright (C) 2025 SyncMyOrders Sp. z o.o.
2// SPDX-License-Identifier: AGPL-3.0-or-later
3//! Runtara SDK - High-level client for instance communication with runtara-core.
4//!
5//! This crate provides an ergonomic API for scenarios/instances to communicate
6//! with the runtara-core durable execution engine. It wraps the low-level
7//! `runtara-protocol` QUIC client and provides strongly-typed methods for all
8//! instance lifecycle operations.
9//!
10//! # Features
11//!
12//! - **Instance Registration**: Self-register with runtara-core on startup
13//! - **Checkpointing**: Save state for durability with automatic resume handling
14//! - **Durable Sleep**: Request sleep with automatic checkpoint/wake
15//! - **Lifecycle Events**: Send heartbeat, completed, failed events
16//! - **Signal Handling**: Poll and handle cancel, pause, resume signals
17//! - **Status Queries**: Query instance status and server health
18//!
19//! # Quick Start
20//!
21//! ```ignore
22//! use runtara_sdk::RuntaraSdk;
23//!
24//! #[tokio::main]
25//! async fn main() -> runtara_sdk::Result<()> {
26//!     let mut sdk = RuntaraSdk::localhost("my-instance", "my-tenant")?;
27//!
28//!     // Connect and register
29//!     sdk.connect().await?;
30//!     sdk.register(None).await?;
31//!
32//!     // Process items with checkpointing
33//!     for i in 0..items.len() {
34//!         let state = serde_json::to_vec(&my_state)?;
35//!         let result = sdk.checkpoint(&format!("item-{}", i), &state).await?;
36//!
37//!         // Check for pause/cancel signals
38//!         if result.should_cancel() {
39//!             return Err(SdkError::Cancelled.into());
40//!         }
41//!         if result.should_pause() {
42//!             sdk.suspended().await?;
43//!             return Ok(());
44//!         }
45//!
46//!         if let Some(existing) = result.existing_state() {
47//!             // Resuming - restore state and skip
48//!             my_state = serde_json::from_slice(existing)?;
49//!             continue;
50//!         }
51//!         // Fresh execution - process item
52//!         process_item(&items[i]);
53//!     }
54//!
55//!     sdk.completed(b"result data").await?;
56//!     Ok(())
57//! }
58//! ```
59//!
60//! # Checkpointing
61//!
62//! The `checkpoint()` method handles both save and resume semantics, and also
63//! returns pending signal information for efficient pause/cancel detection:
64//!
65//! ```ignore
66//! // checkpoint() returns CheckpointResult with:
67//! // - existing_state() -> Some(&[u8]) if checkpoint exists (resume case)
68//! // - existing_state() -> None if new checkpoint was just saved
69//! // - should_pause() / should_cancel() for pending signals
70//! for i in 0..items.len() {
71//!     let state = serde_json::to_vec(&my_state)?;
72//!     let result = sdk.checkpoint(&format!("step-{}", i), &state).await?;
73//!
74//!     if result.should_pause() {
75//!         sdk.suspended().await?;
76//!         return Ok(()); // Exit cleanly - will be resumed later
77//!     }
78//!
79//!     if let Some(existing) = result.existing_state() {
80//!         my_state = serde_json::from_slice(existing)?;
81//!         continue; // Skip - already processed
82//!     }
83//!     // Process item...
84//! }
85//! ```
86//!
87//! # Durable Sleep
88//!
89//! The SDK supports durable sleep:
90//!
91//! ```ignore
92//! use std::time::Duration;
93//!
94//! // Sleep is always handled in-process
95//! sdk.sleep(
96//!     Duration::from_secs(60),    // duration
97//!     "after-sleep",              // checkpoint ID for resume
98//!     &serialized_state,          // state to restore
99//! ).await?;
100//!
101//! // Continue execution after sleep completes
102//! ```
103//!
104//! # Signal Handling
105//!
106//! Instances can receive cancel, pause, and resume signals:
107//!
108//! ```ignore
109//! // Simple cancellation check (returns Err(SdkError::Cancelled) if cancelled)
110//! sdk.check_cancelled().await?;
111//!
112//! // Manual signal polling
113//! if let Some(signal) = sdk.poll_signal().await? {
114//!     match signal.signal_type {
115//!         SignalType::Cancel => {
116//!             sdk.acknowledge_signal(SignalType::Cancel, true).await?;
117//!             return Err(SdkError::Cancelled);
118//!         }
119//!         SignalType::Pause => {
120//!             sdk.checkpoint("paused", &state).await?;
121//!             sdk.acknowledge_signal(SignalType::Pause, true).await?;
122//!             sdk.suspended().await?;
123//!         }
124//!         SignalType::Resume => {
125//!             sdk.acknowledge_signal(SignalType::Resume, true).await?;
126//!         }
127//!     }
128//! }
129//! ```
130//!
131//! # Configuration
132//!
133//! The SDK can be configured via environment variables or programmatically:
134//!
135//! ## Environment Variables
136//!
137//! | Variable | Required | Default | Description |
138//! |----------|----------|---------|-------------|
139//! | `RUNTARA_INSTANCE_ID` | Yes | - | Unique instance identifier |
140//! | `RUNTARA_TENANT_ID` | Yes | - | Tenant identifier |
141//! | `RUNTARA_SERVER_ADDR` | No | `127.0.0.1:8001` | Server address |
142//! | `RUNTARA_SERVER_NAME` | No | `localhost` | TLS server name |
143//! | `RUNTARA_SKIP_CERT_VERIFICATION` | No | `false` | Skip TLS verification |
144//! | `RUNTARA_CONNECT_TIMEOUT_MS` | No | `10000` | Connection timeout |
145//! | `RUNTARA_REQUEST_TIMEOUT_MS` | No | `30000` | Request timeout |
146//! | `RUNTARA_SIGNAL_POLL_INTERVAL_MS` | No | `1000` | Signal poll rate limit |
147//!
148//! ## Programmatic Configuration
149//!
150//! ```ignore
151//! use runtara_sdk::SdkConfig;
152//!
153//! let config = SdkConfig::new("my-instance", "my-tenant")
154//!     .with_server_addr("192.168.1.100:7001".parse()?)
155//!     .with_skip_cert_verification(true)
156//!     .with_signal_poll_interval_ms(500);
157//!
158//! let sdk = RuntaraSdk::new(config)?;
159//! ```
160
161mod backend;
162mod client;
163mod error;
164mod registry;
165mod types;
166
167#[cfg(feature = "quic")]
168mod config;
169#[cfg(feature = "quic")]
170mod events;
171#[cfg(feature = "quic")]
172mod signals;
173
174// Main types
175pub use client::RuntaraSdk;
176pub use error::{Result, SdkError};
177pub use types::{
178    CheckpointResult, CustomSignal, InstanceStatus, RetryConfig, RetryStrategy, Signal, SignalType,
179    StatusResponse,
180};
181
182// QUIC-specific exports
183#[cfg(feature = "quic")]
184pub use config::SdkConfig;
185
186// Global SDK registry for #[durable] macro
187pub use registry::{register_sdk, sdk, stop_heartbeat, try_sdk};
188
189// Re-export the #[durable] macro
190pub use runtara_sdk_macros::durable;
191
192// Re-export protocol client config for advanced usage
193#[cfg(feature = "quic")]
194pub use runtara_protocol::RuntaraClientConfig;
195
196// Re-export persistence trait for embedded mode
197#[cfg(feature = "embedded")]
198pub use runtara_core::persistence::Persistence;