|
15 | 15 |
|
16 | 16 | class FeedService |
17 | 17 | { |
| 18 | + private const int ERROR_CACHE_TTL_SECONDS = 30; |
| 19 | + |
18 | 20 | public function __construct( |
19 | 21 | private readonly iterable $feedTypes, |
20 | 22 | private readonly CacheInterface $feedsCache, |
@@ -103,13 +105,25 @@ public function getData(Feed $feed): ?array |
103 | 105 | /** @var FeedTypeInterface $feedType */ |
104 | 106 | foreach ($this->feedTypes as $feedType) { |
105 | 107 | if ($feedType::class === $feedTypeClassName) { |
106 | | - return $this->feedsCache->get($feedId, function (ItemInterface $item) use ($feed, $feedType, $feedConfiguration) { |
107 | | - if (isset($feedConfiguration['cache_expire'])) { |
108 | | - $item->expiresAfter($feedConfiguration['cache_expire']); |
109 | | - } |
110 | | - |
111 | | - return $feedType->getData($feed); |
112 | | - }); |
| 108 | + try { |
| 109 | + return $this->feedsCache->get($feedId, function (ItemInterface $item) use ($feed, $feedType, $feedConfiguration) { |
| 110 | + if (isset($feedConfiguration['cache_expire'])) { |
| 111 | + $item->expiresAfter($feedConfiguration['cache_expire']); |
| 112 | + } |
| 113 | + |
| 114 | + return $feedType->getData($feed); |
| 115 | + }); |
| 116 | + } catch (\Throwable) { |
| 117 | + // Cache empty result with a short TTL to prevent stampeding |
| 118 | + // the failing service with repeated requests. |
| 119 | + $this->feedsCache->delete($feedId); |
| 120 | + |
| 121 | + return $this->feedsCache->get($feedId, function (ItemInterface $item) { |
| 122 | + $item->expiresAfter(self::ERROR_CACHE_TTL_SECONDS); |
| 123 | + |
| 124 | + return []; |
| 125 | + }); |
| 126 | + } |
113 | 127 | } |
114 | 128 | } |
115 | 129 |
|
|
0 commit comments