pytest-neon 0.5.1__py3-none-any.whl → 0.6.0__py3-none-any.whl
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.
- pytest_neon/__init__.py +1 -1
- pytest_neon/plugin.py +173 -21
- {pytest_neon-0.5.1.dist-info → pytest_neon-0.6.0.dist-info}/METADATA +67 -7
- pytest_neon-0.6.0.dist-info/RECORD +8 -0
- pytest_neon-0.5.1.dist-info/RECORD +0 -8
- {pytest_neon-0.5.1.dist-info → pytest_neon-0.6.0.dist-info}/WHEEL +0 -0
- {pytest_neon-0.5.1.dist-info → pytest_neon-0.6.0.dist-info}/entry_points.txt +0 -0
- {pytest_neon-0.5.1.dist-info → pytest_neon-0.6.0.dist-info}/licenses/LICENSE +0 -0
pytest_neon/__init__.py
CHANGED
pytest_neon/plugin.py
CHANGED
|
@@ -1,7 +1,36 @@
|
|
|
1
|
-
"""Pytest plugin providing Neon database branch fixtures.
|
|
1
|
+
"""Pytest plugin providing Neon database branch fixtures.
|
|
2
|
+
|
|
3
|
+
This plugin provides fixtures for isolated database testing using Neon's
|
|
4
|
+
instant branching feature. Each test gets a clean database state via
|
|
5
|
+
branch reset after each test.
|
|
6
|
+
|
|
7
|
+
Main fixtures:
|
|
8
|
+
neon_branch: Primary fixture - one branch per session, reset after each test
|
|
9
|
+
neon_branch_shared: Shared branch without reset (fastest, no isolation)
|
|
10
|
+
neon_connection: psycopg2 connection (requires psycopg2 extra)
|
|
11
|
+
neon_connection_psycopg: psycopg v3 connection (requires psycopg extra)
|
|
12
|
+
neon_engine: SQLAlchemy engine (requires sqlalchemy extra)
|
|
13
|
+
|
|
14
|
+
SQLAlchemy Users:
|
|
15
|
+
If you create your own SQLAlchemy engine (not using neon_engine fixture),
|
|
16
|
+
you MUST use pool_pre_ping=True:
|
|
17
|
+
|
|
18
|
+
engine = create_engine(DATABASE_URL, pool_pre_ping=True)
|
|
19
|
+
|
|
20
|
+
This is required because branch resets terminate server-side connections.
|
|
21
|
+
Without pool_pre_ping, SQLAlchemy may try to reuse dead pooled connections,
|
|
22
|
+
causing "SSL connection has been closed unexpectedly" errors.
|
|
23
|
+
|
|
24
|
+
Configuration:
|
|
25
|
+
Set NEON_API_KEY and NEON_PROJECT_ID environment variables, or use
|
|
26
|
+
--neon-api-key and --neon-project-id CLI options.
|
|
27
|
+
|
|
28
|
+
For full documentation, see: https://github.com/ZainRizvi/pytest-neon
|
|
29
|
+
"""
|
|
2
30
|
|
|
3
31
|
from __future__ import annotations
|
|
4
32
|
|
|
33
|
+
import contextlib
|
|
5
34
|
import os
|
|
6
35
|
import time
|
|
7
36
|
from collections.abc import Generator
|
|
@@ -17,6 +46,41 @@ from neon_api.schema import EndpointState
|
|
|
17
46
|
# Default branch expiry in seconds (10 minutes)
|
|
18
47
|
DEFAULT_BRANCH_EXPIRY_SECONDS = 600
|
|
19
48
|
|
|
49
|
+
# Sentinel value to detect when neon_apply_migrations was not overridden
|
|
50
|
+
_MIGRATIONS_NOT_DEFINED = object()
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
def _get_schema_fingerprint(connection_string: str) -> tuple[tuple[Any, ...], ...]:
|
|
54
|
+
"""
|
|
55
|
+
Get a fingerprint of the database schema for change detection.
|
|
56
|
+
|
|
57
|
+
Queries information_schema for all tables, columns, and their properties
|
|
58
|
+
in the public schema. Returns a hashable tuple that can be compared
|
|
59
|
+
before/after migrations to detect if the schema actually changed.
|
|
60
|
+
|
|
61
|
+
This is used to avoid creating unnecessary migration branches when
|
|
62
|
+
no actual schema changes occurred.
|
|
63
|
+
"""
|
|
64
|
+
try:
|
|
65
|
+
import psycopg
|
|
66
|
+
except ImportError:
|
|
67
|
+
try:
|
|
68
|
+
import psycopg2 as psycopg # type: ignore[import-not-found]
|
|
69
|
+
except ImportError:
|
|
70
|
+
# No driver available - can't fingerprint, assume migrations changed things
|
|
71
|
+
return ()
|
|
72
|
+
|
|
73
|
+
with psycopg.connect(connection_string) as conn, conn.cursor() as cur:
|
|
74
|
+
cur.execute("""
|
|
75
|
+
SELECT table_name, column_name, data_type, is_nullable,
|
|
76
|
+
column_default, ordinal_position
|
|
77
|
+
FROM information_schema.columns
|
|
78
|
+
WHERE table_schema = 'public'
|
|
79
|
+
ORDER BY table_name, ordinal_position
|
|
80
|
+
""")
|
|
81
|
+
rows = cur.fetchall()
|
|
82
|
+
return tuple(tuple(row) for row in rows)
|
|
83
|
+
|
|
20
84
|
|
|
21
85
|
@dataclass
|
|
22
86
|
class NeonBranch:
|
|
@@ -339,23 +403,47 @@ def _neon_migration_branch(
|
|
|
339
403
|
Note: The migration branch cannot have an expiry because Neon doesn't
|
|
340
404
|
allow creating child branches from branches with expiration dates.
|
|
341
405
|
Cleanup relies on the fixture teardown at session end.
|
|
406
|
+
|
|
407
|
+
Smart Migration Detection:
|
|
408
|
+
Before yielding, this fixture captures a schema fingerprint and stores
|
|
409
|
+
it on request.config. After migrations run, _neon_branch_for_reset
|
|
410
|
+
compares the fingerprint to detect if the schema actually changed.
|
|
342
411
|
"""
|
|
343
412
|
# No expiry - Neon doesn't allow children from branches with expiry
|
|
344
|
-
|
|
413
|
+
branch_gen = _create_neon_branch(
|
|
345
414
|
request,
|
|
346
415
|
branch_expiry_override=0,
|
|
347
416
|
branch_name_suffix="-migrated",
|
|
348
417
|
)
|
|
418
|
+
branch = next(branch_gen)
|
|
419
|
+
|
|
420
|
+
# Capture schema fingerprint BEFORE migrations run
|
|
421
|
+
# This is stored on config so _neon_branch_for_reset can compare after
|
|
422
|
+
pre_migration_fingerprint = _get_schema_fingerprint(branch.connection_string)
|
|
423
|
+
request.config._neon_pre_migration_fingerprint = pre_migration_fingerprint # type: ignore[attr-defined]
|
|
424
|
+
|
|
425
|
+
try:
|
|
426
|
+
yield branch
|
|
427
|
+
finally:
|
|
428
|
+
# Clean up by exhausting the generator (triggers branch deletion)
|
|
429
|
+
with contextlib.suppress(StopIteration):
|
|
430
|
+
next(branch_gen)
|
|
349
431
|
|
|
350
432
|
|
|
351
433
|
@pytest.fixture(scope="session")
|
|
352
|
-
def neon_apply_migrations(_neon_migration_branch: NeonBranch) ->
|
|
434
|
+
def neon_apply_migrations(_neon_migration_branch: NeonBranch) -> Any:
|
|
353
435
|
"""
|
|
354
436
|
Override this fixture to run migrations on the test database.
|
|
355
437
|
|
|
356
438
|
The migration branch is already created and DATABASE_URL is set.
|
|
357
439
|
Migrations run once per test session, before any tests execute.
|
|
358
440
|
|
|
441
|
+
Smart Migration Detection:
|
|
442
|
+
The plugin automatically detects whether migrations actually modified
|
|
443
|
+
the database schema. If no schema changes occurred (or this fixture
|
|
444
|
+
isn't overridden), the plugin skips creating a separate migration
|
|
445
|
+
branch, saving Neon costs and branch slots.
|
|
446
|
+
|
|
359
447
|
Example in conftest.py:
|
|
360
448
|
|
|
361
449
|
@pytest.fixture(scope="session")
|
|
@@ -384,27 +472,69 @@ def neon_apply_migrations(_neon_migration_branch: NeonBranch) -> None:
|
|
|
384
472
|
_neon_migration_branch: The migration branch with connection details.
|
|
385
473
|
Use _neon_migration_branch.connection_string to connect directly,
|
|
386
474
|
or rely on DATABASE_URL which is already set.
|
|
475
|
+
|
|
476
|
+
Returns:
|
|
477
|
+
Any value (ignored). The default returns a sentinel to indicate
|
|
478
|
+
the fixture was not overridden.
|
|
387
479
|
"""
|
|
388
|
-
|
|
480
|
+
return _MIGRATIONS_NOT_DEFINED
|
|
389
481
|
|
|
390
482
|
|
|
391
|
-
@pytest.fixture(scope="
|
|
483
|
+
@pytest.fixture(scope="session")
|
|
392
484
|
def _neon_branch_for_reset(
|
|
393
485
|
request: pytest.FixtureRequest,
|
|
394
486
|
_neon_migration_branch: NeonBranch,
|
|
395
|
-
neon_apply_migrations:
|
|
487
|
+
neon_apply_migrations: Any, # Ensures migrations run first; value for detection
|
|
396
488
|
) -> Generator[NeonBranch, None, None]:
|
|
397
489
|
"""
|
|
398
490
|
Internal fixture that creates a test branch from the migration branch.
|
|
399
491
|
|
|
400
|
-
|
|
401
|
-
|
|
492
|
+
This is session-scoped so DATABASE_URL remains stable throughout the test
|
|
493
|
+
session, avoiding issues with Python's module caching (e.g., SQLAlchemy
|
|
494
|
+
engines created at import time would otherwise point to stale branches).
|
|
495
|
+
|
|
496
|
+
Smart Migration Detection:
|
|
497
|
+
This fixture implements a cost-optimization strategy:
|
|
498
|
+
|
|
499
|
+
1. If neon_apply_migrations was not overridden (returns sentinel),
|
|
500
|
+
skip creating a separate test branch - use the migration branch directly.
|
|
501
|
+
|
|
502
|
+
2. If neon_apply_migrations was overridden, compare schema fingerprints
|
|
503
|
+
before/after migrations. Only create a child branch if the schema
|
|
504
|
+
actually changed.
|
|
505
|
+
|
|
506
|
+
This avoids unnecessary Neon costs and branch slots when:
|
|
507
|
+
- No migration fixture is defined
|
|
508
|
+
- Migrations exist but are already applied (no schema changes)
|
|
402
509
|
"""
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
)
|
|
510
|
+
# Check if migrations fixture was overridden
|
|
511
|
+
migrations_defined = neon_apply_migrations is not _MIGRATIONS_NOT_DEFINED
|
|
512
|
+
|
|
513
|
+
# Check if schema actually changed (if we have a pre-migration fingerprint)
|
|
514
|
+
pre_fingerprint = getattr(request.config, "_neon_pre_migration_fingerprint", ())
|
|
515
|
+
schema_changed = False
|
|
516
|
+
|
|
517
|
+
if migrations_defined and pre_fingerprint:
|
|
518
|
+
# Compare with current schema
|
|
519
|
+
conn_str = _neon_migration_branch.connection_string
|
|
520
|
+
post_fingerprint = _get_schema_fingerprint(conn_str)
|
|
521
|
+
schema_changed = pre_fingerprint != post_fingerprint
|
|
522
|
+
elif migrations_defined and not pre_fingerprint:
|
|
523
|
+
# No fingerprint available (no psycopg/psycopg2 installed)
|
|
524
|
+
# Assume migrations changed something to be safe
|
|
525
|
+
schema_changed = True
|
|
526
|
+
|
|
527
|
+
# Only create a child branch if migrations actually modified the schema
|
|
528
|
+
if schema_changed:
|
|
529
|
+
yield from _create_neon_branch(
|
|
530
|
+
request,
|
|
531
|
+
parent_branch_id_override=_neon_migration_branch.branch_id,
|
|
532
|
+
branch_name_suffix="-test",
|
|
533
|
+
)
|
|
534
|
+
else:
|
|
535
|
+
# No schema changes - reuse the migration branch directly
|
|
536
|
+
# This saves creating an unnecessary branch
|
|
537
|
+
yield _neon_migration_branch
|
|
408
538
|
|
|
409
539
|
|
|
410
540
|
@pytest.fixture(scope="function")
|
|
@@ -416,25 +546,35 @@ def neon_branch(
|
|
|
416
546
|
Provide an isolated Neon database branch for each test.
|
|
417
547
|
|
|
418
548
|
This is the primary fixture for database testing. It creates one branch per
|
|
419
|
-
test
|
|
549
|
+
test session, then resets it to the parent branch's state after each test.
|
|
420
550
|
This provides test isolation with ~0.5s overhead per test.
|
|
421
551
|
|
|
422
|
-
The branch is automatically deleted after all tests
|
|
423
|
-
|
|
552
|
+
The branch is automatically deleted after all tests complete, unless
|
|
553
|
+
--neon-keep-branches is specified. Branches also auto-expire after
|
|
424
554
|
10 minutes by default (configurable via --neon-branch-expiry) as a safety net
|
|
425
555
|
for interrupted test runs.
|
|
426
556
|
|
|
427
557
|
The connection string is automatically set in the DATABASE_URL environment
|
|
428
558
|
variable (configurable via --neon-env-var).
|
|
429
559
|
|
|
560
|
+
SQLAlchemy Users:
|
|
561
|
+
If you create your own engine (not using the neon_engine fixture),
|
|
562
|
+
you MUST use pool_pre_ping=True::
|
|
563
|
+
|
|
564
|
+
engine = create_engine(DATABASE_URL, pool_pre_ping=True)
|
|
565
|
+
|
|
566
|
+
Branch resets terminate server-side connections. Without pool_pre_ping,
|
|
567
|
+
SQLAlchemy may reuse dead pooled connections, causing SSL errors.
|
|
568
|
+
|
|
430
569
|
Requires either:
|
|
431
|
-
|
|
432
|
-
|
|
570
|
+
- NEON_API_KEY and NEON_PROJECT_ID environment variables, or
|
|
571
|
+
- --neon-api-key and --neon-project-id command line options
|
|
433
572
|
|
|
434
573
|
Yields:
|
|
435
574
|
NeonBranch: Object with branch_id, project_id, connection_string, and host.
|
|
436
575
|
|
|
437
|
-
Example
|
|
576
|
+
Example::
|
|
577
|
+
|
|
438
578
|
def test_database_operation(neon_branch):
|
|
439
579
|
# DATABASE_URL is automatically set
|
|
440
580
|
conn_string = os.environ["DATABASE_URL"]
|
|
@@ -602,12 +742,24 @@ def neon_engine(neon_branch: NeonBranch):
|
|
|
602
742
|
Requires the sqlalchemy optional dependency:
|
|
603
743
|
pip install pytest-neon[sqlalchemy]
|
|
604
744
|
|
|
605
|
-
The engine is disposed after each test
|
|
745
|
+
The engine is disposed after each test, which handles stale connections
|
|
746
|
+
after branch resets automatically.
|
|
747
|
+
|
|
748
|
+
Note:
|
|
749
|
+
If you create your own module-level engine instead of using this
|
|
750
|
+
fixture, you MUST use pool_pre_ping=True::
|
|
751
|
+
|
|
752
|
+
engine = create_engine(DATABASE_URL, pool_pre_ping=True)
|
|
753
|
+
|
|
754
|
+
This is required because branch resets terminate server-side
|
|
755
|
+
connections, and without pool_pre_ping SQLAlchemy may reuse dead
|
|
756
|
+
pooled connections.
|
|
606
757
|
|
|
607
758
|
Yields:
|
|
608
759
|
SQLAlchemy Engine object
|
|
609
760
|
|
|
610
|
-
Example
|
|
761
|
+
Example::
|
|
762
|
+
|
|
611
763
|
def test_query(neon_engine):
|
|
612
764
|
with neon_engine.connect() as conn:
|
|
613
765
|
result = conn.execute(text("SELECT 1"))
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: pytest-neon
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.6.0
|
|
4
4
|
Summary: Pytest plugin for Neon database branch isolation in tests
|
|
5
5
|
Project-URL: Homepage, https://github.com/ZainRizvi/pytest-neon
|
|
6
6
|
Project-URL: Repository, https://github.com/ZainRizvi/pytest-neon
|
|
@@ -29,9 +29,12 @@ Requires-Dist: pytest>=7.0
|
|
|
29
29
|
Requires-Dist: requests>=2.20
|
|
30
30
|
Provides-Extra: dev
|
|
31
31
|
Requires-Dist: mypy>=1.0; extra == 'dev'
|
|
32
|
+
Requires-Dist: psycopg2-binary>=2.9; extra == 'dev'
|
|
33
|
+
Requires-Dist: psycopg[binary]>=3.1; extra == 'dev'
|
|
32
34
|
Requires-Dist: pytest-cov>=4.0; extra == 'dev'
|
|
33
35
|
Requires-Dist: pytest-mock>=3.0; extra == 'dev'
|
|
34
36
|
Requires-Dist: ruff>=0.8; extra == 'dev'
|
|
37
|
+
Requires-Dist: sqlalchemy>=2.0; extra == 'dev'
|
|
35
38
|
Provides-Extra: psycopg
|
|
36
39
|
Requires-Dist: psycopg[binary]>=3.1; extra == 'psycopg'
|
|
37
40
|
Provides-Extra: psycopg2
|
|
@@ -42,6 +45,8 @@ Description-Content-Type: text/markdown
|
|
|
42
45
|
|
|
43
46
|
# pytest-neon
|
|
44
47
|
|
|
48
|
+
[](https://github.com/ZainRizvi/pytest-neon/actions/workflows/tests.yml)
|
|
49
|
+
|
|
45
50
|
Pytest plugin for [Neon](https://neon.tech) database branch isolation in tests.
|
|
46
51
|
|
|
47
52
|
Each test gets its own isolated database state via Neon's instant branching and reset features. Branches are automatically cleaned up after tests complete.
|
|
@@ -112,7 +117,7 @@ pytest
|
|
|
112
117
|
|
|
113
118
|
### `neon_branch` (default, recommended)
|
|
114
119
|
|
|
115
|
-
The primary fixture for database testing. Creates one branch per test
|
|
120
|
+
The primary fixture for database testing. Creates one branch per test session, then resets it to the parent branch's state after each test. This provides test isolation with ~0.5s overhead per test.
|
|
116
121
|
|
|
117
122
|
Returns a `NeonBranch` dataclass with:
|
|
118
123
|
|
|
@@ -134,7 +139,7 @@ def test_branch_info(neon_branch):
|
|
|
134
139
|
conn = psycopg.connect(neon_branch.connection_string)
|
|
135
140
|
```
|
|
136
141
|
|
|
137
|
-
**Performance**: ~1.5s initial setup per
|
|
142
|
+
**Performance**: ~1.5s initial setup per session + ~0.5s reset per test. For 10 tests, expect ~6.5s total overhead.
|
|
138
143
|
|
|
139
144
|
### `neon_branch_shared` (fastest, no isolation)
|
|
140
145
|
|
|
@@ -200,22 +205,65 @@ def test_query(neon_engine):
|
|
|
200
205
|
assert result.scalar() == 1
|
|
201
206
|
```
|
|
202
207
|
|
|
208
|
+
### Using Your Own SQLAlchemy Engine
|
|
209
|
+
|
|
210
|
+
If you have a module-level SQLAlchemy engine (common pattern), you **must** use `pool_pre_ping=True`:
|
|
211
|
+
|
|
212
|
+
```python
|
|
213
|
+
# database.py
|
|
214
|
+
from sqlalchemy import create_engine
|
|
215
|
+
from config import DATABASE_URL
|
|
216
|
+
|
|
217
|
+
# pool_pre_ping=True is REQUIRED for pytest-neon
|
|
218
|
+
# It verifies connections are alive before using them
|
|
219
|
+
engine = create_engine(DATABASE_URL, pool_pre_ping=True)
|
|
220
|
+
```
|
|
221
|
+
|
|
222
|
+
**Why?** After each test, pytest-neon resets the branch which terminates server-side connections. Without `pool_pre_ping`, SQLAlchemy may try to reuse a dead pooled connection, causing `SSL connection has been closed unexpectedly` errors.
|
|
223
|
+
|
|
224
|
+
This is also a best practice for any cloud database (Neon, RDS, etc.) where connections can be terminated externally.
|
|
225
|
+
|
|
203
226
|
## Migrations
|
|
204
227
|
|
|
205
228
|
pytest-neon supports running migrations once before tests, with all test resets preserving the migrated state.
|
|
206
229
|
|
|
230
|
+
### Smart Migration Detection
|
|
231
|
+
|
|
232
|
+
The plugin automatically detects whether migrations actually modified the database schema. This optimization:
|
|
233
|
+
|
|
234
|
+
- **Saves Neon costs**: No extra branch created when migrations don't change anything
|
|
235
|
+
- **Saves branch slots**: Neon projects have branch limits; this avoids wasting them
|
|
236
|
+
- **Zero configuration**: Works automatically with any migration tool
|
|
237
|
+
|
|
238
|
+
**When a second branch is created:**
|
|
239
|
+
- Only when `neon_apply_migrations` is overridden AND the schema actually changes
|
|
240
|
+
|
|
241
|
+
**When only one branch is used:**
|
|
242
|
+
- If you don't override `neon_apply_migrations` (no migrations defined)
|
|
243
|
+
- If your migrations are already applied (schema unchanged)
|
|
244
|
+
|
|
245
|
+
The detection works by comparing a fingerprint of `information_schema.columns` before and after migrations run.
|
|
246
|
+
|
|
207
247
|
### How It Works
|
|
208
248
|
|
|
209
|
-
When
|
|
249
|
+
When migrations actually modify the schema, the plugin uses a two-branch architecture:
|
|
210
250
|
|
|
211
251
|
```
|
|
212
252
|
Parent Branch (your configured parent)
|
|
213
253
|
└── Migration Branch (session-scoped)
|
|
214
254
|
│ ↑ migrations run here ONCE
|
|
215
|
-
└── Test Branch (
|
|
255
|
+
└── Test Branch (session-scoped)
|
|
216
256
|
↑ resets to migration branch after each test
|
|
217
257
|
```
|
|
218
258
|
|
|
259
|
+
When no schema changes occur, the plugin uses a single-branch architecture:
|
|
260
|
+
|
|
261
|
+
```
|
|
262
|
+
Parent Branch (your configured parent)
|
|
263
|
+
└── Migration/Test Branch (session-scoped)
|
|
264
|
+
↑ resets to parent after each test
|
|
265
|
+
```
|
|
266
|
+
|
|
219
267
|
This means:
|
|
220
268
|
- Migrations run **once per test session** (not per test or per module)
|
|
221
269
|
- Each test reset restores to the **post-migration state**
|
|
@@ -376,11 +424,11 @@ jobs:
|
|
|
376
424
|
|
|
377
425
|
## How It Works
|
|
378
426
|
|
|
379
|
-
1.
|
|
427
|
+
1. At the start of the test session, the plugin creates a new Neon branch from your parent branch
|
|
380
428
|
2. `DATABASE_URL` is set to point to the new branch
|
|
381
429
|
3. Tests run against this isolated branch with full access to your schema and data
|
|
382
430
|
4. After each test, the branch is reset to its parent state (~0.5s)
|
|
383
|
-
5. After all tests
|
|
431
|
+
5. After all tests complete, the branch is deleted
|
|
384
432
|
6. As a safety net, branches auto-expire after 10 minutes even if cleanup fails
|
|
385
433
|
|
|
386
434
|
Branches use copy-on-write storage, so you only pay for data that differs from the parent branch.
|
|
@@ -440,6 +488,18 @@ Set the `NEON_API_KEY` environment variable or use the `--neon-api-key` CLI opti
|
|
|
440
488
|
|
|
441
489
|
Set the `NEON_PROJECT_ID` environment variable or use the `--neon-project-id` CLI option.
|
|
442
490
|
|
|
491
|
+
### "SSL connection has been closed unexpectedly" (SQLAlchemy)
|
|
492
|
+
|
|
493
|
+
This happens when SQLAlchemy tries to reuse a pooled connection after a branch reset. The reset terminates server-side connections, but SQLAlchemy's pool doesn't know.
|
|
494
|
+
|
|
495
|
+
**Fix:** Add `pool_pre_ping=True` to your engine:
|
|
496
|
+
|
|
497
|
+
```python
|
|
498
|
+
engine = create_engine(DATABASE_URL, pool_pre_ping=True)
|
|
499
|
+
```
|
|
500
|
+
|
|
501
|
+
This makes SQLAlchemy verify connections before using them, automatically discarding stale ones.
|
|
502
|
+
|
|
443
503
|
## License
|
|
444
504
|
|
|
445
505
|
MIT
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
pytest_neon/__init__.py,sha256=54UgijfJx44ra6-LyAS2fKVPTdqOKofrD3wQeCt8ydQ,398
|
|
2
|
+
pytest_neon/plugin.py,sha256=6wcZe9E90y2kya4wM0ur9EtUhqWFUll_ebN4xgsogPk,29601
|
|
3
|
+
pytest_neon/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
4
|
+
pytest_neon-0.6.0.dist-info/METADATA,sha256=Aw8Y8MHYAo9Wt8TbthiN-pd8pAQenaHckwobPipvtsk,16057
|
|
5
|
+
pytest_neon-0.6.0.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
|
|
6
|
+
pytest_neon-0.6.0.dist-info/entry_points.txt,sha256=5U88Idj_G8-PSDb9VF3OwYFbGLHnGOo_GxgYvi0dtXw,37
|
|
7
|
+
pytest_neon-0.6.0.dist-info/licenses/LICENSE,sha256=aKKp_Ex4WBHTByY4BhXJ181dzB_qYhi2pCUmZ7Spn_0,1067
|
|
8
|
+
pytest_neon-0.6.0.dist-info/RECORD,,
|
|
@@ -1,8 +0,0 @@
|
|
|
1
|
-
pytest_neon/__init__.py,sha256=YQt1LLTbNLiq4kChmiPEmOD5aTsqhCgpRpxK80VAY7Y,398
|
|
2
|
-
pytest_neon/plugin.py,sha256=1TukuxsBQ88NlgQS6qosUarIrCc94apMrwvVrYt9IOg,23241
|
|
3
|
-
pytest_neon/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
4
|
-
pytest_neon-0.5.1.dist-info/METADATA,sha256=i1h6TFZWjG0QY2h_UZsYzz5e9AaQewfCXKIXi_JC00E,13547
|
|
5
|
-
pytest_neon-0.5.1.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
|
|
6
|
-
pytest_neon-0.5.1.dist-info/entry_points.txt,sha256=5U88Idj_G8-PSDb9VF3OwYFbGLHnGOo_GxgYvi0dtXw,37
|
|
7
|
-
pytest_neon-0.5.1.dist-info/licenses/LICENSE,sha256=aKKp_Ex4WBHTByY4BhXJ181dzB_qYhi2pCUmZ7Spn_0,1067
|
|
8
|
-
pytest_neon-0.5.1.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|