fair_machines 0.1.5-x86_64-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 +36 -0
- data/LICENSE.txt +21 -0
- data/README.md +216 -0
- data/ext/fair_machines_native/core/CHANGELOG.md +29 -0
- data/ext/fair_machines_native/core/README.md +21 -0
- data/ext/fair_machines_native/extconf.rb +3 -0
- data/lib/fair_machines/version.rb +5 -0
- data/lib/fair_machines.rb +4 -0
- data/lib/fair_machines_native/fair_machines_native.so +0 -0
- data/sig/fair_machines.rbs +18 -0
- metadata +62 -0
checksums.yaml
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
SHA256:
|
|
3
|
+
metadata.gz: c3c4faee52a632fb98e06653ec81b1c60b779479aeadc28bf6dde0fa8622762e
|
|
4
|
+
data.tar.gz: cea172d1a0cb2679bbd9ef3db6defbc2d62983082c282c5c688a4bcefc3fca34
|
|
5
|
+
SHA512:
|
|
6
|
+
metadata.gz: 58e781a081e6f3afe3a874cd07224a52cfd8be2ec1cb3cf8598c3d9910e7aa9581ca7c14dc0fc68ce019d2a4e3bc641b14933bcf9cf2944772620a1e2f4264e7
|
|
7
|
+
data.tar.gz: 797826760ab020f80c912e2cdfebcc255de233dd5df05e8643811eb718568e3c8b15f8d15f9018683bd355944b622a1569da6d54d4ad5a537d7f6d6f34fd85ec
|
data/CHANGELOG.md
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
## [0.1.5](https://github.com/seuros/fair_machines/compare/fair_machines/v0.1.4...fair_machines/v0.1.5) (2026-11-07)
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
### Features
|
|
7
|
+
|
|
8
|
+
* add KeyedFair hashed-lane budgets ([33d8f62](https://github.com/seuros/fair_machines/commit/33d8f62d6c8f8e8ad8a9bc759dffbdfa8b7a2706))
|
|
9
|
+
|
|
10
|
+
## [0.1.4](https://github.com/seuros/fair_machines/compare/fair_machines/v0.1.3...fair_machines/v0.1.4) (2026-11-06)
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
### Bug Fixes
|
|
14
|
+
|
|
15
|
+
* share positive count parsing in tenants ffi ([85f970b](https://github.com/seuros/fair_machines/commit/85f970b7e7002248032c25fc36ad03b1b96ca56c))
|
|
16
|
+
|
|
17
|
+
## [0.1.3](https://github.com/seuros/fair_machines/compare/fair_machines/v0.1.2...fair_machines/v0.1.3) (2026-11-06)
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
### Bug Fixes
|
|
21
|
+
|
|
22
|
+
* crates.io keywords and categories for the core ([c9e496f](https://github.com/seuros/fair_machines/commit/c9e496fca380c51529e965ce4c8158f5210ed258))
|
|
23
|
+
|
|
24
|
+
## [0.1.2](https://github.com/seuros/fair_machines/compare/fair_machines/v0.1.1...fair_machines/v0.1.2) (2026-11-06)
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
### Bug Fixes
|
|
28
|
+
|
|
29
|
+
* keep test sources out of the packaged gem and crate ([58a408b](https://github.com/seuros/fair_machines/commit/58a408ba6727d04d8517a6b76c47b90e381203e3))
|
|
30
|
+
|
|
31
|
+
## [0.1.1](https://github.com/seuros/fair_machines/compare/fair_machines-v0.1.0...fair_machines/v0.1.1) (2026-11-06)
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
### Features
|
|
35
|
+
|
|
36
|
+
* initial extraction ([3c25da7](https://github.com/seuros/fair_machines/commit/3c25da7ef06236f290cef43e29ce5519cbf69aff))
|
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,216 @@
|
|
|
1
|
+
# FairMachines
|
|
2
|
+
|
|
3
|
+
> One tenant with ten thousand jobs should not make every other tenant wait ten thousand jobs. ⚖️
|
|
4
|
+
|
|
5
|
+
Cost-aware fair scheduling for Ruby. You give each tenant a budget for how much work it may have queued and a share of each dispatch round. `TenantFair` tells you whose job goes next, charges the tenant for its cost, and refuses to admit work past the budget. The loud tenant still gets served. It just doesn't get served *instead of* everyone else.
|
|
6
|
+
|
|
7
|
+
## Quick Start
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
gem 'fair_machines'
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
```ruby
|
|
14
|
+
# Two tenants. Each may have 100 units of work queued; each gets 2 units per round.
|
|
15
|
+
fair = FairMachines::TenantFair.new(capacities: [100, 100], quanta: [2, 2])
|
|
16
|
+
|
|
17
|
+
# Enterprise tenant enqueues ten PDF exports at cost 5. Small tenant queues five emails at cost 1.
|
|
18
|
+
10.times { fair.admit(tenant: 0, cost: 5) }
|
|
19
|
+
5.times { fair.admit(tenant: 1, cost: 1) }
|
|
20
|
+
|
|
21
|
+
# Each round, tell the scheduler the cost at the head of every tenant's queue.
|
|
22
|
+
# It answers with the tenant whose head runs now. You pop that job and run it.
|
|
23
|
+
fair.select([5, 1]) # => 1 the email goes first
|
|
24
|
+
fair.select([5, 1]) # => 1
|
|
25
|
+
fair.select([5, 1]) # => 1
|
|
26
|
+
fair.select([5, 1]) # => 1
|
|
27
|
+
fair.select([5, 1]) # => 0 the PDF has saved up enough share
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
Tenants are indices into the arrays you passed in. Heads are the cost of each tenant's next job, or `nil` when that tenant has nothing waiting.
|
|
31
|
+
|
|
32
|
+
## A Message to the Resistance
|
|
33
|
+
|
|
34
|
+
So you built the job queue. Priorities, retries, a dashboard. Then your biggest customer clicked "export everything" and ten thousand PDF jobs landed in the queue at once, and for the next forty minutes nobody else's password reset email went out.
|
|
35
|
+
|
|
36
|
+
You added a second queue for the big customer. Then a third for the next big customer. Then a priority flag that every customer's jobs somehow ended up setting. None of it fixes the actual problem, which is that the queue has no idea what a tenant is, let alone how much of the worker pool that tenant has already consumed.
|
|
37
|
+
|
|
38
|
+
Fairness is two numbers per tenant. How much work may this tenant have waiting at once, and how big a slice of each round does it get. With those, a ten-thousand-job export gets a fixed share of the workers and the password reset goes out in seconds, because the small tenant's slice is sitting there unused.
|
|
39
|
+
|
|
40
|
+
This gem keeps those two numbers honest. You keep the queues.
|
|
41
|
+
|
|
42
|
+
## Chapter 1: Stardate 2025.186, the AMS Shreedhar
|
|
43
|
+
|
|
44
|
+
> **AMS Shreedhar**, NCC-4207, *Shreedhar*-class convoy escort.
|
|
45
|
+
> The flagship does not get to spend every tenant's fuel allowance.
|
|
46
|
+
|
|
47
|
+
The escort doesn't fly the convoy. It holds the fuel ledger: how much each ship may have on request at once, and how much is pumped to each per cycle. A ship with a loud captain and a long manifest still draws from its own allowance, charged by the actual mass of what it asked for. A dinghy asking for one pallet gets its pallet on the next cycle.
|
|
48
|
+
|
|
49
|
+
Somewhere on the bridge, a junior officer suggests:
|
|
50
|
+
|
|
51
|
+
```ruby
|
|
52
|
+
def next_job
|
|
53
|
+
queue.shift # it's a queue, first come first served, that's fair, right?
|
|
54
|
+
end
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
"That," says the convoy master, "is how a dinghy starves behind a freighter."
|
|
58
|
+
|
|
59
|
+
## The Flight Plan
|
|
60
|
+
|
|
61
|
+
Each tenant is either idle or has cost queued. Admission moves cost in, dispatch and cancellation move it out, and a full budget turns new work away.
|
|
62
|
+
|
|
63
|
+
```mermaid
|
|
64
|
+
stateDiagram-v2
|
|
65
|
+
[*] --> idle
|
|
66
|
+
idle --> queued: admit within budget
|
|
67
|
+
queued --> queued: admit within budget
|
|
68
|
+
queued --> queued: admit over budget, refused
|
|
69
|
+
queued --> queued: select, head cost charged
|
|
70
|
+
queued --> idle: last head dispatched or cancelled
|
|
71
|
+
queued --> queued: cancel, cost returned
|
|
72
|
+
idle --> [*]
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
And the afternoon the enterprise tenant clicked "export everything":
|
|
76
|
+
|
|
77
|
+
```mermaid
|
|
78
|
+
sequenceDiagram
|
|
79
|
+
participant E as Enterprise (tenant 0)
|
|
80
|
+
participant F as TenantFair
|
|
81
|
+
participant S as Small shop (tenant 1)
|
|
82
|
+
participant W as Workers
|
|
83
|
+
|
|
84
|
+
E->>F: admit cost 5, ten times
|
|
85
|
+
F-->>E: true, true, true...
|
|
86
|
+
S->>F: admit cost 1
|
|
87
|
+
F-->>S: true
|
|
88
|
+
|
|
89
|
+
W->>F: select([5, 1])
|
|
90
|
+
F-->>W: tenant 1
|
|
91
|
+
Note over W: password reset goes out
|
|
92
|
+
|
|
93
|
+
W->>F: select([5, nil])
|
|
94
|
+
F-->>W: tenant 0
|
|
95
|
+
Note over W: first PDF renders
|
|
96
|
+
|
|
97
|
+
S->>F: admit cost 1
|
|
98
|
+
W->>F: select([5, 1])
|
|
99
|
+
F-->>W: tenant 1
|
|
100
|
+
Note over S: still seconds, not forty minutes
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
## How the Ledger Works
|
|
104
|
+
|
|
105
|
+
### Admit
|
|
106
|
+
|
|
107
|
+
Before a job is enqueued, ask whether the tenant has room for its cost. The answer is a plain boolean, and `false` means the job doesn't go on the queue.
|
|
108
|
+
|
|
109
|
+
```ruby
|
|
110
|
+
fair = FairMachines::TenantFair.new(capacities: [10, 5], quanta: [3, 1])
|
|
111
|
+
|
|
112
|
+
fair.admit(tenant: 1, cost: 5) # => true budget 5, now full
|
|
113
|
+
fair.admit(tenant: 1, cost: 1) # => false over budget
|
|
114
|
+
fair.admit(tenant: 2, cost: 1) # => false no such tenant
|
|
115
|
+
fair.tenant_count # => 2
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
Over-budget is a refusal, not an error. Decide what to do with the job: reject it, hold it, tell the tenant.
|
|
119
|
+
|
|
120
|
+
### Select
|
|
121
|
+
|
|
122
|
+
Each round, pass the head cost of every tenant's queue. You get back the tenant whose head runs now, or `nil` when nobody is eligible. The chosen tenant is charged that cost against its budget, so the room comes back as the work drains.
|
|
123
|
+
|
|
124
|
+
```ruby
|
|
125
|
+
fair = FairMachines::TenantFair.new(capacities: [10, 5], quanta: [3, 1])
|
|
126
|
+
fair.admit(tenant: 0, cost: 4)
|
|
127
|
+
fair.admit(tenant: 1, cost: 1)
|
|
128
|
+
|
|
129
|
+
fair.select([4, 1]) # => 1
|
|
130
|
+
fair.select([4, nil]) # => 0
|
|
131
|
+
fair.select([4, nil]) # => nil tenant 0 has nothing left queued
|
|
132
|
+
fair.admit(tenant: 0, cost: 7) # => true the dispatched 4 freed its room
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
Heads have to be truthful. A head that costs more than the tenant has admitted is not eligible and `select` returns `nil` for it.
|
|
136
|
+
|
|
137
|
+
### Share is by cost, not by count
|
|
138
|
+
|
|
139
|
+
Quanta are how much cost each tenant may dispatch per round, with leftover carried to the next round. A tenant whose head is expensive waits until it has saved up. A tenant with cheap jobs runs one every round.
|
|
140
|
+
|
|
141
|
+
```ruby
|
|
142
|
+
fair = FairMachines::TenantFair.new(capacities: [50, 50], quanta: [2, 2])
|
|
143
|
+
fair.admit(tenant: 0, cost: 40) # heavy jobs at cost 4
|
|
144
|
+
fair.admit(tenant: 1, cost: 10) # light jobs at cost 1
|
|
145
|
+
|
|
146
|
+
Array.new(10) { fair.select([4, 1]) }
|
|
147
|
+
# => [1, 1, 0, 1, 1, 1, 1, 0, 1, 1]
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
Same quanta, same share of the pool, and the light tenant still dispatches four jobs for every one heavy job, because the heavy job costs four times as much.
|
|
151
|
+
|
|
152
|
+
Give one tenant a bigger quantum and it gets a bigger share of the pool outright:
|
|
153
|
+
|
|
154
|
+
```ruby
|
|
155
|
+
fair = FairMachines::TenantFair.new(capacities: [20, 20], quanta: [3, 1])
|
|
156
|
+
fair.admit(tenant: 0, cost: 20)
|
|
157
|
+
fair.admit(tenant: 1, cost: 20)
|
|
158
|
+
|
|
159
|
+
Array.new(8) { fair.select([1, 1]) }
|
|
160
|
+
# => [0, 0, 0, 1, 0, 0, 0, 1]
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
### Cancel
|
|
164
|
+
|
|
165
|
+
A job that leaves the queue without being dispatched has to give its cost back, or the tenant's budget leaks until nothing is admitted.
|
|
166
|
+
|
|
167
|
+
```ruby
|
|
168
|
+
fair = FairMachines::TenantFair.new(capacities: [10, 5], quanta: [3, 1])
|
|
169
|
+
fair.admit(tenant: 0, cost: 4)
|
|
170
|
+
|
|
171
|
+
fair.cancel(tenant: 0, cost: 4) # => true
|
|
172
|
+
fair.cancel(tenant: 0, cost: 1) # => false nothing left to cancel
|
|
173
|
+
fair.select([4, nil]) # => nil the queue is empty, and so is the ledger
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
Cancelling more than was admitted is refused, and the ledger is untouched.
|
|
177
|
+
|
|
178
|
+
### Too many tenants to name: `KeyedFair`
|
|
179
|
+
|
|
180
|
+
Sixteen tenant slots is a crew roster. Orgs, API keys and IP prefixes are a starfield. `KeyedFair` hashes each key onto a fixed set of lanes, every lane with its own budget and no queue. A loud org fills its lane and gets `nil`; the other lanes don't notice. Keys that hash to the same lane share its budget, so pick enough lanes that a collision is rare.
|
|
181
|
+
|
|
182
|
+
```ruby
|
|
183
|
+
fair = FairMachines::KeyedFair.new(lanes: 64, capacity: 8)
|
|
184
|
+
|
|
185
|
+
lane = fair.admit(hash: org_id.hash, cost: 1) # => 17, or nil when that lane is full
|
|
186
|
+
# ... do the work ...
|
|
187
|
+
fair.release(lane:, cost: 1) # => true
|
|
188
|
+
```
|
|
189
|
+
|
|
190
|
+
`lanes:` is a power of two from 1 to 256. Any Integer works as `hash:`, negatives included, so `key.hash` goes straight in.
|
|
191
|
+
|
|
192
|
+
## The Rules of the Escort
|
|
193
|
+
|
|
194
|
+
- **1 to 16 tenants**, set by the length of `capacities:`. `quanta:` must be the same length. Empty, longer, or mismatched arrays raise `ArgumentError`.
|
|
195
|
+
- **Capacities, quanta, and costs are positive Integers** below `2**32`. Zero is `ArgumentError`, negative or too large is `RangeError`, floats and strings are `TypeError`. Nothing is rounded for you.
|
|
196
|
+
- **Heads are exactly `tenant_count` entries**, each a positive Integer or `nil`. The wrong length or a zero raises `ArgumentError`, and a float raises `TypeError`, before anything changes.
|
|
197
|
+
- **Bad tenant indices don't raise.** `admit` and `cancel` with an unknown tenant return `false`.
|
|
198
|
+
- **Frozen means frozen.** `freeze` the scheduler and `admit`, `cancel`, and `select` raise `FrozenError`. `tenant_count` still answers.
|
|
199
|
+
- **A tenant that drains to empty loses its saved-up share.** Leftover quantum doesn't accumulate for a tenant with nothing queued.
|
|
200
|
+
|
|
201
|
+
## What You Think You're Doing vs Reality
|
|
202
|
+
|
|
203
|
+
### You Think: "Round robin between tenants. Everybody gets a turn."
|
|
204
|
+
### Reality: A turn is not a share. One tenant's turn is a thumbnail, another's is a 40-page PDF.
|
|
205
|
+
|
|
206
|
+
Taking turns by job count hands the pool to whoever submits the heaviest jobs. Fairness has to be measured in what a job costs, which is why every call here carries a cost and why the scheduler charges it before it hands out the next turn.
|
|
207
|
+
|
|
208
|
+
## What's Yours
|
|
209
|
+
|
|
210
|
+
The scheduler is a ledger, not a queue. You keep a queue per tenant, call `admit` before you push, hand `select` the cost at the head of each queue, and pop the job from the tenant it names. Your cost estimates have to be honest, because the ledger believes them. And when a job dies on the floor without running, call `cancel`, or the tenant pays for it forever.
|
|
211
|
+
|
|
212
|
+
## License
|
|
213
|
+
|
|
214
|
+
MIT License. See [LICENSE.txt](LICENSE.txt) for details.
|
|
215
|
+
|
|
216
|
+
**Remember: The export job will finish. The question is whether the password reset has to wait for it.**
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
## [0.3.0](https://github.com/seuros/fair_machines/compare/fair-machines-v0.2.2...fair-machines-v0.3.0) (2026-11-07)
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
### Features
|
|
7
|
+
|
|
8
|
+
* add KeyedFair hashed-lane budgets ([33d8f62](https://github.com/seuros/fair_machines/commit/33d8f62d6c8f8e8ad8a9bc759dffbdfa8b7a2706))
|
|
9
|
+
|
|
10
|
+
## [0.2.2](https://github.com/seuros/fair_machines/compare/fair-machines-v0.2.1...fair-machines-v0.2.2) (2026-11-06)
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
### Bug Fixes
|
|
14
|
+
|
|
15
|
+
* crates.io keywords and categories for the core ([c9e496f](https://github.com/seuros/fair_machines/commit/c9e496fca380c51529e965ce4c8158f5210ed258))
|
|
16
|
+
|
|
17
|
+
## [0.2.1](https://github.com/seuros/fair_machines/compare/fair-machines-v0.2.0...fair-machines-v0.2.1) (2026-11-06)
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
### Bug Fixes
|
|
21
|
+
|
|
22
|
+
* keep test sources out of the packaged gem and crate ([58a408b](https://github.com/seuros/fair_machines/commit/58a408ba6727d04d8517a6b76c47b90e381203e3))
|
|
23
|
+
|
|
24
|
+
## [0.2.0](https://github.com/seuros/fair_machines/compare/fair-machines-v0.1.0...fair-machines-v0.2.0) (2026-11-06)
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
### Features
|
|
28
|
+
|
|
29
|
+
* initial extraction ([3c25da7](https://github.com/seuros/fair_machines/commit/3c25da7ef06236f290cef43e29ce5519cbf69aff))
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
# fair-machines
|
|
2
|
+
|
|
3
|
+
> **AMS Shreedhar**, NCC-4207, *Shreedhar*-class convoy escort.
|
|
4
|
+
|
|
5
|
+
Engine-room flight manual: share departure capacity without starving smaller crews.
|
|
6
|
+
|
|
7
|
+
Allocation-free `no_std` tenant isolation: independent cost-credit budgets for
|
|
8
|
+
admission, with DRR selection delegated to `priority-machines`. This is not a
|
|
9
|
+
second implementation of DRR. Admission accounts queued cost; selection returns
|
|
10
|
+
that cost to the tenant budget. Running work after selection is not bounded here.
|
|
11
|
+
|
|
12
|
+
Adapters own queues, present truthful head costs, and serialize admission,
|
|
13
|
+
selection, and cancellation. Aggregate credits do not identify individual jobs.
|
|
14
|
+
`cancel` returns admitted cost; when a tenant drains its DRR visit is reset.
|
|
15
|
+
Fairness assumes continuously backlogged tenants and meaningful cost estimates.
|
|
16
|
+
`KeyedFair<LANES>` covers an open tenant set: caller-hashed keys map onto fixed
|
|
17
|
+
lanes with independent credit budgets and no scheduler. Keys sharing a lane share
|
|
18
|
+
its budget.
|
|
19
|
+
|
|
20
|
+
Tenant hierarchies, per-flow WFQ, runtime preemption, and strict latency/starvation
|
|
21
|
+
bounds are not implemented.
|
|
Binary file
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
module FairMachines
|
|
2
|
+
VERSION: String
|
|
3
|
+
class TenantFair
|
|
4
|
+
def self.new: (capacities: Array[Integer], quanta: Array[Integer]) -> instance
|
|
5
|
+
def admit: (tenant: Integer, cost: Integer) -> bool
|
|
6
|
+
def cancel: (tenant: Integer, cost: Integer) -> bool
|
|
7
|
+
def select: (Array[Integer?] heads) -> Integer?
|
|
8
|
+
def tenant_count: () -> Integer
|
|
9
|
+
end
|
|
10
|
+
class KeyedFair
|
|
11
|
+
def self.new: (lanes: Integer, capacity: Integer) -> instance
|
|
12
|
+
def admit: (hash: Integer, cost: Integer) -> Integer?
|
|
13
|
+
def release: (lane: Integer, cost: Integer) -> bool
|
|
14
|
+
def in_flight: (Integer lane) -> Integer
|
|
15
|
+
def lane: (Integer hash) -> Integer
|
|
16
|
+
def lane_count: () -> Integer
|
|
17
|
+
end
|
|
18
|
+
end
|
metadata
ADDED
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: fair_machines
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
version: 0.1.5
|
|
5
|
+
platform: x86_64-linux
|
|
6
|
+
authors:
|
|
7
|
+
- Abdelkader Boudih
|
|
8
|
+
autorequire:
|
|
9
|
+
bindir: bin
|
|
10
|
+
cert_chain: []
|
|
11
|
+
date: 2026-11-07 00:00:00.000000000 Z
|
|
12
|
+
dependencies: []
|
|
13
|
+
description: Initial unstable Ruby adapter to the no_std fair-machines Rust core.
|
|
14
|
+
Local source builds require unpublished path-only flow and priority siblings.
|
|
15
|
+
email:
|
|
16
|
+
- oss@seuros.com
|
|
17
|
+
executables: []
|
|
18
|
+
extensions: []
|
|
19
|
+
extra_rdoc_files: []
|
|
20
|
+
files:
|
|
21
|
+
- CHANGELOG.md
|
|
22
|
+
- LICENSE.txt
|
|
23
|
+
- README.md
|
|
24
|
+
- ext/fair_machines_native/core/CHANGELOG.md
|
|
25
|
+
- ext/fair_machines_native/core/README.md
|
|
26
|
+
- ext/fair_machines_native/extconf.rb
|
|
27
|
+
- lib/fair_machines.rb
|
|
28
|
+
- lib/fair_machines/version.rb
|
|
29
|
+
- lib/fair_machines_native/fair_machines_native.so
|
|
30
|
+
- sig/fair_machines.rbs
|
|
31
|
+
homepage: https://github.com/seuros/fair_machines
|
|
32
|
+
licenses:
|
|
33
|
+
- MIT
|
|
34
|
+
metadata:
|
|
35
|
+
source_code_uri: https://github.com/seuros/fair_machines
|
|
36
|
+
bug_tracker_uri: https://github.com/seuros/fair_machines/issues
|
|
37
|
+
rubygems_mfa_required: 'true'
|
|
38
|
+
cargo_crate_name: fair_machines_native
|
|
39
|
+
cargo_manifest_path: ext/fair_machines_native/ffi/Cargo.toml
|
|
40
|
+
post_install_message:
|
|
41
|
+
rdoc_options: []
|
|
42
|
+
require_paths:
|
|
43
|
+
- lib
|
|
44
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
45
|
+
requirements:
|
|
46
|
+
- - ">="
|
|
47
|
+
- !ruby/object:Gem::Version
|
|
48
|
+
version: '4.0'
|
|
49
|
+
- - "<"
|
|
50
|
+
- !ruby/object:Gem::Version
|
|
51
|
+
version: 4.1.dev
|
|
52
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
53
|
+
requirements:
|
|
54
|
+
- - ">="
|
|
55
|
+
- !ruby/object:Gem::Version
|
|
56
|
+
version: '0'
|
|
57
|
+
requirements: []
|
|
58
|
+
rubygems_version: 3.5.23
|
|
59
|
+
signing_key:
|
|
60
|
+
specification_version: 4
|
|
61
|
+
summary: Native per-tenant admission budgets and deficit fairness
|
|
62
|
+
test_files: []
|