matrx-orm 3.1.78__tar.gz → 3.1.79__tar.gz

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 (344) hide show
  1. {matrx_orm-3.1.78 → matrx_orm-3.1.79}/PKG-INFO +1 -1
  2. {matrx_orm-3.1.78 → matrx_orm-3.1.79}/matrx_orm/core/FEATURE.md +16 -2
  3. {matrx_orm-3.1.78 → matrx_orm-3.1.79}/matrx_orm/core/async_db_manager.py +40 -14
  4. {matrx_orm-3.1.78 → matrx_orm-3.1.79}/matrx_orm/exceptions.py +42 -22
  5. {matrx_orm-3.1.78 → matrx_orm-3.1.79}/pyproject.toml +1 -1
  6. {matrx_orm-3.1.78 → matrx_orm-3.1.79}/tests/level1/test_exceptions.py +23 -0
  7. {matrx_orm-3.1.78 → matrx_orm-3.1.79}/tests/level1/test_pool_maintenance.py +33 -0
  8. {matrx_orm-3.1.78 → matrx_orm-3.1.79}/.gitignore +0 -0
  9. {matrx_orm-3.1.78 → matrx_orm-3.1.79}/AGENTS.md +0 -0
  10. {matrx_orm-3.1.78 → matrx_orm-3.1.79}/BASE_CLASS_METHODS.md +0 -0
  11. {matrx_orm-3.1.78 → matrx_orm-3.1.79}/CLAUDE.md +0 -0
  12. {matrx_orm-3.1.78 → matrx_orm-3.1.79}/MODEL_API.md +0 -0
  13. {matrx_orm-3.1.78 → matrx_orm-3.1.79}/README.md +0 -0
  14. {matrx_orm-3.1.78 → matrx_orm-3.1.79}/database/orm/extended/managers/ai_model_base.py +0 -0
  15. {matrx_orm-3.1.78 → matrx_orm-3.1.79}/docs/migrations.md +0 -0
  16. {matrx_orm-3.1.78 → matrx_orm-3.1.79}/matrx_orm/MODULE_README.md +0 -0
  17. {matrx_orm-3.1.78 → matrx_orm-3.1.79}/matrx_orm/__init__.py +0 -0
  18. {matrx_orm-3.1.78 → matrx_orm-3.1.79}/matrx_orm/adapters/MODULE_README.md +0 -0
  19. {matrx_orm-3.1.78 → matrx_orm-3.1.79}/matrx_orm/adapters/__init__.py +0 -0
  20. {matrx_orm-3.1.78 → matrx_orm-3.1.79}/matrx_orm/adapters/async_postgresql.py +0 -0
  21. {matrx_orm-3.1.78 → matrx_orm-3.1.79}/matrx_orm/adapters/base_adapter.py +0 -0
  22. {matrx_orm-3.1.78 → matrx_orm-3.1.79}/matrx_orm/adapters/postgresql.py +0 -0
  23. {matrx_orm-3.1.78 → matrx_orm-3.1.79}/matrx_orm/adapters/postgrest_client_adapter.py +0 -0
  24. {matrx_orm-3.1.78 → matrx_orm-3.1.79}/matrx_orm/adapters/supabase_adapter.py +0 -0
  25. {matrx_orm-3.1.78 → matrx_orm-3.1.79}/matrx_orm/admin/FEATURE.md +0 -0
  26. {matrx_orm-3.1.78 → matrx_orm-3.1.79}/matrx_orm/admin/__init__.py +0 -0
  27. {matrx_orm-3.1.78 → matrx_orm-3.1.79}/matrx_orm/admin/router.py +0 -0
  28. {matrx_orm-3.1.78 → matrx_orm-3.1.79}/matrx_orm/api/__init__.py +0 -0
  29. {matrx_orm-3.1.78 → matrx_orm-3.1.79}/matrx_orm/api/auth.py +0 -0
  30. {matrx_orm-3.1.78 → matrx_orm-3.1.79}/matrx_orm/api/config.py +0 -0
  31. {matrx_orm-3.1.78 → matrx_orm-3.1.79}/matrx_orm/api/handlers.py +0 -0
  32. {matrx_orm-3.1.78 → matrx_orm-3.1.79}/matrx_orm/api/protocol.py +0 -0
  33. {matrx_orm-3.1.78 → matrx_orm-3.1.79}/matrx_orm/api/server.py +0 -0
  34. {matrx_orm-3.1.78 → matrx_orm-3.1.79}/matrx_orm/associations.py +0 -0
  35. {matrx_orm-3.1.78 → matrx_orm-3.1.79}/matrx_orm/cache_debug.py +0 -0
  36. {matrx_orm-3.1.78 → matrx_orm-3.1.79}/matrx_orm/catalog.py +0 -0
  37. {matrx_orm-3.1.78 → matrx_orm-3.1.79}/matrx_orm/catalog_sql.py +0 -0
  38. {matrx_orm-3.1.78 → matrx_orm-3.1.79}/matrx_orm/client/MODULE_README.md +0 -0
  39. {matrx_orm-3.1.78 → matrx_orm-3.1.79}/matrx_orm/client/__init__.py +0 -0
  40. {matrx_orm-3.1.78 → matrx_orm-3.1.79}/matrx_orm/client/postgres_connection.py +0 -0
  41. {matrx_orm-3.1.78 → matrx_orm-3.1.79}/matrx_orm/client/postgrest.py +0 -0
  42. {matrx_orm-3.1.78 → matrx_orm-3.1.79}/matrx_orm/client/supabase_auth.py +0 -0
  43. {matrx_orm-3.1.78 → matrx_orm-3.1.79}/matrx_orm/client/supabase_config.py +0 -0
  44. {matrx_orm-3.1.78 → matrx_orm-3.1.79}/matrx_orm/client/supabase_manager.py +0 -0
  45. {matrx_orm-3.1.78 → matrx_orm-3.1.79}/matrx_orm/core/EXTENDED-TASK.md +0 -0
  46. {matrx_orm-3.1.78 → matrx_orm-3.1.79}/matrx_orm/core/MODULE_README.md +0 -0
  47. {matrx_orm-3.1.78 → matrx_orm-3.1.79}/matrx_orm/core/RELATIONS-TASKS.md +0 -0
  48. {matrx_orm-3.1.78 → matrx_orm-3.1.79}/matrx_orm/core/TASKS.md +0 -0
  49. {matrx_orm-3.1.78 → matrx_orm-3.1.79}/matrx_orm/core/__init__.py +0 -0
  50. {matrx_orm-3.1.78 → matrx_orm-3.1.79}/matrx_orm/core/base.py +0 -0
  51. {matrx_orm-3.1.78 → matrx_orm-3.1.79}/matrx_orm/core/config.py +0 -0
  52. {matrx_orm-3.1.78 → matrx_orm-3.1.79}/matrx_orm/core/connections.py +0 -0
  53. {matrx_orm-3.1.78 → matrx_orm-3.1.79}/matrx_orm/core/db_function.py +0 -0
  54. {matrx_orm-3.1.78 → matrx_orm-3.1.79}/matrx_orm/core/diagnostics.py +0 -0
  55. {matrx_orm-3.1.78 → matrx_orm-3.1.79}/matrx_orm/core/expressions.py +0 -0
  56. {matrx_orm-3.1.78 → matrx_orm-3.1.79}/matrx_orm/core/extended.py +0 -0
  57. {matrx_orm-3.1.78 → matrx_orm-3.1.79}/matrx_orm/core/fields.py +0 -0
  58. {matrx_orm-3.1.78 → matrx_orm-3.1.79}/matrx_orm/core/introspection.py +0 -0
  59. {matrx_orm-3.1.78 → matrx_orm-3.1.79}/matrx_orm/core/listen.py +0 -0
  60. {matrx_orm-3.1.78 → matrx_orm-3.1.79}/matrx_orm/core/loop_filters.py +0 -0
  61. {matrx_orm-3.1.78 → matrx_orm-3.1.79}/matrx_orm/core/model_dto.py +0 -0
  62. {matrx_orm-3.1.78 → matrx_orm-3.1.79}/matrx_orm/core/model_view.py +0 -0
  63. {matrx_orm-3.1.78 → matrx_orm-3.1.79}/matrx_orm/core/one_database.py +0 -0
  64. {matrx_orm-3.1.78 → matrx_orm-3.1.79}/matrx_orm/core/paginator.py +0 -0
  65. {matrx_orm-3.1.78 → matrx_orm-3.1.79}/matrx_orm/core/pool_maintenance.py +0 -0
  66. {matrx_orm-3.1.78 → matrx_orm-3.1.79}/matrx_orm/core/pool_watch.py +0 -0
  67. {matrx_orm-3.1.78 → matrx_orm-3.1.79}/matrx_orm/core/pooler_role.py +0 -0
  68. {matrx_orm-3.1.78 → matrx_orm-3.1.79}/matrx_orm/core/pydantic_bridge.py +0 -0
  69. {matrx_orm-3.1.78 → matrx_orm-3.1.79}/matrx_orm/core/registry.py +0 -0
  70. {matrx_orm-3.1.78 → matrx_orm-3.1.79}/matrx_orm/core/relations.py +0 -0
  71. {matrx_orm-3.1.78 → matrx_orm-3.1.79}/matrx_orm/core/resilience.py +0 -0
  72. {matrx_orm-3.1.78 → matrx_orm-3.1.79}/matrx_orm/core/rls_session.py +0 -0
  73. {matrx_orm-3.1.78 → matrx_orm-3.1.79}/matrx_orm/core/signals.py +0 -0
  74. {matrx_orm-3.1.78 → matrx_orm-3.1.79}/matrx_orm/core/tls.py +0 -0
  75. {matrx_orm-3.1.78 → matrx_orm-3.1.79}/matrx_orm/core/transaction.py +0 -0
  76. {matrx_orm-3.1.78 → matrx_orm-3.1.79}/matrx_orm/core/types.py +0 -0
  77. {matrx_orm-3.1.78 → matrx_orm-3.1.79}/matrx_orm/core/write_queue.py +0 -0
  78. {matrx_orm-3.1.78 → matrx_orm-3.1.79}/matrx_orm/entity.py +0 -0
  79. {matrx_orm-3.1.78 → matrx_orm-3.1.79}/matrx_orm/error_handling.py +0 -0
  80. {matrx_orm-3.1.78 → matrx_orm-3.1.79}/matrx_orm/extended/__init__.py +0 -0
  81. {matrx_orm-3.1.78 → matrx_orm-3.1.79}/matrx_orm/extended/app_error_handler.py +0 -0
  82. {matrx_orm-3.1.78 → matrx_orm-3.1.79}/matrx_orm/identity_rule.py +0 -0
  83. {matrx_orm-3.1.78 → matrx_orm-3.1.79}/matrx_orm/local_sqlite.py +0 -0
  84. {matrx_orm-3.1.78 → matrx_orm-3.1.79}/matrx_orm/middleware/__init__.py +0 -0
  85. {matrx_orm-3.1.78 → matrx_orm-3.1.79}/matrx_orm/middleware/base.py +0 -0
  86. {matrx_orm-3.1.78 → matrx_orm-3.1.79}/matrx_orm/migrations/MODULE_README.md +0 -0
  87. {matrx_orm-3.1.78 → matrx_orm-3.1.79}/matrx_orm/migrations/__init__.py +0 -0
  88. {matrx_orm-3.1.78 → matrx_orm-3.1.79}/matrx_orm/migrations/cli.py +0 -0
  89. {matrx_orm-3.1.78 → matrx_orm-3.1.79}/matrx_orm/migrations/ddl.py +0 -0
  90. {matrx_orm-3.1.78 → matrx_orm-3.1.79}/matrx_orm/migrations/diff.py +0 -0
  91. {matrx_orm-3.1.78 → matrx_orm-3.1.79}/matrx_orm/migrations/executor.py +0 -0
  92. {matrx_orm-3.1.78 → matrx_orm-3.1.79}/matrx_orm/migrations/integration.py +0 -0
  93. {matrx_orm-3.1.78 → matrx_orm-3.1.79}/matrx_orm/migrations/loader.py +0 -0
  94. {matrx_orm-3.1.78 → matrx_orm-3.1.79}/matrx_orm/migrations/operations.py +0 -0
  95. {matrx_orm-3.1.78 → matrx_orm-3.1.79}/matrx_orm/migrations/state.py +0 -0
  96. {matrx_orm-3.1.78 → matrx_orm-3.1.79}/matrx_orm/migrations/table_filter.py +0 -0
  97. {matrx_orm-3.1.78 → matrx_orm-3.1.79}/matrx_orm/operations/FEATURE.md +0 -0
  98. {matrx_orm-3.1.78 → matrx_orm-3.1.79}/matrx_orm/operations/MODULE_README.md +0 -0
  99. {matrx_orm-3.1.78 → matrx_orm-3.1.79}/matrx_orm/operations/__init__.py +0 -0
  100. {matrx_orm-3.1.78 → matrx_orm-3.1.79}/matrx_orm/operations/bulk_update_values.py +0 -0
  101. {matrx_orm-3.1.78 → matrx_orm-3.1.79}/matrx_orm/operations/conflict.py +0 -0
  102. {matrx_orm-3.1.78 → matrx_orm-3.1.79}/matrx_orm/operations/conflict_writes.py +0 -0
  103. {matrx_orm-3.1.78 → matrx_orm-3.1.79}/matrx_orm/operations/create.py +0 -0
  104. {matrx_orm-3.1.78 → matrx_orm-3.1.79}/matrx_orm/operations/db_functions.py +0 -0
  105. {matrx_orm-3.1.78 → matrx_orm-3.1.79}/matrx_orm/operations/delete.py +0 -0
  106. {matrx_orm-3.1.78 → matrx_orm-3.1.79}/matrx_orm/operations/dynamic_admin.py +0 -0
  107. {matrx_orm-3.1.78 → matrx_orm-3.1.79}/matrx_orm/operations/dynamic_crud.py +0 -0
  108. {matrx_orm-3.1.78 → matrx_orm-3.1.79}/matrx_orm/operations/graph_walk.py +0 -0
  109. {matrx_orm-3.1.78 → matrx_orm-3.1.79}/matrx_orm/operations/latest_rows.py +0 -0
  110. {matrx_orm-3.1.78 → matrx_orm-3.1.79}/matrx_orm/operations/queue_claim.py +0 -0
  111. {matrx_orm-3.1.78 → matrx_orm-3.1.79}/matrx_orm/operations/read.py +0 -0
  112. {matrx_orm-3.1.78 → matrx_orm-3.1.79}/matrx_orm/operations/relational_backfill.py +0 -0
  113. {matrx_orm-3.1.78 → matrx_orm-3.1.79}/matrx_orm/operations/staging_load.py +0 -0
  114. {matrx_orm-3.1.78 → matrx_orm-3.1.79}/matrx_orm/operations/update.py +0 -0
  115. {matrx_orm-3.1.78 → matrx_orm-3.1.79}/matrx_orm/platform_db.py +0 -0
  116. {matrx_orm-3.1.78 → matrx_orm-3.1.79}/matrx_orm/py.typed +0 -0
  117. {matrx_orm-3.1.78 → matrx_orm-3.1.79}/matrx_orm/python_sql/MODULE_README.md +0 -0
  118. {matrx_orm-3.1.78 → matrx_orm-3.1.79}/matrx_orm/python_sql/__init__.py +0 -0
  119. {matrx_orm-3.1.78 → matrx_orm-3.1.79}/matrx_orm/python_sql/db_objects.py +0 -0
  120. {matrx_orm-3.1.78 → matrx_orm-3.1.79}/matrx_orm/python_sql/table_detailed_relationships.py +0 -0
  121. {matrx_orm-3.1.78 → matrx_orm-3.1.79}/matrx_orm/python_sql/table_typescript_relationship.py +0 -0
  122. {matrx_orm-3.1.78 → matrx_orm-3.1.79}/matrx_orm/query/FEATURE.md +0 -0
  123. {matrx_orm-3.1.78 → matrx_orm-3.1.79}/matrx_orm/query/MODULE_README.md +0 -0
  124. {matrx_orm-3.1.78 → matrx_orm-3.1.79}/matrx_orm/query/__init__.py +0 -0
  125. {matrx_orm-3.1.78 → matrx_orm-3.1.79}/matrx_orm/query/builder.py +0 -0
  126. {matrx_orm-3.1.78 → matrx_orm-3.1.79}/matrx_orm/query/executor.py +0 -0
  127. {matrx_orm-3.1.78 → matrx_orm-3.1.79}/matrx_orm/schema_builder/FEATURE.md +0 -0
  128. {matrx_orm-3.1.78 → matrx_orm-3.1.79}/matrx_orm/schema_builder/MODULE_README.md +0 -0
  129. {matrx_orm-3.1.78 → matrx_orm-3.1.79}/matrx_orm/schema_builder/__init__.py +0 -0
  130. {matrx_orm-3.1.78 → matrx_orm-3.1.79}/matrx_orm/schema_builder/code_handler.py +0 -0
  131. {matrx_orm-3.1.78 → matrx_orm-3.1.79}/matrx_orm/schema_builder/codegen_writer.py +0 -0
  132. {matrx_orm-3.1.78 → matrx_orm-3.1.79}/matrx_orm/schema_builder/columns.py +0 -0
  133. {matrx_orm-3.1.78 → matrx_orm-3.1.79}/matrx_orm/schema_builder/common.py +0 -0
  134. {matrx_orm-3.1.78 → matrx_orm-3.1.79}/matrx_orm/schema_builder/diff_preview.py +0 -0
  135. {matrx_orm-3.1.78 → matrx_orm-3.1.79}/matrx_orm/schema_builder/entity_capabilities.py +0 -0
  136. {matrx_orm-3.1.78 → matrx_orm-3.1.79}/matrx_orm/schema_builder/generator.py +0 -0
  137. {matrx_orm-3.1.78 → matrx_orm-3.1.79}/matrx_orm/schema_builder/helpers/__init__.py +0 -0
  138. {matrx_orm-3.1.78 → matrx_orm-3.1.79}/matrx_orm/schema_builder/helpers/base_generators.py +0 -0
  139. {matrx_orm-3.1.78 → matrx_orm-3.1.79}/matrx_orm/schema_builder/helpers/entity_generators.py +0 -0
  140. {matrx_orm-3.1.78 → matrx_orm-3.1.79}/matrx_orm/schema_builder/helpers/git_checker.py +0 -0
  141. {matrx_orm-3.1.78 → matrx_orm-3.1.79}/matrx_orm/schema_builder/package_wiring.py +0 -0
  142. {matrx_orm-3.1.78 → matrx_orm-3.1.79}/matrx_orm/schema_builder/relationships.py +0 -0
  143. {matrx_orm-3.1.78 → matrx_orm-3.1.79}/matrx_orm/schema_builder/runner.py +0 -0
  144. {matrx_orm-3.1.78 → matrx_orm-3.1.79}/matrx_orm/schema_builder/schema.py +0 -0
  145. {matrx_orm-3.1.78 → matrx_orm-3.1.79}/matrx_orm/schema_builder/schema_manager.py +0 -0
  146. {matrx_orm-3.1.78 → matrx_orm-3.1.79}/matrx_orm/schema_builder/tables.py +0 -0
  147. {matrx_orm-3.1.78 → matrx_orm-3.1.79}/matrx_orm/schema_builder/views.py +0 -0
  148. {matrx_orm-3.1.78 → matrx_orm-3.1.79}/matrx_orm/secrets_battery/FEATURE.md +0 -0
  149. {matrx_orm-3.1.78 → matrx_orm-3.1.79}/matrx_orm/secrets_battery/__init__.py +0 -0
  150. {matrx_orm-3.1.78 → matrx_orm-3.1.79}/matrx_orm/secrets_battery/attachments.py +0 -0
  151. {matrx_orm-3.1.78 → matrx_orm-3.1.79}/matrx_orm/secrets_battery/crypto.py +0 -0
  152. {matrx_orm-3.1.78 → matrx_orm-3.1.79}/matrx_orm/secrets_battery/items.py +0 -0
  153. {matrx_orm-3.1.78 → matrx_orm-3.1.79}/matrx_orm/secrets_battery/model.py +0 -0
  154. {matrx_orm-3.1.78 → matrx_orm-3.1.79}/matrx_orm/secrets_battery/org_service.py +0 -0
  155. {matrx_orm-3.1.78 → matrx_orm-3.1.79}/matrx_orm/secrets_battery/service.py +0 -0
  156. {matrx_orm-3.1.78 → matrx_orm-3.1.79}/matrx_orm/session/README.md +0 -0
  157. {matrx_orm-3.1.78 → matrx_orm-3.1.79}/matrx_orm/session/__init__.py +0 -0
  158. {matrx_orm-3.1.78 → matrx_orm-3.1.79}/matrx_orm/session/coalesce.py +0 -0
  159. {matrx_orm-3.1.78 → matrx_orm-3.1.79}/matrx_orm/session/dag.py +0 -0
  160. {matrx_orm-3.1.78 → matrx_orm-3.1.79}/matrx_orm/session/errors.py +0 -0
  161. {matrx_orm-3.1.78 → matrx_orm-3.1.79}/matrx_orm/session/fallback.py +0 -0
  162. {matrx_orm-3.1.78 → matrx_orm-3.1.79}/matrx_orm/session/flush.py +0 -0
  163. {matrx_orm-3.1.78 → matrx_orm-3.1.79}/matrx_orm/session/lifecycle.py +0 -0
  164. {matrx_orm-3.1.78 → matrx_orm-3.1.79}/matrx_orm/session/managed.py +0 -0
  165. {matrx_orm-3.1.78 → matrx_orm-3.1.79}/matrx_orm/session/op.py +0 -0
  166. {matrx_orm-3.1.78 → matrx_orm-3.1.79}/matrx_orm/session/reads.py +0 -0
  167. {matrx_orm-3.1.78 → matrx_orm-3.1.79}/matrx_orm/session/session.py +0 -0
  168. {matrx_orm-3.1.78 → matrx_orm-3.1.79}/matrx_orm/session/telemetry.py +0 -0
  169. {matrx_orm-3.1.78 → matrx_orm-3.1.79}/matrx_orm/sql_executor/MODULE_README.md +0 -0
  170. {matrx_orm-3.1.78 → matrx_orm-3.1.79}/matrx_orm/sql_executor/__init__.py +0 -0
  171. {matrx_orm-3.1.78 → matrx_orm-3.1.79}/matrx_orm/sql_executor/executor.py +0 -0
  172. {matrx_orm-3.1.78 → matrx_orm-3.1.79}/matrx_orm/sql_executor/queries.py +0 -0
  173. {matrx_orm-3.1.78 → matrx_orm-3.1.79}/matrx_orm/sql_executor/registry.py +0 -0
  174. {matrx_orm-3.1.78 → matrx_orm-3.1.79}/matrx_orm/sql_executor/types.py +0 -0
  175. {matrx_orm-3.1.78 → matrx_orm-3.1.79}/matrx_orm/sql_executor/utils.py +0 -0
  176. {matrx_orm-3.1.78 → matrx_orm-3.1.79}/matrx_orm/state.py +0 -0
  177. {matrx_orm-3.1.78 → matrx_orm-3.1.79}/matrx_orm/utils/__init__.py +0 -0
  178. {matrx_orm-3.1.78 → matrx_orm-3.1.79}/matrx_orm/utils/sql_utils.py +0 -0
  179. {matrx_orm-3.1.78 → matrx_orm-3.1.79}/matrx_orm/utils/type_converters.py +0 -0
  180. {matrx_orm-3.1.78 → matrx_orm-3.1.79}/scripts/git-branches.sh +0 -0
  181. {matrx_orm-3.1.78 → matrx_orm-3.1.79}/scripts/publish.sh +0 -0
  182. {matrx_orm-3.1.78 → matrx_orm-3.1.79}/scripts/release.sh +0 -0
  183. {matrx_orm-3.1.78 → matrx_orm-3.1.79}/tests/MODULE_README.md +0 -0
  184. {matrx_orm-3.1.78 → matrx_orm-3.1.79}/tests/__init__.py +0 -0
  185. {matrx_orm-3.1.78 → matrx_orm-3.1.79}/tests/conftest.py +0 -0
  186. {matrx_orm-3.1.78 → matrx_orm-3.1.79}/tests/level1/MODULE_README.md +0 -0
  187. {matrx_orm-3.1.78 → matrx_orm-3.1.79}/tests/level1/__init__.py +0 -0
  188. {matrx_orm-3.1.78 → matrx_orm-3.1.79}/tests/level1/test_admin_db_columns.py +0 -0
  189. {matrx_orm-3.1.78 → matrx_orm-3.1.79}/tests/level1/test_admin_response_rendering.py +0 -0
  190. {matrx_orm-3.1.78 → matrx_orm-3.1.79}/tests/level1/test_admin_search_negation.py +0 -0
  191. {matrx_orm-3.1.78 → matrx_orm-3.1.79}/tests/level1/test_admin_search_sentinels.py +0 -0
  192. {matrx_orm-3.1.78 → matrx_orm-3.1.79}/tests/level1/test_advisory_lock.py +0 -0
  193. {matrx_orm-3.1.78 → matrx_orm-3.1.79}/tests/level1/test_agent_message.py +0 -0
  194. {matrx_orm-3.1.78 → matrx_orm-3.1.79}/tests/level1/test_api_auth.py +0 -0
  195. {matrx_orm-3.1.78 → matrx_orm-3.1.79}/tests/level1/test_api_config.py +0 -0
  196. {matrx_orm-3.1.78 → matrx_orm-3.1.79}/tests/level1/test_api_handlers.py +0 -0
  197. {matrx_orm-3.1.78 → matrx_orm-3.1.79}/tests/level1/test_api_protocol.py +0 -0
  198. {matrx_orm-3.1.78 → matrx_orm-3.1.79}/tests/level1/test_array_agg_order_by.py +0 -0
  199. {matrx_orm-3.1.78 → matrx_orm-3.1.79}/tests/level1/test_associations.py +0 -0
  200. {matrx_orm-3.1.78 → matrx_orm-3.1.79}/tests/level1/test_bulk_hydration_offload.py +0 -0
  201. {matrx_orm-3.1.78 → matrx_orm-3.1.79}/tests/level1/test_bulk_update_by_pk.py +0 -0
  202. {matrx_orm-3.1.78 → matrx_orm-3.1.79}/tests/level1/test_bulk_upsert_increment_set_fields.py +0 -0
  203. {matrx_orm-3.1.78 → matrx_orm-3.1.79}/tests/level1/test_cache_debug.py +0 -0
  204. {matrx_orm-3.1.78 → matrx_orm-3.1.79}/tests/level1/test_call_function.py +0 -0
  205. {matrx_orm-3.1.78 → matrx_orm-3.1.79}/tests/level1/test_case_when.py +0 -0
  206. {matrx_orm-3.1.78 → matrx_orm-3.1.79}/tests/level1/test_catalog.py +0 -0
  207. {matrx_orm-3.1.78 → matrx_orm-3.1.79}/tests/level1/test_composite_pk_filter.py +0 -0
  208. {matrx_orm-3.1.78 → matrx_orm-3.1.79}/tests/level1/test_composite_pk_write_paths.py +0 -0
  209. {matrx_orm-3.1.78 → matrx_orm-3.1.79}/tests/level1/test_config.py +0 -0
  210. {matrx_orm-3.1.78 → matrx_orm-3.1.79}/tests/level1/test_conflict_target.py +0 -0
  211. {matrx_orm-3.1.78 → matrx_orm-3.1.79}/tests/level1/test_conflict_writes.py +0 -0
  212. {matrx_orm-3.1.78 → matrx_orm-3.1.79}/tests/level1/test_connection_codecs.py +0 -0
  213. {matrx_orm-3.1.78 → matrx_orm-3.1.79}/tests/level1/test_connection_poison_guard.py +0 -0
  214. {matrx_orm-3.1.78 → matrx_orm-3.1.79}/tests/level1/test_connection_primitives.py +0 -0
  215. {matrx_orm-3.1.78 → matrx_orm-3.1.79}/tests/level1/test_count_composite_distinct.py +0 -0
  216. {matrx_orm-3.1.78 → matrx_orm-3.1.79}/tests/level1/test_db_function_field.py +0 -0
  217. {matrx_orm-3.1.78 → matrx_orm-3.1.79}/tests/level1/test_ddl_generator.py +0 -0
  218. {matrx_orm-3.1.78 → matrx_orm-3.1.79}/tests/level1/test_derived_table.py +0 -0
  219. {matrx_orm-3.1.78 → matrx_orm-3.1.79}/tests/level1/test_dirty_tracking.py +0 -0
  220. {matrx_orm-3.1.78 → matrx_orm-3.1.79}/tests/level1/test_dynamic_crud.py +0 -0
  221. {matrx_orm-3.1.78 → matrx_orm-3.1.79}/tests/level1/test_execute_query_integrity_mapping.py +0 -0
  222. {matrx_orm-3.1.78 → matrx_orm-3.1.79}/tests/level1/test_expression_primitives.py +0 -0
  223. {matrx_orm-3.1.78 → matrx_orm-3.1.79}/tests/level1/test_fields.py +0 -0
  224. {matrx_orm-3.1.78 → matrx_orm-3.1.79}/tests/level1/test_filter_jsonb_agg_array_index.py +0 -0
  225. {matrx_orm-3.1.78 → matrx_orm-3.1.79}/tests/level1/test_from_alias.py +0 -0
  226. {matrx_orm-3.1.78 → matrx_orm-3.1.79}/tests/level1/test_fts.py +0 -0
  227. {matrx_orm-3.1.78 → matrx_orm-3.1.79}/tests/level1/test_func_expression_args.py +0 -0
  228. {matrx_orm-3.1.78 → matrx_orm-3.1.79}/tests/level1/test_graph_walk.py +0 -0
  229. {matrx_orm-3.1.78 → matrx_orm-3.1.79}/tests/level1/test_having_annotate_alias.py +0 -0
  230. {matrx_orm-3.1.78 → matrx_orm-3.1.79}/tests/level1/test_insert_ignore_and_admin_primitives.py +0 -0
  231. {matrx_orm-3.1.78 → matrx_orm-3.1.79}/tests/level1/test_introspect_rls_policies.py +0 -0
  232. {matrx_orm-3.1.78 → matrx_orm-3.1.79}/tests/level1/test_join_and_aggregate.py +0 -0
  233. {matrx_orm-3.1.78 → matrx_orm-3.1.79}/tests/level1/test_latest_rows.py +0 -0
  234. {matrx_orm-3.1.78 → matrx_orm-3.1.79}/tests/level1/test_listen_notify.py +0 -0
  235. {matrx_orm-3.1.78 → matrx_orm-3.1.79}/tests/level1/test_local_sqlite.py +0 -0
  236. {matrx_orm-3.1.78 → matrx_orm-3.1.79}/tests/level1/test_managed_write_guard.py +0 -0
  237. {matrx_orm-3.1.78 → matrx_orm-3.1.79}/tests/level1/test_matrx_entity.py +0 -0
  238. {matrx_orm-3.1.78 → matrx_orm-3.1.79}/tests/level1/test_migration_diff_types.py +0 -0
  239. {matrx_orm-3.1.78 → matrx_orm-3.1.79}/tests/level1/test_migration_loader.py +0 -0
  240. {matrx_orm-3.1.78 → matrx_orm-3.1.79}/tests/level1/test_model_instance.py +0 -0
  241. {matrx_orm-3.1.78 → matrx_orm-3.1.79}/tests/level1/test_model_meta.py +0 -0
  242. {matrx_orm-3.1.78 → matrx_orm-3.1.79}/tests/level1/test_optimistic_concurrency.py +0 -0
  243. {matrx_orm-3.1.78 → matrx_orm-3.1.79}/tests/level1/test_order_by_expression.py +0 -0
  244. {matrx_orm-3.1.78 → matrx_orm-3.1.79}/tests/level1/test_parameter_name_hydration.py +0 -0
  245. {matrx_orm-3.1.78 → matrx_orm-3.1.79}/tests/level1/test_patch_jsonb_path.py +0 -0
  246. {matrx_orm-3.1.78 → matrx_orm-3.1.79}/tests/level1/test_pool_holder_census.py +0 -0
  247. {matrx_orm-3.1.78 → matrx_orm-3.1.79}/tests/level1/test_pool_loop_recreation.py +0 -0
  248. {matrx_orm-3.1.78 → matrx_orm-3.1.79}/tests/level1/test_postgrest_filters.py +0 -0
  249. {matrx_orm-3.1.78 → matrx_orm-3.1.79}/tests/level1/test_pydantic_bridge.py +0 -0
  250. {matrx_orm-3.1.78 → matrx_orm-3.1.79}/tests/level1/test_query_builder.py +0 -0
  251. {matrx_orm-3.1.78 → matrx_orm-3.1.79}/tests/level1/test_query_error_detail.py +0 -0
  252. {matrx_orm-3.1.78 → matrx_orm-3.1.79}/tests/level1/test_query_executor_sql.py +0 -0
  253. {matrx_orm-3.1.78 → matrx_orm-3.1.79}/tests/level1/test_query_timeout.py +0 -0
  254. {matrx_orm-3.1.78 → matrx_orm-3.1.79}/tests/level1/test_querybuilder_clone_semantics.py +0 -0
  255. {matrx_orm-3.1.78 → matrx_orm-3.1.79}/tests/level1/test_queue_claim.py +0 -0
  256. {matrx_orm-3.1.78 → matrx_orm-3.1.79}/tests/level1/test_registry.py +0 -0
  257. {matrx_orm-3.1.78 → matrx_orm-3.1.79}/tests/level1/test_registry_multi_database.py +0 -0
  258. {matrx_orm-3.1.78 → matrx_orm-3.1.79}/tests/level1/test_registry_multi_schema.py +0 -0
  259. {matrx_orm-3.1.78 → matrx_orm-3.1.79}/tests/level1/test_relational_backfill.py +0 -0
  260. {matrx_orm-3.1.78 → matrx_orm-3.1.79}/tests/level1/test_relations.py +0 -0
  261. {matrx_orm-3.1.78 → matrx_orm-3.1.79}/tests/level1/test_resilience.py +0 -0
  262. {matrx_orm-3.1.78 → matrx_orm-3.1.79}/tests/level1/test_rls_session_scope.py +0 -0
  263. {matrx_orm-3.1.78 → matrx_orm-3.1.79}/tests/level1/test_schema_exists.py +0 -0
  264. {matrx_orm-3.1.78 → matrx_orm-3.1.79}/tests/level1/test_session_advisory_lock.py +0 -0
  265. {matrx_orm-3.1.78 → matrx_orm-3.1.79}/tests/level1/test_state_cache.py +0 -0
  266. {matrx_orm-3.1.78 → matrx_orm-3.1.79}/tests/level1/test_subquery_filter_raw.py +0 -0
  267. {matrx_orm-3.1.78 → matrx_orm-3.1.79}/tests/level1/test_subquery_in_filter.py +0 -0
  268. {matrx_orm-3.1.78 → matrx_orm-3.1.79}/tests/level1/test_supabase_auth.py +0 -0
  269. {matrx_orm-3.1.78 → matrx_orm-3.1.79}/tests/level1/test_supabase_config.py +0 -0
  270. {matrx_orm-3.1.78 → matrx_orm-3.1.79}/tests/level1/test_tls.py +0 -0
  271. {matrx_orm-3.1.78 → matrx_orm-3.1.79}/tests/level1/test_typed_join_projection.py +0 -0
  272. {matrx_orm-3.1.78 → matrx_orm-3.1.79}/tests/level1/test_typed_predicates.py +0 -0
  273. {matrx_orm-3.1.78 → matrx_orm-3.1.79}/tests/level1/test_update_case_expression.py +0 -0
  274. {matrx_orm-3.1.78 → matrx_orm-3.1.79}/tests/level1/test_update_subquery.py +0 -0
  275. {matrx_orm-3.1.78 → matrx_orm-3.1.79}/tests/level1/test_update_with_rebase.py +0 -0
  276. {matrx_orm-3.1.78 → matrx_orm-3.1.79}/tests/level1/test_upsert_default_leak.py +0 -0
  277. {matrx_orm-3.1.78 → matrx_orm-3.1.79}/tests/level1/test_upsert_with_conflict.py +0 -0
  278. {matrx_orm-3.1.78 → matrx_orm-3.1.79}/tests/level1/test_values_jsonb_decode.py +0 -0
  279. {matrx_orm-3.1.78 → matrx_orm-3.1.79}/tests/level1/test_watched_lifecycle.py +0 -0
  280. {matrx_orm-3.1.78 → matrx_orm-3.1.79}/tests/level1/test_write_retry_semantics.py +0 -0
  281. {matrx_orm-3.1.78 → matrx_orm-3.1.79}/tests/level2/MODULE_README.md +0 -0
  282. {matrx_orm-3.1.78 → matrx_orm-3.1.79}/tests/level2/__init__.py +0 -0
  283. {matrx_orm-3.1.78 → matrx_orm-3.1.79}/tests/level2/conftest.py +0 -0
  284. {matrx_orm-3.1.78 → matrx_orm-3.1.79}/tests/level2/test_bulk_ops.py +0 -0
  285. {matrx_orm-3.1.78 → matrx_orm-3.1.79}/tests/level2/test_cache_integration.py +0 -0
  286. {matrx_orm-3.1.78 → matrx_orm-3.1.79}/tests/level2/test_crud.py +0 -0
  287. {matrx_orm-3.1.78 → matrx_orm-3.1.79}/tests/level2/test_foreign_keys.py +0 -0
  288. {matrx_orm-3.1.78 → matrx_orm-3.1.79}/tests/level2/test_m2m.py +0 -0
  289. {matrx_orm-3.1.78 → matrx_orm-3.1.79}/tests/level2/test_manager.py +0 -0
  290. {matrx_orm-3.1.78 → matrx_orm-3.1.79}/tests/level2/test_migrations_live.py +0 -0
  291. {matrx_orm-3.1.78 → matrx_orm-3.1.79}/tests/level2/test_query_execution.py +0 -0
  292. {matrx_orm-3.1.78 → matrx_orm-3.1.79}/tests/level2/test_schema_diff.py +0 -0
  293. {matrx_orm-3.1.78 → matrx_orm-3.1.79}/tests/live_pooler_role.py +0 -0
  294. {matrx_orm-3.1.78 → matrx_orm-3.1.79}/tests/sample_project/.env.example +0 -0
  295. {matrx_orm-3.1.78 → matrx_orm-3.1.79}/tests/sample_project/README.md +0 -0
  296. {matrx_orm-3.1.78 → matrx_orm-3.1.79}/tests/sample_project/__init__.py +0 -0
  297. {matrx_orm-3.1.78 → matrx_orm-3.1.79}/tests/sample_project/generate.py +0 -0
  298. {matrx_orm-3.1.78 → matrx_orm-3.1.79}/tests/sample_project/generated/.gitkeep +0 -0
  299. {matrx_orm-3.1.78 → matrx_orm-3.1.79}/tests/sample_project/matrx_orm.yaml +0 -0
  300. {matrx_orm-3.1.78 → matrx_orm-3.1.79}/tests/sample_project/test_schema_generation.py +0 -0
  301. {matrx_orm-3.1.78 → matrx_orm-3.1.79}/tests/sample_project_desktop/.env.example +0 -0
  302. {matrx_orm-3.1.78 → matrx_orm-3.1.79}/tests/sample_project_desktop/README.md +0 -0
  303. {matrx_orm-3.1.78 → matrx_orm-3.1.79}/tests/sample_project_desktop/__init__.py +0 -0
  304. {matrx_orm-3.1.78 → matrx_orm-3.1.79}/tests/sample_project_desktop/client_example.py +0 -0
  305. {matrx_orm-3.1.78 → matrx_orm-3.1.79}/tests/sample_project_desktop/client_example.ts +0 -0
  306. {matrx_orm-3.1.78 → matrx_orm-3.1.79}/tests/sample_project_desktop/client_supabase_example.py +0 -0
  307. {matrx_orm-3.1.78 → matrx_orm-3.1.79}/tests/sample_project_desktop/server.py +0 -0
  308. {matrx_orm-3.1.78 → matrx_orm-3.1.79}/tests/schema/entity_tests.py +0 -0
  309. {matrx_orm-3.1.78 → matrx_orm-3.1.79}/tests/schema/test_base_generation.py +0 -0
  310. {matrx_orm-3.1.78 → matrx_orm-3.1.79}/tests/schema/test_composite_pk_fk_generation.py +0 -0
  311. {matrx_orm-3.1.78 → matrx_orm-3.1.79}/tests/schema/test_composite_unique_generation.py +0 -0
  312. {matrx_orm-3.1.78 → matrx_orm-3.1.79}/tests/schema/test_dto_identity_generation.py +0 -0
  313. {matrx_orm-3.1.78 → matrx_orm-3.1.79}/tests/schema/test_entity_capability_generation.py +0 -0
  314. {matrx_orm-3.1.78 → matrx_orm-3.1.79}/tests/schema/test_generate_schema.py +0 -0
  315. {matrx_orm-3.1.78 → matrx_orm-3.1.79}/tests/schema/test_junction_analysis.py +0 -0
  316. {matrx_orm-3.1.78 → matrx_orm-3.1.79}/tests/schema/test_multi_schema_output.py +0 -0
  317. {matrx_orm-3.1.78 → matrx_orm-3.1.79}/tests/schema/test_sql_expression_defaults.py +0 -0
  318. {matrx_orm-3.1.78 → matrx_orm-3.1.79}/tests/schema/test_table_relationships_query.py +0 -0
  319. {matrx_orm-3.1.78 → matrx_orm-3.1.79}/tests/schema/test_view_generation.py +0 -0
  320. {matrx_orm-3.1.78 → matrx_orm-3.1.79}/tests/schema_builder/test_array_default_parsing.py +0 -0
  321. {matrx_orm-3.1.78 → matrx_orm-3.1.79}/tests/schema_builder/test_focused_generation_plan.py +0 -0
  322. {matrx_orm-3.1.78 → matrx_orm-3.1.79}/tests/schema_builder/test_host_models_aggregator.py +0 -0
  323. {matrx_orm-3.1.78 → matrx_orm-3.1.79}/tests/session/__init__.py +0 -0
  324. {matrx_orm-3.1.78 → matrx_orm-3.1.79}/tests/session/conftest.py +0 -0
  325. {matrx_orm-3.1.78 → matrx_orm-3.1.79}/tests/session/test_cache_no_downgrade.py +0 -0
  326. {matrx_orm-3.1.78 → matrx_orm-3.1.79}/tests/session/test_capture_poison_proof.py +0 -0
  327. {matrx_orm-3.1.78 → matrx_orm-3.1.79}/tests/session/test_coalesce.py +0 -0
  328. {matrx_orm-3.1.78 → matrx_orm-3.1.79}/tests/session/test_dag.py +0 -0
  329. {matrx_orm-3.1.78 → matrx_orm-3.1.79}/tests/session/test_disk_spill_fallback.py +0 -0
  330. {matrx_orm-3.1.78 → matrx_orm-3.1.79}/tests/session/test_flush_individual_fallback.py +0 -0
  331. {matrx_orm-3.1.78 → matrx_orm-3.1.79}/tests/session/test_governed_write.py +0 -0
  332. {matrx_orm-3.1.78 → matrx_orm-3.1.79}/tests/session/test_managed.py +0 -0
  333. {matrx_orm-3.1.78 → matrx_orm-3.1.79}/tests/session/test_op_enqueue_site.py +0 -0
  334. {matrx_orm-3.1.78 → matrx_orm-3.1.79}/tests/session/test_reads.py +0 -0
  335. {matrx_orm-3.1.78 → matrx_orm-3.1.79}/tests/session/test_session.py +0 -0
  336. {matrx_orm-3.1.78 → matrx_orm-3.1.79}/tests/test_database_name_alias.py +0 -0
  337. {matrx_orm-3.1.78 → matrx_orm-3.1.79}/tests/test_db_functions.py +0 -0
  338. {matrx_orm-3.1.78 → matrx_orm-3.1.79}/tests/test_model_cls_refactor.py +0 -0
  339. {matrx_orm-3.1.78 → matrx_orm-3.1.79}/tests/test_no_blocking_asyncpg_tls.py +0 -0
  340. {matrx_orm-3.1.78 → matrx_orm-3.1.79}/tests/test_one_database.py +0 -0
  341. {matrx_orm-3.1.78 → matrx_orm-3.1.79}/tests/test_pooler_role_guard.py +0 -0
  342. {matrx_orm-3.1.78 → matrx_orm-3.1.79}/tests/test_secrets_battery.py +0 -0
  343. {matrx_orm-3.1.78 → matrx_orm-3.1.79}/tests/test_sql_param_casts.py +0 -0
  344. {matrx_orm-3.1.78 → matrx_orm-3.1.79}/tests/test_sync_twins_guard.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: matrx-orm
3
- Version: 3.1.78
3
+ Version: 3.1.79
4
4
  Summary: Async-first PostgreSQL ORM with bidirectional migrations, schema introspection, many-to-many relationships, and built-in state caching
5
5
  Project-URL: Homepage, https://github.com/AI-Matrix-Engine/aidream-current
6
6
  Project-URL: Repository, https://github.com/AI-Matrix-Engine/aidream-current
@@ -36,7 +36,12 @@ labels (`unique`, `foreign_key`, `not_null`, `check`, `exclusion`, and
36
36
  It is diagnostic and intentionally verbose, so a known PostgreSQL error must
37
37
  never reach that fallback.
38
38
 
39
- Verified against code: 2026-08-09.
39
+ `QueryTimeoutError` diagnoses by SQL verb. A fresh `INSERT` points first to a
40
+ relation/DDL lock convoy, FK parent lock, or trigger work—not same-row fan-out.
41
+ `UPDATE`/`DELETE` retain the hot-row diagnosis while also naming a database-wide
42
+ DDL convoy when unrelated relations fail in the same window.
43
+
44
+ Verified against code: 2026-08-17.
40
45
 
41
46
  ## Pool recovery
42
47
 
@@ -68,10 +73,15 @@ as `releasing` until reset really finishes. If reset exceeds the bound, asyncpg
68
73
  discards the physical connection and the failure is logged loudly rather than
69
74
  leaving an invisible checkout forever.
70
75
 
76
+ **Pool growth never blocks reuse.** Only physical connection setup is serialized.
77
+ Waiters behind a slow TLS/database handshake continue checking for healthy holders
78
+ released by normal traffic, so an available connection cannot sit idle until the
79
+ outer acquire deadline expires.
80
+
71
81
  Pinned by `tests/level1/test_pool_holder_census.py` and
72
82
  `tests/level1/test_pool_maintenance.py`.
73
83
 
74
- Verified against code: 2026-08-15.
84
+ Verified against code: 2026-08-18.
75
85
 
76
86
  ## Transaction-pooled role scope
77
87
 
@@ -107,6 +117,10 @@ Verified against code: 2026-08-17.
107
117
 
108
118
  ## Change log
109
119
 
120
+ - 2026-08-18: Let waiters reuse healthy connections released behind a slow
121
+ serialized-growth handshake.
122
+ - 2026-08-17: Corrected INSERT timeout guidance to identify relation/DDL lock
123
+ convoys before application row contention.
110
124
  - 2026-08-17: Required direct before/after role evidence before recording
111
125
  contamination; concurrent empty-to-nonempty reads reconcile without a false
112
126
  repair-queue row, and session-authorization contamination is reset too.
@@ -120,6 +120,7 @@ READ_TIMEOUT_RETRY_BACKOFF = 0.25 # seconds before the read retry
120
120
  POOL_MAINTENANCE_SWEEP_SECONDS = 45.0
121
121
  POOL_MAINTENANCE_MIN_INTERVAL = 0.25
122
122
  POOL_MAINTENANCE_MAX_BACKOFF = 30.0
123
+ POOL_GROWTH_GATE_POLL_SECONDS = 0.05
123
124
 
124
125
 
125
126
  try:
@@ -708,21 +709,45 @@ class AsyncDatabaseManager:
708
709
  try:
709
710
  ready = await acquire_connected_holder(pool)
710
711
  except UnsupportedPoolInternalsError:
711
- ready = None
712
+ lock = cls._growth_locks.setdefault(config_name, asyncio.Lock())
713
+ async with lock:
714
+ return await pool.acquire()
712
715
  if ready is not None:
713
716
  return ready
714
717
 
715
718
  lock = cls._growth_locks.setdefault(config_name, asyncio.Lock())
716
- async with lock:
717
- # Maintenance or the previous waiter may have established/released
718
- # a ready holder while this caller waited for the growth gate.
719
+ while True:
720
+ try:
721
+ await asyncio.wait_for(
722
+ lock.acquire(),
723
+ timeout=POOL_GROWTH_GATE_POLL_SECONDS,
724
+ )
725
+ except TimeoutError:
726
+ # A slow TLS/database handshake owns the growth gate. Traffic
727
+ # must still consume healthy connections released meanwhile;
728
+ # otherwise those slots sit idle while every waiter times out
729
+ # behind work that has no bearing on them.
730
+ try:
731
+ ready = await acquire_connected_holder(pool)
732
+ except UnsupportedPoolInternalsError:
733
+ async with lock:
734
+ return await pool.acquire()
735
+ if ready is not None:
736
+ return ready
737
+ continue
738
+
719
739
  try:
720
- ready = await acquire_connected_holder(pool)
721
- except UnsupportedPoolInternalsError:
722
- ready = None
723
- if ready is not None:
724
- return ready
725
- return await pool.acquire()
740
+ # Maintenance or the previous waiter may have established or
741
+ # released a ready holder while this caller waited briefly.
742
+ try:
743
+ ready = await acquire_connected_holder(pool)
744
+ except UnsupportedPoolInternalsError:
745
+ return await pool.acquire()
746
+ if ready is not None:
747
+ return ready
748
+ return await pool.acquire()
749
+ finally:
750
+ lock.release()
726
751
 
727
752
  @classmethod
728
753
  @asynccontextmanager
@@ -1102,8 +1127,9 @@ class AsyncDatabaseManager:
1102
1127
  # asyncpg's command_timeout fired — query held a connection
1103
1128
  # but didn't return in time. NOT a pool-acquire timeout
1104
1129
  # (that's PoolAcquireTimeoutError, raised in get_connection
1105
- # before the connection is held). Most common cause:
1106
- # row-lock contention on a hot row.
1130
+ # before the connection is held). Diagnose by query verb:
1131
+ # INSERT commonly means relation/DDL/FK/trigger waits, while
1132
+ # UPDATE/DELETE commonly means a hot-row lock.
1107
1133
  raise QueryTimeoutError(
1108
1134
  operation="execute_query",
1109
1135
  query=query,
@@ -1225,8 +1251,8 @@ class AsyncDatabaseManager:
1225
1251
  ) from None
1226
1252
  except TimeoutError as e:
1227
1253
  # asyncpg's command_timeout fired — query held a
1228
- # connection but didn't return in time. See twin comment
1229
- # in the tx_conn branch above for the full rationale.
1254
+ # connection but didn't return in time. See the twin
1255
+ # query-verb diagnosis in the tx_conn branch above.
1230
1256
  # A READ is side-effect-free → retry once on a fresh
1231
1257
  # connection before surfacing the timeout (self-heal for
1232
1258
  # starvation blips). Writes fall straight through to raise.
@@ -1008,10 +1008,11 @@ class QueryTimeoutError(DatabaseError):
1008
1008
  timeout = self.details.get("timeout")
1009
1009
  query = self._str("query")
1010
1010
  args = self.details.get("args", [])
1011
- # First-token heuristic: SELECT timeouts and UPDATE/INSERT/DELETE
1012
- # timeouts have totally different root-cause distributions, so
1013
- # the hint changes accordingly. Reads almost never row-lock under
1014
- # MVCC; writes almost always do under fan-out concurrency.
1011
+ # First-token heuristic: SELECT, INSERT, and UPDATE/DELETE timeouts
1012
+ # have different root-cause distributions, so the hint changes
1013
+ # accordingly. In particular, a fresh INSERT cannot be contending on
1014
+ # the row it is creating; relation/DDL locks and FK/trigger work must
1015
+ # be investigated before blaming application fan-out.
1015
1016
  query_kind = _classify_query_shape(query)
1016
1017
  lines = ["\n" + "-" * 80]
1017
1018
  lines.append("Matrx ORM | QueryTimeoutError")
@@ -1054,25 +1055,40 @@ class QueryTimeoutError(DatabaseError):
1054
1055
  lines.append(" - Safe to retry ONCE for cold-cache / transient cases. If it times out")
1055
1056
  lines.append(" twice in a row, fix the index — retrying won't help.")
1056
1057
  elif query_kind == "write":
1057
- # Writes are where row-lock contention actually bites.
1058
- lines.append(
1059
- " - Write query ({op}). Most common cause: row-lock contention.".format(
1060
- op=query_kind.upper()
1061
- if query_kind != "write"
1062
- else (query.lstrip().split(None, 1)[0].upper() if query else "WRITE")
1058
+ write_verb = query.lstrip().split(None, 1)[0].upper() if query else "WRITE"
1059
+ lines.append(f" - Write query ({write_verb}).")
1060
+ if write_verb == "INSERT":
1061
+ lines.append(
1062
+ " - A fresh INSERT is not waiting on the row it is creating. Check, in"
1063
1063
  )
1064
- )
1065
- lines.append(" If N async tasks UPDATE the same row in a tight loop they serialize")
1066
- lines.append(" on the row lock and the late ones blow past command_timeout. Either")
1067
- lines.append(" (a) batch the writes, (b) replace read-modify-write with an atomic")
1068
- lines.append(" SQL UPDATE (e.g. ``col = col || jsonb_build_object(...)``),")
1069
- lines.append(" or (c) gate the fan-out via RequestLane ``db_write`` bucket caps.")
1070
- lines.append(" - Less common: missing index on a WHERE clause (forces a slow scan to")
1071
- lines.append(" locate the row to update), server-side overload, statement_timeout.")
1064
+ lines.append(
1065
+ " order: a queued live DDL/relation lock; FK parent-row locks; trigger"
1066
+ )
1067
+ lines.append(
1068
+ " work; then database-wide overload. Correlate other unrelated query"
1069
+ )
1070
+ lines.append(
1071
+ " timeouts in the same window before changing application concurrency."
1072
+ )
1073
+ else:
1074
+ lines.append(
1075
+ " - UPDATE/DELETE commonly wait on a hot row. If N tasks change the same"
1076
+ )
1077
+ lines.append(
1078
+ " row, batch them, use one atomic SQL update, or gate that write lane."
1079
+ )
1080
+ lines.append(
1081
+ " - Also check for a queued live DDL/relation lock when unrelated tables"
1082
+ )
1083
+ lines.append(
1084
+ " time out in the same window; that is a database-wide lock convoy."
1085
+ )
1086
+ lines.append(" - A missing WHERE-clause index, trigger work, or sustained server load")
1087
+ lines.append(" can also exceed command_timeout.")
1072
1088
  lines.append(
1073
- " - NOT safe to retry blindly — if it's row-lock contention, a retry just"
1089
+ " - NOT safe to retry blindly — a write may have ambiguous completion, and"
1074
1090
  )
1075
- lines.append(" rejoins the back of the lock queue and times out again.")
1091
+ lines.append(" an unchanged lock convoy will time out again.")
1076
1092
  else:
1077
1093
  # DDL / CALL / unknown — keep both hints concise.
1078
1094
  lines.append(" - Couldn't classify the query shape — see the Query line above. Common")
@@ -1204,11 +1220,15 @@ class IntegrityError(DatabaseError):
1204
1220
  lines.append(' `null value in column "<name>"` and the rest of the message.')
1205
1221
  lines.append(" - Pass the field explicitly (or set a default on the model).")
1206
1222
  elif constraint == "check":
1207
- lines.append(" - A CHECK constraint rejected values that violate a database invariant.")
1223
+ lines.append(
1224
+ " - A CHECK constraint rejected values that violate a database invariant."
1225
+ )
1208
1226
  lines.append(" - Read the DB error above for the invariant and offending row.")
1209
1227
  lines.append(" - This is not transient; correct the input or the write ordering.")
1210
1228
  elif constraint == "exclusion":
1211
- lines.append(" - This row conflicts with an existing row under an exclusion constraint.")
1229
+ lines.append(
1230
+ " - This row conflicts with an existing row under an exclusion constraint."
1231
+ )
1212
1232
  lines.append(" - Read the DB error above for the conflicting key or range.")
1213
1233
  lines.append(" - This is not safe to blindly retry with unchanged input.")
1214
1234
  elif constraint == "restrict":
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "matrx-orm"
3
- version = "3.1.78"
3
+ version = "3.1.79"
4
4
  description = "Async-first PostgreSQL ORM with bidirectional migrations, schema introspection, many-to-many relationships, and built-in state caching"
5
5
  readme = "README.md"
6
6
  license = { text = "MIT" }
@@ -15,6 +15,7 @@ from matrx_orm.exceptions import (
15
15
  ParameterError,
16
16
  PermissionDeniedError,
17
17
  QueryError,
18
+ QueryTimeoutError,
18
19
  RelationshipError,
19
20
  SchemaMismatchError,
20
21
  StateError,
@@ -200,6 +201,28 @@ class TestSubclasses:
200
201
  e = UnknownDatabaseError(operation="insert", original_error=Exception("unknown"))
201
202
  assert "Unexpected database error" in e.message
202
203
 
204
+ def test_insert_timeout_diagnoses_relation_lock_before_row_contention(self):
205
+ e = QueryTimeoutError(
206
+ operation="execute_query",
207
+ query="INSERT INTO chat.agent_run (id) VALUES ($1)",
208
+ args=["run-id"],
209
+ timeout=20,
210
+ )
211
+ banner = e.format_message()
212
+ assert "fresh INSERT is not waiting on the row it is creating" in banner
213
+ assert "queued live DDL/relation lock" in banner
214
+
215
+ def test_update_timeout_keeps_hot_row_and_ddl_diagnoses(self):
216
+ e = QueryTimeoutError(
217
+ operation="execute_query",
218
+ query="UPDATE public.counter SET value = value + 1 WHERE id = $1",
219
+ args=["counter-id"],
220
+ timeout=20,
221
+ )
222
+ banner = e.format_message()
223
+ assert "UPDATE/DELETE commonly wait on a hot row" in banner
224
+ assert "database-wide lock convoy" in banner
225
+
203
226
  def test_schema_mismatch_column_derives_kind_from_sqlstate(self):
204
227
  pg = _FakePgError(
205
228
  "42703",
@@ -39,6 +39,8 @@ class FakeHolder:
39
39
  self.pool.worst_connecting,
40
40
  self.pool.connecting,
41
41
  )
42
+ if self.pool.connect_gate is not None:
43
+ await self.pool.connect_gate.wait()
42
44
  await asyncio.sleep(0.001)
43
45
  self.connects += 1
44
46
  self.connected = True
@@ -51,6 +53,7 @@ class FakeHolder:
51
53
  class FakePool:
52
54
  def __init__(self, connected: int, maximum: int) -> None:
53
55
  self._generation = 0
56
+ self.connect_gate: asyncio.Event | None = None
54
57
  self.connecting = 0
55
58
  self.worst_connecting = 0
56
59
  self._queue: asyncio.LifoQueue[FakeHolder] = asyncio.LifoQueue(maxsize=maximum)
@@ -204,6 +207,36 @@ async def test_idle_connection_does_not_let_burst_bypass_growth_gate() -> None:
204
207
  assert pool.worst_connecting == 1
205
208
 
206
209
 
210
+ @pytest.mark.asyncio
211
+ async def test_waiter_uses_connection_released_behind_slow_growth_gate() -> None:
212
+ pool = FakePool(connected=1, maximum=2)
213
+ pool.connect_gate = asyncio.Event()
214
+ AsyncDatabaseManager._growth_locks.clear()
215
+
216
+ held = await acquire_connected_holder(pool)
217
+ assert held is not None
218
+
219
+ growing = asyncio.create_task(
220
+ AsyncDatabaseManager._acquire_bounded_growth("slow-growth", pool)
221
+ )
222
+ await asyncio.sleep(0)
223
+ assert pool.connecting == 1
224
+
225
+ waiting = asyncio.create_task(
226
+ AsyncDatabaseManager._acquire_bounded_growth("slow-growth", pool)
227
+ )
228
+ await pool.release(held, timeout=1.0)
229
+
230
+ reused = await asyncio.wait_for(waiting, timeout=0.2)
231
+ assert reused.holder is held.holder
232
+ assert not growing.done()
233
+
234
+ pool.connect_gate.set()
235
+ grown = await growing
236
+ await pool.release(reused, timeout=1.0)
237
+ await pool.release(grown, timeout=1.0)
238
+
239
+
207
240
  @pytest.mark.asyncio
208
241
  async def test_zero_connection_pool_is_replaced_once_and_reacquired(monkeypatch) -> None:
209
242
  class DeadPool:
File without changes
File without changes
File without changes
File without changes
File without changes