feat: use redis to fix state sync issues

This commit is contained in:
shiftinv 2022-09-06 21:21:40 +02:00
parent 188aadf638
commit 6027c8271d
6 changed files with 40 additions and 11 deletions

View File

@ -1,3 +1,4 @@
SIGN_KEY=
DEBUG=0
MAIN_REDIRECT=https://github.com/shiftinv/github-webhook-filter
REDIS_URL=

View File

@ -1,4 +1,4 @@
export * as http from "https://deno.land/std@0.154.0/http/mod.ts";
export * as log from "https://deno.land/std@0.154.0/log/mod.ts";
export * as hex from "https://deno.land/std@0.154.0/encoding/hex.ts";
export { default as TTL } from "https://deno.land/x/ttl@1.0.1/mod.ts";
export * as redis from "https://deno.land/x/redis@v0.27.0/mod.ts";

View File

@ -17,6 +17,7 @@ export default {
maxWebhookRetries: parseInt(get("MAX_RETRIES", "3")),
maxWebhookRetryMs: parseInt(get("MAX_RETRY_MS", "30000")),
mainRedirect: get("MAIN_REDIRECT", null),
redisUrl: get("REDIS_URL"),
// set by deno deploy
deployId: get("DENO_DEPLOYMENT_ID", "<unknown>"),

View File

@ -1,10 +1,12 @@
import { TTL } from "../deps.ts";
import { reviewCommentManager } from "./manager.ts";
import { UrlConfig } from "./types.d.ts";
import { requestLog } from "./util.ts";
const reviewComments = new TTL<number>(2 * 1000);
export default function filter(headers: Headers, json: any, config: UrlConfig): string | null {
export default async function filter(
headers: Headers,
json: any,
config: UrlConfig,
): Promise<string | null> {
const reqLog = requestLog(headers);
const event = headers.get("x-github-event") || "unknown";
const login: string | undefined = json.sender?.login?.toLowerCase();
@ -47,14 +49,11 @@ export default function filter(headers: Headers, json: any, config: UrlConfig):
if (config.commentBurstLimit && reviewId) {
const cacheKey = `${reviewId}-${login}`;
reqLog.debug(`filter: checking cache key ${cacheKey}`);
reqLog.debug(
`filter: full comment cache ${JSON.stringify(Array.from(reviewComments))}`,
);
const curr = reviewComments.get(cacheKey);
const curr = await reviewCommentManager.getAndIncrement(cacheKey);
reqLog.debug(`filter: current value: ${curr}`);
if (curr && curr >= config.commentBurstLimit) {
return `exceeded comment burst limit (${config.commentBurstLimit}) for review ${reviewId}`;
}
reviewComments.set(cacheKey, (curr ?? 0) + 1);
}
}

View File

@ -40,7 +40,7 @@ export default async function handle(
const json = JSON.parse(data);
// do the thing
const filterReason = filterWebhook(req.headers, json, urlConfig);
const filterReason = await filterWebhook(req.headers, json, urlConfig);
if (filterReason !== null) {
const reqLog = requestLog(req.headers);
reqLog.debug(`handler: ignored due to '${filterReason}'`);

28
lib/manager.ts Normal file
View File

@ -0,0 +1,28 @@
import { redis } from "../deps.ts";
import config from "./config.ts";
class ReviewCommentManager {
private redis: redis.Redis;
constructor() {
this.redis = redis.createLazyClient({
...redis.parseURL(config.redisUrl),
maxRetryCount: 3,
});
}
async getAndIncrement(key: string): Promise<number> {
if (!this.redis.isConnected) {
// seems like pipelines don't quite work with the lazy client
await this.redis.ping();
}
const pl = this.redis.pipeline();
pl.incr(`reviewcomment:${key}`);
pl.expire(`reviewcomment:${key}`, 3);
const results = await pl.flush();
const newValue = results[0] as number;
return newValue - 1; // return old value
}
}
export const reviewCommentManager = new ReviewCommentManager();