Skip to content

Commit 46db1ac

Browse files
committed
RetryMode
1 parent 56980e3 commit 46db1ac

8 files changed

Lines changed: 595 additions & 103 deletions

File tree

Cargo.lock

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[package]
22
name = "cano"
3-
version = "0.1.3"
3+
version = "0.1.4"
44
edition = "2024"
55
description = "Simple & Fast Async Workflows in Rust - Build powerful data processing pipelines with minimal code"
66
license = "MIT"
@@ -13,12 +13,12 @@ authors = ["Nassor Frazier-Silva"]
1313
[dependencies]
1414
async-trait = "0.1"
1515
tokio = { version = "1.0", features = ["full"] }
16+
rand = "0.9"
1617

1718
[dev-dependencies]
1819
cargo-audit = "0.21.2"
1920
criterion = { version = "0.6", features = ["html_reports", "async_tokio"] }
2021
futures = "0.3"
21-
rand = "0.9"
2222
reqwest = { version = "0.12", features = ["json"] }
2323

2424
[[example]]

README.md

Lines changed: 62 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -87,8 +87,8 @@ That's it! You just ran your first Cano workflow.
8787

8888
- **🏗️ Simple API**: A single `Node` trait handles everything - no complex type hierarchies
8989
- **🔗 Simple Configuration**: Fluent builder pattern makes setup intuitive
90-
- **🔄 Built-in Retries**: Configurable retry logic for resilient workflows
91-
- **💾 Shared store**: Thread-safe key-value store for data passing between nodes
90+
- **🔄 Smart Retries**: Multiple retry strategies (none, fixed, exponential backoff) with jitter support
91+
- **💾 Shared Store**: Thread-safe key-value store for data passing between nodes
9292
- **🌊 Complex Workflows**: Chain nodes together into sophisticated state machine pipelines
9393
- **⚡ Type Safety**: Enum-driven state transitions with compile-time safety
9494
- **🚀 High Performance**: Minimal overhead with direct execution for maximum throughput
@@ -178,16 +178,71 @@ let result = flow.orchestrate(&store).await?;
178178

179179
### Built-in Retry Logic
180180

181-
Every node includes configurable retry logic:
181+
Every node includes configurable retry logic with multiple strategies:
182+
183+
#### Retry Modes
184+
185+
**No Retries** - Fail fast for critical operations:
182186

183187
```rust
184-
let config = NodeConfig::new()
185-
.with_retries(5, Duration::from_secs(1)); // 5 retries, 1s wait between attempts
188+
impl Node<WorkflowState> for CriticalNode {
189+
fn config(&self) -> NodeConfig {
190+
NodeConfig::minimal() // No retries, fail immediately
191+
}
192+
// ... rest of implementation
193+
}
194+
```
195+
196+
**Fixed Retries** - Consistent delays between attempts:
186197

187-
// Or use minimal retries for fast-failing nodes
188-
let config = NodeConfig::minimal(); // 1 retry, no wait
198+
```rust
199+
impl Node<WorkflowState> for ReliableNode {
200+
fn config(&self) -> NodeConfig {
201+
NodeConfig::new().with_fixed_retry(3, Duration::from_secs(2))
202+
// 3 retries with 2 second delays
203+
}
204+
// ... rest of implementation
205+
}
189206
```
190207

208+
**Exponential Backoff** - Smart retry with increasing delays:
209+
210+
```rust
211+
impl Node<WorkflowState> for ResilientNode {
212+
fn config(&self) -> NodeConfig {
213+
NodeConfig::new().with_exponential_retry(5)
214+
// 5 retries with exponential backoff (100ms, 200ms, 400ms, 800ms, 1.6s)
215+
}
216+
// ... rest of implementation
217+
}
218+
```
219+
220+
**Custom Exponential Backoff** - Full control over retry behavior:
221+
222+
```rust
223+
impl Node<WorkflowState> for CustomNode {
224+
fn config(&self) -> NodeConfig {
225+
NodeConfig::new().with_retry(
226+
RetryMode::exponential_custom(
227+
3, // max retries
228+
Duration::from_millis(50), // base delay
229+
3.0, // multiplier
230+
Duration::from_secs(10), // max delay cap
231+
0.2, // 20% jitter
232+
)
233+
)
234+
}
235+
// ... rest of implementation
236+
}
237+
```
238+
239+
#### Why Use Different Retry Modes?
240+
241+
- **None**: Database transactions, critical validations where failure should be immediate
242+
- **Fixed**: Network calls, file operations where consistent timing is preferred
243+
- **Exponential**: API calls, external services where you want to back off gracefully
244+
- **Custom Exponential**: High-load scenarios where you need precise control over timing and jitter
245+
191246
### Complex Workflows
192247

193248
Chain multiple nodes together to build sophisticated state machine pipelines:

examples/book_prepositions.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -271,7 +271,7 @@ impl Node<BookPrepositionAction> for BookDownloaderNode {
271271
}
272272

273273
fn config(&self) -> NodeConfig {
274-
NodeConfig::new().with_retries(2, Duration::from_secs(1))
274+
NodeConfig::new().with_fixed_retry(2, Duration::from_secs(1))
275275
}
276276

277277
/// Preparation: Get the list of books to download

src/flow.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -234,7 +234,7 @@ where
234234
}
235235
}
236236

237-
/// Register a node for a specific state (accepts any type implementing Node<T>)
237+
/// Register a node for a specific state (accepts any type implementing `Node<T>`)
238238
pub fn register_node<N>(&mut self, state: T, node: N) -> &mut Self
239239
where
240240
N: Node<T, crate::node::DefaultParams, S> + Send + Sync + 'static,
@@ -339,7 +339,7 @@ where
339339
}
340340
}
341341

342-
/// Register a node for a state (accepts any type implementing Node<T>)
342+
/// Register a node for a state (accepts any type implementing `Node<T>`)
343343
pub fn register_node<N>(mut self, state: T, node: N) -> Self
344344
where
345345
N: Node<T, crate::node::DefaultParams, S> + Send + Sync + 'static,

src/lib.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,7 @@ pub mod store;
7979
// Core public API - simplified imports
8080
pub use error::{CanoError, CanoResult};
8181
pub use flow::{Flow, FlowBuilder};
82-
pub use node::{DefaultNodeResult, DefaultParams, DynNode, Node, NodeConfig};
82+
pub use node::{DefaultNodeResult, DefaultParams, DynNode, Node, NodeConfig, RetryMode};
8383
pub use store::{MemoryStore, Store};
8484

8585
// Convenience re-exports for common patterns
@@ -90,7 +90,7 @@ pub mod prelude {
9090
9191
pub use crate::{
9292
CanoError, CanoResult, DefaultNodeResult, DefaultParams, Flow, FlowBuilder, MemoryStore,
93-
Node, NodeConfig, Store,
93+
Node, NodeConfig, RetryMode, Store,
9494
};
9595

9696
// Re-export async_trait for convenience

0 commit comments

Comments
 (0)