Skip to content

Commit 9e3dda8

Browse files
committed
connection leaks fix
1 parent ecfc5ca commit 9e3dda8

File tree

5 files changed

+150
-127
lines changed

5 files changed

+150
-127
lines changed

load-testing/README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,5 +21,5 @@ uvicorn app:app
2121
Run yandex-tank:
2222

2323
```bash
24-
docker run -v $(pwd):/var/loadtest --net host -it yandex/yandex-tank
24+
docker run -v $(pwd):/var/loadtest --net host -it yandex/yandex-tank
2525
```

load-testing/app.py

Lines changed: 77 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -1,51 +1,56 @@
1-
import peewee
2-
from fastapi import FastAPI
31
import logging
4-
import uvicorn
52
import random
3+
from contextlib import asynccontextmanager
4+
5+
import peewee
6+
import uvicorn
7+
from aiopg.connection import Connection
8+
from aiopg.pool import Pool
9+
from fastapi import FastAPI
10+
11+
acquire = Pool.acquire
12+
cursor = Connection.cursor
13+
14+
15+
def new_acquire(self):
16+
choice = random.randint(1, 5)
17+
if choice == 5:
18+
raise Exception("some network error") # network error imitation
19+
return acquire(self)
20+
21+
22+
def new_cursor(self):
23+
choice = random.randint(1, 5)
24+
if choice == 5:
25+
raise Exception("some network error") # network error imitation
26+
return cursor(self)
27+
28+
Connection.cursor = new_cursor
29+
Pool.acquire = new_acquire
630

731
import peewee_async
8-
from contextlib import asynccontextmanager
9-
import functools
32+
1033

1134
logging.basicConfig()
12-
pg_db = peewee_async.PooledPostgresqlDatabase(None)
35+
pg_db = peewee_async.PooledPostgresqlDatabase(
36+
database='postgres',
37+
user='postgres',
38+
password='postgres',
39+
host='localhost',
40+
port=5432,
41+
max_connections=3
42+
)
1343

1444

1545
class Manager(peewee_async.Manager):
1646
"""Async models manager."""
1747

18-
database = peewee_async.PooledPostgresqlDatabase(
19-
database='postgres',
20-
user='postgres',
21-
password='postgres',
22-
host='localhost',
23-
port=5432,
24-
)
48+
database = pg_db
2549

2650

2751
manager = Manager()
2852

2953

30-
def patch_manager(manager):
31-
async def cursor(self, conn=None, *args, **kwargs):
32-
33-
choice = random.randint(1, 5)
34-
if choice == 5:
35-
raise Exception("some network error") # network error imitation
36-
37-
# actual code
38-
in_transaction = conn is not None
39-
if not conn:
40-
conn = await self.acquire()
41-
cursor = await conn.cursor(*args, **kwargs)
42-
cursor.release = functools.partial(
43-
self.release_cursor, cursor,
44-
in_transaction=in_transaction)
45-
return cursor
46-
47-
manager.database._async_conn_cls.cursor = cursor
48-
4954
def setup_logging():
5055
logger = logging.getLogger("uvicorn.error")
5156
handler = logging.FileHandler(filename="app.log", mode="w")
@@ -70,7 +75,6 @@ async def lifespan(app: FastAPI):
7075
operation='TRUNCATE TABLE MySimplestModel;',
7176
)
7277
setup_logging()
73-
patch_manager(manager)
7478
yield
7579
# Clean up the ML models and release the resources
7680
await manager.close()
@@ -90,6 +94,46 @@ async def test():
9094
return errors
9195

9296

97+
async def nested_transaction():
98+
async with manager.transaction():
99+
await manager.execute(MySimplestModel.update(id=1))
100+
101+
102+
async def nested_atomic():
103+
async with manager.atomic():
104+
await manager.execute(MySimplestModel.update(id=1))
105+
106+
107+
@app.get("/transaction")
108+
async def test():
109+
try:
110+
async with manager.transaction():
111+
await manager.execute(MySimplestModel.update(id=1))
112+
await nested_transaction()
113+
except Exception as e:
114+
errors.add(str(e))
115+
raise
116+
return errors
117+
118+
119+
@app.get("/atomic")
120+
async def test():
121+
try:
122+
async with manager.atomic():
123+
await manager.execute(MySimplestModel.update(id=1))
124+
await nested_atomic()
125+
except Exception as e:
126+
errors.add(str(e))
127+
raise
128+
return errors
129+
130+
131+
@app.get("/recreate_pool")
132+
async def test():
133+
await manager.database.close_async()
134+
await manager.database.connect_async()
135+
136+
93137
if __name__ == "__main__":
94138
uvicorn.run(
95139
app,

load-testing/load.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ phantom:
44
- /select
55
load_profile:
66
load_type: rps # schedule load by defining requests per second
7-
schedule: const(50, 10m) # starting from 1rps growing linearly to 10rps during 10 minutes
7+
schedule: const(100, 10m) # starting from 1rps growing linearly to 10rps during 10 minutes
88
console:
99
enabled: true # enable console output
1010
telegraf:

0 commit comments

Comments
 (0)