arcade-mcp/toolkits/jira/arcade_jira/cache.py
Renato Byrro a538f4bf81
Jira Toolkit (#407)
Unusually big PR, in large part due to Jira's data model complexities.
2025-06-11 12:06:33 -03:00

105 lines
3.2 KiB
Python

import asyncio
from collections import OrderedDict
from threading import Lock
from typing import Generic, TypeVar
from arcade_jira.constants import JIRA_CACHE_MAX_ITEMS
T = TypeVar("T")
class LRUCache(Generic[T]):
def __init__(self, max_size: int):
self.cache: OrderedDict[str, T] = OrderedDict()
self.max_size = max_size
self.thread_lock = Lock()
self.async_lock = asyncio.Lock()
# Thread-safe synchronous methods
def get(self, key: str) -> T | None:
with self.thread_lock:
if key not in self.cache:
return None
value = self.cache.pop(key)
self.cache[key] = value
return value
def set(self, key: str, value: T) -> None:
with self.thread_lock:
if key in self.cache:
self.cache.pop(key)
elif len(self.cache) >= self.max_size:
self.cache.popitem(last=False)
self.cache[key] = value
# Async-safe methods
async def async_get(self, key: str) -> T | None:
async with self.async_lock:
if key not in self.cache:
return None
value = self.cache.pop(key)
self.cache[key] = value
return value
async def async_set(self, key: str, value: T) -> None:
async with self.async_lock:
if key in self.cache:
self.cache.pop(key)
elif len(self.cache) >= self.max_size:
self.cache.popitem(last=False)
self.cache[key] = value
CLOUD_ID_CACHE = LRUCache[str](max_size=JIRA_CACHE_MAX_ITEMS)
CLOUD_NAME_CACHE = LRUCache[str](max_size=JIRA_CACHE_MAX_ITEMS)
CLIENT_SEMAPHORE_CACHE = LRUCache[asyncio.Semaphore](max_size=JIRA_CACHE_MAX_ITEMS)
def get_cloud_id(auth_token: str) -> str | None:
return CLOUD_ID_CACHE.get(auth_token)
def get_cloud_name(auth_token: str) -> str | None:
return CLOUD_NAME_CACHE.get(auth_token)
def set_cloud_id(auth_token: str, cloud_id: str) -> None:
CLOUD_ID_CACHE.set(auth_token, cloud_id)
def set_cloud_name(auth_token: str, cloud_name: str) -> None:
CLOUD_NAME_CACHE.set(auth_token, cloud_name)
def get_jira_client_semaphore(auth_token: str) -> asyncio.Semaphore | None:
return CLIENT_SEMAPHORE_CACHE.get(auth_token)
def set_jira_client_semaphore(auth_token: str, semaphore: asyncio.Semaphore) -> None:
CLIENT_SEMAPHORE_CACHE.set(auth_token, semaphore)
async def async_get_cloud_id(auth_token: str) -> str | None:
return await CLOUD_ID_CACHE.async_get(auth_token)
async def async_get_cloud_name(auth_token: str) -> str | None:
return await CLOUD_NAME_CACHE.async_get(auth_token)
async def async_set_cloud_id(auth_token: str, cloud_id: str) -> None:
await CLOUD_ID_CACHE.async_set(auth_token, cloud_id)
async def async_set_cloud_name(auth_token: str, cloud_name: str) -> None:
await CLOUD_NAME_CACHE.async_set(auth_token, cloud_name)
async def async_get_jira_client_semaphore(auth_token: str) -> asyncio.Semaphore | None:
return await CLIENT_SEMAPHORE_CACHE.async_get(auth_token)
async def async_set_jira_client_semaphore(auth_token: str, semaphore: asyncio.Semaphore) -> None:
await CLIENT_SEMAPHORE_CACHE.async_set(auth_token, semaphore)