From ae10810caaebb909ffe5c51c37dc4a01184391a9 Mon Sep 17 00:00:00 2001 From: WebStreamr <210764791+webstreamr@users.noreply.github.com> Date: Mon, 23 Feb 2026 11:57:07 +0000 Subject: [PATCH] perf: cache all non-error requests for 15m, tune axios caching --- src/index.ts | 8 ++++++-- src/utils/Fetcher.ts | 11 ++++++++++- 2 files changed, 16 insertions(+), 3 deletions(-) diff --git a/src/index.ts b/src/index.ts index c3cdcc1..33faf6f 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,6 +1,6 @@ import { randomUUID } from 'node:crypto'; import axios from 'axios'; -import { setupCache } from 'axios-cache-interceptor'; +import { buildMemoryStorage, setupCache } from 'axios-cache-interceptor'; import axiosRetry from 'axios-retry'; import express, { NextFunction, Request, Response } from 'express'; // eslint-disable-next-line import/no-named-as-default @@ -39,7 +39,11 @@ process.on('unhandledRejection', (error: Error) => { logger.error(`Unhandled rejection: ${error}, cause: ${error.cause}, stack: ${error.stack}`); }); -const cachedAxios = setupCache(axios); +const cachedAxios = setupCache(axios, { + interpretHeader: true, + storage: buildMemoryStorage(false, 3 * 60 * 60 * 1000, 4096, 12 * 60 * 60 * 1000), + ttl: 15 * 60 * 1000, // 15m +}); axiosRetry(cachedAxios, { retries: 3, retryDelay: () => 333 }); const fetcher = new Fetcher(cachedAxios, logger); diff --git a/src/utils/Fetcher.ts b/src/utils/Fetcher.ts index cb02e68..ef511b4 100644 --- a/src/utils/Fetcher.ts +++ b/src/utils/Fetcher.ts @@ -54,7 +54,8 @@ export class Fetcher { private readonly DEFAULT_QUEUE_LIMIT = 50; private readonly DEFAULT_QUEUE_TIMEOUT = 10000; private readonly DEFAULT_TIMEOUTS_COUNT_THROW = 30; - private readonly TIMEOUT_CACHE_TTL = 3600000; // 1h + private readonly TIMEOUT_CACHE_TTL = 60 * 60 * 1000; // 1h + private readonly FLARESOLVERR_CACHE_TTL = 15 * 60 * 1000; // 15m private readonly MAX_WAIT_RETRY_AFTER = 10000; private readonly axios: AxiosInstance; @@ -73,6 +74,7 @@ export class Fetcher { private readonly httpStatus = new Map>(); private readonly httpStatusMutex = new Mutex(); + private readonly flareSolverrCache = new Cacheable({ primary: new Keyv({ store: new CacheableMemory({ lruSize: 1024 }) }) }); private readonly flareSolverrMutexes = new Map(); public constructor(axios: AxiosInstance, logger: winston.Logger) { @@ -244,6 +246,12 @@ export class Fetcher { throw new BlockedError(url, BlockedReason.cloudflare_challenge, response.headers); } + const cachedResponseData = await this.flareSolverrCache.get(url.href); + if (cachedResponseData) { + response.data = cachedResponseData; + return response; + } + const session = `${envGetAppId()}_${url.host}`; let mutex = this.flareSolverrMutexes.get(session); @@ -297,6 +305,7 @@ export class Fetcher { this.hostUserAgentMap.set(url.host, challengeResult.solution.userAgent); response.data = challengeResult.solution.response; + await this.flareSolverrCache.set(url.href, response.data, this.FLARESOLVERR_CACHE_TTL); return response; }