familia 2.0.0.pre15 → 2.0.0.pre17
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 +2 -2
- data/.github/workflows/code-quality.yml +138 -0
- data/.github/workflows/code-smells.yml +85 -0
- data/.github/workflows/docs.yml +31 -8
- data/.gitignore +3 -1
- data/.pre-commit-config.yaml +7 -1
- data/.reek.yml +98 -0
- data/.rubocop.yml +54 -10
- data/.talismanrc +9 -0
- data/.yardopts +18 -13
- data/CHANGELOG.rst +86 -4
- data/CLAUDE.md +39 -1
- data/Gemfile +6 -5
- data/Gemfile.lock +99 -23
- data/LICENSE.txt +1 -1
- data/README.md +285 -85
- data/changelog.d/README.md +2 -2
- data/docs/archive/FAMILIA_RELATIONSHIPS.md +22 -22
- data/docs/archive/FAMILIA_TECHNICAL.md +42 -42
- data/docs/archive/FAMILIA_UPDATE.md +3 -3
- data/docs/archive/README.md +3 -2
- data/docs/{guides/API-Reference.md → archive/api-reference.md} +87 -101
- data/docs/conf.py +29 -0
- data/docs/guides/{Field-System-Guide.md → core-field-system.md} +9 -9
- data/docs/guides/feature-encrypted-fields.md +785 -0
- data/docs/guides/{Expiration-Feature-Guide.md → feature-expiration.md} +11 -2
- data/docs/guides/feature-external-identifiers.md +637 -0
- data/docs/guides/feature-object-identifiers.md +435 -0
- data/docs/guides/{Quantization-Feature-Guide.md → feature-quantization.md} +94 -29
- data/docs/guides/feature-relationships-methods.md +684 -0
- data/docs/guides/feature-relationships.md +200 -0
- data/docs/guides/{Features-System-Developer-Guide.md → feature-system-devs.md} +4 -4
- data/docs/guides/{Feature-System-Guide.md → feature-system.md} +5 -5
- data/docs/guides/{Transient-Fields-Guide.md → feature-transient-fields.md} +2 -2
- data/docs/guides/{Implementation-Guide.md → implementation.md} +3 -3
- data/docs/guides/index.md +176 -0
- data/docs/guides/{Security-Model.md → security-model.md} +1 -1
- data/docs/migrating/v2.0.0-pre.md +1 -1
- data/docs/migrating/v2.0.0-pre11.md +2 -2
- data/docs/migrating/v2.0.0-pre12.md +2 -2
- data/docs/migrating/v2.0.0-pre5.md +33 -12
- data/docs/migrating/v2.0.0-pre6.md +2 -2
- data/docs/migrating/v2.0.0-pre7.md +8 -8
- data/docs/overview.md +624 -20
- data/docs/reference/api-technical.md +1365 -0
- data/examples/autoloader/mega_customer/features/deprecated_fields.rb +7 -0
- data/examples/autoloader/mega_customer/safe_dump_fields.rb +1 -1
- data/examples/autoloader/mega_customer.rb +3 -1
- data/examples/encrypted_fields.rb +378 -0
- data/examples/json_usage_patterns.rb +144 -0
- data/examples/relationships.rb +13 -13
- data/examples/safe_dump.rb +7 -7
- data/examples/single_connection_transaction_confusions.rb +379 -0
- data/lib/familia/base.rb +51 -10
- data/lib/familia/connection/handlers.rb +223 -0
- data/lib/familia/connection/individual_command_proxy.rb +64 -0
- data/lib/familia/connection/middleware.rb +75 -0
- data/lib/familia/connection/operation_core.rb +93 -0
- data/lib/familia/connection/operations.rb +277 -0
- data/lib/familia/connection/pipeline_core.rb +87 -0
- data/lib/familia/connection/transaction_core.rb +100 -0
- data/lib/familia/connection.rb +60 -186
- data/lib/familia/data_type/class_methods.rb +63 -0
- data/lib/familia/data_type/commands.rb +53 -51
- data/lib/familia/data_type/connection.rb +83 -0
- data/lib/familia/data_type/serialization.rb +108 -107
- data/lib/familia/data_type/settings.rb +96 -0
- data/lib/familia/data_type/types/counter.rb +1 -1
- data/lib/familia/data_type/types/hashkey.rb +15 -11
- data/lib/familia/data_type/types/{list.rb → listkey.rb} +13 -5
- data/lib/familia/data_type/types/lock.rb +3 -2
- data/lib/familia/data_type/types/sorted_set.rb +128 -14
- data/lib/familia/data_type/types/{string.rb → stringkey.rb} +7 -9
- data/lib/familia/data_type/types/unsorted_set.rb +20 -27
- data/lib/familia/data_type.rb +12 -171
- data/lib/familia/distinguisher.rb +85 -0
- data/lib/familia/encryption/encrypted_data.rb +15 -24
- data/lib/familia/encryption/manager.rb +6 -4
- data/lib/familia/encryption/providers/aes_gcm_provider.rb +1 -1
- data/lib/familia/encryption/providers/secure_xchacha20_poly1305_provider.rb +7 -9
- data/lib/familia/encryption/providers/xchacha20_poly1305_provider.rb +4 -5
- data/lib/familia/encryption/request_cache.rb +7 -7
- data/lib/familia/encryption.rb +2 -3
- data/lib/familia/errors.rb +9 -3
- data/lib/familia/features/autoloader.rb +30 -12
- data/lib/familia/features/encrypted_fields/concealed_string.rb +3 -4
- data/lib/familia/features/encrypted_fields/encrypted_field_type.rb +13 -14
- data/lib/familia/features/encrypted_fields.rb +71 -66
- data/lib/familia/features/expiration/extensions.rb +1 -1
- data/lib/familia/features/expiration.rb +31 -26
- data/lib/familia/features/external_identifier.rb +57 -19
- data/lib/familia/features/object_identifier.rb +134 -25
- data/lib/familia/features/quantization.rb +16 -21
- data/lib/familia/features/relationships/README.md +97 -0
- data/lib/familia/features/relationships/collection_operations.rb +104 -0
- data/lib/familia/features/relationships/indexing/multi_index_generators.rb +202 -0
- data/lib/familia/features/relationships/indexing/unique_index_generators.rb +306 -0
- data/lib/familia/features/relationships/indexing.rb +182 -256
- data/lib/familia/features/relationships/indexing_relationship.rb +35 -0
- data/lib/familia/features/relationships/participation/participant_methods.rb +164 -0
- data/lib/familia/features/relationships/participation/target_methods.rb +225 -0
- data/lib/familia/features/relationships/participation.rb +656 -0
- data/lib/familia/features/relationships/participation_relationship.rb +31 -0
- data/lib/familia/features/relationships/score_encoding.rb +20 -20
- data/lib/familia/features/relationships.rb +65 -266
- data/lib/familia/features/safe_dump.rb +127 -130
- data/lib/familia/features/transient_fields/redacted_string.rb +6 -6
- data/lib/familia/features/transient_fields/transient_field_type.rb +5 -5
- data/lib/familia/features/transient_fields.rb +10 -7
- data/lib/familia/features.rb +10 -14
- data/lib/familia/field_type.rb +6 -4
- data/lib/familia/horreum/connection.rb +297 -0
- data/lib/familia/horreum/{core/database_commands.rb → database_commands.rb} +27 -17
- data/lib/familia/horreum/{subclass/definition.rb → definition.rb} +139 -74
- data/lib/familia/horreum/{subclass/management.rb → management.rb} +73 -27
- data/lib/familia/horreum/{core/serialization.rb → persistence.rb} +108 -185
- data/lib/familia/horreum/{subclass/related_fields_management.rb → related_fields.rb} +104 -23
- data/lib/familia/horreum/serialization.rb +172 -0
- data/lib/familia/horreum/{shared/settings.rb → settings.rb} +2 -1
- data/lib/familia/horreum/{core/utils.rb → utils.rb} +2 -1
- data/lib/familia/horreum.rb +222 -119
- data/lib/familia/json_serializer.rb +0 -1
- data/lib/familia/logging.rb +11 -114
- data/lib/familia/refinements/dear_json.rb +122 -0
- data/lib/familia/refinements/logger_trace.rb +20 -17
- data/lib/familia/refinements/stylize_words.rb +65 -0
- data/lib/familia/refinements/time_literals.rb +60 -52
- data/lib/familia/refinements.rb +2 -1
- data/lib/familia/secure_identifier.rb +60 -28
- data/lib/familia/settings.rb +83 -7
- data/lib/familia/utils.rb +5 -87
- data/lib/familia/verifiable_identifier.rb +4 -4
- data/lib/familia/version.rb +1 -1
- data/lib/familia.rb +72 -14
- data/lib/middleware/database_middleware.rb +56 -14
- data/lib/{familia/multi_result.rb → multi_result.rb} +23 -16
- data/try/configuration/scenarios_try.rb +2 -2
- data/try/connection/fiber_context_preservation_try.rb +250 -0
- data/try/connection/handler_constraints_try.rb +59 -0
- data/try/connection/operation_mode_guards_try.rb +208 -0
- data/try/connection/pipeline_fallback_integration_try.rb +128 -0
- data/try/connection/responsibility_chain_tracking_try.rb +72 -0
- data/try/connection/transaction_fallback_integration_try.rb +288 -0
- data/try/connection/transaction_mode_permissive_try.rb +153 -0
- data/try/connection/transaction_mode_strict_try.rb +98 -0
- data/try/connection/transaction_mode_warn_try.rb +131 -0
- data/try/connection/transaction_modes_try.rb +249 -0
- data/try/core/autoloader_try.rb +120 -2
- data/try/core/connection_try.rb +10 -10
- data/try/core/conventional_inheritance_try.rb +130 -0
- data/try/core/create_method_try.rb +15 -23
- data/try/core/database_consistency_try.rb +11 -10
- data/try/core/errors_try.rb +11 -14
- data/try/core/familia_extended_try.rb +2 -2
- data/try/core/familia_members_methods_try.rb +76 -0
- data/try/core/familia_try.rb +1 -1
- data/try/core/isolated_dbclient_try.rb +165 -0
- data/try/core/middleware_try.rb +16 -16
- data/try/core/persistence_operations_try.rb +4 -4
- data/try/core/pools_try.rb +42 -26
- data/try/core/secure_identifier_try.rb +28 -24
- data/try/core/time_utils_try.rb +10 -10
- data/try/core/tools_try.rb +3 -3
- data/try/core/utils_try.rb +2 -2
- data/try/data_types/boolean_try.rb +4 -4
- data/try/data_types/datatype_base_try.rb +0 -2
- data/try/data_types/list_try.rb +10 -10
- data/try/data_types/sorted_set_try.rb +5 -5
- data/try/data_types/sorted_set_zadd_options_try.rb +625 -0
- data/try/data_types/string_try.rb +12 -12
- data/try/data_types/unsortedset_try.rb +33 -0
- data/try/debugging/cache_behavior_tracer.rb +7 -7
- data/try/debugging/debug_aad_process.rb +1 -1
- data/try/debugging/debug_concealed_internal.rb +1 -1
- data/try/debugging/debug_cross_context.rb +1 -1
- data/try/debugging/debug_fresh_cross_context.rb +1 -1
- data/try/debugging/encryption_method_tracer.rb +10 -10
- data/try/edge_cases/hash_symbolization_try.rb +1 -1
- data/try/edge_cases/ttl_side_effects_try.rb +1 -1
- data/try/encryption/config_persistence_try.rb +2 -2
- data/try/encryption/encryption_core_try.rb +19 -19
- data/try/encryption/instance_variable_scope_try.rb +1 -1
- data/try/encryption/module_loading_try.rb +2 -2
- data/try/encryption/providers/aes_gcm_provider_try.rb +1 -1
- data/try/encryption/providers/xchacha20_poly1305_provider_try.rb +1 -1
- data/try/encryption/secure_memory_handling_try.rb +1 -1
- data/try/features/encrypted_fields/concealed_string_core_try.rb +11 -7
- data/try/features/encrypted_fields/encrypted_fields_core_try.rb +1 -1
- data/try/features/encrypted_fields/encrypted_fields_integration_try.rb +3 -3
- data/try/features/encrypted_fields/encrypted_fields_no_cache_security_try.rb +10 -10
- data/try/features/encrypted_fields/encrypted_fields_security_try.rb +14 -14
- data/try/features/encrypted_fields/error_conditions_try.rb +7 -7
- data/try/features/encrypted_fields/fresh_key_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 +7 -7
- data/try/features/encrypted_fields/universal_serialization_safety_try.rb +13 -20
- data/try/features/external_identifier/external_identifier_try.rb +1 -1
- data/try/features/feature_dependencies_try.rb +3 -3
- data/try/features/field_groups_try.rb +244 -0
- data/try/features/object_identifier/object_identifier_integration_try.rb +28 -34
- data/try/features/object_identifier/object_identifier_try.rb +10 -0
- data/try/features/quantization/quantization_try.rb +1 -1
- data/try/features/relationships/indexing_commands_verification_try.rb +136 -0
- data/try/features/relationships/indexing_try.rb +443 -0
- data/try/features/relationships/participation_commands_verification_spec.rb +102 -0
- data/try/features/relationships/participation_commands_verification_try.rb +105 -0
- data/try/features/relationships/participation_performance_improvements_try.rb +124 -0
- data/try/features/relationships/participation_reverse_index_try.rb +196 -0
- data/try/features/relationships/relationships_api_changes_try.rb +72 -71
- data/try/features/relationships/relationships_edge_cases_try.rb +15 -18
- data/try/features/relationships/relationships_performance_minimal_try.rb +2 -2
- data/try/features/relationships/relationships_performance_simple_try.rb +8 -8
- data/try/features/relationships/relationships_performance_try.rb +20 -20
- data/try/features/relationships/relationships_try.rb +27 -38
- data/try/features/safe_dump/safe_dump_advanced_try.rb +2 -2
- data/try/features/transient_fields/refresh_reset_try.rb +3 -1
- data/try/features/transient_fields/simple_refresh_test.rb +1 -1
- data/try/helpers/test_cleanup.rb +86 -0
- data/try/helpers/test_helpers.rb +6 -7
- data/try/horreum/auto_indexing_on_save_try.rb +212 -0
- data/try/horreum/base_try.rb +3 -2
- data/try/horreum/commands_try.rb +3 -1
- data/try/horreum/defensive_initialization_try.rb +86 -0
- data/try/horreum/destroy_related_fields_cleanup_try.rb +332 -0
- data/try/horreum/initialization_try.rb +11 -7
- data/try/horreum/relations_try.rb +21 -13
- data/try/horreum/serialization_try.rb +12 -11
- data/try/horreum/settings_try.rb +2 -0
- data/try/integration/cross_component_try.rb +3 -3
- data/try/memory/memory_basic_test.rb +1 -1
- data/try/memory/memory_docker_ruby_dump.sh +2 -2
- data/try/models/customer_safe_dump_try.rb +1 -1
- data/try/models/customer_try.rb +13 -15
- data/try/models/datatype_base_try.rb +3 -3
- data/try/models/familia_object_try.rb +9 -8
- data/try/performance/benchmarks_try.rb +2 -2
- data/try/prototypes/atomic_saves_v1_context_proxy.rb +2 -2
- data/try/prototypes/atomic_saves_v3_connection_pool.rb +3 -3
- data/try/prototypes/atomic_saves_v4.rb +1 -1
- data/try/prototypes/lib/atomic_saves_v2_connection_switching_helpers.rb +4 -4
- data/try/prototypes/lib/atomic_saves_v3_connection_pool_helpers.rb +4 -4
- data/try/prototypes/pooling/lib/atomic_saves_v3_connection_pool_helpers.rb +4 -4
- data/try/prototypes/pooling/lib/connection_pool_metrics.rb +5 -5
- data/try/prototypes/pooling/lib/connection_pool_stress_test.rb +26 -26
- data/try/prototypes/pooling/lib/connection_pool_threading_models.rb +7 -7
- data/try/prototypes/pooling/lib/visualize_stress_results.rb +1 -1
- data/try/prototypes/pooling/pool_siege.rb +11 -11
- data/try/prototypes/pooling/run_stress_tests.rb +7 -7
- data/try/refinements/dear_json_array_methods_try.rb +53 -0
- data/try/refinements/dear_json_hash_methods_try.rb +54 -0
- data/try/refinements/logger_trace_methods_try.rb +44 -0
- data/try/refinements/time_literals_numeric_methods_try.rb +141 -0
- data/try/refinements/time_literals_string_methods_try.rb +80 -0
- data/try/valkey.conf +26 -0
- metadata +92 -52
- data/.rubocop_todo.yml +0 -208
- data/docs/connection_pooling.md +0 -192
- data/docs/guides/Connection-Pooling-Guide.md +0 -437
- data/docs/guides/Encrypted-Fields-Overview.md +0 -101
- data/docs/guides/Feature-System-Autoloading.md +0 -198
- data/docs/guides/Home.md +0 -116
- data/docs/guides/Relationships-Guide.md +0 -737
- data/docs/guides/relationships-methods.md +0 -266
- data/docs/reference/auditing_database_commands.rb +0 -228
- data/examples/permissions.rb +0 -240
- data/lib/familia/features/relationships/cascading.rb +0 -437
- data/lib/familia/features/relationships/membership.rb +0 -497
- data/lib/familia/features/relationships/permission_management.rb +0 -264
- data/lib/familia/features/relationships/querying.rb +0 -615
- data/lib/familia/features/relationships/redis_operations.rb +0 -274
- data/lib/familia/features/relationships/tracking.rb +0 -418
- data/lib/familia/horreum/core/connection.rb +0 -73
- data/lib/familia/horreum/core.rb +0 -21
- data/lib/familia/refinements/snake_case.rb +0 -40
- data/lib/familia/validation/command_recorder.rb +0 -336
- data/lib/familia/validation/expectations.rb +0 -519
- data/lib/familia/validation/validation_helpers.rb +0 -443
- data/lib/familia/validation/validator.rb +0 -412
- data/lib/familia/validation.rb +0 -140
- data/try/data_types/set_try.rb +0 -33
- data/try/features/relationships/categorical_permissions_try.rb +0 -515
- data/try/features/safe_dump/module_based_extensions_try.rb +0 -100
- data/try/features/safe_dump/safe_dump_autoloading_try.rb +0 -107
- data/try/validation/atomic_operations_try.rb.disabled +0 -320
- data/try/validation/command_validation_try.rb.disabled +0 -207
- data/try/validation/performance_validation_try.rb.disabled +0 -324
- data/try/validation/real_world_scenarios_try.rb.disabled +0 -390
@@ -0,0 +1,128 @@
|
|
1
|
+
#
|
2
|
+
# Tests pipeline fallback modes when connection handlers don't support pipelines.
|
3
|
+
# Validates that pipeline_mode configuration works correctly with cached connections
|
4
|
+
# and that the fallback behavior matches transaction fallback patterns.
|
5
|
+
#
|
6
|
+
|
7
|
+
require_relative '../helpers/test_helpers'
|
8
|
+
|
9
|
+
# Store original values
|
10
|
+
$original_pipeline_mode = Familia.pipeline_mode
|
11
|
+
$original_transaction_mode = Familia.transaction_mode
|
12
|
+
|
13
|
+
# Test model for pipeline fallback scenarios
|
14
|
+
class PipelineFallbackTest < Familia::Horreum
|
15
|
+
identifier_field :testid
|
16
|
+
field :testid
|
17
|
+
end
|
18
|
+
|
19
|
+
## Test 1: Strict mode raises error with cached connection
|
20
|
+
Familia.configure { |c| c.pipeline_mode = :strict }
|
21
|
+
|
22
|
+
# Cache connection at class level (uses DefaultConnectionHandler which doesn't support pipelines)
|
23
|
+
PipelineFallbackTest.instance_variable_set(:@dbclient, Familia.create_dbclient)
|
24
|
+
|
25
|
+
customer = PipelineFallbackTest.new(testid: 'strict_test')
|
26
|
+
customer.pipelined { |c| c.set('key', 'value') }
|
27
|
+
#=:> Familia::OperationModeError
|
28
|
+
#=~> /Cannot start pipeline with.*CachedConnectionHandler/
|
29
|
+
|
30
|
+
## Test 2: Warn mode falls back successfully with cached connection
|
31
|
+
Familia.configure { |c| c.pipeline_mode = :warn }
|
32
|
+
|
33
|
+
# Cache connection at class level
|
34
|
+
PipelineFallbackTest.instance_variable_set(:@dbclient, Familia.create_dbclient)
|
35
|
+
|
36
|
+
customer2 = PipelineFallbackTest.new(testid: 'warn_test')
|
37
|
+
$warn_result = customer2.pipelined do |conn|
|
38
|
+
conn.set(customer2.dbkey('field1'), 'value1')
|
39
|
+
conn.set(customer2.dbkey('field2'), 'value2')
|
40
|
+
conn.get(customer2.dbkey('field1'))
|
41
|
+
end
|
42
|
+
$warn_result.successful?
|
43
|
+
#=> true
|
44
|
+
|
45
|
+
## Test 2b: Warn mode result contains all command results
|
46
|
+
$warn_result.results.size
|
47
|
+
#=> 3
|
48
|
+
|
49
|
+
## Test 2c: Warn mode result contains correct value
|
50
|
+
$warn_result.results[2]
|
51
|
+
#=> 'value1'
|
52
|
+
|
53
|
+
## Test 3: Fresh connections still support real pipelines in strict mode
|
54
|
+
Familia.configure { |c| c.pipeline_mode = :strict }
|
55
|
+
|
56
|
+
# Clear cached class-level connection to force CreateConnectionHandler
|
57
|
+
PipelineFallbackTest.remove_instance_variable(:@dbclient) if PipelineFallbackTest.instance_variable_defined?(:@dbclient)
|
58
|
+
|
59
|
+
customer3 = PipelineFallbackTest.new(testid: 'fresh_test')
|
60
|
+
$fresh_result = customer3.pipelined do |conn|
|
61
|
+
conn.set('pipeline_key1', 'val1')
|
62
|
+
conn.set('pipeline_key2', 'val2')
|
63
|
+
conn.get('pipeline_key1')
|
64
|
+
end
|
65
|
+
$fresh_result.successful?
|
66
|
+
#=> true
|
67
|
+
|
68
|
+
## Test 3b: Real pipeline returns correct result count
|
69
|
+
$fresh_result.results.size
|
70
|
+
#=> 3
|
71
|
+
|
72
|
+
## Test 4: MultiResult format is correct for fallback
|
73
|
+
Familia.configure { |c| c.pipeline_mode = :permissive }
|
74
|
+
|
75
|
+
# Cache connection at class level
|
76
|
+
PipelineFallbackTest.instance_variable_set(:@dbclient, Familia.create_dbclient)
|
77
|
+
|
78
|
+
customer4 = PipelineFallbackTest.new(testid: 'multiresult_test')
|
79
|
+
$multi_result = customer4.pipelined do |conn|
|
80
|
+
conn.set(customer4.dbkey('test'), 'value')
|
81
|
+
conn.get(customer4.dbkey('test'))
|
82
|
+
end
|
83
|
+
$multi_result.class.name
|
84
|
+
#=> 'MultiResult'
|
85
|
+
|
86
|
+
## Test 4b: MultiResult successful? returns correct value
|
87
|
+
$multi_result.successful?
|
88
|
+
#=> true
|
89
|
+
|
90
|
+
## Test 4c: MultiResult results is an Array
|
91
|
+
$multi_result.results.class
|
92
|
+
#=> Array
|
93
|
+
|
94
|
+
## Test 5: Permissive mode silently falls back
|
95
|
+
Familia.configure { |c| c.pipeline_mode = :permissive }
|
96
|
+
|
97
|
+
# Cache connection at class level
|
98
|
+
PipelineFallbackTest.instance_variable_set(:@dbclient, Familia.create_dbclient)
|
99
|
+
|
100
|
+
customer5 = PipelineFallbackTest.new(testid: 'permissive_test')
|
101
|
+
$permissive_result = customer5.pipelined do |conn|
|
102
|
+
conn.set(customer5.dbkey('counter'), '0')
|
103
|
+
conn.incr(customer5.dbkey('counter'))
|
104
|
+
conn.get(customer5.dbkey('counter'))
|
105
|
+
end
|
106
|
+
$permissive_result.successful?
|
107
|
+
#=> true
|
108
|
+
|
109
|
+
## Test 5b: Permissive mode result contains correct values
|
110
|
+
$permissive_result.results
|
111
|
+
#=> ['OK', 1, '1']
|
112
|
+
|
113
|
+
## Test 6: Pipeline mode configuration validation
|
114
|
+
Familia.configure { |c| c.pipeline_mode = :invalid }
|
115
|
+
#=:> ArgumentError
|
116
|
+
#=~> /Pipeline mode must be :strict, :warn, or :permissive/
|
117
|
+
|
118
|
+
## Test 7: Default pipeline_mode is :warn
|
119
|
+
Familia.instance_variable_set(:@pipeline_mode, nil)
|
120
|
+
Familia.pipeline_mode
|
121
|
+
#=> :warn
|
122
|
+
|
123
|
+
## Cleanup: Restore original values
|
124
|
+
Familia.configure do |c|
|
125
|
+
c.pipeline_mode = $original_pipeline_mode
|
126
|
+
c.transaction_mode = $original_transaction_mode
|
127
|
+
end
|
128
|
+
PipelineFallbackTest.remove_instance_variable(:@dbclient) if PipelineFallbackTest.instance_variable_defined?(:@dbclient)
|
@@ -0,0 +1,72 @@
|
|
1
|
+
# ResponsibilityChain Handler Tracking Tryouts
|
2
|
+
#
|
3
|
+
# Tests that the ResponsibilityChain correctly tracks which handler provided
|
4
|
+
# each connection by setting Fiber[:familia_connection_handler_class]. This enables
|
5
|
+
# operation mode guards to enforce constraints based on connection source.
|
6
|
+
#
|
7
|
+
# The chain should:
|
8
|
+
# - Try handlers in order until one returns a connection
|
9
|
+
# - Set Fiber[:familia_connection_handler_class] to the successful handler's class
|
10
|
+
# - Return the connection from the successful handler
|
11
|
+
# - Return nil if no handler provides a connection
|
12
|
+
|
13
|
+
require_relative '../helpers/test_helpers'
|
14
|
+
|
15
|
+
# Setup - clear any existing fiber state
|
16
|
+
Fiber[:familia_connection_handler_class] = nil
|
17
|
+
Fiber[:familia_connection] = nil
|
18
|
+
|
19
|
+
## Chain tracks CreateConnectionHandler for normal connections
|
20
|
+
customer = Customer.new
|
21
|
+
customer.custid = 'tracking_test'
|
22
|
+
connection = customer.dbclient
|
23
|
+
Fiber[:familia_connection_handler_class]
|
24
|
+
#=> Familia::Connection::CreateConnectionHandler
|
25
|
+
|
26
|
+
## Chain tracks FiberConnectionHandler for middleware connections
|
27
|
+
begin
|
28
|
+
test_conn = Customer.create_dbclient
|
29
|
+
Fiber[:familia_connection] = [test_conn, Familia.middleware_version]
|
30
|
+
|
31
|
+
customer = Customer.new
|
32
|
+
customer.custid = 'fiber_test'
|
33
|
+
connection = customer.dbclient
|
34
|
+
Fiber[:familia_connection_handler_class]
|
35
|
+
ensure
|
36
|
+
Fiber[:familia_connection] = nil
|
37
|
+
Fiber[:familia_connection_handler_class] = nil
|
38
|
+
end
|
39
|
+
#=> Familia::Connection::FiberConnectionHandler
|
40
|
+
|
41
|
+
## Chain tracks ProviderConnectionHandler for connection providers
|
42
|
+
original_provider = Familia.connection_provider
|
43
|
+
begin
|
44
|
+
Familia.connection_provider = ->(uri) { Redis.new(url: uri) }
|
45
|
+
|
46
|
+
customer = Customer.new
|
47
|
+
customer.custid = 'provider_test'
|
48
|
+
connection = customer.dbclient
|
49
|
+
Fiber[:familia_connection_handler_class]
|
50
|
+
ensure
|
51
|
+
Familia.connection_provider = original_provider
|
52
|
+
Fiber[:familia_connection_handler_class] = nil
|
53
|
+
end
|
54
|
+
#=> Familia::Connection::ProviderConnectionHandler
|
55
|
+
|
56
|
+
## Chain returns nil when no handler provides connection
|
57
|
+
# Create a mock chain with handlers that return nil
|
58
|
+
chain = Familia::Connection::ResponsibilityChain.new
|
59
|
+
chain.add_handler(Familia::Connection::FiberTransactionHandler.new)
|
60
|
+
chain.add_handler(Familia::Connection::FiberConnectionHandler.new)
|
61
|
+
|
62
|
+
result = chain.handle('test_uri')
|
63
|
+
result.nil?
|
64
|
+
#=> true
|
65
|
+
|
66
|
+
## Chain sets handler class even when tracking connections
|
67
|
+
customer = Customer.new
|
68
|
+
customer.custid = 'final_test'
|
69
|
+
conn = customer.dbclient
|
70
|
+
handler_class = Fiber[:familia_connection_handler_class]
|
71
|
+
handler_class == Familia::Connection::CreateConnectionHandler && !conn.nil?
|
72
|
+
#=> true
|
@@ -0,0 +1,288 @@
|
|
1
|
+
# Transaction Fallback Integration Tryouts
|
2
|
+
#
|
3
|
+
# Tests real-world scenarios where transaction fallback is needed, particularly
|
4
|
+
# focusing on save operations, relationship updates, and other high-level
|
5
|
+
# operations that internally use transactions.
|
6
|
+
#
|
7
|
+
# These tests verify that the transaction mode configuration works seamlessly
|
8
|
+
# with Familia's existing features when connection handlers don't support
|
9
|
+
# transactions (e.g., cached connections, middleware connections).
|
10
|
+
|
11
|
+
require_relative '../helpers/test_helpers'
|
12
|
+
|
13
|
+
# Setup - store original values
|
14
|
+
$original_transaction_mode = Familia.transaction_mode
|
15
|
+
$original_provider = Familia.connection_provider
|
16
|
+
|
17
|
+
# Create test classes for integration testing
|
18
|
+
class IntegrationTestUser < Familia::Horreum
|
19
|
+
identifier_field :user_id
|
20
|
+
field :user_id
|
21
|
+
field :name
|
22
|
+
field :email
|
23
|
+
field :status
|
24
|
+
list :activity_log
|
25
|
+
set :tags
|
26
|
+
zset :scores
|
27
|
+
end
|
28
|
+
|
29
|
+
class IntegrationTestSession < Familia::Horreum
|
30
|
+
identifier_field :session_id
|
31
|
+
field :session_id
|
32
|
+
field :user_id
|
33
|
+
field :created_at
|
34
|
+
field :expires_at
|
35
|
+
end
|
36
|
+
|
37
|
+
## Save operation works with transaction fallback in warn mode
|
38
|
+
begin
|
39
|
+
Familia.configure { |config| config.transaction_mode = :warn }
|
40
|
+
|
41
|
+
# Force CachedConnectionHandler
|
42
|
+
IntegrationTestUser.instance_variable_set(:@dbclient, Familia.create_dbclient)
|
43
|
+
|
44
|
+
user = IntegrationTestUser.new(
|
45
|
+
user_id: 'save_test_001',
|
46
|
+
name: 'Test User',
|
47
|
+
email: 'test@example.com',
|
48
|
+
status: 'active'
|
49
|
+
)
|
50
|
+
|
51
|
+
# Save internally uses transactions for atomicity
|
52
|
+
result = user.save
|
53
|
+
|
54
|
+
# Should complete successfully using individual commands
|
55
|
+
result && user.exists?
|
56
|
+
ensure
|
57
|
+
IntegrationTestUser.remove_instance_variable(:@dbclient)
|
58
|
+
Familia.configure { |config| config.transaction_mode = :strict }
|
59
|
+
end
|
60
|
+
#=> true
|
61
|
+
|
62
|
+
## Save operation works with transaction fallback in permissive mode
|
63
|
+
begin
|
64
|
+
Familia.configure { |config| config.transaction_mode = :permissive }
|
65
|
+
|
66
|
+
# Force CachedConnectionHandler
|
67
|
+
IntegrationTestUser.instance_variable_set(:@dbclient, Familia.create_dbclient)
|
68
|
+
|
69
|
+
user = IntegrationTestUser.new(
|
70
|
+
user_id: 'save_test_002',
|
71
|
+
name: 'Permissive User',
|
72
|
+
email: 'permissive@example.com'
|
73
|
+
)
|
74
|
+
|
75
|
+
# Should save silently using individual commands
|
76
|
+
result = user.save
|
77
|
+
|
78
|
+
result && user.exists?
|
79
|
+
ensure
|
80
|
+
IntegrationTestUser.remove_instance_variable(:@dbclient)
|
81
|
+
Familia.configure { |config| config.transaction_mode = :strict }
|
82
|
+
end
|
83
|
+
#=> true
|
84
|
+
|
85
|
+
## Multiple field updates work with fallback
|
86
|
+
begin
|
87
|
+
Familia.configure { |config| config.transaction_mode = :permissive }
|
88
|
+
IntegrationTestUser.instance_variable_set(:@dbclient, Familia.create_dbclient)
|
89
|
+
|
90
|
+
user = IntegrationTestUser.new(user_id: 'update_test_001')
|
91
|
+
user.save
|
92
|
+
|
93
|
+
# Update multiple fields - should use individual commands
|
94
|
+
user.name = 'Updated Name'
|
95
|
+
user.email = 'updated@example.com'
|
96
|
+
user.status = 'updated'
|
97
|
+
|
98
|
+
result = user.save
|
99
|
+
|
100
|
+
# Verify all fields were updated
|
101
|
+
reloaded_user = IntegrationTestUser.load('update_test_001')
|
102
|
+
result &&
|
103
|
+
reloaded_user.name == 'Updated Name' &&
|
104
|
+
reloaded_user.email == 'updated@example.com' &&
|
105
|
+
reloaded_user.status == 'updated'
|
106
|
+
ensure
|
107
|
+
IntegrationTestUser.remove_instance_variable(:@dbclient)
|
108
|
+
Familia.configure { |config| config.transaction_mode = :strict }
|
109
|
+
end
|
110
|
+
#=> true
|
111
|
+
|
112
|
+
## Data type operations work with transaction fallback
|
113
|
+
|
114
|
+
Familia.configure { |config| config.transaction_mode = :permissive }
|
115
|
+
IntegrationTestUser.instance_variable_set(:@dbclient, Familia.create_dbclient)
|
116
|
+
|
117
|
+
user = IntegrationTestUser.new(user_id: 'datatype_test_001')
|
118
|
+
user.transaction { |conn| conn.set('test', 'value') }
|
119
|
+
|
120
|
+
# Test list operations
|
121
|
+
user.activity_log.add('user_created')
|
122
|
+
user.activity_log.add('profile_updated')
|
123
|
+
|
124
|
+
# Test set operations
|
125
|
+
user.tags.add('premium')
|
126
|
+
user.tags.add('verified')
|
127
|
+
|
128
|
+
# Test sorted set operations
|
129
|
+
user.scores.add('game_score', 100)
|
130
|
+
user.scores.add('quiz_score', 85)
|
131
|
+
|
132
|
+
# Verify all operations worked
|
133
|
+
results = [
|
134
|
+
user.activity_log.size,
|
135
|
+
user.tags.include?('premium'),
|
136
|
+
user.scores.score('game_score')
|
137
|
+
]
|
138
|
+
|
139
|
+
user.destroy!
|
140
|
+
|
141
|
+
# Clean up test data
|
142
|
+
IntegrationTestUser.remove_instance_variable(:@dbclient)
|
143
|
+
Familia.configure { |config| config.transaction_mode = :strict }
|
144
|
+
|
145
|
+
results
|
146
|
+
#=> [2, true, 100.0]
|
147
|
+
|
148
|
+
## Connection provider integration with transaction modes
|
149
|
+
begin
|
150
|
+
Familia.configure { |config| config.transaction_mode = :warn }
|
151
|
+
|
152
|
+
# Set up connection provider that doesn't support transactions
|
153
|
+
test_connections = {}
|
154
|
+
Familia.connection_provider = lambda do |uri|
|
155
|
+
test_connections[uri] ||= Familia.create_dbclient(uri)
|
156
|
+
end
|
157
|
+
|
158
|
+
user = IntegrationTestUser.new(
|
159
|
+
user_id: 'provider_test_001',
|
160
|
+
name: 'Provider Test User'
|
161
|
+
)
|
162
|
+
|
163
|
+
# Should work with connection provider
|
164
|
+
result = user.save
|
165
|
+
|
166
|
+
result && user.exists?
|
167
|
+
ensure
|
168
|
+
Familia.connection_provider = $original_provider
|
169
|
+
Familia.configure { |config| config.transaction_mode = :strict }
|
170
|
+
end
|
171
|
+
#=> true
|
172
|
+
|
173
|
+
## Mixed connection handlers in same operation
|
174
|
+
begin
|
175
|
+
Familia.configure { |config| config.transaction_mode = :permissive }
|
176
|
+
|
177
|
+
# User class with cached connection (fallback mode)
|
178
|
+
IntegrationTestUser.instance_variable_set(:@dbclient, Familia.create_dbclient)
|
179
|
+
|
180
|
+
# Session class without cached connection (normal mode)
|
181
|
+
user = IntegrationTestUser.new(user_id: 'mixed_test_001')
|
182
|
+
session = IntegrationTestSession.new(
|
183
|
+
session_id: 'sess_mixed_001',
|
184
|
+
user_id: 'mixed_test_001',
|
185
|
+
created_at: Time.now.to_i
|
186
|
+
)
|
187
|
+
|
188
|
+
# Both should save successfully despite different connection handlers
|
189
|
+
user_saved = user.save
|
190
|
+
session_saved = session.save
|
191
|
+
|
192
|
+
user_saved && session_saved && user.exists? && session.exists?
|
193
|
+
ensure
|
194
|
+
IntegrationTestUser.remove_instance_variable(:@dbclient)
|
195
|
+
Familia.configure { |config| config.transaction_mode = :strict }
|
196
|
+
end
|
197
|
+
#=> true
|
198
|
+
|
199
|
+
## Transaction mode changes during runtime
|
200
|
+
begin
|
201
|
+
IntegrationTestUser.instance_variable_set(:@dbclient, Familia.create_dbclient)
|
202
|
+
|
203
|
+
user = IntegrationTestUser.new(user_id: 'runtime_test_001')
|
204
|
+
|
205
|
+
# Start in strict mode - should fail
|
206
|
+
Familia.configure { |config| config.transaction_mode = :strict }
|
207
|
+
strict_failed = false
|
208
|
+
begin
|
209
|
+
user.transaction { |conn| conn.set('test_key', 'test_value') }
|
210
|
+
rescue Familia::OperationModeError
|
211
|
+
strict_failed = true
|
212
|
+
end
|
213
|
+
|
214
|
+
# Switch to permissive mode - should work
|
215
|
+
Familia.configure { |config| config.transaction_mode = :permissive }
|
216
|
+
result = user.transaction { |conn| conn.set('test_key', 'test_value') }
|
217
|
+
permissive_worked = result.successful?
|
218
|
+
|
219
|
+
strict_failed && permissive_worked
|
220
|
+
ensure
|
221
|
+
IntegrationTestUser.remove_instance_variable(:@dbclient)
|
222
|
+
Familia.configure { |config| config.transaction_mode = :strict }
|
223
|
+
end
|
224
|
+
#=> true
|
225
|
+
|
226
|
+
## Large batch operations with transaction fallback
|
227
|
+
begin
|
228
|
+
Familia.configure { |config| config.transaction_mode = :permissive }
|
229
|
+
IntegrationTestUser.instance_variable_set(:@dbclient, Familia.create_dbclient)
|
230
|
+
|
231
|
+
user = IntegrationTestUser.new(user_id: 'batch_test_001')
|
232
|
+
|
233
|
+
# Simulate a large batch operation that would normally use transactions
|
234
|
+
result = user.transaction do |conn|
|
235
|
+
# Add multiple activity log entries
|
236
|
+
(1..10).each do |i|
|
237
|
+
conn.rpush(user.activity_log.dbkey, "activity_#{i}")
|
238
|
+
end
|
239
|
+
|
240
|
+
# Add multiple tags
|
241
|
+
%w[tag1 tag2 tag3 tag4 tag5].each do |tag|
|
242
|
+
conn.sadd(user.tags.dbkey, tag)
|
243
|
+
end
|
244
|
+
|
245
|
+
# Add user fields
|
246
|
+
conn.hset(user.dbkey, 'batch_processed', 'true')
|
247
|
+
end
|
248
|
+
|
249
|
+
# Should complete successfully and return MultiResult
|
250
|
+
result.is_a?(MultiResult) && result.successful?
|
251
|
+
ensure
|
252
|
+
IntegrationTestUser.remove_instance_variable(:@dbclient)
|
253
|
+
Familia.configure { |config| config.transaction_mode = :strict }
|
254
|
+
end
|
255
|
+
#=> true
|
256
|
+
|
257
|
+
## Error handling in individual command mode
|
258
|
+
begin
|
259
|
+
Familia.configure { |config| config.transaction_mode = :permissive }
|
260
|
+
IntegrationTestUser.instance_variable_set(:@dbclient, Familia.create_dbclient)
|
261
|
+
|
262
|
+
user = IntegrationTestUser.new(user_id: 'error_test_001')
|
263
|
+
|
264
|
+
# Execute commands where some might fail
|
265
|
+
result = user.transaction do |conn|
|
266
|
+
conn.hset(user.dbkey, 'field1', 'value1') # Should succeed
|
267
|
+
|
268
|
+
# This might fail but shouldn't stop other commands
|
269
|
+
begin
|
270
|
+
conn.incr('non_numeric_key') # Might fail if key exists as string
|
271
|
+
rescue => e
|
272
|
+
# Individual commands can handle their own errors
|
273
|
+
end
|
274
|
+
|
275
|
+
conn.hset(user.dbkey, 'field2', 'value2') # Should succeed
|
276
|
+
end
|
277
|
+
|
278
|
+
# Should return MultiResult even with mixed success/failure
|
279
|
+
result.is_a?(MultiResult)
|
280
|
+
ensure
|
281
|
+
IntegrationTestUser.remove_instance_variable(:@dbclient)
|
282
|
+
Familia.configure { |config| config.transaction_mode = :strict }
|
283
|
+
end
|
284
|
+
#=> true
|
285
|
+
|
286
|
+
# Cleanup - restore original settings
|
287
|
+
Familia.configure { |config| config.transaction_mode = $original_transaction_mode }
|
288
|
+
Familia.connection_provider = $original_provider
|
@@ -0,0 +1,153 @@
|
|
1
|
+
# Transaction Mode: Permissive Tryouts
|
2
|
+
#
|
3
|
+
# Tests permissive transaction mode behavior where operations silently
|
4
|
+
# execute commands individually when transactions are unavailable.
|
5
|
+
#
|
6
|
+
# Permissive mode: Silently uses IndividualCommandProxy for fallback
|
7
|
+
|
8
|
+
require_relative '../helpers/test_helpers'
|
9
|
+
|
10
|
+
# Test class for permissive mode testing
|
11
|
+
class PermissiveModeTestCustomer < Familia::Horreum
|
12
|
+
identifier_field :custid
|
13
|
+
field :custid
|
14
|
+
field :name
|
15
|
+
field :status
|
16
|
+
end
|
17
|
+
|
18
|
+
## Permissive mode can be configured
|
19
|
+
Familia.configure { |config| config.transaction_mode = :permissive }
|
20
|
+
Familia.transaction_mode
|
21
|
+
#=> :permissive
|
22
|
+
|
23
|
+
## Permissive mode silently executes individual commands with CachedConnectionHandler
|
24
|
+
begin
|
25
|
+
# Force CachedConnectionHandler
|
26
|
+
PermissiveModeTestCustomer.instance_variable_set(:@dbclient, Familia.create_dbclient)
|
27
|
+
|
28
|
+
customer = PermissiveModeTestCustomer.new(custid: 'permissive_test')
|
29
|
+
result = customer.transaction do |conn|
|
30
|
+
# Should be IndividualCommandProxy
|
31
|
+
conn.class == Familia::Connection::IndividualCommandProxy &&
|
32
|
+
conn.hset(customer.dbkey, 'name', 'Permissive Mode Works') &&
|
33
|
+
conn.hget(customer.dbkey, 'name')
|
34
|
+
end
|
35
|
+
|
36
|
+
result.is_a?(MultiResult) && result.results.last == 'Permissive Mode Works'
|
37
|
+
ensure
|
38
|
+
PermissiveModeTestCustomer.remove_instance_variable(:@dbclient)
|
39
|
+
end
|
40
|
+
#=> true
|
41
|
+
|
42
|
+
## Permissive mode works silently with FiberConnectionHandler
|
43
|
+
begin
|
44
|
+
# Simulate middleware connection
|
45
|
+
Fiber[:familia_connection] = [Customer.create_dbclient, Familia.middleware_version]
|
46
|
+
Fiber[:familia_connection_handler_class] = Familia::Connection::FiberConnectionHandler
|
47
|
+
customer = PermissiveModeTestCustomer.new(custid: 'fiber_permissive_test')
|
48
|
+
|
49
|
+
result = customer.transaction do |conn|
|
50
|
+
conn.class == Familia::Connection::IndividualCommandProxy &&
|
51
|
+
conn.hset(customer.dbkey, 'source', 'fiber_permissive') &&
|
52
|
+
conn.hget(customer.dbkey, 'source')
|
53
|
+
end
|
54
|
+
|
55
|
+
result.is_a?(MultiResult) && result.results.last == 'fiber_permissive'
|
56
|
+
ensure
|
57
|
+
Fiber[:familia_connection] = nil
|
58
|
+
Fiber[:familia_connection_handler_class] = nil
|
59
|
+
end
|
60
|
+
#=> true
|
61
|
+
|
62
|
+
## Permissive mode still uses normal transactions when available
|
63
|
+
begin
|
64
|
+
customer = PermissiveModeTestCustomer.new(custid: 'normal_permissive_test')
|
65
|
+
result = customer.transaction do |conn|
|
66
|
+
# Should be Redis::MultiConnection for normal transactions
|
67
|
+
conn.class == Redis::MultiConnection &&
|
68
|
+
conn.hset(customer.dbkey, 'type', 'normal in permissive mode') &&
|
69
|
+
conn.hget(customer.dbkey, 'type')
|
70
|
+
end
|
71
|
+
result.is_a?(MultiResult) && result.results.last == 'normal in permissive mode'
|
72
|
+
end
|
73
|
+
#=> true
|
74
|
+
|
75
|
+
## Permissive mode handles complex operations silently
|
76
|
+
begin
|
77
|
+
PermissiveModeTestCustomer.instance_variable_set(:@dbclient, Familia.create_dbclient)
|
78
|
+
|
79
|
+
customer = PermissiveModeTestCustomer.new(custid: 'complex_permissive_test')
|
80
|
+
result = customer.transaction do |conn|
|
81
|
+
# Multiple operations that would normally be atomic
|
82
|
+
conn.hset(customer.dbkey, 'status', 'processing')
|
83
|
+
conn.hset(customer.dbkey, 'updated_at', Time.now.to_i)
|
84
|
+
conn.hset(customer.dbkey, 'version', '1.0')
|
85
|
+
conn.hget(customer.dbkey, 'status')
|
86
|
+
end
|
87
|
+
|
88
|
+
result.is_a?(MultiResult) && result.results.last == 'processing'
|
89
|
+
ensure
|
90
|
+
PermissiveModeTestCustomer.remove_instance_variable(:@dbclient)
|
91
|
+
end
|
92
|
+
#=> true
|
93
|
+
|
94
|
+
## Permissive mode works with nested calls
|
95
|
+
begin
|
96
|
+
PermissiveModeTestCustomer.instance_variable_set(:@dbclient, Familia.create_dbclient)
|
97
|
+
|
98
|
+
customer = PermissiveModeTestCustomer.new(custid: 'nested_permissive_test')
|
99
|
+
|
100
|
+
outer_result = customer.transaction do |outer_conn|
|
101
|
+
outer_conn.hset(customer.dbkey, 'outer', 'value')
|
102
|
+
|
103
|
+
inner_result = customer.transaction do |inner_conn|
|
104
|
+
inner_conn.hset(customer.dbkey, 'inner', 'nested_value')
|
105
|
+
end
|
106
|
+
|
107
|
+
# Both should return MultiResult
|
108
|
+
inner_result.is_a?(MultiResult)
|
109
|
+
end
|
110
|
+
|
111
|
+
outer_result.is_a?(MultiResult)
|
112
|
+
ensure
|
113
|
+
PermissiveModeTestCustomer.remove_instance_variable(:@dbclient)
|
114
|
+
end
|
115
|
+
#=> true
|
116
|
+
|
117
|
+
## Batch operations work silently in permissive mode
|
118
|
+
begin
|
119
|
+
PermissiveModeTestCustomer.instance_variable_set(:@dbclient, Familia.create_dbclient)
|
120
|
+
|
121
|
+
customer = PermissiveModeTestCustomer.new(custid: 'batch_permissive_test')
|
122
|
+
|
123
|
+
# Large batch that would normally be atomic
|
124
|
+
result = customer.transaction do |conn|
|
125
|
+
10.times do |i|
|
126
|
+
conn.hset(customer.dbkey, "field_#{i}", "value_#{i}")
|
127
|
+
end
|
128
|
+
conn.hget(customer.dbkey, 'field_9')
|
129
|
+
end
|
130
|
+
|
131
|
+
result.is_a?(MultiResult) && result.results.last == 'value_9'
|
132
|
+
ensure
|
133
|
+
PermissiveModeTestCustomer.remove_instance_variable(:@dbclient)
|
134
|
+
end
|
135
|
+
#=> true
|
136
|
+
|
137
|
+
## Save operations work in permissive mode
|
138
|
+
begin
|
139
|
+
PermissiveModeTestCustomer.instance_variable_set(:@dbclient, Familia.create_dbclient)
|
140
|
+
|
141
|
+
customer = PermissiveModeTestCustomer.new(
|
142
|
+
custid: 'save_permissive_test',
|
143
|
+
name: 'Permissive Save User',
|
144
|
+
status: 'active'
|
145
|
+
)
|
146
|
+
|
147
|
+
# Should save successfully using individual commands
|
148
|
+
save_result = customer.save
|
149
|
+
save_result && customer.exists?
|
150
|
+
ensure
|
151
|
+
PermissiveModeTestCustomer.remove_instance_variable(:@dbclient)
|
152
|
+
end
|
153
|
+
#=> true
|