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
data/CLAUDE.md CHANGED
@@ -53,7 +53,7 @@ Add changelog fragment with each user-facing or documented change (optional but
53
53
  ### Known Issues & Quirks
54
54
  - **Reserved Keywords**: Cannot use `ttl`, `db`, `valkey`, `redis` as field names - use prefixed alternatives
55
55
  - **Empty Identifiers**: Cause stack overflow in key generation - validate before operations
56
- - **Connection Pool Race Conditions**: Thread safety issues under high concurrency
56
+ - **Lazy Initialization Races**: Connection chains and field collections use lazy initialization without synchronization (generally safe due to Ruby GIL, but not guaranteed)
57
57
 
58
58
  ### Debugging
59
59
  - **Database command logging**: You can request real-time Database command monitoring from the user
@@ -191,3 +191,30 @@ end
191
191
  **Memory Efficiency**: Only non-nil values are stored in keystore database to optimize memory usage.
192
192
 
193
193
  **Thread Safety**: Data types are frozen after instantiation to ensure immutability.
194
+
195
+ ## Thread Safety Considerations
196
+
197
+ ### Current Thread Safety Status (as of 2025-10-21)
198
+
199
+ Familia has **good thread safety** for standard multi-threaded environments:
200
+
201
+ ### Testing Thread Safety
202
+
203
+ Thread safety tests are available in `try/thread_safety/`:
204
+ - **100% passing** (56/56 tests)
205
+ - **CyclicBarrier pattern** for maximum contention testing
206
+ - **Test execution**: ~300ms for full suite with 1,000+ concurrent operations
207
+ - **Production monitoring**: 10/10 monitoring tests passing
208
+
209
+ Run thread safety tests:
210
+ ```bash
211
+ bundle exec try --agent try/thread_safety/
212
+ bundle exec try --agent try/unit/thread_safety_monitor_try.rb
213
+ ```
214
+
215
+ ### Best Practices for Thread-Safe Usage
216
+
217
+ 1. **Configure Once at Startup**: Module-level configuration should be set before threads spawn
218
+ 2. **Use Immutable DataTypes**: Leverage the fact that DataType instances are frozen
219
+ 3. **Test Under Concurrency**: Use the patterns in `try/thread_safety/` to verify thread safety
220
+ 4. **Enable Production Monitoring**: Use `Familia.start_monitoring!` to track contention in production
data/Gemfile CHANGED
@@ -9,7 +9,7 @@ group :test do
9
9
  gem 'ruby-prof'
10
10
  gem 'stackprof'
11
11
  gem 'timecop', require: false
12
- gem 'tryouts', '~> 3.6.0', require: false
12
+ gem 'tryouts', '~> 3.7.1', require: false
13
13
  end
14
14
 
15
15
  group :development, :test do
data/Gemfile.lock CHANGED
@@ -1,8 +1,9 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- familia (2.0.0.pre19)
4
+ familia (2.0.0.pre21)
5
5
  benchmark (~> 0.4)
6
+ concurrent-ruby (~> 1.3)
6
7
  connection_pool (~> 2.5)
7
8
  csv (~> 3.3)
8
9
  logger (~> 1.7)
@@ -21,7 +22,7 @@ GEM
21
22
  concurrent-ruby (1.3.5)
22
23
  connection_pool (2.5.3)
23
24
  csv (3.3.5)
24
- date (3.4.1)
25
+ date (3.5.0)
25
26
  debug (1.11.0)
26
27
  irb (~> 1.10)
27
28
  reline (>= 0.3.8)
@@ -55,11 +56,11 @@ GEM
55
56
  dry-inflector (~> 1.0)
56
57
  dry-logic (~> 1.4)
57
58
  zeitwerk (~> 2.6)
58
- erb (5.0.2)
59
+ erb (5.1.3)
59
60
  ffi (1.17.2)
60
61
  ffi (1.17.2-arm64-darwin)
61
62
  io-console (0.8.1)
62
- irb (1.15.2)
63
+ irb (1.15.3)
63
64
  pp (>= 0.6.0)
64
65
  rdoc (>= 4.0.0)
65
66
  reline (>= 0.4.2)
@@ -67,7 +68,7 @@ GEM
67
68
  language_server-protocol (3.17.0.5)
68
69
  lint_roller (1.1.0)
69
70
  logger (1.7.0)
70
- minitest (5.25.5)
71
+ minitest (5.26.0)
71
72
  oj (3.16.11)
72
73
  bigdecimal (>= 3.0)
73
74
  ostruct (>= 0.2)
@@ -78,10 +79,10 @@ GEM
78
79
  racc
79
80
  pastel (0.8.0)
80
81
  tty-color (~> 0.5)
81
- pp (0.6.2)
82
+ pp (0.6.3)
82
83
  prettyprint
83
84
  prettyprint (0.2.0)
84
- prism (1.5.2)
85
+ prism (1.6.0)
85
86
  psych (5.2.6)
86
87
  date
87
88
  stringio
@@ -91,9 +92,10 @@ GEM
91
92
  ffi (~> 1)
92
93
  rbs (3.9.5)
93
94
  logger
94
- rdoc (6.14.2)
95
+ rdoc (6.15.1)
95
96
  erb
96
97
  psych (>= 4.0.0)
98
+ tsort
97
99
  redcarpet (3.6.1)
98
100
  redis (5.4.1)
99
101
  redis-client (>= 0.22.0)
@@ -109,19 +111,19 @@ GEM
109
111
  reline (0.6.2)
110
112
  io-console (~> 0.5)
111
113
  rexml (3.4.1)
112
- rspec (3.13.1)
114
+ rspec (3.13.2)
113
115
  rspec-core (~> 3.13.0)
114
116
  rspec-expectations (~> 3.13.0)
115
117
  rspec-mocks (~> 3.13.0)
116
- rspec-core (3.13.5)
118
+ rspec-core (3.13.6)
117
119
  rspec-support (~> 3.13.0)
118
120
  rspec-expectations (3.13.5)
119
121
  diff-lcs (>= 1.2.0, < 2.0)
120
122
  rspec-support (~> 3.13.0)
121
- rspec-mocks (3.13.5)
123
+ rspec-mocks (3.13.7)
122
124
  diff-lcs (>= 1.2.0, < 2.0)
123
125
  rspec-support (~> 3.13.0)
124
- rspec-support (3.13.4)
126
+ rspec-support (3.13.6)
125
127
  rubocop (1.81.1)
126
128
  json (~> 2.3)
127
129
  language_server-protocol (~> 3.17.0.2)
@@ -154,15 +156,16 @@ GEM
154
156
  stackprof (0.2.27)
155
157
  stringio (3.1.7)
156
158
  timecop (0.9.10)
157
- tryouts (3.6.0)
158
- concurrent-ruby (~> 1.0)
159
+ tryouts (3.7.1)
160
+ concurrent-ruby (~> 1.0, < 2)
159
161
  irb
160
162
  minitest (~> 5.0)
161
163
  pastel (~> 0.8)
162
164
  prism (~> 1.0)
163
- rspec (~> 3.0)
165
+ rspec (>= 3.0, < 5.0)
164
166
  tty-cursor (~> 0.7)
165
167
  tty-screen (~> 0.8)
168
+ tsort (0.2.0)
166
169
  tty-color (0.6.0)
167
170
  tty-cursor (0.7.1)
168
171
  tty-screen (0.8.2)
@@ -192,8 +195,8 @@ DEPENDENCIES
192
195
  ruby-prof
193
196
  stackprof
194
197
  timecop
195
- tryouts (~> 3.6.0)
198
+ tryouts (~> 3.7.1)
196
199
  yard (~> 0.9)
197
200
 
198
201
  BUNDLED WITH
199
- 2.6.2
202
+ 2.7.2
data/bin/try ADDED
@@ -0,0 +1,16 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ #
5
+ # This file was generated by Bundler.
6
+ #
7
+ # The application 'try' is installed as part of a gem, and
8
+ # this file is here to facilitate running it.
9
+ #
10
+
11
+ ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../Gemfile", __dir__)
12
+
13
+ require "rubygems"
14
+ require "bundler/setup"
15
+
16
+ load Gem.bin_path("tryouts", "try")
data/bin/tryouts ADDED
@@ -0,0 +1,16 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ #
5
+ # This file was generated by Bundler.
6
+ #
7
+ # The application 'tryouts' is installed as part of a gem, and
8
+ # this file is here to facilitate running it.
9
+ #
10
+
11
+ ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../Gemfile", __dir__)
12
+
13
+ require "rubygems"
14
+ require "bundler/setup"
15
+
16
+ load Gem.bin_path("tryouts", "tryouts")
@@ -0,0 +1,66 @@
1
+ .. Added
2
+ .. -----
3
+
4
+ .. Changed
5
+ .. -------
6
+
7
+ - **ExternalIdentifier Format Flexibility**: The `external_identifier` feature now supports customizable format templates via the `format` option. This allows you to control the entire format of generated external IDs, including the prefix, separator, and overall structure.
8
+
9
+ **Default format** (unchanged behavior):
10
+
11
+ .. code-block:: ruby
12
+
13
+ class User < Familia::Horreum
14
+ feature :external_identifier
15
+ end
16
+ user.extid # => "ext_abc123def456ghi789"
17
+
18
+ **Custom format with different prefix**:
19
+
20
+ .. code-block:: ruby
21
+
22
+ class Customer < Familia::Horreum
23
+ feature :external_identifier, format: 'cust_%{id}'
24
+ end
25
+ customer.extid # => "cust_abc123def456ghi789"
26
+
27
+ **Custom format with different separator**:
28
+
29
+ .. code-block:: ruby
30
+
31
+ class APIKey < Familia::Horreum
32
+ feature :external_identifier, format: 'api-%{id}'
33
+ end
34
+ key.extid # => "api-abc123def456ghi789"
35
+
36
+ **Custom format without traditional prefix**:
37
+
38
+ .. code-block:: ruby
39
+
40
+ class Resource < Familia::Horreum
41
+ feature :external_identifier, format: 'v2/%{id}'
42
+ end
43
+ resource.extid # => "v2/abc123def456ghi789"
44
+
45
+ The `format` option accepts a Ruby format string with the `%{id}` placeholder for the generated identifier. The default format is `'ext_%{id}'`. This provides complete flexibility for various ID formatting needs including different prefixes, separators (underscore, hyphen, slash), URL paths, or no prefix at all.
46
+
47
+ .. Deprecated
48
+ .. ----------
49
+
50
+ .. Removed
51
+ .. -------
52
+
53
+ .. Fixed
54
+ .. -----
55
+
56
+ .. Security
57
+ .. --------
58
+
59
+ .. Documentation
60
+ .. -------------
61
+
62
+ .. AI Assistance
63
+ .. -------------
64
+
65
+ - **Design Review**: Claude Code provided analysis of the current implementation and recommended several idiomatic Ruby approaches for format flexibility, ultimately suggesting the format template pattern using Ruby's native string formatting.
66
+ - **Implementation**: Claude Code implemented the format template feature including code changes, test cases, and documentation updates.
@@ -0,0 +1,44 @@
1
+ .. A new scriv changelog fragment.
2
+ ..
3
+ .. Uncomment the section that is right (remove the leading dots).
4
+ .. For top level release notes, leave all the headers commented out.
5
+ ..
6
+ Added
7
+ -----
8
+
9
+ - Bidirectional reverse collection methods for ``participates_in`` with ``_instances`` suffix (e.g., ``user.project_team_instances``, ``user.project_team_ids``). Supports union behavior for multiple collections and custom naming via ``as:`` parameter. Closes #179.
10
+
11
+ .. Changed
12
+ .. -------
13
+ ..
14
+ .. - A bullet item for the Changed category.
15
+ ..
16
+ .. Deprecated
17
+ .. ----------
18
+ ..
19
+ .. - A bullet item for the Deprecated category.
20
+ ..
21
+ .. Removed
22
+ .. -------
23
+ ..
24
+ .. - A bullet item for the Removed category.
25
+ ..
26
+ .. Fixed
27
+ .. -----
28
+ ..
29
+ .. - A bullet item for the Fixed category.
30
+ ..
31
+ .. Security
32
+ .. --------
33
+ ..
34
+ .. - A bullet item for the Security category.
35
+ ..
36
+ .. Documentation
37
+ .. -------------
38
+ ..
39
+ .. - A bullet item for the Documentation category.
40
+ ..
41
+ AI Assistance
42
+ -------------
43
+
44
+ - Claude Opus 4 assisted with implementation of bidirectional participation relationships using ``_instances`` suffix pattern. Pivoted from initial dry-inflector pluralization approach based on feedback.
@@ -0,0 +1,20 @@
1
+ .. Fixed mutex race conditions in thread safety implementation
2
+ ..
3
+
4
+ Fixed
5
+ -----
6
+
7
+ - Fixed critical race condition in mutex initialization for connection chain lazy loading. The mutex itself was being lazily initialized with ``||=``, which is not atomic and could result in multiple threads creating different mutex instances, defeating synchronization. Changed to eager initialization via ``Connection.included`` hook. (`lib/familia/horreum/connection.rb`)
8
+
9
+ - Fixed critical race condition in mutex initialization for logger lazy loading. Similar to connection chain issue, the logger mutex was lazily initialized with ``||=``. Changed to eager initialization at module definition time. (`lib/familia/logging.rb`)
10
+
11
+ - Fixed logger assignment atomicity issue where ``Familia.logger=`` set ``DatabaseLogger.logger`` outside the mutex synchronization block, potentially causing ``Familia.logger`` and ``DatabaseLogger.logger`` to be temporarily out of sync during concurrent access. Moved ``DatabaseLogger.logger`` assignment inside the synchronization block. (`lib/familia/logging.rb`)
12
+
13
+ - Added explicit return statement to ``Familia.logger`` method for robustness against future refactoring. (`lib/familia/logging.rb`)
14
+
15
+ AI Assistance
16
+ -------------
17
+
18
+ - Code review analysis to identify critical race conditions in mutex initialization
19
+ - Implementation of proper eager mutex initialization patterns
20
+ - Test file updates to reflect new initialization approach
@@ -0,0 +1,91 @@
1
+ .. Added
2
+ .. -----
3
+
4
+ .. Changed
5
+ .. -------
6
+
7
+ .. Deprecated
8
+ .. ----------
9
+
10
+ .. Removed
11
+ .. -------
12
+
13
+ .. Fixed
14
+ .. -----
15
+
16
+ - **Participation Relationships with Symbol/String Target Classes**: Fixed four bugs that occurred when calling `participates_in` with a Symbol or String target class instead of a Class object.
17
+
18
+ **Bug 1 - NoMethodError during relationship definition**:
19
+
20
+ The error was: ``private method 'member_by_config_name' called for module Familia``.
21
+
22
+ **Background**: The `participates_in` method supports flexible target class specifications:
23
+
24
+ .. code-block:: ruby
25
+
26
+ class Domain < Familia::Horreum
27
+ # All three forms should work:
28
+ participates_in Customer, :domains # Class object (always worked)
29
+ participates_in :Customer, :domains # Symbol (was broken)
30
+ participates_in 'Customer', :domains # String (was broken)
31
+ end
32
+
33
+ **Root Cause**: The method had redundant class resolution code that directly called the private `Familia.member_by_config_name` method instead of using the public `Familia.resolve_class` API.
34
+
35
+ **Solution**: Removed the redundant resolution code and now uses the already-resolved class from the public API, simplifying the implementation and fixing the visibility issue.
36
+
37
+ **Bug 2 - NoMethodError in current_participations**:
38
+
39
+ When calling `current_participations` on objects that used Symbol/String target classes, it would fail with ``undefined method 'familia_name' for Symbol``.
40
+
41
+ **Root Cause**: The `current_participations` method was calling `.familia_name` on `config.target_class`, which stores the original Symbol/String value passed to `participates_in`.
42
+
43
+ **Solution**: Use the resolved `target_class` variable instead of the stored config value. The resolved class is already available from the `Familia.resolve_class` call earlier in the method.
44
+
45
+ **Bug 3 - NoMethodError in target_class_config_name**:
46
+
47
+ When calling `current_participations`, the internal `target_class_config_name` method would fail with ``undefined method 'config_name' for Symbol``.
48
+
49
+ **Root Cause**: The `ParticipationRelationship.target_class_config_name` method was calling `.config_name` directly on the stored `target_class` value, which could be a Symbol or String.
50
+
51
+ **Solution**: Resolve the target class before calling `config_name` by using `Familia.resolve_class(target_class)`, which handles all input types (Class, Symbol, String) correctly.
52
+
53
+ **Bug 4 - Confusing error when target class not loaded**:
54
+
55
+ When the target class hasn't been loaded yet (load order issue), the error was: ``undefined method 'method_defined?' for nil``.
56
+
57
+ **Root Cause**: When `Familia.resolve_class` returns `nil` (because the target class isn't registered in `Familia.members` yet), the code would pass `nil` to `TargetMethods::Builder.build`, which then failed with a confusing error message that didn't explain the actual problem.
58
+
59
+ **Solution**: Added explicit nil check after `resolve_class` with a detailed ArgumentError that:
60
+
61
+ - Clearly states which target class couldn't be resolved
62
+ - Lists the three most common causes (load order, typo, not inheriting from Horreum)
63
+ - Shows all currently registered Familia classes for debugging
64
+ - Provides a clear solution for fixing the load order
65
+
66
+ **Impact**: Projects using Symbol or String target classes in `participates_in` declarations will now work correctly throughout the entire lifecycle, including relationship definition, method generation, and participation queries. When there's a load order issue or typo, developers get a clear, actionable error message instead of a confusing nil error. This pattern is common when avoiding circular dependencies or when target classes are defined in different files.
67
+
68
+ .. Security
69
+ .. --------
70
+
71
+ .. Documentation
72
+ .. -------------
73
+
74
+ .. AI Assistance
75
+ .. -------------
76
+
77
+ - **Root Cause Analysis**: Claude Code analyzed the error stack trace from the implementing project and identified that a private method was being called as a public method from outside the Familia module.
78
+ - **Fix Implementation**: Claude Code identified redundant class resolution code and simplified it to use the already-resolved class from the public API.
79
+ - **Test Coverage**: Claude Code created comprehensive regression tests including:
80
+
81
+ - Feature-level tests for Symbol/String target class resolution in participation relationships
82
+ - Unit tests for the `Familia.resolve_class` public API
83
+ - Edge case coverage for case-insensitive resolution and modularized classes
84
+
85
+ - **Second Bug Discovery**: During test execution, Claude Code discovered a related bug in `current_participations` that was also failing with Symbol/String target classes. The test coverage revealed that `.familia_name` was being called on the unresolved config value instead of the resolved class instance.
86
+
87
+ - **Third Bug Discovery**: Further test execution revealed another Symbol/String bug in `target_class_config_name`, where `.config_name` was being called directly on Symbol/String values. This was fixed by resolving the class first using `Familia.resolve_class`.
88
+
89
+ - **Test Coverage Refinement**: Claude Code identified and removed unrealistic test cases (all-uppercase, all-lowercase class names) that don't occur in real Ruby code and don't work with the `snake_case` method's design. Updated tests to focus on realistic naming conventions: PascalCase and snake_case, with clear documentation explaining why certain formats aren't supported.
90
+
91
+ - **Fourth Bug Discovery**: After merging to main, the implementing project revealed a load order issue where `Familia.resolve_class` returned `nil`, causing a confusing "undefined method for nil" error. Claude Code added explicit error handling with a detailed, actionable error message that helps developers quickly identify and fix load order issues, typos, or inheritance problems.
@@ -0,0 +1,94 @@
1
+ .. Added
2
+ .. -----
3
+
4
+ - **Pipelined Bulk Loading Methods**: New `load_multi` and `load_multi_by_keys` methods enable efficient bulk object loading using Redis pipelining. These methods reduce network round trips from N×2 commands (EXISTS + HGETALL per object) to a single pipelined batch of HGETALL commands.
5
+
6
+ **Standard loading** (N objects, N×2 commands):
7
+
8
+ .. code-block:: ruby
9
+
10
+ users = ids.map { |id| User.find_by_id(id) }
11
+ # For 14 objects: 28 Redis commands (14 EXISTS + 14 HGETALL)
12
+
13
+ **Pipelined bulk loading** (N objects, 1 round trip):
14
+
15
+ .. code-block:: ruby
16
+
17
+ users = User.load_multi(ids)
18
+ # For 14 objects: 1 pipelined batch with 14 HGETALL commands
19
+ # Up to 2N× performance improvement
20
+
21
+ **Load by identifiers**:
22
+
23
+ .. code-block:: ruby
24
+
25
+ metadata_objects = Metadata.load_multi(['id1', 'id2', 'id3'])
26
+ # Returns array: [obj1, obj2, obj3]
27
+
28
+ # Filter out nils for missing objects
29
+ existing_only = Metadata.load_multi(ids).compact
30
+
31
+ **Load by full dbkeys**:
32
+
33
+ .. code-block:: ruby
34
+
35
+ keys = ['user:123:object', 'user:456:object']
36
+ users = User.load_multi_by_keys(keys)
37
+
38
+ The methods maintain the same nil-return contract as `find_by_id` for non-existent objects, preserve input order, and properly deserialize all Horreum field types. Ideal for loading collections of related objects, processing query results, or any scenario requiring multiple object lookups.
39
+
40
+ .. Changed
41
+ .. -------
42
+
43
+ - **Optional EXISTS Check Optimization**: The `find_by_dbkey` and `find_by_identifier` methods now accept a `check_exists:` parameter (default: `true`) to optionally skip the EXISTS check before HGETALL. This reduces Redis commands from 2 to 1 per object while maintaining backwards compatibility.
44
+
45
+ - **Parameter Consistency in find_by_identifier**: The `suffix` parameter is now a keyword parameter (was optional positional) for consistency with `check_exists`. This follows Ruby conventions that keyword parameters should not follow optional positional parameters. Maintains backwards compatibility since custom suffixes are rarely used.
46
+
47
+ **Safe mode** (default behavior, 2 commands):
48
+
49
+ .. code-block:: ruby
50
+
51
+ user = User.find_by_id(123)
52
+ # Commands: EXISTS user:123:object, then HGETALL user:123:object
53
+
54
+ **Optimized mode** (1 command):
55
+
56
+ .. code-block:: ruby
57
+
58
+ user = User.find_by_id(123, check_exists: false)
59
+ # Command: HGETALL user:123:object only
60
+ # Returns nil if key doesn't exist (empty hash detected)
61
+
62
+ **Use cases for optimized mode**:
63
+
64
+ - Performance-critical paths where 50% reduction matters
65
+ - Bulk operations with known-to-exist keys
66
+ - High-throughput APIs processing collections
67
+ - Loading objects from sorted set members (ZRANGEBYSCORE results)
68
+
69
+ The optimization is backwards compatible (default unchanged) and maintains the same nil-return behavior for non-existent keys by detecting empty hashes returned from HGETALL.
70
+
71
+ .. Deprecated
72
+ .. ----------
73
+
74
+ .. Removed
75
+ .. -------
76
+
77
+ .. Fixed
78
+ .. -----
79
+
80
+ - **Position Alignment in load_multi_by_keys**: Fixed bug where empty or nil keys caused result array misalignment. The method now tracks valid positions (like `load_multi`) to ensure the results array maintains the same positions as the input array, with nils for invalid keys.
81
+
82
+ .. Security
83
+ .. --------
84
+
85
+ .. Documentation
86
+ .. -------------
87
+
88
+ .. AI Assistance
89
+ .. -------------
90
+
91
+ - **Performance Analysis**: Claude Code analyzed the Redis command trace log provided by the user, identifying the EXISTS + HGETALL pattern as the performance bottleneck in bulk object loading scenarios.
92
+ - **Solution Design**: Claude Code designed a multi-faceted optimization approach: (1) optional EXISTS check bypass with backwards compatibility, (2) pipelined bulk loading methods, (3) comprehensive test coverage. The design balanced performance gains with API safety and backwards compatibility.
93
+ - **Implementation**: Claude Code implemented both optimization strategies including parameter additions, new bulk loading methods, comprehensive documentation with performance characteristics, and 28 test cases covering all scenarios including edge cases (nil identifiers, missing objects, order preservation).
94
+ - **Code Review**: Claude Code ensured the implementation follows Familia's existing patterns for field deserialization, maintains nil-return contracts, and properly handles Redis::Future objects in transaction contexts.
@@ -0,0 +1,44 @@
1
+ .. A new scriv changelog fragment.
2
+ ..
3
+ .. Uncomment the section that is right (remove the leading dots).
4
+ .. For top level release notes, leave all the headers commented out.
5
+ ..
6
+ .. Added
7
+ .. -----
8
+ ..
9
+ .. - A bullet item for the Added category.
10
+ ..
11
+ Changed
12
+ -------
13
+
14
+ - All Ruby files now include consistent headers with ``frozen_string_literal: true`` pragma for improved performance and memory efficiency. Headers follow the format: filename comment, blank comment line, frozen string literal pragma. Executable scripts properly place shebang first.
15
+
16
+ .. Deprecated
17
+ .. ----------
18
+ ..
19
+ .. - A bullet item for the Deprecated category.
20
+ ..
21
+ .. Removed
22
+ .. -------
23
+ ..
24
+ .. - A bullet item for the Removed category.
25
+ ..
26
+ .. Fixed
27
+ .. -----
28
+ ..
29
+ .. - A bullet item for the Fixed category.
30
+ ..
31
+ .. Security
32
+ .. --------
33
+ ..
34
+ .. - A bullet item for the Security category.
35
+ ..
36
+ .. Documentation
37
+ .. -------------
38
+ ..
39
+ .. - A bullet item for the Documentation category.
40
+ ..
41
+ AI Assistance
42
+ -------------
43
+
44
+ - Claude Sonnet 4.5 automated the addition of consistent file headers with frozen_string_literal pragma across 308 Ruby files, then corrected 35 executable scripts to ensure shebangs remain as the first line.