lease_machines 0.1.6
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 +43 -0
- data/Cargo.toml +26 -0
- data/LICENSE.txt +21 -0
- data/README.md +236 -0
- data/ext/lease_machines_native/core/CHANGELOG.md +36 -0
- data/ext/lease_machines_native/core/Cargo.toml +13 -0
- data/ext/lease_machines_native/core/README.md +172 -0
- data/ext/lease_machines_native/core/src/authority.rs +287 -0
- data/ext/lease_machines_native/core/src/fencing.rs +67 -0
- data/ext/lease_machines_native/core/src/holder.rs +193 -0
- data/ext/lease_machines_native/core/src/lease.rs +76 -0
- data/ext/lease_machines_native/core/src/lib.rs +12 -0
- data/ext/lease_machines_native/extconf.rb +2 -0
- data/ext/lease_machines_native/ffi/Cargo.toml +17 -0
- data/ext/lease_machines_native/ffi/extconf.rb +12 -0
- data/ext/lease_machines_native/ffi/src/bindings.rs +218 -0
- data/ext/lease_machines_native/ffi/src/lib.rs +2 -0
- data/lib/lease_machines/version.rb +4 -0
- data/lib/lease_machines.rb +28 -0
- data/sig/lease_machines.rbs +30 -0
- metadata +86 -0
|
@@ -0,0 +1,218 @@
|
|
|
1
|
+
use std::{cell::RefCell, time::Duration};
|
|
2
|
+
|
|
3
|
+
use lease_machines::{
|
|
4
|
+
Checkpoint, Decision, Fence, FencingToken, Lease, LeaseAuthority, OwnerId, Rejection,
|
|
5
|
+
};
|
|
6
|
+
use magnus::typed_data::Obj;
|
|
7
|
+
use magnus::{Error, Integer, RArray, Ruby, TryConvert, Value, function, method, prelude::*};
|
|
8
|
+
|
|
9
|
+
#[derive(Clone, Copy)]
|
|
10
|
+
struct Count(u64);
|
|
11
|
+
|
|
12
|
+
impl TryConvert for Count {
|
|
13
|
+
fn try_convert(value: Value) -> Result<Self, Error> {
|
|
14
|
+
if Integer::from_value(value).is_none() {
|
|
15
|
+
return Err(Error::new(
|
|
16
|
+
Ruby::get_with(value).exception_type_error(),
|
|
17
|
+
"expected an Integer",
|
|
18
|
+
));
|
|
19
|
+
}
|
|
20
|
+
u64::try_convert(value).map(Self)
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
fn duration(ruby: &Ruby, seconds: f64) -> Result<Duration, Error> {
|
|
25
|
+
Duration::try_from_secs_f64(seconds).map_err(|_| {
|
|
26
|
+
Error::new(
|
|
27
|
+
ruby.exception_arg_error(),
|
|
28
|
+
"time must be finite, nonnegative, and representable",
|
|
29
|
+
)
|
|
30
|
+
})
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
fn token(ruby: &Ruby, value: Count) -> Result<FencingToken, Error> {
|
|
34
|
+
FencingToken::new(value.0)
|
|
35
|
+
.ok_or_else(|| Error::new(ruby.exception_arg_error(), "token must be positive"))
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
#[magnus::wrap(class = "LeaseMachines::Lease", free_immediately, size)]
|
|
39
|
+
struct Grant(Lease);
|
|
40
|
+
|
|
41
|
+
impl Grant {
|
|
42
|
+
const fn owner(&self) -> u64 {
|
|
43
|
+
self.0.owner().value()
|
|
44
|
+
}
|
|
45
|
+
const fn token(&self) -> u64 {
|
|
46
|
+
self.0.token().value()
|
|
47
|
+
}
|
|
48
|
+
const fn expires_at(&self) -> f64 {
|
|
49
|
+
self.0.expires_at().as_secs_f64()
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
#[magnus::wrap(class = "LeaseMachines::Checkpoint", free_immediately, size)]
|
|
54
|
+
struct Saved(Checkpoint);
|
|
55
|
+
|
|
56
|
+
impl Saved {
|
|
57
|
+
fn last_issued(&self) -> Option<u64> {
|
|
58
|
+
self.0.last_issued().map(FencingToken::value)
|
|
59
|
+
}
|
|
60
|
+
const fn observed(&self) -> f64 {
|
|
61
|
+
self.0.observed().as_secs_f64()
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
fn decision(ruby: &Ruby, value: Decision) -> Result<RArray, Error> {
|
|
66
|
+
let result = ruby.ary_new();
|
|
67
|
+
match value {
|
|
68
|
+
Decision::Granted(lease) | Decision::Renewed(lease) => {
|
|
69
|
+
let name = if matches!(value, Decision::Granted(_)) {
|
|
70
|
+
"granted"
|
|
71
|
+
} else {
|
|
72
|
+
"renewed"
|
|
73
|
+
};
|
|
74
|
+
result.push(ruby.to_symbol(name))?;
|
|
75
|
+
result.push(Grant(lease))?;
|
|
76
|
+
result.push(())?;
|
|
77
|
+
}
|
|
78
|
+
Decision::Released => {
|
|
79
|
+
result.push(ruby.to_symbol("released"))?;
|
|
80
|
+
result.push(())?;
|
|
81
|
+
result.push(())?;
|
|
82
|
+
}
|
|
83
|
+
Decision::Rejected(reason) => {
|
|
84
|
+
let name = match reason {
|
|
85
|
+
Rejection::Occupied => "occupied",
|
|
86
|
+
Rejection::NoLease => "no_lease",
|
|
87
|
+
Rejection::WrongOwner => "wrong_owner",
|
|
88
|
+
Rejection::StaleToken => "stale_token",
|
|
89
|
+
Rejection::Expired => "expired",
|
|
90
|
+
Rejection::ClockRegressed => "clock_regressed",
|
|
91
|
+
Rejection::InvalidTtl => "invalid_ttl",
|
|
92
|
+
Rejection::TokenExhausted => "token_exhausted",
|
|
93
|
+
_ => {
|
|
94
|
+
return Err(Error::new(
|
|
95
|
+
ruby.exception_runtime_error(),
|
|
96
|
+
"unsupported rejection",
|
|
97
|
+
));
|
|
98
|
+
}
|
|
99
|
+
};
|
|
100
|
+
result.push(ruby.to_symbol("rejected"))?;
|
|
101
|
+
result.push(())?;
|
|
102
|
+
result.push(ruby.to_symbol(name))?;
|
|
103
|
+
}
|
|
104
|
+
_ => {
|
|
105
|
+
return Err(Error::new(
|
|
106
|
+
ruby.exception_runtime_error(),
|
|
107
|
+
"unsupported decision",
|
|
108
|
+
));
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
Ok(result)
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
#[magnus::wrap(class = "LeaseMachines::Authority", free_immediately, size)]
|
|
115
|
+
struct Authority(RefCell<LeaseAuthority>);
|
|
116
|
+
|
|
117
|
+
impl Authority {
|
|
118
|
+
const fn new() -> Self {
|
|
119
|
+
Self(RefCell::new(LeaseAuthority::new()))
|
|
120
|
+
}
|
|
121
|
+
const fn restore(saved: &Saved) -> Self {
|
|
122
|
+
Self(RefCell::new(LeaseAuthority::restore(saved.0)))
|
|
123
|
+
}
|
|
124
|
+
fn checkpoint(&self) -> Saved {
|
|
125
|
+
Saved(self.0.borrow().checkpoint())
|
|
126
|
+
}
|
|
127
|
+
fn acquire(
|
|
128
|
+
ruby: &Ruby,
|
|
129
|
+
this: Obj<Self>,
|
|
130
|
+
owner: Count,
|
|
131
|
+
now: f64,
|
|
132
|
+
ttl: f64,
|
|
133
|
+
) -> Result<RArray, Error> {
|
|
134
|
+
this.check_frozen()?;
|
|
135
|
+
let (now, ttl) = (duration(ruby, now)?, duration(ruby, ttl)?);
|
|
136
|
+
let value = this.0.borrow_mut().acquire(OwnerId::new(owner.0), now, ttl);
|
|
137
|
+
decision(ruby, value)
|
|
138
|
+
}
|
|
139
|
+
fn renew(
|
|
140
|
+
ruby: &Ruby,
|
|
141
|
+
this: Obj<Self>,
|
|
142
|
+
owner: Count,
|
|
143
|
+
value: Count,
|
|
144
|
+
now: f64,
|
|
145
|
+
ttl: f64,
|
|
146
|
+
) -> Result<RArray, Error> {
|
|
147
|
+
this.check_frozen()?;
|
|
148
|
+
let (value, now, ttl) = (
|
|
149
|
+
token(ruby, value)?,
|
|
150
|
+
duration(ruby, now)?,
|
|
151
|
+
duration(ruby, ttl)?,
|
|
152
|
+
);
|
|
153
|
+
let value = this
|
|
154
|
+
.0
|
|
155
|
+
.borrow_mut()
|
|
156
|
+
.renew(OwnerId::new(owner.0), value, now, ttl);
|
|
157
|
+
decision(ruby, value)
|
|
158
|
+
}
|
|
159
|
+
fn release(
|
|
160
|
+
ruby: &Ruby,
|
|
161
|
+
this: Obj<Self>,
|
|
162
|
+
owner: Count,
|
|
163
|
+
value: Count,
|
|
164
|
+
now: f64,
|
|
165
|
+
) -> Result<RArray, Error> {
|
|
166
|
+
this.check_frozen()?;
|
|
167
|
+
let (value, now) = (token(ruby, value)?, duration(ruby, now)?);
|
|
168
|
+
let value = this
|
|
169
|
+
.0
|
|
170
|
+
.borrow_mut()
|
|
171
|
+
.release(OwnerId::new(owner.0), value, now);
|
|
172
|
+
decision(ruby, value)
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
#[magnus::wrap(class = "LeaseMachines::Fence", free_immediately, size)]
|
|
177
|
+
struct Guard(RefCell<Fence>);
|
|
178
|
+
|
|
179
|
+
impl Guard {
|
|
180
|
+
fn new(ruby: &Ruby, highest: Option<Count>) -> Result<Self, Error> {
|
|
181
|
+
Ok(Self(RefCell::new(Fence::new(
|
|
182
|
+
highest.map(|v| token(ruby, v)).transpose()?,
|
|
183
|
+
))))
|
|
184
|
+
}
|
|
185
|
+
fn accept(ruby: &Ruby, this: Obj<Self>, value: Count) -> Result<bool, Error> {
|
|
186
|
+
this.check_frozen()?;
|
|
187
|
+
let value = token(ruby, value)?;
|
|
188
|
+
Ok(this.0.borrow_mut().accept(value))
|
|
189
|
+
}
|
|
190
|
+
fn highest(&self) -> Option<u64> {
|
|
191
|
+
self.0.borrow().highest().map(FencingToken::value)
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
#[magnus::init]
|
|
196
|
+
fn init(ruby: &Ruby) -> Result<(), Error> {
|
|
197
|
+
let module = ruby.define_module("LeaseMachines")?;
|
|
198
|
+
let grant = module.define_class("Lease", ruby.class_object())?;
|
|
199
|
+
grant.undef_default_alloc_func();
|
|
200
|
+
grant.define_method("owner", method!(Grant::owner, 0))?;
|
|
201
|
+
grant.define_method("token", method!(Grant::token, 0))?;
|
|
202
|
+
grant.define_method("expires_at", method!(Grant::expires_at, 0))?;
|
|
203
|
+
let saved = module.define_class("Checkpoint", ruby.class_object())?;
|
|
204
|
+
saved.undef_default_alloc_func();
|
|
205
|
+
saved.define_method("last_issued", method!(Saved::last_issued, 0))?;
|
|
206
|
+
saved.define_method("observed", method!(Saved::observed, 0))?;
|
|
207
|
+
let authority = module.define_class("Authority", ruby.class_object())?;
|
|
208
|
+
authority.define_singleton_method("new", function!(Authority::new, 0))?;
|
|
209
|
+
authority.define_singleton_method("restore", function!(Authority::restore, 1))?;
|
|
210
|
+
authority.define_method("checkpoint", method!(Authority::checkpoint, 0))?;
|
|
211
|
+
authority.define_method("_acquire", method!(Authority::acquire, 3))?;
|
|
212
|
+
authority.define_method("_renew", method!(Authority::renew, 4))?;
|
|
213
|
+
authority.define_method("_release", method!(Authority::release, 3))?;
|
|
214
|
+
let fence = module.define_class("Fence", ruby.class_object())?;
|
|
215
|
+
fence.define_singleton_method("_new", function!(Guard::new, 1))?;
|
|
216
|
+
fence.define_method("accept", method!(Guard::accept, 1))?;
|
|
217
|
+
fence.define_method("highest", method!(Guard::highest, 0))
|
|
218
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
require_relative 'lease_machines/version'
|
|
3
|
+
require 'lease_machines_native/lease_machines_native'
|
|
4
|
+
|
|
5
|
+
module LeaseMachines
|
|
6
|
+
Decision = Data.define(:status, :lease, :reason)
|
|
7
|
+
|
|
8
|
+
class Authority
|
|
9
|
+
def acquire(owner:, now:, ttl:)
|
|
10
|
+
Decision.new(*_acquire(owner, now, ttl))
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def renew(owner:, token:, now:, ttl:)
|
|
14
|
+
Decision.new(*_renew(owner, token, now, ttl))
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def release(owner:, token:, now:)
|
|
18
|
+
Decision.new(*_release(owner, token, now))
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
private :_acquire, :_renew, :_release
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
class Fence
|
|
25
|
+
def self.new(highest: nil) = _new(highest)
|
|
26
|
+
private_class_method :_new
|
|
27
|
+
end
|
|
28
|
+
end
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
module LeaseMachines
|
|
2
|
+
VERSION: String
|
|
3
|
+
class Lease
|
|
4
|
+
def owner: () -> Integer
|
|
5
|
+
def token: () -> Integer
|
|
6
|
+
def expires_at: () -> Float
|
|
7
|
+
end
|
|
8
|
+
class Checkpoint
|
|
9
|
+
def last_issued: () -> Integer?
|
|
10
|
+
def observed: () -> Float
|
|
11
|
+
end
|
|
12
|
+
class Decision < Data
|
|
13
|
+
attr_reader status: Symbol
|
|
14
|
+
attr_reader lease: Lease?
|
|
15
|
+
attr_reader reason: Symbol?
|
|
16
|
+
end
|
|
17
|
+
class Authority
|
|
18
|
+
def self.new: () -> instance
|
|
19
|
+
def self.restore: (Checkpoint) -> instance
|
|
20
|
+
def acquire: (owner: Integer, now: Float, ttl: Float) -> Decision
|
|
21
|
+
def renew: (owner: Integer, token: Integer, now: Float, ttl: Float) -> Decision
|
|
22
|
+
def release: (owner: Integer, token: Integer, now: Float) -> Decision
|
|
23
|
+
def checkpoint: () -> Checkpoint
|
|
24
|
+
end
|
|
25
|
+
class Fence
|
|
26
|
+
def self.new: (?highest: Integer?) -> instance
|
|
27
|
+
def accept: (Integer) -> bool
|
|
28
|
+
def highest: () -> Integer?
|
|
29
|
+
end
|
|
30
|
+
end
|
metadata
ADDED
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: lease_machines
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
version: 0.1.6
|
|
5
|
+
platform: ruby
|
|
6
|
+
authors:
|
|
7
|
+
- Abdelkader Boudih
|
|
8
|
+
bindir: bin
|
|
9
|
+
cert_chain: []
|
|
10
|
+
date: 1980-01-02 00:00:00.000000000 Z
|
|
11
|
+
dependencies:
|
|
12
|
+
- !ruby/object:Gem::Dependency
|
|
13
|
+
name: rb_sys
|
|
14
|
+
requirement: !ruby/object:Gem::Requirement
|
|
15
|
+
requirements:
|
|
16
|
+
- - "~>"
|
|
17
|
+
- !ruby/object:Gem::Version
|
|
18
|
+
version: '0.9'
|
|
19
|
+
- - ">="
|
|
20
|
+
- !ruby/object:Gem::Version
|
|
21
|
+
version: 0.9.130
|
|
22
|
+
type: :runtime
|
|
23
|
+
prerelease: false
|
|
24
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
25
|
+
requirements:
|
|
26
|
+
- - "~>"
|
|
27
|
+
- !ruby/object:Gem::Version
|
|
28
|
+
version: '0.9'
|
|
29
|
+
- - ">="
|
|
30
|
+
- !ruby/object:Gem::Version
|
|
31
|
+
version: 0.9.130
|
|
32
|
+
description: Thin Ruby bindings to the standalone lease-machines core; durability,
|
|
33
|
+
clocks, and resource enforcement belong to the caller.
|
|
34
|
+
email:
|
|
35
|
+
- oss@seuros.com
|
|
36
|
+
executables: []
|
|
37
|
+
extensions:
|
|
38
|
+
- ext/lease_machines_native/extconf.rb
|
|
39
|
+
extra_rdoc_files: []
|
|
40
|
+
files:
|
|
41
|
+
- CHANGELOG.md
|
|
42
|
+
- Cargo.toml
|
|
43
|
+
- LICENSE.txt
|
|
44
|
+
- README.md
|
|
45
|
+
- ext/lease_machines_native/core/CHANGELOG.md
|
|
46
|
+
- ext/lease_machines_native/core/Cargo.toml
|
|
47
|
+
- ext/lease_machines_native/core/README.md
|
|
48
|
+
- ext/lease_machines_native/core/src/authority.rs
|
|
49
|
+
- ext/lease_machines_native/core/src/fencing.rs
|
|
50
|
+
- ext/lease_machines_native/core/src/holder.rs
|
|
51
|
+
- ext/lease_machines_native/core/src/lease.rs
|
|
52
|
+
- ext/lease_machines_native/core/src/lib.rs
|
|
53
|
+
- ext/lease_machines_native/extconf.rb
|
|
54
|
+
- ext/lease_machines_native/ffi/Cargo.toml
|
|
55
|
+
- ext/lease_machines_native/ffi/extconf.rb
|
|
56
|
+
- ext/lease_machines_native/ffi/src/bindings.rs
|
|
57
|
+
- ext/lease_machines_native/ffi/src/lib.rs
|
|
58
|
+
- lib/lease_machines.rb
|
|
59
|
+
- lib/lease_machines/version.rb
|
|
60
|
+
- sig/lease_machines.rbs
|
|
61
|
+
homepage: https://github.com/seuros/lease_machines
|
|
62
|
+
licenses:
|
|
63
|
+
- MIT
|
|
64
|
+
metadata:
|
|
65
|
+
source_code_uri: https://github.com/seuros/lease_machines
|
|
66
|
+
rubygems_mfa_required: 'true'
|
|
67
|
+
cargo_crate_name: lease_machines_native
|
|
68
|
+
cargo_manifest_path: ext/lease_machines_native/ffi/Cargo.toml
|
|
69
|
+
rdoc_options: []
|
|
70
|
+
require_paths:
|
|
71
|
+
- lib
|
|
72
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
73
|
+
requirements:
|
|
74
|
+
- - ">="
|
|
75
|
+
- !ruby/object:Gem::Version
|
|
76
|
+
version: '4.0'
|
|
77
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
78
|
+
requirements:
|
|
79
|
+
- - ">="
|
|
80
|
+
- !ruby/object:Gem::Version
|
|
81
|
+
version: '0'
|
|
82
|
+
requirements: []
|
|
83
|
+
rubygems_version: 4.0.20
|
|
84
|
+
specification_version: 4
|
|
85
|
+
summary: Native lease lifecycle and fencing decisions for Ruby
|
|
86
|
+
test_files: []
|