✨ iterated fn + defer + retry
This commit is contained in:
parent
c010cc13e8
commit
18c663fcdb
11 changed files with 678 additions and 65 deletions
120
README.md
120
README.md
|
@ -11,37 +11,47 @@
|
|||
|
||||
## Core Concepts
|
||||
|
||||
### Parallel Execution
|
||||
### Higher Level Functions
|
||||
|
||||
`comrade` provides a simple interface for running tasks in parallel, perfect for independent tasks that can be processed concurrently.
|
||||
`comrade` provides various convenient functions.
|
||||
|
||||
```rust
|
||||
// process every item in parallel
|
||||
let results: Vec<i32> = parallel(items, |item: &i32| {
|
||||
// ...
|
||||
});
|
||||
```
|
||||
|
||||
### Rally Execution
|
||||
|
||||
The `rally` function allows you to run multiple tasks in parallel and return the result of the **first task to finish**. This is useful when you want to prioritize the first available result from several tasks (example: download from multiple HTTP mirrors).
|
||||
|
||||
```rust
|
||||
// rally (return fastest computed result out of items)
|
||||
// example: run multiple downloads and return the first finished one
|
||||
let res: (i32, i32) = rally(items, |item: &i32| {
|
||||
// ...
|
||||
});
|
||||
```
|
||||
|
||||
### Background Tasks
|
||||
// Run background tasks without blocking the main thread
|
||||
background(|| {
|
||||
// Background task logic
|
||||
println!("This is a background task!");
|
||||
});
|
||||
|
||||
Easily run tasks in the background without blocking the main thread. This is useful for code that needs to be run without waiting for a result.
|
||||
fn some_fn() {
|
||||
println!("Hello World!");
|
||||
|
||||
```rust
|
||||
fn handle() {
|
||||
background(|| {
|
||||
// Background task logic
|
||||
println!("This is a background task!");
|
||||
defer!(|| {
|
||||
// this will run at the end of the scope
|
||||
println!("Bye World!");
|
||||
});
|
||||
|
||||
println!("doing something");
|
||||
}
|
||||
|
||||
// Retry a `Fn() -> Option<X>` until it returns `Some(_)`.
|
||||
let value: &str = retry(|| {
|
||||
if rand::rng().random_range(0..10) > 5 {
|
||||
Some("hello")
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
```
|
||||
|
||||
### Service Management
|
||||
|
@ -113,6 +123,21 @@ fn main() {
|
|||
}
|
||||
```
|
||||
|
||||
You could effeciently run batch work:
|
||||
```rust
|
||||
fn batch_work() {
|
||||
let mut work = Vec::new();
|
||||
|
||||
for i in 0..10 {
|
||||
work.push((i.to_string(), multiply_async(i, i)));
|
||||
}
|
||||
|
||||
for (label, res) in LabelPendingTaskIterator(work) {
|
||||
println!("Finished task {label} -> {res}");
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
These tasks can now be distributed with Valkey.
|
||||
|
||||
Make sure you have a Valkey server running and the `$VALKEY_URL` environment variable is set for your application:
|
||||
|
@ -155,3 +180,66 @@ fn main() {
|
|||
println!("x is {x}");
|
||||
}
|
||||
```
|
||||
|
||||
### Stateful Functions
|
||||
If you have a workload which can be iteratively computed by modifying state it can be modeled as a `IteratedFunction`.
|
||||
These functions can be paused, stopped, saved to disk and revived later.
|
||||
|
||||
First define a iterative function:
|
||||
```rust
|
||||
#[derive(Debug, Default, Clone)]
|
||||
pub struct AccFnContext {
|
||||
pub iter: u64,
|
||||
pub acc: u64,
|
||||
}
|
||||
|
||||
pub fn multiply_iterated(
|
||||
mut ctx: FunctionContext<AccFnContext, (u64, u64), u64>,
|
||||
) -> FunctionContext<AccFnContext, (u64, u64), u64> {
|
||||
// init
|
||||
let (a, b) = ctx.initial;
|
||||
|
||||
// end condition (return)
|
||||
if b == ctx.context.iter {
|
||||
ctx.done(ctx.context.acc);
|
||||
return ctx;
|
||||
}
|
||||
|
||||
// computation
|
||||
println!("doing calc {}", ctx.context.acc);
|
||||
std::thread::sleep(Duration::from_millis(50));
|
||||
let val = ctx.context.acc + a;
|
||||
|
||||
// saving state
|
||||
ctx.state(|x| {
|
||||
x.iter += 1;
|
||||
x.acc = val;
|
||||
});
|
||||
|
||||
return ctx;
|
||||
}
|
||||
```
|
||||
|
||||
Then you can use it like:
|
||||
```rust
|
||||
fn main() {
|
||||
// run normally
|
||||
let f = IteratedFunction::new_threaded(multiply_iterated, (5, 5));
|
||||
println!("Result is {}", f.output());
|
||||
|
||||
// function starts running
|
||||
let f = IteratedFunction::new_threaded(multiply_iterated, (5, 50));
|
||||
|
||||
// pause the function
|
||||
f.pause();
|
||||
|
||||
// stop the function and get state
|
||||
let state = f.stop();
|
||||
|
||||
// revive and start running again from state
|
||||
let f = IteratedFunction::new_threaded_from_state(multiply_iterated, state);
|
||||
|
||||
// get output
|
||||
println!("Result is {}", f.output());
|
||||
}
|
||||
```
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue