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,245 @@
1
+ # try/unit/core/middleware_thread_safety_try.rb
2
+ #
3
+ # frozen_string_literal: true
4
+
5
+ # Thread Safety Tests for DatabaseLogger Middleware
6
+ #
7
+ # These tests specifically target potential race conditions and concurrency issues
8
+ # in the DatabaseLogger middleware that could lead to nil entries in the @commands array.
9
+ #
10
+ # Covers:
11
+ # - Concurrent append_command operations
12
+ # - Thread-safe pipeline command logging
13
+ # - Mixed operation types under contention
14
+ # - Sampling counter atomicity
15
+ # - Rapid sequential calls within threads
16
+ # - clear_commands during active logging
17
+ #
18
+ # Background:
19
+ # An intermittent NoMethodError was observed where .command was called on nil
20
+ # during teardown of middleware_sampling_try.rb. The error suggests potential
21
+ # corruption of the @commands Concurrent::Array, possibly due to race conditions
22
+ # between append_command, clear_commands, or middleware state management.
23
+
24
+ require_relative '../../support/helpers/test_helpers'
25
+ require 'concurrent'
26
+
27
+ # Setup: Reset DatabaseLogger and ensure middleware is registered
28
+ Familia.connection_provider = nil
29
+ Familia.enable_database_logging = true
30
+ Familia.reconnect!
31
+ DatabaseLogger.clear_commands
32
+ DatabaseLogger.sample_rate = nil
33
+ DatabaseLogger.structured_logging = false
34
+
35
+ ## DatabaseLogger.append_command is thread-safe under concurrent access from 50 threads
36
+ # First verify middleware is working in main thread
37
+ DatabaseLogger.clear_commands
38
+ test_client = Familia.dbclient
39
+ test_client.set("setup_test", "value")
40
+ setup_commands = DatabaseLogger.commands.size
41
+
42
+ # Now run the actual test
43
+ DatabaseLogger.clear_commands
44
+ barrier = Concurrent::CyclicBarrier.new(50)
45
+ threads = []
46
+ dbclient = Familia.dbclient # Get connection in main thread (has middleware)
47
+
48
+ 50.times do |i|
49
+ threads << Thread.new do
50
+ barrier.wait # Synchronize start to maximize contention
51
+ dbclient.set("thread_key_#{i}", "value_#{i}")
52
+ end
53
+ end
54
+
55
+ threads.each(&:join)
56
+ commands = DatabaseLogger.commands
57
+
58
+ # All commands should be captured (no lost writes)
59
+ # No nil entries should exist in the commands array
60
+ # Note: setup_commands verifies middleware is working (should be 1)
61
+ [setup_commands, commands.size, commands.any?(nil), commands.all? { |cmd| cmd.respond_to?(:command) }]
62
+ #=> [1, 50, false, true]
63
+
64
+ ## Pipelined commands are thread-safe under concurrent access from 25 threads
65
+ DatabaseLogger.clear_commands
66
+ barrier = Concurrent::CyclicBarrier.new(25)
67
+ threads = []
68
+ dbclient = Familia.dbclient # Get connection in main thread (has middleware)
69
+
70
+ 25.times do |i|
71
+ threads << Thread.new do
72
+ barrier.wait
73
+ dbclient.pipelined do |pipeline|
74
+ pipeline.set("pipeline_thread_#{i}_key1", "val1")
75
+ pipeline.set("pipeline_thread_#{i}_key2", "val2")
76
+ end
77
+ end
78
+ end
79
+
80
+ threads.each(&:join)
81
+ commands = DatabaseLogger.commands
82
+
83
+ # Each thread creates one pipeline command (25 total)
84
+ # Verify no nil entries and all are proper CommandMessage objects
85
+ [commands.size, commands.any?(nil), commands.all? { |cmd| cmd.respond_to?(:command) }]
86
+ #=> [25, false, true]
87
+
88
+ ## Mixed middleware operations (call, call_pipelined, call_once) are thread-safe
89
+ DatabaseLogger.clear_commands
90
+ barrier = Concurrent::CyclicBarrier.new(60)
91
+ threads = []
92
+ dbclient = Familia.dbclient # Get connection in main thread (has middleware)
93
+
94
+ 60.times do |i|
95
+ threads << Thread.new do
96
+ barrier.wait
97
+
98
+ case i % 3
99
+ when 0 # Regular call
100
+ dbclient.set("mixed_#{i}", "val")
101
+ when 1 # Pipelined
102
+ dbclient.pipelined { |p| p.get("mixed_#{i}") }
103
+ when 2 # Multiple rapid calls
104
+ 3.times { dbclient.get("mixed_#{i}") }
105
+ end
106
+ end
107
+ end
108
+
109
+ threads.each(&:join)
110
+ commands = DatabaseLogger.commands
111
+
112
+ # Verify no nil entries regardless of operation type
113
+ # All entries should be valid CommandMessage instances
114
+ [commands.any?(nil), commands.all? { |cmd| cmd.is_a?(DatabaseLogger::CommandMessage) }]
115
+ #=> [false, true]
116
+
117
+ ## sample_rate counter is thread-safe with 100 concurrent operations
118
+ # REMOVED: This test had incorrect expectations. The sample_rate controls LOGGING
119
+ # output, not COMMAND CAPTURE. Per database_logger.rb:153-154:
120
+ # "Command capture is unaffected - only logger output is sampled."
121
+ # The commands array always contains all commands regardless of sample_rate.
122
+ # Thread safety of the AtomicFixnum counter is verified by other tests.
123
+
124
+ ## Rapid sequential calls within threads don't corrupt shared state
125
+ DatabaseLogger.clear_commands
126
+ DatabaseLogger.sample_rate = nil # Log everything
127
+ latch = Concurrent::CountDownLatch.new(20)
128
+ threads = []
129
+ dbclient = Familia.dbclient # Get connection in main thread (has middleware)
130
+
131
+ 20.times do |i|
132
+ threads << Thread.new do
133
+ # Rapid-fire 10 operations per thread to test state isolation
134
+ 10.times do |j|
135
+ dbclient.set("rapid_#{i}_#{j}", "val")
136
+ end
137
+ latch.count_down
138
+ end
139
+ end
140
+
141
+ latch.wait(5) # 5 second timeout
142
+ commands = DatabaseLogger.commands
143
+
144
+ # Should have 200 commands (20 threads × 10 calls)
145
+ # Most importantly: NO nil entries from rapid sequential access
146
+ [commands.size, commands.any?(nil)]
147
+ #=> [200, false]
148
+
149
+ ## clear_commands doesn't cause nil entries during concurrent logging
150
+ DatabaseLogger.clear_commands
151
+ DatabaseLogger.sample_rate = nil
152
+ barrier = Concurrent::CyclicBarrier.new(51) # 50 loggers + 1 clearer
153
+ threads = []
154
+ dbclient = Familia.dbclient # Get connection in main thread (has middleware)
155
+
156
+ # 50 threads logging continuously
157
+ 50.times do |i|
158
+ threads << Thread.new do
159
+ barrier.wait
160
+ 10.times do |j|
161
+ dbclient.set("clear_test_#{i}_#{j}", "val")
162
+ end
163
+ end
164
+ end
165
+
166
+ # 1 thread clearing repeatedly to create contention
167
+ clearer = Thread.new do
168
+ barrier.wait
169
+ 5.times do
170
+ sleep 0.001 # Small delay between clears
171
+ DatabaseLogger.clear_commands
172
+ end
173
+ end
174
+
175
+ threads.each(&:join)
176
+ clearer.join
177
+
178
+ # After clearing, commands should be empty or valid (never contain nil)
179
+ # This tests whether clear_commands can corrupt the array during active logging
180
+ commands = DatabaseLogger.commands
181
+ [commands.any?(nil)]
182
+ #=> [false]
183
+
184
+ ## CommandMessage structure is preserved under concurrent access
185
+ DatabaseLogger.clear_commands
186
+ barrier = Concurrent::CyclicBarrier.new(30)
187
+ threads = []
188
+ dbclient = Familia.dbclient # Get connection in main thread (has middleware)
189
+
190
+ 30.times do |i|
191
+ threads << Thread.new do
192
+ barrier.wait
193
+ dbclient.set("structure_test_#{i}", "value_#{i}")
194
+ end
195
+ end
196
+
197
+ threads.each(&:join)
198
+ commands = DatabaseLogger.commands
199
+
200
+ # Verify all CommandMessage fields are properly initialized
201
+ # This ensures the Data.define structure isn't corrupted by concurrency
202
+ all_valid = commands.all? do |cmd|
203
+ cmd.command.is_a?(String) &&
204
+ cmd.μs.is_a?(Integer) &&
205
+ cmd.timeline.is_a?(Float)
206
+ end
207
+
208
+ [commands.size, commands.any?(nil), all_valid]
209
+ #=> [30, false, true]
210
+
211
+ ## Concurrent pipelined operations with varying sizes don't cause corruption
212
+ # REMOVED: This test was checking exact pipeline command count which can vary
213
+ # due to intentionally non-atomic append_command trimming logic.
214
+ # See database_logger.rb:214-217 - we don't care about exact count when trimming.
215
+ # The important invariants (no nil entries, successful operations) are tested elsewhere.
216
+
217
+ ## AtomicFixnum counter increments correctly under high contention
218
+ DatabaseLogger.clear_commands
219
+ DatabaseLogger.sample_rate = 1.0 # Track every increment but log everything
220
+ counter_start = DatabaseLogger.instance_variable_get(:@sample_counter).value
221
+
222
+ barrier = Concurrent::CyclicBarrier.new(100)
223
+ threads = []
224
+ dbclient = Familia.dbclient # Get connection in main thread (has middleware)
225
+
226
+ 100.times do |i|
227
+ threads << Thread.new do
228
+ barrier.wait
229
+ dbclient.set("counter_test_#{i}", "val")
230
+ end
231
+ end
232
+
233
+ threads.each(&:join)
234
+ counter_end = DatabaseLogger.instance_variable_get(:@sample_counter).value
235
+ commands = DatabaseLogger.commands
236
+
237
+ # Counter should have incremented by exactly 100
238
+ # Commands should have all 100 entries with no nil values
239
+ [counter_end - counter_start, commands.size, commands.any?(nil)]
240
+ #=> [100, 100, false]
241
+
242
+ # Teardown: Reset to defaults
243
+ DatabaseLogger.sample_rate = nil
244
+ DatabaseLogger.structured_logging = false
245
+ DatabaseLogger.clear_commands
@@ -1,3 +1,7 @@
1
+ # try/unit/core/middleware_try.rb
2
+ #
3
+ # frozen_string_literal: true
4
+
1
5
  # try/core/middleware_try.rb
2
6
 
3
7
  # Test Valkey/Redis middleware components
@@ -1,3 +1,7 @@
1
+ # try/unit/core/settings_try.rb
2
+ #
3
+ # frozen_string_literal: true
4
+
1
5
  # try/core/settings_try.rb
2
6
 
3
7
  require_relative '../../support/helpers/test_helpers'
@@ -1,3 +1,7 @@
1
+ # try/unit/core/time_utils_try.rb
2
+ #
3
+ # frozen_string_literal: true
4
+
1
5
  require_relative '../../support/helpers/test_helpers'
2
6
 
3
7
  module RefinedContext
@@ -1,3 +1,7 @@
1
+ # try/unit/core/tools_try.rb
2
+ #
3
+ # frozen_string_literal: true
4
+
1
5
  # try/core/tools_try.rb
2
6
 
3
7
  # Test Familia::Tools - key migration and utility functions
@@ -1,3 +1,7 @@
1
+ # try/unit/core/utils_try.rb
2
+ #
3
+ # frozen_string_literal: true
4
+
1
5
  # try/core/utils_try.rb
2
6
 
3
7
  require_relative '../../support/helpers/test_helpers'
@@ -85,6 +89,39 @@ rescue Familia::NotDistinguishableError => e
85
89
  end
86
90
  #=> Familia::NotDistinguishableError
87
91
 
92
+ ##
93
+ ## Time utilities
94
+ ##
95
+
96
+ ## now returns Float timestamp in seconds
97
+ time_result = Familia.now
98
+ [time_result.is_a?(Float), time_result > 0]
99
+ #=> [true, true]
100
+
101
+ ## now accepts Time argument
102
+ test_time = Time.utc(2023, 6, 15, 14, 30, 0)
103
+ result_time = Familia.now(test_time)
104
+ [result_time.is_a?(Float), result_time == test_time.utc.to_f]
105
+ #=> [true, true]
106
+
107
+ ## now_in_μs returns Integer microseconds
108
+ μs_result = Familia.now_in_μs
109
+ [μs_result.is_a?(Integer), μs_result > 0]
110
+ #=> [true, true]
111
+
112
+ ## now_in_microseconds is an alias for now_in_μs
113
+ alias_result = Familia.now_in_microseconds
114
+ [alias_result.is_a?(Integer), alias_result > 0]
115
+ #=> [true, true]
116
+
117
+ ## now_in_μs returns monotonic time suitable for duration measurement
118
+ start = Familia.now_in_μs
119
+ sleep 0.001 # Sleep for at least 1ms
120
+ finish = Familia.now_in_μs
121
+ duration = finish - start
122
+ [duration.is_a?(Integer), duration >= 1000] # At least 1000 microseconds (1ms)
123
+ #=> [true, true]
124
+
88
125
  # Cleanup - restore defaults, leave nothing but footprints
89
126
  Familia.delim(':')
90
127
  Familia.suffix(:object)
@@ -1,4 +1,9 @@
1
+ # try/unit/data_types/boolean_try.rb
2
+ #
3
+ # frozen_string_literal: true
4
+
1
5
  # try/data_types/boolean_try.rb
6
+ # Issue #190: Updated to reflect JSON serialization with type preservation
2
7
 
3
8
  require_relative '../../support/helpers/test_helpers'
4
9
 
@@ -6,37 +11,49 @@ Familia.debug = false
6
11
 
7
12
  @hashkey = Familia::HashKey.new 'key'
8
13
 
9
- ## Boolean values are returned as strings, on assignment as string
14
+ ## String 'true' is stored and returned as string
10
15
  @hashkey['test'] = 'true'
11
16
  #=> "true"
12
17
 
13
- ## Boolean values are returned as strings
18
+ ## String values are returned as strings
14
19
  @hashkey['test']
15
20
  #=> "true"
16
21
 
17
- ## Trying to store a boolean value to a hash key raises an exception
18
- begin
19
- @hashkey['test'] = true
20
- rescue Familia::NotDistinguishableError => e
21
- e.message
22
- end
23
- #=> "Cannot represent true<TrueClass> as a string"
22
+ ## Boolean true is now stored with type preservation (Issue #190)
23
+ @hashkey['bool_true'] = true
24
+ #=> true
24
25
 
25
- ## Boolean values are returned as strings
26
- @hashkey['test']
27
- #=> "true"
26
+ ## Boolean true is returned as TrueClass
27
+ @hashkey['bool_true']
28
+ #=> true
28
29
 
29
- ## Trying to store a nil value to a hash key raises an exception
30
- begin
31
- @hashkey['test'] = nil
32
- rescue Familia::NotDistinguishableError => e
33
- e.message
34
- end
35
- #=> "Cannot represent <NilClass> as a string"
30
+ ## Boolean true has correct class
31
+ @hashkey['bool_true'].class
32
+ #=> TrueClass
36
33
 
37
- ## The exceptions prevented the hash from being updated
38
- @hashkey['test']
39
- #=> "true"
34
+ ## Boolean false is stored with type preservation
35
+ @hashkey['bool_false'] = false
36
+ #=> false
37
+
38
+ ## Boolean false is returned as FalseClass
39
+ @hashkey['bool_false']
40
+ #=> false
41
+
42
+ ## Boolean false has correct class
43
+ @hashkey['bool_false'].class
44
+ #=> FalseClass
45
+
46
+ ## nil is stored with type preservation
47
+ @hashkey['nil_value'] = nil
48
+ #=> nil
49
+
50
+ ## nil is returned as nil
51
+ @hashkey['nil_value']
52
+ #=> nil
53
+
54
+ ## nil has correct class
55
+ @hashkey['nil_value'].class
56
+ #=> NilClass
40
57
 
41
58
  ## Clear the hash key
42
59
  @hashkey.delete!
@@ -1,3 +1,7 @@
1
+ # try/unit/data_types/counter_try.rb
2
+ #
3
+ # frozen_string_literal: true
4
+
1
5
  # try/data_types/counter_try.rb
2
6
 
3
7
  require_relative '../../support/helpers/test_helpers'
@@ -1,3 +1,7 @@
1
+ # try/unit/data_types/datatype_base_try.rb
2
+ #
3
+ # frozen_string_literal: true
4
+
1
5
  # try/data_types/base_try.rb
2
6
 
3
7
  require_relative '../../support/helpers/test_helpers'
@@ -1,3 +1,7 @@
1
+ # try/unit/data_types/hash_try.rb
2
+ #
3
+ # frozen_string_literal: true
4
+
1
5
  # try/data_types/hash_try.rb
2
6
 
3
7
  require_relative '../../support/helpers/test_helpers'
@@ -46,8 +50,8 @@ require_relative '../../support/helpers/test_helpers'
46
50
  @a.props.decrement 'counter', 60
47
51
  #=> 40
48
52
 
49
- ## Familia::HashKey#values_at
53
+ ## Familia::HashKey#values_at (counter is integer from HINCRBY, others are strings)
50
54
  @a.props.values_at 'fieldA', 'counter', 'fieldC'
51
- #=> ['1', '40', '3']
55
+ #=> ['1', 40, '3']
52
56
 
53
57
  @a.props.delete!
@@ -1,3 +1,7 @@
1
+ # try/unit/data_types/list_try.rb
2
+ #
3
+ # frozen_string_literal: true
4
+
1
5
  # try/data_types/list_try.rb
2
6
 
3
7
  require_relative '../../support/helpers/test_helpers'
@@ -1,3 +1,7 @@
1
+ # try/unit/data_types/lock_try.rb
2
+ #
3
+ # frozen_string_literal: true
4
+
1
5
  # try/data_types/lock_try.rb
2
6
 
3
7
  require_relative '../../support/helpers/test_helpers'