placement_machines 0.1.4-aarch64-linux
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/CHANGELOG.md +29 -0
- data/LICENSE.txt +21 -0
- data/README.md +185 -0
- data/ext/placement_machines_native/core/CHANGELOG.md +22 -0
- data/ext/placement_machines_native/core/LICENSE +21 -0
- data/ext/placement_machines_native/core/README.md +33 -0
- data/ext/placement_machines_native/extconf.rb +3 -0
- data/lib/placement_machines/version.rb +5 -0
- data/lib/placement_machines.rb +6 -0
- data/lib/placement_machines_native/placement_machines_native.so +0 -0
- data/sig/placement_machines.rbs +11 -0
- metadata +64 -0
checksums.yaml
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
SHA256:
|
|
3
|
+
metadata.gz: '0936c43e1833e2f87bba18c899d3af463c5a45a3bd58438228de2e9d94e7104f'
|
|
4
|
+
data.tar.gz: 8f7ff6e486c555f1fbacb11d495d1587c06e578b741ad3ab78bcd40f740e5b01
|
|
5
|
+
SHA512:
|
|
6
|
+
metadata.gz: 15332feebd29dd6a0b08f3514f3ae38c674d1b4186838a2e74da88ee68a13790059c898d6c4e30ccaed425b36a116d20508464037b5990f8a94763b767fbad42
|
|
7
|
+
data.tar.gz: 1703318e995787a2137fe880fc946a1456853701927ce8eb07586ce947f5c8ee80582b07aca714efb4a1d8986b071d3f3f8ee7826fb9d398f103fecf5c29a53c
|
data/CHANGELOG.md
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
## [0.1.4](https://github.com/seuros/placement_machines/compare/placement_machines/v0.1.3...placement_machines/v0.1.4) (2026-11-06)
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
### Bug Fixes
|
|
7
|
+
|
|
8
|
+
* crates.io keywords and categories for the core ([997f0d7](https://github.com/seuros/placement_machines/commit/997f0d70e6292afdf3212730e3a1ac2e4662b86e))
|
|
9
|
+
|
|
10
|
+
## [0.1.3](https://github.com/seuros/placement_machines/compare/placement_machines/v0.1.2...placement_machines/v0.1.3) (2026-11-06)
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
### Bug Fixes
|
|
14
|
+
|
|
15
|
+
* keep test sources out of the packaged gem and crate ([18a75e8](https://github.com/seuros/placement_machines/commit/18a75e8cdf890fcff09732c53191b957db3fd43d))
|
|
16
|
+
|
|
17
|
+
## [0.1.2](https://github.com/seuros/placement_machines/compare/placement_machines/v0.1.1...placement_machines/v0.1.2) (2026-11-06)
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
### Bug Fixes
|
|
21
|
+
|
|
22
|
+
* cross builds no longer compile for the host ([3218c79](https://github.com/seuros/placement_machines/commit/3218c795d9cd67f08b6d881d53c2488c37a5e2bd))
|
|
23
|
+
|
|
24
|
+
## [0.1.1](https://github.com/seuros/placement_machines/compare/placement_machines-v0.1.0...placement_machines/v0.1.1) (2026-11-06)
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
### Features
|
|
28
|
+
|
|
29
|
+
* initial extraction ([eb26a4d](https://github.com/seuros/placement_machines/commit/eb26a4d14c4ee79cbd0f6afc8edbd8b55e598bae))
|
data/LICENSE.txt
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Abdelkader Boudih
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
data/README.md
ADDED
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
# PlacementMachines
|
|
2
|
+
|
|
3
|
+
> When one station goes dark, only its freight moves. ðŸ§
|
|
4
|
+
|
|
5
|
+
Consistent placement for Ruby. Give it a key and the list of nodes you have right now, and it tells you which node owns the key. Add a node, lose a node, ask again, and only the keys that belonged to the changed node get a new answer. Everything else stays exactly where it was. It can also pick three replicas spread across failure domains, and it can walk a hash ring if you already have one.
|
|
6
|
+
|
|
7
|
+
## Quick Start
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
gem 'placement_machines'
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
```ruby
|
|
14
|
+
# [node_id, failure_domain] for every node currently alive
|
|
15
|
+
nodes = [[10, 1], [20, 1], [30, 2], [40, 2]]
|
|
16
|
+
|
|
17
|
+
PlacementMachines.rendezvous_owner(42, nodes) # => 30
|
|
18
|
+
PlacementMachines.replicas(42, nodes) # => [30, 20, nil]
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
Node 30 owns key 42. If you want copies, the second goes to node 20, in a different domain. The third slot is `nil` because there are only two domains and the gem refuses to put two copies in the same one.
|
|
22
|
+
|
|
23
|
+
## A Message to the Resistance
|
|
24
|
+
|
|
25
|
+
So you sharded the cache. `node = nodes[key.hash % nodes.size]`. Four nodes, keys spread evenly, latency graphs flat, everyone goes home.
|
|
26
|
+
|
|
27
|
+
Then node 30 reboots for a kernel patch. `nodes.size` goes from 4 to 3. Every single key gets a new modulus, which means three out of four keys now point at a node that has never heard of them. The cache hit rate drops to the floor, the database behind it catches the full read load, and the on-call engineer learns what a thundering herd feels like at 2 AM. Over one node. That was patched. On purpose.
|
|
28
|
+
|
|
29
|
+
The fix is not a smarter modulus. The fix is to score every node for every key independently, so removing one node only disturbs the keys that scored highest on that node. Those keys move. Nothing else does. That is what this gem computes, and it does it the same way on every machine that asks, with no coordination and nothing to sync.
|
|
30
|
+
|
|
31
|
+
## Chapter 1: Stardate 2025.186, the AMS Karger
|
|
32
|
+
|
|
33
|
+
> **AMS Karger**, NCC-4213, *Karger*-class navigation tender.
|
|
34
|
+
> A missing station should not require redrawing the entire galaxy.
|
|
35
|
+
|
|
36
|
+
The tender holds no freight and commands no ships. It answers one question for any manifest and any list of stations: where does this go? Ask it on any bridge in the fleet with the same station list and you get the same answer. Strike a station from the list and the only manifests that get a new destination are the ones that were bound for it.
|
|
37
|
+
|
|
38
|
+
Somewhere in the fleet, a cadet proposes:
|
|
39
|
+
|
|
40
|
+
```ruby
|
|
41
|
+
def station_for(manifest)
|
|
42
|
+
STATIONS[manifest.id % STATIONS.size]
|
|
43
|
+
end
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
"That," says the navigator, "is how one dark station reroutes the whole galaxy."
|
|
47
|
+
|
|
48
|
+
## The Flight Plan
|
|
49
|
+
|
|
50
|
+
Here is the life of one key:
|
|
51
|
+
|
|
52
|
+
```mermaid
|
|
53
|
+
stateDiagram-v2
|
|
54
|
+
[*] --> owned_by_30: rendezvous_owner(42, nodes)
|
|
55
|
+
owned_by_30 --> owned_by_30: some other node joins or leaves
|
|
56
|
+
owned_by_30 --> owned_by_20: node 30 leaves, next-best score wins
|
|
57
|
+
owned_by_20 --> owned_by_30: node 30 returns, its score still wins
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
And the night node 30 went down for a patch:
|
|
61
|
+
|
|
62
|
+
```mermaid
|
|
63
|
+
sequenceDiagram
|
|
64
|
+
participant App
|
|
65
|
+
participant P as PlacementMachines
|
|
66
|
+
participant N30 as Node 30
|
|
67
|
+
participant N20 as Node 20
|
|
68
|
+
|
|
69
|
+
App->>P: rendezvous_owner(42, [10, 20, 30, 40])
|
|
70
|
+
P-->>App: 30
|
|
71
|
+
App->>N30: write key 42
|
|
72
|
+
Note over N30: reboots for a kernel patch
|
|
73
|
+
|
|
74
|
+
App->>P: rendezvous_owner(42, [10, 20, 40])
|
|
75
|
+
P-->>App: 20
|
|
76
|
+
App->>N20: write key 42
|
|
77
|
+
Note over App: keys owned by 10, 20 and 40 never moved
|
|
78
|
+
|
|
79
|
+
Note over N30: back online
|
|
80
|
+
App->>P: rendezvous_owner(42, [10, 20, 30, 40])
|
|
81
|
+
P-->>App: 30
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
## How Placement Works
|
|
85
|
+
|
|
86
|
+
### Rendezvous
|
|
87
|
+
|
|
88
|
+
Every node gets a score for every key. Highest score owns the key. The score depends only on the key and the node id, never on who else is in the list.
|
|
89
|
+
|
|
90
|
+
```ruby
|
|
91
|
+
nodes = [[10, 1], [20, 1], [30, 2], [40, 2]]
|
|
92
|
+
|
|
93
|
+
PlacementMachines.rendezvous_owner(42, nodes) # => 30
|
|
94
|
+
PlacementMachines.rendezvous_owner(42, nodes.reject { |id, _| id == 30 }) # => 20
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
Node 30 drops out and key 42 goes to node 20, the runner-up. Measure it over a thousand keys and the point lands:
|
|
98
|
+
|
|
99
|
+
```ruby
|
|
100
|
+
before = nodes
|
|
101
|
+
after = nodes.reject { |id, _| id == 30 }
|
|
102
|
+
|
|
103
|
+
moved = (0...1000).count do |key|
|
|
104
|
+
PlacementMachines.rendezvous_owner(key, before) != PlacementMachines.rendezvous_owner(key, after)
|
|
105
|
+
end
|
|
106
|
+
owned_by_30 = (0...1000).count { |key| PlacementMachines.rendezvous_owner(key, before) == 30 }
|
|
107
|
+
|
|
108
|
+
moved == owned_by_30 # => true
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
Only node 30's keys moved. Compare that with `key % 4` becoming `key % 3`, which reassigns roughly three quarters of them.
|
|
112
|
+
|
|
113
|
+
The scores are available directly if you want to rank nodes yourself:
|
|
114
|
+
|
|
115
|
+
```ruby
|
|
116
|
+
PlacementMachines.score(42, 30) # => a large Integer
|
|
117
|
+
PlacementMachines.score(42, 30) > PlacementMachines.score(42, 20) # => true
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
Larger wins. Equal scores go to the lower node id, so there is never a tie.
|
|
121
|
+
|
|
122
|
+
### Replicas across failure domains
|
|
123
|
+
|
|
124
|
+
The second number on each node is its failure domain: a rack, an availability zone, a power circuit, whatever you don't want to lose twice. `replicas` returns three slots, highest score first, and skips any node whose domain is already represented.
|
|
125
|
+
|
|
126
|
+
```ruby
|
|
127
|
+
nodes = [[10, 1], [20, 1], [30, 2], [40, 2]]
|
|
128
|
+
PlacementMachines.replicas(42, nodes) # => [30, 20, nil]
|
|
129
|
+
|
|
130
|
+
PlacementMachines.replicas(7, [[10, 1]]) # => [10, nil, nil]
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
Two domains, two replicas, one empty slot. One node, one replica, two empty slots. The gem never fills a slot with a node that doesn't exist or a node that shares a domain with an earlier pick. An empty slot is information: you are under-replicated, and now you know by how much.
|
|
134
|
+
|
|
135
|
+
The first replica is always the rendezvous owner, so the two calls agree.
|
|
136
|
+
|
|
137
|
+
### The ring
|
|
138
|
+
|
|
139
|
+
If you already run a hash ring with virtual nodes, hand it the points. Each point is `[position, node_id]`, in any order. The owner is the first point clockwise from the key, wrapping around at the top.
|
|
140
|
+
|
|
141
|
+
```ruby
|
|
142
|
+
ring = [[100, 1], [500, 2], [900, 3]]
|
|
143
|
+
|
|
144
|
+
PlacementMachines.ring_owner(250, ring) # => 2
|
|
145
|
+
PlacementMachines.ring_owner(500, ring) # => 2, exact hit
|
|
146
|
+
PlacementMachines.ring_owner(950, ring) # => 1, wrapped around
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
A node may appear at many positions. Two points on the same position go to the lower node id.
|
|
150
|
+
|
|
151
|
+
### Nothing to place
|
|
152
|
+
|
|
153
|
+
No nodes means no owner, not an error:
|
|
154
|
+
|
|
155
|
+
```ruby
|
|
156
|
+
PlacementMachines.rendezvous_owner(1, []) # => nil
|
|
157
|
+
PlacementMachines.replicas(1, []) # => [nil, nil, nil]
|
|
158
|
+
PlacementMachines.ring_owner(1, []) # => nil
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
## The Rules of the Tender
|
|
162
|
+
|
|
163
|
+
- **Everything is an Integer** in `0..2**64-1`: keys, node ids, domains, ring positions. Floats raise `TypeError`, negatives and overflow raise `RangeError`. Objects that merely respond to `to_int` are not integers.
|
|
164
|
+
- **Nodes are `[id, domain]` pairs**, ring points are `[position, id]` pairs, each a real two-element Array. Anything else raises.
|
|
165
|
+
- **Node ids are unique.** The same id twice in one list raises `ArgumentError`, even with different domains.
|
|
166
|
+
- **Lists have ceilings.** At most 64 nodes and 1024 ring points per call. Over that raises `ArgumentError`.
|
|
167
|
+
- **Inputs are never touched.** Pass frozen arrays if you like. Results are fresh arrays you own.
|
|
168
|
+
- **Three replicas, always three slots.** The count is fixed and unfilled slots are `nil`.
|
|
169
|
+
|
|
170
|
+
## What You Think You're Doing vs Reality
|
|
171
|
+
|
|
172
|
+
### You Think: "Three replicas means I can lose two nodes."
|
|
173
|
+
### Reality: Three replicas on one rack means you can lose one power strip.
|
|
174
|
+
|
|
175
|
+
Spreading copies across nodes protects you from a node dying. It does nothing when the thing that dies is the rack, the zone, or the circuit all three nodes share. The domain is how you tell the placer what fails together. Give every node the same domain and `replicas` will honestly hand you one copy and two empty slots, because that is what you actually have.
|
|
176
|
+
|
|
177
|
+
## What's Yours
|
|
178
|
+
|
|
179
|
+
The placer decides where a key should live. It does not know which nodes are alive, so the list you pass in has to be your real, current membership, and every machine that calls it has to pass the same list or they will disagree. Moving data when ownership changes, repairing replicas, and choosing how keys and ring positions are hashed are all yours.
|
|
180
|
+
|
|
181
|
+
## License
|
|
182
|
+
|
|
183
|
+
MIT License. See [LICENSE.txt](LICENSE.txt) for details.
|
|
184
|
+
|
|
185
|
+
**Remember: `key % nodes.size` is not a placement strategy. It's a scheduled outage with extra steps.**
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
## [0.2.2](https://github.com/seuros/placement_machines/compare/placement-machines-v0.2.1...placement-machines-v0.2.2) (2026-11-06)
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
### Bug Fixes
|
|
7
|
+
|
|
8
|
+
* crates.io keywords and categories for the core ([997f0d7](https://github.com/seuros/placement_machines/commit/997f0d70e6292afdf3212730e3a1ac2e4662b86e))
|
|
9
|
+
|
|
10
|
+
## [0.2.1](https://github.com/seuros/placement_machines/compare/placement-machines-v0.2.0...placement-machines-v0.2.1) (2026-11-06)
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
### Bug Fixes
|
|
14
|
+
|
|
15
|
+
* keep test sources out of the packaged gem and crate ([18a75e8](https://github.com/seuros/placement_machines/commit/18a75e8cdf890fcff09732c53191b957db3fd43d))
|
|
16
|
+
|
|
17
|
+
## [0.2.0](https://github.com/seuros/placement_machines/compare/placement-machines-v0.1.0...placement-machines-v0.2.0) (2026-11-06)
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
### Features
|
|
21
|
+
|
|
22
|
+
* initial extraction ([eb26a4d](https://github.com/seuros/placement_machines/commit/eb26a4d14c4ee79cbd0f6afc8edbd8b55e598bae))
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Abdelkader Boudih
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
# placement-machines
|
|
2
|
+
|
|
3
|
+
> **AMS Karger**, NCC-4213, *Karger*-class navigation tender.
|
|
4
|
+
|
|
5
|
+
Engine-room flight manual: chart deterministic destinations and replica berths.
|
|
6
|
+
|
|
7
|
+
Allocation-free `no_std` placement over stable caller-supplied numeric key hashes
|
|
8
|
+
and node identities. Rendezvous scores use a documented SplitMix64-style mixer,
|
|
9
|
+
not a cryptographic hash. Do not use these scores for adversarial admission or
|
|
10
|
+
security decisions. Ties favor lower node IDs, independent of input order.
|
|
11
|
+
|
|
12
|
+
Replica selection requires unique node IDs and chooses at most one node in each
|
|
13
|
+
failure domain. Missing replicas are explicit `None` values, never repeated nodes.
|
|
14
|
+
Weights, fallback within an exhausted domain, and topology hierarchies are not
|
|
15
|
+
implemented. Ring lookup accepts caller-generated virtual points, including
|
|
16
|
+
wraparound; ring construction and hash-version agreement are external contracts.
|
|
17
|
+
Placement does not define replica membership changes or consensus quorums.
|
|
18
|
+
|
|
19
|
+
```rust
|
|
20
|
+
use placement_machines::{Node, NodeId, replicas};
|
|
21
|
+
let nodes = [
|
|
22
|
+
Node { id: NodeId(1), domain: 10 },
|
|
23
|
+
Node { id: NodeId(2), domain: 20 },
|
|
24
|
+
];
|
|
25
|
+
# fn example(nodes: &[Node]) -> Option<()> {
|
|
26
|
+
let selected = replicas::<2>(42, nodes)?;
|
|
27
|
+
assert!(selected[0].is_some());
|
|
28
|
+
assert!(selected[1].is_some());
|
|
29
|
+
assert_ne!(selected[0], selected[1]);
|
|
30
|
+
# Some(())
|
|
31
|
+
# }
|
|
32
|
+
# assert_eq!(example(&nodes), Some(()));
|
|
33
|
+
```
|
|
Binary file
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
module PlacementMachines
|
|
2
|
+
VERSION: String
|
|
3
|
+
REPLICA_COUNT: Integer
|
|
4
|
+
MAX_NODES: Integer
|
|
5
|
+
MAX_RING_POINTS: Integer
|
|
6
|
+
|
|
7
|
+
def self.score: (Integer key, Integer node_id) -> Integer
|
|
8
|
+
def self.replicas: (Integer key, Array[[Integer, Integer]] nodes) -> Array[Integer?]
|
|
9
|
+
def self.rendezvous_owner: (Integer key, Array[[Integer, Integer]] nodes) -> Integer?
|
|
10
|
+
def self.ring_owner: (Integer key, Array[[Integer, Integer]] points) -> Integer?
|
|
11
|
+
end
|
metadata
ADDED
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: placement_machines
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
version: 0.1.4
|
|
5
|
+
platform: aarch64-linux
|
|
6
|
+
authors:
|
|
7
|
+
- Abdelkader Boudih
|
|
8
|
+
autorequire:
|
|
9
|
+
bindir: bin
|
|
10
|
+
cert_chain: []
|
|
11
|
+
date: 2026-11-06 00:00:00.000000000 Z
|
|
12
|
+
dependencies: []
|
|
13
|
+
description: A thin binding to the no_std placement-machines Rust core. Provides deterministic
|
|
14
|
+
rendezvous scores, ring ownership and distinct-domain replica choices without membership
|
|
15
|
+
services.
|
|
16
|
+
email:
|
|
17
|
+
- oss@seuros.com
|
|
18
|
+
executables: []
|
|
19
|
+
extensions: []
|
|
20
|
+
extra_rdoc_files: []
|
|
21
|
+
files:
|
|
22
|
+
- CHANGELOG.md
|
|
23
|
+
- LICENSE.txt
|
|
24
|
+
- README.md
|
|
25
|
+
- ext/placement_machines_native/core/CHANGELOG.md
|
|
26
|
+
- ext/placement_machines_native/core/LICENSE
|
|
27
|
+
- ext/placement_machines_native/core/README.md
|
|
28
|
+
- ext/placement_machines_native/extconf.rb
|
|
29
|
+
- lib/placement_machines.rb
|
|
30
|
+
- lib/placement_machines/version.rb
|
|
31
|
+
- lib/placement_machines_native/placement_machines_native.so
|
|
32
|
+
- sig/placement_machines.rbs
|
|
33
|
+
homepage: https://github.com/seuros/placement_machines
|
|
34
|
+
licenses:
|
|
35
|
+
- MIT
|
|
36
|
+
metadata:
|
|
37
|
+
source_code_uri: https://github.com/seuros/placement_machines
|
|
38
|
+
bug_tracker_uri: https://github.com/seuros/placement_machines/issues
|
|
39
|
+
rubygems_mfa_required: 'true'
|
|
40
|
+
cargo_crate_name: placement_machines_native
|
|
41
|
+
cargo_manifest_path: ext/placement_machines_native/ffi/Cargo.toml
|
|
42
|
+
post_install_message:
|
|
43
|
+
rdoc_options: []
|
|
44
|
+
require_paths:
|
|
45
|
+
- lib
|
|
46
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
47
|
+
requirements:
|
|
48
|
+
- - ">="
|
|
49
|
+
- !ruby/object:Gem::Version
|
|
50
|
+
version: '4.0'
|
|
51
|
+
- - "<"
|
|
52
|
+
- !ruby/object:Gem::Version
|
|
53
|
+
version: 4.1.dev
|
|
54
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
55
|
+
requirements:
|
|
56
|
+
- - ">="
|
|
57
|
+
- !ruby/object:Gem::Version
|
|
58
|
+
version: '0'
|
|
59
|
+
requirements: []
|
|
60
|
+
rubygems_version: 3.5.23
|
|
61
|
+
signing_key:
|
|
62
|
+
specification_version: 4
|
|
63
|
+
summary: Native rendezvous and ring placement decisions for Ruby
|
|
64
|
+
test_files: []
|