mudis 0.8.0 → 0.8.1

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.
@@ -1,29 +1,29 @@
1
- # frozen_string_literal: true
2
-
3
- require "spec_helper"
4
-
5
- RSpec.describe "Mudis LRU Eviction" do
6
- before do
7
- Mudis.reset!
8
- Mudis.stop_expiry_thread
9
-
10
- Mudis.instance_variable_set(:@buckets, 1)
11
- Mudis.instance_variable_set(:@stores, [{}])
12
- Mudis.instance_variable_set(:@mutexes, [Mutex.new])
13
- Mudis.instance_variable_set(:@lru_heads, [nil])
14
- Mudis.instance_variable_set(:@lru_tails, [nil])
15
- Mudis.instance_variable_set(:@lru_nodes, [{}])
16
- Mudis.instance_variable_set(:@current_bytes, [0])
17
- Mudis.hard_memory_limit = false
18
- Mudis.instance_variable_set(:@threshold_bytes, 60)
19
- Mudis.max_value_bytes = 100
20
- end
21
-
22
- it "evicts old entries when size limit is reached" do
23
- Mudis.write("a", "a" * 50)
24
- Mudis.write("b", "b" * 50)
25
-
26
- expect(Mudis.read("a")).to be_nil
27
- expect(Mudis.read("b")).not_to be_nil
28
- end
29
- end
1
+ # frozen_string_literal: true
2
+
3
+ require "spec_helper"
4
+
5
+ RSpec.describe "Mudis LRU Eviction" do
6
+ before do
7
+ Mudis.reset!
8
+ Mudis.stop_expiry_thread
9
+
10
+ Mudis.instance_variable_set(:@buckets, 1)
11
+ Mudis.instance_variable_set(:@stores, [{}])
12
+ Mudis.instance_variable_set(:@mutexes, [Mutex.new])
13
+ Mudis.instance_variable_set(:@lru_heads, [nil])
14
+ Mudis.instance_variable_set(:@lru_tails, [nil])
15
+ Mudis.instance_variable_set(:@lru_nodes, [{}])
16
+ Mudis.instance_variable_set(:@current_bytes, [0])
17
+ Mudis.hard_memory_limit = false
18
+ Mudis.instance_variable_set(:@threshold_bytes, 60)
19
+ Mudis.max_value_bytes = 100
20
+ end
21
+
22
+ it "evicts old entries when size limit is reached" do
23
+ Mudis.write("a", "a" * 50)
24
+ Mudis.write("b", "b" * 50)
25
+
26
+ expect(Mudis.read("a")).to be_nil
27
+ expect(Mudis.read("b")).not_to be_nil
28
+ end
29
+ end
@@ -1,138 +1,138 @@
1
- # frozen_string_literal: true
2
-
3
- require "spec_helper"
4
- require "climate_control"
5
-
6
- RSpec.describe "Mudis TTL Guardrail" do # rubocop:disable Metrics/BlockLength
7
- before do
8
- Mudis.reset!
9
- Mudis.configure do |c|
10
- c.max_ttl = 60 # 60 seconds max
11
- end
12
- end
13
-
14
- describe "default_ttl configuration" do # rubocop:disable Metrics/BlockLength
15
- before do
16
- Mudis.reset!
17
- Mudis.configure do |c|
18
- c.default_ttl = 60
19
- end
20
- end
21
-
22
- it "applies default_ttl when expires_in is nil" do
23
- Mudis.write("foo", "bar") # no explicit expires_in
24
- meta = Mudis.inspect("foo")
25
- expect(meta[:expires_at]).not_to be_nil
26
- expect(meta[:expires_at]).to be_within(5).of(Time.now + 60)
27
- end
28
-
29
- it "respects expires_in if explicitly given" do
30
- Mudis.write("short_lived", "bar", expires_in: 10)
31
- meta = Mudis.inspect("short_lived")
32
- expect(meta[:expires_at]).not_to be_nil
33
- expect(meta[:expires_at]).to be_within(5).of(Time.now + 10)
34
- end
35
-
36
- it "applies max_ttl over default_ttl if both are set" do
37
- Mudis.configure do |c|
38
- c.default_ttl = 120
39
- c.max_ttl = 30
40
- end
41
-
42
- Mudis.write("capped", "baz") # no explicit expires_in
43
- meta = Mudis.inspect("capped")
44
- expect(meta[:expires_at]).not_to be_nil
45
- expect(meta[:expires_at]).to be_within(5).of(Time.now + 30)
46
- end
47
-
48
- it "stores forever if default_ttl and expires_in are nil" do
49
- Mudis.configure do |c|
50
- c.default_ttl = nil
51
- end
52
-
53
- Mudis.write("forever", "ever")
54
- meta = Mudis.inspect("forever")
55
- expect(meta[:expires_at]).to be_nil
56
- end
57
- end
58
-
59
- it "clamps expires_in to max_ttl if it exceeds max_ttl" do
60
- Mudis.write("foo", "bar", expires_in: 300) # user requests 5 minutes
61
-
62
- metadata = Mudis.inspect("foo")
63
- ttl = metadata[:expires_at] - metadata[:created_at]
64
-
65
- expect(ttl).to be <= 60
66
- expect(ttl).to be > 0
67
- end
68
-
69
- it "respects expires_in if below max_ttl" do
70
- Mudis.write("bar", "baz", expires_in: 30) # under the max_ttl
71
-
72
- metadata = Mudis.inspect("bar")
73
- ttl = metadata[:expires_at] - metadata[:created_at]
74
-
75
- expect(ttl).to be_within(1).of(30)
76
- end
77
-
78
- it "allows nil expires_in (no expiry) if not required" do
79
- Mudis.write("baz", "no expiry")
80
-
81
- metadata = Mudis.inspect("baz")
82
- expect(metadata[:expires_at]).to be_nil
83
- end
84
- end
85
-
86
- RSpec.describe "Mudis Configuration Guardrails" do # rubocop:disable Metrics/BlockLength
87
- after { Mudis.reset! }
88
-
89
- describe "bucket configuration" do
90
- it "defaults to 32 buckets if ENV is nil" do
91
- Mudis.instance_variable_set(:@buckets, nil) # force recomputation
92
- ClimateControl.modify(MUDIS_BUCKETS: nil) do
93
- expect(Mudis.send(:buckets)).to eq(32)
94
- end
95
- end
96
-
97
- it "raises if MUDIS_BUCKETS is 0 or less" do
98
- expect do
99
- Mudis.instance_variable_set(:@buckets, nil) # force recomputation
100
- ClimateControl.modify(MUDIS_BUCKETS: "0") { Mudis.send(:buckets) }
101
- end.to raise_error(ArgumentError, /bucket count must be > 0/)
102
-
103
- expect do
104
- Mudis.instance_variable_set(:@buckets, nil) # force recomputation
105
- ClimateControl.modify(MUDIS_BUCKETS: "-5") { Mudis.send(:buckets) }
106
- end.to raise_error(ArgumentError, /bucket count must be > 0/)
107
- end
108
- end
109
-
110
- describe "memory configuration" do
111
- it "raises if max_bytes is set to 0 or less" do
112
- expect do
113
- Mudis.max_bytes = 0
114
- end.to raise_error(ArgumentError, /max_bytes must be > 0/)
115
-
116
- expect do
117
- Mudis.max_bytes = -1
118
- end.to raise_error(ArgumentError, /max_bytes must be > 0/)
119
- end
120
-
121
- it "raises if max_value_bytes is 0 or less via config" do
122
- expect do
123
- Mudis.configure do |c|
124
- c.max_value_bytes = 0
125
- end
126
- end.to raise_error(ArgumentError, /max_value_bytes must be > 0/)
127
- end
128
-
129
- it "raises if max_value_bytes exceeds max_bytes" do
130
- expect do
131
- Mudis.configure do |c|
132
- c.max_bytes = 1_000_000
133
- c.max_value_bytes = 2_000_000
134
- end
135
- end.to raise_error(ArgumentError, /max_value_bytes cannot exceed max_bytes/)
136
- end
137
- end
138
- end
1
+ # frozen_string_literal: true
2
+
3
+ require "spec_helper"
4
+ require "climate_control"
5
+
6
+ RSpec.describe "Mudis TTL Guardrail" do # rubocop:disable Metrics/BlockLength
7
+ before do
8
+ Mudis.reset!
9
+ Mudis.configure do |c|
10
+ c.max_ttl = 60 # 60 seconds max
11
+ end
12
+ end
13
+
14
+ describe "default_ttl configuration" do # rubocop:disable Metrics/BlockLength
15
+ before do
16
+ Mudis.reset!
17
+ Mudis.configure do |c|
18
+ c.default_ttl = 60
19
+ end
20
+ end
21
+
22
+ it "applies default_ttl when expires_in is nil" do
23
+ Mudis.write("foo", "bar") # no explicit expires_in
24
+ meta = Mudis.inspect("foo")
25
+ expect(meta[:expires_at]).not_to be_nil
26
+ expect(meta[:expires_at]).to be_within(5).of(Time.now + 60)
27
+ end
28
+
29
+ it "respects expires_in if explicitly given" do
30
+ Mudis.write("short_lived", "bar", expires_in: 10)
31
+ meta = Mudis.inspect("short_lived")
32
+ expect(meta[:expires_at]).not_to be_nil
33
+ expect(meta[:expires_at]).to be_within(5).of(Time.now + 10)
34
+ end
35
+
36
+ it "applies max_ttl over default_ttl if both are set" do
37
+ Mudis.configure do |c|
38
+ c.default_ttl = 120
39
+ c.max_ttl = 30
40
+ end
41
+
42
+ Mudis.write("capped", "baz") # no explicit expires_in
43
+ meta = Mudis.inspect("capped")
44
+ expect(meta[:expires_at]).not_to be_nil
45
+ expect(meta[:expires_at]).to be_within(5).of(Time.now + 30)
46
+ end
47
+
48
+ it "stores forever if default_ttl and expires_in are nil" do
49
+ Mudis.configure do |c|
50
+ c.default_ttl = nil
51
+ end
52
+
53
+ Mudis.write("forever", "ever")
54
+ meta = Mudis.inspect("forever")
55
+ expect(meta[:expires_at]).to be_nil
56
+ end
57
+ end
58
+
59
+ it "clamps expires_in to max_ttl if it exceeds max_ttl" do
60
+ Mudis.write("foo", "bar", expires_in: 300) # user requests 5 minutes
61
+
62
+ metadata = Mudis.inspect("foo")
63
+ ttl = metadata[:expires_at] - metadata[:created_at]
64
+
65
+ expect(ttl).to be <= 60
66
+ expect(ttl).to be > 0
67
+ end
68
+
69
+ it "respects expires_in if below max_ttl" do
70
+ Mudis.write("bar", "baz", expires_in: 30) # under the max_ttl
71
+
72
+ metadata = Mudis.inspect("bar")
73
+ ttl = metadata[:expires_at] - metadata[:created_at]
74
+
75
+ expect(ttl).to be_within(1).of(30)
76
+ end
77
+
78
+ it "allows nil expires_in (no expiry) if not required" do
79
+ Mudis.write("baz", "no expiry")
80
+
81
+ metadata = Mudis.inspect("baz")
82
+ expect(metadata[:expires_at]).to be_nil
83
+ end
84
+ end
85
+
86
+ RSpec.describe "Mudis Configuration Guardrails" do # rubocop:disable Metrics/BlockLength
87
+ after { Mudis.reset! }
88
+
89
+ describe "bucket configuration" do
90
+ it "defaults to 32 buckets if ENV is nil" do
91
+ Mudis.instance_variable_set(:@buckets, nil) # force recomputation
92
+ ClimateControl.modify(MUDIS_BUCKETS: nil) do
93
+ expect(Mudis.send(:buckets)).to eq(32)
94
+ end
95
+ end
96
+
97
+ it "raises if MUDIS_BUCKETS is 0 or less" do
98
+ expect do
99
+ Mudis.instance_variable_set(:@buckets, nil) # force recomputation
100
+ ClimateControl.modify(MUDIS_BUCKETS: "0") { Mudis.send(:buckets) }
101
+ end.to raise_error(ArgumentError, /bucket count must be > 0/)
102
+
103
+ expect do
104
+ Mudis.instance_variable_set(:@buckets, nil) # force recomputation
105
+ ClimateControl.modify(MUDIS_BUCKETS: "-5") { Mudis.send(:buckets) }
106
+ end.to raise_error(ArgumentError, /bucket count must be > 0/)
107
+ end
108
+ end
109
+
110
+ describe "memory configuration" do
111
+ it "raises if max_bytes is set to 0 or less" do
112
+ expect do
113
+ Mudis.max_bytes = 0
114
+ end.to raise_error(ArgumentError, /max_bytes must be > 0/)
115
+
116
+ expect do
117
+ Mudis.max_bytes = -1
118
+ end.to raise_error(ArgumentError, /max_bytes must be > 0/)
119
+ end
120
+
121
+ it "raises if max_value_bytes is 0 or less via config" do
122
+ expect do
123
+ Mudis.configure do |c|
124
+ c.max_value_bytes = 0
125
+ end
126
+ end.to raise_error(ArgumentError, /max_value_bytes must be > 0/)
127
+ end
128
+
129
+ it "raises if max_value_bytes exceeds max_bytes" do
130
+ expect do
131
+ Mudis.configure do |c|
132
+ c.max_bytes = 1_000_000
133
+ c.max_value_bytes = 2_000_000
134
+ end
135
+ end.to raise_error(ArgumentError, /max_value_bytes cannot exceed max_bytes/)
136
+ end
137
+ end
138
+ end
@@ -1,33 +1,33 @@
1
- # frozen_string_literal: true
2
-
3
- require "spec_helper"
4
-
5
- RSpec.describe "Mudis Memory Guardrails" do
6
- before do
7
- Mudis.reset!
8
- Mudis.stop_expiry_thread
9
- Mudis.instance_variable_set(:@buckets, 1)
10
- Mudis.instance_variable_set(:@stores, [{}])
11
- Mudis.instance_variable_set(:@mutexes, [Mutex.new])
12
- Mudis.instance_variable_set(:@lru_heads, [nil])
13
- Mudis.instance_variable_set(:@lru_tails, [nil])
14
- Mudis.instance_variable_set(:@lru_nodes, [{}])
15
- Mudis.instance_variable_set(:@current_bytes, [0])
16
-
17
- Mudis.max_value_bytes = nil
18
- Mudis.instance_variable_set(:@threshold_bytes, 1_000_000)
19
- Mudis.hard_memory_limit = true
20
- Mudis.instance_variable_set(:@max_bytes, 100)
21
- end
22
-
23
- it "rejects writes that exceed max memory" do
24
- big_value = "a" * 90
25
- Mudis.write("a", big_value)
26
- expect(Mudis.read("a")).to eq(big_value)
27
-
28
- big_value2 = "b" * 90
29
- Mudis.write("b", big_value2)
30
- expect(Mudis.read("b")).to be_nil
31
- expect(Mudis.metrics[:rejected]).to be > 0
32
- end
33
- end
1
+ # frozen_string_literal: true
2
+
3
+ require "spec_helper"
4
+
5
+ RSpec.describe "Mudis Memory Guardrails" do
6
+ before do
7
+ Mudis.reset!
8
+ Mudis.stop_expiry_thread
9
+ Mudis.instance_variable_set(:@buckets, 1)
10
+ Mudis.instance_variable_set(:@stores, [{}])
11
+ Mudis.instance_variable_set(:@mutexes, [Mutex.new])
12
+ Mudis.instance_variable_set(:@lru_heads, [nil])
13
+ Mudis.instance_variable_set(:@lru_tails, [nil])
14
+ Mudis.instance_variable_set(:@lru_nodes, [{}])
15
+ Mudis.instance_variable_set(:@current_bytes, [0])
16
+
17
+ Mudis.max_value_bytes = nil
18
+ Mudis.instance_variable_set(:@threshold_bytes, 1_000_000)
19
+ Mudis.hard_memory_limit = true
20
+ Mudis.instance_variable_set(:@max_bytes, 100)
21
+ end
22
+
23
+ it "rejects writes that exceed max memory" do
24
+ big_value = "a" * 90
25
+ Mudis.write("a", big_value)
26
+ expect(Mudis.read("a")).to eq(big_value)
27
+
28
+ big_value2 = "b" * 90
29
+ Mudis.write("b", big_value2)
30
+ expect(Mudis.read("b")).to be_nil
31
+ expect(Mudis.metrics[:rejected]).to be > 0
32
+ end
33
+ end
data/spec/metrics_spec.rb CHANGED
@@ -1,34 +1,34 @@
1
- # frozen_string_literal: true
2
-
3
- require_relative "spec_helper"
4
-
5
- RSpec.describe "Mudis Metrics" do # rubocop:disable Metrics/BlockLength
6
- it "tracks hits and misses" do
7
- Mudis.write("hit_me", "value")
8
- Mudis.read("hit_me")
9
- Mudis.read("miss_me")
10
- metrics = Mudis.metrics
11
- expect(metrics[:hits]).to eq(1)
12
- expect(metrics[:misses]).to eq(1)
13
- end
14
-
15
- it "includes per-bucket stats" do
16
- Mudis.write("a", "x" * 50)
17
- metrics = Mudis.metrics
18
- expect(metrics).to include(:buckets)
19
- expect(metrics[:buckets]).to be_an(Array)
20
- expect(metrics[:buckets].first).to include(:index, :keys, :memory_bytes, :lru_size)
21
- end
22
-
23
- it "resets only the metrics without clearing cache" do
24
- Mudis.write("metrics_key", "value")
25
- Mudis.read("metrics_key")
26
- Mudis.read("missing_key")
27
- expect(Mudis.metrics[:hits]).to eq(1)
28
- expect(Mudis.metrics[:misses]).to eq(1)
29
- Mudis.reset_metrics!
30
- expect(Mudis.metrics[:hits]).to eq(0)
31
- expect(Mudis.metrics[:misses]).to eq(0)
32
- expect(Mudis.read("metrics_key")).to eq("value")
33
- end
34
- end
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "spec_helper"
4
+
5
+ RSpec.describe "Mudis Metrics" do # rubocop:disable Metrics/BlockLength
6
+ it "tracks hits and misses" do
7
+ Mudis.write("hit_me", "value")
8
+ Mudis.read("hit_me")
9
+ Mudis.read("miss_me")
10
+ metrics = Mudis.metrics
11
+ expect(metrics[:hits]).to eq(1)
12
+ expect(metrics[:misses]).to eq(1)
13
+ end
14
+
15
+ it "includes per-bucket stats" do
16
+ Mudis.write("a", "x" * 50)
17
+ metrics = Mudis.metrics
18
+ expect(metrics).to include(:buckets)
19
+ expect(metrics[:buckets]).to be_an(Array)
20
+ expect(metrics[:buckets].first).to include(:index, :keys, :memory_bytes, :lru_size)
21
+ end
22
+
23
+ it "resets only the metrics without clearing cache" do
24
+ Mudis.write("metrics_key", "value")
25
+ Mudis.read("metrics_key")
26
+ Mudis.read("missing_key")
27
+ expect(Mudis.metrics[:hits]).to eq(1)
28
+ expect(Mudis.metrics[:misses]).to eq(1)
29
+ Mudis.reset_metrics!
30
+ expect(Mudis.metrics[:hits]).to eq(0)
31
+ expect(Mudis.metrics[:misses]).to eq(0)
32
+ expect(Mudis.read("metrics_key")).to eq("value")
33
+ end
34
+ end