Class: Parse::Embeddings::Cache::MonetaStore

Inherits:
Object
  • Object
show all
Defined in:
lib/parse/embeddings/cache.rb

Overview

Adapter exposing any Moneta-compatible key/value store ([] / []=, optionally store(key, value, expires:)) through the get/set duck enable! expects — the persistent-L2 option. Point it at the same Redis your Parse.cache uses and query-embed cache entries survive process restarts and are shared across processes:

require "moneta" moneta = Moneta.new(:Redis, url: ENV["REDIS_URL"]) Parse::Embeddings::Cache.enable!( store: Parse::Embeddings::Cache::MonetaStore.new(moneta, ttl: 30 * 24 * 3600), )

Keys are namespaced (emb: by default) so the entries are recognizable next to other application keys; values are the raw vector Arrays (Moneta's own serializer handles encoding). TTL is forwarded via Moneta's expires: option when the backend supports it, ignored otherwise.

Fail-open by design: a backend error (Redis down, serialization hiccup) degrades to a cache miss / dropped write — the embed path must never fail because the CACHE is unhealthy.

The cross-process race the in-process LRU doesn't have applies here: two processes missing the same key concurrently both call the provider and both write. That is correct (embeddings are deterministic per key) and bounded — no locking is attempted.

Instance Method Summary collapse

Constructor Details

#initialize(moneta, ttl: nil, namespace: "emb:") ⇒ MonetaStore

Returns a new instance of MonetaStore.

Parameters:

  • moneta (#[], #[]=)

    a Moneta store (or anything with the same indexing duck).

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

    per-entry lifetime in seconds, forwarded as expires: when the backend supports store(key, value, expires:). nil = no expiry.

  • namespace (String) (defaults to: "emb:")

    key prefix.



118
119
120
121
122
123
124
125
126
127
# File 'lib/parse/embeddings/cache.rb', line 118

def initialize(moneta, ttl: nil, namespace: "emb:")
  unless moneta.respond_to?(:[]) && moneta.respond_to?(:[]=)
    raise ArgumentError,
          "Parse::Embeddings::Cache::MonetaStore expects a Moneta-compatible " \
          "store responding to #[] and #[]= (got #{moneta.class})."
  end
  @moneta = moneta
  @ttl = ttl && Float(ttl)
  @namespace = namespace.to_s
end

Instance Method Details

#get(key) ⇒ Array<Float>?

Returns:



130
131
132
133
134
135
# File 'lib/parse/embeddings/cache.rb', line 130

def get(key)
  value = @moneta[@namespace + key]
  value.is_a?(Array) ? value : nil
rescue StandardError
  nil
end

#set(key, vector) ⇒ Array<Float>

Returns the vector, unchanged.

Returns:

  • (Array<Float>)

    the vector, unchanged.



138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
# File 'lib/parse/embeddings/cache.rb', line 138

def set(key, vector)
  k = @namespace + key
  if @ttl && @moneta.respond_to?(:store)
    begin
      @moneta.store(k, vector, expires: @ttl)
    rescue ArgumentError
      # Hash-like backends define #store(key, value) with no
      # options arg, so the expires: form raises ArgumentError.
      # Fall back to a plain write (no expiry) rather than letting
      # the fail-open rescue below silently drop every vector.
      @moneta[k] = vector
    end
  else
    @moneta[k] = vector
  end
  vector
rescue StandardError
  vector
end