lutaml-store 0.3.1 → 0.3.3
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 +4 -4
- data/docs/src/pages/stores.md +22 -0
- data/lib/lutaml/store/adapter/memory.rb +17 -0
- data/lib/lutaml/store/cache_store.rb +4 -2
- data/lib/lutaml/store/config.rb +2 -1
- data/lib/lutaml/store/version.rb +1 -1
- metadata +4 -4
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: b35bad6d5f81534647fb355041ff434f9155e4488121d0870ef40611c1b9dc93
|
|
4
|
+
data.tar.gz: 71bca0ca15d5134199bad07710cb2bad8b35c3369abc5426a22bfa299352ba4a
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: eb4a0da9eb8a5dc529d8dbac816b8e1c98cc0da1826b7c9be5e04ceb9ae92b7e1fb3bbac65eca28f7c7a90025adf7391918beb5270e5b081c92baee31ffb69c5
|
|
7
|
+
data.tar.gz: af1af6380f9cd19f0ae14f1d7fc7987ae15cc47f26535338d919fa8cac2f83ceef84a4084a7f80bf626617182da65318d0207c6086274df078b388dbb4811b65
|
data/docs/src/pages/stores.md
CHANGED
|
@@ -76,6 +76,28 @@ cache.fetch("key") { expensive_call } # block runs only on miss
|
|
|
76
76
|
|
|
77
77
|
Entries past their TTL are evicted lazily on access; when `max_size` is exceeded the least-recently-used entries are evicted first.
|
|
78
78
|
|
|
79
|
+
### TTL guarantees (pinned)
|
|
80
|
+
|
|
81
|
+
The TTL semantics are pinned by specs and safe to build expiry policy on:
|
|
82
|
+
|
|
83
|
+
- **Persistence** — entries serialize `created_at` as ISO-8601 with
|
|
84
|
+
microsecond precision, so a FileSystem (or SQLite) cache reopened in a new
|
|
85
|
+
process keeps every entry's original clock; expiry survives restarts.
|
|
86
|
+
The memory adapter keeps no state across processes by nature.
|
|
87
|
+
- **Clock** — wall clock, not monotonic. This is the deliberate trade:
|
|
88
|
+
wall-clock timestamps survive restarts, but a system clock jump changes
|
|
89
|
+
apparent freshness (a backward jump un-expires nothing an entry's absolute
|
|
90
|
+
`created_at + ttl` already expired). Consumers needing jump-immunity
|
|
91
|
+
re-derive freshness from the source.
|
|
92
|
+
- **Expired reads** — `get` on an expired entry returns `nil` **and deletes
|
|
93
|
+
the entry**: the key disappears from `keys`/`size`/`exists?`.
|
|
94
|
+
- **Sweep** — expiry is lazy-on-read plus an opportunistic
|
|
95
|
+
`cleanup_expired` (interval `cleanup_interval`, default 300 s) invoked
|
|
96
|
+
inside mutating reads. There is **no background reaper**; a consumer like
|
|
97
|
+
the relaton shard cache needs none.
|
|
98
|
+
- **TTL values** — `ttl: nil` (and no `default_ttl`) never expires;
|
|
99
|
+
`ttl: 0` is immediately expired.
|
|
100
|
+
|
|
79
101
|
## PackageStore — multi-model packages
|
|
80
102
|
|
|
81
103
|
```ruby
|
|
@@ -18,6 +18,7 @@ module Lutaml
|
|
|
18
18
|
@ttl_enabled = @config.fetch(:ttl_enabled, false)
|
|
19
19
|
@ttl_data = @ttl_enabled ? {} : nil
|
|
20
20
|
@default_ttl = @config[:default_ttl] || 3600
|
|
21
|
+
@max_entries = @config[:max_entries]
|
|
21
22
|
end
|
|
22
23
|
|
|
23
24
|
# ── Read operations (lock-free via snapshot) ──
|
|
@@ -64,6 +65,7 @@ module Lutaml
|
|
|
64
65
|
|
|
65
66
|
def set(key, value, metadata = {})
|
|
66
67
|
@write_mutex.synchronize do
|
|
68
|
+
evict_oldest_if_needed(key)
|
|
67
69
|
@data[key] = value
|
|
68
70
|
invalidate_snapshot
|
|
69
71
|
|
|
@@ -118,6 +120,7 @@ module Lutaml
|
|
|
118
120
|
def bulk_set(key_value_pairs, ttl: nil)
|
|
119
121
|
@write_mutex.synchronize do
|
|
120
122
|
key_value_pairs.each do |key, value|
|
|
123
|
+
evict_oldest_if_needed(key)
|
|
121
124
|
@data[key] = value
|
|
122
125
|
|
|
123
126
|
if @ttl_enabled
|
|
@@ -255,6 +258,7 @@ module Lutaml
|
|
|
255
258
|
snap = snapshot
|
|
256
259
|
super.merge(
|
|
257
260
|
size: snap.size,
|
|
261
|
+
max_entries: @max_entries,
|
|
258
262
|
ttl_enabled: @ttl_enabled,
|
|
259
263
|
expired_keys: @ttl_enabled ? count_expired_keys : 0
|
|
260
264
|
)
|
|
@@ -279,6 +283,19 @@ module Lutaml
|
|
|
279
283
|
@snapshot_stale = true
|
|
280
284
|
end
|
|
281
285
|
|
|
286
|
+
# Capacity bound (max_entries): evict the oldest-INSERTED entry to
|
|
287
|
+
# make room. Insertion order, not access order — refreshing on read
|
|
288
|
+
# would cost the lock-free snapshot reads. Updating an existing key
|
|
289
|
+
# never evicts.
|
|
290
|
+
def evict_oldest_if_needed(incoming_key)
|
|
291
|
+
return if @max_entries.nil? || @data.key?(incoming_key) ||
|
|
292
|
+
@data.size < @max_entries
|
|
293
|
+
|
|
294
|
+
oldest = @data.each_key.first
|
|
295
|
+
@data.delete(oldest)
|
|
296
|
+
@ttl_data&.delete(oldest)
|
|
297
|
+
end
|
|
298
|
+
|
|
282
299
|
def cleanup_expired_if_needed
|
|
283
300
|
return unless @ttl_enabled
|
|
284
301
|
|
|
@@ -36,10 +36,12 @@ module Lutaml
|
|
|
36
36
|
|
|
37
37
|
def to_h
|
|
38
38
|
{
|
|
39
|
+
# sub-second precision: a second-truncated created_at shortens
|
|
40
|
+
# every restored TTL by up to a second (matters at small TTLs)
|
|
39
41
|
value: @value,
|
|
40
|
-
created_at: @created_at.iso8601,
|
|
42
|
+
created_at: @created_at.iso8601(6),
|
|
41
43
|
ttl: @ttl,
|
|
42
|
-
expires_at: expires_at&.iso8601,
|
|
44
|
+
expires_at: expires_at&.iso8601(6),
|
|
43
45
|
metadata: @metadata
|
|
44
46
|
}
|
|
45
47
|
end
|
data/lib/lutaml/store/config.rb
CHANGED
|
@@ -12,7 +12,8 @@ module Lutaml
|
|
|
12
12
|
|
|
13
13
|
def initialize(adapter_type: :memory, adapter_options: {},
|
|
14
14
|
cache: {}, monitoring: {}, events: {},
|
|
15
|
-
compression: {}, serialization: {}, **)
|
|
15
|
+
compression: {}, serialization: {}, adapter: nil, **)
|
|
16
|
+
adapter_type = adapter if adapter.is_a?(Hash)
|
|
16
17
|
@adapter_type = normalize_adapter_type(adapter_type)
|
|
17
18
|
@adapter_options = inline_adapter_options(adapter_type).merge(symbolize_keys(adapter_options))
|
|
18
19
|
|
data/lib/lutaml/store/version.rb
CHANGED
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: lutaml-store
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.3.
|
|
4
|
+
version: 0.3.3
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Ronald Tse
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: exe
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2026-
|
|
11
|
+
date: 2026-11-02 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: lutaml-model
|
|
@@ -16,14 +16,14 @@ dependencies:
|
|
|
16
16
|
requirements:
|
|
17
17
|
- - "~>"
|
|
18
18
|
- !ruby/object:Gem::Version
|
|
19
|
-
version: 0.8.
|
|
19
|
+
version: 0.8.156
|
|
20
20
|
type: :runtime
|
|
21
21
|
prerelease: false
|
|
22
22
|
version_requirements: !ruby/object:Gem::Requirement
|
|
23
23
|
requirements:
|
|
24
24
|
- - "~>"
|
|
25
25
|
- !ruby/object:Gem::Version
|
|
26
|
-
version: 0.8.
|
|
26
|
+
version: 0.8.156
|
|
27
27
|
- !ruby/object:Gem::Dependency
|
|
28
28
|
name: rubyzip
|
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|