karafka-rdkafka 0.19.1 → 0.19.2.rc1

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.
@@ -0,0 +1,359 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'spec_helper'
4
+
5
+ RSpec.describe Rdkafka::Producer::PartitionsCountCache do
6
+ let(:default_ttl) { 1 } # Reduced from 30 to speed up tests
7
+ let(:custom_ttl) { 0.5 } # Half the default TTL
8
+ let(:cache) { described_class.new(default_ttl) }
9
+ let(:custom_ttl_cache) { described_class.new(custom_ttl) }
10
+ let(:topic) { "test_topic" }
11
+ let(:topic2) { "test_topic2" }
12
+ let(:partition_count) { 5 }
13
+ let(:higher_partition_count) { 10 }
14
+ let(:lower_partition_count) { 3 }
15
+ let(:even_higher_partition_count) { 15 }
16
+
17
+ describe "#initialize" do
18
+ it "creates a cache with default TTL when no TTL is specified" do
19
+ standard_cache = described_class.new
20
+ expect(standard_cache).to be_a(described_class)
21
+ end
22
+
23
+ it "creates a cache with custom TTL when specified" do
24
+ expect(custom_ttl_cache).to be_a(described_class)
25
+ end
26
+ end
27
+
28
+ describe "#get" do
29
+ context "when cache is empty" do
30
+ it "yields to get the value and caches it" do
31
+ block_called = false
32
+ result = cache.get(topic) do
33
+ block_called = true
34
+ partition_count
35
+ end
36
+
37
+ expect(block_called).to be true
38
+ expect(result).to eq(partition_count)
39
+
40
+ # Verify caching by checking if block is called again
41
+ second_block_called = false
42
+ second_result = cache.get(topic) do
43
+ second_block_called = true
44
+ partition_count + 1 # Different value to ensure we get cached value
45
+ end
46
+
47
+ expect(second_block_called).to be false
48
+ expect(second_result).to eq(partition_count)
49
+ end
50
+ end
51
+
52
+ context "when cache has a value" do
53
+ before do
54
+ # Seed the cache with a value
55
+ cache.get(topic) { partition_count }
56
+ end
57
+
58
+ it "returns cached value without yielding if not expired" do
59
+ block_called = false
60
+ result = cache.get(topic) do
61
+ block_called = true
62
+ partition_count + 1 # Different value to ensure we get cached one
63
+ end
64
+
65
+ expect(block_called).to be false
66
+ expect(result).to eq(partition_count)
67
+ end
68
+
69
+ it "yields to get new value when TTL has expired" do
70
+ # Wait for TTL to expire
71
+ sleep(default_ttl + 0.1)
72
+
73
+ block_called = false
74
+ new_count = partition_count + 1
75
+ result = cache.get(topic) do
76
+ block_called = true
77
+ new_count
78
+ end
79
+
80
+ expect(block_called).to be true
81
+ expect(result).to eq(new_count)
82
+
83
+ # Verify the new value is cached
84
+ second_block_called = false
85
+ second_result = cache.get(topic) do
86
+ second_block_called = true
87
+ new_count + 1 # Different value again
88
+ end
89
+
90
+ expect(second_block_called).to be false
91
+ expect(second_result).to eq(new_count)
92
+ end
93
+
94
+ it "respects a custom TTL" do
95
+ # Seed the custom TTL cache with a value
96
+ custom_ttl_cache.get(topic) { partition_count }
97
+
98
+ # Wait for custom TTL to expire but not default TTL
99
+ sleep(custom_ttl + 0.1)
100
+
101
+ # Custom TTL cache should refresh
102
+ custom_block_called = false
103
+ custom_result = custom_ttl_cache.get(topic) do
104
+ custom_block_called = true
105
+ higher_partition_count
106
+ end
107
+
108
+ expect(custom_block_called).to be true
109
+ expect(custom_result).to eq(higher_partition_count)
110
+
111
+ # Default TTL cache should not refresh yet
112
+ default_block_called = false
113
+ default_result = cache.get(topic) do
114
+ default_block_called = true
115
+ higher_partition_count
116
+ end
117
+
118
+ expect(default_block_called).to be false
119
+ expect(default_result).to eq(partition_count)
120
+ end
121
+ end
122
+
123
+ context "when new value is obtained" do
124
+ before do
125
+ # Seed the cache with initial value
126
+ cache.get(topic) { partition_count }
127
+ end
128
+
129
+ it "updates cache when new value is higher than cached value" do
130
+ # Wait for TTL to expire
131
+ sleep(default_ttl + 0.1)
132
+
133
+ # Get higher value
134
+ result = cache.get(topic) { higher_partition_count }
135
+ expect(result).to eq(higher_partition_count)
136
+
137
+ # Verify it was cached
138
+ second_result = cache.get(topic) { fail "Should not be called" }
139
+ expect(second_result).to eq(higher_partition_count)
140
+ end
141
+
142
+ it "preserves higher cached value when new value is lower" do
143
+ # First update to higher value
144
+ sleep(default_ttl + 0.1)
145
+ cache.get(topic) { higher_partition_count }
146
+
147
+ # Then try to update to lower value
148
+ sleep(default_ttl + 0.1)
149
+ result = cache.get(topic) { lower_partition_count }
150
+
151
+ expect(result).to eq(higher_partition_count)
152
+
153
+ # and subsequent gets should return the previously cached higher value
154
+ second_result = cache.get(topic) { fail "Should not be called" }
155
+ expect(second_result).to eq(higher_partition_count)
156
+ end
157
+
158
+ it "handles multiple topics independently" do
159
+ # Set up both topics with different values
160
+ cache.get(topic) { partition_count }
161
+ cache.get(topic2) { higher_partition_count }
162
+
163
+ # Wait for TTL to expire
164
+ sleep(default_ttl + 0.1)
165
+
166
+ # Update first topic
167
+ first_result = cache.get(topic) { even_higher_partition_count }
168
+ expect(first_result).to eq(even_higher_partition_count)
169
+
170
+ # Update second topic independently
171
+ second_updated = higher_partition_count + 3
172
+ second_result = cache.get(topic2) { second_updated }
173
+ expect(second_result).to eq(second_updated)
174
+
175
+ # Both topics should have their updated values
176
+ expect(cache.get(topic) { fail "Should not be called" }).to eq(even_higher_partition_count)
177
+ expect(cache.get(topic2) { fail "Should not be called" }).to eq(second_updated)
178
+ end
179
+ end
180
+ end
181
+
182
+ describe "#set" do
183
+ context "when cache is empty" do
184
+ it "adds a new entry to the cache" do
185
+ cache.set(topic, partition_count)
186
+
187
+ # Verify through get
188
+ result = cache.get(topic) { fail "Should not be called" }
189
+ expect(result).to eq(partition_count)
190
+ end
191
+ end
192
+
193
+ context "when cache already has a value" do
194
+ before do
195
+ cache.set(topic, partition_count)
196
+ end
197
+
198
+ it "updates cache when new value is higher" do
199
+ cache.set(topic, higher_partition_count)
200
+
201
+ result = cache.get(topic) { fail "Should not be called" }
202
+ expect(result).to eq(higher_partition_count)
203
+ end
204
+
205
+ it "keeps original value when new value is lower" do
206
+ cache.set(topic, lower_partition_count)
207
+
208
+ result = cache.get(topic) { fail "Should not be called" }
209
+ expect(result).to eq(partition_count)
210
+ end
211
+
212
+ it "updates the timestamp even when keeping original value" do
213
+ # Set initial value
214
+ cache.set(topic, partition_count)
215
+
216
+ # Wait until close to TTL expiring
217
+ sleep(default_ttl - 0.2)
218
+
219
+ # Set lower value (should update timestamp but not value)
220
+ cache.set(topic, lower_partition_count)
221
+
222
+ # Wait a bit more, but still under the full TTL if timestamp was refreshed
223
+ sleep(0.3)
224
+
225
+ # Should still be valid due to timestamp refresh
226
+ result = cache.get(topic) { fail "Should not be called" }
227
+ expect(result).to eq(partition_count)
228
+ end
229
+ end
230
+
231
+ context "with concurrent access" do
232
+ it "correctly handles simultaneous updates to the same topic" do
233
+ # This test focuses on the final value after concurrent updates
234
+ threads = []
235
+
236
+ # Create 5 threads that all try to update the same topic with increasing values
237
+ 5.times do |i|
238
+ threads << Thread.new do
239
+ value = 10 + i # Start at 10 to ensure all are higher than initial value
240
+ cache.set(topic, value)
241
+ end
242
+ end
243
+
244
+ # Wait for all threads to complete
245
+ threads.each(&:join)
246
+
247
+ # The highest value (14) should be stored and accessible through get
248
+ result = cache.get(topic) { fail "Should not be called" }
249
+ expect(result).to eq(14)
250
+ end
251
+ end
252
+ end
253
+
254
+ describe "TTL behavior" do
255
+ it "treats entries as expired when they exceed TTL" do
256
+ # Set initial value
257
+ cache.get(topic) { partition_count }
258
+
259
+ # Wait just under TTL
260
+ sleep(default_ttl - 0.1)
261
+
262
+ # Value should still be cached (block should not be called)
263
+ result = cache.get(topic) { fail "Should not be called when cache is valid" }
264
+ expect(result).to eq(partition_count)
265
+
266
+ # Now wait to exceed TTL
267
+ sleep(0.2) # Total sleep is now default_ttl + 0.1
268
+
269
+ # Cache should be expired, block should be called
270
+ block_called = false
271
+ new_value = partition_count + 3
272
+ result = cache.get(topic) do
273
+ block_called = true
274
+ new_value
275
+ end
276
+
277
+ expect(block_called).to be true
278
+ expect(result).to eq(new_value)
279
+ end
280
+ end
281
+
282
+ describe "comprehensive scenarios" do
283
+ it "handles a full lifecycle of cache operations" do
284
+ # 1. Initial cache miss, fetch and store
285
+ result1 = cache.get(topic) { partition_count }
286
+ expect(result1).to eq(partition_count)
287
+
288
+ # 2. Cache hit
289
+ result2 = cache.get(topic) { fail "Should not be called" }
290
+ expect(result2).to eq(partition_count)
291
+
292
+ # 3. Attempt to set lower value
293
+ cache.set(topic, lower_partition_count)
294
+ result3 = cache.get(topic) { fail "Should not be called" }
295
+ # Should still return the higher original value
296
+ expect(result3).to eq(partition_count)
297
+
298
+ # 4. Set higher value
299
+ cache.set(topic, higher_partition_count)
300
+ result4 = cache.get(topic) { fail "Should not be called" }
301
+ expect(result4).to eq(higher_partition_count)
302
+
303
+ # 5. TTL expires, new value provided is lower
304
+ sleep(default_ttl + 0.1)
305
+ result5 = cache.get(topic) { lower_partition_count }
306
+ # This returns the highest value
307
+ expect(result5).to eq(higher_partition_count)
308
+
309
+ # 6. But subsequent get should return the higher cached value
310
+ result6 = cache.get(topic) { fail "Should not be called" }
311
+ expect(result6).to eq(higher_partition_count)
312
+
313
+ # 7. Set new highest value directly
314
+ even_higher = higher_partition_count + 5
315
+ cache.set(topic, even_higher)
316
+ result7 = cache.get(topic) { fail "Should not be called" }
317
+ expect(result7).to eq(even_higher)
318
+ end
319
+
320
+ it "handles multiple topics with different TTLs correctly" do
321
+ # Set up initial values
322
+ cache.get(topic) { partition_count }
323
+ custom_ttl_cache.get(topic) { partition_count }
324
+
325
+ # Wait past custom TTL but not default TTL
326
+ sleep(custom_ttl + 0.1)
327
+
328
+ # Default cache should NOT refresh (still within default TTL)
329
+ default_result = cache.get(topic) { fail "Should not be called for default cache" }
330
+ # Original value should be maintained
331
+ expect(default_result).to eq(partition_count)
332
+
333
+ # Custom TTL cache SHOULD refresh (past custom TTL)
334
+ custom_cache_value = partition_count + 8
335
+ custom_block_called = false
336
+ custom_result = custom_ttl_cache.get(topic) do
337
+ custom_block_called = true
338
+ custom_cache_value
339
+ end
340
+
341
+ expect(custom_block_called).to be true
342
+ expect(custom_result).to eq(custom_cache_value)
343
+
344
+ # Now wait past default TTL
345
+ sleep(default_ttl - custom_ttl + 0.1)
346
+
347
+ # Now default cache should also refresh
348
+ default_block_called = false
349
+ new_default_value = partition_count + 10
350
+ new_default_result = cache.get(topic) do
351
+ default_block_called = true
352
+ new_default_value
353
+ end
354
+
355
+ expect(default_block_called).to be true
356
+ expect(new_default_result).to eq(new_default_value)
357
+ end
358
+ end
359
+ end
@@ -0,0 +1,359 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'spec_helper'
4
+
5
+ RSpec.describe Rdkafka::Producer::PartitionsCountCache do
6
+ let(:default_ttl) { 1 } # Reduced from 30 to speed up tests
7
+ let(:custom_ttl) { 0.5 } # Half the default TTL
8
+ let(:cache) { described_class.new(default_ttl) }
9
+ let(:custom_ttl_cache) { described_class.new(custom_ttl) }
10
+ let(:topic) { "test_topic" }
11
+ let(:topic2) { "test_topic2" }
12
+ let(:partition_count) { 5 }
13
+ let(:higher_partition_count) { 10 }
14
+ let(:lower_partition_count) { 3 }
15
+ let(:even_higher_partition_count) { 15 }
16
+
17
+ describe "#initialize" do
18
+ it "creates a cache with default TTL when no TTL is specified" do
19
+ standard_cache = described_class.new
20
+ expect(standard_cache).to be_a(described_class)
21
+ end
22
+
23
+ it "creates a cache with custom TTL when specified" do
24
+ expect(custom_ttl_cache).to be_a(described_class)
25
+ end
26
+ end
27
+
28
+ describe "#get" do
29
+ context "when cache is empty" do
30
+ it "yields to get the value and caches it" do
31
+ block_called = false
32
+ result = cache.get(topic) do
33
+ block_called = true
34
+ partition_count
35
+ end
36
+
37
+ expect(block_called).to be true
38
+ expect(result).to eq(partition_count)
39
+
40
+ # Verify caching by checking if block is called again
41
+ second_block_called = false
42
+ second_result = cache.get(topic) do
43
+ second_block_called = true
44
+ partition_count + 1 # Different value to ensure we get cached value
45
+ end
46
+
47
+ expect(second_block_called).to be false
48
+ expect(second_result).to eq(partition_count)
49
+ end
50
+ end
51
+
52
+ context "when cache has a value" do
53
+ before do
54
+ # Seed the cache with a value
55
+ cache.get(topic) { partition_count }
56
+ end
57
+
58
+ it "returns cached value without yielding if not expired" do
59
+ block_called = false
60
+ result = cache.get(topic) do
61
+ block_called = true
62
+ partition_count + 1 # Different value to ensure we get cached one
63
+ end
64
+
65
+ expect(block_called).to be false
66
+ expect(result).to eq(partition_count)
67
+ end
68
+
69
+ it "yields to get new value when TTL has expired" do
70
+ # Wait for TTL to expire
71
+ sleep(default_ttl + 0.1)
72
+
73
+ block_called = false
74
+ new_count = partition_count + 1
75
+ result = cache.get(topic) do
76
+ block_called = true
77
+ new_count
78
+ end
79
+
80
+ expect(block_called).to be true
81
+ expect(result).to eq(new_count)
82
+
83
+ # Verify the new value is cached
84
+ second_block_called = false
85
+ second_result = cache.get(topic) do
86
+ second_block_called = true
87
+ new_count + 1 # Different value again
88
+ end
89
+
90
+ expect(second_block_called).to be false
91
+ expect(second_result).to eq(new_count)
92
+ end
93
+
94
+ it "respects a custom TTL" do
95
+ # Seed the custom TTL cache with a value
96
+ custom_ttl_cache.get(topic) { partition_count }
97
+
98
+ # Wait for custom TTL to expire but not default TTL
99
+ sleep(custom_ttl + 0.1)
100
+
101
+ # Custom TTL cache should refresh
102
+ custom_block_called = false
103
+ custom_result = custom_ttl_cache.get(topic) do
104
+ custom_block_called = true
105
+ higher_partition_count
106
+ end
107
+
108
+ expect(custom_block_called).to be true
109
+ expect(custom_result).to eq(higher_partition_count)
110
+
111
+ # Default TTL cache should not refresh yet
112
+ default_block_called = false
113
+ default_result = cache.get(topic) do
114
+ default_block_called = true
115
+ higher_partition_count
116
+ end
117
+
118
+ expect(default_block_called).to be false
119
+ expect(default_result).to eq(partition_count)
120
+ end
121
+ end
122
+
123
+ context "when new value is obtained" do
124
+ before do
125
+ # Seed the cache with initial value
126
+ cache.get(topic) { partition_count }
127
+ end
128
+
129
+ it "updates cache when new value is higher than cached value" do
130
+ # Wait for TTL to expire
131
+ sleep(default_ttl + 0.1)
132
+
133
+ # Get higher value
134
+ result = cache.get(topic) { higher_partition_count }
135
+ expect(result).to eq(higher_partition_count)
136
+
137
+ # Verify it was cached
138
+ second_result = cache.get(topic) { fail "Should not be called" }
139
+ expect(second_result).to eq(higher_partition_count)
140
+ end
141
+
142
+ it "preserves higher cached value when new value is lower" do
143
+ # First update to higher value
144
+ sleep(default_ttl + 0.1)
145
+ cache.get(topic) { higher_partition_count }
146
+
147
+ # Then try to update to lower value
148
+ sleep(default_ttl + 0.1)
149
+ result = cache.get(topic) { lower_partition_count }
150
+
151
+ expect(result).to eq(higher_partition_count)
152
+
153
+ # and subsequent gets should return the previously cached higher value
154
+ second_result = cache.get(topic) { fail "Should not be called" }
155
+ expect(second_result).to eq(higher_partition_count)
156
+ end
157
+
158
+ it "handles multiple topics independently" do
159
+ # Set up both topics with different values
160
+ cache.get(topic) { partition_count }
161
+ cache.get(topic2) { higher_partition_count }
162
+
163
+ # Wait for TTL to expire
164
+ sleep(default_ttl + 0.1)
165
+
166
+ # Update first topic
167
+ first_result = cache.get(topic) { even_higher_partition_count }
168
+ expect(first_result).to eq(even_higher_partition_count)
169
+
170
+ # Update second topic independently
171
+ second_updated = higher_partition_count + 3
172
+ second_result = cache.get(topic2) { second_updated }
173
+ expect(second_result).to eq(second_updated)
174
+
175
+ # Both topics should have their updated values
176
+ expect(cache.get(topic) { fail "Should not be called" }).to eq(even_higher_partition_count)
177
+ expect(cache.get(topic2) { fail "Should not be called" }).to eq(second_updated)
178
+ end
179
+ end
180
+ end
181
+
182
+ describe "#set" do
183
+ context "when cache is empty" do
184
+ it "adds a new entry to the cache" do
185
+ cache.set(topic, partition_count)
186
+
187
+ # Verify through get
188
+ result = cache.get(topic) { fail "Should not be called" }
189
+ expect(result).to eq(partition_count)
190
+ end
191
+ end
192
+
193
+ context "when cache already has a value" do
194
+ before do
195
+ cache.set(topic, partition_count)
196
+ end
197
+
198
+ it "updates cache when new value is higher" do
199
+ cache.set(topic, higher_partition_count)
200
+
201
+ result = cache.get(topic) { fail "Should not be called" }
202
+ expect(result).to eq(higher_partition_count)
203
+ end
204
+
205
+ it "keeps original value when new value is lower" do
206
+ cache.set(topic, lower_partition_count)
207
+
208
+ result = cache.get(topic) { fail "Should not be called" }
209
+ expect(result).to eq(partition_count)
210
+ end
211
+
212
+ it "updates the timestamp even when keeping original value" do
213
+ # Set initial value
214
+ cache.set(topic, partition_count)
215
+
216
+ # Wait until close to TTL expiring
217
+ sleep(default_ttl - 0.2)
218
+
219
+ # Set lower value (should update timestamp but not value)
220
+ cache.set(topic, lower_partition_count)
221
+
222
+ # Wait a bit more, but still under the full TTL if timestamp was refreshed
223
+ sleep(0.3)
224
+
225
+ # Should still be valid due to timestamp refresh
226
+ result = cache.get(topic) { fail "Should not be called" }
227
+ expect(result).to eq(partition_count)
228
+ end
229
+ end
230
+
231
+ context "with concurrent access" do
232
+ it "correctly handles simultaneous updates to the same topic" do
233
+ # This test focuses on the final value after concurrent updates
234
+ threads = []
235
+
236
+ # Create 5 threads that all try to update the same topic with increasing values
237
+ 5.times do |i|
238
+ threads << Thread.new do
239
+ value = 10 + i # Start at 10 to ensure all are higher than initial value
240
+ cache.set(topic, value)
241
+ end
242
+ end
243
+
244
+ # Wait for all threads to complete
245
+ threads.each(&:join)
246
+
247
+ # The highest value (14) should be stored and accessible through get
248
+ result = cache.get(topic) { fail "Should not be called" }
249
+ expect(result).to eq(14)
250
+ end
251
+ end
252
+ end
253
+
254
+ describe "TTL behavior" do
255
+ it "treats entries as expired when they exceed TTL" do
256
+ # Set initial value
257
+ cache.get(topic) { partition_count }
258
+
259
+ # Wait just under TTL
260
+ sleep(default_ttl - 0.1)
261
+
262
+ # Value should still be cached (block should not be called)
263
+ result = cache.get(topic) { fail "Should not be called when cache is valid" }
264
+ expect(result).to eq(partition_count)
265
+
266
+ # Now wait to exceed TTL
267
+ sleep(0.2) # Total sleep is now default_ttl + 0.1
268
+
269
+ # Cache should be expired, block should be called
270
+ block_called = false
271
+ new_value = partition_count + 3
272
+ result = cache.get(topic) do
273
+ block_called = true
274
+ new_value
275
+ end
276
+
277
+ expect(block_called).to be true
278
+ expect(result).to eq(new_value)
279
+ end
280
+ end
281
+
282
+ describe "comprehensive scenarios" do
283
+ it "handles a full lifecycle of cache operations" do
284
+ # 1. Initial cache miss, fetch and store
285
+ result1 = cache.get(topic) { partition_count }
286
+ expect(result1).to eq(partition_count)
287
+
288
+ # 2. Cache hit
289
+ result2 = cache.get(topic) { fail "Should not be called" }
290
+ expect(result2).to eq(partition_count)
291
+
292
+ # 3. Attempt to set lower value
293
+ cache.set(topic, lower_partition_count)
294
+ result3 = cache.get(topic) { fail "Should not be called" }
295
+ # Should still return the higher original value
296
+ expect(result3).to eq(partition_count)
297
+
298
+ # 4. Set higher value
299
+ cache.set(topic, higher_partition_count)
300
+ result4 = cache.get(topic) { fail "Should not be called" }
301
+ expect(result4).to eq(higher_partition_count)
302
+
303
+ # 5. TTL expires, new value provided is lower
304
+ sleep(default_ttl + 0.1)
305
+ result5 = cache.get(topic) { lower_partition_count }
306
+ # This returns the highest value
307
+ expect(result5).to eq(higher_partition_count)
308
+
309
+ # 6. But subsequent get should return the higher cached value
310
+ result6 = cache.get(topic) { fail "Should not be called" }
311
+ expect(result6).to eq(higher_partition_count)
312
+
313
+ # 7. Set new highest value directly
314
+ even_higher = higher_partition_count + 5
315
+ cache.set(topic, even_higher)
316
+ result7 = cache.get(topic) { fail "Should not be called" }
317
+ expect(result7).to eq(even_higher)
318
+ end
319
+
320
+ it "handles multiple topics with different TTLs correctly" do
321
+ # Set up initial values
322
+ cache.get(topic) { partition_count }
323
+ custom_ttl_cache.get(topic) { partition_count }
324
+
325
+ # Wait past custom TTL but not default TTL
326
+ sleep(custom_ttl + 0.1)
327
+
328
+ # Default cache should NOT refresh (still within default TTL)
329
+ default_result = cache.get(topic) { fail "Should not be called for default cache" }
330
+ # Original value should be maintained
331
+ expect(default_result).to eq(partition_count)
332
+
333
+ # Custom TTL cache SHOULD refresh (past custom TTL)
334
+ custom_cache_value = partition_count + 8
335
+ custom_block_called = false
336
+ custom_result = custom_ttl_cache.get(topic) do
337
+ custom_block_called = true
338
+ custom_cache_value
339
+ end
340
+
341
+ expect(custom_block_called).to be true
342
+ expect(custom_result).to eq(custom_cache_value)
343
+
344
+ # Now wait past default TTL
345
+ sleep(default_ttl - custom_ttl + 0.1)
346
+
347
+ # Now default cache should also refresh
348
+ default_block_called = false
349
+ new_default_value = partition_count + 10
350
+ new_default_result = cache.get(topic) do
351
+ default_block_called = true
352
+ new_default_value
353
+ end
354
+
355
+ expect(default_block_called).to be true
356
+ expect(new_default_result).to eq(new_default_value)
357
+ end
358
+ end
359
+ end