Skip to content

Commit 6a3b72f

Browse files
authored
Merge pull request #37 from QIUZHILEI/dev
Improve documentation, code and comments
2 parents 2803988 + a1598d2 commit 6a3b72f

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

48 files changed

+1351
-1432
lines changed

.vscode/settings.json

Lines changed: 0 additions & 3 deletions
This file was deleted.

Cargo.toml

Lines changed: 17 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -9,30 +9,33 @@ readme = "README.md"
99
repository = "https://github.com/open-rust-initiative/dagrs"
1010
keywords = ["DAG", "task", "async", "parallel", "concurrent"]
1111

12+
[workspace]
13+
members = ["derive","."]
14+
15+
[dependencies]
16+
yaml-rust = "0.4.5"
17+
bimap = "0.6.1"
18+
clap = { version = "4.2.2", features = ["derive"] }
19+
anymap2 = "0.13.0"
20+
tokio = { version = "1.28", features = ["rt", "sync","rt-multi-thread"] }
21+
derive ={ path = "derive", version = "0.3.0"}
1222

1323
[dev-dependencies]
1424
log = "0.4"
1525
simplelog = "0.12"
1626

17-
[workspace]
18-
members = ["dagrs_derive","dagrs_core"]
19-
20-
21-
[dependencies]
22-
dagrs_core = {path = "dagrs_core" , version = "0.3.0"}
23-
dagrs_derive ={ path = "dagrs_derive", version = "0.3.0"}
24-
2527
[features]
26-
default = ["dagrs_core/logger"]
27-
yaml = ["dagrs_core/yaml"]
28-
derive = ["dagrs_derive/derive"]
28+
default = ["logger"]
29+
logger = []
30+
yaml = []
31+
derive = ["derive/derive"]
2932

30-
[[example]]
31-
name = "custom_log"
33+
[[bin]]
34+
name = "dagrs"
3235
required-features = ["yaml"]
3336

3437
[[example]]
35-
name = "custom_parser"
38+
name = "custom_log"
3639
required-features = ["yaml"]
3740

3841
[[example]]

README.md

Lines changed: 138 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -48,97 +48,166 @@ Among them, each task may produce output, and may also require the output of som
4848

4949
### Programmatically implement task definition
5050

51-
Users need to program to implement the `Action` trait to define the specific logic of the task, and then build a series of `DefaultTask`. The example: `examples/compute_dag.rs`. `DefaultTask` is the default implementation of the Task trait, and it has several mandatory attributes:
51+
Users need to program to implement the `Action` trait to define the specific logic of the task, and then build a series of `DefaultTask`.
5252

53-
- `id`: uniquely identifies the task assigned by the global ID assigner
54-
- `name`: the name of the task
55-
- `predecessor_tasks`: the predecessor tasks of this task
56-
- `action`: is a dynamic type that implements the Action trait in user programming, and it is the specific logic to be executed by the task
53+
First, users need to define some specific task logic. There are two ways to define task logic:
54+
55+
- Create a closure whose type is `Simple`, which is suitable for simple scenarios.
56+
- Create a type and implement the `Complex` trait, which is suitable for more complex situations. For example, if the logic of the task is to execute a system command, the command string needs to be recorded in some way. You can create a `Commad` structure with a string attribute inside to store the command string.
57+
58+
You can refer to examples:`examples/actions.rs`.
59+
60+
In the second step, you need to use the defined task logic to create specific tasks. Here you may need to use the `DefaultTask` type, which provides users with several ways to create `Task`. `DefaultTask` allows you to specify specific task logic for the task and give the task a name. Please refer to the documentation for specific function functions.
61+
62+
In the third step, you need to specify dependencies for the defined series of tasks. Here you need to use the `set_predecessors` function of `DefaultTask`. This function requires you to specify a series of predecessor tasks for the current task.
63+
64+
The fourth step is to create a `Dag` and put all the defined tasks into the `Dag` scheduler.
65+
66+
Optional step: You can specify an environment variable for `Dag`. This environment variable is available in all tasks. In some specific tasks, this behavior can be useful.
67+
68+
Finally, don’t forget to initialize the logger, and then you can call the `start` function of `Dag` to start executing all tasks.
69+
70+
You can refer to an example for the above complete steps: `examples/compute_dag.rs`
5771

5872

5973
Here is the `examples/impl_action.rs` example:
6074

6175
```rust
62-
//! Implement the Action trait to define the task logic.
76+
//! Only use Dag, execute a job. The graph is as follows:
77+
//!
78+
//! ↱----------↴
79+
//! B -→ E --→ G
80+
//! ↗ ↗ ↗
81+
//! A --→ C /
82+
//! ↘ ↘ /
83+
//! D -→ F
84+
//!
85+
//! The final execution result is 272.
86+
87+
extern crate dagrs;
6388

6489
use std::sync::Arc;
65-
use dagrs::{log, Action, Dag, DefaultTask, EnvVar, Input, LogLevel, Output, RunningError};
90+
use dagrs::{log, Complex, Dag, DefaultTask, EnvVar, Input, LogLevel, Output};
6691

67-
struct SimpleAction(usize);
92+
struct Compute(usize);
6893

69-
/// Implement the `Action` trait for `SimpleAction`, defining the logic of the `run` function.
70-
/// The logic here is simply to get the output value (usize) of all predecessor tasks and then accumulate.
71-
impl Action for SimpleAction {
72-
fn run(&self, input: Input, env: Arc<EnvVar>) -> Result<Output, RunningError> {
94+
impl Complex for Compute {
95+
fn run(&self, input: Input, env: Arc<EnvVar>) -> Output {
7396
let base = env.get::<usize>("base").unwrap();
7497
let mut sum = self.0;
7598
input
7699
.get_iter()
77100
.for_each(|i| sum += i.get::<usize>().unwrap() * base);
78-
Ok(Output::new(sum))
101+
Output::new(sum)
79102
}
80103
}
81104

82105
fn main() {
83-
// Initialize the global logger
106+
// initialization log.
84107
let _initialized = log::init_logger(LogLevel::Info, None);
85-
// Generate four tasks.
86-
let a = DefaultTask::new(SimpleAction(10), "Task a");
87-
let mut b = DefaultTask::new(SimpleAction(20), "Task b");
88-
let mut c = DefaultTask::new(SimpleAction(30), "Task c");
89-
let mut d = DefaultTask::new(SimpleAction(40), "Task d");
90-
// Set the precursor for each task.
108+
// generate some tasks.
109+
let a = DefaultTask::with_action("Compute A", Compute(1));
110+
111+
let mut b = DefaultTask::with_action("Compute B", Compute(2));
112+
113+
let mut c = DefaultTask::new("Compute C");
114+
c.set_action(Compute(4));
115+
116+
let mut d = DefaultTask::new("Compute D");
117+
d.set_action(Compute(8));
118+
119+
let mut e = DefaultTask::with_closure("Compute E", |input, env| {
120+
let base = env.get::<usize>("base").unwrap();
121+
let mut sum = 16;
122+
input
123+
.get_iter()
124+
.for_each(|i| sum += i.get::<usize>().unwrap() * base);
125+
Output::new(sum)
126+
});
127+
let mut f = DefaultTask::with_closure("Compute F", |input, env| {
128+
let base = env.get::<usize>("base").unwrap();
129+
let mut sum = 32;
130+
input
131+
.get_iter()
132+
.for_each(|i| sum += i.get::<usize>().unwrap() * base);
133+
Output::new(sum)
134+
});
135+
136+
let mut g = DefaultTask::new("Compute G");
137+
g.set_closure(|input, env| {
138+
let base = env.get::<usize>("base").unwrap();
139+
let mut sum = 64;
140+
input
141+
.get_iter()
142+
.for_each(|i| sum += i.get::<usize>().unwrap() * base);
143+
Output::new(sum)
144+
});
145+
146+
// Set up task dependencies.
91147
b.set_predecessors(&[&a]);
92148
c.set_predecessors(&[&a]);
93-
d.set_predecessors(&[&b, &c]);
94-
// Take these four tasks as a Dag.
95-
let mut dag = Dag::with_tasks(vec![a, b, c, d]);
149+
d.set_predecessors(&[&a]);
150+
e.set_predecessors(&[&b, &c]);
151+
f.set_predecessors(&[&c, &d]);
152+
g.set_predecessors(&[&b, &e, &f]);
153+
// Create a new Dag.
154+
let mut dag = Dag::with_tasks(vec![a, b, c, d, e, f, g]);
96155
// Set a global environment variable for this dag.
97156
let mut env = EnvVar::new();
98157
env.set("base", 2usize);
99158
dag.set_env(env);
100-
// Begin execution.
159+
// Start executing this dag
101160
assert!(dag.start().unwrap());
102-
// Get execution result
103-
assert_eq!(dag.get_result::<usize>().unwrap(), 220);
161+
// Get execution result.
162+
let res = dag.get_result::<usize>().unwrap();
163+
println!("The result is {}.", res);
104164
}
105165
```
106166

107167
**explain:**
108168

109-
First, we define `SimpleAction` and implement the `Action` trait for this structure. In the rewritten run function, we simply get the output value of the predecessor task and multiply it by the environment variable `base`. Then accumulate the multiplied result to itself self.0.
110-
111-
After defining the specific task logic, start creating the prerequisites for executing `Dag`:
112-
Initialize the global logger first. Here we set the log level to Info, and do not give the log output file, let the log output to the console by default.
169+
First, we initialize the logger, declare the `Compute` type, and implement the `Complex` trait for it. In the rewritten run function, we simply get the output value of the predecessor task and multiply it by the environment variable `base`. Then accumulate the multiplied result to itself self.0.
113170

114-
Create a `DefaultTask` with `SimpleAction` and give the task a name. Then set the dependencies between tasks.
171+
Next, we define 6 tasks and show the usage of some functions in the `DefaultTask` type. Set predecessor tasks for each task.
115172

116-
Then create a Dag and assign it a global environment variable.
173+
Then, create a `Dag`, set a base environment variable for it, and use the start method to start executing all tasks.
117174

118175
Finally we call the `start` function of `Dag` to execute all tasks. After the task is executed, call the `get_result` function to obtain the final execution result of the task.
119176

120177
The graph formed by the task is shown below:
121178

122179
```mermaid
123-
flowchart LR;
124-
A((Task a))-->B; A-->C; B((Task b))-->D; C((Task c))-->D((Task d));
180+
flowchart LR
181+
A-->B
182+
A-->C
183+
B-->D
184+
B-->F
185+
C-->D
186+
C-->E
187+
D-->F
188+
E-->F
125189
```
126190

127191
The execution order is a->c->b->d.
128192

129193
```bash
130-
$cargo run
131-
[Start] -> Task a -> Task c -> Task b -> Task d -> [End]
132-
Executing Task[name: Task a]
133-
Task executed successfully. [name: Task a]
134-
Executing Task[name: Task b]
135-
Executing Task[name: Task c]
136-
Task executed successfully. [name: Task b]
137-
Task executed successfully. [name: Task c]
138-
Executing Task[name: Task d]
139-
Task executed successfully. [name: Task d]
140-
141-
Process finished with exit code 0
194+
$ cargo run --example compute_dag
195+
[Start] -> Compute A -> Compute B -> Compute D -> Compute C -> Compute F -> Compute E -> Compute G -> [End]
196+
Executing task [name: Compute A, id: 1]
197+
Execution succeed [name: Compute A, id: 1]
198+
Executing task [name: Compute C, id: 3]
199+
Executing task [name: Compute B, id: 2]
200+
Executing task [name: Compute D, id: 4]
201+
Execution succeed [name: Compute C, id: 3]
202+
Execution succeed [name: Compute B, id: 2]
203+
Execution succeed [name: Compute D, id: 4]
204+
Executing task [name: Compute F, id: 6]
205+
Executing task [name: Compute E, id: 5]
206+
Execution succeed [name: Compute F, id: 6]
207+
Execution succeed [name: Compute E, id: 5]
208+
Executing task [name: Compute G, id: 7]
209+
Execution succeed [name: Compute G, id: 7]
210+
The result is 272.
142211
```
143212

144213
### `Yaml` configuration file
@@ -192,8 +261,8 @@ These yaml-defined task items form a complex dependency graph. In the yaml confi
192261
To parse the yaml configured file, you need to compile this project, requiring rust version >= 1.70:
193262

194263
```bash
195-
$cargo build --release
196-
$ .\target\release\dagrs.exe --help
264+
$ cargo build --release --features=yaml
265+
$ ./target/release/dagrs.exe --help
197266
Usage: dagrs.exe [OPTIONS] --yaml <YAML>
198267
199268
Options:
@@ -213,7 +282,7 @@ Options:
213282
We can try an already defined file at `tests/config/correct.yaml`
214283

215284
```bash
216-
$./target/release/dagrs --yaml=./tests/config/correct.yaml --log-path=./dagrs.log --log-level=info
285+
$ ./target/release/dagrs --yaml=./tests/config/correct.yaml --log-path=./dagrs.log --log-level=info
217286
[Start] -> Task 8 -> Task 5 -> Task 7 -> Task 6 -> Task 3 -> Task 2 -> Task 1 -> Task 4 -> [End]
218287
Executing Task[name: Task 8]
219288
Executing Task[name: Task 5]
@@ -227,10 +296,19 @@ Executing Task[name: Task 1]
227296

228297
You can see an example: `examples/yaml_dag.rs`. In fact, you can also programmatically read the yaml configuration file generation task, which is very simple, just use the `with_yaml` function provided by `Dag` to parse the configuration file.
229298

299+
--------------------------------------
300+
230301
**In addition to these two methods, `dagrs` also supports advanced task custom configuration.**
231302

232-
- `DefaultTask` is a default implementation of the `Task` trait. Users can also customize tasks and add more functions and attributes to tasks, but they still need to have the four necessary attributes in `DefaultTask`. `YamlTask` is another example of `Task` concrete implementation, its source code is available for reference, or refer to `example/custom_task.rs`.
233-
- In addition to yaml-type configuration files, users can also provide other types of configuration files, but in order to allow other types of configuration files to be parsed as tasks, users need to implement the `Parser` trait. `YamlParser` source code is available for reference, or refer to `examples/custom_parser.rs`
303+
- `DefaultTask` is a default implementation of the `Task` trait. Users can also customize tasks and add more functions and attributes to tasks, but they still need to have the four necessary attributes in `DefaultTask`. `YamlTask` is another example of `Task` concrete implementation, its source code is available for reference. No matter how you customize the task type, the customized task type must have the following attributes:
304+
- `id`: uniquely identifies the task assigned by the global ID assigner
305+
- `name`: the name of the task
306+
- `predecessor_tasks`: the predecessor tasks of this task
307+
- `action`: is a dynamic type that implements the Action trait in user programming, and it is the specific logic to be executed by the task
308+
309+
- In addition to yaml-type configuration files, users can also provide other types of configuration files, but in order to allow other types of configuration files to be parsed as tasks, users need to implement the `Parser` trait. `YamlParser` source code is available for reference.
310+
311+
`examples/custom_parser_and_task.rs` is an example of a custom task type and a custom configuration file parser
234312

235313
## Analyze the logic of task execution
236314

@@ -323,27 +401,27 @@ gantt
323401

324402
### Basic function usage
325403

326-
`examples/compute_dag.rs`: Use a custom macro to generate multiple simple tasks.
404+
`examples/compute_dag.rs`: A complete usage example of dagrs.
327405

328-
`examples/impl_action.rs`: Define a simple Action to build multiple tasks with the same logic.
406+
`examples/action.rs`: Two ways to define the specific logic of a task.
329407

330-
`examples/yaml_dag.rs`: Spawn multiple tasks with a given yaml configuration file。
331-
332-
`examples/use_macro.rs`: Use the `gen_task` macro provided by `dagrs` to generate multiple simple tasks。
408+
`examples/yaml_dag.rs`: Example of reading yaml configuration file (needs to enable `yaml` features).
333409

334410
`examples/engine.rs`: Using `Engine` to manage multiple dags with different task types.
335411

336412
### Advanced Features
337413

338-
`examples/custom_task.rs`: Implement the `Task` trait and define your own task type.
339-
340-
`examples/custom_parser.rs`: Implement the `Parser` trait to define your own task configuration file parser。
414+
`examples/custom_parser_and_task.rs`: Custom task types and configuration file parsers.
341415

342416
`examples/custom_log.rs`: Implement the `Logger` trait to define your own global logger.
343417

418+
`examples/derive_task.rs`:Use `CustomTask` derived macros to help customize task types.
419+
420+
`examples/dependencies.rs`:Use the `dependencies!` macro to specify dependencies in an intuitive way and define a series of tasks.
421+
344422
## Contribution
345423

346-
The dagrs project relies on community contributions and aims to simplify getting started. To develop `dagrs`, clone the repository, then install all dependencies, run the test suite and try it out locally. Pick an issue, make changes, and submit a pull request for community review.
424+
The `dagrs` project relies on community contributions and aims to simplify getting started. To develop `dagrs`, clone the repository, then install all dependencies, run the test suite and try it out locally. Pick an issue, make changes, and submit a pull request for community review.
347425

348426
### What's the contribution
349427

dagrs_core/Cargo.toml

Lines changed: 0 additions & 25 deletions
This file was deleted.

0 commit comments

Comments
 (0)