Class: Woods::Cache::InMemory

Inherits:
CacheStore show all
Defined in:
lib/woods/cache/cache_store.rb

Overview

In-memory cache store with LRU eviction and TTL support.

Zero external dependencies. Suitable for single-process use, development, and as a fallback when Redis/SolidCache are not available. Thread-safe.

Examples:

store = InMemory.new(max_entries: 200)
store.write("ci:emb:abc", [0.1, 0.2], ttl: 3600)
store.read("ci:emb:abc") # => [0.1, 0.2]

Instance Method Summary collapse

Methods inherited from CacheStore

#fetch

Constructor Details

#initialize(max_entries: 500) ⇒ InMemory

Returns a new instance of InMemory.

Parameters:

  • max_entries (Integer) (defaults to: 500)

    Maximum cached entries before LRU eviction



149
150
151
152
153
154
155
# File 'lib/woods/cache/cache_store.rb', line 149

def initialize(max_entries: 500)
  super()
  @max_entries = max_entries
  @entries = {}
  @access_order = []
  @mutex = Mutex.new
end

Instance Method Details

#clear(namespace: nil) ⇒ void

This method returns an undefined value.

Clear entries. If namespace is given, only clear keys matching that domain.

Parameters:

  • namespace (Symbol, nil) (defaults to: nil)

    Domain to clear (:embeddings, :metadata, etc.)



223
224
225
226
227
228
229
230
231
232
233
234
# File 'lib/woods/cache/cache_store.rb', line 223

def clear(namespace: nil)
  @mutex.synchronize do
    if namespace
      prefix = "woods:cache:#{namespace}:"
      keys_to_delete = @entries.keys.select { |k| k.start_with?(prefix) }
      keys_to_delete.each { |k| evict_key(k) }
    else
      @entries.clear
      @access_order.clear
    end
  end
end

#delete(key) ⇒ void

This method returns an undefined value.

Delete a key.

Parameters:

  • key (String)

    Cache key



201
202
203
# File 'lib/woods/cache/cache_store.rb', line 201

def delete(key)
  @mutex.synchronize { evict_key(key) }
end

#exist?(key) ⇒ Boolean

Check if a key exists and is not expired.

Parameters:

  • key (String)

    Cache key

Returns:

  • (Boolean)


209
210
211
212
213
214
215
216
217
# File 'lib/woods/cache/cache_store.rb', line 209

def exist?(key)
  @mutex.synchronize do
    entry = @entries[key]
    return false unless entry
    return false if entry[:expires_at] && Time.now > entry[:expires_at]

    true
  end
end

#read(key) ⇒ Object?

Read a value, returning nil if missing or expired.

Parameters:

  • key (String)

    Cache key

Returns:

  • (Object, nil)


161
162
163
164
165
166
167
168
169
170
171
172
173
174
# File 'lib/woods/cache/cache_store.rb', line 161

def read(key)
  @mutex.synchronize do
    entry = @entries[key]
    return nil unless entry

    if entry[:expires_at] && Time.now > entry[:expires_at]
      evict_key(key)
      return nil
    end

    touch(key)
    entry[:value]
  end
end

#sizeInteger

Number of entries currently in the cache (for testing/diagnostics).

Returns:

  • (Integer)


239
240
241
# File 'lib/woods/cache/cache_store.rb', line 239

def size
  @mutex.synchronize { @entries.size }
end

#write(key, value, ttl: nil) ⇒ void

This method returns an undefined value.

Write a value with optional TTL.

Parameters:

  • key (String)

    Cache key

  • value (Object)

    Value to cache

  • ttl (Integer, nil) (defaults to: nil)

    TTL in seconds



182
183
184
185
186
187
188
189
190
191
192
193
194
195
# File 'lib/woods/cache/cache_store.rb', line 182

def write(key, value, ttl: nil)
  @mutex.synchronize do
    evict_key(key) if @entries.key?(key)

    if @entries.size >= @max_entries
      oldest = @access_order.shift
      @entries.delete(oldest) if oldest
    end

    expires_at = ttl ? Time.now + ttl : nil
    @entries[key] = { value: value, expires_at: expires_at }
    @access_order.push(key)
  end
end