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;