Skip to content

Commit d543a8c

Browse files
authored
Test cache invalidation update insert delete (#4)
1 parent bfe7ee7 commit d543a8c

File tree

2 files changed

+107
-2
lines changed

2 files changed

+107
-2
lines changed

db/init_db.sh

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,5 +15,6 @@ psql -v ON_ERROR_STOP=1 --username "$POSTGRES_USER" --dbname testdb <<-EOSQL
1515
INSERT INTO sysconf (key, value) VALUES
1616
('app_name', 'MyApplication'),
1717
('app_version', '1.0.0'),
18-
('maintenance_mode', 'false');
18+
('maintenance_mode', 'false'),
19+
('updated_at', '2024-02-19 23:01:54.609243+00');
1920
EOSQL

tests/test_integration.py

Lines changed: 105 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,16 @@
11
import asyncio
22
import collections
3+
import datetime
34

45
import asyncpg
56
import pytest
67
from pgcachewatch import cli, decorators, listeners, models, strategies
78

89

10+
def utcnow() -> datetime.datetime:
11+
return datetime.datetime.now(tz=datetime.timezone.utc)
12+
13+
914
async def test_1_install_triggers(
1015
monkeypatch: pytest.MonkeyPatch,
1116
pgconn: asyncpg.Connection,
@@ -54,7 +59,106 @@ async def fetch_sysconf() -> list:
5459
assert statistics["hit"] == N - 1
5560

5661

57-
async def test_3_uninstall_triggers(
62+
async def test_3_cache_invalidation_update(
63+
pgconn: asyncpg.Connection,
64+
pgpool: asyncpg.Pool,
65+
) -> None:
66+
statistics = collections.Counter[str]()
67+
listener = await listeners.PGEventQueue.create(
68+
models.PGChannel("ch_pgcachewatch_table_change"),
69+
pgconn=pgconn,
70+
)
71+
72+
@decorators.cache(
73+
strategy=strategies.Gready(listener=listener),
74+
statistics_callback=lambda x: statistics.update([x]),
75+
)
76+
async def fetch_sysconf() -> list:
77+
return await pgpool.fetch("SELECT * FROM sysconf")
78+
79+
async def blast() -> list:
80+
before = await fetch_sysconf()
81+
while (rv := await fetch_sysconf()) == before:
82+
await asyncio.sleep(0.001)
83+
return rv
84+
85+
blast_task = asyncio.create_task(blast())
86+
await pgpool.execute(
87+
"UPDATE sysconf set value = $1 where key = 'updated_at'",
88+
utcnow().isoformat(),
89+
)
90+
await asyncio.wait_for(blast_task, 1)
91+
# First fetch and update
92+
assert statistics["miss"] == 2
93+
94+
95+
async def test_3_cache_invalidation_insert(
96+
pgconn: asyncpg.Connection,
97+
pgpool: asyncpg.Pool,
98+
) -> None:
99+
statistics = collections.Counter[str]()
100+
listener = await listeners.PGEventQueue.create(
101+
models.PGChannel("ch_pgcachewatch_table_change"),
102+
pgconn=pgconn,
103+
)
104+
105+
@decorators.cache(
106+
strategy=strategies.Gready(listener=listener),
107+
statistics_callback=lambda x: statistics.update([x]),
108+
)
109+
async def fetch_sysconf() -> list:
110+
return await pgpool.fetch("SELECT * FROM sysconf")
111+
112+
async def blast() -> list:
113+
before = await fetch_sysconf()
114+
while (rv := await fetch_sysconf()) == before:
115+
await asyncio.sleep(0.001)
116+
return rv
117+
118+
blast_task = asyncio.create_task(blast())
119+
await pgpool.execute(
120+
"INSERT INTO sysconf (key, value) VALUES ($1, $2);",
121+
utcnow().isoformat(),
122+
utcnow().isoformat(),
123+
)
124+
await asyncio.wait_for(blast_task, 1)
125+
# First fetch and insert
126+
assert statistics["miss"] == 2
127+
128+
129+
async def test_3_cache_invalidation_delete(
130+
pgconn: asyncpg.Connection,
131+
pgpool: asyncpg.Pool,
132+
) -> None:
133+
statistics = collections.Counter[str]()
134+
listener = await listeners.PGEventQueue.create(
135+
models.PGChannel("ch_pgcachewatch_table_change"),
136+
pgconn=pgconn,
137+
)
138+
139+
@decorators.cache(
140+
strategy=strategies.Gready(listener=listener),
141+
statistics_callback=lambda x: statistics.update([x]),
142+
)
143+
async def fetch_sysconf() -> list:
144+
return await pgpool.fetch("SELECT * FROM sysconf")
145+
146+
async def blast() -> list:
147+
before = await fetch_sysconf()
148+
while (rv := await fetch_sysconf()) == before:
149+
await asyncio.sleep(0.001)
150+
return rv
151+
152+
blast_task = asyncio.create_task(blast())
153+
await pgpool.execute(
154+
"DELETE FROM sysconf WHERE key ~ '^\\d{4}-\\d{2}-\\d{2}';",
155+
)
156+
await asyncio.wait_for(blast_task, 1)
157+
# First fetch and insert
158+
assert statistics["miss"] == 2
159+
160+
161+
async def test_4_uninstall_triggers(
58162
monkeypatch: pytest.MonkeyPatch,
59163
pgconn: asyncpg.Connection,
60164
) -> None:

0 commit comments

Comments
 (0)