familia 2.0.0.pre17 → 2.0.0.pre19
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/CHANGELOG.rst +118 -6
- data/CLAUDE.md +43 -11
- data/Gemfile +2 -2
- data/Gemfile.lock +9 -47
- data/README.md +52 -0
- data/bin/irb +1 -1
- data/changelog.d/20251011_012003_delano_159_datatype_transaction_pipeline_support.rst +91 -0
- data/changelog.d/20251011_203905_delano_next.rst +30 -0
- data/changelog.d/20251011_212633_delano_next.rst +13 -0
- data/changelog.d/20251011_221253_delano_next.rst +26 -0
- data/docs/guides/core-field-system.md +48 -26
- data/docs/guides/feature-expiration.md +18 -18
- data/docs/migrating/v2.0.0-pre18.md +58 -0
- data/docs/migrating/v2.0.0-pre19.md +197 -0
- data/docs/qodo-merge-compliance.md +96 -0
- data/examples/datatype_standalone.rb +281 -0
- data/lib/familia/base.rb +0 -2
- data/lib/familia/connection/behavior.rb +252 -0
- data/lib/familia/connection/handlers.rb +95 -0
- data/lib/familia/connection/middleware.rb +58 -4
- data/lib/familia/connection/operation_core.rb +1 -1
- data/lib/familia/connection/{pipeline_core.rb → pipelined_core.rb} +2 -2
- data/lib/familia/connection/transaction_core.rb +7 -9
- data/lib/familia/connection.rb +2 -1
- data/lib/familia/data_type/connection.rb +151 -7
- data/lib/familia/data_type/{commands.rb → database_commands.rb} +9 -6
- data/lib/familia/data_type/serialization.rb +9 -5
- data/lib/familia/data_type/types/hashkey.rb +1 -1
- data/lib/familia/data_type.rb +2 -2
- data/lib/familia/encryption/encrypted_data.rb +12 -2
- data/lib/familia/encryption/manager.rb +11 -4
- data/lib/familia/errors.rb +51 -14
- data/lib/familia/features/autoloader.rb +3 -1
- data/lib/familia/features/encrypted_fields/encrypted_field_type.rb +11 -3
- data/lib/familia/features/expiration/extensions.rb +8 -10
- data/lib/familia/features/expiration.rb +19 -19
- data/lib/familia/features/relationships/indexing/multi_index_generators.rb +45 -44
- data/lib/familia/features/relationships/indexing/unique_index_generators.rb +151 -65
- data/lib/familia/features/relationships/indexing.rb +37 -42
- data/lib/familia/features/relationships/indexing_relationship.rb +14 -4
- data/lib/familia/features/safe_dump.rb +2 -3
- data/lib/familia/field_type.rb +2 -1
- data/lib/familia/horreum/connection.rb +11 -35
- data/lib/familia/horreum/database_commands.rb +130 -11
- data/lib/familia/horreum/definition.rb +8 -38
- data/lib/familia/horreum/management.rb +38 -27
- data/lib/familia/horreum/persistence.rb +191 -67
- data/lib/familia/horreum/serialization.rb +94 -73
- data/lib/familia/horreum/utils.rb +0 -8
- data/lib/familia/horreum.rb +41 -18
- data/lib/familia/identifier_extractor.rb +60 -0
- data/lib/familia/logging.rb +268 -112
- data/lib/familia/refinements.rb +0 -1
- data/lib/familia/settings.rb +7 -7
- data/lib/familia/version.rb +1 -1
- data/lib/familia.rb +2 -2
- data/lib/middleware/{database_middleware.rb → database_logger.rb} +118 -14
- data/pr_agent.toml +31 -0
- data/pr_compliance_checklist.yaml +45 -0
- data/try/edge_cases/empty_identifiers_try.rb +1 -1
- data/try/edge_cases/hash_symbolization_try.rb +31 -31
- data/try/edge_cases/json_serialization_try.rb +2 -2
- data/try/edge_cases/legacy_data_detection/deserialization_edge_cases_try.rb +170 -0
- data/try/edge_cases/race_conditions_try.rb +1 -1
- data/try/edge_cases/reserved_keywords_try.rb +1 -1
- data/try/edge_cases/string_coercion_try.rb +5 -5
- data/try/edge_cases/ttl_side_effects_try.rb +1 -1
- data/try/features/encrypted_fields/aad_protection_try.rb +1 -1
- data/try/features/encrypted_fields/concealed_string_core_try.rb +1 -1
- data/try/features/encrypted_fields/context_isolation_try.rb +1 -1
- data/try/features/encrypted_fields/encrypted_fields_core_try.rb +1 -1
- data/try/features/encrypted_fields/encrypted_fields_integration_try.rb +1 -1
- data/try/features/encrypted_fields/encrypted_fields_no_cache_security_try.rb +1 -1
- data/try/features/encrypted_fields/encrypted_fields_security_try.rb +1 -1
- data/try/features/encrypted_fields/error_conditions_try.rb +1 -1
- data/try/features/encrypted_fields/fresh_key_derivation_try.rb +1 -1
- data/try/features/encrypted_fields/fresh_key_try.rb +1 -1
- data/try/features/encrypted_fields/key_rotation_try.rb +1 -1
- data/try/features/encrypted_fields/memory_security_try.rb +1 -1
- data/try/features/encrypted_fields/missing_current_key_version_try.rb +1 -1
- data/try/features/encrypted_fields/nonce_uniqueness_try.rb +1 -1
- data/try/features/encrypted_fields/secure_by_default_behavior_try.rb +1 -1
- data/try/features/encrypted_fields/thread_safety_try.rb +1 -1
- data/try/features/encrypted_fields/universal_serialization_safety_try.rb +1 -1
- data/try/{encryption → features/encryption}/config_persistence_try.rb +1 -1
- data/try/{encryption/encryption_core_try.rb → features/encryption/core_try.rb} +2 -2
- data/try/{encryption → features/encryption}/instance_variable_scope_try.rb +1 -1
- data/try/{encryption → features/encryption}/module_loading_try.rb +1 -1
- data/try/{encryption → features/encryption}/providers/aes_gcm_provider_try.rb +1 -1
- data/try/{encryption → features/encryption}/providers/xchacha20_poly1305_provider_try.rb +1 -1
- data/try/{encryption → features/encryption}/roundtrip_validation_try.rb +1 -1
- data/try/{encryption → features/encryption}/secure_memory_handling_try.rb +2 -2
- data/try/features/expiration/expiration_try.rb +2 -2
- data/try/features/external_identifier/external_identifier_try.rb +1 -1
- data/try/features/feature_dependencies_try.rb +1 -1
- data/try/features/feature_improvements_try.rb +1 -1
- data/try/features/object_identifier/object_identifier_integration_try.rb +1 -1
- data/try/features/object_identifier/object_identifier_try.rb +1 -1
- data/try/features/quantization/quantization_try.rb +1 -1
- data/try/features/real_feature_integration_try.rb +17 -14
- data/try/features/relationships/indexing_commands_verification_try.rb +8 -3
- data/try/features/relationships/indexing_try.rb +34 -5
- data/try/features/relationships/participation_commands_verification_spec.rb +1 -1
- data/try/features/relationships/participation_commands_verification_try.rb +4 -4
- data/try/features/relationships/participation_performance_improvements_try.rb +1 -1
- data/try/features/relationships/participation_reverse_index_try.rb +1 -1
- data/try/features/relationships/relationships_api_changes_try.rb +5 -5
- data/try/features/relationships/relationships_edge_cases_try.rb +3 -3
- data/try/features/relationships/relationships_performance_minimal_try.rb +1 -1
- data/try/features/relationships/relationships_performance_simple_try.rb +1 -1
- data/try/features/relationships/relationships_performance_try.rb +1 -1
- data/try/features/relationships/relationships_performance_working_try.rb +1 -1
- data/try/features/relationships/relationships_try.rb +1 -1
- data/try/features/safe_dump/safe_dump_advanced_try.rb +1 -1
- data/try/features/safe_dump/safe_dump_try.rb +1 -1
- data/try/features/transient_fields/redacted_string_try.rb +1 -1
- data/try/features/transient_fields/refresh_reset_try.rb +1 -1
- data/try/features/transient_fields/single_use_redacted_string_try.rb +1 -1
- data/try/features/transient_fields/transient_fields_core_try.rb +1 -1
- data/try/features/transient_fields/transient_fields_integration_try.rb +1 -1
- data/try/{connection → integration/connection}/fiber_context_preservation_try.rb +4 -4
- data/try/{connection → integration/connection}/handler_constraints_try.rb +1 -1
- data/try/{core → integration/connection}/isolated_dbclient_try.rb +1 -1
- data/try/integration/connection/middleware_reconnect_try.rb +87 -0
- data/try/{connection → integration/connection}/operation_mode_guards_try.rb +2 -2
- data/try/{connection → integration/connection}/pipeline_fallback_integration_try.rb +13 -13
- data/try/{core → integration/connection}/pools_try.rb +1 -1
- data/try/{connection → integration/connection}/responsibility_chain_tracking_try.rb +1 -1
- data/try/{connection → integration/connection}/transaction_fallback_integration_try.rb +1 -1
- data/try/{connection → integration/connection}/transaction_mode_permissive_try.rb +1 -1
- data/try/{connection → integration/connection}/transaction_mode_strict_try.rb +1 -1
- data/try/{connection → integration/connection}/transaction_mode_warn_try.rb +1 -1
- data/try/{connection → integration/connection}/transaction_modes_try.rb +1 -1
- data/try/{core → integration}/conventional_inheritance_try.rb +1 -1
- data/try/{core → integration}/create_method_try.rb +23 -23
- data/try/integration/cross_component_try.rb +1 -1
- data/try/integration/data_types/datatype_pipelines_try.rb +104 -0
- data/try/integration/data_types/datatype_transactions_try.rb +247 -0
- data/try/{core → integration}/database_consistency_try.rb +11 -8
- data/try/{core → integration}/familia_extended_try.rb +1 -1
- data/try/{core → integration}/familia_members_methods_try.rb +1 -1
- data/try/{models → integration/models}/customer_safe_dump_try.rb +6 -2
- data/try/{models → integration/models}/customer_try.rb +1 -1
- data/try/{models → integration/models}/datatype_base_try.rb +1 -1
- data/try/{models → integration/models}/familia_object_try.rb +2 -2
- data/try/{core → integration}/persistence_operations_try.rb +163 -11
- data/try/integration/relationships_persistence_round_trip_try.rb +441 -0
- data/try/{configuration → integration}/scenarios_try.rb +1 -1
- data/try/{core → integration}/secure_identifier_try.rb +1 -1
- data/try/{core → integration}/verifiable_identifier_try.rb +1 -1
- data/try/performance/benchmarks_try.rb +2 -2
- data/try/support/benchmarks/deserialization_benchmark.rb +180 -0
- data/try/support/benchmarks/deserialization_correctness_test.rb +237 -0
- data/try/{helpers → support/helpers}/test_helpers.rb +12 -3
- data/try/{core → unit/core}/autoloader_try.rb +1 -1
- data/try/{core → unit/core}/base_enhancements_try.rb +1 -9
- data/try/{core → unit/core}/connection_try.rb +1 -1
- data/try/{core → unit/core}/errors_try.rb +1 -1
- data/try/{core → unit/core}/extensions_try.rb +1 -1
- data/try/unit/core/familia_logger_try.rb +110 -0
- data/try/{core → unit/core}/familia_try.rb +1 -1
- data/try/{core → unit/core}/middleware_try.rb +41 -1
- data/try/{core → unit/core}/settings_try.rb +1 -1
- data/try/{core → unit/core}/time_utils_try.rb +1 -1
- data/try/{core → unit/core}/tools_try.rb +1 -1
- data/try/{core → unit/core}/utils_try.rb +17 -14
- data/try/{data_types → unit/data_types}/boolean_try.rb +2 -2
- data/try/{data_types → unit/data_types}/counter_try.rb +1 -1
- data/try/{data_types → unit/data_types}/datatype_base_try.rb +1 -1
- data/try/{data_types → unit/data_types}/hash_try.rb +1 -1
- data/try/{data_types → unit/data_types}/list_try.rb +1 -1
- data/try/{data_types → unit/data_types}/lock_try.rb +1 -1
- data/try/{data_types → unit/data_types}/sorted_set_try.rb +1 -1
- data/try/{data_types → unit/data_types}/sorted_set_zadd_options_try.rb +1 -1
- data/try/{data_types → unit/data_types}/string_try.rb +2 -2
- data/try/{data_types → unit/data_types}/unsortedset_try.rb +1 -1
- data/try/{horreum → unit/horreum}/auto_indexing_on_save_try.rb +33 -17
- data/try/unit/horreum/automatic_index_validation_try.rb +253 -0
- data/try/{horreum → unit/horreum}/base_try.rb +4 -4
- data/try/{horreum → unit/horreum}/class_methods_try.rb +3 -3
- data/try/{horreum → unit/horreum}/commands_try.rb +1 -1
- data/try/{horreum → unit/horreum}/defensive_initialization_try.rb +1 -1
- data/try/{horreum → unit/horreum}/destroy_related_fields_cleanup_try.rb +1 -1
- data/try/{horreum → unit/horreum}/enhanced_conflict_handling_try.rb +1 -1
- data/try/{horreum → unit/horreum}/field_categories_try.rb +27 -18
- data/try/{horreum → unit/horreum}/field_definition_try.rb +1 -1
- data/try/{horreum → unit/horreum}/initialization_try.rb +3 -3
- data/try/unit/horreum/json_type_preservation_try.rb +248 -0
- data/try/{horreum → unit/horreum}/relations_try.rb +5 -5
- data/try/{horreum → unit/horreum}/serialization_persistent_fields_try.rb +24 -18
- data/try/{horreum → unit/horreum}/serialization_try.rb +6 -6
- data/try/{horreum → unit/horreum}/settings_try.rb +1 -1
- data/try/unit/horreum/unique_index_edge_cases_try.rb +376 -0
- data/try/unit/horreum/unique_index_guard_validation_try.rb +281 -0
- data/try/{refinements → unit/refinements}/dear_json_array_methods_try.rb +1 -1
- data/try/{refinements → unit/refinements}/dear_json_hash_methods_try.rb +1 -1
- data/try/{refinements → unit/refinements}/time_literals_numeric_methods_try.rb +1 -1
- data/try/{refinements → unit/refinements}/time_literals_string_methods_try.rb +1 -1
- metadata +147 -126
- data/lib/familia/distinguisher.rb +0 -85
- data/lib/familia/refinements/logger_trace.rb +0 -60
- data/try/refinements/logger_trace_methods_try.rb +0 -44
- /data/try/{debugging → support/debugging}/README.md +0 -0
- /data/try/{debugging → support/debugging}/cache_behavior_tracer.rb +0 -0
- /data/try/{debugging → support/debugging}/debug_aad_process.rb +0 -0
- /data/try/{debugging → support/debugging}/debug_concealed_internal.rb +0 -0
- /data/try/{debugging → support/debugging}/debug_concealed_reveal.rb +0 -0
- /data/try/{debugging → support/debugging}/debug_context_aad.rb +0 -0
- /data/try/{debugging → support/debugging}/debug_context_simple.rb +0 -0
- /data/try/{debugging → support/debugging}/debug_cross_context.rb +0 -0
- /data/try/{debugging → support/debugging}/debug_database_load.rb +0 -0
- /data/try/{debugging → support/debugging}/debug_encrypted_json_check.rb +0 -0
- /data/try/{debugging → support/debugging}/debug_encrypted_json_step_by_step.rb +0 -0
- /data/try/{debugging → support/debugging}/debug_exists_lifecycle.rb +0 -0
- /data/try/{debugging → support/debugging}/debug_field_decrypt.rb +0 -0
- /data/try/{debugging → support/debugging}/debug_fresh_cross_context.rb +0 -0
- /data/try/{debugging → support/debugging}/debug_load_path.rb +0 -0
- /data/try/{debugging → support/debugging}/debug_method_definition.rb +0 -0
- /data/try/{debugging → support/debugging}/debug_method_resolution.rb +0 -0
- /data/try/{debugging → support/debugging}/debug_minimal.rb +0 -0
- /data/try/{debugging → support/debugging}/debug_provider.rb +0 -0
- /data/try/{debugging → support/debugging}/debug_secure_behavior.rb +0 -0
- /data/try/{debugging → support/debugging}/debug_string_class.rb +0 -0
- /data/try/{debugging → support/debugging}/debug_test.rb +0 -0
- /data/try/{debugging → support/debugging}/debug_test_design.rb +0 -0
- /data/try/{debugging → support/debugging}/encryption_method_tracer.rb +0 -0
- /data/try/{debugging → support/debugging}/provider_diagnostics.rb +0 -0
- /data/try/{helpers → support/helpers}/test_cleanup.rb +0 -0
- /data/try/{memory → support/memory}/memory_basic_test.rb +0 -0
- /data/try/{memory → support/memory}/memory_detailed_test.rb +0 -0
- /data/try/{memory → support/memory}/memory_docker_ruby_dump.sh +0 -0
- /data/try/{memory → support/memory}/memory_search_for_string.rb +0 -0
- /data/try/{memory → support/memory}/test_actual_redactedstring_protection.rb +0 -0
- /data/try/{prototypes → support/prototypes}/atomic_saves_v1_context_proxy.rb +0 -0
- /data/try/{prototypes → support/prototypes}/atomic_saves_v2_connection_switching.rb +0 -0
- /data/try/{prototypes → support/prototypes}/atomic_saves_v3_connection_pool.rb +0 -0
- /data/try/{prototypes → support/prototypes}/atomic_saves_v4.rb +0 -0
- /data/try/{prototypes → support/prototypes}/lib/atomic_saves_v2_connection_switching_helpers.rb +0 -0
- /data/try/{prototypes → support/prototypes}/lib/atomic_saves_v3_connection_pool_helpers.rb +0 -0
- /data/try/{prototypes → support/prototypes}/pooling/README.md +0 -0
- /data/try/{prototypes → support/prototypes}/pooling/configurable_stress_test.rb +0 -0
- /data/try/{prototypes → support/prototypes}/pooling/lib/atomic_saves_v3_connection_pool_helpers.rb +0 -0
- /data/try/{prototypes → support/prototypes}/pooling/lib/connection_pool_metrics.rb +0 -0
- /data/try/{prototypes → support/prototypes}/pooling/lib/connection_pool_stress_test.rb +0 -0
- /data/try/{prototypes → support/prototypes}/pooling/lib/connection_pool_threading_models.rb +0 -0
- /data/try/{prototypes → support/prototypes}/pooling/lib/visualize_stress_results.rb +0 -0
- /data/try/{prototypes → support/prototypes}/pooling/pool_siege.rb +0 -0
- /data/try/{prototypes → support/prototypes}/pooling/run_stress_tests.rb +0 -0
@@ -2,7 +2,7 @@
|
|
2
2
|
|
3
3
|
# These tryouts test the safe dumping functionality.
|
4
4
|
|
5
|
-
require_relative '../../helpers/test_helpers'
|
5
|
+
require_relative '../../support/helpers/test_helpers'
|
6
6
|
|
7
7
|
## By default Familia::Base has no safe_dump_fields method
|
8
8
|
Familia::Base.respond_to?(:safe_dump_fields)
|
@@ -12,7 +12,7 @@
|
|
12
12
|
# - Operation guards prevent unsafe scenarios before fiber issues arise
|
13
13
|
# - Method aliases work correctly
|
14
14
|
|
15
|
-
require_relative '
|
15
|
+
require_relative '../../support/helpers/test_helpers'
|
16
16
|
|
17
17
|
## Transaction method works without previous_conn preservation
|
18
18
|
customer = Customer.new(custid: 'tx_test')
|
@@ -166,7 +166,7 @@ end
|
|
166
166
|
## Operation guards prevent pipeline fiber issues before they occur
|
167
167
|
begin
|
168
168
|
# Ensure we're in strict mode for this test
|
169
|
-
Familia.configure { |config| config.
|
169
|
+
Familia.configure { |config| config.pipelined_mode = :strict }
|
170
170
|
|
171
171
|
Fiber[:familia_connection] = [Customer.create_dbclient, Familia.middleware_version]
|
172
172
|
Fiber[:familia_connection_handler_class] = Familia::Connection::FiberConnectionHandler
|
@@ -185,7 +185,7 @@ end
|
|
185
185
|
|
186
186
|
## Method aliases work correctly
|
187
187
|
# pipeline alias for pipelined
|
188
|
-
result1 = Familia.
|
188
|
+
result1 = Familia.pipelined do |conn|
|
189
189
|
conn.set('alias_test', 'alias_success')
|
190
190
|
conn.get('alias_test')
|
191
191
|
end
|
@@ -203,7 +203,7 @@ result1.results.last == 'alias_success' && result2.results.last == 'alias_succes
|
|
203
203
|
customer = Customer.new(custid: 'alias_test')
|
204
204
|
|
205
205
|
# pipeline alias
|
206
|
-
result1 = customer.
|
206
|
+
result1 = customer.pipelined do |conn|
|
207
207
|
conn.set('horreum:alias1', 'success1')
|
208
208
|
conn.get('horreum:alias1')
|
209
209
|
end
|
@@ -8,7 +8,7 @@
|
|
8
8
|
# - Fresh connections (provider/create) → safe for all operations
|
9
9
|
# - Transaction connections → safe for reentrant transactions only
|
10
10
|
|
11
|
-
require_relative '
|
11
|
+
require_relative '../../support/helpers/test_helpers'
|
12
12
|
|
13
13
|
## FiberTransactionHandler constraints
|
14
14
|
Familia::Connection::FiberTransactionHandler.allows_transaction
|
@@ -5,7 +5,7 @@
|
|
5
5
|
# Tests for isolated database connections that don't interfere
|
6
6
|
# with the cached connection pool or existing model connections.
|
7
7
|
|
8
|
-
require_relative '
|
8
|
+
require_relative '../../support/helpers/test_helpers'
|
9
9
|
|
10
10
|
# Clean up any existing test data in all test databases
|
11
11
|
(0..2).each do |db|
|
@@ -0,0 +1,87 @@
|
|
1
|
+
# try/integration/connection/middleware_reconnect_try.rb
|
2
|
+
#
|
3
|
+
# Tests for Familia.reconnect! method that refreshes connection pools
|
4
|
+
# with current middleware configuration
|
5
|
+
|
6
|
+
require_relative '../../support/helpers/test_helpers'
|
7
|
+
require 'connection_pool'
|
8
|
+
|
9
|
+
# Disable logging for cleaner test output
|
10
|
+
Familia.enable_database_logging = false
|
11
|
+
|
12
|
+
# Test model for middleware reconnection testing
|
13
|
+
class ReconnectTestUser < Familia::Horreum
|
14
|
+
identifier_field :user_id
|
15
|
+
field :user_id
|
16
|
+
field :name
|
17
|
+
|
18
|
+
def init
|
19
|
+
@user_id ||= SecureRandom.hex(4)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
## Test 5: Middleware re-registration flag is reset and re-set
|
24
|
+
Familia.enable_database_logging = true
|
25
|
+
Familia.instance_variable_set(:@middleware_registered, true)
|
26
|
+
Familia.reconnect!
|
27
|
+
was_registered = Familia.instance_variable_get(:@middleware_registered)
|
28
|
+
was_registered
|
29
|
+
#=> true
|
30
|
+
|
31
|
+
## Test 6: reconnect! safely handles no middleware enabled
|
32
|
+
Familia.enable_database_logging = false
|
33
|
+
Familia.enable_database_counter = false
|
34
|
+
Familia.reconnect!
|
35
|
+
chain_cleared = Familia.instance_variable_get(:@connection_chain).nil?
|
36
|
+
chain_cleared
|
37
|
+
#=> true
|
38
|
+
|
39
|
+
|
40
|
+
|
41
|
+
## Setup: Clean database
|
42
|
+
ReconnectTestUser.dbclient.flushdb
|
43
|
+
#=> "OK"
|
44
|
+
|
45
|
+
## Test 1: Basic reconnect functionality clears chain and increments version
|
46
|
+
Familia.enable_database_logging = true
|
47
|
+
initial = Familia.middleware_version
|
48
|
+
Familia.reconnect!
|
49
|
+
chain_cleared = Familia.instance_variable_get(:@connection_chain).nil?
|
50
|
+
version_incremented = Familia.middleware_version > initial
|
51
|
+
[chain_cleared, version_incremented]
|
52
|
+
#=> [true, true]
|
53
|
+
|
54
|
+
## Test 2: Reconnect works with connection providers
|
55
|
+
|
56
|
+
# Create a simple connection provider
|
57
|
+
Familia.connection_provider = ->(uri) { Redis.new(url: uri) }
|
58
|
+
|
59
|
+
## Reconnect clears chain even with provider
|
60
|
+
Familia.reconnect!
|
61
|
+
Familia.instance_variable_get(:@connection_chain)
|
62
|
+
#=> nil
|
63
|
+
|
64
|
+
## Test 3: Verify new connections work after reconnect
|
65
|
+
|
66
|
+
# Create user to trigger connection
|
67
|
+
@user = ReconnectTestUser.new(name: "Bob")
|
68
|
+
@user.save
|
69
|
+
#=> true
|
70
|
+
|
71
|
+
## User should be retrievable
|
72
|
+
@retrieved = ReconnectTestUser.find(@user.identifier)
|
73
|
+
@retrieved.name
|
74
|
+
#=> "Bob"
|
75
|
+
|
76
|
+
## Test 4: Multiple reconnects are safe
|
77
|
+
Familia.reconnect!
|
78
|
+
Familia.reconnect!
|
79
|
+
Familia.reconnect!
|
80
|
+
|
81
|
+
## Connection chain should still be cleared (will rebuild on next use)
|
82
|
+
Familia.instance_variable_get(:@connection_chain)
|
83
|
+
#=> nil
|
84
|
+
|
85
|
+
## Cleanup
|
86
|
+
Familia.enable_database_logging = false
|
87
|
+
Familia.connection_provider = nil
|
@@ -12,7 +12,7 @@
|
|
12
12
|
# This prevents bugs where middleware/cached connections return "QUEUED" instead
|
13
13
|
# of actual values, breaking conditional logic and business rules.
|
14
14
|
|
15
|
-
require_relative '
|
15
|
+
require_relative '../../support/helpers/test_helpers'
|
16
16
|
|
17
17
|
## FiberConnectionHandler blocks transactions in strict mode
|
18
18
|
begin
|
@@ -36,7 +36,7 @@ end
|
|
36
36
|
## FiberConnectionHandler blocks pipelines
|
37
37
|
begin
|
38
38
|
# Ensure we're in strict mode for this test
|
39
|
-
Familia.configure { |config| config.
|
39
|
+
Familia.configure { |config| config.pipelined_mode = :strict }
|
40
40
|
|
41
41
|
# Simulate middleware connection
|
42
42
|
Fiber[:familia_connection] = [Customer.create_dbclient, Familia.middleware_version]
|
@@ -1,13 +1,13 @@
|
|
1
1
|
#
|
2
2
|
# Tests pipeline fallback modes when connection handlers don't support pipelines.
|
3
|
-
# Validates that
|
3
|
+
# Validates that pipelined_mode configuration works correctly with cached connections
|
4
4
|
# and that the fallback behavior matches transaction fallback patterns.
|
5
5
|
#
|
6
6
|
|
7
|
-
require_relative '
|
7
|
+
require_relative '../../support/helpers/test_helpers'
|
8
8
|
|
9
9
|
# Store original values
|
10
|
-
$
|
10
|
+
$original_pipelined_mode = Familia.pipelined_mode
|
11
11
|
$original_transaction_mode = Familia.transaction_mode
|
12
12
|
|
13
13
|
# Test model for pipeline fallback scenarios
|
@@ -17,7 +17,7 @@ class PipelineFallbackTest < Familia::Horreum
|
|
17
17
|
end
|
18
18
|
|
19
19
|
## Test 1: Strict mode raises error with cached connection
|
20
|
-
Familia.configure { |c| c.
|
20
|
+
Familia.configure { |c| c.pipelined_mode = :strict }
|
21
21
|
|
22
22
|
# Cache connection at class level (uses DefaultConnectionHandler which doesn't support pipelines)
|
23
23
|
PipelineFallbackTest.instance_variable_set(:@dbclient, Familia.create_dbclient)
|
@@ -28,7 +28,7 @@ customer.pipelined { |c| c.set('key', 'value') }
|
|
28
28
|
#=~> /Cannot start pipeline with.*CachedConnectionHandler/
|
29
29
|
|
30
30
|
## Test 2: Warn mode falls back successfully with cached connection
|
31
|
-
Familia.configure { |c| c.
|
31
|
+
Familia.configure { |c| c.pipelined_mode = :warn }
|
32
32
|
|
33
33
|
# Cache connection at class level
|
34
34
|
PipelineFallbackTest.instance_variable_set(:@dbclient, Familia.create_dbclient)
|
@@ -51,7 +51,7 @@ $warn_result.results[2]
|
|
51
51
|
#=> 'value1'
|
52
52
|
|
53
53
|
## Test 3: Fresh connections still support real pipelines in strict mode
|
54
|
-
Familia.configure { |c| c.
|
54
|
+
Familia.configure { |c| c.pipelined_mode = :strict }
|
55
55
|
|
56
56
|
# Clear cached class-level connection to force CreateConnectionHandler
|
57
57
|
PipelineFallbackTest.remove_instance_variable(:@dbclient) if PipelineFallbackTest.instance_variable_defined?(:@dbclient)
|
@@ -70,7 +70,7 @@ $fresh_result.results.size
|
|
70
70
|
#=> 3
|
71
71
|
|
72
72
|
## Test 4: MultiResult format is correct for fallback
|
73
|
-
Familia.configure { |c| c.
|
73
|
+
Familia.configure { |c| c.pipelined_mode = :permissive }
|
74
74
|
|
75
75
|
# Cache connection at class level
|
76
76
|
PipelineFallbackTest.instance_variable_set(:@dbclient, Familia.create_dbclient)
|
@@ -92,7 +92,7 @@ $multi_result.results.class
|
|
92
92
|
#=> Array
|
93
93
|
|
94
94
|
## Test 5: Permissive mode silently falls back
|
95
|
-
Familia.configure { |c| c.
|
95
|
+
Familia.configure { |c| c.pipelined_mode = :permissive }
|
96
96
|
|
97
97
|
# Cache connection at class level
|
98
98
|
PipelineFallbackTest.instance_variable_set(:@dbclient, Familia.create_dbclient)
|
@@ -111,18 +111,18 @@ $permissive_result.results
|
|
111
111
|
#=> ['OK', 1, '1']
|
112
112
|
|
113
113
|
## Test 6: Pipeline mode configuration validation
|
114
|
-
Familia.configure { |c| c.
|
114
|
+
Familia.configure { |c| c.pipelined_mode = :invalid }
|
115
115
|
#=:> ArgumentError
|
116
116
|
#=~> /Pipeline mode must be :strict, :warn, or :permissive/
|
117
117
|
|
118
|
-
## Test 7: Default
|
119
|
-
Familia.instance_variable_set(:@
|
120
|
-
Familia.
|
118
|
+
## Test 7: Default pipelined_mode is :warn
|
119
|
+
Familia.instance_variable_set(:@pipelined_mode, nil)
|
120
|
+
Familia.pipelined_mode
|
121
121
|
#=> :warn
|
122
122
|
|
123
123
|
## Cleanup: Restore original values
|
124
124
|
Familia.configure do |c|
|
125
|
-
c.
|
125
|
+
c.pipelined_mode = $original_pipelined_mode
|
126
126
|
c.transaction_mode = $original_transaction_mode
|
127
127
|
end
|
128
128
|
PipelineFallbackTest.remove_instance_variable(:@dbclient) if PipelineFallbackTest.instance_variable_defined?(:@dbclient)
|
@@ -10,7 +10,7 @@
|
|
10
10
|
# - Return the connection from the successful handler
|
11
11
|
# - Return nil if no handler provides a connection
|
12
12
|
|
13
|
-
require_relative '
|
13
|
+
require_relative '../../support/helpers/test_helpers'
|
14
14
|
|
15
15
|
# Setup - clear any existing fiber state
|
16
16
|
Fiber[:familia_connection_handler_class] = nil
|
@@ -8,7 +8,7 @@
|
|
8
8
|
# with Familia's existing features when connection handlers don't support
|
9
9
|
# transactions (e.g., cached connections, middleware connections).
|
10
10
|
|
11
|
-
require_relative '
|
11
|
+
require_relative '../../support/helpers/test_helpers'
|
12
12
|
|
13
13
|
# Setup - store original values
|
14
14
|
$original_transaction_mode = Familia.transaction_mode
|
@@ -5,7 +5,7 @@
|
|
5
5
|
#
|
6
6
|
# Permissive mode: Silently uses IndividualCommandProxy for fallback
|
7
7
|
|
8
|
-
require_relative '
|
8
|
+
require_relative '../../support/helpers/test_helpers'
|
9
9
|
|
10
10
|
# Test class for permissive mode testing
|
11
11
|
class PermissiveModeTestCustomer < Familia::Horreum
|
@@ -5,7 +5,7 @@
|
|
5
5
|
#
|
6
6
|
# Strict mode: Raises OperationModeError when transaction unavailable
|
7
7
|
|
8
|
-
require_relative '
|
8
|
+
require_relative '../../support/helpers/test_helpers'
|
9
9
|
|
10
10
|
# Test class for strict mode testing
|
11
11
|
class StrictModeTestCustomer < Familia::Horreum
|
@@ -5,7 +5,7 @@
|
|
5
5
|
#
|
6
6
|
# Warn mode: Logs warning and uses IndividualCommandProxy for fallback
|
7
7
|
|
8
|
-
require_relative '
|
8
|
+
require_relative '../../support/helpers/test_helpers'
|
9
9
|
|
10
10
|
# Test class for warn mode testing
|
11
11
|
class WarnModeTestCustomer < Familia::Horreum
|
@@ -10,7 +10,7 @@
|
|
10
10
|
# The IndividualCommandProxy executes Redis commands immediately instead of queuing
|
11
11
|
# them in a transaction, maintaining the same MultiResult interface for consistency.
|
12
12
|
|
13
|
-
require_relative '
|
13
|
+
require_relative '../../support/helpers/test_helpers'
|
14
14
|
|
15
15
|
# Setup - ensure clean state
|
16
16
|
@original_transaction_mode = Familia.transaction_mode
|
@@ -3,7 +3,7 @@
|
|
3
3
|
# Comprehensive test coverage for the create method
|
4
4
|
# Tests the correct exception type and error message handling
|
5
5
|
|
6
|
-
require_relative '../helpers/test_helpers'
|
6
|
+
require_relative '../support/helpers/test_helpers'
|
7
7
|
|
8
8
|
# Test class for create method behavior
|
9
9
|
class CreateTestModel < Familia::Horreum
|
@@ -37,7 +37,7 @@ end
|
|
37
37
|
# =============================================
|
38
38
|
|
39
39
|
## create method successfully creates new object
|
40
|
-
@created_obj = CreateTestModel.create(id: @first_test_id, name: 'Created Object', value: 'test_value')
|
40
|
+
@created_obj = CreateTestModel.create!(id: @first_test_id, name: 'Created Object', value: 'test_value')
|
41
41
|
[@created_obj.class, @created_obj.exists?, @created_obj.name]
|
42
42
|
#=> [CreateTestModel, true, 'Created Object']
|
43
43
|
|
@@ -55,17 +55,17 @@ end
|
|
55
55
|
# =============================================
|
56
56
|
|
57
57
|
## create method raises RecordExistsError for duplicate
|
58
|
-
CreateTestModel.create(id: @first_test_id, name: 'Duplicate Attempt')
|
58
|
+
CreateTestModel.create!(id: @first_test_id, name: 'Duplicate Attempt')
|
59
59
|
#=!> Familia::RecordExistsError
|
60
60
|
|
61
61
|
## RecordExistsError includes the dbkey in the message
|
62
|
-
CreateTestModel.create(id: @first_test_id, name: 'Another Duplicate')
|
62
|
+
CreateTestModel.create!(id: @first_test_id, name: 'Another Duplicate')
|
63
63
|
#=!> Familia::RecordExistsError
|
64
64
|
#==> !!error.message.match(/create_test_model:#{@first_test_id}:object/)
|
65
65
|
|
66
66
|
## RecordExistsError message follows consistent format
|
67
67
|
begin
|
68
|
-
CreateTestModel.create(id: @first_test_id, name: 'Yet Another Duplicate')
|
68
|
+
CreateTestModel.create!(id: @first_test_id, name: 'Yet Another Duplicate')
|
69
69
|
false # Should not reach here
|
70
70
|
rescue Familia::RecordExistsError => e
|
71
71
|
e.message.start_with?('Key already exists:')
|
@@ -74,10 +74,10 @@ end
|
|
74
74
|
|
75
75
|
## RecordExistsError exposes key property for programmatic access
|
76
76
|
@final_test_id = next_test_id
|
77
|
-
CreateTestModel.create(id: @final_test_id, name: 'Setup for Key Test')
|
77
|
+
CreateTestModel.create!(id: @final_test_id, name: 'Setup for Key Test')
|
78
78
|
|
79
79
|
begin
|
80
|
-
CreateTestModel.create(id: @final_test_id, name: 'Key Test Duplicate')
|
80
|
+
CreateTestModel.create!(id: @final_test_id, name: 'Key Test Duplicate')
|
81
81
|
false # Should not reach here
|
82
82
|
rescue Familia::RecordExistsError => e
|
83
83
|
# Key should be accessible and contain the identifier
|
@@ -90,22 +90,22 @@ end
|
|
90
90
|
# =============================================
|
91
91
|
|
92
92
|
## create with empty identifier raises NoIdentifier error
|
93
|
-
CreateTestModel.create(id: '')
|
93
|
+
CreateTestModel.create!(id: '')
|
94
94
|
#=!> Familia::NoIdentifier
|
95
95
|
|
96
96
|
## create with nil identifier raises NoIdentifier error
|
97
|
-
CreateTestModel.create(id: nil)
|
97
|
+
CreateTestModel.create!(id: nil)
|
98
98
|
#=!> Familia::NoIdentifier
|
99
99
|
|
100
100
|
## create with only some fields set
|
101
101
|
@partial_id = next_test_id
|
102
|
-
@partial_obj = CreateTestModel.create(id: @partial_id, name: 'Partial Object')
|
102
|
+
@partial_obj = CreateTestModel.create!(id: @partial_id, name: 'Partial Object')
|
103
103
|
[@partial_obj.exists?, @partial_obj.name, @partial_obj.value]
|
104
104
|
#=> [true, 'Partial Object', nil]
|
105
105
|
|
106
106
|
## create with no additional fields (only identifier)
|
107
107
|
@minimal_id = next_test_id
|
108
|
-
@minimal_obj = CreateTestModel.create(id: @minimal_id)
|
108
|
+
@minimal_obj = CreateTestModel.create!(id: @minimal_id)
|
109
109
|
[@minimal_obj.exists?, @minimal_obj.id]
|
110
110
|
#=> [true, @minimal_id]
|
111
111
|
|
@@ -115,14 +115,14 @@ CreateTestModel.create(id: nil)
|
|
115
115
|
|
116
116
|
## create is atomic - no partial state on failure
|
117
117
|
@concurrent_id = next_test_id
|
118
|
-
@first_obj = CreateTestModel.create(id: @concurrent_id, name: 'First')
|
118
|
+
@first_obj = CreateTestModel.create!(id: @concurrent_id, name: 'First')
|
119
119
|
|
120
120
|
# Verify first object exists
|
121
121
|
first_exists = @first_obj.exists?
|
122
122
|
|
123
123
|
# Attempt to create duplicate should not affect existing object
|
124
124
|
begin
|
125
|
-
CreateTestModel.create(id: @concurrent_id, name: 'Concurrent Attempt')
|
125
|
+
CreateTestModel.create!(id: @concurrent_id, name: 'Concurrent Attempt')
|
126
126
|
false # Should not reach here
|
127
127
|
rescue Familia::RecordExistsError
|
128
128
|
# Original object should be unchanged
|
@@ -134,7 +134,7 @@ end
|
|
134
134
|
## create failure doesn't leave partial data
|
135
135
|
before_failed_create = Familia.dbclient.keys("create_test_model:#{@concurrent_id}:*").length
|
136
136
|
begin
|
137
|
-
CreateTestModel.create(id: @concurrent_id, name: 'Should Fail')
|
137
|
+
CreateTestModel.create!(id: @concurrent_id, name: 'Should Fail')
|
138
138
|
rescue Familia::RecordExistsError
|
139
139
|
# Should not create any additional keys
|
140
140
|
after_failed_create = Familia.dbclient.keys("create_test_model:#{@concurrent_id}:*").length
|
@@ -148,20 +148,20 @@ end
|
|
148
148
|
|
149
149
|
## Both create and save_if_not_exists raise same error type for duplicates
|
150
150
|
@consistency_id = next_test_id
|
151
|
-
@consistency_obj = CreateTestModel.create(id: @consistency_id, name: 'Consistency Test')
|
151
|
+
@consistency_obj = CreateTestModel.create!(id: @consistency_id, name: 'Consistency Test')
|
152
152
|
|
153
153
|
# Test create raises RecordExistsError
|
154
154
|
create_error_class = begin
|
155
|
-
CreateTestModel.create(id: @consistency_id, name: 'Create Duplicate')
|
155
|
+
CreateTestModel.create!(id: @consistency_id, name: 'Create Duplicate')
|
156
156
|
nil
|
157
157
|
rescue => e
|
158
158
|
e.class
|
159
159
|
end
|
160
160
|
|
161
|
-
# Test save_if_not_exists raises RecordExistsError
|
161
|
+
# Test save_if_not_exists! raises RecordExistsError
|
162
162
|
sine_error_class = begin
|
163
163
|
duplicate_obj = CreateTestModel.new(id: @consistency_id, name: 'SINE Duplicate')
|
164
|
-
duplicate_obj.save_if_not_exists
|
164
|
+
duplicate_obj.save_if_not_exists!
|
165
165
|
nil
|
166
166
|
rescue => e
|
167
167
|
e.class
|
@@ -172,17 +172,17 @@ end
|
|
172
172
|
|
173
173
|
## Both methods have similar error message patterns
|
174
174
|
@error_comparison_id = next_test_id
|
175
|
-
CreateTestModel.create(id: @error_comparison_id, name: 'Error Comparison')
|
175
|
+
CreateTestModel.create!(id: @error_comparison_id, name: 'Error Comparison')
|
176
176
|
|
177
177
|
create_error_msg = begin
|
178
|
-
CreateTestModel.create(id: @error_comparison_id, name: 'Create Error')
|
178
|
+
CreateTestModel.create!(id: @error_comparison_id, name: 'Create Error')
|
179
179
|
nil
|
180
180
|
rescue => e
|
181
181
|
e.message
|
182
182
|
end
|
183
183
|
|
184
184
|
sine_error_msg = begin
|
185
|
-
CreateTestModel.new(id: @error_comparison_id, name: 'SINE Error').save_if_not_exists
|
185
|
+
CreateTestModel.new(id: @error_comparison_id, name: 'SINE Error').save_if_not_exists!
|
186
186
|
nil
|
187
187
|
rescue => e
|
188
188
|
e.message
|
@@ -198,7 +198,7 @@ end
|
|
198
198
|
|
199
199
|
## create works with complex field values
|
200
200
|
@complex_id = next_test_id
|
201
|
-
@complex_obj = CreateTestModel.create(
|
201
|
+
@complex_obj = CreateTestModel.create!(
|
202
202
|
id: @complex_id,
|
203
203
|
name: 'Complex Object',
|
204
204
|
value: { nested: 'data', array: [1, 2, 3] }
|
@@ -214,7 +214,7 @@ end
|
|
214
214
|
@consistency_check_id = next_test_id
|
215
215
|
|
216
216
|
# Create via class method
|
217
|
-
@class_created = CreateTestModel.create(id: @consistency_check_id, name: 'Class Created')
|
217
|
+
@class_created = CreateTestModel.create!(id: @consistency_check_id, name: 'Class Created')
|
218
218
|
|
219
219
|
# Both class and instance methods should see the object as existing
|
220
220
|
class_sees_exists = CreateTestModel.exists?(@consistency_check_id)
|
@@ -0,0 +1,104 @@
|
|
1
|
+
# DataType Pipeline Support Tryouts
|
2
|
+
#
|
3
|
+
# Tests pipeline support for DataType objects. Pipelines provide performance
|
4
|
+
# optimization by batching commands without the atomicity guarantee of transactions.
|
5
|
+
|
6
|
+
require_relative '../../support/helpers/test_helpers'
|
7
|
+
|
8
|
+
# Setup
|
9
|
+
class PipelineTestUser < Familia::Horreum
|
10
|
+
logical_database 4
|
11
|
+
identifier_field :userid
|
12
|
+
field :userid
|
13
|
+
field :name
|
14
|
+
|
15
|
+
sorted_set :scores
|
16
|
+
hashkey :profile
|
17
|
+
set :tags
|
18
|
+
counter :visits
|
19
|
+
end
|
20
|
+
|
21
|
+
@user = PipelineTestUser.new(userid: 'pipe_user_001')
|
22
|
+
@user.name = 'Pipeline Tester'
|
23
|
+
@user.save
|
24
|
+
|
25
|
+
## Parent-owned SortedSet can execute pipeline
|
26
|
+
result = @user.scores.pipelined do |pipe|
|
27
|
+
pipe.zadd(@user.scores.dbkey, 100, 'p1')
|
28
|
+
pipe.zadd(@user.scores.dbkey, 200, 'p2')
|
29
|
+
pipe.zcard(@user.scores.dbkey)
|
30
|
+
end
|
31
|
+
[result.is_a?(MultiResult), @user.scores.members.size]
|
32
|
+
#=> [true, 2]
|
33
|
+
|
34
|
+
## Parent-owned HashKey can execute pipeline
|
35
|
+
result = @user.profile.pipelined do |pipe|
|
36
|
+
pipe.hset(@user.profile.dbkey, 'city', 'NYC')
|
37
|
+
pipe.hset(@user.profile.dbkey, 'state', 'NY')
|
38
|
+
pipe.hgetall(@user.profile.dbkey)
|
39
|
+
end
|
40
|
+
[result.is_a?(MultiResult), @user.profile.keys.sort]
|
41
|
+
#=> [true, ["city", "state"]]
|
42
|
+
|
43
|
+
## Standalone SortedSet can execute pipeline
|
44
|
+
leaderboard = Familia::SortedSet.new('pipeline:leaderboard')
|
45
|
+
leaderboard.delete!
|
46
|
+
result = leaderboard.pipelined do |pipe|
|
47
|
+
pipe.zadd(leaderboard.dbkey, 100, 'player1')
|
48
|
+
pipe.zadd(leaderboard.dbkey, 200, 'player2')
|
49
|
+
pipe.zcard(leaderboard.dbkey)
|
50
|
+
end
|
51
|
+
[result.is_a?(MultiResult), leaderboard.members.size]
|
52
|
+
#=> [true, 2]
|
53
|
+
|
54
|
+
## Pipeline with direct_access works correctly
|
55
|
+
result = @user.profile.pipelined do |pipe_conn|
|
56
|
+
pipe_conn.hset(@user.profile.dbkey, 'pipeline_test', 'yes')
|
57
|
+
|
58
|
+
@user.profile.direct_access do |conn, key|
|
59
|
+
conn.object_id == pipe_conn.object_id &&
|
60
|
+
conn.hset(key, 'direct_test', 'yes')
|
61
|
+
end
|
62
|
+
end
|
63
|
+
[@user.profile['pipeline_test'], @user.profile['direct_test']]
|
64
|
+
#=> ["yes", "yes"]
|
65
|
+
|
66
|
+
## Pipeline returns MultiResult with correct structure
|
67
|
+
result = @user.scores.pipelined do |pipe|
|
68
|
+
pipe.zadd(@user.scores.dbkey, 300, 'p3')
|
69
|
+
pipe.zadd(@user.scores.dbkey, 400, 'p4')
|
70
|
+
end
|
71
|
+
[result.is_a?(MultiResult), result.results.is_a?(Array)]
|
72
|
+
#=> [true, true]
|
73
|
+
|
74
|
+
## Empty pipeline returns empty MultiResult
|
75
|
+
result = @user.scores.pipelined { |pipe| }
|
76
|
+
[result.is_a?(MultiResult), result.results.empty?]
|
77
|
+
#=> [true, true]
|
78
|
+
|
79
|
+
## Multiple DataType operations in single pipeline
|
80
|
+
result = @user.scores.pipelined do |pipe|
|
81
|
+
pipe.zadd(@user.scores.dbkey, 500, 'multi')
|
82
|
+
pipe.hset(@user.profile.dbkey, 'multi', 'pipeline')
|
83
|
+
pipe.sadd(@user.tags.dbkey, 'multi_tag')
|
84
|
+
end
|
85
|
+
[
|
86
|
+
result.is_a?(MultiResult),
|
87
|
+
@user.scores.member?('multi'),
|
88
|
+
@user.profile['multi'],
|
89
|
+
@user.tags.member?('multi_tag')
|
90
|
+
]
|
91
|
+
#=> [true, true, "pipeline", true]
|
92
|
+
|
93
|
+
## Standalone HashKey with logical_database option
|
94
|
+
custom = Familia::HashKey.new('pipeline:custom', logical_database: 5)
|
95
|
+
custom.delete!
|
96
|
+
result = custom.pipelined do |pipe|
|
97
|
+
pipe.hset(custom.dbkey, 'key1', 'value1')
|
98
|
+
pipe.hget(custom.dbkey, 'key1')
|
99
|
+
end
|
100
|
+
result.is_a?(MultiResult)
|
101
|
+
#=> true
|
102
|
+
|
103
|
+
# Cleanup
|
104
|
+
@user.destroy!
|