mirror of
https://github.com/HabitRPG/habitica.git
synced 2026-03-13 08:41:14 +08:00
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:
@@ -122,7 +122,7 @@ export default defineConfig({
|
||||
},
|
||||
rollupOptions: {
|
||||
output: {
|
||||
experimentalMinChunkSize: 1000
|
||||
experimentalMinChunkSize: 20000
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
39
website/server/controllers/api-v4/status.js
Normal file
39
website/server/controllers/api-v4/status.js
Normal 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;
|
||||
@@ -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'));
|
||||
}
|
||||
|
||||
@@ -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.');
|
||||
});
|
||||
|
||||
7
website/server/libs/serverStatus.js
Normal file
7
website/server/libs/serverStatus.js
Normal file
@@ -0,0 +1,7 @@
|
||||
const SERVER_STATUS = {
|
||||
MONGODB: false,
|
||||
REDIS: false,
|
||||
EXPRESS: false,
|
||||
};
|
||||
|
||||
export default SERVER_STATUS;
|
||||
@@ -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) {
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user