Skip to content

Commit

Permalink
[#PE-851] Fixed Redis ping interval and connections (#26)
Browse files Browse the repository at this point in the history
  • Loading branch information
michaeldisaro authored Dec 11, 2024
1 parent fc4c552 commit 83cacf0
Show file tree
Hide file tree
Showing 18 changed files with 103 additions and 184 deletions.
7 changes: 7 additions & 0 deletions .changeset/shiny-tools-repeat.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
"merchant-func": patch
"search-func": patch
"card-func": patch
---

updated redis integration
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,8 @@ import {
import { getConfigOrThrow } from "../utils/config";
import { cosmosdbClient } from "../utils/cosmosdb";
import { deleteCard, DeleteEycaCard, preIssueCard, PreIssueEycaCard } from "../utils/eyca";
import { QueueStorage } from "../utils/queue";
import { RedisClientFactory } from "../utils/redis";
import { deleteCardExpiration, insertCardExpiration } from "../utils/table_storage";
import { getRedisClientFactory } from "../utils/redis";
import { deleteCardExpiration } from "../utils/table_storage";
import { handler } from "./handler";

const config = getConfigOrThrow();
Expand All @@ -30,7 +29,7 @@ const deleteEycaExpiration = deleteCardExpiration(
config.EYCA_EXPIRATION_TABLE_NAME
);

const redisClientFactory = new RedisClientFactory(config);
const redisClientFactory = getRedisClientFactory(config);

const eycaClient = EycaAPIClient(config.EYCA_API_BASE_URL);

Expand Down
4 changes: 2 additions & 2 deletions apps/card-func/EycaActivation_2_ProcessPendingQueue/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import { getConfigOrThrow } from "../utils/config";
import { cosmosdbClient } from "../utils/cosmosdb";
import { preIssueCard, PreIssueEycaCard } from "../utils/eyca";
import { QueueStorage } from "../utils/queue";
import { RedisClientFactory } from "../utils/redis";
import { getRedisClientFactory } from "../utils/redis";
import { insertCardExpiration } from "../utils/table_storage";
import { handler } from "./handler";

Expand All @@ -30,7 +30,7 @@ const storeEycaExpiration = insertCardExpiration(
config.EYCA_EXPIRATION_TABLE_NAME
);

const redisClientFactory = new RedisClientFactory(config);
const redisClientFactory = getRedisClientFactory(config);

const eycaClient = EycaAPIClient(config.EYCA_API_BASE_URL);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import {
} from "../models/user_eyca_card";
import { getConfigOrThrow } from "../utils/config";
import { cosmosdbClient } from "../utils/cosmosdb";
import { RedisClientFactory } from "../utils/redis";
import { getRedisClientFactory } from "../utils/redis";
import { handler } from "./handler";
import { updateCard, UpdateCcdbEycaCard } from "../utils/eyca";
import { QueueStorage } from "../utils/queue";
Expand All @@ -21,7 +21,7 @@ const userEycaCardsContainer = cosmosdbClient

const userEycaCardModel = new UserEycaCardModel(userEycaCardsContainer);

const redisClientFactory = new RedisClientFactory(config);
const redisClientFactory = getRedisClientFactory(config);

const eycaClient = EycaAPIClient(config.EYCA_API_BASE_URL);

Expand Down
4 changes: 2 additions & 2 deletions apps/card-func/GenerateOtp/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import { setAppContext } from "@pagopa/io-functions-commons/dist/src/utils/middl
import { USER_CGN_COLLECTION_NAME, UserCgnModel } from "../models/user_cgn";
import { getConfigOrThrow } from "../utils/config";
import { cosmosdbClient } from "../utils/cosmosdb";
import { RedisClientFactory } from "../utils/redis";
import { getRedisClientFactory } from "../utils/redis";
import { GetGenerateOtp } from "./handler";

//
Expand All @@ -36,7 +36,7 @@ winston.add(contextTransport);
const app = express();
secureExpressApp(app);

const redisClientFactory = new RedisClientFactory(config);
const redisClientFactory = getRedisClientFactory(config);

// Add express route
app.post(
Expand Down
2 changes: 1 addition & 1 deletion apps/card-func/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@
"fp-ts": "^2.11.1",
"io-ts": "^2.2.16",
"node-fetch": "^2.6.1",
"redis": "^4.6.4",
"redis": "^4.6.15",
"ulid": "^2.3.0",
"winston": "^3.2.1"
},
Expand Down
1 change: 0 additions & 1 deletion apps/card-func/utils/__tests__/redis.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@ describe("RedisClientFactory", () => {
${"simple"} | ${false} | ${false}
${"simple"} | ${true} | ${false}
${"simple"} | ${false} | ${true}
${"cluster"} | ${true} | ${true}
`(
"should resolve to a $instanceType client when isProduction = $isProduction and REDIS_CLUSTER_ENABLED = $isClusterEnabled",
async ({ isProduction, isClusterEnabled }) => {
Expand Down
4 changes: 2 additions & 2 deletions apps/card-func/utils/healthcheck.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ import * as TE from "fp-ts/lib/TaskEither";

import { sequenceT } from "fp-ts/lib/Apply";
import fetch from "node-fetch";
import { RedisClientFactory } from "../utils/redis";
import { getRedisClientFactory } from "../utils/redis";
import { getConfig, IConfig } from "./config";

type ProblemSource =
Expand Down Expand Up @@ -163,7 +163,7 @@ export const checkRedisConnection = (
): HealthCheck<"RedisConnection", true> =>
pipe(
TE.tryCatch(
() => new RedisClientFactory(config).getInstance(),
() => getRedisClientFactory(config).getInstance(),
toHealthProblems("RedisConnection")
),
TE.map(_ => true)
Expand Down
76 changes: 24 additions & 52 deletions apps/card-func/utils/redis.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,25 @@
/* eslint-disable no-invalid-this */
import { identity, pipe } from "fp-ts/lib/function";
import * as O from "fp-ts/lib/Option";
import * as redis from "redis";
import { IConfig } from "./config";

const DEFAULT_REDIS_PORT = "6379";
export type RedisClient = redis.RedisClientType;

export type RedisClient = redis.RedisClientType | redis.RedisClusterType;
/**
* RedisClientFactory singleton
*/
let redisClientFactory: RedisClientFactory;

export const getRedisClientFactory = (config: IConfig) => {
if (!redisClientFactory) {
const newRedisClientFactory = new RedisClientFactory(config);
redisClientFactory = newRedisClientFactory;
}
return redisClientFactory;
};

/**
* Class that instantiate connection to Redis
*/
export class RedisClientFactory {
protected readonly config: IConfig;
// eslint-disable-next-line functional/prefer-readonly-type
Expand All @@ -19,27 +31,11 @@ export class RedisClientFactory {

public readonly getInstance = async (): Promise<RedisClient> => {
if (!this.redisClient) {
// eslint-disable-next-line functional/immutable-data
this.redisClient = await pipe(
this.config.isProduction,
O.fromPredicate(identity),
O.chainNullableK(_ => this.config.REDIS_CLUSTER_ENABLED),
O.chain(O.fromPredicate(identity)),
O.map(() =>
this.createClusterRedisClient(
this.config.REDIS_URL,
this.config.REDIS_PASSWORD,
this.config.REDIS_PORT
)
),
O.getOrElse(() =>
this.createSimpleRedisClient(
this.config.REDIS_URL,
this.config.REDIS_PASSWORD,
this.config.REDIS_PORT,
this.config.REDIS_TLS_ENABLED
)
)
this.redisClient = await this.createSimpleRedisClient(
this.config.REDIS_URL,
this.config.REDIS_PASSWORD,
this.config.REDIS_PORT,
this.config.REDIS_TLS_ENABLED
);
}
return this.redisClient;
Expand All @@ -51,44 +47,20 @@ export class RedisClientFactory {
port?: string,
useTls: boolean = true
): Promise<RedisClient> => {
const DEFAULT_REDIS_PORT = useTls ? "6380" : "6379";
const redisPort: number = parseInt(port || DEFAULT_REDIS_PORT, 10);
const redisClientConnection = redis.createClient<
redis.RedisDefaultModules,
Record<string, never>,
Record<string, never>
>({
password,
pingInterval: 1000 * 60 * 9,
socket: {
port: redisPort,
tls: useTls
},
url: `redis://${redisUrl}`
});
await redisClientConnection.connect();
return redisClientConnection;
};

protected readonly createClusterRedisClient = async (
redisUrl: string,
password?: string,
port?: string
): Promise<RedisClient> => {
const redisPort: number = parseInt(port || DEFAULT_REDIS_PORT, 10);
const redisClientConnection = redis.createCluster<
redis.RedisDefaultModules,
Record<string, never>,
Record<string, never>
>({
defaults: {
legacyMode: true,
password
},
rootNodes: [
{
url: `redis://${redisUrl}:${redisPort}`
}
],
useReplicas: true
url: useTls ? `rediss://${redisUrl}` : `redis://${redisUrl}`
});
await redisClientConnection.connect();
return redisClientConnection;
Expand Down
4 changes: 2 additions & 2 deletions apps/merchant-func/ValidateOtp/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { secureExpressApp } from "@pagopa/io-functions-commons/dist/src/utils/ex
import { AzureContextTransport } from "@pagopa/io-functions-commons/dist/src/utils/logging";
import { setAppContext } from "@pagopa/io-functions-commons/dist/src/utils/middlewares/context_middleware";
import createAzureFunctionHandler from "@pagopa/express-azure-functions/dist/src/createAzureFunctionsHandler";
import { RedisClientFactory } from "../utils/redis";
import { getRedisClientFactory } from "../utils/redis";
import { getConfigOrThrow } from "../utils/config";
import { ValidateOtp } from "./handler";

Expand All @@ -22,7 +22,7 @@ winston.add(contextTransport);
const app = express();
secureExpressApp(app);

const redisClientFactory = new RedisClientFactory(config);
const redisClientFactory = getRedisClientFactory(config);

// Add express route
app.post("/api/v1/merchant/cgn/otp/validate", ValidateOtp(redisClientFactory));
Expand Down
2 changes: 1 addition & 1 deletion apps/merchant-func/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@
"fp-ts": "^2.11.1",
"io-ts": "^2.2.16",
"node-fetch": "^2.6.1",
"redis": "^4.6.4",
"redis": "^4.6.15",
"winston": "^3.2.1"
},
"resolutions": {
Expand Down
1 change: 0 additions & 1 deletion apps/merchant-func/utils/__tests__/redis.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@ describe("RedisClientFactory", () => {
${"simple"} | ${false} | ${false}
${"simple"} | ${true} | ${false}
${"simple"} | ${false} | ${true}
${"cluster"} | ${true} | ${true}
`(
"should resolve to a $instanceType client when isProduction = $isProduction and REDIS_CLUSTER_ENABLED = $isClusterEnabled",
async ({ isProduction, isClusterEnabled }) => {
Expand Down
78 changes: 25 additions & 53 deletions apps/merchant-func/utils/redis.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,25 @@
/* eslint-disable no-invalid-this */
import { identity, pipe } from "fp-ts/lib/function";
import * as O from "fp-ts/lib/Option";
import * as redis from "redis";
import { IConfig } from "./config";

const DEFAULT_REDIS_PORT = "6379";
export type RedisClient = redis.RedisClientType;

export type RedisClient = redis.RedisClientType | redis.RedisClusterType;
/**
* RedisClientFactory singleton
*/
let redisClientFactory: RedisClientFactory;

export const getRedisClientFactory = (config: IConfig) => {
if (!redisClientFactory) {
const newRedisClientFactory = new RedisClientFactory(config);
redisClientFactory = newRedisClientFactory;
}
return redisClientFactory;
};

/**
* Class that instantiate connection to Redis
*/
export class RedisClientFactory {
protected readonly config: IConfig;
// eslint-disable-next-line functional/prefer-readonly-type
Expand All @@ -19,27 +31,11 @@ export class RedisClientFactory {

public readonly getInstance = async (): Promise<RedisClient> => {
if (!this.redisClient) {
// eslint-disable-next-line functional/immutable-data
this.redisClient = await pipe(
this.config.isProduction,
O.fromPredicate(identity),
O.chainNullableK(_ => this.config.REDIS_CLUSTER_ENABLED),
O.chain(O.fromPredicate(identity)),
O.map(() =>
this.createClusterRedisClient(
this.config.REDIS_URL,
this.config.REDIS_PASSWORD,
this.config.REDIS_PORT
)
),
O.getOrElse(() =>
this.createSimpleRedisClient(
this.config.REDIS_URL,
this.config.REDIS_PASSWORD,
this.config.REDIS_PORT,
this.config.REDIS_TLS_ENABLED
)
)
this.redisClient = await this.createSimpleRedisClient(
this.config.REDIS_URL,
this.config.REDIS_PASSWORD,
this.config.REDIS_PORT,
this.config.REDIS_TLS_ENABLED
);
}
return this.redisClient;
Expand All @@ -51,46 +47,22 @@ export class RedisClientFactory {
port?: string,
useTls: boolean = true
): Promise<RedisClient> => {
const DEFAULT_REDIS_PORT = useTls ? "6380" : "6379";
const redisPort: number = parseInt(port || DEFAULT_REDIS_PORT, 10);
const redisClientConnection = redis.createClient<
redis.RedisDefaultModules,
Record<string, never>,
Record<string, never>
>({
password,
pingInterval: 1000 * 60 * 9,
socket: {
port: redisPort,
tls: useTls
},
url: `redis://${redisUrl}`
});
await redisClientConnection.connect();
return redisClientConnection;
};

protected readonly createClusterRedisClient = async (
redisUrl: string,
password?: string,
port?: string
): Promise<RedisClient> => {
const redisPort: number = parseInt(port || DEFAULT_REDIS_PORT, 10);
const redisClientConnection = redis.createCluster<
redis.RedisDefaultModules,
Record<string, never>,
Record<string, never>
>({
defaults: {
legacyMode: true,
password
},
rootNodes: [
{
url: `redis://${redisUrl}:${redisPort}`
}
],
useReplicas: true
url: useTls ? `rediss://${redisUrl}` : `redis://${redisUrl}`
});
await redisClientConnection.connect();
return redisClientConnection;
};
}
}
4 changes: 2 additions & 2 deletions apps/search-func/GetDiscountBucketCode/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import { setAppContext } from "@pagopa/io-functions-commons/dist/src/utils/middl
import createAzureFunctionHandler from "@pagopa/express-azure-functions/dist/src/createAzureFunctionsHandler";

import { getConfigOrThrow } from "../utils/config";
import { RedisClientFactory } from "../utils/redis";
import { getRedisClientFactory } from "../utils/redis";
import { cgnOperatorDb } from "../client/sequelize";
import { GetDiscountBucketCode } from "./handler";

Expand All @@ -30,7 +30,7 @@ app.get(
"/api/v1/cgn/operator-search/discount-bucket-code/:discountId",
GetDiscountBucketCode(
cgnOperatorDb,
new RedisClientFactory(config),
getRedisClientFactory(config),
config.CGN_BUCKET_CODE_LOCK_LIMIT
)
);
Expand Down
Loading

0 comments on commit 83cacf0

Please sign in to comment.