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
@@ -0,0 +1,82 @@
|
|
1
|
+
# lib/familia/horreum/connection.rb
|
2
|
+
|
3
|
+
module Familia
|
4
|
+
class Horreum
|
5
|
+
|
6
|
+
# Familia::Horreum::Connection
|
7
|
+
#
|
8
|
+
module Connection
|
9
|
+
attr_reader :uri
|
10
|
+
|
11
|
+
# Returns the Database connection for the class.
|
12
|
+
#
|
13
|
+
# This method retrieves the Database connection instance for the class. If no
|
14
|
+
# connection is set, it initializes a new connection using the provided URI
|
15
|
+
# or database configuration.
|
16
|
+
#
|
17
|
+
# @return [Redis] the Database connection instance.
|
18
|
+
#
|
19
|
+
def dbclient
|
20
|
+
Fiber[:familia_transaction] || @dbclient || Familia.dbclient(uri || logical_database)
|
21
|
+
end
|
22
|
+
|
23
|
+
def connect(*)
|
24
|
+
Familia.connect(*)
|
25
|
+
end
|
26
|
+
|
27
|
+
def uri=(uri)
|
28
|
+
@uri = normalize_uri(uri)
|
29
|
+
end
|
30
|
+
alias url uri
|
31
|
+
alias url= uri=
|
32
|
+
|
33
|
+
# Perform a sacred Database transaction ritual.
|
34
|
+
#
|
35
|
+
# This method creates a protective circle around your Database operations,
|
36
|
+
# ensuring they all succeed or fail together. It's like a group hug for your
|
37
|
+
# data operations, but with more ACID properties.
|
38
|
+
#
|
39
|
+
# @yield [conn] A block where you can perform your Database incantations.
|
40
|
+
# @yieldparam conn [Redis] A Database connection in multi mode.
|
41
|
+
#
|
42
|
+
# @example Performing a Database rain dance
|
43
|
+
# transaction do |conn|
|
44
|
+
# conn.set("weather", "rainy")
|
45
|
+
# conn.set("mood", "melancholic")
|
46
|
+
# end
|
47
|
+
#
|
48
|
+
# @note This method works with the global Familia.transaction context when available
|
49
|
+
#
|
50
|
+
def transaction
|
51
|
+
# If we're already in a Familia.transaction context, just yield the multi connection
|
52
|
+
if Fiber[:familia_transaction]
|
53
|
+
yield(Fiber[:familia_transaction])
|
54
|
+
else
|
55
|
+
# Otherwise, create a local transaction
|
56
|
+
block_result = dbclient.multi do |conn|
|
57
|
+
yield(conn)
|
58
|
+
end
|
59
|
+
end
|
60
|
+
block_result
|
61
|
+
end
|
62
|
+
alias multi transaction
|
63
|
+
|
64
|
+
def pipeline
|
65
|
+
# If we're already in a Familia.pipeline context, just yield the pipeline connection
|
66
|
+
if Fiber[:familia_pipeline]
|
67
|
+
yield(Fiber[:familia_pipeline])
|
68
|
+
else
|
69
|
+
# Otherwise, create a local transaction
|
70
|
+
block_result = dbclient.pipeline do |conn|
|
71
|
+
yield(conn)
|
72
|
+
end
|
73
|
+
end
|
74
|
+
block_result
|
75
|
+
end
|
76
|
+
|
77
|
+
end
|
78
|
+
|
79
|
+
# Include Connection module for instance methods after it's loaded
|
80
|
+
include Familia::Horreum::Connection
|
81
|
+
end
|
82
|
+
end
|
@@ -1,21 +1,23 @@
|
|
1
|
+
# lib/familia/horreum/related_fields_management.rb
|
2
|
+
|
1
3
|
module Familia
|
2
4
|
class Horreum
|
3
5
|
#
|
4
|
-
#
|
6
|
+
# RelatedFieldsManagement: Manages DataType fields and relations
|
5
7
|
#
|
6
8
|
# This module uses metaprogramming to dynamically create methods
|
7
|
-
# for managing different types of
|
9
|
+
# for managing different types of Database objects (e.g., sets, lists, hashes).
|
8
10
|
#
|
9
11
|
# Key metaprogramming features:
|
10
|
-
# * Dynamically defines methods for each
|
12
|
+
# * Dynamically defines methods for each Database type (e.g., set, list, hashkey)
|
11
13
|
# * Creates both instance-level and class-level relation methods
|
12
14
|
# * Provides query methods for checking relation types
|
13
15
|
#
|
14
16
|
# Usage:
|
15
|
-
# Include this module in classes that need
|
17
|
+
# Include this module in classes that need DataType management
|
16
18
|
# Call setup_relations_accessors to initialize the feature
|
17
19
|
#
|
18
|
-
module
|
20
|
+
module RelatedFieldsManagement
|
19
21
|
# A practical flag to indicate that a Horreum member has relations,
|
20
22
|
# not just theoretically but actually at least one list/haskey/etc.
|
21
23
|
@has_relations = nil
|
@@ -26,18 +28,18 @@ module Familia
|
|
26
28
|
end
|
27
29
|
|
28
30
|
module ClassMethods
|
29
|
-
# Sets up all
|
31
|
+
# Sets up all DataType related methods
|
30
32
|
# This method is the core of the metaprogramming logic
|
31
33
|
#
|
32
34
|
def setup_relations_accessors
|
33
|
-
Familia::
|
35
|
+
Familia::DataType.registered_types.each_pair do |kind, klass|
|
34
36
|
Familia.ld "[registered_types] #{kind} => #{klass}"
|
35
37
|
|
36
38
|
# Dynamically define instance-level relation methods
|
37
39
|
#
|
38
40
|
# Once defined, these methods can be used at the instance-level of a
|
39
41
|
# Familia member to define *instance-level* relations to any of the
|
40
|
-
#
|
42
|
+
# DataType types (e.g. set, list, hash, etc).
|
41
43
|
#
|
42
44
|
define_method :"#{kind}" do |*args|
|
43
45
|
name, opts = *args
|
@@ -45,15 +47,15 @@ module Familia
|
|
45
47
|
# As log as we have at least one relation, we can set this flag.
|
46
48
|
@has_relations = true
|
47
49
|
|
48
|
-
|
50
|
+
attach_instance_related_field name, klass, opts
|
49
51
|
end
|
50
52
|
define_method :"#{kind}?" do |name|
|
51
|
-
obj =
|
53
|
+
obj = related_fields[name.to_s.to_sym]
|
52
54
|
!obj.nil? && klass == obj.klass
|
53
55
|
end
|
54
56
|
define_method :"#{kind}s" do
|
55
|
-
names =
|
56
|
-
names.collect! { |name|
|
57
|
+
names = related_fields.keys.select { |name| send(:"#{kind}?", name) }
|
58
|
+
names.collect! { |name| related_fields[name] }
|
57
59
|
names
|
58
60
|
end
|
59
61
|
|
@@ -61,24 +63,24 @@ module Familia
|
|
61
63
|
#
|
62
64
|
# Once defined, these methods can be used at the class-level of a
|
63
65
|
# Familia member to define *class-level relations* to any of the
|
64
|
-
#
|
66
|
+
# DataType types (e.g. class_set, class_list, class_hash, etc).
|
65
67
|
#
|
66
68
|
define_method :"class_#{kind}" do |*args|
|
67
69
|
name, opts = *args
|
68
|
-
|
70
|
+
attach_class_related_field name, klass, opts
|
69
71
|
end
|
70
72
|
define_method :"class_#{kind}?" do |name|
|
71
|
-
obj =
|
73
|
+
obj = class_related_fields[name.to_s.to_sym]
|
72
74
|
!obj.nil? && klass == obj.klass
|
73
75
|
end
|
74
76
|
define_method :"class_#{kind}s" do
|
75
|
-
names =
|
76
|
-
# TODO: This returns instances of the
|
77
|
+
names = class_related_fields.keys.select { |name| send(:"class_#{kind}?", name) }
|
78
|
+
# TODO: This returns instances of the DataType class which
|
77
79
|
# also contain the options. This is different from the instance
|
78
|
-
#
|
80
|
+
# DataTypes defined above which returns the Struct of name, klass, and opts.
|
79
81
|
# names.collect! { |name| self.send name }
|
80
82
|
# OR NOT:
|
81
|
-
names.collect! { |name|
|
83
|
+
names.collect! { |name| class_related_fields[name] }
|
82
84
|
names
|
83
85
|
end
|
84
86
|
end
|
@@ -87,17 +89,17 @@ module Familia
|
|
87
89
|
# End of ClassMethods module
|
88
90
|
|
89
91
|
# Creates an instance-level relation
|
90
|
-
def
|
92
|
+
def attach_instance_related_field(name, klass, opts)
|
91
93
|
Familia.ld "[#{self}##{name}] Attaching instance-level #{klass} #{opts}"
|
92
94
|
raise ArgumentError, "Name is blank (#{klass})" if name.to_s.empty?
|
93
95
|
|
94
96
|
name = name.to_s.to_sym
|
95
97
|
opts ||= {}
|
96
98
|
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
99
|
+
related_fields[name] = Struct.new(:name, :klass, :opts).new
|
100
|
+
related_fields[name].name = name
|
101
|
+
related_fields[name].klass = klass
|
102
|
+
related_fields[name].opts = opts
|
101
103
|
|
102
104
|
attr_reader name
|
103
105
|
|
@@ -108,11 +110,11 @@ module Familia
|
|
108
110
|
!send(name).empty?
|
109
111
|
end
|
110
112
|
|
111
|
-
|
113
|
+
related_fields[name]
|
112
114
|
end
|
113
115
|
|
114
116
|
# Creates a class-level relation
|
115
|
-
def
|
117
|
+
def attach_class_related_field(name, klass, opts)
|
116
118
|
Familia.ld "[#{self}.#{name}] Attaching class-level #{klass} #{opts}"
|
117
119
|
raise ArgumentError, 'Name is blank (klass)' if name.to_s.empty?
|
118
120
|
|
@@ -120,10 +122,10 @@ module Familia
|
|
120
122
|
opts = opts.nil? ? {} : opts.clone
|
121
123
|
opts[:parent] = self unless opts.key?(:parent)
|
122
124
|
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
125
|
+
class_related_fields[name] = Struct.new(:name, :klass, :opts).new
|
126
|
+
class_related_fields[name].name = name
|
127
|
+
class_related_fields[name].klass = klass
|
128
|
+
class_related_fields[name].opts = opts
|
127
129
|
|
128
130
|
# An accessor method created in the metaclass will
|
129
131
|
# access the instance variables for this class.
|
@@ -136,13 +138,13 @@ module Familia
|
|
136
138
|
!send(name).empty?
|
137
139
|
end
|
138
140
|
|
139
|
-
|
140
|
-
|
141
|
-
instance_variable_set(:"@#{name}",
|
141
|
+
related_field = klass.new name, opts
|
142
|
+
related_field.freeze
|
143
|
+
instance_variable_set(:"@#{name}", related_field)
|
142
144
|
|
143
|
-
|
145
|
+
class_related_fields[name]
|
144
146
|
end
|
145
147
|
end
|
146
|
-
# End of
|
148
|
+
# End of RelatedFieldsManagement module
|
147
149
|
end
|
148
150
|
end
|