|
| 1 | +from cachetools import LFUCache |
| 2 | +from pymemcache.client.base import Client |
| 3 | + |
| 4 | +from .core import AnalysisProvider, ProductNotFoundError |
| 5 | +from .products import AnalysisProduct |
| 6 | +from .types import AnalysisType |
| 7 | + |
| 8 | + |
| 9 | +class InMemoryAnalysisCache(AnalysisProvider): |
| 10 | + """ |
| 11 | + A simple in-memory cache for tiles. |
| 12 | + """ |
| 13 | + |
| 14 | + cache: LFUCache |
| 15 | + |
| 16 | + def __init__(self, cache_size: int = 8192, internal_provider_id: str | None = None): |
| 17 | + self.cache = LFUCache(maxsize=cache_size) |
| 18 | + super().__init__(internal_provider_id=internal_provider_id) |
| 19 | + |
| 20 | + def pull(self, analysis_id: str, grants: set[str]): |
| 21 | + log = self.logger.bind(analysis_id=analysis_id) |
| 22 | + |
| 23 | + cached = self.cache.get(analysis_id, None) |
| 24 | + |
| 25 | + if cached is None: |
| 26 | + log.debug("inmemory.miss") |
| 27 | + raise ProductNotFoundError(f"Product {analysis_id} not found in cache") |
| 28 | + |
| 29 | + if cached.grant and cached.grant not in grants: |
| 30 | + log = log.bind(product_grant=cached.grant, user_grants=grants) |
| 31 | + log.debug("inmemory.proprietary_hidden") |
| 32 | + raise ProductNotFoundError(f"Product {analysis_id} not found in cache") |
| 33 | + |
| 34 | + log.debug("inmemory.pulled") |
| 35 | + return cached |
| 36 | + |
| 37 | + def push(self, product: AnalysisType): |
| 38 | + log = self.logger.bind(analysis_id=product.hash) |
| 39 | + |
| 40 | + if product.source == self.internal_provider_id: |
| 41 | + log.debug("inmemory.present") |
| 42 | + |
| 43 | + product.source = self.internal_provider_id |
| 44 | + self.cache[product.hash] = product |
| 45 | + log.debug("inmemory.pushed") |
| 46 | + |
| 47 | + |
| 48 | +class MemcachedAnalysisCache(AnalysisProvider): |
| 49 | + """ |
| 50 | + A cache that uses Memcached for storing tiles. |
| 51 | + """ |
| 52 | + |
| 53 | + client: Client |
| 54 | + |
| 55 | + def __init__(self, client: Client, internal_provider_id: str | None = None): |
| 56 | + self.client = client |
| 57 | + super().__init__( |
| 58 | + internal_provider_id=internal_provider_id or "memcached-analysis" |
| 59 | + ) |
| 60 | + |
| 61 | + def pull(self, analysis_id: str, grants: set[str]): |
| 62 | + log = self.logger.bind(analysis_id=analysis_id) |
| 63 | + |
| 64 | + res = self.client.get(analysis_id, None) |
| 65 | + |
| 66 | + if res is None: |
| 67 | + log.debug("memcached.miss") |
| 68 | + raise ProductNotFoundError(f"Product {analysis_id} not found in cache") |
| 69 | + |
| 70 | + res = AnalysisType.model_validate_json(res) |
| 71 | + |
| 72 | + if res.grant and res.grant not in grants: |
| 73 | + log = log.bind(product_grant=res.grant, user_grants=grants) |
| 74 | + log.debug("memcached.proprietary_hidden") |
| 75 | + raise ProductNotFoundError(f"Product {analysis_id} not found in cache") |
| 76 | + |
| 77 | + log.debug("memcached.pulled") |
| 78 | + |
| 79 | + return res |
| 80 | + |
| 81 | + def push(self, product: AnalysisProduct): |
| 82 | + log = self.logger.bind(analysis_id=product.hash) |
| 83 | + |
| 84 | + if product.source == self.internal_provider_id: |
| 85 | + log.debug("memcached.present") |
| 86 | + |
| 87 | + product.source = self.internal_provider_id |
| 88 | + self.client.set(product.hash, product.model_dump_json(), noreply=True) |
| 89 | + log.debug("memcached.pushed") |
0 commit comments