familia 1.2.3 → 2.0.0.pre.pre
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.github/workflows/ci.yml +68 -0
- data/.github/workflows/docs.yml +64 -0
- data/.gitignore +3 -0
- data/.pre-commit-config.yaml +3 -1
- data/.rubocop.yml +16 -9
- data/.rubocop_todo.yml +177 -31
- data/.yardopts +9 -0
- data/CLAUDE.md +141 -0
- data/Gemfile +15 -2
- data/Gemfile.lock +61 -61
- data/README.md +39 -23
- data/bin/irb +3 -0
- data/docs/connection_pooling.md +317 -0
- data/familia.gemspec +8 -5
- data/lib/familia/base.rb +19 -9
- data/lib/familia/connection.rb +232 -65
- data/lib/familia/core_ext.rb +1 -1
- data/lib/familia/datatype/commands.rb +59 -0
- data/lib/familia/{redistype → datatype}/serialization.rb +9 -13
- data/lib/familia/{redistype → datatype}/types/hashkey.rb +25 -25
- data/lib/familia/{redistype → datatype}/types/list.rb +13 -13
- data/lib/familia/{redistype → datatype}/types/sorted_set.rb +20 -20
- data/lib/familia/{redistype → datatype}/types/string.rb +22 -21
- data/lib/familia/{redistype → datatype}/types/unsorted_set.rb +11 -11
- data/lib/familia/datatype.rb +243 -0
- data/lib/familia/errors.rb +5 -2
- data/lib/familia/features/expiration.rb +33 -34
- data/lib/familia/features/quantization.rb +9 -3
- data/lib/familia/features/safe_dump.rb +2 -3
- data/lib/familia/features.rb +2 -2
- data/lib/familia/horreum/class_methods.rb +97 -130
- data/lib/familia/horreum/commands.rb +46 -51
- data/lib/familia/horreum/connection.rb +82 -0
- data/lib/familia/horreum/{relations_management.rb → related_fields_management.rb} +37 -35
- data/lib/familia/horreum/serialization.rb +61 -198
- data/lib/familia/horreum/settings.rb +6 -17
- data/lib/familia/horreum/utils.rb +11 -10
- data/lib/familia/horreum.rb +69 -60
- data/lib/familia/logging.rb +12 -12
- data/lib/familia/multi_result.rb +72 -0
- data/lib/familia/refinements.rb +7 -44
- data/lib/familia/settings.rb +11 -11
- data/lib/familia/utils.rb +123 -90
- data/lib/familia/version.rb +4 -21
- data/lib/familia.rb +17 -12
- data/lib/middleware/database_middleware.rb +150 -0
- data/try/configuration/scenarios_try.rb +65 -0
- data/try/core/connection_try.rb +58 -0
- data/try/core/errors_try.rb +93 -0
- data/try/core/extensions_try.rb +26 -0
- data/try/{10_familia_try.rb → core/familia_extended_try.rb} +11 -10
- data/try/{00_familia_try.rb → core/familia_try.rb} +5 -3
- data/try/core/middleware_try.rb +68 -0
- data/try/core/refinements_try.rb +39 -0
- data/try/core/settings_try.rb +76 -0
- data/try/core/tools_try.rb +54 -0
- data/try/core/utils_try.rb +189 -0
- data/try/{26_redis_bool_try.rb → datatypes/boolean_try.rb} +4 -2
- data/try/datatypes/datatype_base_try.rb +69 -0
- data/try/{25_redis_type_hash_try.rb → datatypes/hash_try.rb} +5 -3
- data/try/{23_redis_type_list_try.rb → datatypes/list_try.rb} +5 -3
- data/try/{22_redis_type_set_try.rb → datatypes/set_try.rb} +5 -3
- data/try/{21_redis_type_zset_try.rb → datatypes/sorted_set_try.rb} +6 -4
- data/try/{24_redis_type_string_try.rb → datatypes/string_try.rb} +8 -8
- data/try/edge_cases/empty_identifiers_try.rb +48 -0
- data/try/{92_symbolize_try.rb → edge_cases/hash_symbolization_try.rb} +12 -8
- data/try/edge_cases/json_serialization_try.rb +85 -0
- data/try/edge_cases/race_conditions_try.rb +60 -0
- data/try/edge_cases/reserved_keywords_try.rb +59 -0
- data/try/{93_string_coercion_try.rb → edge_cases/string_coercion_try.rb} +63 -60
- data/try/edge_cases/ttl_side_effects_try.rb +51 -0
- data/try/features/expiration_try.rb +86 -0
- data/try/features/quantization_try.rb +90 -0
- data/try/{35_feature_safedump_try.rb → features/safe_dump_advanced_try.rb} +7 -6
- data/try/features/safe_dump_try.rb +137 -0
- data/try/{test_helpers.rb → helpers/test_helpers.rb} +25 -60
- data/try/{27_redis_horreum_try.rb → horreum/base_try.rb} +39 -14
- data/try/horreum/class_methods_try.rb +41 -0
- data/try/horreum/commands_try.rb +49 -0
- data/try/{29_redis_horreum_initialization_try.rb → horreum/initialization_try.rb} +9 -7
- data/try/horreum/relations_try.rb +146 -0
- data/try/{28_redis_horreum_serialization_try.rb → horreum/serialization_try.rb} +13 -11
- data/try/horreum/settings_try.rb +43 -0
- data/try/integration/cross_component_try.rb +46 -0
- data/try/{41_customer_safedump_try.rb → models/customer_safe_dump_try.rb} +9 -7
- data/try/{40_customer_try.rb → models/customer_try.rb} +20 -17
- data/try/models/datatype_base_try.rb +101 -0
- data/try/{30_familia_object_try.rb → models/familia_object_try.rb} +18 -16
- data/try/performance/benchmarks_try.rb +55 -0
- data/try/pooling/README.md +20 -0
- data/try/pooling/configurable_stress_test_try.rb +435 -0
- data/try/pooling/connection_pool_test_try.rb +273 -0
- data/try/pooling/lib/atomic_saves_v3_connection_pool_helpers.rb +192 -0
- data/try/pooling/lib/connection_pool_metrics.rb +372 -0
- data/try/pooling/lib/connection_pool_stress_test.rb +959 -0
- data/try/pooling/lib/connection_pool_threading_models.rb +421 -0
- data/try/pooling/lib/visualize_stress_results.rb +434 -0
- data/try/pooling/pool_siege_try.rb +509 -0
- data/try/pooling/run_stress_tests_try.rb +482 -0
- data/try/prototypes/atomic_saves_v1_context_proxy.rb +121 -0
- data/try/prototypes/atomic_saves_v2_connection_switching.rb +161 -0
- data/try/prototypes/atomic_saves_v3_connection_pool.rb +189 -0
- data/try/prototypes/atomic_saves_v4.rb +105 -0
- data/try/prototypes/lib/atomic_saves_v2_connection_switching_helpers.rb +124 -0
- data/try/prototypes/lib/atomic_saves_v3_connection_pool_helpers.rb +192 -0
- metadata +124 -38
- data/.github/workflows/ruby.yml +0 -71
- data/VERSION.yml +0 -4
- data/lib/familia/redistype/commands.rb +0 -59
- data/lib/familia/redistype.rb +0 -228
- data/lib/familia/tools.rb +0 -68
- data/lib/redis_middleware.rb +0 -109
- data/try/20_redis_type_try.rb +0 -70
- data/try/91_json_bug_try.rb +0 -86
@@ -1,62 +1,61 @@
|
|
1
|
-
#
|
1
|
+
# lib/familia/horreum/class_methods.rb
|
2
2
|
|
3
|
-
require_relative '
|
3
|
+
require_relative 'related_fields_management'
|
4
4
|
|
5
5
|
module Familia
|
6
6
|
class Horreum
|
7
7
|
# Class-level instance variables
|
8
8
|
# These are set up as nil initially and populated later
|
9
|
-
@
|
10
|
-
@
|
11
|
-
@
|
12
|
-
@
|
9
|
+
@dbclient = nil # TODO
|
10
|
+
@identifier_field = nil
|
11
|
+
@default_expiration = nil
|
12
|
+
@logical_database = nil
|
13
13
|
@uri = nil
|
14
14
|
@suffix = nil
|
15
15
|
@prefix = nil
|
16
16
|
@fields = nil # []
|
17
|
-
@
|
18
|
-
@
|
17
|
+
@class_related_fields = nil # {}
|
18
|
+
@related_fields = nil # {}
|
19
19
|
@dump_method = nil
|
20
20
|
@load_method = nil
|
21
21
|
|
22
22
|
# ClassMethods: Provides class-level functionality for Horreum
|
23
23
|
#
|
24
24
|
# This module is extended into classes that include Familia::Horreum,
|
25
|
-
# providing methods for
|
25
|
+
# providing methods for Database operations and object management.
|
26
26
|
#
|
27
27
|
# Key features:
|
28
|
-
# * Includes
|
29
|
-
# * Defines methods for managing fields, identifiers, and
|
30
|
-
# * Provides utility methods for working with
|
28
|
+
# * Includes RelatedFieldsManagement for DataType field handling
|
29
|
+
# * Defines methods for managing fields, identifiers, and dbkeys
|
30
|
+
# * Provides utility methods for working with Database objects
|
31
31
|
#
|
32
32
|
module ClassMethods
|
33
33
|
include Familia::Settings
|
34
|
-
include Familia::Horreum::
|
35
|
-
|
36
|
-
#
|
37
|
-
#
|
38
|
-
# This method
|
39
|
-
#
|
40
|
-
#
|
41
|
-
#
|
42
|
-
# @
|
43
|
-
#
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
@
|
59
|
-
@identifier
|
34
|
+
include Familia::Horreum::RelatedFieldsManagement
|
35
|
+
|
36
|
+
# Sets or retrieves the unique identifier field for the class.
|
37
|
+
#
|
38
|
+
# This method defines or returns the field or method that contains the unique
|
39
|
+
# identifier used to generate the dbkey for the object. If a value is provided,
|
40
|
+
# it sets the identifier field; otherwise, it returns the current identifier field.
|
41
|
+
#
|
42
|
+
# @param [Object] val the field name or method to set as the identifier field (optional).
|
43
|
+
# @return [Object] the current identifier field.
|
44
|
+
#
|
45
|
+
def identifier_field(val = nil)
|
46
|
+
if val
|
47
|
+
# Validate identifier field definition at class definition time
|
48
|
+
case val
|
49
|
+
when Symbol, String, Proc
|
50
|
+
@identifier_field = val
|
51
|
+
else
|
52
|
+
raise Problem, <<~ERROR
|
53
|
+
Invalid identifier field definition: #{val.inspect}.
|
54
|
+
Use a field name (Symbol/String) or Proc.
|
55
|
+
ERROR
|
56
|
+
end
|
57
|
+
end
|
58
|
+
@identifier_field
|
60
59
|
end
|
61
60
|
|
62
61
|
# Defines a field for the class and creates accessor methods.
|
@@ -84,12 +83,12 @@ module Familia
|
|
84
83
|
# The dynamically defined method performs the following:
|
85
84
|
# - Acts as both a reader and a writer method.
|
86
85
|
# - When called without arguments, retrieves the current value from Redis.
|
87
|
-
# - When called with an argument, persists the value to
|
86
|
+
# - When called with an argument, persists the value to Database immediately.
|
88
87
|
# - Checks if the correct number of arguments is provided (zero or one).
|
89
|
-
# - Converts the provided value to a format suitable for
|
88
|
+
# - Converts the provided value to a format suitable for Database storage.
|
90
89
|
# - Uses the existing accessor method to set the attribute value when
|
91
90
|
# writing.
|
92
|
-
# - Persists the value to
|
91
|
+
# - Persists the value to Database immediately using the hset command when
|
93
92
|
# writing.
|
94
93
|
# - Includes custom error handling to raise an ArgumentError if the wrong
|
95
94
|
# number of arguments is given.
|
@@ -138,34 +137,28 @@ module Familia
|
|
138
137
|
# # Method implementation
|
139
138
|
# end
|
140
139
|
#
|
141
|
-
if method_defined?(:"#{name}!")
|
142
|
-
warn "Method #{name}! is already defined for #{self}"
|
143
|
-
return
|
144
|
-
end
|
145
|
-
|
146
140
|
define_method :"#{name}!" do |*args|
|
147
|
-
|
148
141
|
# Check if the correct number of arguments is provided (exactly one).
|
149
142
|
raise ArgumentError, "wrong number of arguments (given #{args.size}, expected 0 or 1)" if args.size > 1
|
150
143
|
|
151
144
|
val = args.first
|
152
145
|
|
153
146
|
# If no value is provided to this fast attribute method, make a call
|
154
|
-
# to
|
147
|
+
# to the db to return the current stored value of the hash field.
|
155
148
|
return hget name if val.nil?
|
156
149
|
|
157
150
|
begin
|
158
151
|
# Trace the operation if debugging is enabled.
|
159
|
-
Familia.trace :FAST_WRITER,
|
152
|
+
Familia.trace :FAST_WRITER, dbclient, "#{name}: #{val.inspect}", caller(1..1) if Familia.debug?
|
160
153
|
|
161
|
-
# Convert the provided value to a format suitable for
|
154
|
+
# Convert the provided value to a format suitable for Database storage.
|
162
155
|
prepared = serialize_value(val)
|
163
156
|
Familia.ld "[.fast_attribute!] #{name} val: #{val.class} prepared: #{prepared.class}"
|
164
157
|
|
165
158
|
# Use the existing accessor method to set the attribute value.
|
166
159
|
send :"#{name}=", val
|
167
160
|
|
168
|
-
# Persist the value to
|
161
|
+
# Persist the value to Database immediately using the hset command.
|
169
162
|
hset name, prepared
|
170
163
|
rescue Familia::Problem => e
|
171
164
|
# Raise a custom error message if an exception occurs during the execution of the method.
|
@@ -181,36 +174,24 @@ module Familia
|
|
181
174
|
@fields
|
182
175
|
end
|
183
176
|
|
184
|
-
def
|
185
|
-
@
|
186
|
-
@
|
187
|
-
end
|
188
|
-
|
189
|
-
def class_redis_types?(name)
|
190
|
-
class_redis_types.key? name.to_s.to_sym
|
177
|
+
def class_related_fields
|
178
|
+
@class_related_fields ||= {}
|
179
|
+
@class_related_fields
|
191
180
|
end
|
192
181
|
|
193
|
-
def
|
194
|
-
|
195
|
-
|
196
|
-
|
197
|
-
def redis_types
|
198
|
-
@redis_types ||= {}
|
199
|
-
@redis_types
|
182
|
+
def related_fields
|
183
|
+
@related_fields ||= {}
|
184
|
+
@related_fields
|
200
185
|
end
|
201
186
|
|
202
187
|
def has_relations?
|
203
188
|
@has_relations ||= false
|
204
189
|
end
|
205
190
|
|
206
|
-
def
|
207
|
-
@
|
208
|
-
@
|
209
|
-
|
210
|
-
|
211
|
-
def uri(v = nil)
|
212
|
-
@uri = v unless v.nil?
|
213
|
-
@uri || parent&.uri
|
191
|
+
def logical_database(v = nil)
|
192
|
+
Familia.trace :DB, Familia.dbclient, "#{@logical_database} #{v}", caller(1..1) if Familia.debug?
|
193
|
+
@logical_database = v unless v.nil?
|
194
|
+
@logical_database || parent&.logical_database
|
214
195
|
end
|
215
196
|
|
216
197
|
def all(suffix = nil)
|
@@ -223,11 +204,11 @@ module Familia
|
|
223
204
|
matching_keys_count(filter) > 0
|
224
205
|
end
|
225
206
|
|
226
|
-
# Returns the number of
|
227
|
-
# @param filter [String]
|
207
|
+
# Returns the number of dbkeys matching the given filter pattern
|
208
|
+
# @param filter [String] dbkey pattern to match (default: '*')
|
228
209
|
# @return [Integer] Number of matching keys
|
229
210
|
def matching_keys_count(filter = '*')
|
230
|
-
|
211
|
+
dbclient.keys(dbkey(filter)).compact.size
|
231
212
|
end
|
232
213
|
alias size matching_keys_count # For backwards compatibility
|
233
214
|
|
@@ -241,20 +222,6 @@ module Familia
|
|
241
222
|
@prefix || name.downcase.gsub('::', Familia.delim).to_sym
|
242
223
|
end
|
243
224
|
|
244
|
-
# Converts the class name into a string that can be used to look up
|
245
|
-
# configuration values. This is particularly useful when mapping
|
246
|
-
# familia models with specific database numbers in the configuration.
|
247
|
-
#
|
248
|
-
# @example V2::Session.config_name => 'session'
|
249
|
-
#
|
250
|
-
# @return [String] The underscored class name as a string
|
251
|
-
def config_name
|
252
|
-
name.split('::').last
|
253
|
-
.gsub(/([A-Z]+)([A-Z][a-z])/, '\1_\2')
|
254
|
-
.gsub(/([a-z\d])([A-Z])/, '\1_\2')
|
255
|
-
.downcase
|
256
|
-
end
|
257
|
-
|
258
225
|
# Creates and persists a new instance of the class.
|
259
226
|
#
|
260
227
|
# @param *args [Array] Variable number of positional arguments to be passed
|
@@ -293,7 +260,7 @@ module Familia
|
|
293
260
|
#
|
294
261
|
def create *args, **kwargs
|
295
262
|
fobj = new(*args, **kwargs)
|
296
|
-
raise Familia::Problem, "#{self} already exists: #{fobj.
|
263
|
+
raise Familia::Problem, "#{self} already exists: #{fobj.dbkey}" if fobj.exists?
|
297
264
|
|
298
265
|
fobj.save
|
299
266
|
fobj
|
@@ -305,17 +272,17 @@ module Familia
|
|
305
272
|
end
|
306
273
|
|
307
274
|
def rawmultiget(*ids)
|
308
|
-
ids.collect! { |objid|
|
275
|
+
ids.collect! { |objid| dbkey(objid) }
|
309
276
|
return [] if ids.compact.empty?
|
310
277
|
|
311
|
-
Familia.trace :MULTIGET,
|
312
|
-
|
278
|
+
Familia.trace :MULTIGET, dbclient, "#{ids.size}: #{ids}", caller(1..1) if Familia.debug?
|
279
|
+
dbclient.mget(*ids)
|
313
280
|
end
|
314
281
|
|
315
|
-
# Retrieves and instantiates an object from
|
282
|
+
# Retrieves and instantiates an object from Database using the full object
|
316
283
|
# key.
|
317
284
|
#
|
318
|
-
# @param objkey [String] The full
|
285
|
+
# @param objkey [String] The full dbkey for the object.
|
319
286
|
# @return [Object, nil] An instance of the class if the key exists, nil
|
320
287
|
# otherwise.
|
321
288
|
# @raise [ArgumentError] If the provided key is empty.
|
@@ -345,10 +312,10 @@ module Familia
|
|
345
312
|
|
346
313
|
# We use a lower-level method here b/c we're working with the
|
347
314
|
# full key and not just the identifier.
|
348
|
-
does_exist =
|
315
|
+
does_exist = dbclient.exists(objkey).positive?
|
349
316
|
|
350
317
|
Familia.ld "[.find_by_key] #{self} from key #{objkey} (exists: #{does_exist})"
|
351
|
-
Familia.trace :FROM_KEY,
|
318
|
+
Familia.trace :FROM_KEY, dbclient, objkey, caller(1..1) if Familia.debug?
|
352
319
|
|
353
320
|
# This is the reason for calling exists first. We want to definitively
|
354
321
|
# and without any ambiguity know if the object exists in Redis. If it
|
@@ -357,22 +324,22 @@ module Familia
|
|
357
324
|
# the constructor, which will then be annoying to debug.
|
358
325
|
return unless does_exist
|
359
326
|
|
360
|
-
obj =
|
361
|
-
Familia.trace :FROM_KEY2,
|
327
|
+
obj = dbclient.hgetall(objkey) # horreum objects are persisted as database hashes
|
328
|
+
Familia.trace :FROM_KEY2, dbclient, "#{objkey}: #{obj.inspect}", caller(1..1) if Familia.debug?
|
362
329
|
|
363
330
|
new(**obj)
|
364
331
|
end
|
365
|
-
alias
|
332
|
+
alias from_dbkey find_by_key # deprecated
|
366
333
|
|
367
|
-
# Retrieves and instantiates an object from
|
334
|
+
# Retrieves and instantiates an object from Database using its identifier.
|
368
335
|
#
|
369
336
|
# @param identifier [String, Integer] The unique identifier for the
|
370
337
|
# object.
|
371
|
-
# @param suffix [Symbol] The suffix to use in the
|
338
|
+
# @param suffix [Symbol] The suffix to use in the dbkey (default:
|
372
339
|
# :object).
|
373
340
|
# @return [Object, nil] An instance of the class if found, nil otherwise.
|
374
341
|
#
|
375
|
-
# This method constructs the full
|
342
|
+
# This method constructs the full dbkey using the provided identifier
|
376
343
|
# and suffix, then delegates to `find_by_key` for the actual retrieval and
|
377
344
|
# instantiation.
|
378
345
|
#
|
@@ -387,10 +354,10 @@ module Familia
|
|
387
354
|
suffix ||= self.suffix
|
388
355
|
return nil if identifier.to_s.empty?
|
389
356
|
|
390
|
-
objkey =
|
357
|
+
objkey = dbkey(identifier, suffix)
|
391
358
|
|
392
359
|
Familia.ld "[.find_by_id] #{self} from key #{objkey})"
|
393
|
-
Familia.trace :FIND_BY_ID, Familia.
|
360
|
+
Familia.trace :FIND_BY_ID, Familia.dbclient(uri), objkey, caller(1..1).first if Familia.debug?
|
394
361
|
find_by_key objkey
|
395
362
|
end
|
396
363
|
alias find find_by_id
|
@@ -400,10 +367,10 @@ module Familia
|
|
400
367
|
# Checks if an object with the given identifier exists in Redis.
|
401
368
|
#
|
402
369
|
# @param identifier [String, Integer] The unique identifier for the object.
|
403
|
-
# @param suffix [Symbol, nil] The suffix to use in the
|
370
|
+
# @param suffix [Symbol, nil] The suffix to use in the dbkey (default: class suffix).
|
404
371
|
# @return [Boolean] true if the object exists, false otherwise.
|
405
372
|
#
|
406
|
-
# This method constructs the full
|
373
|
+
# This method constructs the full dbkey using the provided identifier and suffix,
|
407
374
|
# then checks if the key exists in Redis.
|
408
375
|
#
|
409
376
|
# @example
|
@@ -413,24 +380,24 @@ module Familia
|
|
413
380
|
suffix ||= self.suffix
|
414
381
|
return false if identifier.to_s.empty?
|
415
382
|
|
416
|
-
objkey =
|
383
|
+
objkey = dbkey identifier, suffix
|
417
384
|
|
418
|
-
ret =
|
419
|
-
Familia.trace :EXISTS,
|
385
|
+
ret = dbclient.exists objkey
|
386
|
+
Familia.trace :EXISTS, dbclient, "#{objkey} #{ret.inspect}", caller(1..1) if Familia.debug?
|
420
387
|
|
421
|
-
ret.positive? # differs from
|
388
|
+
ret.positive? # differs from Valkey API but I think it's okay bc `exists?` is a predicate method.
|
422
389
|
end
|
423
390
|
|
424
|
-
# Destroys an object in
|
391
|
+
# Destroys an object in Database with the given identifier.
|
425
392
|
#
|
426
393
|
# @param identifier [String, Integer] The unique identifier for the object to destroy.
|
427
|
-
# @param suffix [Symbol, nil] The suffix to use in the
|
394
|
+
# @param suffix [Symbol, nil] The suffix to use in the dbkey (default: class suffix).
|
428
395
|
# @return [Boolean] true if the object was successfully destroyed, false otherwise.
|
429
396
|
#
|
430
397
|
# This method is part of Familia's high-level object lifecycle management. While `delete!`
|
431
|
-
# operates directly on
|
398
|
+
# operates directly on dbkeys, `destroy!` operates at the object level and is used for
|
432
399
|
# ORM-style operations. Use `destroy!` when removing complete objects from the system, and
|
433
|
-
# `delete!` when working directly with
|
400
|
+
# `delete!` when working directly with dbkeys.
|
434
401
|
#
|
435
402
|
# @example
|
436
403
|
# User.destroy!(123) # Removes user:123:object from Redis
|
@@ -439,27 +406,27 @@ module Familia
|
|
439
406
|
suffix ||= self.suffix
|
440
407
|
return false if identifier.to_s.empty?
|
441
408
|
|
442
|
-
objkey =
|
409
|
+
objkey = dbkey identifier, suffix
|
443
410
|
|
444
|
-
ret =
|
445
|
-
Familia.trace :DESTROY!,
|
411
|
+
ret = dbclient.del objkey
|
412
|
+
Familia.trace :DESTROY!, dbclient, "#{objkey} #{ret.inspect}", caller(1..1) if Familia.debug?
|
446
413
|
ret.positive?
|
447
414
|
end
|
448
415
|
|
449
|
-
# Finds all keys in
|
416
|
+
# Finds all keys in Database matching the given suffix pattern.
|
450
417
|
#
|
451
418
|
# @param suffix [String] The suffix pattern to match (default: '*').
|
452
|
-
# @return [Array<String>] An array of matching
|
419
|
+
# @return [Array<String>] An array of matching dbkeys.
|
453
420
|
#
|
454
|
-
# This method searches for all
|
455
|
-
# It uses the class's
|
421
|
+
# This method searches for all dbkeys that match the given suffix pattern.
|
422
|
+
# It uses the class's dbkey method to construct the search pattern.
|
456
423
|
#
|
457
424
|
# @example
|
458
425
|
# User.find # Returns all keys matching user:*:object
|
459
426
|
# User.find('active') # Returns all keys matching user:*:active
|
460
427
|
#
|
461
428
|
def find_keys(suffix = '*')
|
462
|
-
|
429
|
+
dbclient.keys(dbkey('*', suffix)) || []
|
463
430
|
end
|
464
431
|
|
465
432
|
# +identifier+ can be a value or an Array of values used to create the index.
|
@@ -469,15 +436,15 @@ module Familia
|
|
469
436
|
# +suffix+ If a nil value is explicitly passed in, it won't appear in the redis
|
470
437
|
# key that's returned. If no suffix is passed in, the class' suffix is used
|
471
438
|
# as the default (via the class method self.suffix). It's an important
|
472
|
-
# distinction b/c passing in an explicitly nil is how
|
439
|
+
# distinction b/c passing in an explicitly nil is how DataType objects
|
473
440
|
# at the class level are created without the global default 'object'
|
474
|
-
# suffix. See
|
475
|
-
def
|
476
|
-
# Familia.ld "[.
|
441
|
+
# suffix. See DataType#dbkey "parent_class?" for more details.
|
442
|
+
def dbkey(identifier, suffix = self.suffix)
|
443
|
+
# Familia.ld "[.dbkey] #{identifier} for #{self} (suffix:#{suffix})"
|
477
444
|
raise NoIdentifier, self if identifier.to_s.empty?
|
478
445
|
|
479
446
|
identifier &&= identifier.to_s
|
480
|
-
Familia.
|
447
|
+
Familia.dbkey(prefix, identifier, suffix)
|
481
448
|
end
|
482
449
|
|
483
450
|
def dump_method
|
@@ -488,6 +455,6 @@ module Familia
|
|
488
455
|
@load_method || :from_json # Familia.load_method
|
489
456
|
end
|
490
457
|
end
|
491
|
-
|
458
|
+
|
492
459
|
end
|
493
460
|
end
|
@@ -1,25 +1,25 @@
|
|
1
|
-
#
|
2
|
-
|
1
|
+
# lib/familia/horreum/commands.rb
|
2
|
+
|
3
3
|
module Familia
|
4
4
|
# InstanceMethods - Module containing instance-level methods for Familia
|
5
5
|
#
|
6
6
|
# This module is included in classes that include Familia, providing
|
7
|
-
# instance-level functionality for
|
7
|
+
# instance-level functionality for Database operations and object management.
|
8
8
|
#
|
9
9
|
class Horreum
|
10
10
|
|
11
|
-
# Methods that call
|
11
|
+
# Methods that call Database commands (InstanceMethods)
|
12
12
|
#
|
13
13
|
# NOTE: There is no hgetall for Horreum. This is because Horreum
|
14
|
-
# is a single hash in
|
14
|
+
# is a single hash in Database that we aren't meant to have be working
|
15
15
|
# on in memory for more than, making changes -> committing. To
|
16
16
|
# emphasize this, instead of "refreshing" the object with hgetall,
|
17
17
|
# just load the object again.
|
18
18
|
#
|
19
19
|
module Commands
|
20
20
|
|
21
|
-
def move(
|
22
|
-
|
21
|
+
def move(logical_database)
|
22
|
+
dbclient.move dbkey, logical_database
|
23
23
|
end
|
24
24
|
|
25
25
|
# Checks if the calling object's key exists in Redis.
|
@@ -39,7 +39,7 @@ module Familia
|
|
39
39
|
# @note The default behavior maintains backward compatibility by treating empty hashes
|
40
40
|
# as non-existent. Use `check_size: false` for pure key existence checking.
|
41
41
|
def exists?(check_size: true)
|
42
|
-
key_exists = self.class.
|
42
|
+
key_exists = self.class.dbclient.exists?(dbkey)
|
43
43
|
return key_exists unless check_size
|
44
44
|
key_exists && !size.zero?
|
45
45
|
end
|
@@ -47,7 +47,7 @@ module Familia
|
|
47
47
|
# Returns the number of fields in the main object hash
|
48
48
|
# @return [Integer] number of fields
|
49
49
|
def field_count
|
50
|
-
|
50
|
+
dbclient.hlen dbkey
|
51
51
|
end
|
52
52
|
alias size field_count
|
53
53
|
|
@@ -55,44 +55,39 @@ module Familia
|
|
55
55
|
# automatically be deleted. Returns 1 if the timeout was set, 0 if key
|
56
56
|
# does not exist or the timeout could not be set.
|
57
57
|
#
|
58
|
-
def expire(
|
59
|
-
|
60
|
-
Familia.trace :EXPIRE,
|
61
|
-
|
58
|
+
def expire(default_expiration = nil)
|
59
|
+
default_expiration ||= self.class.default_expiration
|
60
|
+
Familia.trace :EXPIRE, dbclient, default_expiration, caller(1..1) if Familia.debug?
|
61
|
+
dbclient.expire dbkey, default_expiration.to_i
|
62
62
|
end
|
63
63
|
|
64
|
-
# Retrieves the remaining time to live (TTL) for the object's
|
64
|
+
# Retrieves the remaining time to live (TTL) for the object's dbkey.
|
65
65
|
#
|
66
|
-
# This method accesses the ovjects
|
66
|
+
# This method accesses the ovjects Database client to obtain the TTL of `dbkey`.
|
67
67
|
# If debugging is enabled, it logs the TTL retrieval operation using `Familia.trace`.
|
68
68
|
#
|
69
69
|
# @return [Integer] The TTL of the key in seconds. Returns -1 if the key does not exist
|
70
70
|
# or has no associated expire time.
|
71
|
-
def
|
72
|
-
Familia.trace :
|
73
|
-
|
71
|
+
def current_expiration
|
72
|
+
Familia.trace :CURRENT_EXPIRATION, dbclient, uri, caller(1..1) if Familia.debug?
|
73
|
+
dbclient.ttl dbkey
|
74
74
|
end
|
75
75
|
|
76
|
-
# Removes a field from the hash stored at the
|
76
|
+
# Removes a field from the hash stored at the dbkey.
|
77
77
|
#
|
78
78
|
# @param field [String] The field to remove from the hash.
|
79
79
|
# @return [Integer] The number of fields that were removed from the hash (0 or 1).
|
80
80
|
def remove_field(field)
|
81
|
-
Familia.trace :HDEL,
|
82
|
-
|
81
|
+
Familia.trace :HDEL, dbclient, field, caller(1..1) if Familia.debug?
|
82
|
+
dbclient.hdel dbkey, field
|
83
83
|
end
|
84
84
|
alias remove remove_field # deprecated
|
85
85
|
|
86
|
-
def
|
87
|
-
Familia.trace :
|
88
|
-
|
86
|
+
def datatype
|
87
|
+
Familia.trace :DATATYPE, dbclient, uri, caller(1..1) if Familia.debug?
|
88
|
+
dbclient.type dbkey(suffix)
|
89
89
|
end
|
90
90
|
|
91
|
-
# Parity with RedisType#rename
|
92
|
-
def rename(newkey)
|
93
|
-
Familia.trace :RENAME, redis, "#{rediskey} -> #{newkey}", caller(1..1) if Familia.debug?
|
94
|
-
redis.rename rediskey, newkey
|
95
|
-
end
|
96
91
|
|
97
92
|
# Retrieves the prefix for the current instance by delegating to its class.
|
98
93
|
#
|
@@ -103,80 +98,80 @@ module Familia
|
|
103
98
|
self.class.prefix
|
104
99
|
end
|
105
100
|
|
106
|
-
# For parity with
|
101
|
+
# For parity with DataType#hgetall
|
107
102
|
def hgetall
|
108
|
-
Familia.trace :HGETALL,
|
109
|
-
|
103
|
+
Familia.trace :HGETALL, dbclient, uri, caller(1..1) if Familia.debug?
|
104
|
+
dbclient.hgetall dbkey(suffix)
|
110
105
|
end
|
111
106
|
alias all hgetall
|
112
107
|
|
113
108
|
def hget(field)
|
114
|
-
Familia.trace :HGET,
|
115
|
-
|
109
|
+
Familia.trace :HGET, dbclient, field, caller(1..1) if Familia.debug?
|
110
|
+
dbclient.hget dbkey(suffix), field
|
116
111
|
end
|
117
112
|
|
118
113
|
# @return The number of fields that were added to the hash. If the
|
119
114
|
# field already exists, this will return 0.
|
120
115
|
def hset(field, value)
|
121
|
-
Familia.trace :HSET,
|
122
|
-
|
116
|
+
Familia.trace :HSET, dbclient, field, caller(1..1) if Familia.debug?
|
117
|
+
dbclient.hset dbkey, field, value
|
123
118
|
end
|
124
119
|
|
125
120
|
def hmset(hsh={})
|
126
121
|
hsh ||= self.to_h
|
127
|
-
Familia.trace :HMSET,
|
128
|
-
|
122
|
+
Familia.trace :HMSET, dbclient, hsh, caller(1..1) if Familia.debug?
|
123
|
+
dbclient.hmset dbkey(suffix), hsh
|
129
124
|
end
|
130
125
|
|
131
126
|
def hkeys
|
132
|
-
Familia.trace :HKEYS,
|
133
|
-
|
127
|
+
Familia.trace :HKEYS, dbclient, 'uri', caller(1..1) if Familia.debug?
|
128
|
+
dbclient.hkeys dbkey(suffix)
|
134
129
|
end
|
135
130
|
|
136
131
|
def hvals
|
137
|
-
|
132
|
+
dbclient.hvals dbkey(suffix)
|
138
133
|
end
|
139
134
|
|
140
135
|
def incr(field)
|
141
|
-
|
136
|
+
dbclient.hincrby dbkey(suffix), field, 1
|
142
137
|
end
|
143
138
|
alias increment incr
|
144
139
|
|
145
140
|
def incrby(field, increment)
|
146
|
-
|
141
|
+
dbclient.hincrby dbkey(suffix), field, increment
|
147
142
|
end
|
148
143
|
alias incrementby incrby
|
149
144
|
|
150
145
|
def incrbyfloat(field, increment)
|
151
|
-
|
146
|
+
dbclient.hincrbyfloat dbkey(suffix), field, increment
|
152
147
|
end
|
153
148
|
alias incrementbyfloat incrbyfloat
|
154
149
|
|
155
150
|
def decrby(field, decrement)
|
156
|
-
|
151
|
+
dbclient.decrby dbkey(suffix), field, decrement
|
157
152
|
end
|
158
153
|
alias decrementby decrby
|
159
154
|
|
160
155
|
def decr(field)
|
161
|
-
|
156
|
+
dbclient.hdecr field
|
162
157
|
end
|
163
158
|
alias decrement decr
|
164
159
|
|
165
160
|
def hstrlen(field)
|
166
|
-
|
161
|
+
dbclient.hstrlen dbkey(suffix), field
|
167
162
|
end
|
168
163
|
alias hstrlength hstrlen
|
169
164
|
|
170
165
|
def key?(field)
|
171
|
-
|
166
|
+
dbclient.hexists dbkey(suffix), field
|
172
167
|
end
|
173
168
|
alias has_key? key?
|
174
169
|
|
175
|
-
# Deletes the entire
|
170
|
+
# Deletes the entire dbkey
|
176
171
|
# @return [Boolean] true if the key was deleted, false otherwise
|
177
172
|
def delete!
|
178
|
-
Familia.trace :DELETE!,
|
179
|
-
ret =
|
173
|
+
Familia.trace :DELETE!, dbclient, uri, caller(1..1) if Familia.debug?
|
174
|
+
ret = dbclient.del dbkey
|
180
175
|
ret.positive?
|
181
176
|
end
|
182
177
|
alias clear delete!
|