familia 2.0.0.pre19 → 2.0.0.pre21

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 (372) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/claude-code-review.yml +4 -9
  3. data/.github/workflows/code-smells.yml +64 -3
  4. data/.pre-commit-config.yaml +8 -6
  5. data/.reek.yml +10 -9
  6. data/.rubocop.yml +4 -0
  7. data/CHANGELOG.rst +177 -112
  8. data/CLAUDE.md +28 -1
  9. data/Gemfile +1 -1
  10. data/Gemfile.lock +20 -17
  11. data/bin/try +16 -0
  12. data/bin/tryouts +16 -0
  13. data/changelog.d/20251105_flexible_external_identifier_format.rst +66 -0
  14. data/changelog.d/20251107_112554_delano_179_participation_asymmetry.rst +44 -0
  15. data/changelog.d/20251107_213121_delano_fix_thread_safety_races_011CUumCP492Twxm4NLt2FvL.rst +20 -0
  16. data/changelog.d/20251107_fix_participates_in_symbol_resolution.rst +91 -0
  17. data/changelog.d/20251107_optimized_redis_exists_checks.rst +94 -0
  18. data/changelog.d/20251108_frozen_string_literal_pragma.rst +44 -0
  19. data/docs/1106-participates_in-bidirectional-solution.md +129 -0
  20. data/docs/guides/encryption.md +486 -0
  21. data/docs/guides/feature-encrypted-fields.md +123 -7
  22. data/docs/guides/feature-expiration.md +161 -117
  23. data/docs/guides/feature-external-identifiers.md +415 -443
  24. data/docs/guides/feature-object-identifiers.md +400 -269
  25. data/docs/guides/feature-quantization.md +120 -6
  26. data/docs/guides/feature-relationships-indexing.md +318 -0
  27. data/docs/guides/feature-relationships-methods.md +146 -604
  28. data/docs/guides/feature-relationships-participation.md +263 -0
  29. data/docs/guides/feature-relationships.md +118 -136
  30. data/docs/guides/feature-system-devs.md +176 -693
  31. data/docs/guides/feature-system.md +119 -6
  32. data/docs/guides/feature-transient-fields.md +81 -0
  33. data/docs/guides/field-system.md +778 -0
  34. data/docs/guides/index.md +32 -15
  35. data/docs/guides/logging.md +187 -0
  36. data/docs/guides/optimized-loading.md +674 -0
  37. data/docs/guides/thread-safety-monitoring.md +61 -0
  38. data/docs/guides/{time-utilities.md → time-literals.md} +12 -12
  39. data/docs/migrating/v2.0.0-pre22.md +241 -0
  40. data/docs/overview.md +7 -9
  41. data/docs/reference/api-technical.md +267 -320
  42. data/examples/autoloader/mega_customer/features/deprecated_fields.rb +2 -0
  43. data/examples/autoloader/mega_customer/safe_dump_fields.rb +2 -0
  44. data/examples/autoloader/mega_customer.rb +2 -0
  45. data/examples/datatype_standalone.rb +4 -3
  46. data/examples/encrypted_fields.rb +2 -1
  47. data/examples/json_usage_patterns.rb +2 -0
  48. data/examples/relationships.rb +3 -0
  49. data/examples/safe_dump.rb +2 -1
  50. data/examples/sampling_demo.rb +53 -0
  51. data/examples/single_connection_transaction_confusions.rb +2 -1
  52. data/familia.gemspec +2 -1
  53. data/lib/familia/base.rb +2 -0
  54. data/lib/familia/connection/behavior.rb +2 -0
  55. data/lib/familia/connection/handlers.rb +2 -0
  56. data/lib/familia/connection/individual_command_proxy.rb +2 -0
  57. data/lib/familia/connection/middleware.rb +34 -24
  58. data/lib/familia/connection/operation_core.rb +2 -0
  59. data/lib/familia/connection/operations.rb +2 -0
  60. data/lib/familia/connection/pipelined_core.rb +2 -0
  61. data/lib/familia/connection/transaction_core.rb +68 -0
  62. data/lib/familia/connection.rb +18 -3
  63. data/lib/familia/data_type/class_methods.rb +3 -1
  64. data/lib/familia/data_type/connection.rb +2 -0
  65. data/lib/familia/data_type/database_commands.rb +2 -0
  66. data/lib/familia/data_type/serialization.rb +6 -4
  67. data/lib/familia/data_type/settings.rb +2 -0
  68. data/lib/familia/data_type/types/counter.rb +2 -0
  69. data/lib/familia/data_type/types/hashkey.rb +7 -5
  70. data/lib/familia/data_type/types/listkey.rb +2 -0
  71. data/lib/familia/data_type/types/lock.rb +2 -0
  72. data/lib/familia/data_type/types/sorted_set.rb +2 -0
  73. data/lib/familia/data_type/types/stringkey.rb +2 -0
  74. data/lib/familia/data_type/types/unsorted_set.rb +2 -0
  75. data/lib/familia/data_type.rb +2 -0
  76. data/lib/familia/encryption/encrypted_data.rb +4 -2
  77. data/lib/familia/encryption/manager.rb +2 -0
  78. data/lib/familia/encryption/provider.rb +2 -0
  79. data/lib/familia/encryption/providers/aes_gcm_provider.rb +2 -0
  80. data/lib/familia/encryption/providers/secure_xchacha20_poly1305_provider.rb +2 -0
  81. data/lib/familia/encryption/providers/xchacha20_poly1305_provider.rb +2 -0
  82. data/lib/familia/encryption/registry.rb +2 -0
  83. data/lib/familia/encryption/request_cache.rb +2 -0
  84. data/lib/familia/encryption.rb +9 -2
  85. data/lib/familia/errors.rb +2 -0
  86. data/lib/familia/features/autoloader.rb +2 -0
  87. data/lib/familia/features/encrypted_fields/concealed_string.rb +2 -0
  88. data/lib/familia/features/encrypted_fields/encrypted_field_type.rb +4 -0
  89. data/lib/familia/features/encrypted_fields.rb +2 -2
  90. data/lib/familia/features/expiration/extensions.rb +3 -1
  91. data/lib/familia/features/expiration.rb +12 -4
  92. data/lib/familia/features/external_identifier.rb +33 -7
  93. data/lib/familia/features/object_identifier.rb +2 -0
  94. data/lib/familia/features/quantization.rb +3 -1
  95. data/lib/familia/features/relationships/README.md +3 -1
  96. data/lib/familia/features/relationships/collection_operations.rb +2 -0
  97. data/lib/familia/features/relationships/indexing/multi_index_generators.rb +138 -9
  98. data/lib/familia/features/relationships/indexing/rebuild_strategies.rb +479 -0
  99. data/lib/familia/features/relationships/indexing/unique_index_generators.rb +89 -21
  100. data/lib/familia/features/relationships/indexing.rb +3 -0
  101. data/lib/familia/features/relationships/indexing_relationship.rb +3 -1
  102. data/lib/familia/features/relationships/participation/participant_methods.rb +131 -14
  103. data/lib/familia/features/relationships/participation/rebuild_strategies.md +41 -0
  104. data/lib/familia/features/relationships/participation/target_methods.rb +6 -6
  105. data/lib/familia/features/relationships/participation.rb +155 -69
  106. data/lib/familia/features/relationships/participation_membership.rb +69 -0
  107. data/lib/familia/features/relationships/participation_relationship.rb +34 -6
  108. data/lib/familia/features/relationships/score_encoding.rb +2 -0
  109. data/lib/familia/features/relationships.rb +5 -3
  110. data/lib/familia/features/safe_dump.rb +2 -0
  111. data/lib/familia/features/transient_fields/redacted_string.rb +2 -0
  112. data/lib/familia/features/transient_fields/single_use_redacted_string.rb +2 -0
  113. data/lib/familia/features/transient_fields/transient_field_type.rb +5 -3
  114. data/lib/familia/features/transient_fields.rb +2 -0
  115. data/lib/familia/features.rb +2 -0
  116. data/lib/familia/field_type.rb +3 -1
  117. data/lib/familia/horreum/connection.rb +17 -1
  118. data/lib/familia/horreum/database_commands.rb +2 -0
  119. data/lib/familia/horreum/definition.rb +16 -6
  120. data/lib/familia/horreum/management.rb +212 -42
  121. data/lib/familia/horreum/persistence.rb +176 -108
  122. data/lib/familia/horreum/related_fields.rb +2 -0
  123. data/lib/familia/horreum/serialization.rb +23 -4
  124. data/lib/familia/horreum/settings.rb +2 -0
  125. data/lib/familia/horreum/utils.rb +2 -0
  126. data/lib/familia/horreum.rb +15 -1
  127. data/lib/familia/identifier_extractor.rb +2 -0
  128. data/lib/familia/instrumentation.rb +156 -0
  129. data/lib/familia/json_serializer.rb +2 -0
  130. data/lib/familia/logging.rb +92 -32
  131. data/lib/familia/refinements/dear_json.rb +2 -0
  132. data/lib/familia/refinements/stylize_words.rb +2 -14
  133. data/lib/familia/refinements/time_literals.rb +2 -0
  134. data/lib/familia/refinements.rb +2 -0
  135. data/lib/familia/secure_identifier.rb +10 -2
  136. data/lib/familia/settings.rb +2 -0
  137. data/lib/familia/thread_safety/instrumented_mutex.rb +166 -0
  138. data/lib/familia/thread_safety/monitor.rb +328 -0
  139. data/lib/familia/utils.rb +13 -0
  140. data/lib/familia/verifiable_identifier.rb +3 -1
  141. data/lib/familia/version.rb +3 -1
  142. data/lib/familia.rb +31 -4
  143. data/lib/middleware/database_command_counter.rb +152 -0
  144. data/lib/middleware/database_logger.rb +295 -170
  145. data/lib/multi_result.rb +2 -0
  146. data/try/edge_cases/empty_identifiers_try.rb +2 -0
  147. data/try/edge_cases/hash_symbolization_try.rb +2 -0
  148. data/try/edge_cases/json_serialization_try.rb +2 -0
  149. data/try/edge_cases/legacy_data_detection/deserialization_edge_cases_try.rb +4 -0
  150. data/try/edge_cases/race_conditions_try.rb +4 -0
  151. data/try/edge_cases/reserved_keywords_try.rb +4 -0
  152. data/try/edge_cases/string_coercion_try.rb +2 -0
  153. data/try/edge_cases/ttl_side_effects_try.rb +4 -0
  154. data/try/features/encrypted_fields/aad_protection_try.rb +4 -0
  155. data/try/features/encrypted_fields/concealed_string_core_try.rb +4 -0
  156. data/try/features/encrypted_fields/context_isolation_try.rb +4 -0
  157. data/try/features/encrypted_fields/encrypted_fields_core_try.rb +33 -0
  158. data/try/features/encrypted_fields/encrypted_fields_integration_try.rb +4 -0
  159. data/try/features/encrypted_fields/encrypted_fields_no_cache_security_try.rb +4 -0
  160. data/try/features/encrypted_fields/encrypted_fields_security_try.rb +4 -0
  161. data/try/features/encrypted_fields/error_conditions_try.rb +4 -0
  162. data/try/features/encrypted_fields/fresh_key_derivation_try.rb +4 -0
  163. data/try/features/encrypted_fields/fresh_key_try.rb +4 -0
  164. data/try/features/encrypted_fields/key_rotation_try.rb +4 -0
  165. data/try/features/encrypted_fields/memory_security_try.rb +4 -0
  166. data/try/features/encrypted_fields/missing_current_key_version_try.rb +4 -0
  167. data/try/features/encrypted_fields/nonce_uniqueness_try.rb +4 -0
  168. data/try/features/encrypted_fields/secure_by_default_behavior_try.rb +4 -0
  169. data/try/features/encrypted_fields/thread_safety_try.rb +4 -0
  170. data/try/features/encrypted_fields/universal_serialization_safety_try.rb +4 -0
  171. data/try/features/encryption/config_persistence_try.rb +4 -0
  172. data/try/features/encryption/core_try.rb +4 -0
  173. data/try/features/encryption/instance_variable_scope_try.rb +4 -0
  174. data/try/features/encryption/module_loading_try.rb +4 -0
  175. data/try/features/encryption/providers/aes_gcm_provider_try.rb +4 -0
  176. data/try/features/encryption/providers/xchacha20_poly1305_provider_try.rb +4 -0
  177. data/try/features/encryption/roundtrip_validation_try.rb +4 -0
  178. data/try/features/encryption/secure_memory_handling_try.rb +4 -0
  179. data/try/features/expiration/expiration_try.rb +4 -0
  180. data/try/features/external_identifier/external_identifier_try.rb +171 -8
  181. data/try/features/feature_dependencies_try.rb +2 -0
  182. data/try/features/feature_improvements_try.rb +2 -0
  183. data/try/features/field_groups_try.rb +2 -0
  184. data/try/features/object_identifier/object_identifier_integration_try.rb +12 -9
  185. data/try/features/object_identifier/object_identifier_try.rb +2 -0
  186. data/try/features/quantization/quantization_try.rb +4 -0
  187. data/try/features/real_feature_integration_try.rb +2 -0
  188. data/try/features/relationships/indexing_commands_verification_try.rb +2 -0
  189. data/try/features/relationships/indexing_rebuild_try.rb +600 -0
  190. data/try/features/relationships/indexing_try.rb +2 -0
  191. data/try/features/relationships/participation_bidirectional_try.rb +242 -0
  192. data/try/features/relationships/participation_commands_verification_spec.rb +4 -0
  193. data/try/features/relationships/participation_commands_verification_try.rb +2 -0
  194. data/try/features/relationships/participation_performance_improvements_try.rb +11 -9
  195. data/try/features/relationships/participation_reverse_index_try.rb +15 -13
  196. data/try/features/relationships/participation_target_class_resolution_try.rb +209 -0
  197. data/try/features/relationships/participation_unresolved_target_try.rb +109 -0
  198. data/try/features/relationships/relationships_api_changes_try.rb +2 -0
  199. data/try/features/relationships/relationships_edge_cases_try.rb +4 -0
  200. data/try/features/relationships/relationships_performance_minimal_try.rb +4 -0
  201. data/try/features/relationships/relationships_performance_simple_try.rb +4 -0
  202. data/try/features/relationships/relationships_performance_try.rb +4 -0
  203. data/try/features/relationships/relationships_performance_working_try.rb +4 -0
  204. data/try/features/relationships/relationships_try.rb +6 -4
  205. data/try/features/safe_dump/safe_dump_advanced_try.rb +4 -0
  206. data/try/features/safe_dump/safe_dump_try.rb +4 -0
  207. data/try/features/transient_fields/redacted_string_try.rb +2 -0
  208. data/try/features/transient_fields/refresh_reset_try.rb +3 -0
  209. data/try/features/transient_fields/simple_refresh_test.rb +3 -0
  210. data/try/features/transient_fields/single_use_redacted_string_try.rb +2 -0
  211. data/try/features/transient_fields/transient_fields_core_try.rb +4 -0
  212. data/try/features/transient_fields/transient_fields_integration_try.rb +4 -0
  213. data/try/integration/connection/fiber_context_preservation_try.rb +4 -0
  214. data/try/integration/connection/handler_constraints_try.rb +4 -0
  215. data/try/integration/connection/isolated_dbclient_try.rb +4 -0
  216. data/try/integration/connection/middleware_reconnect_try.rb +2 -0
  217. data/try/integration/connection/operation_mode_guards_try.rb +4 -0
  218. data/try/integration/connection/pipeline_fallback_integration_try.rb +3 -0
  219. data/try/integration/connection/pools_try.rb +4 -0
  220. data/try/integration/connection/responsibility_chain_tracking_try.rb +4 -0
  221. data/try/integration/connection/transaction_fallback_integration_try.rb +4 -0
  222. data/try/integration/connection/transaction_mode_permissive_try.rb +4 -0
  223. data/try/integration/connection/transaction_mode_strict_try.rb +4 -0
  224. data/try/integration/connection/transaction_mode_warn_try.rb +4 -0
  225. data/try/integration/connection/transaction_modes_try.rb +4 -0
  226. data/try/integration/conventional_inheritance_try.rb +4 -0
  227. data/try/integration/create_method_try.rb +4 -0
  228. data/try/integration/cross_component_try.rb +4 -0
  229. data/try/integration/data_types/datatype_pipelines_try.rb +4 -0
  230. data/try/integration/data_types/datatype_transactions_try.rb +4 -0
  231. data/try/integration/database_consistency_try.rb +4 -0
  232. data/try/integration/familia_extended_try.rb +4 -0
  233. data/try/integration/familia_members_methods_try.rb +4 -0
  234. data/try/integration/models/customer_safe_dump_try.rb +4 -0
  235. data/try/integration/models/customer_try.rb +4 -0
  236. data/try/integration/models/datatype_base_try.rb +4 -0
  237. data/try/integration/models/familia_object_try.rb +4 -0
  238. data/try/integration/persistence_operations_try.rb +4 -0
  239. data/try/integration/relationships_persistence_round_trip_try.rb +17 -14
  240. data/try/integration/save_methods_consistency_try.rb +241 -0
  241. data/try/integration/scenarios_try.rb +4 -0
  242. data/try/integration/secure_identifier_try.rb +4 -0
  243. data/try/integration/transaction_safety_core_try.rb +176 -0
  244. data/try/integration/transaction_safety_workflow_try.rb +291 -0
  245. data/try/integration/verifiable_identifier_try.rb +4 -0
  246. data/try/investigation/pipeline_routing/README.md +228 -0
  247. data/try/performance/benchmarks_try.rb +4 -0
  248. data/try/performance/transaction_safety_benchmark_try.rb +238 -0
  249. data/try/support/benchmarks/deserialization_benchmark.rb +3 -1
  250. data/try/support/benchmarks/deserialization_correctness_test.rb +3 -1
  251. data/try/support/debugging/cache_behavior_tracer.rb +4 -0
  252. data/try/support/debugging/debug_aad_process.rb +3 -0
  253. data/try/support/debugging/debug_concealed_internal.rb +3 -0
  254. data/try/support/debugging/debug_concealed_reveal.rb +3 -0
  255. data/try/support/debugging/debug_context_aad.rb +3 -0
  256. data/try/support/debugging/debug_context_simple.rb +3 -0
  257. data/try/support/debugging/debug_cross_context.rb +3 -0
  258. data/try/support/debugging/debug_database_load.rb +3 -0
  259. data/try/support/debugging/debug_encrypted_json_check.rb +3 -0
  260. data/try/support/debugging/debug_encrypted_json_step_by_step.rb +3 -0
  261. data/try/support/debugging/debug_exists_lifecycle.rb +3 -0
  262. data/try/support/debugging/debug_field_decrypt.rb +3 -0
  263. data/try/support/debugging/debug_fresh_cross_context.rb +3 -0
  264. data/try/support/debugging/debug_load_path.rb +3 -0
  265. data/try/support/debugging/debug_method_definition.rb +3 -0
  266. data/try/support/debugging/debug_method_resolution.rb +3 -0
  267. data/try/support/debugging/debug_minimal.rb +3 -0
  268. data/try/support/debugging/debug_provider.rb +3 -0
  269. data/try/support/debugging/debug_secure_behavior.rb +3 -0
  270. data/try/support/debugging/debug_string_class.rb +3 -0
  271. data/try/support/debugging/debug_test.rb +3 -0
  272. data/try/support/debugging/debug_test_design.rb +3 -0
  273. data/try/support/debugging/encryption_method_tracer.rb +4 -0
  274. data/try/support/debugging/provider_diagnostics.rb +4 -0
  275. data/try/support/helpers/test_cleanup.rb +4 -0
  276. data/try/support/helpers/test_helpers.rb +5 -0
  277. data/try/support/memory/memory_basic_test.rb +4 -0
  278. data/try/support/memory/memory_detailed_test.rb +4 -0
  279. data/try/support/memory/memory_search_for_string.rb +4 -0
  280. data/try/support/memory/test_actual_redactedstring_protection.rb +4 -0
  281. data/try/support/prototypes/atomic_saves_v1_context_proxy.rb +4 -0
  282. data/try/support/prototypes/atomic_saves_v2_connection_switching.rb +4 -0
  283. data/try/support/prototypes/atomic_saves_v3_connection_pool.rb +4 -0
  284. data/try/support/prototypes/atomic_saves_v4.rb +4 -0
  285. data/try/support/prototypes/lib/atomic_saves_v2_connection_switching_helpers.rb +4 -0
  286. data/try/support/prototypes/lib/atomic_saves_v3_connection_pool_helpers.rb +4 -0
  287. data/try/support/prototypes/pooling/configurable_stress_test.rb +4 -0
  288. data/try/support/prototypes/pooling/lib/atomic_saves_v3_connection_pool_helpers.rb +4 -0
  289. data/try/support/prototypes/pooling/lib/connection_pool_metrics.rb +4 -0
  290. data/try/support/prototypes/pooling/lib/connection_pool_stress_test.rb +4 -0
  291. data/try/support/prototypes/pooling/lib/connection_pool_threading_models.rb +4 -0
  292. data/try/support/prototypes/pooling/lib/visualize_stress_results.rb +4 -2
  293. data/try/support/prototypes/pooling/pool_siege.rb +4 -2
  294. data/try/support/prototypes/pooling/run_stress_tests.rb +4 -2
  295. data/try/thread_safety/README.md +496 -0
  296. data/try/thread_safety/class_connection_chain_race_try.rb +265 -0
  297. data/try/thread_safety/connection_chain_race_try.rb +148 -0
  298. data/try/thread_safety/encryption_manager_cache_race_try.rb +166 -0
  299. data/try/thread_safety/feature_registry_race_try.rb +226 -0
  300. data/try/thread_safety/fiber_pipeline_isolation_try.rb +235 -0
  301. data/try/thread_safety/fiber_transaction_isolation_try.rb +208 -0
  302. data/try/thread_safety/field_registration_race_try.rb +222 -0
  303. data/try/thread_safety/logger_initialization_race_try.rb +170 -0
  304. data/try/thread_safety/middleware_registration_race_try.rb +154 -0
  305. data/try/thread_safety/module_config_race_try.rb +175 -0
  306. data/try/thread_safety/secure_identifier_cache_race_try.rb +226 -0
  307. data/try/unit/core/autoloader_try.rb +4 -0
  308. data/try/unit/core/base_enhancements_try.rb +4 -0
  309. data/try/unit/core/connection_try.rb +4 -0
  310. data/try/unit/core/errors_try.rb +4 -0
  311. data/try/unit/core/extensions_try.rb +4 -0
  312. data/try/unit/core/familia_logger_try.rb +2 -0
  313. data/try/unit/core/familia_try.rb +4 -0
  314. data/try/unit/core/middleware_sampling_try.rb +335 -0
  315. data/try/unit/core/middleware_test_helpers_bug_try.rb +58 -0
  316. data/try/unit/core/middleware_thread_safety_try.rb +245 -0
  317. data/try/unit/core/middleware_try.rb +4 -0
  318. data/try/unit/core/settings_try.rb +4 -0
  319. data/try/unit/core/time_utils_try.rb +4 -0
  320. data/try/unit/core/tools_try.rb +4 -0
  321. data/try/unit/core/utils_try.rb +37 -0
  322. data/try/unit/data_types/boolean_try.rb +4 -0
  323. data/try/unit/data_types/counter_try.rb +4 -0
  324. data/try/unit/data_types/datatype_base_try.rb +4 -0
  325. data/try/unit/data_types/hash_try.rb +4 -0
  326. data/try/unit/data_types/list_try.rb +4 -0
  327. data/try/unit/data_types/lock_try.rb +4 -0
  328. data/try/unit/data_types/sorted_set_try.rb +4 -0
  329. data/try/unit/data_types/sorted_set_zadd_options_try.rb +4 -0
  330. data/try/unit/data_types/string_try.rb +4 -0
  331. data/try/unit/data_types/unsortedset_try.rb +4 -0
  332. data/try/unit/familia_resolve_class_try.rb +116 -0
  333. data/try/unit/horreum/auto_indexing_on_save_try.rb +5 -1
  334. data/try/unit/horreum/automatic_index_validation_try.rb +2 -0
  335. data/try/unit/horreum/base_try.rb +4 -0
  336. data/try/unit/horreum/class_methods_try.rb +4 -0
  337. data/try/unit/horreum/commands_try.rb +4 -0
  338. data/try/unit/horreum/defensive_initialization_try.rb +4 -0
  339. data/try/unit/horreum/destroy_related_fields_cleanup_try.rb +4 -0
  340. data/try/unit/horreum/enhanced_conflict_handling_try.rb +4 -0
  341. data/try/unit/horreum/field_categories_try.rb +4 -0
  342. data/try/unit/horreum/field_definition_try.rb +4 -0
  343. data/try/unit/horreum/initialization_try.rb +4 -0
  344. data/try/unit/horreum/json_type_preservation_try.rb +2 -0
  345. data/try/unit/horreum/optimized_loading_try.rb +156 -0
  346. data/try/unit/horreum/relations_try.rb +4 -0
  347. data/try/unit/horreum/serialization_persistent_fields_try.rb +4 -0
  348. data/try/unit/horreum/serialization_try.rb +4 -0
  349. data/try/unit/horreum/settings_try.rb +4 -0
  350. data/try/unit/horreum/unique_index_edge_cases_try.rb +4 -0
  351. data/try/unit/horreum/unique_index_guard_validation_try.rb +2 -0
  352. data/try/unit/middleware/database_command_counter_methods_try.rb +139 -0
  353. data/try/unit/middleware/database_logger_methods_try.rb +251 -0
  354. data/try/unit/refinements/dear_json_array_methods_try.rb +4 -0
  355. data/try/unit/refinements/dear_json_hash_methods_try.rb +4 -0
  356. data/try/unit/refinements/time_literals_numeric_methods_try.rb +4 -0
  357. data/try/unit/refinements/time_literals_string_methods_try.rb +4 -0
  358. data/try/unit/thread_safety_monitor_try.rb +149 -0
  359. metadata +72 -17
  360. data/.github/workflows/code-quality.yml +0 -138
  361. data/changelog.d/20251011_012003_delano_159_datatype_transaction_pipeline_support.rst +0 -91
  362. data/changelog.d/20251011_203905_delano_next.rst +0 -30
  363. data/changelog.d/20251011_212633_delano_next.rst +0 -13
  364. data/changelog.d/20251011_221253_delano_next.rst +0 -26
  365. data/docs/archive/FAMILIA_RELATIONSHIPS.md +0 -210
  366. data/docs/archive/FAMILIA_TECHNICAL.md +0 -823
  367. data/docs/archive/FAMILIA_UPDATE.md +0 -226
  368. data/docs/archive/README.md +0 -64
  369. data/docs/archive/api-reference.md +0 -333
  370. data/docs/guides/core-field-system.md +0 -806
  371. data/docs/guides/implementation.md +0 -276
  372. data/docs/guides/security-model.md +0 -183
@@ -0,0 +1,265 @@
1
+ # try/thread_safety/class_connection_chain_race_try.rb
2
+ #
3
+ # frozen_string_literal: true
4
+
5
+ require_relative '../support/helpers/test_helpers'
6
+
7
+ # Thread safety tests for class-level connection chain initialization
8
+ #
9
+ # Tests concurrent class-level connection chain initialization to ensure
10
+ # that each Horreum subclass properly initializes its own connection chain
11
+ # without race conditions or shared state issues.
12
+ #
13
+ # These tests verify:
14
+ # 1. Concurrent class-level connection chain initialization
15
+ # 2. Multiple model classes initialized concurrently
16
+ # 3. Inheritance chain with concurrent access
17
+ # 4. Connection chain isolation between classes
18
+
19
+ ## Concurrent class-level connection chain initialization
20
+ class TestModel1 < Familia::Horreum
21
+ identifier_field :test_id
22
+ field :test_id
23
+
24
+ def init
25
+ @test_id ||= SecureRandom.hex(4)
26
+ end
27
+ field :name
28
+ end
29
+
30
+ TestModel1.instance_variable_set(:@class_connection_chain, nil)
31
+ # Mutex is now initialized eagerly in Connection.included hook
32
+ barrier = Concurrent::CyclicBarrier.new(50)
33
+ chains = Concurrent::Array.new
34
+
35
+ threads = 50.times.map do
36
+ Thread.new do
37
+ barrier.wait
38
+ client = TestModel1.dbclient
39
+ # Store the actual connection chain object ID to detect singleton violations
40
+ chain = TestModel1.instance_variable_get(:@class_connection_chain)
41
+ chains << chain.object_id
42
+ end
43
+ end
44
+
45
+ threads.each(&:join)
46
+ # Test multiple invariants (pattern from middleware tests):
47
+ # - No nil entries (corruption check)
48
+ # - All chains are same object (singleton property)
49
+ # - Got expected number of results
50
+ [chains.any?(nil), chains.uniq.size, chains.size]
51
+ #=> [false, 1, 50]
52
+
53
+
54
+ ## Multiple model classes initialized concurrently
55
+ barrier = Concurrent::CyclicBarrier.new(10)
56
+ models = Concurrent::Array.new
57
+
58
+ threads = 10.times.map do |i|
59
+ Thread.new do
60
+ model_class = Class.new(Familia::Horreum) do
61
+ def self.name
62
+ "ConcurrentTestModel#{Thread.current.object_id}"
63
+ end
64
+
65
+ identifier_field :test_id
66
+ field :test_id
67
+ field :value
68
+
69
+ def init
70
+ @test_id ||= SecureRandom.hex(4)
71
+ end
72
+ end
73
+
74
+ barrier.wait
75
+ client = model_class.dbclient
76
+ models << [model_class.name, client.class.name]
77
+ end
78
+ end
79
+
80
+ threads.each(&:join)
81
+ models.size
82
+ #=> 10
83
+
84
+
85
+ ## Connection chain per-class isolation
86
+ class TestModel2 < Familia::Horreum
87
+ identifier_field :test_id
88
+ field :test_id
89
+
90
+ def init
91
+ @test_id ||= SecureRandom.hex(4)
92
+ end
93
+ field :name
94
+ end
95
+
96
+ class TestModel3 < Familia::Horreum
97
+ identifier_field :test_id
98
+ field :test_id
99
+
100
+ def init
101
+ @test_id ||= SecureRandom.hex(4)
102
+ end
103
+ field :name
104
+ end
105
+
106
+ TestModel2.instance_variable_set(:@class_connection_chain, nil)
107
+ # Mutex is now initialized eagerly in Connection.included hook
108
+ TestModel3.instance_variable_set(:@class_connection_chain, nil)
109
+ # Mutex is now initialized eagerly in Connection.included hook
110
+
111
+ barrier = Concurrent::CyclicBarrier.new(20)
112
+ results = Concurrent::Array.new
113
+
114
+ threads = 20.times.map do |i|
115
+ Thread.new do
116
+ barrier.wait
117
+ if i.even?
118
+ client = TestModel2.dbclient
119
+ results << [:model2, client.class.name]
120
+ else
121
+ client = TestModel3.dbclient
122
+ results << [:model3, client.class.name]
123
+ end
124
+ end
125
+ end
126
+
127
+ threads.each(&:join)
128
+ results.size
129
+ #=> 20
130
+
131
+
132
+ ## Inheritance chain with concurrent access
133
+ class BaseModel < Familia::Horreum
134
+ identifier_field :test_id
135
+ field :test_id
136
+
137
+ def init
138
+ @test_id ||= SecureRandom.hex(4)
139
+ end
140
+ field :base_field
141
+ end
142
+
143
+ class ChildModel1 < BaseModel
144
+ field :child_field
145
+ end
146
+
147
+ class ChildModel2 < BaseModel
148
+ field :other_field
149
+ end
150
+
151
+ ChildModel1.instance_variable_set(:@class_connection_chain, nil)
152
+ # Mutex is now initialized eagerly in Connection.included hook
153
+ ChildModel2.instance_variable_set(:@class_connection_chain, nil)
154
+ # Mutex is now initialized eagerly in Connection.included hook
155
+
156
+ barrier = Concurrent::CyclicBarrier.new(30)
157
+ chains = Concurrent::Array.new
158
+
159
+ threads = 30.times.map do |i|
160
+ Thread.new do
161
+ barrier.wait
162
+ case i % 3
163
+ when 0
164
+ client = BaseModel.dbclient
165
+ chains << [:base, client.class.name]
166
+ when 1
167
+ client = ChildModel1.dbclient
168
+ chains << [:child1, client.class.name]
169
+ when 2
170
+ client = ChildModel2.dbclient
171
+ chains << [:child2, client.class.name]
172
+ end
173
+ end
174
+ end
175
+
176
+ threads.each(&:join)
177
+ chains.size
178
+ #=> 30
179
+
180
+ ## Concurrent database operations through class connection chain
181
+ class OperationTestModel < Familia::Horreum
182
+ identifier_field :test_id
183
+ field :test_id
184
+ field :value
185
+ end
186
+
187
+ barrier = Concurrent::CyclicBarrier.new(25)
188
+ results = Concurrent::Array.new
189
+
190
+ threads = 25.times.map do |i|
191
+ Thread.new do
192
+ barrier.wait
193
+ obj = OperationTestModel.new(test_id: "test_#{i}", value: i)
194
+ obj.save
195
+ results << obj.test_id
196
+ end
197
+ end
198
+
199
+ threads.each(&:join)
200
+ results.size
201
+ #=> 25
202
+
203
+ ## Class connection chain rebuilds after reconnect
204
+ class ReconnectTestModel < Familia::Horreum
205
+ identifier_field :test_id
206
+ field :test_id
207
+
208
+ def init
209
+ @test_id ||= SecureRandom.hex(4)
210
+ end
211
+ field :name
212
+ end
213
+
214
+ ReconnectTestModel.instance_variable_set(:@class_connection_chain, nil)
215
+ # Mutex is now initialized eagerly in Connection.included hook
216
+ barrier = Concurrent::CyclicBarrier.new(20)
217
+ results = Concurrent::Array.new
218
+
219
+ threads = 20.times.map do |i|
220
+ Thread.new do
221
+ barrier.wait
222
+ if i < 5
223
+ # Some threads trigger reconnect
224
+ Familia.reconnect!
225
+ end
226
+ # All threads access the chain
227
+ client = ReconnectTestModel.dbclient
228
+ results << client.class.name
229
+ end
230
+ end
231
+
232
+ threads.each(&:join)
233
+ results.size
234
+ #=> 20
235
+
236
+
237
+ ## Rapid sequential access to class connection chain
238
+ class RapidAccessModel < Familia::Horreum
239
+ identifier_field :test_id
240
+ field :test_id
241
+
242
+ def init
243
+ @test_id ||= SecureRandom.hex(4)
244
+ end
245
+ field :value
246
+ end
247
+
248
+ barrier = Concurrent::CyclicBarrier.new(15)
249
+ access_counts = Concurrent::Array.new
250
+
251
+ threads = 15.times.map do
252
+ Thread.new do
253
+ barrier.wait
254
+ count = 0
255
+ 20.times do
256
+ RapidAccessModel.dbclient
257
+ count += 1
258
+ end
259
+ access_counts << count
260
+ end
261
+ end
262
+
263
+ threads.each(&:join)
264
+ access_counts.size
265
+ #=> 15
@@ -0,0 +1,148 @@
1
+ # try/thread_safety/connection_chain_race_try.rb
2
+ #
3
+ # frozen_string_literal: true
4
+
5
+ require_relative '../support/helpers/test_helpers'
6
+
7
+ # Thread safety tests for connection chain lazy initialization
8
+ #
9
+ # Tests concurrent connection chain initialization to ensure that the
10
+ # lazy initialization pattern (@connection_chain ||= build_connection_chain)
11
+ # doesn't result in duplicate chain instances or inconsistent state.
12
+ #
13
+ # These tests verify:
14
+ # 1. Concurrent module-level connection chain initialization
15
+ # 2. Connection chain consistency after concurrent reconnect
16
+ # 3. Thread-safe chain access during reconnection
17
+
18
+ @original_chain = nil
19
+
20
+ # Setup: Store original connection chain
21
+ @original_chain = Familia.instance_variable_get(:@connection_chain)
22
+
23
+ ## Concurrent connection chain initialization builds only one chain
24
+ Familia.instance_variable_set(:@connection_chain, nil)
25
+ barrier = Concurrent::CyclicBarrier.new(50)
26
+ chain_ids = Concurrent::Array.new
27
+
28
+ threads = 50.times.map do
29
+ Thread.new do
30
+ barrier.wait
31
+ Familia.dbclient('redis://127.0.0.1:6379') # Trigger chain initialization
32
+ chain_ids << Familia.instance_variable_get(:@connection_chain).object_id
33
+ end
34
+ end
35
+
36
+ threads.each(&:join)
37
+ # Test multiple invariants:
38
+ # - No nil entries (array corruption check from middleware tests)
39
+ # - All threads see same connection chain object (singleton property)
40
+ [chain_ids.any?(nil), chain_ids.uniq.size]
41
+ #=> [false, 1]
42
+
43
+ ## Connection chain remains functional after concurrent access
44
+ Familia.instance_variable_set(:@connection_chain, nil)
45
+ barrier = Concurrent::CyclicBarrier.new(30)
46
+ results = Concurrent::Array.new
47
+
48
+ threads = 30.times.map do
49
+ Thread.new do
50
+ barrier.wait
51
+ begin
52
+ client = Familia.dbclient
53
+ result = client.call('PING')
54
+ results << result
55
+ rescue => e
56
+ results << e.class.name
57
+ end
58
+ end
59
+ end
60
+
61
+ threads.each(&:join)
62
+ # Test multiple invariants (pattern from middleware tests):
63
+ # - No nil entries (corruption check)
64
+ # - All successful PONG responses (correctness check)
65
+ [results.any?(nil), results.all? { |r| r == 'PONG' }]
66
+ #=> [false, true]
67
+
68
+ ## Concurrent reconnect calls maintain chain consistency
69
+ barrier = Concurrent::CyclicBarrier.new(20)
70
+ errors = Concurrent::Array.new
71
+ successes = Concurrent::Array.new
72
+
73
+ threads = 20.times.map do |i|
74
+ Thread.new do
75
+ barrier.wait
76
+ begin
77
+ if i < 10
78
+ # Half the threads reconnect
79
+ Familia.reconnect!
80
+ successes << :reconnect
81
+ else
82
+ # Half access the chain
83
+ client = Familia.dbclient
84
+ client.call('PING')
85
+ successes << :ping
86
+ end
87
+ rescue => e
88
+ errors << e.class.name
89
+ end
90
+ end
91
+ end
92
+
93
+ threads.each(&:join)
94
+ (successes.size >= 15)
95
+ #=> true
96
+
97
+ ## Connection chain rebuilds correctly after nil assignment
98
+ original = Familia.instance_variable_get(:@connection_chain)
99
+ barrier = Concurrent::CyclicBarrier.new(40)
100
+ results = Concurrent::Array.new
101
+
102
+ threads = 40.times.map do |i|
103
+ Thread.new do
104
+ barrier.wait
105
+ if i == 0
106
+ # One thread clears the chain
107
+ Familia.instance_variable_set(:@connection_chain, nil)
108
+ end
109
+ # All threads try to use the chain
110
+ begin
111
+ client = Familia.dbclient
112
+ results << client.class.name
113
+ rescue => e
114
+ results << e.class.name
115
+ end
116
+ end
117
+ end
118
+
119
+ threads.each(&:join)
120
+ # Test multiple invariants:
121
+ # - No nil entries from concurrent chain rebuilding
122
+ # - All got valid Redis instances (protected by Mutex)
123
+ # - All are actually instances, not errors
124
+ [results.any?(nil), results.all? { |r| r == 'Redis' }, results.size]
125
+ #=> [false, true, 40]
126
+
127
+ ## Rapid sequential reconnects from multiple threads
128
+ barrier = Concurrent::CyclicBarrier.new(10)
129
+ reconnect_counts = Concurrent::Array.new
130
+
131
+ threads = 10.times.map do
132
+ Thread.new do
133
+ barrier.wait
134
+ count = 0
135
+ 10.times do
136
+ Familia.reconnect!
137
+ count += 1
138
+ end
139
+ reconnect_counts << count
140
+ end
141
+ end
142
+
143
+ threads.each(&:join)
144
+ reconnect_counts.all? { |c| c == 10 }
145
+ #=> true
146
+
147
+ # Teardown: Restore original connection chain
148
+ Familia.instance_variable_set(:@connection_chain, @original_chain)
@@ -0,0 +1,166 @@
1
+ # try/thread_safety/encryption_manager_cache_race_try.rb
2
+ #
3
+ # frozen_string_literal: true
4
+
5
+ require_relative '../support/helpers/test_helpers'
6
+ require 'base64'
7
+
8
+ # Thread safety tests for encryption manager cache initialization
9
+ #
10
+ # Tests concurrent encryption manager cache access to ensure that only
11
+ # a single Manager instance is created per algorithm even under high
12
+ # concurrent load.
13
+ #
14
+ # These tests verify:
15
+ # 1. Concurrent manager cache initialization (single algorithm)
16
+ # 2. Multiple algorithms initialized concurrently
17
+ # 3. Maximum contention with CyclicBarrier pattern
18
+ # 4. Manager singleton property per algorithm
19
+
20
+ # Setup encryption keys for testing
21
+ test_keys = {
22
+ v1: Base64.strict_encode64('a' * 32),
23
+ v2: Base64.strict_encode64('b' * 32)
24
+ }
25
+ Familia.config.encryption_keys = test_keys
26
+ Familia.config.current_key_version = :v1
27
+
28
+ ## Concurrent manager cache initialization for default algorithm
29
+ # Reset the managers cache to nil to simulate first access
30
+ Familia::Encryption.instance_variable_set(:@managers, nil)
31
+
32
+ barrier = Concurrent::CyclicBarrier.new(50)
33
+ managers = Concurrent::Array.new
34
+
35
+ threads = 50.times.map do
36
+ Thread.new do
37
+ barrier.wait
38
+ # Get manager for default algorithm (nil)
39
+ mgr = Familia::Encryption.manager(algorithm: nil)
40
+ managers << mgr.object_id
41
+ end
42
+ end
43
+
44
+ threads.each(&:join)
45
+
46
+ # All threads should get the same manager instance
47
+ [managers.any?(nil), managers.uniq.size, managers.size]
48
+ #=> [false, 1, 50]
49
+
50
+
51
+ ## Concurrent manager cache initialization for default algorithm only
52
+ # Testing with just default (nil) algorithm to avoid provider availability issues
53
+ Familia::Encryption.instance_variable_set(:@managers, nil)
54
+
55
+ barrier = Concurrent::CyclicBarrier.new(30)
56
+ results = Concurrent::Array.new
57
+
58
+ threads = 30.times.map do |i|
59
+ Thread.new do
60
+ barrier.wait
61
+ # All use default algorithm for simplicity
62
+ mgr = Familia::Encryption.manager(algorithm: nil)
63
+ results << mgr.object_id
64
+ end
65
+ end
66
+
67
+ threads.each(&:join)
68
+
69
+ # All threads should get the same manager instance
70
+ [results.uniq.size, results.size]
71
+ #=> [1, 30]
72
+
73
+
74
+ ## Maximum contention test with encryption operations
75
+ Familia::Encryption.instance_variable_set(:@managers, nil)
76
+ Familia::Encryption.reset_derivation_count!
77
+
78
+ barrier = Concurrent::CyclicBarrier.new(50)
79
+ @encrypt_errors = Concurrent::Array.new
80
+ @encrypted_values = Concurrent::Array.new
81
+
82
+ threads = 50.times.map do |i|
83
+ Thread.new do
84
+ begin
85
+ barrier.wait
86
+ # All threads encrypt concurrently, forcing manager cache access
87
+ plaintext = "secret-#{i}"
88
+ context = "test:#{i}"
89
+ encrypted = Familia::Encryption.encrypt(plaintext, context: context)
90
+ @encrypted_values << encrypted
91
+ rescue => e
92
+ @encrypt_errors << e
93
+ end
94
+ end
95
+ end
96
+
97
+ threads.each(&:join)
98
+
99
+ # Verify no errors and all encryptions succeeded
100
+ [@encrypt_errors.empty?, @encrypted_values.size]
101
+ #=> [true, 50]
102
+
103
+
104
+ ## Manager singleton property maintained across concurrent access
105
+ # Access the managers cache and verify singleton property
106
+ managers_cache = Familia::Encryption.instance_variable_get(:@managers)
107
+
108
+ # Should be a Concurrent::Map
109
+ managers_cache.class.name
110
+ #=> "Concurrent::Map"
111
+
112
+
113
+ ## Concurrent decryption operations with manager cache
114
+ barrier = Concurrent::CyclicBarrier.new(25)
115
+ @decrypted_values = Concurrent::Array.new
116
+ @decrypt_errors = Concurrent::Array.new
117
+
118
+ # First, create some encrypted values
119
+ test_encrypted = 25.times.map do |i|
120
+ Familia::Encryption.encrypt("value-#{i}", context: "test:#{i}")
121
+ end
122
+
123
+ threads = 25.times.map do |i|
124
+ Thread.new do
125
+ begin
126
+ barrier.wait
127
+ decrypted = Familia::Encryption.decrypt(test_encrypted[i], context: "test:#{i}")
128
+ @decrypted_values << decrypted
129
+ rescue => e
130
+ @decrypt_errors << e
131
+ end
132
+ end
133
+ end
134
+
135
+ threads.each(&:join)
136
+
137
+ # Verify no errors and all decryptions succeeded
138
+ [@decrypt_errors.empty?, @decrypted_values.size]
139
+ #=> [true, 25]
140
+
141
+
142
+ ## Rapid sequential manager access per thread
143
+ barrier = Concurrent::CyclicBarrier.new(20)
144
+ access_counts = Concurrent::Array.new
145
+
146
+ threads = 20.times.map do
147
+ Thread.new do
148
+ barrier.wait
149
+ count = 0
150
+ 50.times do
151
+ Familia::Encryption.manager(algorithm: nil)
152
+ count += 1
153
+ end
154
+ access_counts << count
155
+ end
156
+ end
157
+
158
+ threads.each(&:join)
159
+
160
+ # Each thread completed 50 accesses
161
+ access_counts.all? { |c| c == 50 }
162
+ #=> true
163
+
164
+ # Cleanup
165
+ Familia.config.encryption_keys = nil
166
+ Familia.config.current_key_version = nil