fix(street-level): refuse tile misses during a 429 wait; parse HTTP-date Retry-After; cap a cached tile's size

Review of #974 (asturksever):

- After a 429 the other tiles of the view, and the fetches queued behind the
  first, still went to Mapillary. The proxy now answers 429 itself until the
  wait is over, scoped to the token that was refused.
- A Retry-After sent as an HTTP date read as 60 s; it is now the seconds
  left, with the same 10-minute cap.
- A body that failed to trim (up to the 48 MiB cap) could fill the 120-entry
  cache; anything over 4 MiB is served but not kept.

Co-Authored-By: Claude Fable 5.1 <[email protected]>
This commit is contained in:
German KhokhlovandClaude Fable 5.1 committed 2026-10-13 13:27:41 +04:00
1 parent c29ccd55d6
commit 361f1c0fb8
4 files changed
+184 -19

No files matched your search

+2 -7
View File
@@ -4,6 +4,7 @@ import {
fetchTile,
mapillaryToken,
parseTilePath,
retryAfterWait,
TileUpstreamError,
} from './mapillary/tiles.js';
@@ -12,8 +13,6 @@ import {
* 9-tile views a minute, more than a person flying the camera reaches.
*/
export const TILE_ROUTE_MAX_PER_MIN = 600;
/** The longest upstream Retry-After passed on, in seconds. */
const MAX_RETRY_AFTER_SEC = 600;
function sendJson(res, status, payload) {
res.statusCode = status;
@@ -69,11 +68,7 @@ async function handleTile(req, res, allow) {
keyRejected: true,
});
if (status === 429) {
const sent = Number(error.retryAfter);
const retryAfter =
Number.isInteger(sent) && sent > 0
? Math.min(sent, MAX_RETRY_AFTER_SEC)
: 60;
const retryAfter = retryAfterWait(error.retryAfter);
res.setHeader('Retry-After', String(retryAfter));
return sendJson(res, 429, {
error: 'Mapillary is rate-limiting tile requests',