{"id":48284148,"url":"https://github.com/pingidentity/opendst","last_synced_at":"2026-04-04T22:55:00.257Z","repository":{"id":334022194,"uuid":"1138814715","full_name":"pingidentity/opendst","owner":"pingidentity","description":"OpenDST - Deterministic Simulation Testing for Java","archived":false,"fork":false,"pushed_at":"2026-03-22T09:51:03.000Z","size":401,"stargazers_count":3,"open_issues_count":0,"forks_count":2,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-03-23T00:47:23.010Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"","language":"Java","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/pingidentity.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-01-21T06:36:52.000Z","updated_at":"2026-03-22T09:51:10.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/pingidentity/opendst","commit_stats":null,"previous_names":["pingidentity/opendst"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/pingidentity/opendst","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/pingidentity%2Fopendst","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/pingidentity%2Fopendst/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/pingidentity%2Fopendst/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/pingidentity%2Fopendst/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/pingidentity","download_url":"https://codeload.github.com/pingidentity/opendst/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/pingidentity%2Fopendst/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":31418272,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-04-04T20:09:54.854Z","status":"ssl_error","status_checked_at":"2026-04-04T20:09:44.350Z","response_time":60,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"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":[],"created_at":"2026-04-04T22:54:59.079Z","updated_at":"2026-04-04T22:55:00.244Z","avatar_url":"https://github.com/pingidentity.png","language":"Java","funding_links":[],"categories":[],"sub_categories":[],"readme":"\u003c!--\n ! Copyright 2024-2026 Ping Identity Corporation\n !\n ! Licensed under the Apache License, Version 2.0 (the \"License\");\n ! you may not use this file except in compliance with the License.\n ! You may obtain a copy of the License at\n !\n !    http://www.apache.org/licenses/LICENSE-2.0\n !\n ! Unless required by applicable law or agreed to in writing, software\n ! distributed under the License is distributed on an \"AS IS\" BASIS,\n ! WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n ! See the License for the specific language governing permissions and\n ! limitations under the License.\n --\u003e\n# OpenDST: Deterministic Simulation Testing\n\nOpenDST is a Java library designed to enable **Deterministic Simulation Testing (DST)** for distributed systems. It allows developers to test complex, concurrent, and distributed logic in a completely deterministic environment, making flaky tests a thing of the past.\n\nBy intercepting non-deterministic operations (like time, threading, and randomness) and replacing them with a controlled simulation, OpenDST ensures that every test run produces the exact same result for a given seed.\n\n## Key Features\n\n*   **Deterministic Execution:** Eliminates flaky tests by controlling all sources of non-determinism.\n*   **Virtual Time:** Simulates time progression instantly. `Thread.sleep(Duration.ofHours(1))` completes in milliseconds.\n*   **Deterministic Scheduling:** Uses a custom scheduler (leveraging Java Virtual Threads) to control thread execution order.\n*   **Controlled Randomness:** Provides a deterministic source of randomness that can be seeded for reproducibility.\n*   **Bytecode Instrumentation:** Automatically intercepts JDK calls (e.g., `System.currentTimeMillis()`, `new Thread()`, `SecureRandom`) using a Java Agent, so you don't need to change your production code.\n\n## How It Works\n\nOpenDST uses two instrumentation strategies. Application code is instrumented **offline** by the Maven plugin using the JDK 25 ClassFile API (JEP 484), which rewrites SDK stub call-sites at build time. JDK internals are handled at **runtime** by a lightweight Java Agent (`SimulatorAgent`) that intercepts non-deterministic APIs and redirects them to the Simulator.\n\nWhen running inside a simulation:\n*   **Time:** `System.currentTimeMillis()` and `System.nanoTime()` return a simulated time that advances only when the simulator decides.\n*   **Threads:** `new Thread()` and `startVirtualThread()` are intercepted to run as virtual threads managed by the simulator's scheduler.\n*   **Randomness:** `ThreadLocalRandom`, `SecureRandom`, and `Random` are seeded deterministically.\n*   **Network:** Network interactions are simulated with a virtual TCP stack, with configurable latency, connection clogging, connection resets, and timeouts. See [Fault Injection](#fault-injection) for the full list.\n\n## Determinism \u0026 Isolation\n\nOpenDST aims for strict determinism, but some JDK APIs are not yet isolated or fully instrumented. For an exhaustive list of known gaps (including File I/O and certain system properties), see [KNOWN_GAPS.md](KNOWN_GAPS.md).\n\n## Fault Injection\n\nWhen network fault injection is enabled (the default), the simulator injects the following faults into the simulated network stack. All faults are **deterministic** (driven by a seeded RNG) and **gated on `Signals.ready()`** — no faults fire during application startup.\n\n### Network Faults\n\n| Fault | Default | Description |\n|-------|---------|-------------|\n| **Bimodal latency** | 99.9% fast (100µs–800µs), 0.1% slow (up to 100ms) | Every send/receive incurs a half-round-trip delay drawn from a bimodal distribution |\n| **Connection clogging** | Random per connection pair, up to 100ms | Each (source, destination) address pair is assigned a fixed random additional latency on first connection |\n| **Bind failure** | 5% when `SO_REUSEADDR` is off | `bind()` throws `BindException` to simulate address-in-use races |\n| **Connection reset** | 0.1% per send or receive | Throws `SocketException(\"Connection reset\")` |\n| **Timeout** | 0.1% per send or receive | Injects a 10-second delay then throws `SocketTimeoutException` |\n| **Partial receive** | 95% of reads | Receiver gets a random subset of available bytes instead of all of them |\n| **Variable send buffer** | Random per connection | Send buffer size varies between connections, creating back-pressure |\n| **Sender transit delay** | 0–2ms per write | Random delay between a write completing and the data arriving at the receiver |\n\n### Scheduling Faults\n\n| Fault | Default | Description |\n|-------|---------|-------------|\n| **Thread scheduling jitter** | 1–10,000 ns | Random delay on virtual thread wake-up to explore different interleaving orders |\n\n### JVM-Level Determinism Controls\n\nThese are not faults per se, but JVM behaviors that OpenDST overrides to ensure determinism:\n\n*   **Identity hash codes:** `-XX:hashCode=2` for deterministic `System.identityHashCode()`\n*   **Collection salt:** `ImmutableCollections.SALT32L` and `REVERSE` overridden per node\n*   **RNG:** `ThreadLocalRandom`, `SecureRandom`, and `Random` seeded deterministically\n*   **Time:** `System.currentTimeMillis()`, `System.nanoTime()`, `Instant.now()` return simulated virtual time\n*   **Threads:** All `Thread` constructors redirected to virtual threads on the simulator's scheduler\n*   **GC:** `ReferenceQueue.poll()` returns `null` (deterministic GC behavior)\n*   **Process exit:** `Runtime.exit()` throws `SystemExitError` instead of halting the JVM\n\n## Usage\n\n### Prerequisites\n\n*   Java 25 or later.\n*   Maven.\n\n### Running Simulations\n\nThe Maven plugin's `build` goal (bound to the `package` phase) instruments your code offline and packages everything into a self-contained `-opendst.jar`. To run a simulation, execute the JAR directly:\n\n```bash\nmvn clean package\njava -jar target/*-opendst.jar\n```\n\n### Writing a Deterministic Test\n\nEach service is a class with a `public static void main(String[])` entry point. Use the `opendst-sdk` for assertions and lifecycle signals:\n\n```java\nimport com.pingidentity.opendst.sdk.Assert;\nimport com.pingidentity.opendst.sdk.Signals;\nimport java.io.*;\nimport java.net.*;\n\npublic class EchoApp {\n\n    public static class Server {\n        public static void main(String[] args) throws Exception {\n            var port = Integer.parseInt(args[0]);\n            try (var ss = new ServerSocket(port);\n                 var socket = ss.accept();\n                 var in = new DataInputStream(socket.getInputStream());\n                 var out = new DataOutputStream(socket.getOutputStream())) {\n                Signals.ready();\n                int value = in.readInt();\n                Assert.reachable(\"server-received\", null);\n                out.writeInt(value + 1);\n            }\n        }\n    }\n\n    public static class Client {\n        public static void main(String[] args) throws Exception {\n            var host = args[0];\n            var port = Integer.parseInt(args[1]);\n            Signals.ready();\n            try (var socket = new Socket(host, port);\n                 var out = new DataOutputStream(socket.getOutputStream());\n                 var in = new DataInputStream(socket.getInputStream())) {\n                out.writeInt(42);\n                int response = in.readInt();\n                Assert.always(response == 43, \"echo-correct\", null);\n            }\n        }\n    }\n}\n```\n\nDescribe the deployment topology in a `deployment.yaml`:\n\n```yaml\nservices:\n  server:\n    class: com.example.EchoApp$Server\n    ip: 10.0.0.1\n    args: [\"8080\"]\n  client:\n    class: com.example.EchoApp$Client\n    ip: 10.0.0.2\n    args: [\"10.0.0.1\", \"8080\"]\n```\n\nConfigure the Maven plugin with the `build` goal:\n\n```xml\n\u003cplugin\u003e\n    \u003cgroupId\u003ecom.pingidentity.opendst\u003c/groupId\u003e\n    \u003cartifactId\u003eopendst-maven-plugin\u003c/artifactId\u003e\n    \u003cversion\u003e0.1.0\u003c/version\u003e\n    \u003cexecutions\u003e\n        \u003cexecution\u003e\n            \u003cgoals\u003e\u003cgoal\u003ebuild\u003c/goal\u003e\u003c/goals\u003e\n        \u003c/execution\u003e\n    \u003c/executions\u003e\n\u003c/plugin\u003e\n```\n\nRun the simulation from the produced JAR. All orchestration parameters are CLI arguments:\n\n```bash\njava -jar target/*-opendst.jar \\\n  --stagnation-limit 100 \\\n  --duration 100000 \\\n  --branch-probability 0.7 \\\n  --replay-probability 0.05 \\\n  --working-dir target/opendst-work\n```\n\nAvailable CLI options:\n\n| Option | Default | Description |\n|--------|---------|-------------|\n| `--duration` | 100000 | Maximum number of simulation steps per execution |\n| `--stagnation-limit` | 100 | Stop after N executions without new coverage |\n| `--branch-probability` | 0.7 | Probability of branching to explore a new path |\n| `--replay-probability` | 0.05 | Probability of replaying a previous trace |\n| `--fork-count` | max(1, CPUs/2 - 1) | Number of concurrent simulation forks. Supports `C` suffix (e.g. `1C`, `0.5C`) |\n| `--working-dir` | (JAR name sans `.jar`) | Persistent working directory for deployment, runs, and reports |\n| `--stop` | (none) | Early-stopping conditions (combinable): `first-fail` (stop on first assertion failure), `first-pass` (stop when all assertions pass after stagnation-limit runs). Omit for default behavior (run until stagnation) |\n| `--plan` | (none) | Replay a saved plan file instead of exploring |\n| `--extra-jvm-args` | (none) | Additional JVM arguments appended to build-time defaults |\n\nThe working directory has the following structure:\n\n```\nmyapp-opendst/              # --working-dir (default: JAR path minus .jar)\n  deployment/               # extracted from JAR (skipped if already present)\n  runs/                     # ephemeral per-fork directories\n  report/                   # simulation output (persists across runs)\n    report.json\n    plans/                  # execution plans and simulator logs\n```\n\n## Architecture\n\n*   **`Simulator`:** The core engine that manages the simulation loop, virtual time, and task scheduling.\n*   **`SimulatorAgent`:** A Java Agent that uses the ClassFile API (JEP 484) to intercept JDK methods and redirect them to the `Simulator`.\n*   **`Node`:** Represents a node in the distributed system simulation (context for the current execution).\n\n## References \u0026 Further Reading\n\nTo learn more about Deterministic Simulation Testing and why it is a game-changer for distributed systems reliability, check out these resources:\n\n*   **[Antithesis: Deterministic Simulation Testing](https://antithesis.com/docs/resources/deterministic_simulation_testing/):** A deep dive into determinism and its role in testing.\n*   **[Testing Distributed Systems with Deterministic Simulation](https://www.youtube.com/watch?v=4fFDFbi3toc)** (Will Wilson, FoundationDB): The seminal talk that popularized the technique.\n*   **[FoundationDB Testing](https://apple.github.io/foundationdb/testing.html):** Detailed documentation on how FoundationDB uses simulation.\n*   **[TigerBeetle: Simulation](https://github.com/tigerbeetle/tigerbeetle/blob/main/docs/TIGER_STYLE.md#simulation):** How TigerBeetle uses deterministic simulation to ensure correctness.\n*   **[Rewriting the heart of our sync engine](https://dropbox.tech/infrastructure/-testing-our-new-sync-engine):** How Dropbox used deterministic simulation to rewrite their synchronization engine.\n\n## License\n\nThis project is licensed under the Apache License, Version 2.0.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fpingidentity%2Fopendst","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fpingidentity%2Fopendst","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fpingidentity%2Fopendst/lists"}