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.
Files changed (288) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/ci.yml +2 -2
  3. data/.github/workflows/code-quality.yml +138 -0
  4. data/.github/workflows/code-smells.yml +85 -0
  5. data/.github/workflows/docs.yml +31 -8
  6. data/.gitignore +3 -1
  7. data/.pre-commit-config.yaml +7 -1
  8. data/.reek.yml +98 -0
  9. data/.rubocop.yml +54 -10
  10. data/.talismanrc +9 -0
  11. data/.yardopts +18 -13
  12. data/CHANGELOG.rst +86 -4
  13. data/CLAUDE.md +39 -1
  14. data/Gemfile +6 -5
  15. data/Gemfile.lock +99 -23
  16. data/LICENSE.txt +1 -1
  17. data/README.md +285 -85
  18. data/changelog.d/README.md +2 -2
  19. data/docs/archive/FAMILIA_RELATIONSHIPS.md +22 -22
  20. data/docs/archive/FAMILIA_TECHNICAL.md +42 -42
  21. data/docs/archive/FAMILIA_UPDATE.md +3 -3
  22. data/docs/archive/README.md +3 -2
  23. data/docs/{guides/API-Reference.md → archive/api-reference.md} +87 -101
  24. data/docs/conf.py +29 -0
  25. data/docs/guides/{Field-System-Guide.md → core-field-system.md} +9 -9
  26. data/docs/guides/feature-encrypted-fields.md +785 -0
  27. data/docs/guides/{Expiration-Feature-Guide.md → feature-expiration.md} +11 -2
  28. data/docs/guides/feature-external-identifiers.md +637 -0
  29. data/docs/guides/feature-object-identifiers.md +435 -0
  30. data/docs/guides/{Quantization-Feature-Guide.md → feature-quantization.md} +94 -29
  31. data/docs/guides/feature-relationships-methods.md +684 -0
  32. data/docs/guides/feature-relationships.md +200 -0
  33. data/docs/guides/{Features-System-Developer-Guide.md → feature-system-devs.md} +4 -4
  34. data/docs/guides/{Feature-System-Guide.md → feature-system.md} +5 -5
  35. data/docs/guides/{Transient-Fields-Guide.md → feature-transient-fields.md} +2 -2
  36. data/docs/guides/{Implementation-Guide.md → implementation.md} +3 -3
  37. data/docs/guides/index.md +176 -0
  38. data/docs/guides/{Security-Model.md → security-model.md} +1 -1
  39. data/docs/migrating/v2.0.0-pre.md +1 -1
  40. data/docs/migrating/v2.0.0-pre11.md +2 -2
  41. data/docs/migrating/v2.0.0-pre12.md +2 -2
  42. data/docs/migrating/v2.0.0-pre5.md +33 -12
  43. data/docs/migrating/v2.0.0-pre6.md +2 -2
  44. data/docs/migrating/v2.0.0-pre7.md +8 -8
  45. data/docs/overview.md +624 -20
  46. data/docs/reference/api-technical.md +1365 -0
  47. data/examples/autoloader/mega_customer/features/deprecated_fields.rb +7 -0
  48. data/examples/autoloader/mega_customer/safe_dump_fields.rb +1 -1
  49. data/examples/autoloader/mega_customer.rb +3 -1
  50. data/examples/encrypted_fields.rb +378 -0
  51. data/examples/json_usage_patterns.rb +144 -0
  52. data/examples/relationships.rb +13 -13
  53. data/examples/safe_dump.rb +7 -7
  54. data/examples/single_connection_transaction_confusions.rb +379 -0
  55. data/lib/familia/base.rb +51 -10
  56. data/lib/familia/connection/handlers.rb +223 -0
  57. data/lib/familia/connection/individual_command_proxy.rb +64 -0
  58. data/lib/familia/connection/middleware.rb +75 -0
  59. data/lib/familia/connection/operation_core.rb +93 -0
  60. data/lib/familia/connection/operations.rb +277 -0
  61. data/lib/familia/connection/pipeline_core.rb +87 -0
  62. data/lib/familia/connection/transaction_core.rb +100 -0
  63. data/lib/familia/connection.rb +60 -186
  64. data/lib/familia/data_type/class_methods.rb +63 -0
  65. data/lib/familia/data_type/commands.rb +53 -51
  66. data/lib/familia/data_type/connection.rb +83 -0
  67. data/lib/familia/data_type/serialization.rb +108 -107
  68. data/lib/familia/data_type/settings.rb +96 -0
  69. data/lib/familia/data_type/types/counter.rb +1 -1
  70. data/lib/familia/data_type/types/hashkey.rb +15 -11
  71. data/lib/familia/data_type/types/{list.rb → listkey.rb} +13 -5
  72. data/lib/familia/data_type/types/lock.rb +3 -2
  73. data/lib/familia/data_type/types/sorted_set.rb +128 -14
  74. data/lib/familia/data_type/types/{string.rb → stringkey.rb} +7 -9
  75. data/lib/familia/data_type/types/unsorted_set.rb +20 -27
  76. data/lib/familia/data_type.rb +12 -171
  77. data/lib/familia/distinguisher.rb +85 -0
  78. data/lib/familia/encryption/encrypted_data.rb +15 -24
  79. data/lib/familia/encryption/manager.rb +6 -4
  80. data/lib/familia/encryption/providers/aes_gcm_provider.rb +1 -1
  81. data/lib/familia/encryption/providers/secure_xchacha20_poly1305_provider.rb +7 -9
  82. data/lib/familia/encryption/providers/xchacha20_poly1305_provider.rb +4 -5
  83. data/lib/familia/encryption/request_cache.rb +7 -7
  84. data/lib/familia/encryption.rb +2 -3
  85. data/lib/familia/errors.rb +9 -3
  86. data/lib/familia/features/autoloader.rb +30 -12
  87. data/lib/familia/features/encrypted_fields/concealed_string.rb +3 -4
  88. data/lib/familia/features/encrypted_fields/encrypted_field_type.rb +13 -14
  89. data/lib/familia/features/encrypted_fields.rb +71 -66
  90. data/lib/familia/features/expiration/extensions.rb +1 -1
  91. data/lib/familia/features/expiration.rb +31 -26
  92. data/lib/familia/features/external_identifier.rb +57 -19
  93. data/lib/familia/features/object_identifier.rb +134 -25
  94. data/lib/familia/features/quantization.rb +16 -21
  95. data/lib/familia/features/relationships/README.md +97 -0
  96. data/lib/familia/features/relationships/collection_operations.rb +104 -0
  97. data/lib/familia/features/relationships/indexing/multi_index_generators.rb +202 -0
  98. data/lib/familia/features/relationships/indexing/unique_index_generators.rb +306 -0
  99. data/lib/familia/features/relationships/indexing.rb +182 -256
  100. data/lib/familia/features/relationships/indexing_relationship.rb +35 -0
  101. data/lib/familia/features/relationships/participation/participant_methods.rb +164 -0
  102. data/lib/familia/features/relationships/participation/target_methods.rb +225 -0
  103. data/lib/familia/features/relationships/participation.rb +656 -0
  104. data/lib/familia/features/relationships/participation_relationship.rb +31 -0
  105. data/lib/familia/features/relationships/score_encoding.rb +20 -20
  106. data/lib/familia/features/relationships.rb +65 -266
  107. data/lib/familia/features/safe_dump.rb +127 -130
  108. data/lib/familia/features/transient_fields/redacted_string.rb +6 -6
  109. data/lib/familia/features/transient_fields/transient_field_type.rb +5 -5
  110. data/lib/familia/features/transient_fields.rb +10 -7
  111. data/lib/familia/features.rb +10 -14
  112. data/lib/familia/field_type.rb +6 -4
  113. data/lib/familia/horreum/connection.rb +297 -0
  114. data/lib/familia/horreum/{core/database_commands.rb → database_commands.rb} +27 -17
  115. data/lib/familia/horreum/{subclass/definition.rb → definition.rb} +139 -74
  116. data/lib/familia/horreum/{subclass/management.rb → management.rb} +73 -27
  117. data/lib/familia/horreum/{core/serialization.rb → persistence.rb} +108 -185
  118. data/lib/familia/horreum/{subclass/related_fields_management.rb → related_fields.rb} +104 -23
  119. data/lib/familia/horreum/serialization.rb +172 -0
  120. data/lib/familia/horreum/{shared/settings.rb → settings.rb} +2 -1
  121. data/lib/familia/horreum/{core/utils.rb → utils.rb} +2 -1
  122. data/lib/familia/horreum.rb +222 -119
  123. data/lib/familia/json_serializer.rb +0 -1
  124. data/lib/familia/logging.rb +11 -114
  125. data/lib/familia/refinements/dear_json.rb +122 -0
  126. data/lib/familia/refinements/logger_trace.rb +20 -17
  127. data/lib/familia/refinements/stylize_words.rb +65 -0
  128. data/lib/familia/refinements/time_literals.rb +60 -52
  129. data/lib/familia/refinements.rb +2 -1
  130. data/lib/familia/secure_identifier.rb +60 -28
  131. data/lib/familia/settings.rb +83 -7
  132. data/lib/familia/utils.rb +5 -87
  133. data/lib/familia/verifiable_identifier.rb +4 -4
  134. data/lib/familia/version.rb +1 -1
  135. data/lib/familia.rb +72 -14
  136. data/lib/middleware/database_middleware.rb +56 -14
  137. data/lib/{familia/multi_result.rb → multi_result.rb} +23 -16
  138. data/try/configuration/scenarios_try.rb +2 -2
  139. data/try/connection/fiber_context_preservation_try.rb +250 -0
  140. data/try/connection/handler_constraints_try.rb +59 -0
  141. data/try/connection/operation_mode_guards_try.rb +208 -0
  142. data/try/connection/pipeline_fallback_integration_try.rb +128 -0
  143. data/try/connection/responsibility_chain_tracking_try.rb +72 -0
  144. data/try/connection/transaction_fallback_integration_try.rb +288 -0
  145. data/try/connection/transaction_mode_permissive_try.rb +153 -0
  146. data/try/connection/transaction_mode_strict_try.rb +98 -0
  147. data/try/connection/transaction_mode_warn_try.rb +131 -0
  148. data/try/connection/transaction_modes_try.rb +249 -0
  149. data/try/core/autoloader_try.rb +120 -2
  150. data/try/core/connection_try.rb +10 -10
  151. data/try/core/conventional_inheritance_try.rb +130 -0
  152. data/try/core/create_method_try.rb +15 -23
  153. data/try/core/database_consistency_try.rb +11 -10
  154. data/try/core/errors_try.rb +11 -14
  155. data/try/core/familia_extended_try.rb +2 -2
  156. data/try/core/familia_members_methods_try.rb +76 -0
  157. data/try/core/familia_try.rb +1 -1
  158. data/try/core/isolated_dbclient_try.rb +165 -0
  159. data/try/core/middleware_try.rb +16 -16
  160. data/try/core/persistence_operations_try.rb +4 -4
  161. data/try/core/pools_try.rb +42 -26
  162. data/try/core/secure_identifier_try.rb +28 -24
  163. data/try/core/time_utils_try.rb +10 -10
  164. data/try/core/tools_try.rb +3 -3
  165. data/try/core/utils_try.rb +2 -2
  166. data/try/data_types/boolean_try.rb +4 -4
  167. data/try/data_types/datatype_base_try.rb +0 -2
  168. data/try/data_types/list_try.rb +10 -10
  169. data/try/data_types/sorted_set_try.rb +5 -5
  170. data/try/data_types/sorted_set_zadd_options_try.rb +625 -0
  171. data/try/data_types/string_try.rb +12 -12
  172. data/try/data_types/unsortedset_try.rb +33 -0
  173. data/try/debugging/cache_behavior_tracer.rb +7 -7
  174. data/try/debugging/debug_aad_process.rb +1 -1
  175. data/try/debugging/debug_concealed_internal.rb +1 -1
  176. data/try/debugging/debug_cross_context.rb +1 -1
  177. data/try/debugging/debug_fresh_cross_context.rb +1 -1
  178. data/try/debugging/encryption_method_tracer.rb +10 -10
  179. data/try/edge_cases/hash_symbolization_try.rb +1 -1
  180. data/try/edge_cases/ttl_side_effects_try.rb +1 -1
  181. data/try/encryption/config_persistence_try.rb +2 -2
  182. data/try/encryption/encryption_core_try.rb +19 -19
  183. data/try/encryption/instance_variable_scope_try.rb +1 -1
  184. data/try/encryption/module_loading_try.rb +2 -2
  185. data/try/encryption/providers/aes_gcm_provider_try.rb +1 -1
  186. data/try/encryption/providers/xchacha20_poly1305_provider_try.rb +1 -1
  187. data/try/encryption/secure_memory_handling_try.rb +1 -1
  188. data/try/features/encrypted_fields/concealed_string_core_try.rb +11 -7
  189. data/try/features/encrypted_fields/encrypted_fields_core_try.rb +1 -1
  190. data/try/features/encrypted_fields/encrypted_fields_integration_try.rb +3 -3
  191. data/try/features/encrypted_fields/encrypted_fields_no_cache_security_try.rb +10 -10
  192. data/try/features/encrypted_fields/encrypted_fields_security_try.rb +14 -14
  193. data/try/features/encrypted_fields/error_conditions_try.rb +7 -7
  194. data/try/features/encrypted_fields/fresh_key_try.rb +1 -1
  195. data/try/features/encrypted_fields/nonce_uniqueness_try.rb +1 -1
  196. data/try/features/encrypted_fields/secure_by_default_behavior_try.rb +7 -7
  197. data/try/features/encrypted_fields/universal_serialization_safety_try.rb +13 -20
  198. data/try/features/external_identifier/external_identifier_try.rb +1 -1
  199. data/try/features/feature_dependencies_try.rb +3 -3
  200. data/try/features/field_groups_try.rb +244 -0
  201. data/try/features/object_identifier/object_identifier_integration_try.rb +28 -34
  202. data/try/features/object_identifier/object_identifier_try.rb +10 -0
  203. data/try/features/quantization/quantization_try.rb +1 -1
  204. data/try/features/relationships/indexing_commands_verification_try.rb +136 -0
  205. data/try/features/relationships/indexing_try.rb +443 -0
  206. data/try/features/relationships/participation_commands_verification_spec.rb +102 -0
  207. data/try/features/relationships/participation_commands_verification_try.rb +105 -0
  208. data/try/features/relationships/participation_performance_improvements_try.rb +124 -0
  209. data/try/features/relationships/participation_reverse_index_try.rb +196 -0
  210. data/try/features/relationships/relationships_api_changes_try.rb +72 -71
  211. data/try/features/relationships/relationships_edge_cases_try.rb +15 -18
  212. data/try/features/relationships/relationships_performance_minimal_try.rb +2 -2
  213. data/try/features/relationships/relationships_performance_simple_try.rb +8 -8
  214. data/try/features/relationships/relationships_performance_try.rb +20 -20
  215. data/try/features/relationships/relationships_try.rb +27 -38
  216. data/try/features/safe_dump/safe_dump_advanced_try.rb +2 -2
  217. data/try/features/transient_fields/refresh_reset_try.rb +3 -1
  218. data/try/features/transient_fields/simple_refresh_test.rb +1 -1
  219. data/try/helpers/test_cleanup.rb +86 -0
  220. data/try/helpers/test_helpers.rb +6 -7
  221. data/try/horreum/auto_indexing_on_save_try.rb +212 -0
  222. data/try/horreum/base_try.rb +3 -2
  223. data/try/horreum/commands_try.rb +3 -1
  224. data/try/horreum/defensive_initialization_try.rb +86 -0
  225. data/try/horreum/destroy_related_fields_cleanup_try.rb +332 -0
  226. data/try/horreum/initialization_try.rb +11 -7
  227. data/try/horreum/relations_try.rb +21 -13
  228. data/try/horreum/serialization_try.rb +12 -11
  229. data/try/horreum/settings_try.rb +2 -0
  230. data/try/integration/cross_component_try.rb +3 -3
  231. data/try/memory/memory_basic_test.rb +1 -1
  232. data/try/memory/memory_docker_ruby_dump.sh +2 -2
  233. data/try/models/customer_safe_dump_try.rb +1 -1
  234. data/try/models/customer_try.rb +13 -15
  235. data/try/models/datatype_base_try.rb +3 -3
  236. data/try/models/familia_object_try.rb +9 -8
  237. data/try/performance/benchmarks_try.rb +2 -2
  238. data/try/prototypes/atomic_saves_v1_context_proxy.rb +2 -2
  239. data/try/prototypes/atomic_saves_v3_connection_pool.rb +3 -3
  240. data/try/prototypes/atomic_saves_v4.rb +1 -1
  241. data/try/prototypes/lib/atomic_saves_v2_connection_switching_helpers.rb +4 -4
  242. data/try/prototypes/lib/atomic_saves_v3_connection_pool_helpers.rb +4 -4
  243. data/try/prototypes/pooling/lib/atomic_saves_v3_connection_pool_helpers.rb +4 -4
  244. data/try/prototypes/pooling/lib/connection_pool_metrics.rb +5 -5
  245. data/try/prototypes/pooling/lib/connection_pool_stress_test.rb +26 -26
  246. data/try/prototypes/pooling/lib/connection_pool_threading_models.rb +7 -7
  247. data/try/prototypes/pooling/lib/visualize_stress_results.rb +1 -1
  248. data/try/prototypes/pooling/pool_siege.rb +11 -11
  249. data/try/prototypes/pooling/run_stress_tests.rb +7 -7
  250. data/try/refinements/dear_json_array_methods_try.rb +53 -0
  251. data/try/refinements/dear_json_hash_methods_try.rb +54 -0
  252. data/try/refinements/logger_trace_methods_try.rb +44 -0
  253. data/try/refinements/time_literals_numeric_methods_try.rb +141 -0
  254. data/try/refinements/time_literals_string_methods_try.rb +80 -0
  255. data/try/valkey.conf +26 -0
  256. metadata +92 -52
  257. data/.rubocop_todo.yml +0 -208
  258. data/docs/connection_pooling.md +0 -192
  259. data/docs/guides/Connection-Pooling-Guide.md +0 -437
  260. data/docs/guides/Encrypted-Fields-Overview.md +0 -101
  261. data/docs/guides/Feature-System-Autoloading.md +0 -198
  262. data/docs/guides/Home.md +0 -116
  263. data/docs/guides/Relationships-Guide.md +0 -737
  264. data/docs/guides/relationships-methods.md +0 -266
  265. data/docs/reference/auditing_database_commands.rb +0 -228
  266. data/examples/permissions.rb +0 -240
  267. data/lib/familia/features/relationships/cascading.rb +0 -437
  268. data/lib/familia/features/relationships/membership.rb +0 -497
  269. data/lib/familia/features/relationships/permission_management.rb +0 -264
  270. data/lib/familia/features/relationships/querying.rb +0 -615
  271. data/lib/familia/features/relationships/redis_operations.rb +0 -274
  272. data/lib/familia/features/relationships/tracking.rb +0 -418
  273. data/lib/familia/horreum/core/connection.rb +0 -73
  274. data/lib/familia/horreum/core.rb +0 -21
  275. data/lib/familia/refinements/snake_case.rb +0 -40
  276. data/lib/familia/validation/command_recorder.rb +0 -336
  277. data/lib/familia/validation/expectations.rb +0 -519
  278. data/lib/familia/validation/validation_helpers.rb +0 -443
  279. data/lib/familia/validation/validator.rb +0 -412
  280. data/lib/familia/validation.rb +0 -140
  281. data/try/data_types/set_try.rb +0 -33
  282. data/try/features/relationships/categorical_permissions_try.rb +0 -515
  283. data/try/features/safe_dump/module_based_extensions_try.rb +0 -100
  284. data/try/features/safe_dump/safe_dump_autoloading_try.rb +0 -107
  285. data/try/validation/atomic_operations_try.rb.disabled +0 -320
  286. data/try/validation/command_validation_try.rb.disabled +0 -207
  287. data/try/validation/performance_validation_try.rb.disabled +0 -324
  288. data/try/validation/real_world_scenarios_try.rb.disabled +0 -390
@@ -35,8 +35,8 @@ stripe_customer.class.name
35
35
  #=> "Familia::HashKey"
36
36
 
37
37
  ## DataType instances know their owner
38
- @sample_obj.timeline.parent == @sample_obj
39
- #=> true
38
+ @sample_obj.timeline.parent.class
39
+ #=> Familia::Horreum::ParentDefinition
40
40
 
41
41
  ## DataType instances know their field name
42
42
  @sample_obj.timeline.keystring
@@ -54,7 +54,7 @@ stripe_customer.class.name
54
54
  #==> _.respond_to?(:exists?)
55
55
  #=/=> _.respond_to?(:destroy!)
56
56
 
57
- ## Can check if DataType exists in Redis
57
+ ## Can check if DataType exists in Valkey/Redis
58
58
  timeline = @sample_obj.timeline
59
59
  exists_before = timeline.exists?
60
60
  [exists_before.class, [true, false].include?(exists_before)]
@@ -44,7 +44,8 @@ obj = Customer.find_by_id :delano
44
44
 
45
45
  ## Customer.destroy
46
46
  @cust.destroy!
47
- #=> true
47
+ #=:> MultiResult
48
+ #==> result.successful?
48
49
 
49
50
  ## Customer.instances
50
51
  Customer.values.size
@@ -56,20 +57,20 @@ obj.save
56
57
  #=> true
57
58
 
58
59
  ## Familia.class_list
59
- Customer.customers.class
60
- #=> Familia::List
60
+ Customer.all_customers.class
61
+ #=> Familia::ListKey
61
62
 
62
63
  ## Familia class dbkey
63
- Customer.customers.dbkey
64
- #=> 'customer:customers'
64
+ Customer.all_customers.dbkey
65
+ #=> 'customer:all_customers'
65
66
 
66
67
  ## Familia.class_list
67
- Customer.customers << :delano << :tucker << :morton
68
- Customer.customers.size
68
+ Customer.all_customers << :delano << :tucker << :morton
69
+ Customer.all_customers.size
69
70
  #=> 3
70
71
 
71
72
  ## Familia class clear
72
- Customer.customers.delete!
73
+ Customer.all_customers.delete!
73
74
  #=> true
74
75
 
75
76
  ## Familia class replace 1 of 4
@@ -10,7 +10,7 @@ user_class = Class.new(Familia::Horreum) do
10
10
  field :data
11
11
  end
12
12
 
13
- large_data = { items: (1..1000).to_a, metadata: "x" * 1000 }
13
+ large_data = { metadata: "x" * 1000, items: (1..1000).to_a }
14
14
 
15
15
  json_time = Benchmark.realtime do
16
16
  100.times { JSON.dump(large_data) }
@@ -44,7 +44,7 @@ users.each(&:delete!)
44
44
  individual_time > 0
45
45
  #=!> StandardError
46
46
 
47
- ## Redis type access performance
47
+ ## Valkey/Redis type access performance
48
48
  user_class = Class.new(Familia::Horreum) do
49
49
  identifier_field :email
50
50
  field :name
@@ -1,6 +1,6 @@
1
- # try/prototypes/atomic_saves_v1_context_proxy.rb
1
+ # try/prototypes/atomic_saves_v1_target_proxy.rb
2
2
 
3
- # try -vf try/prototypes/atomic_saves_v1_context_proxy.rb
3
+ # try -vf try/prototypes/atomic_saves_v1_target_proxy.rb
4
4
 
5
5
  # ⏺ 🎉 Perfect! All Tests Pass!
6
6
  #
@@ -6,10 +6,10 @@
6
6
  # The issue is that refresh! is being called within the transaction, but
7
7
  # Database MULTI transactions queue commands and don't return results until
8
8
  # EXEC. So refresh! inside the transaction isn't going to see the current
9
- # state from Redis.
9
+ # state from the database.
10
10
  #
11
11
  # The problem is more fundamental: Database MULTI/EXEC transactions don't
12
- # work the way this code expects them to. In Redis:
12
+ # work the way this code expects them to. In Valkey/Redis:
13
13
  #
14
14
  # 1. MULTI starts queuing commands
15
15
  # 2. All subsequent commands are queued, not executed
@@ -20,7 +20,7 @@
20
20
  # work as expected
21
21
  # 2. Read the current balance and modify it - this won't work inside MULTI
22
22
  #
23
- # The atomic operations need to be restructured to work with Redis's
23
+ # The atomic operations need to be restructured to work with Valkey/Redis's
24
24
  # actual transaction model. Let me fix this:
25
25
 
26
26
  require 'bundler/setup'
@@ -99,7 +99,7 @@ class BankAccount < Familia::Horreum
99
99
  end
100
100
 
101
101
  def add(accnt)
102
- relatable_objids.add Time.now.to_f, accnt.identifier
102
+ relatable_objids.add Familia.now, accnt.identifier
103
103
  end
104
104
  end
105
105
  end
@@ -12,7 +12,7 @@
12
12
  # connection or MULTI connection based on Thread-local context
13
13
  # 2. **Thread Safety**: Uses Thread-local storage for transaction state
14
14
  # 3. **No method_missing**: Clean implementation via method overriding
15
- # 4. **Database MULTI/EXEC**: Leverages Redis's native transaction support
15
+ # 4. **Database MULTI/EXEC**: Leverages Valkey/Redis's native transaction support
16
16
  #
17
17
  # Design Decision: TransactionalMethods Module REMOVED
18
18
  #
@@ -70,7 +70,7 @@ class TransactionRecord < Familia::Horreum
70
70
  @to_account = to
71
71
  @amount = amount.to_f
72
72
  @status = "pending"
73
- @created_at = Time.now.to_i
73
+ @created_at = Familia.now.to_i
74
74
  end
75
75
  end
76
76
 
@@ -78,11 +78,11 @@ end
78
78
  module Familia
79
79
  class << self
80
80
  def current_transaction
81
- Thread.current[:familia_current_transaction]
81
+ Fiber[:familia_current_transaction]
82
82
  end
83
83
 
84
84
  def current_transaction=(transaction)
85
- Thread.current[:familia_current_transaction] = transaction
85
+ Fiber[:familia_current_transaction] = transaction
86
86
  end
87
87
 
88
88
  def atomic(&block)
@@ -83,7 +83,7 @@ class TransactionRecord < Familia::Horreum
83
83
  @to_account = to
84
84
  @amount = amount.to_f
85
85
  @status = "pending"
86
- @created_at = Time.now.to_i
86
+ @created_at = Familia.now.to_i
87
87
  end
88
88
 
89
89
  def amount
@@ -121,11 +121,11 @@ module Familia
121
121
  end
122
122
 
123
123
  def current_transaction
124
- Thread.current[:familia_current_transaction_v3]
124
+ Fiber[:familia_current_transaction_v3]
125
125
  end
126
126
 
127
127
  def current_transaction=(transaction)
128
- Thread.current[:familia_current_transaction_v3] = transaction
128
+ Fiber[:familia_current_transaction_v3] = transaction
129
129
  end
130
130
 
131
131
  # Proxy approach - transparent like V2
@@ -135,7 +135,7 @@ module Familia
135
135
  atomic_separate(&block)
136
136
  else
137
137
  # Use connection pool to get connection
138
- # For this prototype, we'll use a simple approach that works with Redis
138
+ # For this prototype, we'll use a simple approach that works with Valkey/Redis
139
139
  connection_pool.with do |conn|
140
140
  begin
141
141
  # Store the connection for use within the block
@@ -83,7 +83,7 @@ class TransactionRecord < Familia::Horreum
83
83
  @to_account = to
84
84
  @amount = amount.to_f
85
85
  @status = "pending"
86
- @created_at = Time.now.to_i
86
+ @created_at = Familia.now.to_i
87
87
  end
88
88
 
89
89
  def amount
@@ -121,11 +121,11 @@ module Familia
121
121
  end
122
122
 
123
123
  def current_transaction
124
- Thread.current[:familia_current_transaction_v3]
124
+ Fiber[:familia_current_transaction_v3]
125
125
  end
126
126
 
127
127
  def current_transaction=(transaction)
128
- Thread.current[:familia_current_transaction_v3] = transaction
128
+ Fiber[:familia_current_transaction_v3] = transaction
129
129
  end
130
130
 
131
131
  # Proxy approach - transparent like V2
@@ -135,7 +135,7 @@ module Familia
135
135
  atomic_separate(&block)
136
136
  else
137
137
  # Use connection pool to get connection
138
- # For this prototype, we'll use a simple approach that works with Redis
138
+ # For this prototype, we'll use a simple approach that works with Valkey/Redis
139
139
  connection_pool.with do |conn|
140
140
  begin
141
141
  # Store the connection for use within the block
@@ -26,7 +26,7 @@ module ConnectionPoolMetrics
26
26
  thread_id: thread_id,
27
27
  wait_time: wait_time,
28
28
  acquired: acquired,
29
- timestamp: Time.now.to_f
29
+ timestamp: Familia.now
30
30
  }
31
31
  end
32
32
  end
@@ -37,7 +37,7 @@ module ConnectionPoolMetrics
37
37
  thread_id: thread_id,
38
38
  state: state, # :waiting, :running, :completed, :failed
39
39
  context: context,
40
- timestamp: Time.now.to_f
40
+ timestamp: Familia.now
41
41
  }
42
42
  end
43
43
  end
@@ -47,7 +47,7 @@ module ConnectionPoolMetrics
47
47
  @metrics[:pool_exhaustion_events] << {
48
48
  wait_time: wait_time,
49
49
  threads_waiting: thread_count_waiting,
50
- timestamp: Time.now.to_f
50
+ timestamp: Familia.now
51
51
  }
52
52
  end
53
53
  end
@@ -99,7 +99,7 @@ module ConnectionPoolMetrics
99
99
  end
100
100
 
101
101
  def export_detailed_csv(filename_prefix = "stress_test")
102
- timestamp = Time.now.strftime("%Y%m%d_%H%M%S")
102
+ timestamp = Familia.now.strftime("%Y%m%d_%H%M%S")
103
103
 
104
104
  # Export operations
105
105
  CSV.open("#{filename_prefix}_operations_#{timestamp}.csv", "w") do |csv|
@@ -274,7 +274,7 @@ module ConnectionPoolMetrics
274
274
 
275
275
  def add_result(config, metrics_summary, model_info = {})
276
276
  @results << {
277
- timestamp: Time.now,
277
+ timestamp: Familia.now,
278
278
  config: config,
279
279
  summary: metrics_summary,
280
280
  model: model_info
@@ -260,7 +260,7 @@ class MetricsCollector
260
260
  duration: duration,
261
261
  success: success,
262
262
  wait_time: wait_time,
263
- timestamp: Time.now.to_f
263
+ timestamp: Familia.now
264
264
  }
265
265
 
266
266
  if @use_concurrent
@@ -277,7 +277,7 @@ class MetricsCollector
277
277
  error: error.class.name,
278
278
  message: error.message,
279
279
  context: context,
280
- timestamp: Time.now.to_f
280
+ timestamp: Familia.now
281
281
  }
282
282
 
283
283
  if @use_concurrent
@@ -301,7 +301,7 @@ class MetricsCollector
301
301
  utilization: utilization,
302
302
  utilization_alt: utilization_alt,
303
303
  in_use: in_use,
304
- timestamp: Time.now.to_f
304
+ timestamp: Familia.now
305
305
  }
306
306
 
307
307
  if @use_concurrent
@@ -582,17 +582,17 @@ class ConnectionPoolStressTest
582
582
  @config[:operations_per_thread].times do |op_index|
583
583
  account = get_account_for_operation(i, op_index)
584
584
  begin
585
- start = Time.now
586
- wait_start = Time.now
585
+ start = Familia.now
586
+ wait_start = Familia.now
587
587
 
588
588
  Familia.atomic do
589
- wait_time = Time.now - wait_start
589
+ wait_time = Familia.now - wait_start
590
590
  account.complex_operation
591
- @metrics.record_operation(:transaction, Time.now - start, true, wait_time)
591
+ @metrics.record_operation(:transaction, Familia.now - start, true, wait_time)
592
592
  end
593
593
  rescue => e
594
594
  @metrics.record_error(e, { thread: i })
595
- @metrics.record_operation(:transaction, Time.now - start, false)
595
+ @metrics.record_operation(:transaction, Familia.now - start, false)
596
596
  end
597
597
  end
598
598
  rescue => e
@@ -629,12 +629,12 @@ class ConnectionPoolStressTest
629
629
  puts "Running rapid fire test with #{@config[:thread_count]} threads"
630
630
 
631
631
  if @config[:duration]
632
- end_time = Time.now + @config[:duration]
632
+ end_time = Familia.now + @config[:duration]
633
633
 
634
634
  @config[:thread_count].times do |i|
635
635
  threads << Thread.new do
636
636
  op_index = 0
637
- while Time.now < end_time
637
+ while Familia.now < end_time
638
638
  account = get_account_for_operation(i, op_index)
639
639
  operation = select_operation
640
640
  execute_operation(account, operation)
@@ -691,7 +691,7 @@ class ConnectionPoolStressTest
691
691
 
692
692
  @config[:operations_per_thread].times do
693
693
  begin
694
- start = Time.now
694
+ start = Familia.now
695
695
 
696
696
  Familia.atomic do
697
697
  # Simulate long-running transaction
@@ -706,10 +706,10 @@ class ConnectionPoolStressTest
706
706
  account2.save
707
707
  end
708
708
 
709
- @metrics.record_operation(:long_transaction, Time.now - start, true)
709
+ @metrics.record_operation(:long_transaction, Familia.now - start, true)
710
710
  rescue => e
711
711
  @metrics.record_error(e, { thread: i })
712
- @metrics.record_operation(:long_transaction, Time.now - start, false)
712
+ @metrics.record_operation(:long_transaction, Familia.now - start, false)
713
713
  end
714
714
  end
715
715
  end
@@ -731,7 +731,7 @@ class ConnectionPoolStressTest
731
731
 
732
732
  @config[:operations_per_thread].times do
733
733
  begin
734
- start = Time.now
734
+ start = Familia.now
735
735
 
736
736
  Familia.atomic do
737
737
  account.deposit(50)
@@ -747,10 +747,10 @@ class ConnectionPoolStressTest
747
747
  account.save
748
748
  end
749
749
 
750
- @metrics.record_operation(:nested_transaction, Time.now - start, true)
750
+ @metrics.record_operation(:nested_transaction, Familia.now - start, true)
751
751
  rescue => e
752
752
  @metrics.record_error(e, { thread: i })
753
- @metrics.record_operation(:nested_transaction, Time.now - start, false)
753
+ @metrics.record_operation(:nested_transaction, Familia.now - start, false)
754
754
  end
755
755
  end
756
756
  end
@@ -773,7 +773,7 @@ class ConnectionPoolStressTest
773
773
 
774
774
  @config[:operations_per_thread].times do |op_num|
775
775
  begin
776
- start = Time.now
776
+ start = Familia.now
777
777
 
778
778
  if rand < error_rate
779
779
  # Inject an error
@@ -784,10 +784,10 @@ class ConnectionPoolStressTest
784
784
  account.complex_operation
785
785
  end
786
786
 
787
- @metrics.record_operation(:with_errors, Time.now - start, true)
787
+ @metrics.record_operation(:with_errors, Familia.now - start, true)
788
788
  rescue => e
789
789
  @metrics.record_error(e, { thread: i, operation: op_num })
790
- @metrics.record_operation(:with_errors, Time.now - start, false)
790
+ @metrics.record_operation(:with_errors, Familia.now - start, false)
791
791
  end
792
792
  end
793
793
  end
@@ -811,12 +811,12 @@ class ConnectionPoolStressTest
811
811
 
812
812
  def run_duration_based_test(mix)
813
813
  threads = []
814
- end_time = Time.now + @config[:duration]
814
+ end_time = Familia.now + @config[:duration]
815
815
 
816
816
  @config[:thread_count].times do |i|
817
817
  threads << Thread.new do
818
818
  op_index = 0
819
- while Time.now < end_time
819
+ while Familia.now < end_time
820
820
  account = get_account_for_operation(i, op_index)
821
821
  operation = select_operation_from_mix(mix)
822
822
  execute_operation(account, operation)
@@ -898,18 +898,18 @@ class ConnectionPoolStressTest
898
898
 
899
899
  def execute_operation(account, operation)
900
900
  begin
901
- start = Time.now
901
+ start = Familia.now
902
902
 
903
903
  case operation
904
904
  when :read
905
905
  account.refresh!
906
906
  _ = account.balance
907
- @metrics.record_operation(:read, Time.now - start, true)
907
+ @metrics.record_operation(:read, Familia.now - start, true)
908
908
  when :write
909
909
  current = account.balance || 0
910
910
  account.balance = current + rand(-10..10)
911
911
  account.save
912
- @metrics.record_operation(:write, Time.now - start, true)
912
+ @metrics.record_operation(:write, Familia.now - start, true)
913
913
  when :transaction
914
914
  Familia.atomic do
915
915
  account.refresh!
@@ -917,12 +917,12 @@ class ConnectionPoolStressTest
917
917
  account.balance = current + rand(-10..10)
918
918
  account.save
919
919
  end
920
- @metrics.record_operation(:transaction, Time.now - start, true)
920
+ @metrics.record_operation(:transaction, Familia.now - start, true)
921
921
  end
922
922
  rescue => e
923
923
  puts "Operation error: #{e.message} (#{e.class})" if ENV['FAMILIA_DEBUG']
924
924
  @metrics.record_error(e, { operation: operation })
925
- @metrics.record_operation(operation, Time.now - start, false)
925
+ @metrics.record_operation(operation, Familia.now - start, false)
926
926
  end
927
927
  end
928
928
 
@@ -227,24 +227,24 @@ module ThreadingModels
227
227
  end
228
228
 
229
229
  def perform_operation(operation, account)
230
- start = Time.now
230
+ start = Familia.now
231
231
 
232
232
  case operation[:type]
233
233
  when :read
234
234
  account.refresh!
235
- { success: true, duration: Time.now - start }
235
+ { success: true, duration: Familia.now - start }
236
236
  when :write
237
237
  account.balance += operation[:amount] || 0
238
238
  account.save
239
- { success: true, duration: Time.now - start }
239
+ { success: true, duration: Familia.now - start }
240
240
  when :transaction
241
241
  Familia.atomic do
242
242
  account.complex_operation
243
243
  end
244
- { success: true, duration: Time.now - start }
244
+ { success: true, duration: Familia.now - start }
245
245
  end
246
246
  rescue => e
247
- { success: false, error: e, duration: Time.now - start }
247
+ { success: false, error: e, duration: Familia.now - start }
248
248
  end
249
249
  end
250
250
 
@@ -334,7 +334,7 @@ class EnhancedConnectionPoolStressTest < ConnectionPoolStressTest
334
334
 
335
335
  puts "\n=== Running with #{model_name} model ==="
336
336
 
337
- start_time = Time.now
337
+ start_time = Familia.now
338
338
 
339
339
  result = model.run do |account, thread_id, op_num|
340
340
  operation = select_operation_from_mix(
@@ -343,7 +343,7 @@ class EnhancedConnectionPoolStressTest < ConnectionPoolStressTest
343
343
  execute_operation(account, operation)
344
344
  end
345
345
 
346
- duration = Time.now - start_time
346
+ duration = Familia.now - start_time
347
347
 
348
348
  result.merge(
349
349
  total_duration: duration,
@@ -46,7 +46,7 @@ class StressTestVisualizer
46
46
  def generate_report
47
47
  report = []
48
48
  report << "# Connection Pool Stress Test Results"
49
- report << "\nGenerated: #{Time.now}"
49
+ report << "\nGenerated: #{Familia.now}"
50
50
  report << "\n"
51
51
 
52
52
  # Summary section
@@ -26,7 +26,7 @@ class PoolSiege
26
26
  if @options[:profile]
27
27
  run_with_profiling
28
28
  else
29
- start_time = Time.now
29
+ start_time = Familia.now
30
30
 
31
31
  if @options[:quiet]
32
32
  run_silent_test
@@ -34,7 +34,7 @@ class PoolSiege
34
34
  run_with_progress
35
35
  end
36
36
 
37
- end_time = Time.now
37
+ end_time = Familia.now
38
38
  print_final_results(end_time - start_time)
39
39
  end
40
40
  end
@@ -157,7 +157,7 @@ class PoolSiege
157
157
  end
158
158
  end.parse!(args)
159
159
 
160
- # Set defaults
160
+ # UnsortedSet defaults
161
161
  options[:threads] ||= 10
162
162
  options[:pool_size] ||= 5
163
163
  options[:operations] ||= 100 unless options[:duration]
@@ -310,16 +310,16 @@ class PoolSiege
310
310
  end
311
311
 
312
312
  # Run the test
313
- start_time = Time.now
313
+ start_time = Familia.now
314
314
  run_silent_test
315
- end_time = Time.now
315
+ end_time = Familia.now
316
316
  total_time = end_time - start_time
317
317
 
318
318
  # Restore original method
319
319
  Familia.define_singleton_method(:atomic, original_atomic)
320
320
 
321
321
  # Generate report
322
- timestamp = Time.now.strftime("%Y%m%d_%H%M%S")
322
+ timestamp = Familia.now.strftime("%Y%m%d_%H%M%S")
323
323
  scenario_name = @options[:scenario].to_s
324
324
  report_file = "pool_siege_#{scenario_name}_#{timestamp}_profile.txt"
325
325
 
@@ -439,8 +439,8 @@ class ProgressTracker
439
439
  @total_ops = total_ops
440
440
  @completed = 0
441
441
  @successful = 0
442
- @start_time = Time.now
443
- @last_update = Time.now
442
+ @start_time = Familia.now
443
+ @last_update = Familia.now
444
444
  end
445
445
 
446
446
  def update(success)
@@ -465,7 +465,7 @@ class ProgressTracker
465
465
  private
466
466
 
467
467
  def should_update?
468
- now = Time.now
468
+ now = Familia.now
469
469
  return false if (now - @last_update) < 0.5 # Update at most every 500ms
470
470
  @last_update = now
471
471
  true
@@ -474,7 +474,7 @@ class ProgressTracker
474
474
  def show_ops_progress
475
475
  percent = (@completed.to_f / @total_ops * 100).round(1)
476
476
  success_rate = (@successful.to_f / @completed * 100).round(1) if @completed > 0
477
- elapsed = Time.now - @start_time
477
+ elapsed = Familia.now - @start_time
478
478
  rate = (@completed / elapsed).round(1) if elapsed > 0
479
479
 
480
480
  bar_width = 20
@@ -489,7 +489,7 @@ class ProgressTracker
489
489
  end
490
490
 
491
491
  def show_time_progress
492
- elapsed = Time.now - @start_time
492
+ elapsed = Familia.now - @start_time
493
493
  success_rate = (@successful.to_f / @completed * 100).round(1) if @completed > 0
494
494
  rate = (@completed / elapsed).round(1) if elapsed > 0
495
495
 
@@ -29,7 +29,7 @@ class StressTestRunner
29
29
  def initialize(options = {})
30
30
  @options = {
31
31
  config_set: :moderate,
32
- output_dir: "stress_test_results_#{Time.now.strftime('%Y%m%d_%H%M%S')}",
32
+ output_dir: "stress_test_results_#{Familia.now.strftime('%Y%m%d_%H%M%S')}",
33
33
  threading_models: [:traditional, :thread_pool, :fiber],
34
34
  operation_mixes: [:balanced, :read_heavy, :write_heavy],
35
35
  generate_visualizations: true,
@@ -67,7 +67,7 @@ class StressTestRunner
67
67
  puts "Total tests to run: #{total_tests}"
68
68
  puts ""
69
69
 
70
- start_time = Time.now
70
+ start_time = Familia.now
71
71
 
72
72
  @config_set[:scenarios].each do |scenario|
73
73
  puts "\n--- Testing Scenario: #{scenario} ---"
@@ -105,7 +105,7 @@ class StressTestRunner
105
105
  end
106
106
  end
107
107
 
108
- duration = Time.now - start_time
108
+ duration = Familia.now - start_time
109
109
  puts "\n" + "=" * 80
110
110
  puts "ALL TESTS COMPLETED"
111
111
  puts "Total duration: #{format_duration(duration)}"
@@ -206,7 +206,7 @@ class StressTestRunner
206
206
  end
207
207
 
208
208
  def save_test_results(config, metrics, model_info)
209
- timestamp = Time.now.strftime('%Y%m%d_%H%M%S_%L')
209
+ timestamp = Familia.now.strftime('%Y%m%d_%H%M%S_%L')
210
210
  test_id = "#{config[:threading_model]}_#{config[:scenario]}_#{timestamp}"
211
211
 
212
212
  # Export detailed CSV files
@@ -217,7 +217,7 @@ class StressTestRunner
217
217
 
218
218
  # Save test configuration and results
219
219
  test_data = {
220
- timestamp: Time.now,
220
+ timestamp: Familia.now,
221
221
  config: config,
222
222
  model_info: model_info,
223
223
  summary: metrics.respond_to?(:detailed_summary) ? metrics.detailed_summary : metrics.summary
@@ -267,7 +267,7 @@ class StressTestRunner
267
267
  <<~README
268
268
  # Connection Pool Stress Test Results
269
269
 
270
- Generated: #{Time.now}
270
+ Generated: #{Familia.now}
271
271
  Configuration: #{@options[:config_set]}
272
272
 
273
273
  ## Directory Structure
@@ -305,7 +305,7 @@ class StressTestRunner
305
305
  summary = <<~SUMMARY
306
306
  # Executive Summary - Connection Pool Stress Testing
307
307
 
308
- **Generated**: #{Time.now}
308
+ **Generated**: #{Familia.now}
309
309
  **Test Configuration**: #{@options[:config_set]}
310
310
 
311
311
  ## Key Findings