From ccf99af7e194b6b8b55d8d2091e9c0aa5c82e88a Mon Sep 17 00:00:00 2001 From: austnv Date: Sun, 14 Jun 2026 23:23:05 +0300 Subject: [PATCH] fix: add async cache to async operations and sync cache (stdlib) for sync operations --- pyproject.toml | 1 + routers/ip.py | 27 ++++++++++++++++++--------- routers/tariffs.py | 15 ++++++++------- uv.lock | 13 ++++++++++++- 4 files changed, 39 insertions(+), 17 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 35495a6..23a9541 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -6,6 +6,7 @@ readme = "README.md" requires-python = ">=3.12" dependencies = [ "aiofiles>=25.1.0", + "async-lru>=2.3.0", "bs4>=0.0.2", "fastapi>=0.136.3", "httpx>=0.28.1", diff --git a/routers/ip.py b/routers/ip.py index 1fbd1cd..40f05d5 100644 --- a/routers/ip.py +++ b/routers/ip.py @@ -3,12 +3,15 @@ from typing import Optional, Dict, Any, Tuple from bs4 import BeautifulSoup from pydantic import BaseModel, Field from fastapi import APIRouter, Request -from functools import lru_cache from httpx import AsyncClient +from async_lru import alru_cache +from functools import lru_cache +import asyncio import logging + logging.basicConfig(level=logging.INFO) @@ -64,6 +67,7 @@ class IPInfo: def __init__(self, html: str): self._data = self._parse(html) + @lru_cache(maxsize=1000) def _parse(self, html: str) -> Optional[IPInfoModel]: """Парсит HTML и возвращает модель""" soup = BeautifulSoup(html, 'html.parser') @@ -168,7 +172,14 @@ class IPInfo: router = APIRouter(prefix='/api/v1', tags=['ip']) -@lru_cache +@alru_cache(maxsize=1000, ttl=86400) +async def get_ip_info(ip: str, user_agent: str): + """Асинхронная функция для получения информации об IP с кешированием.""" + async with AsyncClient(headers={'User-Agent': user_agent}) as client: + response = await client.get(f'https://ipinfo.io/{ip}') + return response.text + + @router.get('/ip') async def get_ip(request: Request): forwarded = request.headers.get('X-Forwarded-For') @@ -180,11 +191,9 @@ async def get_ip(request: Request): else: client_ip = request.client.host - async with AsyncClient(headers={'User-Agent': user_agent}) as client: - response = await client.get(f'https://ipinfo.io/{client_ip}') - - html = response.text - ipinfo = IPInfo(html) - logging.info(ipinfo._data) + html = await get_ip_info(client_ip, user_agent) - return ipinfo.dict() \ No newline at end of file + result = await asyncio.to_thread(lambda: IPInfo(html).dict()) + logging.info(result) + + return result \ No newline at end of file diff --git a/routers/tariffs.py b/routers/tariffs.py index 549e607..eb5ba73 100644 --- a/routers/tariffs.py +++ b/routers/tariffs.py @@ -1,15 +1,16 @@ from fastapi import APIRouter -from functools import lru_cache +import aiofiles import json -import asyncio +from async_lru import alru_cache router = APIRouter(prefix='/api/v1', tags=['tariffs']) -@lru_cache(maxsize=1) -def get_tariffs_sync(): - with open('data/tariffs.json', 'r', encoding='utf-8') as f: - return json.load(f) +@alru_cache(maxsize=1, ttl=86400) +async def get_tariffs_sync(): + async with aiofiles.open('data/tariffs.json', 'r', encoding='utf-8') as f: + content = await f.read() + return json.loads(content) @router.get('/tariffs') async def get_tariffs(): - return await asyncio.to_thread(get_tariffs_sync) \ No newline at end of file + return await get_tariffs_sync() \ No newline at end of file diff --git a/uv.lock b/uv.lock index ea0b499..5c83dd7 100644 --- a/uv.lock +++ b/uv.lock @@ -47,6 +47,15 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/da/42/e921fccf5015463e32a3cf6ee7f980a6ed0f395ceeaa45060b61d86486c2/anyio-4.13.0-py3-none-any.whl", hash = "sha256:08b310f9e24a9594186fd75b4f73f4a4152069e3853f1ed8bfbf58369f4ad708", size = 114353, upload-time = "2026-03-24T12:59:08.246Z" }, ] +[[package]] +name = "async-lru" +version = "2.3.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/e8/1f/989ecfef8e64109a489fff357450cb73fa73a865a92bd8c272170a6922c2/async_lru-2.3.0.tar.gz", hash = "sha256:89bdb258a0140d7313cf8f4031d816a042202faa61d0ab310a0a538baa1c24b6", size = 16332, upload-time = "2026-03-19T01:04:32.413Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e5/e2/c2e3abf398f80732e58b03be77bde9022550d221dd8781bf586bd4d97cc1/async_lru-2.3.0-py3-none-any.whl", hash = "sha256:eea27b01841909316f2cc739807acea1c623df2be8c5cfad7583286397bb8315", size = 8403, upload-time = "2026-03-19T01:04:30.883Z" }, +] + [[package]] name = "beautifulsoup4" version = "4.15.0" @@ -889,10 +898,11 @@ wheels = [ [[package]] name = "uvpn-backend" -version = "0.1.0" +version = "0.2.0" source = { virtual = "." } dependencies = [ { name = "aiofiles" }, + { name = "async-lru" }, { name = "bs4" }, { name = "fastapi" }, { name = "httpx" }, @@ -907,6 +917,7 @@ dev = [ [package.metadata] requires-dist = [ { name = "aiofiles", specifier = ">=25.1.0" }, + { name = "async-lru", specifier = ">=2.3.0" }, { name = "bs4", specifier = ">=0.0.2" }, { name = "fastapi", specifier = ">=0.136.3" }, { name = "httpx", specifier = ">=0.28.1" },