Phillip/prod perf2 (#15596)

* Add new api call for kubernetes startup probe

* add hostname as tag for loggly

* Only listen to one change

* increase vite min chunk size

* respond gracefully to shutdown signal

* update server readiness according to mongodb and redis connection

* make larger vite chunks

* fix lint
This commit is contained in:
Phillip Thelen
2026-02-24 17:17:21 +01:00
committed by GitHub
parent 4ea8636f03
commit 3e93911e70
8 changed files with 82 additions and 4 deletions

View File

@@ -122,7 +122,7 @@ export default defineConfig({
},
rollupOptions: {
output: {
experimentalMinChunkSize: 1000
experimentalMinChunkSize: 20000
}
}
},

View File

@@ -0,0 +1,39 @@
import {
disableCache,
} from '../../middlewares/cache';
import SERVER_STATUS from '../../libs/serverStatus';
const api = {};
/**
* @api {get} /api/v3/ready Get Habitica's Server readiness status
* @apiName GetReady
* @apiGroup Status
*
* @apiSuccess {String} data.status 'ready' if everything is ok
*
* @apiSuccessExample {JSON} Server is Ready
* {
* 'status': 'ready',
* }
*/
api.getReady = {
method: 'GET',
url: '/ready',
// explicitly disable caching so that the server is always checked
middlewares: [disableCache],
async handler (req, res) {
// This allows kubernetes to determine if the server is ready to receive traffic
if (!SERVER_STATUS.MONGODB || !SERVER_STATUS.REDIS || !SERVER_STATUS.EXPRESS) {
res.respond(503, {
status: 'not ready',
});
} else {
res.respond(200, {
status: 'ready',
});
}
},
};
export default api;

View File

@@ -3,6 +3,7 @@ import winston from 'winston';
import { Loggly } from 'winston-loggly-bulk';
import nconf from 'nconf';
import _ from 'lodash';
import os from 'os';
import {
CustomError,
} from './errors';
@@ -65,9 +66,8 @@ if (IS_PROD) {
),
}));
}
if (LOGGLY_TOKEN && LOGGLY_SUBDOMAIN) {
const tags = ['Winston-NodeJS'];
const tags = ['Winston-NodeJS', os.hostname()];
if (nconf.get('SERVER_EMOJI')) {
tags.push(nconf.get('SERVER_EMOJI'));
}

View File

@@ -5,6 +5,7 @@ import {
getDevelopmentConnectionUrl,
getDefaultConnectionOptions,
} from './mongodb';
import SERVER_STATUS from './serverStatus';
const IS_PROD = nconf.get('IS_PROD');
const MAINTENANCE_MODE = nconf.get('MAINTENANCE_MODE');
@@ -24,6 +25,13 @@ const connectionUrl = IS_PROD ? DB_URI : getDevelopmentConnectionUrl(DB_URI);
export default async function connectToMongoDB () {
// Do not connect to MongoDB when in maintenance mode
if (MAINTENANCE_MODE !== 'true') {
mongoose.connection.on('open', () => {
SERVER_STATUS.MONGODB = true;
});
mongoose.connection.on('disconnected', () => {
SERVER_STATUS.MONGODB = false;
});
return mongoose.connect(connectionUrl, mongooseOptions).then(() => {
logger.info('Connected with Mongoose.');
});

View File

@@ -0,0 +1,7 @@
const SERVER_STATUS = {
MONGODB: false,
REDIS: false,
EXPRESS: false,
};
export default SERVER_STATUS;

View File

@@ -10,6 +10,7 @@ import {
} from '../libs/errors';
import logger from '../libs/logger';
import { apiError } from '../libs/apiError';
import SERVER_STATUS from '../libs/serverStatus';
// Middleware to rate limit requests to the API
@@ -47,6 +48,14 @@ if (RATE_LIMITER_ENABLED) {
enable_offline_queue: false,
});
redisClient.on('ready', () => {
SERVER_STATUS.REDIS = true;
});
redisClient.on('reconnecting', () => {
SERVER_STATUS.REDIS = false;
});
redisClient.on('error', error => {
logger.error(error, 'Redis Error');
});
@@ -56,6 +65,8 @@ if (RATE_LIMITER_ENABLED) {
storeClient: redisClient,
});
}
} else {
SERVER_STATUS.REDIS = true;
}
function setResponseHeaders (res, rateLimiterRes) {

View File

@@ -41,7 +41,7 @@ export const logRequestData = (req, res, next) => {
export const logSlowRequests = (req, res, next) => {
req.requestStartTime = Date.now();
req.on('close', () => {
req.once('close', () => {
const requestTime = Date.now() - req.requestStartTime;
if (requestTime > SLOW_REQUEST_THRESHOLD) {
const data = buildBaseLogData(req);

View File

@@ -1,6 +1,8 @@
import nconf from 'nconf';
import express from 'express';
import http from 'http';
import mongoose from 'mongoose';
import redis from 'redis';
import logger from './libs/logger';
// Setup translations
@@ -18,12 +20,22 @@ import './libs/setupFirebase';
import './models/challenge';
import './models/group';
import './models/user';
import SERVER_STATUS from './libs/serverStatus';
connectToMongoDB();
const server = http.createServer();
const app = express();
process.on('SIGTERM', async () => {
console.log('SIGTERM signal received: closing HTTP server');
server.close(async () => {
await mongoose.disconnect();
await redis.quit();
process.exit(0);
});
});
app.set('port', nconf.get('PORT'));
attachMiddlewares(app, server);
@@ -31,6 +43,7 @@ attachMiddlewares(app, server);
server.on('request', app);
server.listen(app.get('port'), () => {
logger.info(`Express server listening on port ${app.get('port')}`);
SERVER_STATUS.EXPRESS = true;
});
export default server;