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,291 @@
1
+ # try/integration/transaction_safety_workflow_try.rb
2
+ #
3
+ # frozen_string_literal: true
4
+
5
+ # Transaction Safety Workflow Integration Test
6
+ #
7
+ # Demonstrates the complete transaction safety workflow with realistic
8
+ # business scenarios that show correct usage patterns.
9
+ #
10
+
11
+ require_relative '../support/helpers/test_helpers'
12
+
13
+ # Business models for realistic workflow testing
14
+ class ::WorkflowCustomer < Familia::Horreum
15
+ identifier_field :customer_id
16
+ field :customer_id
17
+ field :email
18
+ field :balance
19
+ field :status
20
+ field :login_count
21
+ field :last_login
22
+
23
+ list :orders
24
+ set :preferences
25
+ end
26
+
27
+ class ::WorkflowOrder < Familia::Horreum
28
+ identifier_field :order_id
29
+ field :order_id
30
+ field :customer_id
31
+ field :amount
32
+ field :status
33
+ field :created_at
34
+ end
35
+
36
+ class ::WorkflowInventory < Familia::Horreum
37
+ identifier_field :product_id
38
+ field :product_id
39
+ field :quantity
40
+ field :reserved
41
+ end
42
+
43
+ # Helper for unique IDs
44
+ def workflow_id(prefix = 'wf')
45
+ "#{prefix}_#{Time.now.to_i}_#{rand(100000)}"
46
+ end
47
+
48
+ ## Complete customer registration workflow
49
+ @customer_email = "#{workflow_id('customer')}@example.com"
50
+ @customer_id = workflow_id('cust')
51
+
52
+ # Step 1: Create customer (validates uniqueness outside transaction)
53
+ @customer = WorkflowCustomer.new(
54
+ customer_id: @customer_id,
55
+ email: @customer_email,
56
+ balance: 1000,
57
+ status: 'pending',
58
+ login_count: 0
59
+ )
60
+ @customer.save
61
+ #=> true
62
+
63
+ ## Customer exists after registration
64
+ @customer.exists?
65
+ #=> true
66
+
67
+ ## E-commerce order processing workflow
68
+ @product_id = workflow_id('prod')
69
+ @order_id = workflow_id('order')
70
+
71
+ # Setup inventory
72
+ @inventory = WorkflowInventory.new(
73
+ product_id: @product_id,
74
+ quantity: 50,
75
+ reserved: 0
76
+ )
77
+ @inventory.save
78
+
79
+ # Step 1: Create order (outside transaction for validation)
80
+ @order = WorkflowOrder.new(
81
+ order_id: @order_id,
82
+ customer_id: @customer_id,
83
+ amount: 99,
84
+ status: 'pending',
85
+ created_at: Time.now.to_i
86
+ )
87
+ @order.save
88
+ #=> true
89
+
90
+ ## Atomic order processing with inventory update
91
+ @processing_result = WorkflowCustomer.transaction do |conn|
92
+ # Update customer
93
+ conn.hset(@customer.dbkey, 'balance', '901') # 1000 - 99
94
+ conn.hset(@customer.dbkey, 'last_login', Time.now.to_i.to_s)
95
+ @customer.orders.push(@order_id)
96
+
97
+ # Update order
98
+ conn.hset(@order.dbkey, 'status', 'confirmed')
99
+
100
+ # Update inventory
101
+ conn.hset(@inventory.dbkey, 'quantity', '49') # 50 - 1
102
+ conn.hset(@inventory.dbkey, 'reserved', '1')
103
+
104
+ # Add customer preference
105
+ @customer.preferences.add('email_notifications')
106
+ end
107
+ @processing_result.class.name
108
+ #=> "MultiResult"
109
+
110
+ ## All updates applied atomically
111
+ [@customer.hget('balance').to_i, @order.hget('status'), @inventory.hget('quantity').to_i]
112
+ #=> [901, "confirmed", 49]
113
+
114
+ ## Customer login tracking workflow with nested transactions
115
+ @login_start = Time.now
116
+
117
+ # Read current count outside transaction
118
+ @current_count = @customer.hget('login_count').to_i
119
+
120
+ @customer.transaction do |outer_conn|
121
+ # Outer transaction: main login processing
122
+ outer_conn.hset(@customer.dbkey, 'last_login', @login_start.to_i.to_s)
123
+
124
+ # Nested transaction: increment login count (reentrant)
125
+ @customer.transaction do |inner_conn|
126
+ inner_conn.hset(@customer.dbkey, 'login_count', (@current_count + 1).to_s)
127
+
128
+ # Add login preference tracking
129
+ @customer.preferences.add('frequent_user') if @current_count > 5
130
+ end
131
+
132
+ # Continue outer transaction
133
+ outer_conn.hset(@customer.dbkey, 'status', 'active')
134
+ end
135
+
136
+ @customer.hget('login_count').to_i >= 1
137
+ #=> true
138
+
139
+ ## Bulk order fulfillment workflow
140
+ @order_ids = 3.times.map { |i| workflow_id("bulk_#{i}") }
141
+
142
+ # Step 1: Create all orders outside transaction
143
+ @bulk_orders = @order_ids.map do |order_id|
144
+ order = WorkflowOrder.new(
145
+ order_id: order_id,
146
+ customer_id: @customer_id,
147
+ amount: 25,
148
+ status: 'pending',
149
+ created_at: Time.now.to_i
150
+ )
151
+ order.save
152
+ order
153
+ end
154
+ @bulk_orders.all?(&:exists?)
155
+ #=> true
156
+
157
+ ## Bulk fulfillment in single transaction
158
+ # Read balance outside transaction
159
+ @current_balance = @customer.hget('balance').to_i
160
+ @bulk_result = WorkflowOrder.transaction do |conn|
161
+ @bulk_orders.each do |order|
162
+ conn.hset(order.dbkey, 'status', 'fulfilled')
163
+ conn.hset(order.dbkey, 'fulfilled_at', Time.now.to_i.to_s)
164
+ @customer.orders.push(order.order_id)
165
+ end
166
+
167
+ # Update customer balance for all orders
168
+ new_balance = @current_balance - (25 * 3)
169
+ conn.hset(@customer.dbkey, 'balance', new_balance.to_s)
170
+ end
171
+ @bulk_result.class.name
172
+ #=> "MultiResult"
173
+
174
+ ## Error handling in transaction workflow
175
+ @error_order_id = workflow_id('error')
176
+ @error_handled = false
177
+
178
+ begin
179
+ WorkflowOrder.transaction do |conn|
180
+ # Valid operation
181
+ conn.hset("test:#{@error_order_id}", 'status', 'processing')
182
+
183
+ # Simulate error during processing
184
+ raise StandardError, 'Payment processing failed'
185
+
186
+ # This would not execute due to error
187
+ conn.hset("test:#{@error_order_id}", 'status', 'completed')
188
+ end
189
+ rescue StandardError => e
190
+ @error_handled = e.message.include?('Payment processing failed')
191
+ end
192
+
193
+ @error_handled
194
+ #=> true
195
+
196
+ ## Transaction safety violation detection
197
+ @safety_violation_detected = false
198
+
199
+ WorkflowCustomer.transaction do
200
+ test_customer = WorkflowCustomer.new(
201
+ customer_id: workflow_id('safety'),
202
+ email: "#{workflow_id('safety')}@test.com"
203
+ )
204
+
205
+ begin
206
+ # This should raise OperationModeError
207
+ test_customer.save
208
+ rescue Familia::OperationModeError => e
209
+ @safety_violation_detected = e.message.include?('Cannot call save within a transaction')
210
+ end
211
+ end
212
+
213
+ @safety_violation_detected
214
+ #=> true
215
+
216
+ ## Performance comparison: individual vs batch operations
217
+ @perf_customers = 5.times.map do |i|
218
+ customer = WorkflowCustomer.new(
219
+ customer_id: workflow_id("perf_#{i}"),
220
+ email: "perf#{i}@example.com",
221
+ balance: 1000
222
+ )
223
+ customer.save
224
+ customer
225
+ end
226
+
227
+ # Individual transactions
228
+ @individual_start = Time.now
229
+ @perf_customers.each do |customer|
230
+ customer.transaction do |conn|
231
+ conn.hset(customer.dbkey, 'status', 'updated_individual')
232
+ end
233
+ end
234
+ @individual_duration = ((Time.now - @individual_start) * 1000).round(2)
235
+
236
+ # Single batch transaction
237
+ @batch_start = Time.now
238
+ WorkflowCustomer.transaction do |conn|
239
+ @perf_customers.each do |customer|
240
+ conn.hset(customer.dbkey, 'status', 'updated_batch')
241
+ end
242
+ end
243
+ @batch_duration = ((Time.now - @batch_start) * 1000).round(2)
244
+
245
+ # Batch should be faster or at least not significantly slower
246
+ @efficiency_ratio = @individual_duration / @batch_duration
247
+ @efficiency_ratio >= 0.1 # Batch should be reasonably fast
248
+ #=> true
249
+
250
+ ## Watch pattern for optimistic concurrency control
251
+ @concurrent_customer = WorkflowCustomer.new(
252
+ customer_id: workflow_id('concurrent'),
253
+ email: 'concurrent@test.com',
254
+ balance: 500
255
+ )
256
+ @concurrent_customer.save
257
+ @concurrent_customer.hset(:version, '1')
258
+
259
+ @watch_success = @concurrent_customer.watch do
260
+ current_version = @concurrent_customer.hget(:version).to_i
261
+ current_balance = @concurrent_customer.hget(:balance).to_i
262
+
263
+ # Only proceed if version hasn't changed and sufficient balance
264
+ if current_version == 1 && current_balance >= 100
265
+ @concurrent_customer.transaction do |conn|
266
+ conn.hset(@concurrent_customer.dbkey, 'balance', (current_balance - 100).to_s)
267
+ conn.hset(@concurrent_customer.dbkey, 'version', '2')
268
+ conn.hset(@concurrent_customer.dbkey, 'last_transaction', Time.now.to_i.to_s)
269
+ end
270
+ true
271
+ else
272
+ false
273
+ end
274
+ end
275
+
276
+ @concurrent_customer.hget(:balance).to_i == 400
277
+ #=> true
278
+
279
+ ## Workflow completed successfully with all safety checks
280
+ @workflow_summary = {
281
+ customer_created: @customer.exists?,
282
+ order_processed: @order.hget('status') == 'confirmed',
283
+ bulk_fulfilled: @bulk_orders.all? { |o| o.hget('status') == 'fulfilled' },
284
+ error_handled: @error_handled,
285
+ safety_enforced: @safety_violation_detected,
286
+ performance_acceptable: @efficiency_ratio >= 0.1,
287
+ concurrency_controlled: @concurrent_customer.hget(:version).to_i >= 2
288
+ }
289
+
290
+ @workflow_summary.values.all?
291
+ #=> true
@@ -1,3 +1,7 @@
1
+ # try/integration/verifiable_identifier_try.rb
2
+ #
3
+ # frozen_string_literal: true
4
+
1
5
  # try/core/verifiable_identifier_try.rb
2
6
 
3
7
  require_relative '../support/helpers/test_helpers'
@@ -0,0 +1,228 @@
1
+ # Pipeline Routing Investigation (ARCHIVED)
2
+
3
+ **Status**: Investigation complete - NO BUG FOUND
4
+ **Conclusion**: See `CONCLUSION.md` for full analysis
5
+ **Test Files**: Renamed to `.rb.txt` to preserve as documentation without running in CI
6
+
7
+ ## Problem Statement (Original)
8
+
9
+ In a concurrent test with 20 threads executing pipelined operations, we observed:
10
+ - All 20 threads successfully complete
11
+ - All 20 commands are captured in `DatabaseLogger.commands`
12
+ - BUT: Only 16 commands contain the pipeline separator `' | '`
13
+ - This appeared to mean 4 pipeline operations were logged via `call()` instead of `call_pipelined()`
14
+
15
+ **Investigation Result**: Single-command pipelines don't have `' | '` separator (expected `Array#join` behavior). This is NOT a bug.
16
+
17
+ ## Investigation Goals
18
+
19
+ 1. Determine if this is a RedisClient middleware dispatch issue
20
+ 2. Determine if this is a Familia connection chain issue
21
+ 3. Determine if this is timing-dependent (race condition)
22
+ 4. Determine if connection reuse vs. fresh connections affects behavior
23
+
24
+ ## Test Suite Structure
25
+
26
+ ### 01_single_thread_baseline_try.rb
27
+ **Purpose**: Establish baseline behavior in single-threaded environment
28
+
29
+ **Tests**:
30
+ - 10 simple pipeline operations
31
+ - 25 pipeline operations with varying sizes (1-5 commands)
32
+ - Mixed single and pipelined operations (10 each)
33
+
34
+ **Expected**: 100% of pipeline operations route to `call_pipelined()`
35
+
36
+ **Key Question**: Does single-threaded execution work correctly?
37
+
38
+ ### 02_small_concurrency_try.rb
39
+ **Purpose**: Test with minimal thread contention (5 threads)
40
+
41
+ **Tests**:
42
+ - 5 threads with CyclicBarrier (synchronized start)
43
+ - 5 threads without barrier (natural timing)
44
+ - 5 threads with varying pipeline sizes (1-5 commands)
45
+
46
+ **Expected**: 100% of pipeline operations route to `call_pipelined()`
47
+
48
+ **Key Question**: Does adding concurrency break routing?
49
+
50
+ ### 03_reproduce_issue_try.rb
51
+ **Purpose**: Reproduce the exact scenario from the original failing test
52
+
53
+ **Tests**:
54
+ - 20 threads with CyclicBarrier (exact reproduction)
55
+ - 10 repeated trials to check for intermittent behavior
56
+
57
+ **Expected**: May reproduce the routing issue
58
+
59
+ **Key Questions**:
60
+ - Is the issue reproducible?
61
+ - Is it deterministic or intermittent?
62
+ - What's the failure rate?
63
+
64
+ ### 04_high_contention_try.rb
65
+ **Purpose**: Test under high thread contention (50+ threads)
66
+
67
+ **Tests**:
68
+ - 50 threads with synchronized start
69
+ - 50 threads with varying pipeline sizes (1-10 commands)
70
+ - 10 threads × 5 rapid pipelines each (50 total)
71
+
72
+ **Expected**: If timing-related, higher contention should increase failure rate
73
+
74
+ **Key Question**: Does the problem scale with thread count?
75
+
76
+ ### 05_connection_isolation_try.rb
77
+ **Purpose**: Test whether the issue is connection-specific
78
+
79
+ **Tests**:
80
+ - Fresh connection per thread (each thread calls `Familia.dbclient`)
81
+ - Shared connection from main thread (original pattern)
82
+ - Isolated connections via `create_dbclient` (no pooling)
83
+ - Connection via chain per thread (uses connection handlers)
84
+
85
+ **Expected**: Identifies which connection pattern is problematic
86
+
87
+ **Key Questions**:
88
+ - Does connection reuse cause the issue?
89
+ - Does the connection chain have a bug?
90
+ - Is middleware registration timing-dependent?
91
+
92
+ ### 06_fiber_state_inspection_try.rb
93
+ **Purpose**: Inspect Fiber-local state during pipeline operations
94
+
95
+ **Tests**:
96
+ - Single-threaded Fiber state tracking (before/inside/after pipeline)
97
+ - Multi-threaded Fiber isolation verification (10 threads)
98
+ - Middleware call context inspection (capture what middleware sees)
99
+ - Pipeline routing verification (ensure single `call_pipelined` per pipeline)
100
+
101
+ **Expected**: Fiber-local state should be isolated per thread
102
+
103
+ **Key Questions**:
104
+ - Is `Fiber[:familia_pipeline]` being set correctly?
105
+ - Is cleanup happening in ensure blocks?
106
+ - Do threads share Fiber state incorrectly?
107
+ - Does middleware receive correct context?
108
+
109
+ ## Running the Tests
110
+
111
+ ### Run all investigation tests
112
+ ```bash
113
+ FAMILIA_DEBUG=0 bundle exec try --agent try/investigation/pipeline_routing/
114
+ ```
115
+
116
+ ### Run individual tests
117
+ ```bash
118
+ # Baseline
119
+ bundle exec try --agent try/investigation/pipeline_routing/01_single_thread_baseline_try.rb
120
+
121
+ # Small concurrency
122
+ bundle exec try --agent try/investigation/pipeline_routing/02_small_concurrency_try.rb
123
+
124
+ # Reproduce issue
125
+ bundle exec try --agent try/investigation/pipeline_routing/03_reproduce_issue_try.rb
126
+
127
+ # High contention
128
+ bundle exec try --agent try/investigation/pipeline_routing/04_high_contention_try.rb
129
+
130
+ # Connection isolation
131
+ bundle exec try --agent try/investigation/pipeline_routing/05_connection_isolation_try.rb
132
+
133
+ # Fiber state
134
+ bundle exec try --agent try/investigation/pipeline_routing/06_fiber_state_inspection_try.rb
135
+ ```
136
+
137
+ ### Run with verbose output (for failures)
138
+ ```bash
139
+ bundle exec try --verbose --fails --stack try/investigation/pipeline_routing/03_reproduce_issue_try.rb
140
+ ```
141
+
142
+ ## What to Look For
143
+
144
+ ### Success Indicators
145
+ - All pipeline operations contain `' | '` separator
146
+ - Command counts match expected values
147
+ - No "ROUTING ANOMALY" messages in output
148
+
149
+ ### Failure Indicators
150
+ - Pipeline operations logged without `' | '` separator
151
+ - Fewer `call_pipelined()` invocations than expected
152
+ - "ROUTING ANOMALY DETECTED" in output
153
+ - Mismatched command counts
154
+
155
+ ### Diagnostic Output
156
+ Each test prints detailed analysis including:
157
+ - Total commands captured
158
+ - Pipeline commands (with separator)
159
+ - Single commands (without separator)
160
+ - Percentage breakdown
161
+ - Specific examples of misrouted commands
162
+
163
+ ## Hypothesis Checklist
164
+
165
+ After running the tests, we should be able to answer:
166
+
167
+ - [ ] Does single-threaded execution work correctly?
168
+ - [ ] Does small concurrency (5 threads) work correctly?
169
+ - [ ] Can we reproduce the issue with 20 threads?
170
+ - [ ] Is the issue deterministic or intermittent?
171
+ - [ ] Does the problem scale with thread count?
172
+ - [ ] Is it related to connection reuse vs. fresh connections?
173
+ - [ ] Is it related to the connection chain implementation?
174
+ - [ ] Is Fiber-local state being managed correctly?
175
+ - [ ] Does middleware receive the correct context?
176
+
177
+ ## Next Steps
178
+
179
+ Based on results:
180
+
181
+ 1. **If baseline fails**: RedisClient middleware routing is broken in general
182
+ 2. **If only concurrent tests fail**: Thread safety issue in middleware dispatch
183
+ 3. **If only shared connection fails**: Connection chain or pooling issue
184
+ 4. **If Fiber state leaks**: Cleanup logic in `PipelineCore` is broken
185
+ 5. **If intermittent**: Race condition requiring deeper investigation
186
+
187
+ ## Background: How Pipeline Routing Should Work
188
+
189
+ ```ruby
190
+ # RedisClient source (redis-client-0.25.1/lib/redis_client.rb:446)
191
+ def pipelined(exception: true)
192
+ pipeline = Pipeline.new(@command_builder)
193
+ yield pipeline
194
+
195
+ if pipeline._size == 0
196
+ []
197
+ else
198
+ results = ensure_connected(retryable: pipeline._retryable?) do |connection|
199
+ commands = pipeline._commands
200
+ @middlewares.call_pipelined(commands, config) do # <-- Should call this
201
+ connection.call_pipelined(commands, pipeline._timeouts, exception: exception)
202
+ end
203
+ end
204
+
205
+ pipeline._coerce!(results)
206
+ end
207
+ end
208
+ ```
209
+
210
+ **Expected flow**:
211
+ 1. User calls `client.pipelined { |p| p.set(...) }`
212
+ 2. RedisClient builds pipeline commands
213
+ 3. RedisClient calls `@middlewares.call_pipelined(commands, config)`
214
+ 4. DatabaseLogger.call_pipelined receives commands array
215
+ 5. Logs with `' | '` separator joining commands
216
+
217
+ **Anomalous flow** (what we're seeing):
218
+ 1. User calls `client.pipelined { |p| p.set(...) }`
219
+ 2. ??? Something goes wrong ???
220
+ 3. DatabaseLogger.call receives individual command
221
+ 4. Logs without `' | '` separator
222
+
223
+ ## Files Involved
224
+
225
+ - `/Users/d/Projects/opensource/d/familia/lib/middleware/database_logger.rb` - Middleware implementation
226
+ - `/Users/d/Projects/opensource/d/familia/lib/familia/connection.rb` - Connection management
227
+ - `/Users/d/Projects/opensource/d/familia/lib/familia/connection/pipelined_core.rb` - Pipeline execution
228
+ - `/Users/d/Projects/opensource/d/familia/try/unit/core/middleware_thread_safety_try.rb` - Original test that exposed this
@@ -1,3 +1,7 @@
1
+ # try/performance/benchmarks_try.rb
2
+ #
3
+ # frozen_string_literal: true
4
+
1
5
  # Performance benchmarks separate from stress tests
2
6
 
3
7
  require_relative '../support/helpers/test_helpers'