mirror of
https://github.com/monkeytypegame/monkeytype.git
synced 2025-03-09 21:26:49 +08:00
Migrate redis client and configuration client from class syntax (#2802)
This commit is contained in:
parent
20ec6679a7
commit
a3fc6e9997
6 changed files with 105 additions and 115 deletions
|
@ -38,73 +38,69 @@ function mergeConfigurations(
|
|||
merge(baseConfiguration, liveConfiguration);
|
||||
}
|
||||
|
||||
class ConfigurationClient {
|
||||
static configuration: MonkeyTypes.Configuration = BASE_CONFIGURATION;
|
||||
static lastFetchTime = 0;
|
||||
static databaseConfigurationUpdated = false;
|
||||
let configuration = BASE_CONFIGURATION;
|
||||
let lastFetchTime = 0;
|
||||
let serverConfigurationUpdated = false;
|
||||
|
||||
static async getCachedConfiguration(
|
||||
attemptCacheUpdate = false
|
||||
): Promise<MonkeyTypes.Configuration> {
|
||||
if (
|
||||
attemptCacheUpdate &&
|
||||
this.lastFetchTime < Date.now() - CONFIG_UPDATE_INTERVAL
|
||||
) {
|
||||
Logger.info("Cached configuration is stale.");
|
||||
return await this.getLiveConfiguration();
|
||||
}
|
||||
return this.configuration;
|
||||
export async function getCachedConfiguration(
|
||||
attemptCacheUpdate = false
|
||||
): Promise<MonkeyTypes.Configuration> {
|
||||
if (
|
||||
attemptCacheUpdate &&
|
||||
lastFetchTime < Date.now() - CONFIG_UPDATE_INTERVAL
|
||||
) {
|
||||
Logger.info("Cached configuration is stale.");
|
||||
return await getLiveConfiguration();
|
||||
}
|
||||
|
||||
static async getLiveConfiguration(): Promise<MonkeyTypes.Configuration> {
|
||||
this.lastFetchTime = Date.now();
|
||||
|
||||
const configurationCollection = db.collection("configuration");
|
||||
|
||||
try {
|
||||
const liveConfiguration = await configurationCollection.findOne();
|
||||
|
||||
if (liveConfiguration) {
|
||||
const baseConfiguration = _.cloneDeep(BASE_CONFIGURATION);
|
||||
const liveConfigurationWithoutId = _.omit(
|
||||
liveConfiguration,
|
||||
"_id"
|
||||
) as MonkeyTypes.Configuration;
|
||||
mergeConfigurations(baseConfiguration, liveConfigurationWithoutId);
|
||||
|
||||
this.pushConfiguration(baseConfiguration);
|
||||
this.configuration = baseConfiguration;
|
||||
} else {
|
||||
await configurationCollection.insertOne(BASE_CONFIGURATION); // Seed the base configuration.
|
||||
}
|
||||
} catch (error) {
|
||||
Logger.logToDb(
|
||||
"fetch_configuration_failure",
|
||||
`Could not fetch configuration: ${error.message}`
|
||||
);
|
||||
}
|
||||
|
||||
return this.configuration;
|
||||
}
|
||||
|
||||
static async pushConfiguration(
|
||||
configuration: MonkeyTypes.Configuration
|
||||
): Promise<void> {
|
||||
if (this.databaseConfigurationUpdated) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
await db.collection("configuration").replaceOne({}, configuration);
|
||||
|
||||
this.databaseConfigurationUpdated = true;
|
||||
} catch (error) {
|
||||
Logger.logToDb(
|
||||
"push_configuration_failure",
|
||||
`Could not push configuration: ${error.message}`
|
||||
);
|
||||
}
|
||||
}
|
||||
return configuration;
|
||||
}
|
||||
|
||||
export default ConfigurationClient;
|
||||
export async function getLiveConfiguration(): Promise<MonkeyTypes.Configuration> {
|
||||
lastFetchTime = Date.now();
|
||||
|
||||
const configurationCollection = db.collection("configuration");
|
||||
|
||||
try {
|
||||
const liveConfiguration = await configurationCollection.findOne();
|
||||
|
||||
if (liveConfiguration) {
|
||||
const baseConfiguration = _.cloneDeep(BASE_CONFIGURATION);
|
||||
const liveConfigurationWithoutId = _.omit(
|
||||
liveConfiguration,
|
||||
"_id"
|
||||
) as MonkeyTypes.Configuration;
|
||||
mergeConfigurations(baseConfiguration, liveConfigurationWithoutId);
|
||||
|
||||
pushConfiguration(baseConfiguration);
|
||||
configuration = baseConfiguration;
|
||||
} else {
|
||||
await configurationCollection.insertOne(BASE_CONFIGURATION); // Seed the base configuration.
|
||||
}
|
||||
} catch (error) {
|
||||
Logger.logToDb(
|
||||
"fetch_configuration_failure",
|
||||
`Could not fetch configuration: ${error.message}`
|
||||
);
|
||||
}
|
||||
|
||||
return configuration;
|
||||
}
|
||||
|
||||
async function pushConfiguration(
|
||||
configuration: MonkeyTypes.Configuration
|
||||
): Promise<void> {
|
||||
if (serverConfigurationUpdated) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
await db.collection("configuration").replaceOne({}, configuration);
|
||||
serverConfigurationUpdated = true;
|
||||
} catch (error) {
|
||||
Logger.logToDb(
|
||||
"push_configuration_failure",
|
||||
`Could not push configuration: ${error.message}`
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,52 +1,46 @@
|
|||
import IORedis from "ioredis";
|
||||
import Logger from "../utils/logger";
|
||||
|
||||
class RedisClient {
|
||||
static connection: IORedis.Redis;
|
||||
static connected = false;
|
||||
let connection: IORedis.Redis;
|
||||
let connected = false;
|
||||
|
||||
static async connect(): Promise<void> {
|
||||
if (this.connected) {
|
||||
export async function connect(): Promise<void> {
|
||||
if (connected) {
|
||||
return;
|
||||
}
|
||||
|
||||
const { REDIS_URI, MODE } = process.env;
|
||||
|
||||
if (!REDIS_URI && MODE !== "dev") {
|
||||
if (MODE === "dev") {
|
||||
Logger.warning("No redis configuration provided. Running without redis.");
|
||||
return;
|
||||
}
|
||||
|
||||
const { REDIS_URI } = process.env;
|
||||
|
||||
if (!REDIS_URI) {
|
||||
if (process.env.MODE === "dev") {
|
||||
Logger.warning(
|
||||
"No redis configuration provided. Running without redis."
|
||||
);
|
||||
return;
|
||||
}
|
||||
throw new Error("No redis configuration provided");
|
||||
}
|
||||
|
||||
this.connection = new IORedis(REDIS_URI, {
|
||||
maxRetriesPerRequest: null, // These options are required for BullMQ
|
||||
enableReadyCheck: false,
|
||||
lazyConnect: true,
|
||||
});
|
||||
|
||||
try {
|
||||
await this.connection.connect();
|
||||
this.connected = true;
|
||||
} catch (error) {
|
||||
Logger.error(error.message);
|
||||
Logger.error(
|
||||
"Failed to connect to redis. Exiting with exit status code 1."
|
||||
);
|
||||
process.exit(1);
|
||||
}
|
||||
throw new Error("No redis configuration provided");
|
||||
}
|
||||
|
||||
static isConnected(): boolean {
|
||||
return this.connected;
|
||||
}
|
||||
connection = new IORedis(REDIS_URI, {
|
||||
maxRetriesPerRequest: null, // These options are required for BullMQ
|
||||
enableReadyCheck: false,
|
||||
lazyConnect: true,
|
||||
});
|
||||
|
||||
static getConnection(): IORedis.Redis {
|
||||
return this.connection;
|
||||
try {
|
||||
await connection.connect();
|
||||
connected = true;
|
||||
} catch (error) {
|
||||
Logger.error(error.message);
|
||||
Logger.error(
|
||||
"Failed to connect to redis. Exiting with exit status code 1."
|
||||
);
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
export default RedisClient;
|
||||
export function isConnected(): boolean {
|
||||
return connected;
|
||||
}
|
||||
|
||||
export function getConnection(): IORedis.Redis | undefined {
|
||||
return connection;
|
||||
}
|
||||
|
|
|
@ -3,7 +3,7 @@ import BotDAO from "../dao/bot";
|
|||
import George from "../tasks/george";
|
||||
import { Document, WithId } from "mongodb";
|
||||
import LeaderboardsDAO from "../dao/leaderboards";
|
||||
import ConfigurationClient from "../init/configuration";
|
||||
import { getCachedConfiguration } from "../init/configuration";
|
||||
|
||||
const CRON_SCHEDULE = "30 14/15 * * * *";
|
||||
const RECENT_AGE_MINUTES = 10;
|
||||
|
@ -50,7 +50,7 @@ async function updateLeaderboardAndNotifyChanges(
|
|||
});
|
||||
|
||||
if (newRecords.length > 0) {
|
||||
const cachedConfig = await ConfigurationClient.getCachedConfiguration();
|
||||
const cachedConfig = await getCachedConfiguration();
|
||||
|
||||
const leaderboardId = `time ${leaderboardTime} english`;
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import ConfigurationClient from "../init/configuration";
|
||||
import { getCachedConfiguration } from "../init/configuration";
|
||||
import { Response, NextFunction } from "express";
|
||||
|
||||
async function contextMiddleware(
|
||||
|
@ -6,7 +6,7 @@ async function contextMiddleware(
|
|||
_res: Response,
|
||||
next: NextFunction
|
||||
): Promise<void> {
|
||||
const configuration = await ConfigurationClient.getCachedConfiguration(true);
|
||||
const configuration = await getCachedConfiguration(true);
|
||||
|
||||
req.ctx = {
|
||||
configuration,
|
||||
|
|
|
@ -4,12 +4,12 @@ import admin, { ServiceAccount } from "firebase-admin";
|
|||
import serviceAccount from "./credentials/serviceAccountKey.json"; // eslint-disable-line require-path-exists/exists
|
||||
import db from "./init/db";
|
||||
import jobs from "./jobs";
|
||||
import ConfigurationClient from "./init/configuration";
|
||||
import { getLiveConfiguration } from "./init/configuration";
|
||||
import app from "./app";
|
||||
import { Server } from "http";
|
||||
import { version } from "./version";
|
||||
import { recordServerVersion } from "./utils/prometheus";
|
||||
import RedisClient from "./init/redis";
|
||||
import * as RedisClient from "./init/redis";
|
||||
import George from "./tasks/george";
|
||||
import Logger from "./utils/logger";
|
||||
|
||||
|
@ -28,7 +28,7 @@ async function bootServer(port: number): Promise<Server> {
|
|||
Logger.success("Firebase app initialized");
|
||||
|
||||
Logger.info("Fetching live configuration...");
|
||||
await ConfigurationClient.getLiveConfiguration();
|
||||
await getLiveConfiguration();
|
||||
Logger.success("Live configuration fetched");
|
||||
|
||||
Logger.info("Connecting to redis...");
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import { lock } from "../utils/misc";
|
||||
import type IORedis from "ioredis";
|
||||
import { Queue, QueueScheduler } from "bullmq";
|
||||
import RedisClient from "../init/redis";
|
||||
import { isConnected } from "../init/redis";
|
||||
|
||||
const QUEUE_NAME = "george-tasks";
|
||||
|
||||
|
@ -21,7 +21,7 @@ class George {
|
|||
static jobQueue: Queue;
|
||||
static jobQueueScheduler: QueueScheduler;
|
||||
|
||||
static initJobQueue(redisConnection: IORedis.Redis): void {
|
||||
static initJobQueue(redisConnection: IORedis.Redis | undefined): void {
|
||||
this.jobQueue = new Queue(QUEUE_NAME, {
|
||||
connection: redisConnection,
|
||||
defaultJobOptions: {
|
||||
|
@ -101,5 +101,5 @@ class George {
|
|||
}
|
||||
|
||||
export default lock(George, () => {
|
||||
return !RedisClient.isConnected();
|
||||
return !isConnected();
|
||||
});
|
||||
|
|
Loading…
Reference in a new issue