{"id":19494755,"url":"https://github.com/ceejbot/lightcycle","last_synced_at":"2026-04-15T21:31:38.304Z","repository":{"id":66234609,"uuid":"584567806","full_name":"ceejbot/lightcycle","owner":"ceejbot","description":"rendezvous hashing \u0026 a  consistent hash ring in rust","archived":false,"fork":false,"pushed_at":"2025-09-17T20:23:19.000Z","size":60,"stargazers_count":2,"open_issues_count":0,"forks_count":0,"subscribers_count":2,"default_branch":"latest","last_synced_at":"2026-03-29T17:11:51.942Z","etag":null,"topics":["consistent-hashing","rendezvous-hashing","rust"],"latest_commit_sha":null,"homepage":"","language":"Rust","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"other","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/ceejbot.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE.md","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}},"created_at":"2023-01-03T00:00:15.000Z","updated_at":"2025-09-17T20:23:22.000Z","dependencies_parsed_at":"2023-02-24T01:01:15.971Z","dependency_job_id":null,"html_url":"https://github.com/ceejbot/lightcycle","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/ceejbot/lightcycle","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ceejbot%2Flightcycle","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ceejbot%2Flightcycle/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ceejbot%2Flightcycle/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ceejbot%2Flightcycle/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/ceejbot","download_url":"https://codeload.github.com/ceejbot/lightcycle/tar.gz/refs/heads/latest","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ceejbot%2Flightcycle/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":31861298,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-04-15T15:24:51.572Z","status":"ssl_error","status_checked_at":"2026-04-15T15:24:39.138Z","response_time":63,"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":["consistent-hashing","rendezvous-hashing","rust"],"created_at":"2024-11-10T21:32:53.932Z","updated_at":"2026-04-15T21:31:38.298Z","avatar_url":"https://github.com/ceejbot.png","language":"Rust","readme":"# LightCycle\n\nRust implementations of the consistent hash ring and rendezvous hash data structures, with a shared API intended to support use in making nicely-distributed clusters of things. Common use cases include distributing items among caches that might appear and disappear, while handling redistribution, or directing sticky sessions at the hosts that should handle them.\n\nRendezvous hashing is a more general yet simpler-to-implement variation of consistent hashing, so you should probably use that most of the time. Both data structures are handy to have around.\n\nYou may feel free to call these data structures by their more fun names of `LightCycle` and `Recognizer`, or you can be boring and call them `ConsistentRing` and `RendezvousRing`.\n\nI haven't used these in production workloads (yet?), but they are well-tested and have reasonable performance, because that's part of the fun. The crate has very few dependencies: just `thiserror` and whatever hash algorithm you've chosen to use. (The default of murmur3 is generally good enough for most use cases.)\n\n## Examples\n\n### Using ConsistentRing (LightCycle)\n\n```rust\nuse lightcycle::{HasId, ConsistentRing, HashRing};\n// Or use the preferred name: use lightcycle::{HasId, LightCycle, HashRing};\n\n#[derive(Debug, Clone)]\nstruct RedisCacheShard {\n    uri: String,\n    // In a real implementation you'd have a redis::Client here\n}\n\nimpl RedisCacheShard {\n    fn new(host: \u0026str, port: u16, db: u8) -\u003e Self {\n        let uri = format!(\"redis://{}:{}/{}\", host, port, db);\n        Self { uri }\n    }\n}\n\nimpl HasId for RedisCacheShard {\n    fn id(\u0026self) -\u003e \u0026str {\n        // We use the redis URI as a unique id for each shard\n        \u0026self.uri\n    }\n}\n\nfn distribute_to_shards(shards: Vec\u003cRedisCacheShard\u003e) {\n    // Create a consistent hash ring with custom replica count\n    let mut ring = ConsistentRing::new_with_replica_count(5);\n\n    // Add each cache shard to the ring\n    for cache in shards {\n        ring.add(Box::new(cache));\n    }\n\n    // Find the right shard for some data\n    let key = \"user:12345:session\";\n    if let Some(shard) = ring.locate(key) {\n        println!(\"Key '{}' should be stored on shard: {}\", key, shard.id());\n        // In real code: shard.client.set(key, data)?;\n    }\n}\n```\n\n### Using RendezvousRing (Recognizer) with Weighted Nodes\n\n```rust\nuse lightcycle::{HasId, RendezvousRing, HashRing};\n// Or use the preferred name: use lightcycle::{HasId, Recognizer, HashRing};\n\n#[derive(Debug, Clone)]\nstruct CacheNode {\n    name: String,\n    capacity_gb: f64,\n}\n\nimpl HasId for CacheNode {\n    fn id(\u0026self) -\u003e \u0026str {\n        \u0026self.name\n    }\n}\n\nfn weighted_distribution() {\n    let mut ring = RendezvousRing::new();\n\n    // Add cache nodes with different capacities\n    let small = CacheNode { name: \"cache-small\".into(), capacity_gb: 8.0 };\n    let large = CacheNode { name: \"cache-large\".into(), capacity_gb: 64.0 };\n\n    // Weight by capacity - larger cache gets proportionally more keys\n    ring.add_weighted(Box::new(small.clone()), 8.0);\n    ring.add_weighted(Box::new(large.clone()), 64.0);\n\n    // Keys will be distributed proportional to weights\n    for key in [\"session:1\", \"session:2\", \"session:3\"] {\n        if let Some(node) = ring.locate(key) {\n            println!(\"{} -\u003e {}\", key, node.id());\n        }\n    }\n}\n```\n\n## Choosing Between ConsistentRing and RendezvousRing\n\nUse rendezvouz hashing when:\n\n- You're at all uncertain about which to pick\n- You want a weighted distribution based on resource capacity\n- You have fewer resources (\u003c 100) where O(n) lookup is acceptable\n- Memory efficiency is important (no replica storage needed)\n- You want the simplest possible implementation\n\nUse the consistent hash ring when:\n\n- You need the traditional consistent hashing algorithm\n- You have many resources (100+) and need O(log n) lookup time\n- Memory usage is not a primary concern\n- You need fine control over replica placement\n\n## API\n\n### Common Interface (HashRing trait)\n\nBoth ring types implement the `HashRing` trait:\n\n```rust\n// Create a ring\nlet mut ring = ConsistentRing::new();  // or RendezvousRing::new()\n\n// Add resources\nring.add(Box::new(resource));\n\n// Add with weight (RendezvousRing only, ConsistentRing ignores weight)\nring.add_weighted(Box::new(resource), weight);\n\n// Find resource for a key\nlet resource = ring.locate(\"some-key\");\n\n// Remove a resource\nring.remove(\u0026resource);\n\n// Update weight (RendezvousRing only, ConsistentRing returns error)\nring.update_weight(\u0026resource, new_weight);\n```\n\n### ConsistentRing Specific\n\n```rust\n// Create with custom replica count (default is 4)\nlet ring = ConsistentRing::new_with_replica_count(100);\n\n// Get replica count\nlet replicas = ring.replica_count();\n```\n\n### RendezvousRing Specific\n\n```rust\n// Get current weight of a resource\nlet weight = ring.get_weight(\u0026resource);\n\n// List all resources with their weights\nlet weighted_resources = ring.resources_with_weights();\n```\n\n### Hash Functions\n\nThe murmur3 hash, as implemented in the [murmurs crate](https://github.com/owengombas/murmurs) is the default hash function in use. It's a fast non-cryptographic hash, which is the category of hash algorithm idea for this use case. Other algorithms are provided as crate features:\n\n**Hash Function Performance (rendezvous hashing):**\n- `murmur3`: Best overall (51ns/op, excellent distribution), default feature\n- `xxhash`: Fastest (40ns/op, good distribution), `features = [\"hash-xxhash\"]`\n- `rapidhash-fast`: Fast with excellent weighted accuracy (66ns/op, 98.2%), `features = [\"hash-rapidhash\"]`\n- `rapidhash-quality`: Best weighted accuracy (75ns/op, 99.0%), `features = [\"hash-rapidhash\"]`\n- `metrohash`: High-quality distribution (67ns/op), `features = [\"hash-metrohash\"]`\n- `blake3`: Cryptographic option (978ns/op), `features = [\"hash-blake3\"]`\n\nRemember to disable default features to turn off murmur3:\n\n```toml\n[dependencies]\n# Default uses murmur3 (best overall performance)\nlightcycle = \"0.2\"\n\n# Use xxhash for maximum speed\nlightcycle = { version = \"0.2\", default-features = false, features = [\"hash-xxhash\"] }\n\n# Other options: hash-metrohash, hash-rapidhash, hash-blake3\n```\n\n## Performance\n\nBased on benchmarks with 10 nodes and 10,000 lookups:\n\n- **RendezvousRing**: ~500μs (50ns per lookup)\n- **ConsistentRing**: ~1ms (100ns per lookup)\n- **Memory**: RendezvousRing uses ~100x less memory (no replica storage)\n\n## Documentation\n\nRun `cargo doc --open` for detailed API documentation.\n\n## LICENSE\n\nThis code is licensed via [the Parity Public License.](https://paritylicense.com) This license requires people who build on top of this source code to share their work with the community, too. This means if you hack on it for work, you have to make your work repo public somehow. I mean, have fun. See the license text for details.\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fceejbot%2Flightcycle","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fceejbot%2Flightcycle","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fceejbot%2Flightcycle/lists"}