From fd3497a0b2658f75c25f1456775e087c68ed28d3 Mon Sep 17 00:00:00 2001 From: darwin Date: Wed, 15 Jan 2025 12:31:03 -0500 Subject: [PATCH] modifying lambda to only initialize redisclient once --- .../fdr-deploy/getdocs-lambda/src/index.ts | 230 ++++++++++-------- .../fdr-deploy/scripts/fdr-deploy-stack.ts | 2 +- 2 files changed, 135 insertions(+), 97 deletions(-) diff --git a/servers/fdr-deploy/getdocs-lambda/src/index.ts b/servers/fdr-deploy/getdocs-lambda/src/index.ts index 817776c5d1..f25496c3e2 100644 --- a/servers/fdr-deploy/getdocs-lambda/src/index.ts +++ b/servers/fdr-deploy/getdocs-lambda/src/index.ts @@ -6,19 +6,58 @@ import { createClient } from "redis"; import { ParsedBaseUrl } from "./ParsedBaseUrl"; import { DocumentData, GetDocsEvent } from "./types"; -// copied from https://github.com/fern-api/fern-platform/blob/main/servers/fdr/src/services/auth/AuthService.ts#L96 -function getVenusClient({ token }: { token?: string }): FernVenusApiClient { - return new FernVenusApiClient({ - environment: process.env.VENUS_URL || "", - token, - }); +let redisClient: ReturnType | null = null; +let isConnecting = false; + +async function getRedisClient() { + // If we're already connected, return existing client + if (redisClient?.isOpen) { + return redisClient; + } + + // If we're in the process of connecting, wait + while (isConnecting) { + await new Promise((resolve) => setTimeout(resolve, 50)); + } + + try { + isConnecting = true; + redisClient = createClient({ + url: `redis://${process.env.REDIS_ENDPOINT}`, + }); + + // Handle disconnections + redisClient.on("error", (err) => { + console.error("Redis Client Error:", err); + redisClient = null; + }); + + await redisClient.connect(); + console.log("Connected to Redis"); + return redisClient; + } finally { + isConnecting = false; + } } +// Initialize Venus client without token - token will be added per-request +const baseVenusClient = new FernVenusApiClient({ + environment: process.env.VENUS_URL || "", +}); + const BEARER_REGEX = /^bearer\s+/i; export function getTokenFromAuthHeader(authHeader: string) { return authHeader.replace(BEARER_REGEX, ""); } +function getVenusClient({ token }: { token?: string }): FernVenusApiClient { + if (!token) return baseVenusClient; + return new FernVenusApiClient({ + environment: process.env.VENUS_URL || "", + token, + }); +} + async function checkUserBelongsToOrg({ authHeader, orgId, @@ -68,111 +107,110 @@ export const handler: APIGatewayProxyHandler = async (event) => { }; } - // first - try to get from redis cache - const redis = createClient({ - url: `redis://${process.env.REDIS_ENDPOINT}`, - }); - - await redis.connect(); - - // TODO: this is just for debugging - // dummy commit - const keys = await redis.keys("*"); - console.log("All Redis keys:", keys); - - const cachedResponse = await redis.get(parsedUrl.getFullUrl()); - if (cachedResponse != null) { - console.log(`Cache HIT for ${url}`); - console.log(cachedResponse); - const parsedResponse = JSON.parse(cachedResponse); - - const filesV2 = Object.fromEntries( - await Promise.all( - Object.entries(parsedResponse.dbFiles).map( - // eslint-disable-next-line @typescript-eslint/no-explicit-any - async ([fileId, dbFileInfo]: [string, any]) => { - // porting over code from s3service - // going to assume that it uses public s3 instead of private - const presignedUrl = `${process.env.PUBLIC_DOCS_CDN_URL}/${dbFileInfo.s3Key}`; - - switch (dbFileInfo.type) { - case "image": { - const { s3Key, ...image } = dbFileInfo; - return [fileId, { ...image, url: presignedUrl }]; + // const keys = await redisClient.keys("*"); + // console.log("All Redis keys:", keys); + try { + const redis = await getRedisClient(); + const cachedResponse = await redis.get(parsedUrl.getFullUrl()); + if (cachedResponse != null) { + console.log(`Cache HIT for ${url}`); + console.log(cachedResponse); + const parsedResponse = JSON.parse(cachedResponse); + + const filesV2 = Object.fromEntries( + await Promise.all( + Object.entries(parsedResponse.dbFiles).map( + // eslint-disable-next-line @typescript-eslint/no-explicit-any + async ([fileId, dbFileInfo]: [string, any]) => { + // porting over code from s3service + // going to assume that it uses public s3 instead of private + const presignedUrl = `${process.env.PUBLIC_DOCS_CDN_URL}/${dbFileInfo.s3Key}`; + + switch (dbFileInfo.type) { + case "image": { + const { s3Key, ...image } = dbFileInfo; + return [fileId, { ...image, url: presignedUrl }]; + } + default: + return [fileId, { type: "url", url: presignedUrl }]; } - default: - return [fileId, { type: "url", url: presignedUrl }]; } - } + ) ) - ) - ); - return { - statusCode: 200, - body: JSON.stringify({ - ...parsedResponse.response, - definition: { - ...parsedResponse.response.definition, - filesV2, - }, - }), - }; - } else { - console.log(`Cache MISS for ${url}`); - console.log("Connecting to RDS"); - - // second - try to get from rds db - try { - client = new Client({ - host: process.env.RDS_PROXY_ENDPOINT, - port: 5432, - // The RDS Proxy will use IAM authentication, so we don't need username/password - ssl: { - rejectUnauthorized: false, - }, - }); - - await client.connect(); - console.log("Connected to RDS"); - - const query = ` + ); + return { + statusCode: 200, + body: JSON.stringify({ + ...parsedResponse.response, + definition: { + ...parsedResponse.response.definition, + filesV2, + }, + }), + }; + } else { + console.log(`Cache MISS for ${url}`); + console.log("Connecting to RDS"); + + // second - try to get from rds db + try { + client = new Client({ + host: process.env.RDS_PROXY_ENDPOINT, + port: 5432, + // The RDS Proxy will use IAM authentication, so we don't need username/password + ssl: { + rejectUnauthorized: false, + }, + }); + + await client.connect(); + console.log("Connected to RDS"); + + const query = ` SELECT url, docsDefinition FROM Docs WHERE url = $1 `; - const result = await client.query(query, [parsedUrl]); - console.log("result", result); + const result = await client.query(query, [parsedUrl]); + console.log("result", result); + + if (result.rows.length === 0) { + return { + statusCode: 404, + body: JSON.stringify({ error: "Document not found" }), + }; + } - if (result.rows.length === 0) { + console.log("found result"); + console.log(result.rows[0]); return { - statusCode: 404, - body: JSON.stringify({ error: "Document not found" }), + statusCode: 200, + body: JSON.stringify(result.rows[0]), }; - } - - console.log("found result"); - console.log(result.rows[0]); - return { - statusCode: 200, - body: JSON.stringify(result.rows[0]), - }; - } catch (error) { - console.error("Error:", error); - return { - statusCode: 500, - body: JSON.stringify({ - error: "Internal server error", - message: error instanceof Error ? error.message : "Unknown error", - }), - }; - } finally { - if (client) { - await client.end().catch(console.error); + } catch (error) { + console.error("Error:", error); + return { + statusCode: 500, + body: JSON.stringify({ + error: "Internal server error", + message: error instanceof Error ? error.message : "Unknown error", + }), + }; + } finally { + if (client) { + await client.end().catch(console.error); + } } } + } catch (error) { + console.error("Error:", error); } } else { throw new Error("Authorization header was not specified"); } + return { + statusCode: 400, + body: JSON.stringify({ error: "bad request" }), + }; }; diff --git a/servers/fdr-deploy/scripts/fdr-deploy-stack.ts b/servers/fdr-deploy/scripts/fdr-deploy-stack.ts index 9dd774652e..73d5bed76c 100644 --- a/servers/fdr-deploy/scripts/fdr-deploy-stack.ts +++ b/servers/fdr-deploy/scripts/fdr-deploy-stack.ts @@ -279,7 +279,7 @@ export class FdrDeployStack extends Stack { { aliasName: `fern-${environmentType.toLowerCase()}-getdocs-lambda-alias`, version: getDocsLambda.lambdaFunction.currentVersion, - provisionedConcurrentExecutions: 1000, + provisionedConcurrentExecutions: 900, } );