Skip to content

Commit

Permalink
feat: rate limit error chaching
Browse files Browse the repository at this point in the history
Rate limit error caching to alleviate PATs.
  • Loading branch information
rickstaa committed Jan 21, 2023
1 parent c1dc7b8 commit fb594d2
Show file tree
Hide file tree
Showing 8 changed files with 48 additions and 17 deletions.
9 changes: 7 additions & 2 deletions api/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ export default async (req, res) => {
);

const cacheSeconds = clampValue(
parseInt(cache_seconds || CONSTANTS.FOUR_HOURS, 10),
parseInt(cache_seconds || CONSTANTS.CARD_CACHE_SECONDS, 10),
CONSTANTS.FOUR_HOURS,
CONSTANTS.ONE_DAY,
);
Expand Down Expand Up @@ -93,7 +93,12 @@ export default async (req, res) => {
}),
);
} catch (err) {
res.setHeader("Cache-Control", `no-cache, no-store, must-revalidate`); // Don't cache error responses.
res.setHeader(
"Cache-Control",
`max-age=${CONSTANTS.ERROR_CACHE_SECONDS / 2}, s-maxage=${
CONSTANTS.ERROR_CACHE_SECONDS
}, stale-while-revalidate=${CONSTANTS.ONE_DAY}`,
); // Cache the error response less frequently.
return res.send(renderError(err.message, err.secondaryMessage));
}
};
9 changes: 7 additions & 2 deletions api/pin.js
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ export default async (req, res) => {
const repoData = await fetchRepo(username, repo);

let cacheSeconds = clampValue(
parseInt(cache_seconds || CONSTANTS.FOUR_HOURS, 10),
parseInt(cache_seconds || CONSTANTS.CARD_CACHE_SECONDS, 10),
CONSTANTS.FOUR_HOURS,
CONSTANTS.ONE_DAY,
);
Expand Down Expand Up @@ -80,7 +80,12 @@ export default async (req, res) => {
}),
);
} catch (err) {
res.setHeader("Cache-Control", `no-cache, no-store, must-revalidate`); // Don't cache error responses.
res.setHeader(
"Cache-Control",
`max-age=${CONSTANTS.ERROR_CACHE_SECONDS / 2}, s-maxage=${
CONSTANTS.ERROR_CACHE_SECONDS
}, stale-while-revalidate=${CONSTANTS.ONE_DAY}`,
); // Cache the error response less frequently.
return res.send(renderError(err.message, err.secondaryMessage));
}
};
9 changes: 7 additions & 2 deletions api/top-langs.js
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ export default async (req, res) => {
);

const cacheSeconds = clampValue(
parseInt(cache_seconds || CONSTANTS.FOUR_HOURS, 10),
parseInt(cache_seconds || CONSTANTS.CARD_CACHE_SECONDS, 10),
CONSTANTS.FOUR_HOURS,
CONSTANTS.ONE_DAY,
);
Expand Down Expand Up @@ -80,7 +80,12 @@ export default async (req, res) => {
}),
);
} catch (err) {
res.setHeader("Cache-Control", `no-cache, no-store, must-revalidate`); // Don't cache error responses.
res.setHeader(
"Cache-Control",
`max-age=${CONSTANTS.ERROR_CACHE_SECONDS / 2}, s-maxage=${
CONSTANTS.ERROR_CACHE_SECONDS
}, stale-while-revalidate=${CONSTANTS.ONE_DAY}`,
); // Cache the error response less frequently.
return res.send(renderError(err.message, err.secondaryMessage));
}
};
13 changes: 7 additions & 6 deletions api/wakatime.js
Original file line number Diff line number Diff line change
Expand Up @@ -43,15 +43,11 @@ export default async (req, res) => {
const stats = await fetchWakatimeStats({ username, api_domain, range });

let cacheSeconds = clampValue(
parseInt(cache_seconds || CONSTANTS.FOUR_HOURS, 10),
parseInt(cache_seconds || CONSTANTS.CARD_CACHE_SECONDS, 10),
CONSTANTS.FOUR_HOURS,
CONSTANTS.ONE_DAY,
);

if (!cache_seconds) {
cacheSeconds = CONSTANTS.FOUR_HOURS;
}

res.setHeader(
"Cache-Control",
`max-age=${
Expand Down Expand Up @@ -80,7 +76,12 @@ export default async (req, res) => {
}),
);
} catch (err) {
res.setHeader("Cache-Control", `no-cache, no-store, must-revalidate`); // Don't cache error responses.
res.setHeader(
"Cache-Control",
`max-age=${CONSTANTS.ERROR_CACHE_SECONDS / 2}, s-maxage=${
CONSTANTS.ERROR_CACHE_SECONDS
}, stale-while-revalidate=${CONSTANTS.ONE_DAY}`,
); // Cache the error response less frequently.
return res.send(renderError(err.message, err.secondaryMessage));
}
};
2 changes: 1 addition & 1 deletion src/common/retryer.js
Original file line number Diff line number Diff line change
Expand Up @@ -60,5 +60,5 @@ const retryer = async (fetcher, variables, retries = 0) => {
}
};

export { retryer };
export { retryer, RETRIES };
export default retryer;
10 changes: 10 additions & 0 deletions src/common/utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -293,11 +293,21 @@ const noop = () => {};
const logger =
process.env.NODE_ENV !== "test" ? console : { log: noop, error: noop };

// Cache settings.
const CARD_CACHE_SECONDS = 14400;
const ERROR_CACHE_SECONDS = 600;

const CONSTANTS = {
ONE_MINUTE: 60,
FIVE_MINUTES: 300,
TEN_MINUTES: 600,
FIFTEEN_MINUTES: 900,
THIRTY_MINUTES: 1800,
TWO_HOURS: 7200,
FOUR_HOURS: 14400,
ONE_DAY: 86400,
CARD_CACHE_SECONDS: CARD_CACHE_SECONDS,
ERROR_CACHE_SECONDS: ERROR_CACHE_SECONDS,
};

const SECONDARY_ERROR_MESSAGES = {
Expand Down
9 changes: 7 additions & 2 deletions tests/api.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -184,13 +184,18 @@ describe("Test /api/", () => {
]);
});

it("should not store cache when error", async () => {
it("should set shorter cache when error", async () => {
const { req, res } = faker({}, error);
await api(req, res);

expect(res.setHeader.mock.calls).toEqual([
["Content-Type", "image/svg+xml"],
["Cache-Control", `no-cache, no-store, must-revalidate`],
[
"Cache-Control",
`max-age=${CONSTANTS.ERROR_CACHE_SECONDS / 2}, s-maxage=${
CONSTANTS.ERROR_CACHE_SECONDS
}, stale-while-revalidate=${CONSTANTS.ONE_DAY}`,
],
]);
});

Expand Down
4 changes: 2 additions & 2 deletions tests/retryer.test.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { jest } from "@jest/globals";
import "@testing-library/jest-dom";
import { retryer } from "../src/common/retryer.js";
import { retryer, RETRIES } from "../src/common/retryer.js";
import { logger } from "../src/common/utils.js";

const fetcher = jest.fn((variables, token) => {
Expand Down Expand Up @@ -45,7 +45,7 @@ describe("Test Retryer", () => {
try {
res = await retryer(fetcherFail, {});
} catch (err) {
expect(fetcherFail).toBeCalledTimes(8);
expect(fetcherFail).toBeCalledTimes(RETRIES + 1);
expect(err.message).toBe("Maximum retries exceeded");
}
});
Expand Down

0 comments on commit fb594d2

Please sign in to comment.