Cache-aside кэширование в getByIds
Добавьте кэширование в ItemService::getByIds. Cache и Client внедрены. При вызове отдавайте то, что уже в кэше, и запрашивайте только недостающие id, затем сохраняйте свежие элементы, чтобы следующий вызов попал в кэш.
<?php
class ItemService {
public function __construct(
private Client $client,
private Cache $cache,
) {}
public function getByIds(array $ids): array {
// ваш код здесь
}
}
Допишите реализацию.
Примените паттерн cache-aside (read-through). Прочитайте кэш по $ids, вычислите промахи через array_diff($ids, array_keys($cached)), выйдите рано, если их нет, иначе запросите недостающие id у клиента, запишите их обратно в кэш и верните array_merge($cached, $fetched). Внедрённые Cache и Client делают метод тестируемым.
- ✗Diff id со значениями кэша, а не с
array_keys($cached), из-за чего попадания не исключаются - ✗Перезапись кэша только свежими элементами, теряя ранее закэшированные записи
- ✗Запрос всего списка id при каждом вызове, что сводит на нет смысл кэша
- →Как фиксировать метрики hit/miss кэша, не меняя возвращаемое значение метода?
- →Что меняется, если кэш возвращает частичный набор по ключам id, а не плоский список?
Паттерн cache-aside: код сам читает кэш, при промахе идёт к источнику и сам кладёт результат обратно. Ключевая деталь — недостающие id считаются по ключам кэша (array_keys($cached)), а не по значениям.
<?php
public function getByIds(array $ids): array {
$cached = $this->cache->get($ids);
$missingIds = array_diff($ids, array_keys($cached));
if (empty($missingIds)) {
return $cached;
}
$fetched = $this->client->get($missingIds);
$this->cache->set($fetched);
return array_merge($cached, $fetched);
}
Cache и Client приходят через конструктор (DI), поэтому в тесте их легко заменить моками и проверить, что клиент вызывается только для промахов. Метрики hit/miss удобно снимать на размере $cached против $missingIds.