Class: Magick::Adapters::Base
- Inherits:
-
Object
- Object
- Magick::Adapters::Base
- Defined in:
- lib/magick/adapters/base.rb
Direct Known Subclasses
Instance Method Summary collapse
- #all_features ⇒ Object
- #delete(feature_name) ⇒ Object
-
#delete_key(feature_name, key) ⇒ Object
Remove a single key from a feature's data, leaving the rest intact.
- #exists?(feature_name) ⇒ Boolean
- #get(feature_name, key) ⇒ Object
-
#get_all_data(feature_name) ⇒ Object
Load all keys for a single feature in one call (override for efficiency).
-
#load_all_features_data ⇒ Object
Bulk load all features' data in one call (override for efficiency).
-
#load_features_data_with_prefix(prefix) ⇒ Object
Bulk load every feature whose name starts with
prefix, and every feature whose name starts with none ofprefixes. - #load_features_data_without_prefixes(prefixes) ⇒ Object
-
#next_sequence(feature_name, key, floor: 0) ⇒ Object
Atomically allocate the next number in a monotonic counter stored under (feature_name, key) and return it.
- #set(feature_name, key, value) ⇒ Object
-
#set_all_data(feature_name, data_hash) ⇒ Object
Bulk set multiple keys for a feature in one call.
Instance Method Details
#all_features ⇒ Object
22 23 24 |
# File 'lib/magick/adapters/base.rb', line 22 def all_features raise NotImplementedError, "#{self.class} must implement #all_features" end |
#delete(feature_name) ⇒ Object
14 15 16 |
# File 'lib/magick/adapters/base.rb', line 14 def delete(feature_name) raise NotImplementedError, "#{self.class} must implement #delete" end |
#delete_key(feature_name, key) ⇒ Object
Remove a single key from a feature's data, leaving the rest intact. Subclasses MUST implement this — there is no way to express "delete one key" in terms of the other primitives, and retention (pruning the version history hot window) depends on it.
71 72 73 74 |
# File 'lib/magick/adapters/base.rb', line 71 def delete_key(feature_name, key) raise NotImplementedError, "#{self.class} must implement #delete_key (feature=#{feature_name}, key=#{key})" end |
#exists?(feature_name) ⇒ Boolean
18 19 20 |
# File 'lib/magick/adapters/base.rb', line 18 def exists?(feature_name) raise NotImplementedError, "#{self.class} must implement #exists?" end |
#get(feature_name, key) ⇒ Object
6 7 8 |
# File 'lib/magick/adapters/base.rb', line 6 def get(feature_name, key) raise NotImplementedError, "#{self.class} must implement #get" end |
#get_all_data(feature_name) ⇒ Object
Load all keys for a single feature in one call (override for efficiency)
27 28 29 |
# File 'lib/magick/adapters/base.rb', line 27 def get_all_data(feature_name) {} end |
#load_all_features_data ⇒ Object
Bulk load all features' data in one call (override for efficiency)
32 33 34 |
# File 'lib/magick/adapters/base.rb', line 32 def load_all_features_data {} end |
#load_features_data_with_prefix(prefix) ⇒ Object
Bulk load every feature whose name starts with prefix, and every
feature whose name starts with none of prefixes. Bulk callers are
split this way because the reserved bookkeeping namespaces (version
snapshots, audit history) must stay out of the paths that only ever want
features (cache preload, the Admin UI source refresh), while the version
archive wants exactly its own namespace and nothing else.
Adapters backed by a queryable store SHOULD override these to filter at the source: the defaults below are correct, but they still read the rows they then throw away, which is the cost the split exists to avoid.
46 47 48 49 |
# File 'lib/magick/adapters/base.rb', line 46 def load_features_data_with_prefix(prefix) prefix = prefix.to_s load_all_features_data.select { |feature_name, _| feature_name.to_s.start_with?(prefix) } end |
#load_features_data_without_prefixes(prefixes) ⇒ Object
51 52 53 54 55 56 |
# File 'lib/magick/adapters/base.rb', line 51 def load_features_data_without_prefixes(prefixes) prefixes = Array(prefixes).map(&:to_s) load_all_features_data.reject do |feature_name, _| prefixes.any? { |prefix| feature_name.to_s.start_with?(prefix) } end end |
#next_sequence(feature_name, key, floor: 0) ⇒ Object
Atomically allocate the next number in a monotonic counter stored under
(feature_name, key) and return it. The result is always greater than
both the stored value and floor; callers pass the highest number they
can already see as floor so a store that lost its counter (Redis
flush, restored dump, upgrade from a release that had none) resumes
above the surviving data instead of restarting at 1 and overwriting it.
Adapters backed by a store SHARED BETWEEN PROCESSES must override this with a genuinely atomic primitive (Redis HINCRBY, a row lock, a database sequence). This default is a plain read-modify-write: it is correct for a store only one process can reach, and will hand the same number to two processes that call it concurrently.
88 89 90 91 92 |
# File 'lib/magick/adapters/base.rb', line 88 def next_sequence(feature_name, key, floor: 0) value = [get(feature_name, key).to_i, floor.to_i].max + 1 set(feature_name, key, value) value end |
#set(feature_name, key, value) ⇒ Object
10 11 12 |
# File 'lib/magick/adapters/base.rb', line 10 def set(feature_name, key, value) raise NotImplementedError, "#{self.class} must implement #set" end |
#set_all_data(feature_name, data_hash) ⇒ Object
Bulk set multiple keys for a feature in one call. Subclasses MUST implement this — a no-op default silently drops bulk writes, which is why this used to cause hard-to-diagnose lost updates for custom adapters (audit P2-Co6).
62 63 64 65 |
# File 'lib/magick/adapters/base.rb', line 62 def set_all_data(feature_name, data_hash) raise NotImplementedError, "#{self.class} must implement #set_all_data (feature=#{feature_name}, keys=#{data_hash.keys.inspect})" end |