familia 1.2.1 → 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 +4 -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 +76 -34
- data/README.md +39 -23
- data/bin/irb +3 -0
- data/docs/connection_pooling.md +317 -0
- data/familia.gemspec +9 -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 -110
- 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 -7
- 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} +60 -59
- 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 +140 -43
- 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.
|
@@ -145,21 +144,21 @@ module Familia
|
|
145
144
|
val = args.first
|
146
145
|
|
147
146
|
# If no value is provided to this fast attribute method, make a call
|
148
|
-
# to
|
147
|
+
# to the db to return the current stored value of the hash field.
|
149
148
|
return hget name if val.nil?
|
150
149
|
|
151
150
|
begin
|
152
151
|
# Trace the operation if debugging is enabled.
|
153
|
-
Familia.trace :FAST_WRITER,
|
152
|
+
Familia.trace :FAST_WRITER, dbclient, "#{name}: #{val.inspect}", caller(1..1) if Familia.debug?
|
154
153
|
|
155
|
-
# Convert the provided value to a format suitable for
|
154
|
+
# Convert the provided value to a format suitable for Database storage.
|
156
155
|
prepared = serialize_value(val)
|
157
156
|
Familia.ld "[.fast_attribute!] #{name} val: #{val.class} prepared: #{prepared.class}"
|
158
157
|
|
159
158
|
# Use the existing accessor method to set the attribute value.
|
160
159
|
send :"#{name}=", val
|
161
160
|
|
162
|
-
# Persist the value to
|
161
|
+
# Persist the value to Database immediately using the hset command.
|
163
162
|
hset name, prepared
|
164
163
|
rescue Familia::Problem => e
|
165
164
|
# Raise a custom error message if an exception occurs during the execution of the method.
|
@@ -175,36 +174,24 @@ module Familia
|
|
175
174
|
@fields
|
176
175
|
end
|
177
176
|
|
178
|
-
def
|
179
|
-
@
|
180
|
-
@
|
177
|
+
def class_related_fields
|
178
|
+
@class_related_fields ||= {}
|
179
|
+
@class_related_fields
|
181
180
|
end
|
182
181
|
|
183
|
-
def
|
184
|
-
|
185
|
-
|
186
|
-
|
187
|
-
def redis_object?(name)
|
188
|
-
redis_types.key? name.to_s.to_sym
|
189
|
-
end
|
190
|
-
|
191
|
-
def redis_types
|
192
|
-
@redis_types ||= {}
|
193
|
-
@redis_types
|
182
|
+
def related_fields
|
183
|
+
@related_fields ||= {}
|
184
|
+
@related_fields
|
194
185
|
end
|
195
186
|
|
196
187
|
def has_relations?
|
197
188
|
@has_relations ||= false
|
198
189
|
end
|
199
190
|
|
200
|
-
def
|
201
|
-
@
|
202
|
-
@
|
203
|
-
|
204
|
-
|
205
|
-
def uri(v = nil)
|
206
|
-
@uri = v unless v.nil?
|
207
|
-
@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
|
208
195
|
end
|
209
196
|
|
210
197
|
def all(suffix = nil)
|
@@ -217,11 +204,11 @@ module Familia
|
|
217
204
|
matching_keys_count(filter) > 0
|
218
205
|
end
|
219
206
|
|
220
|
-
# Returns the number of
|
221
|
-
# @param filter [String]
|
207
|
+
# Returns the number of dbkeys matching the given filter pattern
|
208
|
+
# @param filter [String] dbkey pattern to match (default: '*')
|
222
209
|
# @return [Integer] Number of matching keys
|
223
210
|
def matching_keys_count(filter = '*')
|
224
|
-
|
211
|
+
dbclient.keys(dbkey(filter)).compact.size
|
225
212
|
end
|
226
213
|
alias size matching_keys_count # For backwards compatibility
|
227
214
|
|
@@ -273,7 +260,7 @@ module Familia
|
|
273
260
|
#
|
274
261
|
def create *args, **kwargs
|
275
262
|
fobj = new(*args, **kwargs)
|
276
|
-
raise Familia::Problem, "#{self} already exists: #{fobj.
|
263
|
+
raise Familia::Problem, "#{self} already exists: #{fobj.dbkey}" if fobj.exists?
|
277
264
|
|
278
265
|
fobj.save
|
279
266
|
fobj
|
@@ -285,17 +272,17 @@ module Familia
|
|
285
272
|
end
|
286
273
|
|
287
274
|
def rawmultiget(*ids)
|
288
|
-
ids.collect! { |objid|
|
275
|
+
ids.collect! { |objid| dbkey(objid) }
|
289
276
|
return [] if ids.compact.empty?
|
290
277
|
|
291
|
-
Familia.trace :MULTIGET,
|
292
|
-
|
278
|
+
Familia.trace :MULTIGET, dbclient, "#{ids.size}: #{ids}", caller(1..1) if Familia.debug?
|
279
|
+
dbclient.mget(*ids)
|
293
280
|
end
|
294
281
|
|
295
|
-
# Retrieves and instantiates an object from
|
282
|
+
# Retrieves and instantiates an object from Database using the full object
|
296
283
|
# key.
|
297
284
|
#
|
298
|
-
# @param objkey [String] The full
|
285
|
+
# @param objkey [String] The full dbkey for the object.
|
299
286
|
# @return [Object, nil] An instance of the class if the key exists, nil
|
300
287
|
# otherwise.
|
301
288
|
# @raise [ArgumentError] If the provided key is empty.
|
@@ -325,10 +312,10 @@ module Familia
|
|
325
312
|
|
326
313
|
# We use a lower-level method here b/c we're working with the
|
327
314
|
# full key and not just the identifier.
|
328
|
-
does_exist =
|
315
|
+
does_exist = dbclient.exists(objkey).positive?
|
329
316
|
|
330
317
|
Familia.ld "[.find_by_key] #{self} from key #{objkey} (exists: #{does_exist})"
|
331
|
-
Familia.trace :FROM_KEY,
|
318
|
+
Familia.trace :FROM_KEY, dbclient, objkey, caller(1..1) if Familia.debug?
|
332
319
|
|
333
320
|
# This is the reason for calling exists first. We want to definitively
|
334
321
|
# and without any ambiguity know if the object exists in Redis. If it
|
@@ -337,22 +324,22 @@ module Familia
|
|
337
324
|
# the constructor, which will then be annoying to debug.
|
338
325
|
return unless does_exist
|
339
326
|
|
340
|
-
obj =
|
341
|
-
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?
|
342
329
|
|
343
330
|
new(**obj)
|
344
331
|
end
|
345
|
-
alias
|
332
|
+
alias from_dbkey find_by_key # deprecated
|
346
333
|
|
347
|
-
# Retrieves and instantiates an object from
|
334
|
+
# Retrieves and instantiates an object from Database using its identifier.
|
348
335
|
#
|
349
336
|
# @param identifier [String, Integer] The unique identifier for the
|
350
337
|
# object.
|
351
|
-
# @param suffix [Symbol] The suffix to use in the
|
338
|
+
# @param suffix [Symbol] The suffix to use in the dbkey (default:
|
352
339
|
# :object).
|
353
340
|
# @return [Object, nil] An instance of the class if found, nil otherwise.
|
354
341
|
#
|
355
|
-
# This method constructs the full
|
342
|
+
# This method constructs the full dbkey using the provided identifier
|
356
343
|
# and suffix, then delegates to `find_by_key` for the actual retrieval and
|
357
344
|
# instantiation.
|
358
345
|
#
|
@@ -367,10 +354,10 @@ module Familia
|
|
367
354
|
suffix ||= self.suffix
|
368
355
|
return nil if identifier.to_s.empty?
|
369
356
|
|
370
|
-
objkey =
|
357
|
+
objkey = dbkey(identifier, suffix)
|
371
358
|
|
372
359
|
Familia.ld "[.find_by_id] #{self} from key #{objkey})"
|
373
|
-
Familia.trace :FIND_BY_ID, Familia.
|
360
|
+
Familia.trace :FIND_BY_ID, Familia.dbclient(uri), objkey, caller(1..1).first if Familia.debug?
|
374
361
|
find_by_key objkey
|
375
362
|
end
|
376
363
|
alias find find_by_id
|
@@ -380,10 +367,10 @@ module Familia
|
|
380
367
|
# Checks if an object with the given identifier exists in Redis.
|
381
368
|
#
|
382
369
|
# @param identifier [String, Integer] The unique identifier for the object.
|
383
|
-
# @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).
|
384
371
|
# @return [Boolean] true if the object exists, false otherwise.
|
385
372
|
#
|
386
|
-
# This method constructs the full
|
373
|
+
# This method constructs the full dbkey using the provided identifier and suffix,
|
387
374
|
# then checks if the key exists in Redis.
|
388
375
|
#
|
389
376
|
# @example
|
@@ -393,24 +380,24 @@ module Familia
|
|
393
380
|
suffix ||= self.suffix
|
394
381
|
return false if identifier.to_s.empty?
|
395
382
|
|
396
|
-
objkey =
|
383
|
+
objkey = dbkey identifier, suffix
|
397
384
|
|
398
|
-
ret =
|
399
|
-
Familia.trace :EXISTS,
|
385
|
+
ret = dbclient.exists objkey
|
386
|
+
Familia.trace :EXISTS, dbclient, "#{objkey} #{ret.inspect}", caller(1..1) if Familia.debug?
|
400
387
|
|
401
|
-
ret.positive? # differs from
|
388
|
+
ret.positive? # differs from Valkey API but I think it's okay bc `exists?` is a predicate method.
|
402
389
|
end
|
403
390
|
|
404
|
-
# Destroys an object in
|
391
|
+
# Destroys an object in Database with the given identifier.
|
405
392
|
#
|
406
393
|
# @param identifier [String, Integer] The unique identifier for the object to destroy.
|
407
|
-
# @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).
|
408
395
|
# @return [Boolean] true if the object was successfully destroyed, false otherwise.
|
409
396
|
#
|
410
397
|
# This method is part of Familia's high-level object lifecycle management. While `delete!`
|
411
|
-
# operates directly on
|
398
|
+
# operates directly on dbkeys, `destroy!` operates at the object level and is used for
|
412
399
|
# ORM-style operations. Use `destroy!` when removing complete objects from the system, and
|
413
|
-
# `delete!` when working directly with
|
400
|
+
# `delete!` when working directly with dbkeys.
|
414
401
|
#
|
415
402
|
# @example
|
416
403
|
# User.destroy!(123) # Removes user:123:object from Redis
|
@@ -419,27 +406,27 @@ module Familia
|
|
419
406
|
suffix ||= self.suffix
|
420
407
|
return false if identifier.to_s.empty?
|
421
408
|
|
422
|
-
objkey =
|
409
|
+
objkey = dbkey identifier, suffix
|
423
410
|
|
424
|
-
ret =
|
425
|
-
Familia.trace :DESTROY!,
|
411
|
+
ret = dbclient.del objkey
|
412
|
+
Familia.trace :DESTROY!, dbclient, "#{objkey} #{ret.inspect}", caller(1..1) if Familia.debug?
|
426
413
|
ret.positive?
|
427
414
|
end
|
428
415
|
|
429
|
-
# Finds all keys in
|
416
|
+
# Finds all keys in Database matching the given suffix pattern.
|
430
417
|
#
|
431
418
|
# @param suffix [String] The suffix pattern to match (default: '*').
|
432
|
-
# @return [Array<String>] An array of matching
|
419
|
+
# @return [Array<String>] An array of matching dbkeys.
|
433
420
|
#
|
434
|
-
# This method searches for all
|
435
|
-
# 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.
|
436
423
|
#
|
437
424
|
# @example
|
438
425
|
# User.find # Returns all keys matching user:*:object
|
439
426
|
# User.find('active') # Returns all keys matching user:*:active
|
440
427
|
#
|
441
428
|
def find_keys(suffix = '*')
|
442
|
-
|
429
|
+
dbclient.keys(dbkey('*', suffix)) || []
|
443
430
|
end
|
444
431
|
|
445
432
|
# +identifier+ can be a value or an Array of values used to create the index.
|
@@ -449,15 +436,15 @@ module Familia
|
|
449
436
|
# +suffix+ If a nil value is explicitly passed in, it won't appear in the redis
|
450
437
|
# key that's returned. If no suffix is passed in, the class' suffix is used
|
451
438
|
# as the default (via the class method self.suffix). It's an important
|
452
|
-
# distinction b/c passing in an explicitly nil is how
|
439
|
+
# distinction b/c passing in an explicitly nil is how DataType objects
|
453
440
|
# at the class level are created without the global default 'object'
|
454
|
-
# suffix. See
|
455
|
-
def
|
456
|
-
# 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})"
|
457
444
|
raise NoIdentifier, self if identifier.to_s.empty?
|
458
445
|
|
459
446
|
identifier &&= identifier.to_s
|
460
|
-
Familia.
|
447
|
+
Familia.dbkey(prefix, identifier, suffix)
|
461
448
|
end
|
462
449
|
|
463
450
|
def dump_method
|
@@ -468,6 +455,6 @@ module Familia
|
|
468
455
|
@load_method || :from_json # Familia.load_method
|
469
456
|
end
|
470
457
|
end
|
471
|
-
|
458
|
+
|
472
459
|
end
|
473
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!
|