Skip to content

Commit

Permalink
Implement single-binary mode
Browse files Browse the repository at this point in the history
Running in a distributed mode is great for bigger tests, but sometimes
you just want to test something locally. This commit adds a `run`
subcommand that can run a wasm scenario directly:

    crows run foo.wasm
  • Loading branch information
drogus committed Mar 23, 2024
1 parent f63376a commit 031ad9c
Show file tree
Hide file tree
Showing 16 changed files with 608 additions and 445 deletions.
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

31 changes: 31 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,32 @@
Crows is a distributed load and stress testing runner. The tests can be written in any language that can compile to WASM given the bindings for the library are available. At the moment the bindings are available only for Rust, but once the ABIs are stable it should be relatively straightforward to add more languages.

### Showcase

A sample scenario written in Rust looks like this:

```
#[config]
fn config() -> ExecutorConfig {
let config = ConstantArrivalRateConfig {
duration: Duration::from_secs(5),
rate: 10,
allocated_vus: 10,
..Default::default()
};
ExecutorConfig::ConstantArrivalRate(config)
}
#[export_name = "scenario"]
pub fn scenario() {
http_request(
"https://google.com".into(), GET, HashMap::new(), "".into(),
);
}
```

It will send 10 requests per second to google.com. For information on how to compile and run it please go to the [Usage section](#usage)

### State of the project

This project is at a "working proof of concept" stage. It has solid foundation as I put a lot of thought into figuring out the best way to implement it, but there are a lot of details that are either missing or are knowingly implemented in a not optimal/best way. I've started thinking about the project about 3 years ago and I've started the current iteration about 1.5 years ago and I decided I have to finish it as soon as possible or it will end up where most of my personal projects - aiming for perfection, half finished and never released.
Expand Down Expand Up @@ -38,6 +64,10 @@ Crows is not production ready, so it misses some of the features, but my persona
* plugin system (probably based on WASM) to allow extending the tool without necessarily extending the source
* a web interface
* a nice TUI (Terminal UI)

### Installing

...

### Usage

Expand Down Expand Up @@ -153,6 +183,7 @@ I don't have a very precise plan on where I want Crows to go, but some loose tho
8. For simplicity I use Reqwest HTTP library on the host, which has an internal pool of clients. I haven't given it too much thought yet, but I think that I would prefer to have a greater control over how HTTP connections are handled. For example I think it makes most sense if a scenario uses dedicated connections and is generally not reused between scenarios. It could be nice to allow sharing a connection between scenarios for performance, which is something for example `wrk` does by default between requests, but it should be configurable and in general we should have more granular control on how it works
9. Look closer into serialization formats, both for the communication between components and communication between WASM modules and the host. I would ideally want something with ability do define a schema and make backwards compatible changes to the schema.
10. More performant memory management. For example at the moment sending an HTTP request means serializing the entire request in the WASM module and then deserializing it on the host side. If a stress test requires sending big files it's generally a waste of CPU cycles as we could quite easily serialize only the headers and url, write body to the WASM memory directly and then send the body straight from the memory rather than copy it anywhere. Another example is that most of the time RPC services and clients could use references.
11. At the moment there is only one stage possible to be executed - running the scenario itself. I want to also add some kind of a "prepare" step which would run before the scenario itself and which would prepare the data, but I need to think a bit more on how exactly it should work.

### License

Expand Down
5 changes: 3 additions & 2 deletions cli/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,6 @@ edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[dependencies]
crows-utils = { path = "../utils" }
crows-service = { path = "../service" }
clap = { version = "4.4.5", features = ["derive"] }
crossterm = "0.27"

Expand All @@ -19,3 +17,6 @@ tokio-util.workspace = true
serde.workspace = true
uuid.workspace = true
anyhow.workspace = true
crows-utils.workspace = true
crows-service.workspace = true
crows-wasm.workspace = true
2 changes: 2 additions & 0 deletions cli/src/commands/mod.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
mod start;
mod run;

pub use start::start;
pub use run::run;
28 changes: 28 additions & 0 deletions cli/src/commands/run.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
use std::path::PathBuf;

use crows_cli::output::{drive_progress, LocalProgressFetcher};
use crows_utils::services::RunId;
use crows_wasm::{fetch_config, run_scenario};

pub async fn run(path: &PathBuf) -> anyhow::Result<()> {
let scenario = std::fs::read(path).unwrap();
let (runtime, info_handle) =
crows_wasm::Runtime::new(&scenario).expect("Could not create a runtime");
let (instance, _, mut store) = runtime
.new_instance()
.await
.expect("Could not create an instance");
let config = fetch_config(instance, &mut store)
.await
.expect("Config not found in the module");

run_scenario(runtime, scenario, config).await;

let mut client = LocalProgressFetcher::new(info_handle, "worker".to_string());

drive_progress(&mut client, &RunId::new(), vec!["worker".to_string()])
.await
.expect("Error while running the scenario");

Ok(())
}
Loading

0 comments on commit 031ad9c

Please sign in to comment.