Skip to content

Commit

Permalink
feat: rate limit error chaching (anuraghazra#2448)
Browse files Browse the repository at this point in the history
* feat: rate limit error chaching

Rate limit error caching to alleviate PATs.

* refactor: improve code comments
  • Loading branch information
rickstaa authored and qwerty541 committed Dec 22, 2023
1 parent 8016acd commit 7441fda
Show file tree
Hide file tree
Showing 9 changed files with 54 additions and 18 deletions.
7 changes: 6 additions & 1 deletion api/gist.js
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,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}`,
); // Use lower cache period for errors.
return res.send(renderError(err.message, err.secondaryMessage));
}
};
9 changes: 7 additions & 2 deletions api/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ export default async (req, res) => {
);

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 @@ -100,7 +100,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}`,
); // Use lower cache period for errors.
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 @@ -83,7 +83,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}`,
); // Use lower cache period for errors.
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 @@ -63,7 +63,7 @@ export default async (req, res) => {
);

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 @@ -99,7 +99,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}`,
); // Use lower cache period for errors.
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 @@ -42,18 +42,14 @@ export default async (req, res) => {
const stats = await fetchWakatimeStats({ username, api_domain });

let cacheSeconds = clampValue(
parseInt(cache_seconds || CONSTANTS.FOUR_HOURS, 10),
parseInt(cache_seconds || CONSTANTS.CARD_CACHE_SECONDS, 10),
CONSTANTS.FOUR_HOURS,
CONSTANTS.ONE_DAY,
);
cacheSeconds = process.env.CACHE_SECONDS
? parseInt(process.env.CACHE_SECONDS, 10) || cacheSeconds
: cacheSeconds;

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

res.setHeader(
"Cache-Control",
`max-age=${
Expand Down Expand Up @@ -82,7 +78,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}`,
); // Use lower cache period for errors.
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 @@ -72,5 +72,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 @@ -373,11 +373,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,
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";
import { expect, it, describe } from "@jest/globals";

Expand Down Expand Up @@ -44,7 +44,7 @@ describe("Test Retryer", () => {
try {
await retryer(fetcherFail, {});
} catch (err) {
expect(fetcherFail).toBeCalledTimes(8);
expect(fetcherFail).toBeCalledTimes(RETRIES + 1);
expect(err.message).toBe("Downtime due to GitHub API rate limiting");
}
});
Expand Down

0 comments on commit 7441fda

Please sign in to comment.