Skip to content

Commit f325ea2

Browse files
committed
refactor(config): Use :indefinite symbol instead of Float::INFINITY for cache_stale_ttl
Improves API ergonomics by replacing Float::INFINITY with a more Ruby-idiomatic :indefinite symbol for never-expiring cache configuration. Rationale: - :indefinite is more readable and Ruby-idiomatic than Float::INFINITY - Symbols are the conventional way to represent special values in Ruby config - Makes intent clearer in configuration code - Aligns with Ruby community conventions (e.g., Rails cache :expires_in) Changes: - Renamed THOUSAND_YEARS_IN_SECONDS constant to INDEFINITE_SECONDS - Updated constant comment to clarify intent ("indefinite cache duration") - Changed normalize_stale_ttl from class method to instance method (normalized_stale_ttl) - Instance method reads from cache_stale_ttl and returns normalized value on-demand - Removed normalization from Config#initialize (now done lazily via method) - Updated create_memory_cache to use config.normalized_stale_ttl - Updated create_rails_cache_adapter to use config.normalized_stale_ttl - Updated validate_swr_config! to handle :indefinite symbol properly - Updated validation error message: "must be non-negative or :indefinite" Documentation Updates: - docs/CACHING.md: Changed Float::INFINITY to :indefinite in examples - docs/CONFIGURATION.md: Changed Float::INFINITY to :indefinite in examples - lib/langfuse/config.rb: Updated attr_accessor doc to mention :indefinite - lib/langfuse/prompt_cache.rb: Updated comment to reflect :indefinite normalization Test Coverage: - Updated all tests to use :indefinite instead of Float::INFINITY - Updated all tests to use INDEFINITE_SECONDS instead of THOUSAND_YEARS_IN_SECONDS - Updated test expectations for new validation error message - All 797 examples passing, 96.86% coverage maintained Benefits: - More intuitive API for developers (config.cache_stale_ttl = :indefinite) - Clearer intent in configuration code - Normalization happens on-demand via instance method - Original config value preserved (not mutated during initialization) - Better alignment with Ruby conventions and best practices
1 parent bd0a896 commit f325ea2

File tree

7 files changed

+84
-33
lines changed

7 files changed

+84
-33
lines changed

docs/CACHING.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -198,7 +198,7 @@ Langfuse.configure do |config|
198198
config.cache_backend = :memory
199199
config.cache_ttl = 300 # Still refreshes every 5 minutes
200200
config.cache_stale_while_revalidate = true
201-
config.cache_stale_ttl = Float::INFINITY # Never expire (normalized to 1000 years internally)
201+
config.cache_stale_ttl = :indefinite # Never expire (normalized to 1000 years internally)
202202
end
203203
```
204204

docs/CONFIGURATION.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -151,7 +151,7 @@ See [CACHING.md](CACHING.md#stale-while-revalidate-swr) for detailed usage.
151151

152152
```ruby
153153
config.cache_stale_ttl = 300 # Serve stale data for up to 5 minutes
154-
config.cache_stale_ttl = Float::INFINITY # Never expire (normalized to 1000 years internally)
154+
config.cache_stale_ttl = :indefinite # Never expire (normalized to 1000 years internally)
155155
```
156156

157157
**How it works:**

lib/langfuse/client.rb

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -292,7 +292,7 @@ def create_memory_cache
292292
PromptCache.new(
293293
ttl: config.cache_ttl,
294294
max_size: config.cache_max_size,
295-
stale_ttl: config.cache_stale_ttl,
295+
stale_ttl: config.normalized_stale_ttl,
296296
refresh_threads: config.cache_refresh_threads,
297297
logger: config.logger
298298
)
@@ -302,7 +302,7 @@ def create_rails_cache_adapter
302302
RailsCacheAdapter.new(
303303
ttl: config.cache_ttl,
304304
lock_timeout: config.cache_lock_timeout,
305-
stale_ttl: config.cache_stale_ttl,
305+
stale_ttl: config.normalized_stale_ttl,
306306
refresh_threads: config.cache_refresh_threads,
307307
logger: config.logger
308308
)

lib/langfuse/config.rb

Lines changed: 15 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -49,8 +49,8 @@ class Config
4949
# @return [Boolean] Enable stale-while-revalidate caching (when true, sets cache_stale_ttl to cache_ttl if not customized)
5050
attr_accessor :cache_stale_while_revalidate
5151

52-
# @return [Integer] Stale TTL in seconds (grace period for serving stale data, default: 0 when SWR disabled, cache_ttl when SWR enabled)
53-
# Accepts Float::INFINITY which is automatically normalized to 1000 years (31,557,600,000 seconds) for practical "never expire" behavior.
52+
# @return [Integer, Symbol] Stale TTL in seconds (grace period for serving stale data, default: 0 when SWR disabled, cache_ttl when SWR enabled)
53+
# Accepts :indefinite which is automatically normalized to 1000 years (31,557,600,000 seconds) for practical "never expire" behavior.
5454
attr_accessor :cache_stale_ttl
5555

5656
# @return [Integer] Number of background threads for cache refresh
@@ -82,8 +82,8 @@ class Config
8282
DEFAULT_FLUSH_INTERVAL = 10
8383
DEFAULT_JOB_QUEUE = :default
8484

85-
# Number of seconds in 1000 years (accounting for leap years)
86-
THOUSAND_YEARS_IN_SECONDS = (1000 * 365.25 * 24 * 60 * 60).to_i
85+
# Number of seconds representing indefinite cache duration (~1000 years)
86+
INDEFINITE_SECONDS = 1000 * 365 * 24 * 60 * 60
8787

8888
# Initialize a new Config object
8989
#
@@ -111,9 +111,6 @@ def initialize
111111

112112
# Apply SWR defaults after yield to allow customization
113113
apply_swr_defaults!
114-
115-
# Normalize stale_ttl after applying defaults
116-
@cache_stale_ttl = self.class.normalize_stale_ttl(@cache_stale_ttl)
117114
end
118115

119116
# Validate the configuration
@@ -143,19 +140,19 @@ def validate!
143140

144141
# Normalize stale_ttl value
145142
#
146-
# Converts Float::INFINITY to 1000 years in seconds for practical "never expire"
143+
# Converts :indefinite to 1000 years in seconds for practical "never expire"
147144
# behavior while keeping the value finite for calculations.
148145
#
149-
# @param stale_ttl [Integer, Float::INFINITY] Stale TTL value
150146
# @return [Integer] Normalized stale TTL in seconds
151147
#
152148
# @example
153-
# Config.normalize_stale_ttl(300) # => 300
154-
# Config.normalize_stale_ttl(Float::INFINITY) # => 31557600000
155-
def self.normalize_stale_ttl(stale_ttl)
156-
return THOUSAND_YEARS_IN_SECONDS if stale_ttl == Float::INFINITY
157-
158-
stale_ttl
149+
# config.cache_stale_ttl = 300
150+
# config.normalized_stale_ttl # => 300
151+
#
152+
# config.cache_stale_ttl = :indefinite
153+
# config.normalized_stale_ttl # => 31557600000
154+
def normalized_stale_ttl
155+
cache_stale_ttl == :indefinite ? INDEFINITE_SECONDS : cache_stale_ttl
159156
end
160157

161158
private
@@ -177,9 +174,10 @@ def validate_cache_backend!
177174
end
178175

179176
def validate_swr_config!
180-
if cache_stale_ttl.nil? || cache_stale_ttl.negative?
177+
# Allow :indefinite symbol, but validate numeric values
178+
if cache_stale_ttl.nil? || (cache_stale_ttl.is_a?(Integer) && cache_stale_ttl.negative?)
181179
raise ConfigurationError,
182-
"cache_stale_ttl must be non-negative"
180+
"cache_stale_ttl must be non-negative or :indefinite"
183181
end
184182

185183
return unless cache_refresh_threads.nil? || cache_refresh_threads <= 0

lib/langfuse/prompt_cache.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@ def expired?
6060
# @param ttl [Integer] Time-to-live in seconds (default: 60)
6161
# @param max_size [Integer] Maximum cache size (default: 1000)
6262
# @param stale_ttl [Integer] Stale TTL for SWR in seconds (default: 0, SWR disabled).
63-
# Note: Float::INFINITY is normalized to 1000 years by Config before being passed here.
63+
# Note: :indefinite is normalized to 1000 years by Config before being passed here.
6464
# @param refresh_threads [Integer] Number of background refresh threads (default: 5)
6565
# @param logger [Logger, nil] Logger instance for error reporting (default: nil, creates new logger)
6666
def initialize(ttl: 60, max_size: 1000, stale_ttl: 0, refresh_threads: 5, logger: default_logger)

spec/langfuse/client_spec.rb

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -172,41 +172,41 @@ class << self
172172
end
173173
end
174174

175-
context "with Float::INFINITY stale_ttl" do
176-
it "normalizes Float::INFINITY to THOUSAND_YEARS_IN_SECONDS during config initialization" do
175+
context "with :indefinite stale_ttl" do
176+
it "normalizes :indefinite to INDEFINITE_SECONDS via normalized_stale_ttl method" do
177177
config = Langfuse::Config.new do |c|
178178
c.public_key = "pk_test_123"
179179
c.secret_key = "sk_test_456"
180-
c.cache_stale_ttl = Float::INFINITY
180+
c.cache_stale_ttl = :indefinite
181181
end
182182

183-
expect(config.cache_stale_ttl).to eq(Langfuse::Config::THOUSAND_YEARS_IN_SECONDS)
183+
expect(config.normalized_stale_ttl).to eq(Langfuse::Config::INDEFINITE_SECONDS)
184184
end
185185

186186
it "passes normalized stale_ttl to cache instances" do
187187
config = Langfuse::Config.new do |c|
188188
c.public_key = "pk_test_123"
189189
c.secret_key = "sk_test_456"
190190
c.cache_ttl = 60
191-
c.cache_stale_ttl = Float::INFINITY
191+
c.cache_stale_ttl = :indefinite
192192
end
193193

194194
client = described_class.new(config)
195195
cache = client.api_client.cache
196196

197-
expect(cache.stale_ttl).to eq(Langfuse::Config::THOUSAND_YEARS_IN_SECONDS)
197+
expect(cache.stale_ttl).to eq(Langfuse::Config::INDEFINITE_SECONDS)
198198
end
199199

200-
it "normalizes Float::INFINITY when set via cache_stale_while_revalidate" do
200+
it "normalizes :indefinite when set via cache_stale_while_revalidate" do
201201
config = Langfuse::Config.new do |c|
202202
c.public_key = "pk_test_123"
203203
c.secret_key = "sk_test_456"
204204
c.cache_ttl = 60
205205
c.cache_stale_while_revalidate = true
206-
c.cache_stale_ttl = Float::INFINITY
206+
c.cache_stale_ttl = :indefinite
207207
end
208208

209-
expect(config.cache_stale_ttl).to eq(Langfuse::Config::THOUSAND_YEARS_IN_SECONDS)
209+
expect(config.normalized_stale_ttl).to eq(Langfuse::Config::INDEFINITE_SECONDS)
210210
end
211211
end
212212
end

spec/langfuse/config_spec.rb

Lines changed: 55 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -228,15 +228,15 @@
228228
config.cache_stale_ttl = -1
229229
expect { config.validate! }.to raise_error(
230230
Langfuse::ConfigurationError,
231-
"cache_stale_ttl must be non-negative"
231+
"cache_stale_ttl must be non-negative or :indefinite"
232232
)
233233
end
234234

235235
it "raises ConfigurationError when nil" do
236236
config.cache_stale_ttl = nil
237237
expect { config.validate! }.to raise_error(
238238
Langfuse::ConfigurationError,
239-
"cache_stale_ttl must be non-negative"
239+
"cache_stale_ttl must be non-negative or :indefinite"
240240
)
241241
end
242242

@@ -249,6 +249,11 @@
249249
config.cache_stale_ttl = 300
250250
expect { config.validate! }.not_to raise_error
251251
end
252+
253+
it "allows :indefinite symbol" do
254+
config.cache_stale_ttl = :indefinite
255+
expect { config.validate! }.not_to raise_error
256+
end
252257
end
253258

254259
context "when cache_refresh_threads is invalid" do
@@ -363,6 +368,11 @@
363368
expect(config.cache_stale_ttl).to eq(600)
364369
end
365370

371+
it "allows setting cache_stale_ttl to :indefinite" do
372+
config.cache_stale_ttl = :indefinite
373+
expect(config.cache_stale_ttl).to eq(:indefinite)
374+
end
375+
366376
it "allows setting cache_refresh_threads" do
367377
config.cache_refresh_threads = 10
368378
expect(config.cache_refresh_threads).to eq(10)
@@ -443,4 +453,47 @@
443453
expect(Langfuse::Config::DEFAULT_CACHE_REFRESH_THREADS).to eq(5)
444454
end
445455
end
456+
457+
describe "#normalized_stale_ttl" do
458+
let(:config) do
459+
described_class.new do |c|
460+
c.public_key = "pk_test"
461+
c.secret_key = "sk_test"
462+
end
463+
end
464+
465+
it "returns the numeric value unchanged for regular TTL" do
466+
config.cache_stale_ttl = 300
467+
expect(config.normalized_stale_ttl).to eq(300)
468+
end
469+
470+
it "returns 0 for zero TTL" do
471+
config.cache_stale_ttl = 0
472+
expect(config.normalized_stale_ttl).to eq(0)
473+
end
474+
475+
it "returns INDEFINITE_SECONDS when cache_stale_ttl is :indefinite" do
476+
config.cache_stale_ttl = :indefinite
477+
expect(config.normalized_stale_ttl).to eq(Langfuse::Config::INDEFINITE_SECONDS)
478+
end
479+
480+
it "does not mutate the original cache_stale_ttl value" do
481+
config.cache_stale_ttl = :indefinite
482+
config.normalized_stale_ttl # Call normalization
483+
expect(config.cache_stale_ttl).to eq(:indefinite) # Original value preserved
484+
end
485+
486+
it "works with SWR auto-configuration" do
487+
config_swr = described_class.new do |c|
488+
c.public_key = "pk_test"
489+
c.secret_key = "sk_test"
490+
c.cache_ttl = 120
491+
c.cache_stale_while_revalidate = true
492+
c.cache_stale_ttl = :indefinite
493+
end
494+
495+
expect(config_swr.cache_stale_ttl).to eq(:indefinite)
496+
expect(config_swr.normalized_stale_ttl).to eq(Langfuse::Config::INDEFINITE_SECONDS)
497+
end
498+
end
446499
end

0 commit comments

Comments
 (0)