{"id":13648617,"url":"https://github.com/tokio-rs/simulation","last_synced_at":"2025-04-22T11:32:49.037Z","repository":{"id":51969940,"uuid":"214657043","full_name":"tokio-rs/simulation","owner":"tokio-rs","description":"Framework for simulating distributed applications","archived":true,"fork":false,"pushed_at":"2020-03-09T01:12:49.000Z","size":331,"stargazers_count":100,"open_issues_count":9,"forks_count":9,"subscribers_count":18,"default_branch":"master","last_synced_at":"2025-04-11T01:17:25.213Z","etag":null,"topics":["async","deterministic","simulation","tokio"],"latest_commit_sha":null,"homepage":"","language":"Rust","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/tokio-rs.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2019-10-12T14:03:09.000Z","updated_at":"2025-04-05T11:29:00.000Z","dependencies_parsed_at":"2022-09-02T14:01:29.013Z","dependency_job_id":null,"html_url":"https://github.com/tokio-rs/simulation","commit_stats":null,"previous_names":["gardnervickers/simulation"],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tokio-rs%2Fsimulation","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tokio-rs%2Fsimulation/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tokio-rs%2Fsimulation/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tokio-rs%2Fsimulation/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/tokio-rs","download_url":"https://codeload.github.com/tokio-rs/simulation/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248703943,"owners_count":21148252,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["async","deterministic","simulation","tokio"],"created_at":"2024-08-02T01:04:23.719Z","updated_at":"2025-04-22T11:32:48.977Z","avatar_url":"https://github.com/tokio-rs.png","language":"Rust","funding_links":[],"categories":["Rust"],"sub_categories":[],"readme":"# Note\nThe Simulation library is being refactored to integrate more directly with Tokio. Currently, Simulation is not compatible with Tokio 0.2.x. As a result, it's recommended that users wait for a future release of Simulation. The issue tracking Tokio integration progress can be found here https://github.com/tokio-rs/tokio/issues/1845. \n\n# simulation\n\nThe goal of Simulation is to provide a set of low level components which can be\nused to write applications amenable to [FoundationDB style simulation testing](https://apple.github.io/foundationdb/testing.html).\n\nSimulation is an abstraction over [Tokio], allowing application developers to write\napplications which are generic over sources of nondeterminism. Additionally, Simulation\nprovides deterministic analogues to time, scheduling, network and eventually disk IO.\n\n## Scheduling and Time\n\nSimulation provides a mock source of time. Mock time will only advance when the executor\nhas no more work to do. This can be used to force deterministic reordering of task execution.\n\nWhen time is advanced, it is advanced instantly to a value which allows the executor to make\nprogress. Applications which rely on timeouts can then be tested in a fraction of the time it\nwould normally take to test a particular execution ordering.\n\nThis can be used to naturally express ordering between tasks\n\n```rust\n   use simulation::{Environment};\n   #[test]\n   fn ordering() {\n       let mut runtime = DeterministicRuntime::new().unwrap();\n       let handle = runtime.localhost_handle();\n       runtime.block_on(async {\n           let delay1 = handle.delay_from(Duration::from_secs(10));\n           let delay2 = handle.delay_from(Duration::from_secs(30));\n\n           let handle1 = handle.clone();\n           let completed_at1 = crate::spawn_with_result(\u0026handle1.clone(), async move {\n               delay1.await;\n               handle1.now()\n           })\n           .await;\n\n           let handle2 = handle.clone();\n           let completed_at2 = crate::spawn_with_result(\u0026handle2.clone(), async move {\n               delay2.await;\n               handle2.now()\n           })\n           .await;\n           assert!(completed_at1 \u003c completed_at2)\n       });\n   }\n```\n\n## Network\n\nSimulation includes an in-memory network. Applications can use `Environment::bind` and `Environment::connect`\nto create in-memory connections between components. The in-memory connections will automatically have delays\nand disconnect faults injected, dependent on an initial seed value.\n\n[`DeterministicRuntime`] supports both a [`DeterministicRuntime::localhost_handle`] as well as creating a handle\nscoped to a particular [`std::net:IpAddr`] with [`DeterministicRuntime::handle`].\n\n## Faults\n\nFaults are injected based on a seedable RNG, causing IO delays and disconnects.\nThis is sufficient to trigger bugs in higher level components, such as message reordering.\n\nBy eliminating sources of nondeterminism, and basing fault injection on a seedable RNG, it's\npossible to run many thousands of tests in the span of a few seconds with different fault\ninjections. This allows testing different execution orderings. If a particular seed causes a\nfailing execution ordering, developers can use the seed value to debug and fix their applications.\n\nOnce the error is fixed, the seed value can be used to setup a regression test to ensure that the\nissue stays fixed.\n\nFault injection is handled by spawned tasks. Currently there is one fault injector which will inject\ndeterminstic latency changes to socket read/write sides based on the initial seed value passed to\n[`DeterministicRuntime::new_with_seed`]. Launching the fault injector involves spawning it at startup.\n\n## Example\nThe following example demonstrates a simple client server app which has latency faults injected.\n\n```rust\n   use simulation::{Environment, TcpListener};\n   use futures::{SinkExt, StreamExt};\n   use std::{io, net, time};\n   use tokio::codec::{Framed, LinesCodec};\n\n   /// Start a client request handler which will write greetings to clients.\n   async fn handle\u003cE\u003e(env: E, socket: \u003cE::TcpListener as TcpListener\u003e::Stream, addr: net::SocketAddr)\n   where\n       E: Environment,\n   {\n       // delay the response, in deterministic mode this will immediately progress time.\n       env.delay_from(time::Duration::from_secs(1));\n       println!(\"handling connection from {:?}\", addr);\n       let mut transport = Framed::new(socket, LinesCodec::new());\n       if let Err(e) = transport.send(String::from(\"Hello World!\")).await {\n           println!(\"failed to send response: {:?}\", e);\n       }\n   }\n\n   /// Start a server which will bind to the provided addr and repyl to clients.\n   async fn server\u003cE\u003e(env: E, addr: net::SocketAddr) -\u003e Result\u003c(), io::Error\u003e\n   where\n       E: Environment,\n   {\n       let mut listener = env.bind(addr).await?;\n\n       while let Ok((socket, addr)) = listener.accept().await {\n           let request = handle(env.clone(), socket, addr);\n           env.spawn(request)\n       }\n       Ok(())\n   }\n\n\n   /// Create a client which will read a message from the server\n   async fn client\u003cE\u003e(env: E, addr: net::SocketAddr) -\u003e Result\u003c(), io::Error\u003e\n   where\n       E: Environment,\n   {\n       loop {\n           match env.connect(addr).await {\n               Err(_) =\u003e {\n                   // Sleep if the connection was rejected, retrying later.\n                   // In deterministic mode, this will just reorder task execution\n                   // without waiting for time to advance.\n                   env.delay_from(time::Duration::from_secs(1)).await;\n                   continue;\n               }\n               Ok(conn) =\u003e {\n                   let mut transport = Framed::new(conn, LinesCodec::new());\n                   let result = transport.next().await.unwrap().unwrap();\n                   assert_eq!(result, \"Hello World!\");\n                   return Ok(());\n               }\n           }\n       }\n   }\n   #[test]\n   fn test() {\n       // Various seed values can be supplied to `DeterministicRuntime::new_with_seed` to find a seed\n       // value for which this example terminates incorrectly.\n       let mut runtime = simulation::deterministic::DeterministicRuntime::new_with_seed(1).unwrap();\n       let handle = runtime.handle();\n       runtime.block_on(async {\n           handle.spawn(runtime.latency_fault().run());\n           let bind_addr: net::SocketAddr = \"127.0.0.1:8080\".parse().unwrap();\n           let server = server(handle.clone(), bind_addr);\n           handle.spawn(async move {\n               server.await.unwrap();\n           });\n           client(handle, bind_addr).await.unwrap();\n       })\n   }\n```\n\n[Tokio]: https://github.com/tokio-rs\n[CurrentThread]:[tokio_executor::current_thread::CurrentThread]\n[Delay]:[tokio_timer::Delay]\n[Timeout]:[tokio_timer::Timeout]\n\nLicense: MIT\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftokio-rs%2Fsimulation","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ftokio-rs%2Fsimulation","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftokio-rs%2Fsimulation/lists"}