Skip to content

Commit de03d5f

Browse files
committed
Use diskcache
1 parent 8dbaf41 commit de03d5f

4 files changed

Lines changed: 93 additions & 8 deletions

File tree

.gitignore

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,2 @@
1-
/http-cache.db*
21
__pycache__
2+
/_http_v1

_http_cache.py

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
from __future__ import annotations
2+
3+
import asyncio
4+
import concurrent.futures
5+
import contextlib
6+
import os
7+
from collections.abc import Callable, Set
8+
from functools import partial
9+
from typing import Never
10+
11+
import aiohttp_client_cache
12+
import diskcache
13+
14+
_http_cache_name = "_http_v1"
15+
16+
17+
@contextlib.asynccontextmanager
18+
async def make_cache():
19+
with concurrent.futures.ThreadPoolExecutor(1, "_http_cache") as executor:
20+
loop = asyncio.get_running_loop()
21+
22+
def run_in_thread2[**P, T](fn: Callable[P, T]):
23+
async def wrapper(*args: P.args, **kwargs: P.kwargs):
24+
return await loop.run_in_executor(executor, partial(fn, *args, **kwargs))
25+
26+
return wrapper
27+
28+
# diskcache will only close the sqlite connection if it was initialised
29+
# in the same thread.
30+
cache = await run_in_thread2(diskcache.Cache)(
31+
os.path.join(os.getcwd(), _http_cache_name), size_limit=2**30 * 10
32+
)
33+
34+
def make_cache_wrapper(prefix: str):
35+
class Cache(aiohttp_client_cache.BaseCache):
36+
@run_in_thread2
37+
def bulk_delete(self, keys: Set[str]):
38+
for key in keys:
39+
cache.delete((prefix, key))
40+
41+
@run_in_thread2
42+
def contains(self, key: str):
43+
return (prefix, key) in cache
44+
45+
@run_in_thread2
46+
def delete(self, key: str):
47+
cache.delete((prefix, key))
48+
49+
@run_in_thread2
50+
def read(self, key: str):
51+
return cache.get((prefix, key))
52+
53+
@run_in_thread2
54+
def write(self, key: str, item: aiohttp_client_cache.ResponseOrKey):
55+
cache[prefix, key] = item
56+
57+
async def clear(self) -> Never:
58+
raise NotImplementedError
59+
60+
async def keys(self):
61+
if False: # pragma: no cover
62+
yield
63+
raise NotImplementedError
64+
65+
async def values(self):
66+
if False: # pragma: no cover
67+
yield
68+
raise NotImplementedError
69+
70+
async def size(self) -> Never:
71+
raise NotImplementedError
72+
73+
return Cache()
74+
75+
try:
76+
yield make_cache_wrapper("responses"), make_cache_wrapper("redirects")
77+
finally:
78+
await run_in_thread2(cache.close)()

cataloguer.py

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -23,9 +23,7 @@
2323
import aiohttp
2424
import click
2525
import structlog
26-
from aiohttp_client_cache import SQLiteBackend
2726
from aiohttp_client_cache.cache_control import ExpirationPatterns
28-
from aiohttp_client_cache.session import CachedSession
2927
from attrs import field, fields, frozen
3028
from cattrs import BaseValidationError, Converter
3129
from yarl import URL
@@ -415,14 +413,23 @@ async def parse_repo(get: Get, repo: Mapping[str, Any]):
415413

416414
@asynccontextmanager
417415
async def _make_http_client(token: str):
418-
async with AsyncExitStack() as stack:
419-
cache_backend = SQLiteBackend(
416+
from aiohttp_client_cache import CacheBackend
417+
from aiohttp_client_cache.session import CachedSession
418+
419+
from _http_cache import make_cache
420+
421+
async with AsyncExitStack() as exit_stack:
422+
cache_backend = CacheBackend(
420423
autoclose=False,
421424
cache_name="http-cache.db",
422425
expire_after=CACHE_INDEFINITELY,
423426
urls_expire_after=EXPIRE_URLS,
427+
timeout=20,
428+
)
429+
cache_backend.responses, cache_backend.redirects = await exit_stack.enter_async_context(
430+
make_cache()
424431
)
425-
client = await stack.enter_async_context(
432+
client = await exit_stack.enter_async_context(
426433
CachedSession(
427434
cache=cache_backend,
428435
connector=aiohttp.TCPConnector(limit_per_host=8),
@@ -434,7 +441,6 @@ async def _make_http_client(token: str):
434441
timeout=aiohttp.ClientTimeout(sock_connect=10, sock_read=10),
435442
)
436443
)
437-
stack.push_async_callback(cache_backend.close)
438444

439445
@asynccontextmanager
440446
async def get(url: str | URL, **kwargs: Any):

pyproject.toml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,11 +9,12 @@ description = "Script which collects WoW add-on metadata from GitHub"
99
requires-python = ">= 3.11"
1010
license.file = "LICENSE"
1111
dependencies = [
12-
"aiohttp-client-cache[sqlite]",
1312
"aiohttp",
13+
"aiohttp-client-cache",
1414
"attrs",
1515
"cattrs",
1616
"click",
17+
"diskcache",
1718
"structlog",
1819
"yarl",
1920
]

0 commit comments

Comments
 (0)