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.
- {matrx_orm-3.1.78 → matrx_orm-3.1.79}/PKG-INFO +1 -1
- {matrx_orm-3.1.78 → matrx_orm-3.1.79}/matrx_orm/core/FEATURE.md +16 -2
- {matrx_orm-3.1.78 → matrx_orm-3.1.79}/matrx_orm/core/async_db_manager.py +40 -14
- {matrx_orm-3.1.78 → matrx_orm-3.1.79}/matrx_orm/exceptions.py +42 -22
- {matrx_orm-3.1.78 → matrx_orm-3.1.79}/pyproject.toml +1 -1
- {matrx_orm-3.1.78 → matrx_orm-3.1.79}/tests/level1/test_exceptions.py +23 -0
- {matrx_orm-3.1.78 → matrx_orm-3.1.79}/tests/level1/test_pool_maintenance.py +33 -0
- {matrx_orm-3.1.78 → matrx_orm-3.1.79}/.gitignore +0 -0
- {matrx_orm-3.1.78 → matrx_orm-3.1.79}/AGENTS.md +0 -0
- {matrx_orm-3.1.78 → matrx_orm-3.1.79}/BASE_CLASS_METHODS.md +0 -0
- {matrx_orm-3.1.78 → matrx_orm-3.1.79}/CLAUDE.md +0 -0
- {matrx_orm-3.1.78 → matrx_orm-3.1.79}/MODEL_API.md +0 -0
- {matrx_orm-3.1.78 → matrx_orm-3.1.79}/README.md +0 -0
- {matrx_orm-3.1.78 → matrx_orm-3.1.79}/database/orm/extended/managers/ai_model_base.py +0 -0
- {matrx_orm-3.1.78 → matrx_orm-3.1.79}/docs/migrations.md +0 -0
- {matrx_orm-3.1.78 → matrx_orm-3.1.79}/matrx_orm/MODULE_README.md +0 -0
- {matrx_orm-3.1.78 → matrx_orm-3.1.79}/matrx_orm/__init__.py +0 -0
- {matrx_orm-3.1.78 → matrx_orm-3.1.79}/matrx_orm/adapters/MODULE_README.md +0 -0
- {matrx_orm-3.1.78 → matrx_orm-3.1.79}/matrx_orm/adapters/__init__.py +0 -0
- {matrx_orm-3.1.78 → matrx_orm-3.1.79}/matrx_orm/adapters/async_postgresql.py +0 -0
- {matrx_orm-3.1.78 → matrx_orm-3.1.79}/matrx_orm/adapters/base_adapter.py +0 -0
- {matrx_orm-3.1.78 → matrx_orm-3.1.79}/matrx_orm/adapters/postgresql.py +0 -0
- {matrx_orm-3.1.78 → matrx_orm-3.1.79}/matrx_orm/adapters/postgrest_client_adapter.py +0 -0
- {matrx_orm-3.1.78 → matrx_orm-3.1.79}/matrx_orm/adapters/supabase_adapter.py +0 -0
- {matrx_orm-3.1.78 → matrx_orm-3.1.79}/matrx_orm/admin/FEATURE.md +0 -0
- {matrx_orm-3.1.78 → matrx_orm-3.1.79}/matrx_orm/admin/__init__.py +0 -0
- {matrx_orm-3.1.78 → matrx_orm-3.1.79}/matrx_orm/admin/router.py +0 -0
- {matrx_orm-3.1.78 → matrx_orm-3.1.79}/matrx_orm/api/__init__.py +0 -0
- {matrx_orm-3.1.78 → matrx_orm-3.1.79}/matrx_orm/api/auth.py +0 -0
- {matrx_orm-3.1.78 → matrx_orm-3.1.79}/matrx_orm/api/config.py +0 -0
- {matrx_orm-3.1.78 → matrx_orm-3.1.79}/matrx_orm/api/handlers.py +0 -0
- {matrx_orm-3.1.78 → matrx_orm-3.1.79}/matrx_orm/api/protocol.py +0 -0
- {matrx_orm-3.1.78 → matrx_orm-3.1.79}/matrx_orm/api/server.py +0 -0
- {matrx_orm-3.1.78 → matrx_orm-3.1.79}/matrx_orm/associations.py +0 -0
- {matrx_orm-3.1.78 → matrx_orm-3.1.79}/matrx_orm/cache_debug.py +0 -0
- {matrx_orm-3.1.78 → matrx_orm-3.1.79}/matrx_orm/catalog.py +0 -0
- {matrx_orm-3.1.78 → matrx_orm-3.1.79}/matrx_orm/catalog_sql.py +0 -0
- {matrx_orm-3.1.78 → matrx_orm-3.1.79}/matrx_orm/client/MODULE_README.md +0 -0
- {matrx_orm-3.1.78 → matrx_orm-3.1.79}/matrx_orm/client/__init__.py +0 -0
- {matrx_orm-3.1.78 → matrx_orm-3.1.79}/matrx_orm/client/postgres_connection.py +0 -0
- {matrx_orm-3.1.78 → matrx_orm-3.1.79}/matrx_orm/client/postgrest.py +0 -0
- {matrx_orm-3.1.78 → matrx_orm-3.1.79}/matrx_orm/client/supabase_auth.py +0 -0
- {matrx_orm-3.1.78 → matrx_orm-3.1.79}/matrx_orm/client/supabase_config.py +0 -0
- {matrx_orm-3.1.78 → matrx_orm-3.1.79}/matrx_orm/client/supabase_manager.py +0 -0
- {matrx_orm-3.1.78 → matrx_orm-3.1.79}/matrx_orm/core/EXTENDED-TASK.md +0 -0
- {matrx_orm-3.1.78 → matrx_orm-3.1.79}/matrx_orm/core/MODULE_README.md +0 -0
- {matrx_orm-3.1.78 → matrx_orm-3.1.79}/matrx_orm/core/RELATIONS-TASKS.md +0 -0
- {matrx_orm-3.1.78 → matrx_orm-3.1.79}/matrx_orm/core/TASKS.md +0 -0
- {matrx_orm-3.1.78 → matrx_orm-3.1.79}/matrx_orm/core/__init__.py +0 -0
- {matrx_orm-3.1.78 → matrx_orm-3.1.79}/matrx_orm/core/base.py +0 -0
- {matrx_orm-3.1.78 → matrx_orm-3.1.79}/matrx_orm/core/config.py +0 -0
- {matrx_orm-3.1.78 → matrx_orm-3.1.79}/matrx_orm/core/connections.py +0 -0
- {matrx_orm-3.1.78 → matrx_orm-3.1.79}/matrx_orm/core/db_function.py +0 -0
- {matrx_orm-3.1.78 → matrx_orm-3.1.79}/matrx_orm/core/diagnostics.py +0 -0
- {matrx_orm-3.1.78 → matrx_orm-3.1.79}/matrx_orm/core/expressions.py +0 -0
- {matrx_orm-3.1.78 → matrx_orm-3.1.79}/matrx_orm/core/extended.py +0 -0
- {matrx_orm-3.1.78 → matrx_orm-3.1.79}/matrx_orm/core/fields.py +0 -0
- {matrx_orm-3.1.78 → matrx_orm-3.1.79}/matrx_orm/core/introspection.py +0 -0
- {matrx_orm-3.1.78 → matrx_orm-3.1.79}/matrx_orm/core/listen.py +0 -0
- {matrx_orm-3.1.78 → matrx_orm-3.1.79}/matrx_orm/core/loop_filters.py +0 -0
- {matrx_orm-3.1.78 → matrx_orm-3.1.79}/matrx_orm/core/model_dto.py +0 -0
- {matrx_orm-3.1.78 → matrx_orm-3.1.79}/matrx_orm/core/model_view.py +0 -0
- {matrx_orm-3.1.78 → matrx_orm-3.1.79}/matrx_orm/core/one_database.py +0 -0
- {matrx_orm-3.1.78 → matrx_orm-3.1.79}/matrx_orm/core/paginator.py +0 -0
- {matrx_orm-3.1.78 → matrx_orm-3.1.79}/matrx_orm/core/pool_maintenance.py +0 -0
- {matrx_orm-3.1.78 → matrx_orm-3.1.79}/matrx_orm/core/pool_watch.py +0 -0
- {matrx_orm-3.1.78 → matrx_orm-3.1.79}/matrx_orm/core/pooler_role.py +0 -0
- {matrx_orm-3.1.78 → matrx_orm-3.1.79}/matrx_orm/core/pydantic_bridge.py +0 -0
- {matrx_orm-3.1.78 → matrx_orm-3.1.79}/matrx_orm/core/registry.py +0 -0
- {matrx_orm-3.1.78 → matrx_orm-3.1.79}/matrx_orm/core/relations.py +0 -0
- {matrx_orm-3.1.78 → matrx_orm-3.1.79}/matrx_orm/core/resilience.py +0 -0
- {matrx_orm-3.1.78 → matrx_orm-3.1.79}/matrx_orm/core/rls_session.py +0 -0
- {matrx_orm-3.1.78 → matrx_orm-3.1.79}/matrx_orm/core/signals.py +0 -0
- {matrx_orm-3.1.78 → matrx_orm-3.1.79}/matrx_orm/core/tls.py +0 -0
- {matrx_orm-3.1.78 → matrx_orm-3.1.79}/matrx_orm/core/transaction.py +0 -0
- {matrx_orm-3.1.78 → matrx_orm-3.1.79}/matrx_orm/core/types.py +0 -0
- {matrx_orm-3.1.78 → matrx_orm-3.1.79}/matrx_orm/core/write_queue.py +0 -0
- {matrx_orm-3.1.78 → matrx_orm-3.1.79}/matrx_orm/entity.py +0 -0
- {matrx_orm-3.1.78 → matrx_orm-3.1.79}/matrx_orm/error_handling.py +0 -0
- {matrx_orm-3.1.78 → matrx_orm-3.1.79}/matrx_orm/extended/__init__.py +0 -0
- {matrx_orm-3.1.78 → matrx_orm-3.1.79}/matrx_orm/extended/app_error_handler.py +0 -0
- {matrx_orm-3.1.78 → matrx_orm-3.1.79}/matrx_orm/identity_rule.py +0 -0
- {matrx_orm-3.1.78 → matrx_orm-3.1.79}/matrx_orm/local_sqlite.py +0 -0
- {matrx_orm-3.1.78 → matrx_orm-3.1.79}/matrx_orm/middleware/__init__.py +0 -0
- {matrx_orm-3.1.78 → matrx_orm-3.1.79}/matrx_orm/middleware/base.py +0 -0
- {matrx_orm-3.1.78 → matrx_orm-3.1.79}/matrx_orm/migrations/MODULE_README.md +0 -0
- {matrx_orm-3.1.78 → matrx_orm-3.1.79}/matrx_orm/migrations/__init__.py +0 -0
- {matrx_orm-3.1.78 → matrx_orm-3.1.79}/matrx_orm/migrations/cli.py +0 -0
- {matrx_orm-3.1.78 → matrx_orm-3.1.79}/matrx_orm/migrations/ddl.py +0 -0
- {matrx_orm-3.1.78 → matrx_orm-3.1.79}/matrx_orm/migrations/diff.py +0 -0
- {matrx_orm-3.1.78 → matrx_orm-3.1.79}/matrx_orm/migrations/executor.py +0 -0
- {matrx_orm-3.1.78 → matrx_orm-3.1.79}/matrx_orm/migrations/integration.py +0 -0
- {matrx_orm-3.1.78 → matrx_orm-3.1.79}/matrx_orm/migrations/loader.py +0 -0
- {matrx_orm-3.1.78 → matrx_orm-3.1.79}/matrx_orm/migrations/operations.py +0 -0
- {matrx_orm-3.1.78 → matrx_orm-3.1.79}/matrx_orm/migrations/state.py +0 -0
- {matrx_orm-3.1.78 → matrx_orm-3.1.79}/matrx_orm/migrations/table_filter.py +0 -0
- {matrx_orm-3.1.78 → matrx_orm-3.1.79}/matrx_orm/operations/FEATURE.md +0 -0
- {matrx_orm-3.1.78 → matrx_orm-3.1.79}/matrx_orm/operations/MODULE_README.md +0 -0
- {matrx_orm-3.1.78 → matrx_orm-3.1.79}/matrx_orm/operations/__init__.py +0 -0
- {matrx_orm-3.1.78 → matrx_orm-3.1.79}/matrx_orm/operations/bulk_update_values.py +0 -0
- {matrx_orm-3.1.78 → matrx_orm-3.1.79}/matrx_orm/operations/conflict.py +0 -0
- {matrx_orm-3.1.78 → matrx_orm-3.1.79}/matrx_orm/operations/conflict_writes.py +0 -0
- {matrx_orm-3.1.78 → matrx_orm-3.1.79}/matrx_orm/operations/create.py +0 -0
- {matrx_orm-3.1.78 → matrx_orm-3.1.79}/matrx_orm/operations/db_functions.py +0 -0
- {matrx_orm-3.1.78 → matrx_orm-3.1.79}/matrx_orm/operations/delete.py +0 -0
- {matrx_orm-3.1.78 → matrx_orm-3.1.79}/matrx_orm/operations/dynamic_admin.py +0 -0
- {matrx_orm-3.1.78 → matrx_orm-3.1.79}/matrx_orm/operations/dynamic_crud.py +0 -0
- {matrx_orm-3.1.78 → matrx_orm-3.1.79}/matrx_orm/operations/graph_walk.py +0 -0
- {matrx_orm-3.1.78 → matrx_orm-3.1.79}/matrx_orm/operations/latest_rows.py +0 -0
- {matrx_orm-3.1.78 → matrx_orm-3.1.79}/matrx_orm/operations/queue_claim.py +0 -0
- {matrx_orm-3.1.78 → matrx_orm-3.1.79}/matrx_orm/operations/read.py +0 -0
- {matrx_orm-3.1.78 → matrx_orm-3.1.79}/matrx_orm/operations/relational_backfill.py +0 -0
- {matrx_orm-3.1.78 → matrx_orm-3.1.79}/matrx_orm/operations/staging_load.py +0 -0
- {matrx_orm-3.1.78 → matrx_orm-3.1.79}/matrx_orm/operations/update.py +0 -0
- {matrx_orm-3.1.78 → matrx_orm-3.1.79}/matrx_orm/platform_db.py +0 -0
- {matrx_orm-3.1.78 → matrx_orm-3.1.79}/matrx_orm/py.typed +0 -0
- {matrx_orm-3.1.78 → matrx_orm-3.1.79}/matrx_orm/python_sql/MODULE_README.md +0 -0
- {matrx_orm-3.1.78 → matrx_orm-3.1.79}/matrx_orm/python_sql/__init__.py +0 -0
- {matrx_orm-3.1.78 → matrx_orm-3.1.79}/matrx_orm/python_sql/db_objects.py +0 -0
- {matrx_orm-3.1.78 → matrx_orm-3.1.79}/matrx_orm/python_sql/table_detailed_relationships.py +0 -0
- {matrx_orm-3.1.78 → matrx_orm-3.1.79}/matrx_orm/python_sql/table_typescript_relationship.py +0 -0
- {matrx_orm-3.1.78 → matrx_orm-3.1.79}/matrx_orm/query/FEATURE.md +0 -0
- {matrx_orm-3.1.78 → matrx_orm-3.1.79}/matrx_orm/query/MODULE_README.md +0 -0
- {matrx_orm-3.1.78 → matrx_orm-3.1.79}/matrx_orm/query/__init__.py +0 -0
- {matrx_orm-3.1.78 → matrx_orm-3.1.79}/matrx_orm/query/builder.py +0 -0
- {matrx_orm-3.1.78 → matrx_orm-3.1.79}/matrx_orm/query/executor.py +0 -0
- {matrx_orm-3.1.78 → matrx_orm-3.1.79}/matrx_orm/schema_builder/FEATURE.md +0 -0
- {matrx_orm-3.1.78 → matrx_orm-3.1.79}/matrx_orm/schema_builder/MODULE_README.md +0 -0
- {matrx_orm-3.1.78 → matrx_orm-3.1.79}/matrx_orm/schema_builder/__init__.py +0 -0
- {matrx_orm-3.1.78 → matrx_orm-3.1.79}/matrx_orm/schema_builder/code_handler.py +0 -0
- {matrx_orm-3.1.78 → matrx_orm-3.1.79}/matrx_orm/schema_builder/codegen_writer.py +0 -0
- {matrx_orm-3.1.78 → matrx_orm-3.1.79}/matrx_orm/schema_builder/columns.py +0 -0
- {matrx_orm-3.1.78 → matrx_orm-3.1.79}/matrx_orm/schema_builder/common.py +0 -0
- {matrx_orm-3.1.78 → matrx_orm-3.1.79}/matrx_orm/schema_builder/diff_preview.py +0 -0
- {matrx_orm-3.1.78 → matrx_orm-3.1.79}/matrx_orm/schema_builder/entity_capabilities.py +0 -0
- {matrx_orm-3.1.78 → matrx_orm-3.1.79}/matrx_orm/schema_builder/generator.py +0 -0
- {matrx_orm-3.1.78 → matrx_orm-3.1.79}/matrx_orm/schema_builder/helpers/__init__.py +0 -0
- {matrx_orm-3.1.78 → matrx_orm-3.1.79}/matrx_orm/schema_builder/helpers/base_generators.py +0 -0
- {matrx_orm-3.1.78 → matrx_orm-3.1.79}/matrx_orm/schema_builder/helpers/entity_generators.py +0 -0
- {matrx_orm-3.1.78 → matrx_orm-3.1.79}/matrx_orm/schema_builder/helpers/git_checker.py +0 -0
- {matrx_orm-3.1.78 → matrx_orm-3.1.79}/matrx_orm/schema_builder/package_wiring.py +0 -0
- {matrx_orm-3.1.78 → matrx_orm-3.1.79}/matrx_orm/schema_builder/relationships.py +0 -0
- {matrx_orm-3.1.78 → matrx_orm-3.1.79}/matrx_orm/schema_builder/runner.py +0 -0
- {matrx_orm-3.1.78 → matrx_orm-3.1.79}/matrx_orm/schema_builder/schema.py +0 -0
- {matrx_orm-3.1.78 → matrx_orm-3.1.79}/matrx_orm/schema_builder/schema_manager.py +0 -0
- {matrx_orm-3.1.78 → matrx_orm-3.1.79}/matrx_orm/schema_builder/tables.py +0 -0
- {matrx_orm-3.1.78 → matrx_orm-3.1.79}/matrx_orm/schema_builder/views.py +0 -0
- {matrx_orm-3.1.78 → matrx_orm-3.1.79}/matrx_orm/secrets_battery/FEATURE.md +0 -0
- {matrx_orm-3.1.78 → matrx_orm-3.1.79}/matrx_orm/secrets_battery/__init__.py +0 -0
- {matrx_orm-3.1.78 → matrx_orm-3.1.79}/matrx_orm/secrets_battery/attachments.py +0 -0
- {matrx_orm-3.1.78 → matrx_orm-3.1.79}/matrx_orm/secrets_battery/crypto.py +0 -0
- {matrx_orm-3.1.78 → matrx_orm-3.1.79}/matrx_orm/secrets_battery/items.py +0 -0
- {matrx_orm-3.1.78 → matrx_orm-3.1.79}/matrx_orm/secrets_battery/model.py +0 -0
- {matrx_orm-3.1.78 → matrx_orm-3.1.79}/matrx_orm/secrets_battery/org_service.py +0 -0
- {matrx_orm-3.1.78 → matrx_orm-3.1.79}/matrx_orm/secrets_battery/service.py +0 -0
- {matrx_orm-3.1.78 → matrx_orm-3.1.79}/matrx_orm/session/README.md +0 -0
- {matrx_orm-3.1.78 → matrx_orm-3.1.79}/matrx_orm/session/__init__.py +0 -0
- {matrx_orm-3.1.78 → matrx_orm-3.1.79}/matrx_orm/session/coalesce.py +0 -0
- {matrx_orm-3.1.78 → matrx_orm-3.1.79}/matrx_orm/session/dag.py +0 -0
- {matrx_orm-3.1.78 → matrx_orm-3.1.79}/matrx_orm/session/errors.py +0 -0
- {matrx_orm-3.1.78 → matrx_orm-3.1.79}/matrx_orm/session/fallback.py +0 -0
- {matrx_orm-3.1.78 → matrx_orm-3.1.79}/matrx_orm/session/flush.py +0 -0
- {matrx_orm-3.1.78 → matrx_orm-3.1.79}/matrx_orm/session/lifecycle.py +0 -0
- {matrx_orm-3.1.78 → matrx_orm-3.1.79}/matrx_orm/session/managed.py +0 -0
- {matrx_orm-3.1.78 → matrx_orm-3.1.79}/matrx_orm/session/op.py +0 -0
- {matrx_orm-3.1.78 → matrx_orm-3.1.79}/matrx_orm/session/reads.py +0 -0
- {matrx_orm-3.1.78 → matrx_orm-3.1.79}/matrx_orm/session/session.py +0 -0
- {matrx_orm-3.1.78 → matrx_orm-3.1.79}/matrx_orm/session/telemetry.py +0 -0
- {matrx_orm-3.1.78 → matrx_orm-3.1.79}/matrx_orm/sql_executor/MODULE_README.md +0 -0
- {matrx_orm-3.1.78 → matrx_orm-3.1.79}/matrx_orm/sql_executor/__init__.py +0 -0
- {matrx_orm-3.1.78 → matrx_orm-3.1.79}/matrx_orm/sql_executor/executor.py +0 -0
- {matrx_orm-3.1.78 → matrx_orm-3.1.79}/matrx_orm/sql_executor/queries.py +0 -0
- {matrx_orm-3.1.78 → matrx_orm-3.1.79}/matrx_orm/sql_executor/registry.py +0 -0
- {matrx_orm-3.1.78 → matrx_orm-3.1.79}/matrx_orm/sql_executor/types.py +0 -0
- {matrx_orm-3.1.78 → matrx_orm-3.1.79}/matrx_orm/sql_executor/utils.py +0 -0
- {matrx_orm-3.1.78 → matrx_orm-3.1.79}/matrx_orm/state.py +0 -0
- {matrx_orm-3.1.78 → matrx_orm-3.1.79}/matrx_orm/utils/__init__.py +0 -0
- {matrx_orm-3.1.78 → matrx_orm-3.1.79}/matrx_orm/utils/sql_utils.py +0 -0
- {matrx_orm-3.1.78 → matrx_orm-3.1.79}/matrx_orm/utils/type_converters.py +0 -0
- {matrx_orm-3.1.78 → matrx_orm-3.1.79}/scripts/git-branches.sh +0 -0
- {matrx_orm-3.1.78 → matrx_orm-3.1.79}/scripts/publish.sh +0 -0
- {matrx_orm-3.1.78 → matrx_orm-3.1.79}/scripts/release.sh +0 -0
- {matrx_orm-3.1.78 → matrx_orm-3.1.79}/tests/MODULE_README.md +0 -0
- {matrx_orm-3.1.78 → matrx_orm-3.1.79}/tests/__init__.py +0 -0
- {matrx_orm-3.1.78 → matrx_orm-3.1.79}/tests/conftest.py +0 -0
- {matrx_orm-3.1.78 → matrx_orm-3.1.79}/tests/level1/MODULE_README.md +0 -0
- {matrx_orm-3.1.78 → matrx_orm-3.1.79}/tests/level1/__init__.py +0 -0
- {matrx_orm-3.1.78 → matrx_orm-3.1.79}/tests/level1/test_admin_db_columns.py +0 -0
- {matrx_orm-3.1.78 → matrx_orm-3.1.79}/tests/level1/test_admin_response_rendering.py +0 -0
- {matrx_orm-3.1.78 → matrx_orm-3.1.79}/tests/level1/test_admin_search_negation.py +0 -0
- {matrx_orm-3.1.78 → matrx_orm-3.1.79}/tests/level1/test_admin_search_sentinels.py +0 -0
- {matrx_orm-3.1.78 → matrx_orm-3.1.79}/tests/level1/test_advisory_lock.py +0 -0
- {matrx_orm-3.1.78 → matrx_orm-3.1.79}/tests/level1/test_agent_message.py +0 -0
- {matrx_orm-3.1.78 → matrx_orm-3.1.79}/tests/level1/test_api_auth.py +0 -0
- {matrx_orm-3.1.78 → matrx_orm-3.1.79}/tests/level1/test_api_config.py +0 -0
- {matrx_orm-3.1.78 → matrx_orm-3.1.79}/tests/level1/test_api_handlers.py +0 -0
- {matrx_orm-3.1.78 → matrx_orm-3.1.79}/tests/level1/test_api_protocol.py +0 -0
- {matrx_orm-3.1.78 → matrx_orm-3.1.79}/tests/level1/test_array_agg_order_by.py +0 -0
- {matrx_orm-3.1.78 → matrx_orm-3.1.79}/tests/level1/test_associations.py +0 -0
- {matrx_orm-3.1.78 → matrx_orm-3.1.79}/tests/level1/test_bulk_hydration_offload.py +0 -0
- {matrx_orm-3.1.78 → matrx_orm-3.1.79}/tests/level1/test_bulk_update_by_pk.py +0 -0
- {matrx_orm-3.1.78 → matrx_orm-3.1.79}/tests/level1/test_bulk_upsert_increment_set_fields.py +0 -0
- {matrx_orm-3.1.78 → matrx_orm-3.1.79}/tests/level1/test_cache_debug.py +0 -0
- {matrx_orm-3.1.78 → matrx_orm-3.1.79}/tests/level1/test_call_function.py +0 -0
- {matrx_orm-3.1.78 → matrx_orm-3.1.79}/tests/level1/test_case_when.py +0 -0
- {matrx_orm-3.1.78 → matrx_orm-3.1.79}/tests/level1/test_catalog.py +0 -0
- {matrx_orm-3.1.78 → matrx_orm-3.1.79}/tests/level1/test_composite_pk_filter.py +0 -0
- {matrx_orm-3.1.78 → matrx_orm-3.1.79}/tests/level1/test_composite_pk_write_paths.py +0 -0
- {matrx_orm-3.1.78 → matrx_orm-3.1.79}/tests/level1/test_config.py +0 -0
- {matrx_orm-3.1.78 → matrx_orm-3.1.79}/tests/level1/test_conflict_target.py +0 -0
- {matrx_orm-3.1.78 → matrx_orm-3.1.79}/tests/level1/test_conflict_writes.py +0 -0
- {matrx_orm-3.1.78 → matrx_orm-3.1.79}/tests/level1/test_connection_codecs.py +0 -0
- {matrx_orm-3.1.78 → matrx_orm-3.1.79}/tests/level1/test_connection_poison_guard.py +0 -0
- {matrx_orm-3.1.78 → matrx_orm-3.1.79}/tests/level1/test_connection_primitives.py +0 -0
- {matrx_orm-3.1.78 → matrx_orm-3.1.79}/tests/level1/test_count_composite_distinct.py +0 -0
- {matrx_orm-3.1.78 → matrx_orm-3.1.79}/tests/level1/test_db_function_field.py +0 -0
- {matrx_orm-3.1.78 → matrx_orm-3.1.79}/tests/level1/test_ddl_generator.py +0 -0
- {matrx_orm-3.1.78 → matrx_orm-3.1.79}/tests/level1/test_derived_table.py +0 -0
- {matrx_orm-3.1.78 → matrx_orm-3.1.79}/tests/level1/test_dirty_tracking.py +0 -0
- {matrx_orm-3.1.78 → matrx_orm-3.1.79}/tests/level1/test_dynamic_crud.py +0 -0
- {matrx_orm-3.1.78 → matrx_orm-3.1.79}/tests/level1/test_execute_query_integrity_mapping.py +0 -0
- {matrx_orm-3.1.78 → matrx_orm-3.1.79}/tests/level1/test_expression_primitives.py +0 -0
- {matrx_orm-3.1.78 → matrx_orm-3.1.79}/tests/level1/test_fields.py +0 -0
- {matrx_orm-3.1.78 → matrx_orm-3.1.79}/tests/level1/test_filter_jsonb_agg_array_index.py +0 -0
- {matrx_orm-3.1.78 → matrx_orm-3.1.79}/tests/level1/test_from_alias.py +0 -0
- {matrx_orm-3.1.78 → matrx_orm-3.1.79}/tests/level1/test_fts.py +0 -0
- {matrx_orm-3.1.78 → matrx_orm-3.1.79}/tests/level1/test_func_expression_args.py +0 -0
- {matrx_orm-3.1.78 → matrx_orm-3.1.79}/tests/level1/test_graph_walk.py +0 -0
- {matrx_orm-3.1.78 → matrx_orm-3.1.79}/tests/level1/test_having_annotate_alias.py +0 -0
- {matrx_orm-3.1.78 → matrx_orm-3.1.79}/tests/level1/test_insert_ignore_and_admin_primitives.py +0 -0
- {matrx_orm-3.1.78 → matrx_orm-3.1.79}/tests/level1/test_introspect_rls_policies.py +0 -0
- {matrx_orm-3.1.78 → matrx_orm-3.1.79}/tests/level1/test_join_and_aggregate.py +0 -0
- {matrx_orm-3.1.78 → matrx_orm-3.1.79}/tests/level1/test_latest_rows.py +0 -0
- {matrx_orm-3.1.78 → matrx_orm-3.1.79}/tests/level1/test_listen_notify.py +0 -0
- {matrx_orm-3.1.78 → matrx_orm-3.1.79}/tests/level1/test_local_sqlite.py +0 -0
- {matrx_orm-3.1.78 → matrx_orm-3.1.79}/tests/level1/test_managed_write_guard.py +0 -0
- {matrx_orm-3.1.78 → matrx_orm-3.1.79}/tests/level1/test_matrx_entity.py +0 -0
- {matrx_orm-3.1.78 → matrx_orm-3.1.79}/tests/level1/test_migration_diff_types.py +0 -0
- {matrx_orm-3.1.78 → matrx_orm-3.1.79}/tests/level1/test_migration_loader.py +0 -0
- {matrx_orm-3.1.78 → matrx_orm-3.1.79}/tests/level1/test_model_instance.py +0 -0
- {matrx_orm-3.1.78 → matrx_orm-3.1.79}/tests/level1/test_model_meta.py +0 -0
- {matrx_orm-3.1.78 → matrx_orm-3.1.79}/tests/level1/test_optimistic_concurrency.py +0 -0
- {matrx_orm-3.1.78 → matrx_orm-3.1.79}/tests/level1/test_order_by_expression.py +0 -0
- {matrx_orm-3.1.78 → matrx_orm-3.1.79}/tests/level1/test_parameter_name_hydration.py +0 -0
- {matrx_orm-3.1.78 → matrx_orm-3.1.79}/tests/level1/test_patch_jsonb_path.py +0 -0
- {matrx_orm-3.1.78 → matrx_orm-3.1.79}/tests/level1/test_pool_holder_census.py +0 -0
- {matrx_orm-3.1.78 → matrx_orm-3.1.79}/tests/level1/test_pool_loop_recreation.py +0 -0
- {matrx_orm-3.1.78 → matrx_orm-3.1.79}/tests/level1/test_postgrest_filters.py +0 -0
- {matrx_orm-3.1.78 → matrx_orm-3.1.79}/tests/level1/test_pydantic_bridge.py +0 -0
- {matrx_orm-3.1.78 → matrx_orm-3.1.79}/tests/level1/test_query_builder.py +0 -0
- {matrx_orm-3.1.78 → matrx_orm-3.1.79}/tests/level1/test_query_error_detail.py +0 -0
- {matrx_orm-3.1.78 → matrx_orm-3.1.79}/tests/level1/test_query_executor_sql.py +0 -0
- {matrx_orm-3.1.78 → matrx_orm-3.1.79}/tests/level1/test_query_timeout.py +0 -0
- {matrx_orm-3.1.78 → matrx_orm-3.1.79}/tests/level1/test_querybuilder_clone_semantics.py +0 -0
- {matrx_orm-3.1.78 → matrx_orm-3.1.79}/tests/level1/test_queue_claim.py +0 -0
- {matrx_orm-3.1.78 → matrx_orm-3.1.79}/tests/level1/test_registry.py +0 -0
- {matrx_orm-3.1.78 → matrx_orm-3.1.79}/tests/level1/test_registry_multi_database.py +0 -0
- {matrx_orm-3.1.78 → matrx_orm-3.1.79}/tests/level1/test_registry_multi_schema.py +0 -0
- {matrx_orm-3.1.78 → matrx_orm-3.1.79}/tests/level1/test_relational_backfill.py +0 -0
- {matrx_orm-3.1.78 → matrx_orm-3.1.79}/tests/level1/test_relations.py +0 -0
- {matrx_orm-3.1.78 → matrx_orm-3.1.79}/tests/level1/test_resilience.py +0 -0
- {matrx_orm-3.1.78 → matrx_orm-3.1.79}/tests/level1/test_rls_session_scope.py +0 -0
- {matrx_orm-3.1.78 → matrx_orm-3.1.79}/tests/level1/test_schema_exists.py +0 -0
- {matrx_orm-3.1.78 → matrx_orm-3.1.79}/tests/level1/test_session_advisory_lock.py +0 -0
- {matrx_orm-3.1.78 → matrx_orm-3.1.79}/tests/level1/test_state_cache.py +0 -0
- {matrx_orm-3.1.78 → matrx_orm-3.1.79}/tests/level1/test_subquery_filter_raw.py +0 -0
- {matrx_orm-3.1.78 → matrx_orm-3.1.79}/tests/level1/test_subquery_in_filter.py +0 -0
- {matrx_orm-3.1.78 → matrx_orm-3.1.79}/tests/level1/test_supabase_auth.py +0 -0
- {matrx_orm-3.1.78 → matrx_orm-3.1.79}/tests/level1/test_supabase_config.py +0 -0
- {matrx_orm-3.1.78 → matrx_orm-3.1.79}/tests/level1/test_tls.py +0 -0
- {matrx_orm-3.1.78 → matrx_orm-3.1.79}/tests/level1/test_typed_join_projection.py +0 -0
- {matrx_orm-3.1.78 → matrx_orm-3.1.79}/tests/level1/test_typed_predicates.py +0 -0
- {matrx_orm-3.1.78 → matrx_orm-3.1.79}/tests/level1/test_update_case_expression.py +0 -0
- {matrx_orm-3.1.78 → matrx_orm-3.1.79}/tests/level1/test_update_subquery.py +0 -0
- {matrx_orm-3.1.78 → matrx_orm-3.1.79}/tests/level1/test_update_with_rebase.py +0 -0
- {matrx_orm-3.1.78 → matrx_orm-3.1.79}/tests/level1/test_upsert_default_leak.py +0 -0
- {matrx_orm-3.1.78 → matrx_orm-3.1.79}/tests/level1/test_upsert_with_conflict.py +0 -0
- {matrx_orm-3.1.78 → matrx_orm-3.1.79}/tests/level1/test_values_jsonb_decode.py +0 -0
- {matrx_orm-3.1.78 → matrx_orm-3.1.79}/tests/level1/test_watched_lifecycle.py +0 -0
- {matrx_orm-3.1.78 → matrx_orm-3.1.79}/tests/level1/test_write_retry_semantics.py +0 -0
- {matrx_orm-3.1.78 → matrx_orm-3.1.79}/tests/level2/MODULE_README.md +0 -0
- {matrx_orm-3.1.78 → matrx_orm-3.1.79}/tests/level2/__init__.py +0 -0
- {matrx_orm-3.1.78 → matrx_orm-3.1.79}/tests/level2/conftest.py +0 -0
- {matrx_orm-3.1.78 → matrx_orm-3.1.79}/tests/level2/test_bulk_ops.py +0 -0
- {matrx_orm-3.1.78 → matrx_orm-3.1.79}/tests/level2/test_cache_integration.py +0 -0
- {matrx_orm-3.1.78 → matrx_orm-3.1.79}/tests/level2/test_crud.py +0 -0
- {matrx_orm-3.1.78 → matrx_orm-3.1.79}/tests/level2/test_foreign_keys.py +0 -0
- {matrx_orm-3.1.78 → matrx_orm-3.1.79}/tests/level2/test_m2m.py +0 -0
- {matrx_orm-3.1.78 → matrx_orm-3.1.79}/tests/level2/test_manager.py +0 -0
- {matrx_orm-3.1.78 → matrx_orm-3.1.79}/tests/level2/test_migrations_live.py +0 -0
- {matrx_orm-3.1.78 → matrx_orm-3.1.79}/tests/level2/test_query_execution.py +0 -0
- {matrx_orm-3.1.78 → matrx_orm-3.1.79}/tests/level2/test_schema_diff.py +0 -0
- {matrx_orm-3.1.78 → matrx_orm-3.1.79}/tests/live_pooler_role.py +0 -0
- {matrx_orm-3.1.78 → matrx_orm-3.1.79}/tests/sample_project/.env.example +0 -0
- {matrx_orm-3.1.78 → matrx_orm-3.1.79}/tests/sample_project/README.md +0 -0
- {matrx_orm-3.1.78 → matrx_orm-3.1.79}/tests/sample_project/__init__.py +0 -0
- {matrx_orm-3.1.78 → matrx_orm-3.1.79}/tests/sample_project/generate.py +0 -0
- {matrx_orm-3.1.78 → matrx_orm-3.1.79}/tests/sample_project/generated/.gitkeep +0 -0
- {matrx_orm-3.1.78 → matrx_orm-3.1.79}/tests/sample_project/matrx_orm.yaml +0 -0
- {matrx_orm-3.1.78 → matrx_orm-3.1.79}/tests/sample_project/test_schema_generation.py +0 -0
- {matrx_orm-3.1.78 → matrx_orm-3.1.79}/tests/sample_project_desktop/.env.example +0 -0
- {matrx_orm-3.1.78 → matrx_orm-3.1.79}/tests/sample_project_desktop/README.md +0 -0
- {matrx_orm-3.1.78 → matrx_orm-3.1.79}/tests/sample_project_desktop/__init__.py +0 -0
- {matrx_orm-3.1.78 → matrx_orm-3.1.79}/tests/sample_project_desktop/client_example.py +0 -0
- {matrx_orm-3.1.78 → matrx_orm-3.1.79}/tests/sample_project_desktop/client_example.ts +0 -0
- {matrx_orm-3.1.78 → matrx_orm-3.1.79}/tests/sample_project_desktop/client_supabase_example.py +0 -0
- {matrx_orm-3.1.78 → matrx_orm-3.1.79}/tests/sample_project_desktop/server.py +0 -0
- {matrx_orm-3.1.78 → matrx_orm-3.1.79}/tests/schema/entity_tests.py +0 -0
- {matrx_orm-3.1.78 → matrx_orm-3.1.79}/tests/schema/test_base_generation.py +0 -0
- {matrx_orm-3.1.78 → matrx_orm-3.1.79}/tests/schema/test_composite_pk_fk_generation.py +0 -0
- {matrx_orm-3.1.78 → matrx_orm-3.1.79}/tests/schema/test_composite_unique_generation.py +0 -0
- {matrx_orm-3.1.78 → matrx_orm-3.1.79}/tests/schema/test_dto_identity_generation.py +0 -0
- {matrx_orm-3.1.78 → matrx_orm-3.1.79}/tests/schema/test_entity_capability_generation.py +0 -0
- {matrx_orm-3.1.78 → matrx_orm-3.1.79}/tests/schema/test_generate_schema.py +0 -0
- {matrx_orm-3.1.78 → matrx_orm-3.1.79}/tests/schema/test_junction_analysis.py +0 -0
- {matrx_orm-3.1.78 → matrx_orm-3.1.79}/tests/schema/test_multi_schema_output.py +0 -0
- {matrx_orm-3.1.78 → matrx_orm-3.1.79}/tests/schema/test_sql_expression_defaults.py +0 -0
- {matrx_orm-3.1.78 → matrx_orm-3.1.79}/tests/schema/test_table_relationships_query.py +0 -0
- {matrx_orm-3.1.78 → matrx_orm-3.1.79}/tests/schema/test_view_generation.py +0 -0
- {matrx_orm-3.1.78 → matrx_orm-3.1.79}/tests/schema_builder/test_array_default_parsing.py +0 -0
- {matrx_orm-3.1.78 → matrx_orm-3.1.79}/tests/schema_builder/test_focused_generation_plan.py +0 -0
- {matrx_orm-3.1.78 → matrx_orm-3.1.79}/tests/schema_builder/test_host_models_aggregator.py +0 -0
- {matrx_orm-3.1.78 → matrx_orm-3.1.79}/tests/session/__init__.py +0 -0
- {matrx_orm-3.1.78 → matrx_orm-3.1.79}/tests/session/conftest.py +0 -0
- {matrx_orm-3.1.78 → matrx_orm-3.1.79}/tests/session/test_cache_no_downgrade.py +0 -0
- {matrx_orm-3.1.78 → matrx_orm-3.1.79}/tests/session/test_capture_poison_proof.py +0 -0
- {matrx_orm-3.1.78 → matrx_orm-3.1.79}/tests/session/test_coalesce.py +0 -0
- {matrx_orm-3.1.78 → matrx_orm-3.1.79}/tests/session/test_dag.py +0 -0
- {matrx_orm-3.1.78 → matrx_orm-3.1.79}/tests/session/test_disk_spill_fallback.py +0 -0
- {matrx_orm-3.1.78 → matrx_orm-3.1.79}/tests/session/test_flush_individual_fallback.py +0 -0
- {matrx_orm-3.1.78 → matrx_orm-3.1.79}/tests/session/test_governed_write.py +0 -0
- {matrx_orm-3.1.78 → matrx_orm-3.1.79}/tests/session/test_managed.py +0 -0
- {matrx_orm-3.1.78 → matrx_orm-3.1.79}/tests/session/test_op_enqueue_site.py +0 -0
- {matrx_orm-3.1.78 → matrx_orm-3.1.79}/tests/session/test_reads.py +0 -0
- {matrx_orm-3.1.78 → matrx_orm-3.1.79}/tests/session/test_session.py +0 -0
- {matrx_orm-3.1.78 → matrx_orm-3.1.79}/tests/test_database_name_alias.py +0 -0
- {matrx_orm-3.1.78 → matrx_orm-3.1.79}/tests/test_db_functions.py +0 -0
- {matrx_orm-3.1.78 → matrx_orm-3.1.79}/tests/test_model_cls_refactor.py +0 -0
- {matrx_orm-3.1.78 → matrx_orm-3.1.79}/tests/test_no_blocking_asyncpg_tls.py +0 -0
- {matrx_orm-3.1.78 → matrx_orm-3.1.79}/tests/test_one_database.py +0 -0
- {matrx_orm-3.1.78 → matrx_orm-3.1.79}/tests/test_pooler_role_guard.py +0 -0
- {matrx_orm-3.1.78 → matrx_orm-3.1.79}/tests/test_secrets_battery.py +0 -0
- {matrx_orm-3.1.78 → matrx_orm-3.1.79}/tests/test_sql_param_casts.py +0 -0
- {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.
|
|
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
|
-
|
|
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-
|
|
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
|
-
|
|
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
|
-
|
|
717
|
-
|
|
718
|
-
|
|
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
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
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).
|
|
1106
|
-
#
|
|
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
|
|
1229
|
-
# in the tx_conn branch above
|
|
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
|
|
1012
|
-
#
|
|
1013
|
-
#
|
|
1014
|
-
#
|
|
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
|
-
|
|
1058
|
-
lines.append(
|
|
1059
|
-
|
|
1060
|
-
|
|
1061
|
-
|
|
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
|
-
|
|
1066
|
-
|
|
1067
|
-
|
|
1068
|
-
|
|
1069
|
-
|
|
1070
|
-
|
|
1071
|
-
|
|
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 —
|
|
1089
|
+
" - NOT safe to retry blindly — a write may have ambiguous completion, and"
|
|
1074
1090
|
)
|
|
1075
|
-
lines.append("
|
|
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(
|
|
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(
|
|
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.
|
|
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
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|