Card 08/ 09
Practice: A Caching Proxy for an Exchange Rate Service
The service is slow; the proxy keeps each rate for a minute.
A slow rate service is a natural Proxy, and this exercise asks you to name its kind, write it, and reason about a self-call. RemoteRateService.rate(pair) fetches a currency rate over the network. A rate may be reused for 60 seconds.
- Name the kind of proxy this is.
- Write
CachingRateServicewith the samerate(pair)method. Take the clock as a parameter so a test can control the time. - Requests arrive at 0, 10, 30 and 75 seconds. How many reach the remote service?
- If
RemoteRateServicehad arates(pairs)method that calledself.ratefor each pair, would those calls be cached?
Show a solution
from abc import ABC, abstractmethod
from typing import Callable
class RateService(ABC):
@abstractmethod
def rate(self, pair: str) -> float: ...
class RemoteRateService(RateService):
def __init__(self) -> None:
self.calls = 0
def rate(self, pair: str) -> float:
self.calls += 1
print(f"remote: fetching {pair}")
return {"GBP/EUR": 1.17, "GBP/USD": 1.27}[pair]
class CachingRateService(RateService):
def __init__(self, service: RateService, clock: Callable[[], float], ttl_seconds: float) -> None:
self._service = service
self._clock = clock
self._ttl = ttl_seconds
self._cache: dict[str, tuple[float, float]] = {}
def rate(self, pair: str) -> float:
now = self._clock()
cached = self._cache.get(pair)
if cached is not None and now - cached[1] < self._ttl:
return cached[0]
value = self._service.rate(pair)
self._cache[pair] = (value, now)
return value
times = iter([0, 10, 30, 75])
remote = RemoteRateService()
rates = CachingRateService(remote, clock=lambda: next(times), ttl_seconds=60)
for _ in range(4):
print(rates.rate("GBP/EUR"))
print(f"remote calls: {remote.calls}")remote: fetching GBP/EUR
1.17
1.17
1.17
remote: fetching GBP/EUR
1.17
remote calls: 2It is a caching proxy. The requests at 10 and 30 seconds are under a minute after the fetch at 0, so they come from the cache. At 75 seconds the cached rate is 75 seconds old, so the proxy fetches again: two remote calls, as the output shows. Taking the clock as a parameter is what makes the answer exact rather than dependent on how fast the test runs.
The calls inside rates would not be cached. They are self-calls on the remote service and never pass through the proxy, so each would fetch. To cache them, give the proxy its own rates method that calls its own rate.
← → move through sections and cards