familia 2.0.0.pre19 → 2.0.0.pre22

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 (370) 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/.talismanrc +5 -1
  8. data/CHANGELOG.rst +220 -112
  9. data/CLAUDE.md +28 -1
  10. data/Gemfile +1 -1
  11. data/Gemfile.lock +20 -17
  12. data/bin/try +16 -0
  13. data/bin/tryouts +16 -0
  14. data/docs/1106-participates_in-bidirectional-solution.md +129 -0
  15. data/docs/guides/encryption.md +486 -0
  16. data/docs/guides/feature-encrypted-fields.md +123 -7
  17. data/docs/guides/feature-expiration.md +161 -117
  18. data/docs/guides/feature-external-identifiers.md +415 -443
  19. data/docs/guides/feature-object-identifiers.md +400 -269
  20. data/docs/guides/feature-quantization.md +120 -6
  21. data/docs/guides/feature-relationships-indexing.md +318 -0
  22. data/docs/guides/feature-relationships-methods.md +146 -604
  23. data/docs/guides/feature-relationships-participation.md +263 -0
  24. data/docs/guides/feature-relationships.md +118 -136
  25. data/docs/guides/feature-system-devs.md +176 -693
  26. data/docs/guides/feature-system.md +119 -6
  27. data/docs/guides/feature-transient-fields.md +81 -0
  28. data/docs/guides/field-system.md +778 -0
  29. data/docs/guides/index.md +32 -15
  30. data/docs/guides/logging.md +187 -0
  31. data/docs/guides/optimized-loading.md +674 -0
  32. data/docs/guides/thread-safety-monitoring.md +61 -0
  33. data/docs/guides/{time-utilities.md → time-literals.md} +12 -12
  34. data/docs/migrating/v2.0.0-pre22.md +241 -0
  35. data/docs/overview.md +7 -9
  36. data/docs/reference/api-technical.md +267 -320
  37. data/examples/autoloader/mega_customer/features/deprecated_fields.rb +2 -0
  38. data/examples/autoloader/mega_customer/safe_dump_fields.rb +2 -0
  39. data/examples/autoloader/mega_customer.rb +2 -0
  40. data/examples/datatype_standalone.rb +4 -3
  41. data/examples/encrypted_fields.rb +2 -1
  42. data/examples/json_usage_patterns.rb +2 -0
  43. data/examples/relationships.rb +3 -0
  44. data/examples/safe_dump.rb +2 -1
  45. data/examples/sampling_demo.rb +53 -0
  46. data/examples/single_connection_transaction_confusions.rb +2 -1
  47. data/familia.gemspec +2 -1
  48. data/lib/familia/base.rb +2 -0
  49. data/lib/familia/connection/behavior.rb +2 -0
  50. data/lib/familia/connection/handlers.rb +2 -0
  51. data/lib/familia/connection/individual_command_proxy.rb +2 -0
  52. data/lib/familia/connection/middleware.rb +34 -24
  53. data/lib/familia/connection/operation_core.rb +3 -2
  54. data/lib/familia/connection/operations.rb +2 -0
  55. data/lib/familia/connection/pipelined_core.rb +3 -3
  56. data/lib/familia/connection/transaction_core.rb +69 -2
  57. data/lib/familia/connection.rb +18 -3
  58. data/lib/familia/data_type/class_methods.rb +3 -1
  59. data/lib/familia/data_type/connection.rb +2 -0
  60. data/lib/familia/data_type/database_commands.rb +2 -0
  61. data/lib/familia/data_type/serialization.rb +79 -52
  62. data/lib/familia/data_type/settings.rb +2 -0
  63. data/lib/familia/data_type/types/counter.rb +2 -0
  64. data/lib/familia/data_type/types/hashkey.rb +7 -5
  65. data/lib/familia/data_type/types/listkey.rb +2 -0
  66. data/lib/familia/data_type/types/lock.rb +2 -0
  67. data/lib/familia/data_type/types/sorted_set.rb +7 -10
  68. data/lib/familia/data_type/types/stringkey.rb +24 -0
  69. data/lib/familia/data_type/types/unsorted_set.rb +2 -0
  70. data/lib/familia/data_type.rb +2 -0
  71. data/lib/familia/encryption/encrypted_data.rb +4 -2
  72. data/lib/familia/encryption/manager.rb +2 -0
  73. data/lib/familia/encryption/provider.rb +2 -0
  74. data/lib/familia/encryption/providers/aes_gcm_provider.rb +2 -0
  75. data/lib/familia/encryption/providers/secure_xchacha20_poly1305_provider.rb +2 -0
  76. data/lib/familia/encryption/providers/xchacha20_poly1305_provider.rb +2 -0
  77. data/lib/familia/encryption/registry.rb +2 -0
  78. data/lib/familia/encryption/request_cache.rb +2 -0
  79. data/lib/familia/encryption.rb +9 -2
  80. data/lib/familia/errors.rb +2 -0
  81. data/lib/familia/features/autoloader.rb +2 -0
  82. data/lib/familia/features/encrypted_fields/concealed_string.rb +2 -0
  83. data/lib/familia/features/encrypted_fields/encrypted_field_type.rb +4 -0
  84. data/lib/familia/features/encrypted_fields.rb +2 -2
  85. data/lib/familia/features/expiration/extensions.rb +3 -1
  86. data/lib/familia/features/expiration.rb +12 -4
  87. data/lib/familia/features/external_identifier.rb +62 -7
  88. data/lib/familia/features/object_identifier.rb +49 -0
  89. data/lib/familia/features/quantization.rb +3 -1
  90. data/lib/familia/features/relationships/README.md +3 -1
  91. data/lib/familia/features/relationships/collection_operations.rb +2 -0
  92. data/lib/familia/features/relationships/indexing/multi_index_generators.rb +138 -9
  93. data/lib/familia/features/relationships/indexing/rebuild_strategies.rb +479 -0
  94. data/lib/familia/features/relationships/indexing/unique_index_generators.rb +97 -21
  95. data/lib/familia/features/relationships/indexing.rb +3 -0
  96. data/lib/familia/features/relationships/indexing_relationship.rb +3 -1
  97. data/lib/familia/features/relationships/participation/participant_methods.rb +131 -14
  98. data/lib/familia/features/relationships/participation/rebuild_strategies.md +41 -0
  99. data/lib/familia/features/relationships/participation/target_methods.rb +6 -6
  100. data/lib/familia/features/relationships/participation.rb +155 -69
  101. data/lib/familia/features/relationships/participation_membership.rb +69 -0
  102. data/lib/familia/features/relationships/participation_relationship.rb +34 -6
  103. data/lib/familia/features/relationships/score_encoding.rb +2 -0
  104. data/lib/familia/features/relationships.rb +5 -3
  105. data/lib/familia/features/safe_dump.rb +2 -0
  106. data/lib/familia/features/transient_fields/redacted_string.rb +2 -0
  107. data/lib/familia/features/transient_fields/single_use_redacted_string.rb +2 -0
  108. data/lib/familia/features/transient_fields/transient_field_type.rb +5 -3
  109. data/lib/familia/features/transient_fields.rb +2 -0
  110. data/lib/familia/features.rb +2 -0
  111. data/lib/familia/field_type.rb +3 -1
  112. data/lib/familia/horreum/connection.rb +17 -1
  113. data/lib/familia/horreum/database_commands.rb +8 -1
  114. data/lib/familia/horreum/definition.rb +16 -6
  115. data/lib/familia/horreum/management.rb +353 -52
  116. data/lib/familia/horreum/persistence.rb +179 -108
  117. data/lib/familia/horreum/related_fields.rb +2 -0
  118. data/lib/familia/horreum/serialization.rb +23 -4
  119. data/lib/familia/horreum/settings.rb +2 -0
  120. data/lib/familia/horreum/utils.rb +2 -0
  121. data/lib/familia/horreum.rb +15 -1
  122. data/lib/familia/identifier_extractor.rb +3 -1
  123. data/lib/familia/instrumentation.rb +156 -0
  124. data/lib/familia/json_serializer.rb +2 -0
  125. data/lib/familia/logging.rb +92 -32
  126. data/lib/familia/refinements/dear_json.rb +2 -0
  127. data/lib/familia/refinements/stylize_words.rb +2 -14
  128. data/lib/familia/refinements/time_literals.rb +2 -0
  129. data/lib/familia/refinements.rb +2 -0
  130. data/lib/familia/secure_identifier.rb +10 -2
  131. data/lib/familia/settings.rb +2 -0
  132. data/lib/familia/thread_safety/instrumented_mutex.rb +166 -0
  133. data/lib/familia/thread_safety/monitor.rb +328 -0
  134. data/lib/familia/utils.rb +13 -0
  135. data/lib/familia/verifiable_identifier.rb +3 -1
  136. data/lib/familia/version.rb +3 -1
  137. data/lib/familia.rb +31 -4
  138. data/lib/middleware/database_command_counter.rb +152 -0
  139. data/lib/middleware/database_logger.rb +295 -170
  140. data/lib/multi_result.rb +61 -31
  141. data/try/edge_cases/empty_identifiers_try.rb +2 -0
  142. data/try/edge_cases/hash_symbolization_try.rb +2 -0
  143. data/try/edge_cases/json_serialization_try.rb +2 -0
  144. data/try/edge_cases/legacy_data_detection/deserialization_edge_cases_try.rb +4 -0
  145. data/try/edge_cases/race_conditions_try.rb +4 -0
  146. data/try/edge_cases/reserved_keywords_try.rb +4 -0
  147. data/try/edge_cases/string_coercion_try.rb +2 -0
  148. data/try/edge_cases/ttl_side_effects_try.rb +4 -0
  149. data/try/features/count_any_edge_cases_try.rb +486 -0
  150. data/try/features/count_any_methods_try.rb +197 -0
  151. data/try/features/encrypted_fields/aad_protection_try.rb +4 -0
  152. data/try/features/encrypted_fields/concealed_string_core_try.rb +4 -0
  153. data/try/features/encrypted_fields/context_isolation_try.rb +4 -0
  154. data/try/features/encrypted_fields/encrypted_fields_core_try.rb +33 -0
  155. data/try/features/encrypted_fields/encrypted_fields_integration_try.rb +4 -0
  156. data/try/features/encrypted_fields/encrypted_fields_no_cache_security_try.rb +4 -0
  157. data/try/features/encrypted_fields/encrypted_fields_security_try.rb +4 -0
  158. data/try/features/encrypted_fields/error_conditions_try.rb +4 -0
  159. data/try/features/encrypted_fields/fresh_key_derivation_try.rb +4 -0
  160. data/try/features/encrypted_fields/fresh_key_try.rb +4 -0
  161. data/try/features/encrypted_fields/key_rotation_try.rb +4 -0
  162. data/try/features/encrypted_fields/memory_security_try.rb +4 -0
  163. data/try/features/encrypted_fields/missing_current_key_version_try.rb +4 -0
  164. data/try/features/encrypted_fields/nonce_uniqueness_try.rb +4 -0
  165. data/try/features/encrypted_fields/secure_by_default_behavior_try.rb +4 -0
  166. data/try/features/encrypted_fields/thread_safety_try.rb +4 -0
  167. data/try/features/encrypted_fields/universal_serialization_safety_try.rb +4 -0
  168. data/try/features/encryption/config_persistence_try.rb +4 -0
  169. data/try/features/encryption/core_try.rb +4 -0
  170. data/try/features/encryption/instance_variable_scope_try.rb +4 -0
  171. data/try/features/encryption/module_loading_try.rb +4 -0
  172. data/try/features/encryption/providers/aes_gcm_provider_try.rb +4 -0
  173. data/try/features/encryption/providers/xchacha20_poly1305_provider_try.rb +4 -0
  174. data/try/features/encryption/roundtrip_validation_try.rb +4 -0
  175. data/try/features/encryption/secure_memory_handling_try.rb +4 -0
  176. data/try/features/expiration/expiration_try.rb +4 -0
  177. data/try/features/external_identifier/external_identifier_try.rb +305 -8
  178. data/try/features/feature_dependencies_try.rb +2 -0
  179. data/try/features/feature_improvements_try.rb +2 -0
  180. data/try/features/field_groups_try.rb +2 -0
  181. data/try/features/object_identifier/object_identifier_integration_try.rb +12 -9
  182. data/try/features/object_identifier/object_identifier_try.rb +140 -0
  183. data/try/features/quantization/quantization_try.rb +4 -0
  184. data/try/features/real_feature_integration_try.rb +2 -0
  185. data/try/features/relationships/indexing_commands_verification_try.rb +2 -0
  186. data/try/features/relationships/indexing_rebuild_try.rb +606 -0
  187. data/try/features/relationships/indexing_try.rb +2 -0
  188. data/try/features/relationships/participation_bidirectional_try.rb +242 -0
  189. data/try/features/relationships/participation_commands_verification_spec.rb +4 -0
  190. data/try/features/relationships/participation_commands_verification_try.rb +2 -0
  191. data/try/features/relationships/participation_performance_improvements_try.rb +11 -9
  192. data/try/features/relationships/participation_reverse_index_try.rb +15 -13
  193. data/try/features/relationships/participation_target_class_resolution_try.rb +209 -0
  194. data/try/features/relationships/participation_unresolved_target_try.rb +109 -0
  195. data/try/features/relationships/relationships_api_changes_try.rb +2 -0
  196. data/try/features/relationships/relationships_edge_cases_try.rb +4 -0
  197. data/try/features/relationships/relationships_performance_minimal_try.rb +4 -0
  198. data/try/features/relationships/relationships_performance_simple_try.rb +4 -0
  199. data/try/features/relationships/relationships_performance_try.rb +4 -0
  200. data/try/features/relationships/relationships_performance_working_try.rb +4 -0
  201. data/try/features/relationships/relationships_try.rb +6 -4
  202. data/try/features/safe_dump/safe_dump_advanced_try.rb +4 -0
  203. data/try/features/safe_dump/safe_dump_try.rb +4 -0
  204. data/try/features/transient_fields/redacted_string_try.rb +2 -0
  205. data/try/features/transient_fields/refresh_reset_try.rb +3 -0
  206. data/try/features/transient_fields/simple_refresh_test.rb +3 -0
  207. data/try/features/transient_fields/single_use_redacted_string_try.rb +2 -0
  208. data/try/features/transient_fields/transient_fields_core_try.rb +4 -0
  209. data/try/features/transient_fields/transient_fields_integration_try.rb +4 -0
  210. data/try/integration/connection/fiber_context_preservation_try.rb +4 -0
  211. data/try/integration/connection/handler_constraints_try.rb +4 -0
  212. data/try/integration/connection/isolated_dbclient_try.rb +4 -0
  213. data/try/integration/connection/middleware_reconnect_try.rb +2 -0
  214. data/try/integration/connection/operation_mode_guards_try.rb +4 -0
  215. data/try/integration/connection/pipeline_fallback_integration_try.rb +3 -0
  216. data/try/integration/connection/pools_try.rb +4 -0
  217. data/try/integration/connection/responsibility_chain_tracking_try.rb +4 -0
  218. data/try/integration/connection/transaction_fallback_integration_try.rb +4 -0
  219. data/try/integration/connection/transaction_mode_permissive_try.rb +4 -0
  220. data/try/integration/connection/transaction_mode_strict_try.rb +4 -0
  221. data/try/integration/connection/transaction_mode_warn_try.rb +4 -0
  222. data/try/integration/connection/transaction_modes_try.rb +4 -0
  223. data/try/integration/conventional_inheritance_try.rb +4 -0
  224. data/try/integration/create_method_try.rb +4 -0
  225. data/try/integration/cross_component_try.rb +4 -0
  226. data/try/integration/data_types/datatype_pipelines_try.rb +9 -3
  227. data/try/integration/data_types/datatype_transactions_try.rb +17 -7
  228. data/try/integration/database_consistency_try.rb +4 -0
  229. data/try/integration/familia_extended_try.rb +4 -0
  230. data/try/integration/familia_members_methods_try.rb +4 -0
  231. data/try/integration/models/customer_safe_dump_try.rb +4 -0
  232. data/try/integration/models/customer_try.rb +7 -3
  233. data/try/integration/models/datatype_base_try.rb +4 -0
  234. data/try/integration/models/familia_object_try.rb +4 -0
  235. data/try/integration/persistence_operations_try.rb +4 -0
  236. data/try/integration/relationships_persistence_round_trip_try.rb +17 -14
  237. data/try/integration/save_methods_consistency_try.rb +241 -0
  238. data/try/integration/scenarios_try.rb +4 -0
  239. data/try/integration/secure_identifier_try.rb +4 -0
  240. data/try/integration/transaction_safety_core_try.rb +176 -0
  241. data/try/integration/transaction_safety_workflow_try.rb +291 -0
  242. data/try/integration/verifiable_identifier_try.rb +4 -0
  243. data/try/investigation/pipeline_routing/README.md +228 -0
  244. data/try/performance/benchmarks_try.rb +4 -0
  245. data/try/performance/transaction_safety_benchmark_try.rb +238 -0
  246. data/try/support/benchmarks/deserialization_benchmark.rb +3 -1
  247. data/try/support/benchmarks/deserialization_correctness_test.rb +3 -1
  248. data/try/support/debugging/cache_behavior_tracer.rb +4 -0
  249. data/try/support/debugging/debug_aad_process.rb +3 -0
  250. data/try/support/debugging/debug_concealed_internal.rb +3 -0
  251. data/try/support/debugging/debug_concealed_reveal.rb +3 -0
  252. data/try/support/debugging/debug_context_aad.rb +3 -0
  253. data/try/support/debugging/debug_context_simple.rb +3 -0
  254. data/try/support/debugging/debug_cross_context.rb +3 -0
  255. data/try/support/debugging/debug_database_load.rb +3 -0
  256. data/try/support/debugging/debug_encrypted_json_check.rb +3 -0
  257. data/try/support/debugging/debug_encrypted_json_step_by_step.rb +3 -0
  258. data/try/support/debugging/debug_exists_lifecycle.rb +3 -0
  259. data/try/support/debugging/debug_field_decrypt.rb +3 -0
  260. data/try/support/debugging/debug_fresh_cross_context.rb +3 -0
  261. data/try/support/debugging/debug_load_path.rb +3 -0
  262. data/try/support/debugging/debug_method_definition.rb +3 -0
  263. data/try/support/debugging/debug_method_resolution.rb +3 -0
  264. data/try/support/debugging/debug_minimal.rb +3 -0
  265. data/try/support/debugging/debug_provider.rb +3 -0
  266. data/try/support/debugging/debug_secure_behavior.rb +3 -0
  267. data/try/support/debugging/debug_string_class.rb +3 -0
  268. data/try/support/debugging/debug_test.rb +3 -0
  269. data/try/support/debugging/debug_test_design.rb +3 -0
  270. data/try/support/debugging/encryption_method_tracer.rb +4 -0
  271. data/try/support/debugging/provider_diagnostics.rb +4 -0
  272. data/try/support/helpers/test_cleanup.rb +4 -0
  273. data/try/support/helpers/test_helpers.rb +5 -0
  274. data/try/support/memory/memory_basic_test.rb +4 -0
  275. data/try/support/memory/memory_detailed_test.rb +4 -0
  276. data/try/support/memory/memory_search_for_string.rb +4 -0
  277. data/try/support/memory/test_actual_redactedstring_protection.rb +4 -0
  278. data/try/support/prototypes/atomic_saves_v1_context_proxy.rb +4 -0
  279. data/try/support/prototypes/atomic_saves_v2_connection_switching.rb +4 -0
  280. data/try/support/prototypes/atomic_saves_v3_connection_pool.rb +4 -0
  281. data/try/support/prototypes/atomic_saves_v4.rb +4 -0
  282. data/try/support/prototypes/lib/atomic_saves_v2_connection_switching_helpers.rb +4 -0
  283. data/try/support/prototypes/lib/atomic_saves_v3_connection_pool_helpers.rb +4 -0
  284. data/try/support/prototypes/pooling/configurable_stress_test.rb +4 -0
  285. data/try/support/prototypes/pooling/lib/atomic_saves_v3_connection_pool_helpers.rb +4 -0
  286. data/try/support/prototypes/pooling/lib/connection_pool_metrics.rb +4 -0
  287. data/try/support/prototypes/pooling/lib/connection_pool_stress_test.rb +4 -0
  288. data/try/support/prototypes/pooling/lib/connection_pool_threading_models.rb +4 -0
  289. data/try/support/prototypes/pooling/lib/visualize_stress_results.rb +4 -2
  290. data/try/support/prototypes/pooling/pool_siege.rb +4 -2
  291. data/try/support/prototypes/pooling/run_stress_tests.rb +4 -2
  292. data/try/thread_safety/README.md +496 -0
  293. data/try/thread_safety/class_connection_chain_race_try.rb +265 -0
  294. data/try/thread_safety/connection_chain_race_try.rb +148 -0
  295. data/try/thread_safety/encryption_manager_cache_race_try.rb +166 -0
  296. data/try/thread_safety/feature_registry_race_try.rb +226 -0
  297. data/try/thread_safety/fiber_pipeline_isolation_try.rb +235 -0
  298. data/try/thread_safety/fiber_transaction_isolation_try.rb +208 -0
  299. data/try/thread_safety/field_registration_race_try.rb +222 -0
  300. data/try/thread_safety/logger_initialization_race_try.rb +170 -0
  301. data/try/thread_safety/middleware_registration_race_try.rb +154 -0
  302. data/try/thread_safety/module_config_race_try.rb +175 -0
  303. data/try/thread_safety/secure_identifier_cache_race_try.rb +226 -0
  304. data/try/unit/core/autoloader_try.rb +4 -0
  305. data/try/unit/core/base_enhancements_try.rb +4 -0
  306. data/try/unit/core/connection_try.rb +4 -0
  307. data/try/unit/core/errors_try.rb +4 -0
  308. data/try/unit/core/extensions_try.rb +4 -0
  309. data/try/unit/core/familia_logger_try.rb +2 -0
  310. data/try/unit/core/familia_try.rb +4 -0
  311. data/try/unit/core/middleware_sampling_try.rb +335 -0
  312. data/try/unit/core/middleware_test_helpers_bug_try.rb +58 -0
  313. data/try/unit/core/middleware_thread_safety_try.rb +245 -0
  314. data/try/unit/core/middleware_try.rb +4 -0
  315. data/try/unit/core/settings_try.rb +4 -0
  316. data/try/unit/core/time_utils_try.rb +4 -0
  317. data/try/unit/core/tools_try.rb +4 -0
  318. data/try/unit/core/utils_try.rb +37 -0
  319. data/try/unit/data_types/boolean_try.rb +39 -22
  320. data/try/unit/data_types/counter_try.rb +4 -0
  321. data/try/unit/data_types/datatype_base_try.rb +4 -0
  322. data/try/unit/data_types/hash_try.rb +6 -2
  323. data/try/unit/data_types/list_try.rb +4 -0
  324. data/try/unit/data_types/lock_try.rb +4 -0
  325. data/try/unit/data_types/serialization_try.rb +386 -0
  326. data/try/unit/data_types/sorted_set_try.rb +4 -0
  327. data/try/unit/data_types/sorted_set_zadd_options_try.rb +4 -0
  328. data/try/unit/data_types/string_try.rb +4 -0
  329. data/try/unit/data_types/unsortedset_try.rb +4 -0
  330. data/try/unit/familia_resolve_class_try.rb +116 -0
  331. data/try/unit/horreum/auto_indexing_on_save_try.rb +5 -1
  332. data/try/unit/horreum/automatic_index_validation_try.rb +2 -0
  333. data/try/unit/horreum/base_try.rb +4 -0
  334. data/try/unit/horreum/class_methods_try.rb +4 -0
  335. data/try/unit/horreum/commands_try.rb +4 -0
  336. data/try/unit/horreum/defensive_initialization_try.rb +4 -0
  337. data/try/unit/horreum/destroy_related_fields_cleanup_try.rb +6 -1
  338. data/try/unit/horreum/enhanced_conflict_handling_try.rb +4 -0
  339. data/try/unit/horreum/field_categories_try.rb +4 -0
  340. data/try/unit/horreum/field_definition_try.rb +4 -0
  341. data/try/unit/horreum/initialization_try.rb +4 -0
  342. data/try/unit/horreum/json_type_preservation_try.rb +2 -0
  343. data/try/unit/horreum/optimized_loading_try.rb +156 -0
  344. data/try/unit/horreum/relations_try.rb +4 -0
  345. data/try/unit/horreum/serialization_persistent_fields_try.rb +4 -0
  346. data/try/unit/horreum/serialization_try.rb +4 -0
  347. data/try/unit/horreum/settings_try.rb +4 -0
  348. data/try/unit/horreum/unique_index_edge_cases_try.rb +4 -0
  349. data/try/unit/horreum/unique_index_guard_validation_try.rb +2 -0
  350. data/try/unit/middleware/database_command_counter_methods_try.rb +139 -0
  351. data/try/unit/middleware/database_logger_methods_try.rb +251 -0
  352. data/try/unit/refinements/dear_json_array_methods_try.rb +4 -0
  353. data/try/unit/refinements/dear_json_hash_methods_try.rb +4 -0
  354. data/try/unit/refinements/time_literals_numeric_methods_try.rb +4 -0
  355. data/try/unit/refinements/time_literals_string_methods_try.rb +4 -0
  356. data/try/unit/thread_safety_monitor_try.rb +149 -0
  357. metadata +69 -17
  358. data/.github/workflows/code-quality.yml +0 -138
  359. data/changelog.d/20251011_012003_delano_159_datatype_transaction_pipeline_support.rst +0 -91
  360. data/changelog.d/20251011_203905_delano_next.rst +0 -30
  361. data/changelog.d/20251011_212633_delano_next.rst +0 -13
  362. data/changelog.d/20251011_221253_delano_next.rst +0 -26
  363. data/docs/archive/FAMILIA_RELATIONSHIPS.md +0 -210
  364. data/docs/archive/FAMILIA_TECHNICAL.md +0 -823
  365. data/docs/archive/FAMILIA_UPDATE.md +0 -226
  366. data/docs/archive/README.md +0 -64
  367. data/docs/archive/api-reference.md +0 -333
  368. data/docs/guides/core-field-system.md +0 -806
  369. data/docs/guides/implementation.md +0 -276
  370. data/docs/guides/security-model.md +0 -183
@@ -0,0 +1,69 @@
1
+ # lib/familia/features/relationships/participation_membership.rb
2
+ #
3
+ # frozen_string_literal: true
4
+
5
+ module Familia
6
+ module Features
7
+ module Relationships
8
+ #
9
+ # ParticipationMembership
10
+ #
11
+ # Represents runtime snapshot of a participant's membership in a target collection.
12
+ # Returned by current_participations to provide a type-safe, structured view
13
+ # of actual participation state.
14
+ #
15
+ # @note This represents what currently exists in Redis, not just configuration.
16
+ # See ParticipationRelationship for static configuration metadata.
17
+ #
18
+ # @example
19
+ # membership = user.current_participations.first
20
+ # membership.target_class # => "Team"
21
+ # membership.target_id # => "team123"
22
+ # membership.collection_name # => :members
23
+ # membership.type # => :sorted_set
24
+ # membership.score # => 1762554020.05
25
+ #
26
+ ParticipationMembership = Data.define(
27
+ :target_class, # String - class name (e.g., "Customer")
28
+ :target_id, # String - target instance identifier
29
+ :collection_name, # Symbol - collection name (e.g., :domains)
30
+ :type, # Symbol - collection type (:sorted_set, :set, :list)
31
+ :score, # Float - optional, for sorted_set only
32
+ :decoded_score, # Hash - optional, decoded score data
33
+ :position # Integer - optional, for list only
34
+ ) do
35
+ # Check if this membership is a sorted set
36
+ # @return [Boolean]
37
+ def sorted_set?
38
+ type == :sorted_set
39
+ end
40
+
41
+ # Check if this membership is a set
42
+ # @return [Boolean]
43
+ def set?
44
+ type == :set
45
+ end
46
+
47
+ # Check if this membership is a list
48
+ # @return [Boolean]
49
+ def list?
50
+ type == :list
51
+ end
52
+
53
+ # Get the target instance (requires loading from database)
54
+ # @return [Familia::Horreum, nil] the loaded target instance
55
+ def target_instance
56
+ return nil unless target_class
57
+
58
+ # Resolve class from string name
59
+ # Only rescue NameError (class doesn't exist), not all exceptions
60
+ klass = Object.const_get(target_class)
61
+ klass.find_by_id(target_id)
62
+ rescue NameError
63
+ # Target class doesn't exist or isn't loaded
64
+ nil
65
+ end
66
+ end
67
+ end
68
+ end
69
+ end
@@ -1,4 +1,6 @@
1
1
  # lib/familia/features/relationships/participation_relationship.rb
2
+ #
3
+ # frozen_string_literal: true
2
4
 
3
5
  module Familia
4
6
  module Features
@@ -10,20 +12,46 @@ module Familia
10
12
  # Used to configure code generation and runtime behavior for participates_in
11
13
  # and class_participates_in declarations.
12
14
  #
15
+ # @note target_class is resolved once at definition time for performance.
16
+ # Use _original_target for debugging/introspection to see what was passed.
17
+ #
13
18
  ParticipationRelationship = Data.define(
14
- :target_class, # Class object that owns the collection
19
+ :_original_target, # Original Symbol/String/Class as passed to participates_in
20
+ :target_class, # Resolved Class object (e.g., User class, not :User symbol)
15
21
  :collection_name, # Symbol name of the collection (e.g., :members, :domains)
16
22
  :score, # Proc/Symbol/nil - score calculator for sorted sets
17
23
  :type, # Symbol - collection type (:sorted_set, :set, :list)
18
- :bidirectional, # Boolean - whether to generate reverse methods
24
+ :bidirectional, # Boolean/Symbol - whether to generate reverse methods
19
25
  ) do
26
+ # Get a unique key for this participation relationship
27
+ # Useful for comparisons and hash keys
20
28
  #
21
- # Get the normalized config name for the target class
29
+ # @return [String] unique identifier in format "TargetClass:collection_name"
30
+ def unique_key
31
+ Familia.join(target_class_base, collection_name)
32
+ end
33
+
34
+ # Get the base class name without namespace
35
+ # Handles anonymous class wrappers like "#<Class:0x123>::SymbolResolutionCustomer"
22
36
  #
23
- # @return [String] The config name (e.g., "user", "perf_test_customer")
37
+ # @return [String] base class name (e.g., "Customer")
38
+ def target_class_base
39
+ target_class.name.split('::').last
40
+ end
41
+
42
+ # Check if this relationship matches the given target and collection
43
+ # Handles namespace-agnostic class comparison
24
44
  #
25
- def target_class_config_name
26
- target_class.config_name
45
+ # @param comparison_target [Class, String, Symbol] target to compare against
46
+ # @param comparison_collection [Symbol, String] collection name to compare
47
+ # @return [Boolean] true if both target and collection match
48
+ def matches?(comparison_target, comparison_collection)
49
+ # Normalize comparison target to base class name
50
+ comparison_target = comparison_target.name if comparison_target.is_a?(Class)
51
+ comparison_target_base = comparison_target.to_s.split('::').last
52
+
53
+ target_class_base == comparison_target_base &&
54
+ collection_name == comparison_collection.to_sym
27
55
  end
28
56
  end
29
57
  end
@@ -1,4 +1,6 @@
1
1
  # lib/familia/features/relationships/score_encoding.rb
2
+ #
3
+ # frozen_string_literal: true
2
4
 
3
5
  module Familia
4
6
  module Features
@@ -1,4 +1,6 @@
1
1
  # lib/familia/features/relationships.rb
2
+ #
3
+ # frozen_string_literal: true
2
4
 
3
5
  require 'securerandom'
4
6
  require_relative 'relationships/score_encoding'
@@ -84,7 +86,7 @@ module Familia
84
86
 
85
87
  # Feature initialization
86
88
  def self.included(base)
87
- Familia.ld "[#{base}] Relationships included"
89
+ Familia.debug "[#{base}] Relationships included"
88
90
  base.extend ModelClassMethods
89
91
  base.include ModelInstanceMethods
90
92
 
@@ -157,7 +159,7 @@ module Familia
157
159
  def create_temp_key(base_name, ttl = 300)
158
160
  timestamp = Familia.now.to_i
159
161
  random_suffix = SecureRandom.hex(3)
160
- temp_key = "temp:#{base_name}:#{timestamp}:#{random_suffix}"
162
+ temp_key = Familia.join('temp', base_name, timestamp, random_suffix)
161
163
 
162
164
  # UnsortedSet immediate expiry to ensure cleanup even if operation fails
163
165
  if respond_to?(:dbclient)
@@ -256,7 +258,7 @@ module Familia
256
258
  def create_temp_key(base_name, ttl = 300)
257
259
  timestamp = Familia.now.to_i
258
260
  random_suffix = SecureRandom.hex(3)
259
- temp_key = "temp:#{base_name}:#{timestamp}:#{random_suffix}"
261
+ temp_key = Familia.join('temp', base_name, timestamp, random_suffix)
260
262
 
261
263
  # UnsortedSet immediate expiry to ensure cleanup even if operation fails
262
264
  dbclient.expire(temp_key, ttl)
@@ -1,4 +1,6 @@
1
1
  # lib/familia/features/safe_dump.rb
2
+ #
3
+ # frozen_string_literal: true
2
4
 
3
5
  #
4
6
  # Class instance variables are used here for configuration. These are set
@@ -1,4 +1,6 @@
1
1
  # lib/familia/features/transient_fields/redacted_string.rb
2
+ #
3
+ # frozen_string_literal: true
2
4
 
3
5
  # RedactedString
4
6
  #
@@ -1,4 +1,6 @@
1
1
  # lib/familia/features/transient_fields/single_use_redacted_string.rb
2
+ #
3
+ # frozen_string_literal: true
2
4
 
3
5
  require_relative 'redacted_string'
4
6
 
@@ -1,4 +1,6 @@
1
1
  # lib/familia/features/transient_fields/transient_field_type.rb
2
+ #
3
+ # frozen_string_literal: true
2
4
 
3
5
  require 'familia/field_type'
4
6
 
@@ -80,7 +82,7 @@ module Familia
80
82
  #
81
83
  def define_fast_writer(_klass)
82
84
  # No fast writer for transient fields since they're not persisted
83
- Familia.ld "[TransientFieldType] Skipping fast writer for transient field: #{@name}"
85
+ Familia.debug "[TransientFieldType] Skipping fast writer for transient field: #{@name}"
84
86
  nil
85
87
  end
86
88
 
@@ -117,7 +119,7 @@ module Familia
117
119
  #
118
120
  def serialize(_value, _record = nil)
119
121
  # Transient fields should never be serialized
120
- Familia.ld "[TransientFieldType] WARNING: serialize called on transient field #{@name}"
122
+ Familia.debug "[TransientFieldType] WARNING: serialize called on transient field #{@name}"
121
123
  nil
122
124
  end
123
125
 
@@ -132,7 +134,7 @@ module Familia
132
134
  #
133
135
  def deserialize(_value, _record = nil)
134
136
  # Transient fields should never be deserialized
135
- Familia.ld "[TransientFieldType] WARNING: deserialize called on transient field #{@name}"
137
+ Familia.debug "[TransientFieldType] WARNING: deserialize called on transient field #{@name}"
136
138
  nil
137
139
  end
138
140
  end
@@ -1,4 +1,6 @@
1
1
  # lib/familia/features/transient_fields.rb
2
+ #
3
+ # frozen_string_literal: true
2
4
 
3
5
  require_relative 'transient_fields/redacted_string'
4
6
  require_relative 'transient_fields/transient_field_type'
@@ -1,4 +1,6 @@
1
1
  # lib/familia/features.rb
2
+ #
3
+ # frozen_string_literal: true
2
4
 
3
5
  # Load the Autoloader first, then use it to load all other features
4
6
  require_relative 'features/autoloader'
@@ -1,4 +1,6 @@
1
1
  # lib/familia/field_type.rb
2
+ #
3
+ # frozen_string_literal: true
2
4
 
3
5
  module Familia
4
6
  # Base class for all field types in Familia
@@ -152,7 +154,7 @@ module Familia
152
154
 
153
155
  # Convert value for database storage
154
156
  prepared = serialize_value(val)
155
- Familia.ld "[FieldType#define_fast_writer] #{fast_method_name} val: #{val.class} prepared: #{prepared.class}"
157
+ Familia.debug "[FieldType#define_fast_writer] #{fast_method_name} val: #{val.class} prepared: #{prepared.class}"
156
158
 
157
159
  # Use the setter method to update instance variable
158
160
  send(:"#{method_name}=", val) if method_name
@@ -1,4 +1,6 @@
1
1
  # lib/familia/horreum/connection.rb
2
+ #
3
+ # frozen_string_literal: true
2
4
 
3
5
  module Familia
4
6
  class Horreum
@@ -24,10 +26,19 @@ module Familia
24
26
  # 2. DefaultConnectionHandler - Horreum model class-level @dbclient
25
27
  # 3. GlobalFallbackHandler - Familia.dbclient(uri || logical_database) (global fallback)
26
28
  #
29
+ # Thread-safe lazy initialization using double-checked locking to ensure
30
+ # only a single connection chain is built even under high concurrent load.
31
+ #
27
32
  # @return [Redis] the Database connection instance.
28
33
  #
29
34
  def dbclient(uri = nil)
30
- @class_connection_chain ||= build_connection_chain
35
+ # Fast path: return existing chain if already initialized
36
+ return @class_connection_chain.handle(uri) if @class_connection_chain
37
+
38
+ # Slow path: thread-safe initialization
39
+ @class_connection_chain_mutex.synchronize do
40
+ @class_connection_chain ||= build_connection_chain
41
+ end
31
42
  @class_connection_chain.handle(uri)
32
43
  end
33
44
 
@@ -268,6 +279,11 @@ module Familia
268
279
  .add_handler(@cached_connection_handler)
269
280
  .add_handler(@create_connection_handler)
270
281
  end
282
+
283
+ # Thread-safe mutex initialization when module is extended
284
+ def self.extended(base)
285
+ base.instance_variable_set(:@class_connection_chain_mutex, Mutex.new)
286
+ end
271
287
  end
272
288
  end
273
289
  end
@@ -1,4 +1,6 @@
1
1
  # lib/familia/horreum/database_commands.rb
2
+ #
3
+ # frozen_string_literal: true
2
4
 
3
5
  module Familia
4
6
  # Familia::Horreum
@@ -261,15 +263,20 @@ module Familia
261
263
  #
262
264
  # | Scenario | Use | Why |
263
265
  # |----------|-----|-----|
264
- # | Check if exists, then create | WATCH | Must prevent duplicate creation |
266
+ # | First-one-wins / idempotency | SET NX | Atomic claim, no read needed |
267
+ # | Distributed lock acquisition | SET NX EX | Claim with automatic expiry |
265
268
  # | Read value, update conditionally | WATCH | Decision depends on current state |
266
269
  # | Compare-and-swap operations | WATCH | Need optimistic locking |
267
270
  # | Version-based updates | WATCH | Must detect concurrent changes |
271
+ # | Status transitions (pending→processing) | WATCH | Must verify current state |
268
272
  # | Batch field updates | MULTI only | No conditional logic |
269
273
  # | Increment + timestamp together | MULTI only | Concurrent increments OK |
270
274
  # | Save object atomically | MULTI only | Just need atomicity |
271
275
  # | Update indexes with save | MULTI only | No state checking needed |
272
276
  #
277
+ # If you don't need to read before deciding, WATCH adds complexity
278
+ # without benefit. SET NX handles the "claim" pattern in one atomic shot.
279
+ #
273
280
  # @param suffix_override [String, nil] Optional suffix override
274
281
  # @return [String] 'OK' on success
275
282
  def watch(...)
@@ -1,4 +1,6 @@
1
1
  # lib/familia/horreum/definition.rb
2
+ #
3
+ # frozen_string_literal: true
2
4
 
3
5
  require_relative 'settings'
4
6
 
@@ -98,7 +100,7 @@ module Familia
98
100
  @current_field_group = nil
99
101
  end
100
102
  else
101
- Familia.ld "[field_group] Created field group :#{name} but no block given" if Familia.debug?
103
+ Familia.debug "[field_group] Created field group :#{name} but no block given"
102
104
  end
103
105
 
104
106
  field_groups[name.to_sym]
@@ -124,7 +126,10 @@ module Familia
124
126
  # ]
125
127
  #
126
128
  def field_groups
127
- @field_groups ||= {}
129
+ @field_groups_mutex ||= Familia::ThreadSafety::InstrumentedMutex.new('field_groups')
130
+ @field_groups || @field_groups_mutex.synchronize do
131
+ @field_groups ||= {}
132
+ end
128
133
  end
129
134
 
130
135
  # Sets or retrieves the unique identifier field for the class.
@@ -215,8 +220,10 @@ module Familia
215
220
  # Returns the list of field names defined for the class in the order
216
221
  # that they were defined. i.e. `field :a; field :b; fields => [:a, :b]`.
217
222
  def fields
218
- @fields ||= []
219
- @fields
223
+ @fields_mutex ||= Familia::ThreadSafety::InstrumentedMutex.new('fields')
224
+ @fields || @fields_mutex.synchronize do
225
+ @fields ||= []
226
+ end
220
227
  end
221
228
 
222
229
  def class_related_fields
@@ -235,7 +242,10 @@ module Familia
235
242
 
236
243
  # Storage for field type instances
237
244
  def field_types
238
- @field_types ||= {}
245
+ @field_types_mutex ||= Familia::ThreadSafety::InstrumentedMutex.new('field_types')
246
+ @field_types || @field_types_mutex.synchronize do
247
+ @field_types ||= {}
248
+ end
239
249
  end
240
250
 
241
251
  # Returns a hash mapping field names to method names for backward compatibility
@@ -476,7 +486,7 @@ module Familia
476
486
 
477
487
  # Convert the provided value to a format suitable for Database storage.
478
488
  prepared = serialize_value(val)
479
- Familia.ld "[define_fast_writer_method] #{fast_method_name} val: #{val.class} prepared: #{prepared.class}"
489
+ Familia.debug "[define_fast_writer_method] #{fast_method_name} val: #{val.class} prepared: #{prepared.class}"
480
490
 
481
491
  # Use the existing accessor method to set the attribute value.
482
492
  send :"#{method_name}=", val