solid_cache 0.6.0 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (29) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +110 -106
  3. data/Rakefile +11 -2
  4. data/app/models/solid_cache/entry/encryption.rb +15 -0
  5. data/app/models/solid_cache/entry/size/estimate.rb +1 -1
  6. data/app/models/solid_cache/entry/size/moving_average_estimate.rb +2 -2
  7. data/app/models/solid_cache/entry.rb +47 -97
  8. data/db/migrate/20240820123641_create_solid_cache_entries.rb +29 -0
  9. data/lib/generators/solid_cache/install/templates/config/solid_cache.yml.tt +1 -1
  10. data/lib/solid_cache/configuration.rb +20 -2
  11. data/lib/solid_cache/connections/sharded.rb +3 -4
  12. data/lib/solid_cache/connections.rb +1 -6
  13. data/lib/solid_cache/engine.rb +10 -0
  14. data/lib/solid_cache/store/api.rb +17 -6
  15. data/lib/solid_cache/store/connections.rb +108 -0
  16. data/lib/solid_cache/store/entries.rb +9 -7
  17. data/lib/solid_cache/{cluster → store}/execution.rb +4 -4
  18. data/lib/solid_cache/{cluster → store}/expiry.rb +1 -1
  19. data/lib/solid_cache/{cluster → store}/stats.rb +2 -2
  20. data/lib/solid_cache/store.rb +1 -5
  21. data/lib/solid_cache/version.rb +1 -1
  22. metadata +15 -19
  23. data/db/migrate/20230724121448_create_solid_cache_entries.rb +0 -11
  24. data/db/migrate/20240108155507_add_key_hash_and_byte_size_to_solid_cache_entries.rb +0 -8
  25. data/db/migrate/20240110111600_add_key_hash_and_byte_size_indexes_and_null_constraints_to_solid_cache_entries.rb +0 -11
  26. data/db/migrate/20240110111702_remove_key_index_from_solid_cache_entries.rb +0 -7
  27. data/lib/solid_cache/cluster/connections.rb +0 -55
  28. data/lib/solid_cache/cluster.rb +0 -18
  29. data/lib/solid_cache/store/clusters.rb +0 -83
@@ -2,12 +2,15 @@
2
2
 
3
3
  module SolidCache
4
4
  class Configuration
5
- attr_reader :store_options, :connects_to, :executor, :size_estimate_samples
5
+ attr_reader :store_options, :connects_to, :executor, :size_estimate_samples, :encrypt, :encryption_context_properties
6
6
 
7
- def initialize(store_options: {}, database: nil, databases: nil, connects_to: nil, executor: nil, size_estimate_samples: 10_000)
7
+ def initialize(store_options: {}, database: nil, databases: nil, connects_to: nil, executor: nil, encrypt: false, encryption_context_properties: nil, size_estimate_samples: 10_000)
8
8
  @store_options = store_options
9
9
  @size_estimate_samples = size_estimate_samples
10
10
  @executor = executor
11
+ @encrypt = encrypt
12
+ @encryption_context_properties = encryption_context_properties
13
+ @encryption_context_properties ||= default_encryption_context_properties if encrypt?
11
14
  set_connects_to(database: database, databases: databases, connects_to: connects_to)
12
15
  end
13
16
 
@@ -19,6 +22,10 @@ module SolidCache
19
22
  sharded? ? connects_to[:shards].keys : []
20
23
  end
21
24
 
25
+ def encrypt?
26
+ encrypt.present?
27
+ end
28
+
22
29
  private
23
30
  def set_connects_to(database:, databases:, connects_to:)
24
31
  if [database, databases, connects_to].compact.size > 1
@@ -37,5 +44,16 @@ module SolidCache
37
44
  nil
38
45
  end
39
46
  end
47
+
48
+ def default_encryption_context_properties
49
+ require "active_record/encryption/message_pack_message_serializer"
50
+
51
+ {
52
+ # No need to compress, the cache does that already
53
+ encryptor: ActiveRecord::Encryption::Encryptor.new(compress: false),
54
+ # Binary column only serializer that is 40% more efficient than the default MessageSerializer
55
+ message_serializer: ActiveRecord::Encryption::MessagePackMessageSerializer.new
56
+ }
57
+ end
40
58
  end
41
59
  end
@@ -5,10 +5,9 @@ module SolidCache
5
5
  class Sharded
6
6
  attr_reader :names, :nodes, :consistent_hash
7
7
 
8
- def initialize(names, nodes)
8
+ def initialize(names)
9
9
  @names = names
10
- @nodes = nodes
11
- @consistent_hash = MaglevHash.new(@nodes.keys)
10
+ @consistent_hash = MaglevHash.new(names)
12
11
  end
13
12
 
14
13
  def with_each(&block)
@@ -35,7 +34,7 @@ module SolidCache
35
34
 
36
35
  private
37
36
  def shard_for(key)
38
- nodes[consistent_hash.node(key)]
37
+ consistent_hash.node(key)
39
38
  end
40
39
  end
41
40
  end
@@ -7,13 +7,8 @@ module SolidCache
7
7
  case options
8
8
  when NilClass
9
9
  names = SolidCache.configuration.shard_keys
10
- nodes = names.to_h { |name| [ name, name ] }
11
10
  when Array
12
11
  names = options.map(&:to_sym)
13
- nodes = names.to_h { |name| [ name, name ] }
14
- when Hash
15
- names = options.keys.map(&:to_sym)
16
- nodes = options.to_h { |names, nodes| [ nodes.to_sym, names.to_sym ] }
17
12
  end
18
13
 
19
14
  if (unknown_shards = names - SolidCache.configuration.shard_keys).any?
@@ -23,7 +18,7 @@ module SolidCache
23
18
  if names.size == 1
24
19
  Single.new(names.first)
25
20
  else
26
- Sharded.new(names, nodes)
21
+ Sharded.new(names)
27
22
  end
28
23
  else
29
24
  Unmanaged.new
@@ -18,6 +18,8 @@ module SolidCache
18
18
 
19
19
  options[:connects_to] = config.solid_cache.connects_to if config.solid_cache.connects_to
20
20
  options[:size_estimate_samples] = config.solid_cache.size_estimate_samples if config.solid_cache.size_estimate_samples
21
+ options[:encrypt] = config.solid_cache.encrypt if config.solid_cache.encrypt
22
+ options[:encryption_context_properties] = config.solid_cache.encryption_context_properties if config.solid_cache.encryption_context_properties
21
23
 
22
24
  SolidCache.configuration = SolidCache::Configuration.new(**options)
23
25
 
@@ -33,5 +35,13 @@ module SolidCache
33
35
  config.after_initialize do
34
36
  Rails.cache.setup! if Rails.cache.is_a?(Store)
35
37
  end
38
+
39
+ config.after_initialize do
40
+ if SolidCache.configuration.encrypt? && SolidCache::Record.connection.adapter_name == "PostgreSQL"
41
+ raise \
42
+ "Cannot enable encryption for Solid Cache: Active Record Encryption does not currently support " \
43
+ "encrypting binary columns on PostgreSQL"
44
+ end
45
+ end
36
46
  end
37
47
  end
@@ -39,16 +39,27 @@ module SolidCache
39
39
  entry_read(key)
40
40
  end
41
41
 
42
- def write_entry(key, entry, raw: false, **options)
42
+ def write_entry(key, entry, raw: false, unless_exist: false, **options)
43
43
  payload = serialize_entry(entry, raw: raw, **options)
44
- # No-op for us, but this writes it to the local cache
45
- write_serialized_entry(key, payload, raw: raw, **options)
46
44
 
47
- entry_write(key, payload)
45
+ if unless_exist
46
+ written = false
47
+ entry_lock_and_write(key) do |value|
48
+ if value.nil? || deserialize_entry(value, **options).expired?
49
+ written = true
50
+ payload
51
+ end
52
+ end
53
+ else
54
+ written = entry_write(key, payload)
55
+ end
56
+
57
+ write_serialized_entry(key, payload, raw: raw, returning: written, **options)
58
+ written
48
59
  end
49
60
 
50
- def write_serialized_entry(key, payload, raw: false, unless_exist: false, expires_in: nil, race_condition_ttl: nil, **options)
51
- true
61
+ def write_serialized_entry(key, payload, raw: false, unless_exist: false, expires_in: nil, race_condition_ttl: nil, returning: true, **options)
62
+ returning
52
63
  end
53
64
 
54
65
  def read_serialized_entries(keys)
@@ -0,0 +1,108 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SolidCache
4
+ class Store
5
+ module Connections
6
+ attr_reader :shard_options
7
+
8
+ def initialize(options = {})
9
+ super(options)
10
+ if options[:clusters].present?
11
+ if options[:clusters].size > 1
12
+ raise ArgumentError, "Multiple clusters are no longer supported"
13
+ else
14
+ ActiveSupport.deprecator.warn(":clusters is deprecated, use :shards instead.")
15
+ end
16
+ @shard_options = options.fetch(:clusters).first[:shards]
17
+ elsif options[:cluster].present?
18
+ ActiveSupport.deprecator.warn(":cluster is deprecated, use :shards instead.")
19
+ @shard_options = options.fetch(:cluster, {})[:shards]
20
+ else
21
+ @shard_options = options.fetch(:shards, nil)
22
+ end
23
+
24
+ if [ Array, NilClass ].none? { |klass| @shard_options.is_a? klass }
25
+ raise ArgumentError, "`shards` is a `#{@shard_options.class.name}`, it should be Array or nil"
26
+ end
27
+ end
28
+
29
+ def with_each_connection(async: false, &block)
30
+ return enum_for(:with_each_connection) unless block_given?
31
+
32
+ connections.with_each do
33
+ execute(async, &block)
34
+ end
35
+ end
36
+
37
+ def with_connection_for(key, async: false, &block)
38
+ connections.with_connection_for(key) do
39
+ execute(async, &block)
40
+ end
41
+ end
42
+
43
+ def with_connection(name, async: false, &block)
44
+ connections.with(name) do
45
+ execute(async, &block)
46
+ end
47
+ end
48
+
49
+ def group_by_connection(keys)
50
+ connections.assign(keys)
51
+ end
52
+
53
+ def connection_names
54
+ connections.names
55
+ end
56
+
57
+ def connections
58
+ @connections ||= SolidCache::Connections.from_config(@shard_options)
59
+ end
60
+
61
+ private
62
+ def setup!
63
+ connections
64
+ end
65
+
66
+ def reading_key(key, failsafe:, failsafe_returning: nil, &block)
67
+ failsafe(failsafe, returning: failsafe_returning) do
68
+ with_connection_for(key, &block)
69
+ end
70
+ end
71
+
72
+ def reading_keys(keys, failsafe:, failsafe_returning: nil)
73
+ group_by_connection(keys).map do |connection, keys|
74
+ failsafe(failsafe, returning: failsafe_returning) do
75
+ with_connection(connection) do
76
+ yield keys
77
+ end
78
+ end
79
+ end
80
+ end
81
+
82
+
83
+ def writing_key(key, failsafe:, failsafe_returning: nil, &block)
84
+ failsafe(failsafe, returning: failsafe_returning) do
85
+ with_connection_for(key, &block)
86
+ end
87
+ end
88
+
89
+ def writing_keys(entries, failsafe:, failsafe_returning: nil)
90
+ group_by_connection(entries).map do |connection, entries|
91
+ failsafe(failsafe, returning: failsafe_returning) do
92
+ with_connection(connection) do
93
+ yield entries
94
+ end
95
+ end
96
+ end
97
+ end
98
+
99
+ def writing_all(failsafe:, failsafe_returning: nil, &block)
100
+ connection_names.map do |connection|
101
+ failsafe(failsafe, returning: failsafe_returning) do
102
+ with_connection(connection, &block)
103
+ end
104
+ end.first
105
+ end
106
+ end
107
+ end
108
+ end
@@ -29,7 +29,9 @@ module SolidCache
29
29
 
30
30
  def entry_lock_and_write(key, &block)
31
31
  writing_key(key, failsafe: :increment) do
32
- Entry.lock_and_write(key, &block)
32
+ Entry.lock_and_write(key) do |value|
33
+ block.call(value).tap { |result| track_writes(1) if result }
34
+ end
33
35
  end
34
36
  end
35
37
 
@@ -46,30 +48,30 @@ module SolidCache
46
48
  end
47
49
 
48
50
  def entry_write(key, payload)
49
- writing_key(key, failsafe: :write_entry, failsafe_returning: nil) do |cluster|
51
+ writing_key(key, failsafe: :write_entry, failsafe_returning: nil) do
50
52
  Entry.write(key, payload)
51
- cluster.track_writes(1)
53
+ track_writes(1)
52
54
  true
53
55
  end
54
56
  end
55
57
 
56
58
  def entry_write_multi(entries)
57
- writing_keys(entries, failsafe: :write_multi_entries, failsafe_returning: false) do |cluster, entries|
59
+ writing_keys(entries, failsafe: :write_multi_entries, failsafe_returning: false) do |entries|
58
60
  Entry.write_multi(entries)
59
- cluster.track_writes(entries.count)
61
+ track_writes(entries.count)
60
62
  true
61
63
  end
62
64
  end
63
65
 
64
66
  def entry_delete(key)
65
67
  writing_key(key, failsafe: :delete_entry, failsafe_returning: false) do
66
- Entry.delete_by_key(key)
68
+ Entry.delete_by_key(key) > 0
67
69
  end
68
70
  end
69
71
 
70
72
  def entry_delete_multi(entries)
71
73
  writing_keys(entries, failsafe: :delete_multi_entries, failsafe_returning: 0) do
72
- Entry.delete_multi(entries)
74
+ Entry.delete_by_key(*entries)
73
75
  end
74
76
  end
75
77
  end
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module SolidCache
4
- class Cluster
4
+ class Store
5
5
  module Execution
6
6
  def initialize(options = {})
7
7
  super(options)
@@ -16,7 +16,7 @@ module SolidCache
16
16
  @background << ->() do
17
17
  wrap_in_rails_executor do
18
18
  connections.with(current_shard) do
19
- instrument(&block)
19
+ setup_instrumentation(&block)
20
20
  end
21
21
  end
22
22
  rescue Exception => exception
@@ -28,7 +28,7 @@ module SolidCache
28
28
  if async
29
29
  async(&block)
30
30
  else
31
- instrument(&block)
31
+ setup_instrumentation(&block)
32
32
  end
33
33
  end
34
34
 
@@ -44,7 +44,7 @@ module SolidCache
44
44
  @active_record_instrumentation
45
45
  end
46
46
 
47
- def instrument(&block)
47
+ def setup_instrumentation(&block)
48
48
  if active_record_instrumentation?
49
49
  block.call
50
50
  else
@@ -3,7 +3,7 @@
3
3
  require "concurrent/atomic/atomic_fixnum"
4
4
 
5
5
  module SolidCache
6
- class Cluster
6
+ class Store
7
7
  module Expiry
8
8
  # For every write that we do, we attempt to delete EXPIRY_MULTIPLIER times as many records.
9
9
  # This ensures there is downward pressure on the cache size while there is valid data to delete
@@ -1,10 +1,10 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module SolidCache
4
- class Cluster
4
+ class Store
5
5
  module Stats
6
6
  def initialize(options = {})
7
- super()
7
+ super(options)
8
8
  end
9
9
 
10
10
  def stats
@@ -2,7 +2,7 @@
2
2
 
3
3
  module SolidCache
4
4
  class Store < ActiveSupport::Cache::Store
5
- include Api, Clusters, Entries, Failsafe
5
+ include Api, Connections, Entries, Execution, Expiry, Failsafe, Stats
6
6
  prepend ActiveSupport::Cache::Strategy::LocalCache
7
7
 
8
8
  def initialize(options = {})
@@ -16,9 +16,5 @@ module SolidCache
16
16
  def setup!
17
17
  super
18
18
  end
19
-
20
- def stats
21
- primary_cluster.stats
22
- end
23
19
  end
24
20
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module SolidCache
4
- VERSION = "0.6.0"
4
+ VERSION = "1.0.0"
5
5
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: solid_cache
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.6.0
4
+ version: 1.0.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Donal McBreen
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2024-03-20 00:00:00.000000000 Z
11
+ date: 2024-08-21 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activerecord
@@ -16,42 +16,42 @@ dependencies:
16
16
  requirements:
17
17
  - - ">="
18
18
  - !ruby/object:Gem::Version
19
- version: '7'
19
+ version: '7.2'
20
20
  type: :runtime
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
24
  - - ">="
25
25
  - !ruby/object:Gem::Version
26
- version: '7'
26
+ version: '7.2'
27
27
  - !ruby/object:Gem::Dependency
28
28
  name: activejob
29
29
  requirement: !ruby/object:Gem::Requirement
30
30
  requirements:
31
31
  - - ">="
32
32
  - !ruby/object:Gem::Version
33
- version: '7'
33
+ version: '7.2'
34
34
  type: :runtime
35
35
  prerelease: false
36
36
  version_requirements: !ruby/object:Gem::Requirement
37
37
  requirements:
38
38
  - - ">="
39
39
  - !ruby/object:Gem::Version
40
- version: '7'
40
+ version: '7.2'
41
41
  - !ruby/object:Gem::Dependency
42
42
  name: railties
43
43
  requirement: !ruby/object:Gem::Requirement
44
44
  requirements:
45
45
  - - ">="
46
46
  - !ruby/object:Gem::Version
47
- version: '7'
47
+ version: '7.2'
48
48
  type: :runtime
49
49
  prerelease: false
50
50
  version_requirements: !ruby/object:Gem::Requirement
51
51
  requirements:
52
52
  - - ">="
53
53
  - !ruby/object:Gem::Version
54
- version: '7'
54
+ version: '7.2'
55
55
  - !ruby/object:Gem::Dependency
56
56
  name: debug
57
57
  requirement: !ruby/object:Gem::Requirement
@@ -106,25 +106,18 @@ files:
106
106
  - Rakefile
107
107
  - app/jobs/solid_cache/expiry_job.rb
108
108
  - app/models/solid_cache/entry.rb
109
+ - app/models/solid_cache/entry/encryption.rb
109
110
  - app/models/solid_cache/entry/expiration.rb
110
111
  - app/models/solid_cache/entry/size.rb
111
112
  - app/models/solid_cache/entry/size/estimate.rb
112
113
  - app/models/solid_cache/entry/size/moving_average_estimate.rb
113
114
  - app/models/solid_cache/record.rb
114
- - db/migrate/20230724121448_create_solid_cache_entries.rb
115
- - db/migrate/20240108155507_add_key_hash_and_byte_size_to_solid_cache_entries.rb
116
- - db/migrate/20240110111600_add_key_hash_and_byte_size_indexes_and_null_constraints_to_solid_cache_entries.rb
117
- - db/migrate/20240110111702_remove_key_index_from_solid_cache_entries.rb
115
+ - db/migrate/20240820123641_create_solid_cache_entries.rb
118
116
  - lib/active_support/cache/solid_cache_store.rb
119
117
  - lib/generators/solid_cache/install/USAGE
120
118
  - lib/generators/solid_cache/install/install_generator.rb
121
119
  - lib/generators/solid_cache/install/templates/config/solid_cache.yml.tt
122
120
  - lib/solid_cache.rb
123
- - lib/solid_cache/cluster.rb
124
- - lib/solid_cache/cluster/connections.rb
125
- - lib/solid_cache/cluster/execution.rb
126
- - lib/solid_cache/cluster/expiry.rb
127
- - lib/solid_cache/cluster/stats.rb
128
121
  - lib/solid_cache/configuration.rb
129
122
  - lib/solid_cache/connections.rb
130
123
  - lib/solid_cache/connections/sharded.rb
@@ -134,9 +127,12 @@ files:
134
127
  - lib/solid_cache/maglev_hash.rb
135
128
  - lib/solid_cache/store.rb
136
129
  - lib/solid_cache/store/api.rb
137
- - lib/solid_cache/store/clusters.rb
130
+ - lib/solid_cache/store/connections.rb
138
131
  - lib/solid_cache/store/entries.rb
132
+ - lib/solid_cache/store/execution.rb
133
+ - lib/solid_cache/store/expiry.rb
139
134
  - lib/solid_cache/store/failsafe.rb
135
+ - lib/solid_cache/store/stats.rb
140
136
  - lib/solid_cache/version.rb
141
137
  - lib/tasks/solid_cache_tasks.rake
142
138
  homepage: http://github.com/rails/solid_cache
@@ -162,7 +158,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
162
158
  - !ruby/object:Gem::Version
163
159
  version: '0'
164
160
  requirements: []
165
- rubygems_version: 3.5.6
161
+ rubygems_version: 3.5.11
166
162
  signing_key:
167
163
  specification_version: 4
168
164
  summary: A database backed ActiveSupport::Cache::Store
@@ -1,11 +0,0 @@
1
- class CreateSolidCacheEntries < ActiveRecord::Migration[7.0]
2
- def change
3
- create_table :solid_cache_entries do |t|
4
- t.binary :key, null: false, limit: 1024
5
- t.binary :value, null: false, limit: 512.megabytes
6
- t.datetime :created_at, null: false
7
-
8
- t.index :key, unique: true
9
- end
10
- end
11
- end
@@ -1,8 +0,0 @@
1
- class AddKeyHashAndByteSizeToSolidCacheEntries < ActiveRecord::Migration[7.0]
2
- def change
3
- change_table :solid_cache_entries do |t|
4
- t.column :key_hash, :integer, null: true, limit: 8
5
- t.column :byte_size, :integer, null: true, limit: 4
6
- end
7
- end
8
- end
@@ -1,11 +0,0 @@
1
- class AddKeyHashAndByteSizeIndexesAndNullConstraintsToSolidCacheEntries < ActiveRecord::Migration[7.0]
2
- def change
3
- change_table :solid_cache_entries, bulk: true do |t|
4
- t.change_null :key_hash, false
5
- t.change_null :byte_size, false
6
- t.index :key_hash, unique: true
7
- t.index [:key_hash, :byte_size]
8
- t.index :byte_size
9
- end
10
- end
11
- end
@@ -1,7 +0,0 @@
1
- class RemoveKeyIndexFromSolidCacheEntries < ActiveRecord::Migration[7.0]
2
- def change
3
- change_table :solid_cache_entries do |t|
4
- t.remove_index :key, unique: true
5
- end
6
- end
7
- end
@@ -1,55 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module SolidCache
4
- class Cluster
5
- module Connections
6
- attr_reader :shard_options
7
-
8
- def initialize(options = {})
9
- super(options)
10
- @shard_options = options.fetch(:shards, nil)
11
-
12
- if [ Hash, Array, NilClass ].none? { |klass| @shard_options.is_a? klass }
13
- raise ArgumentError, "`shards` is a `#{@shard_options.class.name}`, it should be one of Array, Hash or nil"
14
- end
15
- end
16
-
17
- def with_each_connection(async: false, &block)
18
- return enum_for(:with_each_connection) unless block_given?
19
-
20
- connections.with_each do
21
- execute(async, &block)
22
- end
23
- end
24
-
25
- def with_connection_for(key, async: false, &block)
26
- connections.with_connection_for(key) do
27
- execute(async, &block)
28
- end
29
- end
30
-
31
- def with_connection(name, async: false, &block)
32
- connections.with(name) do
33
- execute(async, &block)
34
- end
35
- end
36
-
37
- def group_by_connection(keys)
38
- connections.assign(keys)
39
- end
40
-
41
- def connection_names
42
- connections.names
43
- end
44
-
45
- def connections
46
- @connections ||= SolidCache::Connections.from_config(@shard_options)
47
- end
48
-
49
- private
50
- def setup!
51
- connections
52
- end
53
- end
54
- end
55
- end
@@ -1,18 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module SolidCache
4
- class Cluster
5
- include Connections, Execution, Expiry, Stats
6
-
7
- attr_reader :error_handler
8
-
9
- def initialize(options = {})
10
- @error_handler = options[:error_handler]
11
- super(options)
12
- end
13
-
14
- def setup!
15
- super
16
- end
17
- end
18
- end