{"id":30196271,"url":"https://github.com/krasin-ga/respsody","last_synced_at":"2026-04-17T02:32:09.114Z","repository":{"id":307993752,"uuid":"1030501370","full_name":"krasin-ga/respsody","owner":"krasin-ga","description":"General purpose RESP client","archived":false,"fork":false,"pushed_at":"2025-12-14T09:03:44.000Z","size":216,"stargazers_count":4,"open_issues_count":1,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2025-12-16T13:16:14.836Z","etag":null,"topics":["csharp","dotnet","redis-client"],"latest_commit_sha":null,"homepage":"","language":"C#","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/krasin-ga.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":"2025-08-01T18:45:01.000Z","updated_at":"2025-09-19T05:34:43.000Z","dependencies_parsed_at":"2025-08-03T14:44:49.555Z","dependency_job_id":"e076ab8a-4164-4c30-8d93-988bb4cfd585","html_url":"https://github.com/krasin-ga/respsody","commit_stats":null,"previous_names":["krasin-ga/respsody"],"tags_count":3,"template":false,"template_full_name":null,"purl":"pkg:github/krasin-ga/respsody","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/krasin-ga%2Frespsody","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/krasin-ga%2Frespsody/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/krasin-ga%2Frespsody/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/krasin-ga%2Frespsody/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/krasin-ga","download_url":"https://codeload.github.com/krasin-ga/respsody/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/krasin-ga%2Frespsody/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":31912373,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-04-16T18:22:33.417Z","status":"online","status_checked_at":"2026-04-17T02:00:06.879Z","response_time":62,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"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":["csharp","dotnet","redis-client"],"created_at":"2025-08-13T05:17:28.878Z","updated_at":"2026-04-17T02:32:09.098Z","avatar_url":"https://github.com/krasin-ga.png","language":"C#","funding_links":[],"categories":[],"sub_categories":[],"readme":"﻿# Respsody\n\n[![License](https://img.shields.io/badge/license-MIT-blue.svg)](LICENSE) [![respsody Nuget](https://img.shields.io/nuget/v/Respsody?\u0026label=Respsody)](https://www.nuget.org/packages/Respsody/)\n\n\u003cimg alt=\"Respsody\" src=\"assets/respsody_logo.svg\" align=\"right\" /\u003e **Respsody** is an experimental, high-performance, asynchronous, general-purpose [RESP3](https://github.com/redis/redis-specifications/blob/master/protocol/RESP3.md) client library written in C#. It's currently in an early stage of development and intended for experimentation and community feedback. Expect breaking changes, missing features, and limited error handling. \n\n## Features\n\n- Compatibility with any RESP3-compliant server, including Redis, Garnet, Dragonfly, Valkey, and others\n- Code generation for arbitrary commands\n- Efficient memory usage through scoped buffer reuse\n- Full Redis Cluster protocol support with advanced routing: primary/replica node targeting, slot-aware command dispatch, and optimized MGETs by grouping keys per responsible node\n\n\n## Installation\n\n```\ndotnet add package respsody --prerelease\n```\n\n## Benchmarks\nTested against the StackExchange.Redis client. Click on the spoilers to reveal the results.\n\n\u003eHardware: AMD Ryzen 9 3900X, 1 CPU, 24 logical and 12 physical cores\n\n\u003eRESP Server: [Garnet](https://github.com/microsoft/garnet) was run in a separate process on localhost\n\n### BenchmarkDotNet\n\n\u003cdetails\u003e\n\u003csummary\u003e\nWindows 11, .NET 9.0.5 \u003cbr\u003e\n\n\u003c/summary\u003e\n\n| Method                                                                | Mean       | Op/s        | Gen0   | Gen1   | Gen2   | Allocated |\n|---------------------------------------------------------------------- |-----------:|------------:|-------:|-------:|-------:|----------:|\n| '[when_all 12_500 tasks] [Respsody] Get (noop) commands/s'            |   614.5 ns | 1,627,287.0 | 0.0200 | 0.0125 |      - |     184 B |\n| '[when_all 12_500 tasks] [StackExchange.Redis] Get (noop) commands/s' |   981.8 ns | 1,018,550.5 | 0.0488 | 0.0325 |      - |     416 B |\n| '[when_all 12_500 tasks] [Respsody] Get (json) commands/s'            |   913.7 ns | 1,094,486.0 | 0.1288 | 0.0613 | 0.0025 |    1041 B |\n| '[when_all 12_500 tasks] [StackExchange.Redis] Get (json) commands/s' | 1,512.3 ns |   661,241.9 | 0.1800 | 0.0675 | 0.0125 |    1464 B |\n\n\n| Method                | MessageSize | Mean     | Op/s    | Gen0   | Gen1   | Allocated |\n|---------------------- |------------ |---------:|--------:|-------:|-------:|----------:|\n| Respsody_Set_Get      | 256         | 183.4 us | 5,453.9 |      - |      - |     353 B |\n| SE_Set_Get            | 256         | 224.8 us | 4,449.0 |      - |      - |    1064 B |\n| Respsody_MSet_MGet_10 | 256         | 186.9 us | 5,350.5 |      - |      - |    1570 B |\n| SE_MSet_MGet_10       | 256         | 219.2 us | 4,561.5 |      - |      - |    5192 B |\n| Respsody_Set_Get      | 32786       | 198.7 us | 5,032.4 |      - |      - |     377 B |\n| SE_Set_Get            | 32786       | 269.0 us | 3,718.1 | 0.4883 |      - |   33601 B |\n| Respsody_MSet_MGet_10 | 32786       | 530.5 us | 1,885.0 |      - |      - |    1810 B |\n| SE_MSet_MGet_10       | 32786       | 608.7 us | 1,642.8 | 5.8594 | 1.9531 |  330554 B |\n\n*SE -\u003e StackExchange.Redis*\n\u003c/details\u003e\n\n\n\u003cdetails \u003e\n\u003csummary\u003eUbuntu 20 LTS (Focal Fossa) *WSL Virtualization*, .NET 9.0.5\u003c/summary\u003e\n\n| Method                                                                | Mean       | Op/s        | Gen0   | Gen1   | Gen2   | Allocated |\n|---------------------------------------------------------------------- |-----------:|------------:|-------:|-------:|-------:|----------:|\n| '[when_all 12_500 tasks] [Respsody] Get (noop) commands/s'            |   736.9 ns | 1,357,001.3 | 0.0200 | 0.0100 |      - |     184 B |\n| '[when_all 12_500 tasks] [StackExchange.Redis] Get (noop) commands/s' | 1,283.0 ns |   779,449.1 | 0.0475 | 0.0325 |      - |     416 B |\n| '[when_all 12_500 tasks] [Respsody] Get (json) commands/s'            | 1,148.7 ns |   870,574.8 | 0.1288 | 0.0563 | 0.0025 |    1042 B |\n| '[when_all 12_500 tasks] [StackExchange.Redis] Get (json) commands/s' | 1,737.4 ns |   575,570.6 | 0.1825 | 0.0950 | 0.0200 |    1464 B |\n\u003c/details\u003e\n\n#### Non-scientific\nThese benchmarks run a series of test scenarios and measure total metrics.\n\n\u003cdetails\u003e\n\u003csummary\u003e\nWindows 11, .NET 9.0.5 \u003cbr\u003e\n\n\u003c/summary\u003e\n\n\n| Scenario                              | Target             | Dataset                    | Iterations | TotalElapsed |  BestRun |  CpuTime | TotalAllocated |\n|---------------------------------------|--------------------|----------------------------|------------|--------------|----------|----------|----------------|\n| SetGet_Sequential                     | Respsody           | small_20K 20000KV 2.45 MiB |         50 |      157.19s |    2.97s |  179.31s |      46.77 MiB |\n| SetGet_Sequential                     | StackOverflowRedis | small_20K 20000KV 2.45 MiB |         50 |      209.82s |    3.82s |  218.55s |     667.54 MiB |\n| -                                     | -                  | -                          |          - |            - |        - |        - |              - |\n| SetGet_Sequential                     | Respsody           | large_300 300KV 151.62 MiB |         50 |       13.61s | 184.48ms |    8.78s |       9.69 MiB |\n| SetGet_Sequential                     | StackOverflowRedis | large_300 300KV 151.62 MiB |         50 |       24.22s | 372.82ms |   14.38s |       7.42 GiB |\n| -                                     | -                  | -                          |          - |            - |        - |        - |              - |\n| SetGet_Sequential_WithJsonDeserialize | Respsody           | jsons_10K 10000KV 2.25 MiB |         50 |       90.31s |    1.73s |  107.67s |     461.92 MiB |\n| SetGet_Sequential_WithJsonDeserialize | StackOverflowRedis | jsons_10K 10000KV 2.25 MiB |         50 |      128.20s |    2.16s |  128.30s |       1.04 GiB |\n| -                                     | -                  | -                          |          - |            - |        - |        - |              - |\n| SetGet_WhenEach                       | Respsody           | small_20K 20000KV 2.45 MiB |         50 |        1.47s |  24.02ms |    9.09s |     316.49 MiB |\n| SetGet_WhenEach                       | StackOverflowRedis | small_20K 20000KV 2.45 MiB |         50 |        2.38s |  34.12ms |   15.62s |     852.86 MiB |\n| -                                     | -                  | -                          |          - |            - |        - |        - |              - |\n| OneSetTwoGets_WhenEach                | Respsody           | small_20K 20000KV 2.45 MiB |         50 |        1.74s |  30.78ms |   16.52s |     387.12 MiB |\n| OneSetTwoGets_WhenEach                | StackOverflowRedis | small_20K 20000KV 2.45 MiB |         50 |        3.29s |  52.22ms |   21.67s |       1.24 GiB |\n| -                                     | -                  | -                          |          - |            - |        - |        - |              - |\n| SetGet_WhenEach_WithJsonDeserialize   | Respsody           | jsons_10K 10000KV 2.25 MiB |         50 |     763.14ms |  13.88ms |    4.33s |     597.01 MiB |\n| SetGet_WhenEach_WithJsonDeserialize   | StackOverflowRedis | jsons_10K 10000KV 2.25 MiB |         50 |        1.24s |  20.73ms |    7.42s |       1.12 GiB |\n| -                                     | -                  | -                          |          - |            - |        - |        - |              - |\n| OneSetTwoGets_WhenEach                | Respsody           | large_300 300KV 151.62 MiB |         50 |       18.14s | 280.61ms |   12.23s |      74.49 MiB |\n| OneSetTwoGets_WhenEach                | StackOverflowRedis | large_300 300KV 151.62 MiB |         50 |       23.55s | 339.96ms |   22.36s |      15.23 GiB |\n| -                                     | -                  | -                          |          - |            - |        - |        - |              - |\n| MSetGet                               | Respsody           | small_20K 20000KV 2.45 MiB |         50 |     649.52ms |   8.35ms | 890.62ms |     157.21 KiB |\n| MSetGet                               | StackOverflowRedis | small_20K 20000KV 2.45 MiB |         50 |        1.07s |  16.51ms |    1.78s |     241.06 MiB |\n| -                                     | -                  | -                          |          - |            - |        - |        - |              - |\n| MSetGet                               | Respsody           | large_300 300KV 151.62 MiB |         50 |        9.27s | 165.54ms |    5.00s |      11.91 MiB |\n| MSetGet                               | StackOverflowRedis | large_300 300KV 151.62 MiB |         50 |       22.07s | 297.64ms |   43.58s |      27.06 GiB |\n\u003c/details\u003e\n\n## Quick Start\n\n```csharp\nusing Respsody;\nusing Respsody.Resp;\nusing System.Net;\nusing System.Text;\nusing static System.Console;\n\nvar clientFactory = new RespClientFactory();\nusing var client = await clientFactory.Create(new IPEndPoint(IPAddress.Loopback, 6379));\n\nvar utf8Key = Key.Utf8(\"respsody\");\nKey strKey = \"gob_key\"; // same as Key.Utf8\nvar arrKey = Key.ByteArray([1, 0, 1]);\nvar utf16Key = Key.Unicode(\"‼️\");\n\nawait client.Set(utf8Key, Value.Utf8(\"hello, respsody!\"));\nawait client.Set(arrKey, Value.ByteArray([0, 3, 0, 3, 6, 6]));\nawait client.Set(strKey, Value.Utf8(\"we need a gimmick!\"));\nawait client.Set(utf16Key, Value.Unicode(\"⚡\"));\n\n//dispose non-void responses to release underlying buffers\nusing var getResponse = await client.Get(utf8Key);\n\nWriteLine(\"- GET result -\");\nWriteLine(getResponse.ToString());\n\n//for aggregate responses(arrays, maps, sets) dispose only root response\nusing var mgetResponse = await client.Mget([strKey, utf16Key, arrKey]);\n\nOutputEncoding = Encoding.Unicode;\nWriteLine(\"- MGET result -\");\nWrite(\"strings: \");\nWrite(mgetResponse[0].ToRespString());\nWriteLine(mgetResponse[1].ToRespString().AsUnicodeSpan());\n\nWrite(\"byte[]: \");\nWriteLine($\"{string.Join(\",\", [..mgetResponse[2].ToRespString().GetSpan()])}\");\n\n//define and generate commands:\n[RespCommand(\"GET key\", ResponseType.String)]\n[RespCommand(\"SET key value\", ResponseType.Void)]\n[RespCommand(\"MGET key [key ...]\", ResponseType.Array)]\n[RespCommand(\"MSET key value [key value ...]\", ResponseType.Void)]\n[RespCommand(\"COMMAND DOCS [command-name:string [command-name:string ...]]\", ResponseType.Map)]\npublic static class Commands;\n```\n\nCluster usage:\n\n```csharp\nusing Respsody.Cluster;\n\nvar clusterRouter = new ClusterRouter(\n    new ClusterRouterOptions\n    {\n        SeedEndpoints = [\"some_cluster_host1:6379\", \"some_cluster_host2:6379\"],\n        ClientOptions = new RespClientOptions()\n    });\n\nawait clusterRouter.Initialize();\n\n// execute command on primary node that is responsible for key\nusing var v1 = await clusterRouter.RouteTo(RolePreference.Primary).Get(Key.Utf8(\"some_key_1\"));\n\n// pick random node and execute COMMAND DOCS on it\nusing var docs = await clusterRouter.PickRandom().Command(COMMAND.DOCS);\n\n//group objects by node and execute commands on it\n(Key Key, Value Value)[] objects = [(Key.Utf8(\"k_1\"), Value.Utf8(\"v_1\")), /* ... */ (Key.Utf8(\"k_n\"), Value.Utf8(\"v_n\"))];\nforeach (var (node, nodeObjects)in clusterRouter.RouteTo(RolePreference.Primary).GroupBy(objects, o =\u003e o.Key))\n    await node.Mset(nodeObjects);\n\n```\n\n## Known Limitations\n\n* Pub/Sub API is limited to the RESP3 variant\n* No support for secure connections\n* No specialized API for selecting logical databases\n* No flow control/backpressure — use an external rate limiter\n* No support for Sentinel\n* No dedicated API for transactions. They can be made with command combos, though the approach is a bit hacky:\n```\n    using var comboResult = await Combo.Build(client)\n        .Cmd(r =\u003e r.MultiCommand())\n        .Cmd(r =\u003e r.IncrVoidCommand(key)) // Void overload used to avoid processing the -QUEUED response\n        .Cmd(r =\u003e r.ExecCommand())\n        .Execute();\n```\n\n## Contributing\n\nContributions, bug reports, and feedback are welcome and appreciated.\n\n## Disclaimer\n\nThis project is an independent work and is not endorsed, supported, or certified by Redis.\n\n## License\n\nThis project is licensed under the [MIT License](LICENSE).\n\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fkrasin-ga%2Frespsody","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fkrasin-ga%2Frespsody","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fkrasin-ga%2Frespsody/lists"}