Class: ActiveSupport::Cache::CouchbaseStore

Inherits:
Store
  • Object
show all
Includes:
LocalCacheWithRaw, Strategy::LocalCache
Defined in:
lib/active_support/cache/couchbase_store.rb

Overview

A cache store implementation which stores data in Couchbase: couchbase.com

  • Local cache. Hot in-memory primary cache within block/middleware scope.

  • read_multi and write_multi support.

  • delete_matched support using N1QL queries.

  • clear for erasing whole collection (optionally can flush the bucket).

To use this store, add the select it in application config

config.cache_store = :couchbase_store, {
  connection_string: "couchbase://localhost",
  username: "app_cache_user",
  password: "s3cret",
  bucket: "app_cache"
}

Defined Under Namespace

Modules: LocalCacheWithRaw

Constant Summary collapse

MAX_KEY_BYTESIZE =
250
DEFAULT_ERROR_HANDLER =
lambda do |method:, returning:, exception:, logger: CouchbaseStore.logger|
  logger&.error { "CouchbaseStore: #{method} failed, returned #{returning.inspect}: #{exception.class}: #{exception.message}" }
end

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(options = nil) ⇒ CouchbaseStore

Returns a new instance of CouchbaseStore.



77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
# File 'lib/active_support/cache/couchbase_store.rb', line 77

def initialize(options = nil)
  super
  @error_handler = options.delete(:error_handler) { DEFAULT_ERROR_HANDLER }
  @couchbase_options = {}
  @couchbase_options[:connection_string] =
    @options.delete(:connection_string) do
      raise ArgumentError, "Missing connection string for Couchbase cache store. Use :connection_string in the store options"
    end
  @couchbase_options[:username] =
    @options.delete(:username) do
      raise ArgumentError, "Missing username for Couchbase cache store. Use :username in the store options"
    end
  @couchbase_options[:password] =
    @options.delete(:password) do
      raise ArgumentError, "Missing password for Couchbase cache store. Use :password in the store options"
    end
  @couchbase_options[:bucket] =
    @options.delete(:bucket) { raise ArgumentError, "Missing bucket for Couchbase cache store. Use :bucket in the store options" }
  @couchbase_options[:scope] = @options.delete(:scope) if @options.key?(:scope)
  @couchbase_options[:collection] = @options.delete(:collection) if @options.key?(:collection)
  @last_mutation_token = nil
end

Class Method Details

.supports_cache_versioning?Boolean

Advertise cache versioning support.

Returns:

  • (Boolean)


43
44
45
# File 'lib/active_support/cache/couchbase_store.rb', line 43

def self.supports_cache_versioning?
  true
end

Instance Method Details

#clear(use_flush: false, **_options) ⇒ Object

Clears the entire cache. Be careful with this method since it could affect other processes if shared cache is being used.

When use_flush option set to true it will flush the bucket. Otherwise, it uses N1QL query and relies on default index.



183
184
185
186
187
188
189
190
191
192
193
# File 'lib/active_support/cache/couchbase_store.rb', line 183

def clear(use_flush: false, **_options)
  failsafe(:clear) do
    if use_flush
      cluster.buckets.flush_bucket(@couchbase_options[:bucket_name])
    else
      operation_options = ::Couchbase::Options::Query.new
      operation_options.consistent_with(::Couchbase::MutationState.new(@last_mutation_token)) if @last_mutation_token
      cluster.query("DELETE FROM #{scope_qualifier}", operation_options)
    end
  end
end

#clusterObject



104
105
106
# File 'lib/active_support/cache/couchbase_store.rb', line 104

def cluster
  @cluster ||= build_cluster
end

#collectionObject



100
101
102
# File 'lib/active_support/cache/couchbase_store.rb', line 100

def collection
  @collection ||= build_collection
end

#decrement(name, amount = 1, expires_in: nil, initial: nil, **_options) ⇒ Object

Decrements an integer value in the cache.

Note that it uses binary collection interface, therefore will fail if the value is not represented as ASCII-encoded number using :raw.

Note that the counter represented by non-negative number on the server, and it will not cycle to maximum integer when decrementing zero.



165
166
167
168
169
170
171
172
173
174
175
176
# File 'lib/active_support/cache/couchbase_store.rb', line 165

def decrement(name, amount = 1, expires_in: nil, initial: nil, **_options)
  instrument :decrement, name, amount: amount do
    failsafe :decrement do
      key = normalize_key(name, options)
      res = collection.binary.decrement(
        key, ::Couchbase::Options::Decrement(delta: amount, expiry: expires_in, initial: initial)
      )
      @last_mutation_token = res.mutation_token
      res.content
    end
  end
end

#delete_matched(matcher, _options = nil) ⇒ Object

Deletes all entries with keys matching the regular expression.

The matcher must be valid pattern for N1QL REGEXP_MATCHES function. More info at docs.couchbase.com/server/current/n1ql/n1ql-language-reference/patternmatchingfun.html#section_regex_matches

Because the operation performed on query engine, and it might take time to propagate changes from key/value engine to the indexer. Therefore the keys, that were created a moment ago might not be deleted.

Also this method assumes, that primary index created on the target bucket



122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
# File 'lib/active_support/cache/couchbase_store.rb', line 122

def delete_matched(matcher, _options = nil)
  pattern =
    case matcher
    when Regexp
      matcher.inspect[1..-2]
    when String
      matcher.tr("?", ".").gsub("*", ".*")
    else
      raise NotImplementedError, "Unable to convert #{matcher.inspect} to Regexp pattern"
    end
  operation_options = ::Couchbase::Options::Query(named_parameters: {"pattern" => pattern})
  operation_options.consistent_with(::Couchbase::MutationState.new(@last_mutation_token)) if @last_mutation_token
  begin
    cluster.query("DELETE FROM #{scope_qualifier} cache WHERE REGEXP_MATCHES(META(cache).id, $pattern)", operation_options)
  rescue ::Couchbase::Error::ParsingFailure, ::Couchbase::Error::ServiceNotAvailable
    raise NotImplementedError, "The server does not support delete_matched operation"
  end
end

#increment(name, amount = 1, expires_in: nil, initial: nil, **options) ⇒ Object

Increments an integer value in the cache.

Note that it uses binary collection interface, therefore will fail if the value is not represented as ASCII-encoded number using :raw.



145
146
147
148
149
150
151
152
153
154
155
156
# File 'lib/active_support/cache/couchbase_store.rb', line 145

def increment(name, amount = 1, expires_in: nil, initial: nil, **options)
  instrument :increment, name, amount: amount do
    failsafe :increment do
      key = normalize_key(name, options)
      res = collection.binary.increment(
        key, ::Couchbase::Options::Increment(delta: amount, expiry: expires_in, initial: initial)
      )
      @last_mutation_token = res.mutation_token
      res.content
    end
  end
end

#inspectObject



108
109
110
# File 'lib/active_support/cache/couchbase_store.rb', line 108

def inspect
  "#<#{self.class} options=#{options.inspect} collection=#{@collection.inspect}>"
end