{"id":47727526,"url":"https://github.com/s2-streamstore/s2-verification","last_synced_at":"2026-04-02T20:54:47.836Z","repository":{"id":333941499,"uuid":"1032221586","full_name":"s2-streamstore/s2-verification","owner":"s2-streamstore","description":"Tools for verifying linearizability of S2","archived":false,"fork":false,"pushed_at":"2026-03-04T04:54:33.000Z","size":126,"stargazers_count":4,"open_issues_count":6,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2026-03-04T09:49:41.003Z","etag":null,"topics":["distributed-systems","linearizability","rust","s2"],"latest_commit_sha":null,"homepage":"https://s2.dev","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/s2-streamstore.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,"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":"2025-08-05T02:12:53.000Z","updated_at":"2026-03-04T04:54:37.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/s2-streamstore/s2-verification","commit_stats":null,"previous_names":["s2-streamstore/s2-verification"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/s2-streamstore/s2-verification","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/s2-streamstore%2Fs2-verification","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/s2-streamstore%2Fs2-verification/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/s2-streamstore%2Fs2-verification/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/s2-streamstore%2Fs2-verification/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/s2-streamstore","download_url":"https://codeload.github.com/s2-streamstore/s2-verification/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/s2-streamstore%2Fs2-verification/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":31316008,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-04-02T12:59:32.332Z","status":"ssl_error","status_checked_at":"2026-04-02T12:54:48.875Z","response_time":89,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.5:443 state=error: 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":["distributed-systems","linearizability","rust","s2"],"created_at":"2026-04-02T20:54:47.119Z","updated_at":"2026-04-02T20:54:47.817Z","avatar_url":"https://github.com/s2-streamstore.png","language":"Rust","funding_links":[],"categories":[],"sub_categories":[],"readme":"# s2-verification\n\nThis repo contains a [Porcupine](https://github.com/anishathalye/porcupine) model for verifying linearizability of the core [s2.dev](https://s2.dev/) service, as well as a binary for collecting concurrent client logs which can be provided to that model.\n\nWe use this internally as a part of our custom [turmoil](https://github.com/tokio-rs/turmoil)-based [deterministic simulation testing framework](https://s2.dev/blog/dst), as well as on workloads that we run on the [Antithesis](https://antithesis.com/) platform. In these setups, we subject (simulated) S2 to a range of stresses, and assert on different invariants (maintaining linearizability being a basic one of them). More context on this can be found in [this blog post](https://s2.dev/blog/linearizability).\n\nA concurrent history can also be collected against the prod S2 service (or self-hosted [s2-lite](https://github.com/s2-streamstore/s2)), and evaluated with Porcupine.\n\n## Installing\n\nYou will need active installations of Rust and Golang. (Also `make`.)\n\n### Porcupine model (golang)\n\n```bash\nmake install-go\n```\n\nYou should then have `s2-porcupine` in your `$GOBIN`.\n\n### History collector (Rust)\n\nEasiest to run this with `cargo`.\n\n## Running\n\n### Create a basin\n\nCreate a S2 basin for your test streams. This can be done on the [dashboard](https://s2.dev/dashboard), or via the [CLI](https://s2.dev/docs/quickstart):\n```bash\ns2 create-basin linearizability-testing-0001\n```\n\nFeel free to use an existing basin if that is easier.\n\n### Collect concurrent history\n\nRun the `collect-history` binary. This will make a new stream with the provided name if one does not already exist.\n\nNote that the Porcupine model assumes that the `tail` of the stream is 0 at start. The `collect-history` bin will check the current tail, before starting the concurrent clients; if it is not 0, it will synthesize (necessarily successful) append logs which represent a move from 0 -\u003e the actual current tail, and these will be the first two entries in the resulting log. Actual concurrent client logs will begin after that.\n\n```bash\nexport RUST_LOG=info\nexport S2_ACCESS_TOKEN=\"my-token\"\n\ncargo run --release -- \\\n  linearizability-testing-0001 \\\n  stream-1 \\\n  --num-concurrent-clients 5 \\\n  --num-ops-per-client 100\n```\n\nA few things to keep in mind:\n- All requests within a client are made sequentially, and there are no sleeps. A client will move on to a subsequent request as soon as it receives a response from a prior one.\n- Each client will randomly choose between an append, read, and check-tail operation.\n- It is hard to predict how hard of a time Porcupine will have assembling a linear history. In general, the more clients, the harder it is to construct a history in a reasonable amount of time.\n\nThere are three types of client behaviors that can be selected from, using the `workflow` argument in the CLI.\n\n#### `regular` (default)\n\nThe concurrent clients will not use any concurrency primitives. All appends are expected to succeed, unless there is a loss of availability.\n\n#### `match-seq-num`\n\nConcurrent clients will all maintain a local guess of what the next expected sequence number is, updated whenever `tail` info is received (from a successful append, read, or check-tail op). This value will be used for a `matchSeqNum` guard on the subsequent append made by that client.\n\nAll clients will be racing, in effect, so we expect to see many appends receiving definite failures (whenever the `matchSeqNum` condition cannot be satisfied). The more concurrent clients, the more apparent this race becomes.\n\n#### `fencing`\n\nConcurrent clients will attempt to set a fencing token, to a token unique to each client, whenever a client's operation counter % 100 == 0. Similar to above, this will result in a race, and we expect many definite append failures.\n\nThe `setFencingToken` op will be guarded by a `matchSeqNum` to avoid a simple last-write-win situation.\n\n### Outputs\n\nIf all goes well, you should see something like: `writer finished, path: \"./data/records.1754354415.jsonl\"`. This file contains your history.\n\nThe log contains JSON objects (per line) with details about calls and returns.\n\nThe ordering expresses the relative timing of events (start and finish calls) observed by the history binary.\n\nFor example:\n\nThe start of an append, for a batch containing 5 records, which specifies a `match_seq_num` clause, is represented like:\n```json\n{\n  \"event\": {\n    \"Start\": {\n      \"Append\": {\n        \"num_records\": 5,\n        \"set_fencing_token\": null,\n        \"fencing_token\": null,\n        \"match_seq_num\": 2733\n      }\n    }\n  },\n  \"client_id\": 2,\n  \"op_id\": 4984\n}\n```\n\n... and its eventual (successful) return would be represented like:\n```json\n{\n  \"event\": {\n    \"Finish\": {\n      \"Success\": {\n        \"tail\": 2738\n      }\n    }\n  },\n  \"client_id\": 2,\n  \"op_id\": 4984\n}\n```\n\n### Evaluate linearizability\n\nProvide the log obtained above to the Porcupine model:\n\n```bash\n# if you haven't already:\nmake install-go\n\n# invoke the binary with your history jsonl file\ns2-porcupine \\\n  -file=\"./data/records.1754354415.jsonl\"\n```\n\n## Running against `s2-lite`\n\n[s2-lite](https://github.com/s2-streamstore/s2) is an open-source, self-hosted S2.\n\nStart `s2-lite` in a Docker container:\n\n```bash\ndocker run -p 8080:80 ghcr.io/s2-streamstore/s2-lite\n```\n\nCreate a new basin, gather history, and check it against the model:\n\n```bash\n# These vars point to the local instance\nexport S2_ACCOUNT_ENDPOINT=\"http://localhost:8080\"\nexport S2_BASIN_ENDPOINT=\"http://localhost:8080\"\n# No token creation is needed for s2-lite\nexport S2_ACCESS_TOKEN=\"redundant\"\n\ns2 create-basin linearizability-test\n\nout_path=$(\n  cargo run -- \\\n    linearizability-test \\\n    t1 \\\n    --num-concurrent-clients 5 \\\n    --num-ops-per-client 100 \\\n    --workflow regular \n)\n  \ns2-porcupine -file=\"$out_path\"\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fs2-streamstore%2Fs2-verification","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fs2-streamstore%2Fs2-verification","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fs2-streamstore%2Fs2-verification/lists"}