diff --git a/.vscode/settings.json b/.vscode/settings.json index d46ed5d..b881eff 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,3 +1,3 @@ { - "python.analysis.autoImportCompletions": false + "python.analysis.autoImportCompletions": true } \ No newline at end of file diff --git a/README.md b/README.md index f7ec803..3a10514 100644 --- a/README.md +++ b/README.md @@ -38,6 +38,7 @@ - 🔍 时间范围审计日志: 从时间范围获取交易记录 - 🚀 导出数据: 支持从 Json 文件导入/导出到 Json 文件 - 🔧 依赖注入: 支持依赖注入模式调用 +- ⚡️ 高性能: LRU淘汰策略应用层缓存 ### 快速开始 @@ -74,6 +75,12 @@ plugins = ["nonebot_plugin_value"] ### [API Docs](https://docs.suggar.top/project/value/docs/api) +### 配置项 + +```dotenv +VALUE_PRE_BUILD_CACHE = true # 是否在启动时预构建缓存 +``` + ### 更新迁移 1. 升级 nonebot_plugin_value 到最新版本 diff --git a/nonebot_plugin_value/__init__.py b/nonebot_plugin_value/__init__.py index 0e70cec..1277d01 100644 --- a/nonebot_plugin_value/__init__.py +++ b/nonebot_plugin_value/__init__.py @@ -1,12 +1,13 @@ -from nonebot import get_driver +from nonebot import get_driver, get_plugin_config, logger from nonebot.plugin import PluginMetadata, require require("nonebot_plugin_orm") require("nonebot_plugin_localstore") from .api import api_balance, api_currency, api_transaction -from .api.api_currency import get_or_create_currency +from .api.api_currency import get_or_create_currency, list_currencies from .api.depends import factory +from .config import Config from .hook import context, hooks_manager, hooks_type from .models import currency from .pyd_models import balance_pyd, base_pyd, currency_pyd @@ -44,3 +45,9 @@ async def init_db(): 初始化数据库 """ await get_or_create_currency(CurrencyData(id=DEFAULT_CURRENCY_UUID.hex)) + if get_plugin_config(Config).value_pre_build_cache: + logger.info("正在初始化缓存...") + logger.info("正在初始化货币缓存...") + await list_currencies() + logger.info("正在初始化账户缓存...") + await api_balance.list_accounts() diff --git a/nonebot_plugin_value/_cache.py b/nonebot_plugin_value/_cache.py new file mode 100644 index 0000000..7b88090 --- /dev/null +++ b/nonebot_plugin_value/_cache.py @@ -0,0 +1,151 @@ +from __future__ import annotations + +import enum +from asyncio import Lock +from collections import OrderedDict +from collections.abc import Hashable +from dataclasses import dataclass, field +from functools import lru_cache +from typing import Any, Generic, TypeVar + +from typing_extensions import Self + +from .pyd_models.balance_pyd import UserAccountData +from .pyd_models.base_pyd import BaseData +from .pyd_models.currency_pyd import CurrencyData +from .pyd_models.transaction_pyd import TransactionData + + +class CacheCategoryEnum(str, enum.Enum): + CURRENCY = "currency" + ACCOUNT = "account" + TRANSACTION = "transaction" + + +T = TypeVar("T", BaseData, CurrencyData, TransactionData, UserAccountData) + + +@dataclass +class Cache(Generic[T]): + """Cache存储模型""" + + # 默认缓存最大条目数 + max_size: int = 1000 + + # LRU实现 + _cache: OrderedDict[str, BaseData] = field(default_factory=lambda: OrderedDict()) + + def __post_init__(self): + if self.max_size <= 0: + self.max_size = 1000 + + async def update(self, *, data: BaseData) -> bool: + data_id = data.uni_id if isinstance(data, UserAccountData) else data.id + async with self._lock(data_id): + if existing := self._cache.get(data_id): + existing.model_validate(data, from_attributes=True) + self._cache.move_to_end(data_id) + return True + + # 添加新数据 + self._cache[data_id] = data + self._cache.move_to_end(data_id) + + # 如果超出最大大小,删除最久未使用的项(第一个) + if len(self._cache) > self.max_size: + self._cache.popitem(last=False) + + return False + + async def get(self, *, data_id: str) -> BaseData | None: + async with self._lock(data_id): + item = self._cache.get(data_id) + if item is not None: + # 访问后移到末尾(标记为最近使用) + self._cache.move_to_end(data_id) + return item + + async def get_all(self) -> list[BaseData]: + async with self._lock(): + # 返回所有缓存项的副本 + return list(self._cache.values()) + + async def delete(self, *, data_id: str): + async with self._lock(data_id): + self._cache.pop(data_id, None) + + async def clear(self): + async with self._lock(0): + self._cache.clear() + + @staticmethod + @lru_cache(1024) + def _lock(*args: Hashable) -> Lock: + return Lock() + + +class CacheManager: + _instance = None + _cached: dict[CacheCategoryEnum, Cache[Any]] + + def __new__(cls) -> Self: + if cls._instance is None: + cls._instance = super().__new__(cls) + cls._cached = {} + return cls._instance + + async def get_cache( + self, category: CacheCategoryEnum, max_size: int = 1000 + ) -> Cache[Any]: + # 为不同类别创建具有不同大小的缓存 + if category not in self._cached: + self._cached[category] = Cache(max_size=max_size) + return self._cached[category] + + async def update_cache( + self, + *, + category: CacheCategoryEnum, + data: BaseData, + max_size: int = 1000, + ) -> Self: + """更新缓存 + + Args: + category (CacheCategoryEnum): 缓存板块 + data (BaseData): 数据 + max_size (int): 缓存最大大小 + """ + async with self._get_lock(category): + cache = await self.get_cache(category, max_size) + await cache.update(data=data) + return self + + async def expire_cache( + self, *, category: CacheCategoryEnum, data_id: str | None = None + ) -> Self: + """使缓存过期(当数据库操作中该条删除时使用) + + Args: + category (CacheCategoryEnum): 缓存板块 + data_id (str | None, optional): 数据ID. Defaults to None. + """ + async with self._get_lock(category): + if category in self._cached: + if data_id is not None: + cache = await self.get_cache(category) + await cache.delete(data_id=data_id) + else: + self._cached.pop(category, None) + return self + + async def expire_all_cache(self) -> Self: + """使所有缓存过期""" + + self._cached.clear() + return self + + @staticmethod + @lru_cache(1024) + def _get_lock(*args: Hashable) -> Lock: + return Lock() diff --git a/nonebot_plugin_value/api/api_balance.py b/nonebot_plugin_value/api/api_balance.py index 396c344..155b70a 100644 --- a/nonebot_plugin_value/api/api_balance.py +++ b/nonebot_plugin_value/api/api_balance.py @@ -1,5 +1,8 @@ +import typing + from nonebot_plugin_orm import get_session +from .._cache import CacheCategoryEnum, CacheManager from ..pyd_models.balance_pyd import UserAccountData from ..services.balance import add_balance as _a_balance from ..services.balance import batch_add_balance as _batch_add @@ -11,7 +14,8 @@ from ..services.balance import set_frozen as _set_frozen from ..services.balance import set_frozen_all as _set_frozen_all from ..services.balance import transfer_funds as _transfer -from ..uuid_lib import DEFAULT_CURRENCY_UUID +from ..uuid_lib import DEFAULT_CURRENCY_UUID, get_uni_id +from .api_currency import list_currencies async def set_frozen_all(account_id: str, frozen: bool) -> None: @@ -22,6 +26,12 @@ async def set_frozen_all(account_id: str, frozen: bool) -> None: frozen (bool): 是否冻结 """ async with get_session() as session: + currencies = await list_currencies() + for currency in currencies: + await CacheManager().expire_cache( + category=CacheCategoryEnum.ACCOUNT, + data_id=get_uni_id(account_id, currency.id), + ) await _set_frozen_all(account_id, frozen, session) @@ -34,11 +44,17 @@ async def set_frozen(account_id: str, currency_id: str, frozen: bool) -> None: frozen (bool): 是否冻结 """ async with get_session() as session: + await CacheManager().expire_cache( + category=CacheCategoryEnum.ACCOUNT, + data_id=get_uni_id(account_id, currency_id), + ) await _set_frozen(account_id, frozen, currency_id, session) -async def list_accounts(currency_id: str | None = None) -> list[UserAccountData]: - """获取指定货币(或默认)的账户列表 +async def list_accounts( + currency_id: str | None = None, no_cache_refresh: bool = False +) -> list[UserAccountData]: + """获取指定货币(或默认)的账户列表(无法命中缓存,但是可以更新缓存) Args: currency_id (str | None, optional): 货币ID. Defaults to None. @@ -49,7 +65,7 @@ async def list_accounts(currency_id: str | None = None) -> list[UserAccountData] if currency_id is None: currency_id = DEFAULT_CURRENCY_UUID.hex async with get_session() as session: - return [ + result = [ UserAccountData( id=account.id, uni_id=account.uni_id, @@ -59,6 +75,13 @@ async def list_accounts(currency_id: str | None = None) -> list[UserAccountData] ) for account in await _list_accounts(session, currency_id) ] + if not no_cache_refresh: + for account in result: + await CacheManager().update_cache( + category=CacheCategoryEnum.ACCOUNT, + data=account, + ) + return result async def del_account(user_id: str, currency_id: str | None = None) -> bool: @@ -73,11 +96,17 @@ async def del_account(user_id: str, currency_id: str | None = None) -> bool: """ if currency_id is None: currency_id = DEFAULT_CURRENCY_UUID.hex + await CacheManager().expire_cache( + category=CacheCategoryEnum.ACCOUNT, data_id=user_id + ) return await _del_account(user_id, currency_id=currency_id) async def get_or_create_account( - user_id: str, currency_id: str | None = None + user_id: str, + currency_id: str | None = None, + no_cache_use: bool = False, + no_cache_refresh: bool = False, ) -> UserAccountData: """获取账户数据(不存在就创建) @@ -88,19 +117,34 @@ async def get_or_create_account( Returns: UserAccountData: 用户数据 """ + cache_manager = CacheManager() if currency_id is None: currency_id = DEFAULT_CURRENCY_UUID.hex + if not no_cache_use: + if ( + result := await ( + await cache_manager.get_cache(category=CacheCategoryEnum.ACCOUNT) + ).get(data_id=user_id) + is not None + ): + return typing.cast(UserAccountData, result) async with get_session() as session: data = await _go_account(user_id, currency_id, session) session.add(data) - return UserAccountData.model_validate(data, from_attributes=True) + data = UserAccountData.model_validate(data, from_attributes=True) + if not no_cache_refresh: + await cache_manager.update_cache( + category=CacheCategoryEnum.ACCOUNT, + data=data, + ) + return data async def batch_del_balance( updates: list[tuple[str, float]], currency_id: str | None = None, source: str = "batch_update", -) -> list[UserAccountData]: +) -> list[UserAccountData] | None: """批量减少账户余额 Args: @@ -109,22 +153,22 @@ async def batch_del_balance( source (str, optional): 源说明. Defaults to "batch_update". Returns: - list[UserAccountData]: 用户账户数据列表 + None: None """ - data_list: list[UserAccountData] = [] if currency_id is None: currency_id = DEFAULT_CURRENCY_UUID.hex await _batch_del(updates, currency_id, source, return_all_on_fail=True) for user_id, _ in updates: - data_list.append(await get_or_create_account(user_id, currency_id)) - return data_list + await CacheManager().expire_cache( + category=CacheCategoryEnum.ACCOUNT, data_id=get_uni_id(user_id, currency_id) + ) async def batch_add_balance( updates: list[tuple[str, float]], currency_id: str | None = None, source: str = "batch_update", -) -> list[UserAccountData]: +) -> list[UserAccountData] | None: """批量添加账户余额 Args: @@ -133,15 +177,15 @@ async def batch_add_balance( source (str, optional): 源说明. Defaults to "batch_update". Returns: - list[UserAccountData]: 用户账户数据列表 + None: None """ - data_list: list[UserAccountData] = [] if currency_id is None: currency_id = DEFAULT_CURRENCY_UUID.hex await _batch_add(updates, currency_id, source, return_all_on_fail=True) for user_id, _ in updates: - data_list.append(await get_or_create_account(user_id, currency_id)) - return data_list + await CacheManager().expire_cache( + category=CacheCategoryEnum.ACCOUNT, data_id=get_uni_id(user_id, currency_id) + ) async def add_balance( @@ -149,7 +193,7 @@ async def add_balance( amount: float, source: str = "_transfer", currency_id: str | None = None, -) -> UserAccountData: +) -> None: """添加用户余额 Args: @@ -162,7 +206,7 @@ async def add_balance( RuntimeError: 如果添加失败则抛出异常 Returns: - UserAccountData: 用户账户数据 + None: None """ if currency_id is None: @@ -170,7 +214,9 @@ async def add_balance( data = await _a_balance(user_id, currency_id, amount, source) if not data.success: raise RuntimeError(data.message) - return await get_or_create_account(user_id, currency_id) + await CacheManager().expire_cache( + category=CacheCategoryEnum.ACCOUNT, data_id=get_uni_id(user_id, currency_id) + ) async def del_balance( @@ -178,7 +224,7 @@ async def del_balance( amount: float, source: str = "_transfer", currency_id: str | None = None, -) -> UserAccountData: +) -> None: """减少一个账户的余额 Args: @@ -198,7 +244,9 @@ async def del_balance( data = await _d_balance(user_id, currency_id, amount, source) if not data.success: raise RuntimeError(data.message) - return await get_or_create_account(user_id, currency_id) + await CacheManager().expire_cache( + category=CacheCategoryEnum.ACCOUNT, data_id=get_uni_id(user_id, currency_id) + ) async def transfer_funds( @@ -207,7 +255,7 @@ async def transfer_funds( amount: float, source: str = "", currency_id: str | None = None, -) -> UserAccountData: +) -> None: """转账 Args: @@ -221,7 +269,7 @@ async def transfer_funds( RuntimeError: 失败则抛出 Returns: - UserAccountData: 用户账户数据 + None: None """ if currency_id is None: currency_id = DEFAULT_CURRENCY_UUID.hex @@ -230,4 +278,3 @@ async def transfer_funds( data = await _transfer(from_id, to_id, currency_id, amount, source) if not data.success: raise RuntimeError(data.message) - return await get_or_create_account(to_id, currency_id) diff --git a/nonebot_plugin_value/api/api_currency.py b/nonebot_plugin_value/api/api_currency.py index 53fb5fd..dba6f75 100644 --- a/nonebot_plugin_value/api/api_currency.py +++ b/nonebot_plugin_value/api/api_currency.py @@ -1,5 +1,8 @@ +import typing + from nonebot_plugin_orm import get_session +from .._cache import CacheCategoryEnum, CacheManager from ..pyd_models.currency_pyd import CurrencyData from ..services.currency import create_currency as _create_currency from ..services.currency import get_currency as _g_currency @@ -22,7 +25,12 @@ async def update_currency(currency_data: CurrencyData) -> CurrencyData: """ async with get_session() as session: currency = await _update_currency(currency_data, session) - return CurrencyData.model_validate(currency, from_attributes=True) + data = CurrencyData.model_validate(currency, from_attributes=True) + await CacheManager().update_cache( + category=CacheCategoryEnum.CURRENCY, + data=data, + ) + return data async def remove_currency(currency_id: str) -> None: @@ -31,24 +39,34 @@ async def remove_currency(currency_id: str) -> None: Args: currency_id (str): 货币唯一ID """ - + await CacheManager().expire_cache( + category=CacheCategoryEnum.CURRENCY, data_id=currency_id + ) await _remove_currency(currency_id) -async def list_currencies() -> list[CurrencyData]: - """获取所有已存在货币 +async def list_currencies(no_cache_update: bool = False) -> list[CurrencyData]: + """获取所有已存在货币(无法命中缓存) Returns: list[CurrencyData]: 包含所有已存在货币的列表 """ async with get_session() as session: currencies = await _currencies(session) - return [ + result = [ CurrencyData.model_validate(currency, from_attributes=True) for currency in currencies ] + if not no_cache_update: + for currency in result: + await CacheManager().update_cache( + category=CacheCategoryEnum.CURRENCY, + data=currency, + ) + return result -async def get_currency(currency_id: str) -> CurrencyData | None: + +async def get_currency(currency_id: str, no_cache: bool = False) -> CurrencyData | None: """获取一个货币信息 Args: @@ -58,6 +76,11 @@ async def get_currency(currency_id: str) -> CurrencyData | None: CurrencyData | None: 货币数据,如果不存在则返回None """ async with get_session() as session: + if not no_cache: + if currency := await ( + await CacheManager().get_cache(CacheCategoryEnum.CURRENCY) + ).get(data_id=currency_id): + return typing.cast(CurrencyData, currency) currency = await _g_currency(currency_id, session) if currency is None: return None @@ -65,7 +88,7 @@ async def get_currency(currency_id: str) -> CurrencyData | None: async def get_currency_by_kwargs(**kwargs: object) -> CurrencyData | None: - """获取一个货币信息 + """获取一个货币信息(此函数无法命中缓存) Args: **kwargs (object): 通过货币属性联合查询获取货币信息 @@ -101,6 +124,10 @@ async def create_currency(currency_data: CurrencyData) -> None: CurrencyData: 货币信息 """ async with get_session() as session: + await CacheManager().update_cache( + category=CacheCategoryEnum.CURRENCY, + data=currency_data, + ) await _create_currency(currency_data, session) @@ -115,4 +142,9 @@ async def get_or_create_currency(currency_data: CurrencyData) -> CurrencyData: """ async with get_session() as session: currency, _ = await _get_or_create_currency(currency_data, session) - return CurrencyData.model_validate(currency, from_attributes=True) + data = CurrencyData.model_validate(currency, from_attributes=True) + await CacheManager().update_cache( + category=CacheCategoryEnum.CURRENCY, + data=data, + ) + return data diff --git a/nonebot_plugin_value/api/api_transaction.py b/nonebot_plugin_value/api/api_transaction.py index c8db244..5591a3d 100644 --- a/nonebot_plugin_value/api/api_transaction.py +++ b/nonebot_plugin_value/api/api_transaction.py @@ -1,3 +1,4 @@ +# 交易记录API(注:均无法命中缓存) from datetime import datetime from nonebot_plugin_orm import get_session diff --git a/nonebot_plugin_value/api/executor.py b/nonebot_plugin_value/api/executor.py index 5db950e..f1e001b 100644 --- a/nonebot_plugin_value/api/executor.py +++ b/nonebot_plugin_value/api/executor.py @@ -14,16 +14,12 @@ @dataclass class AccountExecutor: + # 更改说明:由于已经做了一个全局缓存,不再需要再在这里存储账户信息映射。 currency_id: str = field(default=DEFAULT_CURRENCY_UUID.hex) user_id: str = field(default="") - data_map: dict[str, UserAccountData] = field(default_factory=lambda: {}) async def __call__(self, event: Event) -> Self: self.user_id = to_uuid(event.get_user_id()) - currency_id = self.currency_id - self.data_map[currency_id] = await get_or_create_account( - self.user_id, currency_id - ) return self async def get_data(self, currency_id: str | None = None) -> UserAccountData: @@ -36,15 +32,7 @@ async def get_data(self, currency_id: str | None = None) -> UserAccountData: UserAccountData: 账号数据 """ currency_id = currency_id or self.currency_id - if data := self.data_map.get( - currency_id, - ): - return data - self.data_map[currency_id] = self.data_map.get( - currency_id, - await get_or_create_account(self.user_id, currency_id), - ) - return self.data_map[currency_id] + return await get_or_create_account(self.user_id, currency_id) async def get_balance( self, @@ -77,7 +65,7 @@ async def add_balance( Self: Self """ currency_id = currency_id or self.currency_id - self.data_map[currency_id] = await add_balance( + await add_balance( self.user_id, amount, source, @@ -102,7 +90,7 @@ async def decrease_balance( Self: Self """ currency_id = currency_id or self.currency_id - self.data_map[currency_id] = await del_balance( + await del_balance( self.user_id, amount, source, diff --git a/nonebot_plugin_value/config.py b/nonebot_plugin_value/config.py new file mode 100644 index 0000000..ee80701 --- /dev/null +++ b/nonebot_plugin_value/config.py @@ -0,0 +1,9 @@ +from pydantic import BaseModel + + +class Config(BaseModel): + """ + 配置 + """ + + value_pre_build_cache: bool = True diff --git a/nonebot_plugin_value/hook/hooks_type.py b/nonebot_plugin_value/hook/hooks_type.py index e9ecf08..4e059c2 100644 --- a/nonebot_plugin_value/hook/hooks_type.py +++ b/nonebot_plugin_value/hook/hooks_type.py @@ -12,6 +12,7 @@ def pre(cls) -> str: @classmethod def post(cls) -> str: return cls.POST.value + @classmethod def methods(cls) -> list[str]: return [hook.value for hook in cls] diff --git a/nonebot_plugin_value/pyd_models/balance_pyd.py b/nonebot_plugin_value/pyd_models/balance_pyd.py index c505286..a00cf4e 100644 --- a/nonebot_plugin_value/pyd_models/balance_pyd.py +++ b/nonebot_plugin_value/pyd_models/balance_pyd.py @@ -11,4 +11,4 @@ class UserAccountData(BaseData): currency_id: str = Field(default="") balance: float = Field(default=0.0) last_updated: datetime = Field(default_factory=lambda: datetime.now(timezone.utc)) - frozen:bool = Field(default=False) + frozen: bool = Field(default=False) diff --git a/nonebot_plugin_value/repositories/currency.py b/nonebot_plugin_value/repositories/currency.py index 8b5c50a..6e789d3 100644 --- a/nonebot_plugin_value/repositories/currency.py +++ b/nonebot_plugin_value/repositories/currency.py @@ -95,7 +95,6 @@ async def update_currency(self, currency_data: CurrencyData) -> CurrencyMeta: await session.rollback() raise - async def list_currencies(self) -> Sequence[CurrencyMeta]: """列出所有货币""" async with self.session as session: diff --git a/nonebot_plugin_value/uuid_lib.py b/nonebot_plugin_value/uuid_lib.py index 2b1a677..4af5b67 100644 --- a/nonebot_plugin_value/uuid_lib.py +++ b/nonebot_plugin_value/uuid_lib.py @@ -5,6 +5,7 @@ DEFAULT_NAME = "DEFAULT_CURRENCY_USD" DEFAULT_CURRENCY_UUID = uuid5(NAMESPACE_VALUE, DEFAULT_NAME) + def to_uuid(s: str) -> str: """获取UUID diff --git a/pyproject.toml b/pyproject.toml index 429bbcf..ae2651d 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "nonebot-plugin-value" -version = "0.1.6.post1" +version = "0.1.7" description = "Economy API for NoneBot2" readme = "README.md" requires-python = ">=3.10, <4.0.0" diff --git a/tests/value_tests/balance_transfer.py b/tests/value_tests/balance_transfer.py index ef00d75..d98571b 100644 --- a/tests/value_tests/balance_transfer.py +++ b/tests/value_tests/balance_transfer.py @@ -16,4 +16,4 @@ async def test_transfer(app: App): await get_or_create_account(u1) await get_or_create_account(u2) await add_balance(u1, 100) - assert (await transfer_funds(u1, u2, 50)).balance == 50 + await transfer_funds(u1, u2, 50) diff --git a/tests/value_tests/cache_test.py b/tests/value_tests/cache_test.py new file mode 100644 index 0000000..bc705ff --- /dev/null +++ b/tests/value_tests/cache_test.py @@ -0,0 +1,58 @@ +import pytest +from nonebug import App # type:ignore + + +@pytest.mark.asyncio +async def test_cache(app: App): + from nonebot_plugin_value._cache import CacheCategoryEnum, CacheManager + from nonebot_plugin_value.pyd_models.balance_pyd import UserAccountData + from nonebot_plugin_value.pyd_models.currency_pyd import CurrencyData + from nonebot_plugin_value.pyd_models.transaction_pyd import TransactionData + + TEST_CURRENCY = CurrencyData(id="1", display_name="test", symbol="t") + TEST_USER = UserAccountData(id="1", uni_id="1", currency_id="1") + TEST_TRANSACTION = TransactionData(id="1", source="test", amount=1, currency_id="1") + cache_manager = CacheManager() + + await cache_manager.update_cache( + category=CacheCategoryEnum.CURRENCY, + data=TEST_CURRENCY, + ) + await cache_manager.update_cache( + category=CacheCategoryEnum.ACCOUNT, + data=TEST_USER, + ) + await cache_manager.update_cache( + category=CacheCategoryEnum.TRANSACTION, + data=TEST_TRANSACTION, + ) + assert ( + await (await cache_manager.get_cache(CacheCategoryEnum.CURRENCY)).get( + data_id="1" + ) + == TEST_CURRENCY + ) + assert ( + await (await cache_manager.get_cache(CacheCategoryEnum.ACCOUNT)).get( + data_id="1" + ) + ) == TEST_USER + assert ( + await (await cache_manager.get_cache(CacheCategoryEnum.TRANSACTION)).get( + data_id="1" + ) + == TEST_TRANSACTION + ) + await cache_manager.expire_cache(category=CacheCategoryEnum.CURRENCY) + assert not await (await cache_manager.get_cache(CacheCategoryEnum.CURRENCY)).get( + data_id="1" + ) + await cache_manager.expire_cache(category=CacheCategoryEnum.ACCOUNT) + assert not await (await cache_manager.get_cache(CacheCategoryEnum.ACCOUNT)).get( + data_id="1" + ) + await cache_manager.expire_cache(category=CacheCategoryEnum.TRANSACTION) + assert not await (await cache_manager.get_cache(CacheCategoryEnum.TRANSACTION)).get( + data_id="1" + ) + await cache_manager.expire_all_cache()