Skip to content

Commit 025af94

Browse files
authored
Add cache for IPFS fetching (#105)
Signed-off-by: cyc60 <[email protected]> Signed-off-by: cyc60 <[email protected]>
1 parent d202ae1 commit 025af94

File tree

2 files changed

+50
-20
lines changed

2 files changed

+50
-20
lines changed

oracle/oracle/common/ipfs.py

Lines changed: 33 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
import ipfshttpclient
66
from aiohttp import ClientSession, ClientTimeout
77

8+
from oracle.oracle.utils import LimitedSizeDict
89
from oracle.settings import (
910
INFURA_IPFS_CLIENT_ENDPOINT,
1011
INFURA_IPFS_CLIENT_PASSWORD,
@@ -17,39 +18,51 @@
1718

1819
timeout = ClientTimeout(total=60)
1920

21+
CACHE_SIZE = 1024
22+
IPFS_CACHE = LimitedSizeDict(size_limit=CACHE_SIZE)
23+
2024

2125
@backoff.on_exception(backoff.expo, Exception, max_time=900)
2226
async def ipfs_fetch(ipfs_hash: str) -> Union[Dict[Any, Any], List[Dict[Any, Any]]]:
2327
"""Tries to fetch IPFS hash from different sources."""
2428
_ipfs_hash = ipfs_hash.replace("ipfs://", "").replace("/ipfs/", "")
2529

26-
async with ClientSession(timeout=timeout) as session:
27-
for endpoint in IPFS_FETCH_ENDPOINTS:
30+
if IPFS_CACHE.get(_ipfs_hash):
31+
return IPFS_CACHE.get(_ipfs_hash)
32+
33+
async def _fetch(_ipfs_hash):
34+
async with ClientSession(timeout=timeout) as session:
35+
for endpoint in IPFS_FETCH_ENDPOINTS:
36+
try:
37+
response = await session.get(
38+
f"{endpoint.rstrip('/')}/ipfs/{_ipfs_hash}"
39+
)
40+
response.raise_for_status()
41+
return await response.json()
42+
except BaseException as e: # noqa: E722
43+
logger.exception(e)
44+
pass
45+
46+
if LOCAL_IPFS_CLIENT_ENDPOINT:
2847
try:
29-
response = await session.get(
30-
f"{endpoint.rstrip('/')}/ipfs/{_ipfs_hash}"
31-
)
32-
response.raise_for_status()
33-
return await response.json()
34-
except BaseException as e: # noqa: E722
35-
logger.exception(e)
48+
with ipfshttpclient.connect(LOCAL_IPFS_CLIENT_ENDPOINT) as client:
49+
return client.get_json(_ipfs_hash)
50+
except ipfshttpclient.exceptions.TimeoutError:
3651
pass
3752

38-
if LOCAL_IPFS_CLIENT_ENDPOINT:
3953
try:
40-
with ipfshttpclient.connect(LOCAL_IPFS_CLIENT_ENDPOINT) as client:
54+
with ipfshttpclient.connect(
55+
INFURA_IPFS_CLIENT_ENDPOINT,
56+
username=INFURA_IPFS_CLIENT_USERNAME,
57+
password=INFURA_IPFS_CLIENT_PASSWORD,
58+
) as client:
4159
return client.get_json(_ipfs_hash)
4260
except ipfshttpclient.exceptions.TimeoutError:
4361
pass
4462

45-
try:
46-
with ipfshttpclient.connect(
47-
INFURA_IPFS_CLIENT_ENDPOINT,
48-
username=INFURA_IPFS_CLIENT_USERNAME,
49-
password=INFURA_IPFS_CLIENT_PASSWORD,
50-
) as client:
51-
return client.get_json(_ipfs_hash)
52-
except ipfshttpclient.exceptions.TimeoutError:
53-
pass
63+
data = await _fetch(_ipfs_hash)
64+
if data:
65+
IPFS_CACHE[_ipfs_hash] = data
66+
return data
5467

5568
raise RuntimeError(f"Failed to fetch IPFS data at {_ipfs_hash}")

oracle/oracle/utils.py

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import asyncio
22
import logging
3+
from collections import OrderedDict
34
from functools import wraps
45

56
logger = logging.getLogger(__name__)
@@ -25,3 +26,19 @@ def wrapper(*args, **kwargs):
2526
logger.exception(e)
2627

2728
return wrapper
29+
30+
31+
class LimitedSizeDict(OrderedDict):
32+
def __init__(self, *args, **kwds):
33+
self.size_limit = kwds.pop("size_limit", None)
34+
OrderedDict.__init__(self, *args, **kwds)
35+
self._check_size_limit()
36+
37+
def __setitem__(self, key, value):
38+
OrderedDict.__setitem__(self, key, value)
39+
self._check_size_limit()
40+
41+
def _check_size_limit(self):
42+
if self.size_limit is not None:
43+
while len(self) > self.size_limit:
44+
self.popitem(last=False)

0 commit comments

Comments
 (0)