Skip to content

Commit

Permalink
feat(dal): Configure minimum connection pool size by env var
Browse files Browse the repository at this point in the history
Running Novu self-hosted on Mongo Atlas, we quickly hit problems with the 500 connection limit.
The DAL service configures the connection pool based on `MONGO_MAX_POOL_SIZE` for the upper limit,
and a hard-coded value based on `NODE_ENV` for the lower limit. For both our development and
production boxes, we want to run the environment in production to benefit from CORS protection and
other optimisations, so the minimum connection pool size is being set to 200. Given that the API,
worker and web processes setup a mongo connection (I believe), this equates to 3 x 200 minimum
connections and 3 x 500 maximum. This triggers alerts on Atlas and sometimes prevents our Novu
service to come up, if say we restart the API process but the workers / web are hogging all the
connections.

A minimum of 200 also means that we cannot set the `MONGO_MAX_POOL_SIZE` to be less than 200, so
to workaround this we're setting our `MONGO_MAX_POOL_SIZE` to be 200 as well to reduce the
likelihood of hitting these limits.

This issue affects all shared Mongo Atlas instances up to M5, so requiring an M10 instance to run
Novu seems overkill.

To allow us to have a more modest connection pool for our dev instances, this PR adds a
`MONGO_MIN_POOL_SIZE` so that we can adjust this for our setup to keep within the 500 limit.
We'd probably have the following settings to keep within 500 connections:

MONGO_MIN_POOL_SIZE = 50
MONGO_MAX_POOL_SIZE = 166
  • Loading branch information
dmgarland committed Sep 1, 2023
1 parent 190cfa8 commit 244ecdc
Show file tree
Hide file tree
Showing 14 changed files with 28 additions and 4 deletions.
3 changes: 3 additions & 0 deletions apps/api/src/config/env-validator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,9 @@ const validators: { [K in keyof any]: ValidatorSpec<any[K]> } = {
default: '',
}),
MONGO_URL: str(),
MONGO_MIN_POOL_SIZE: num({
default: 10,
}),
MONGO_MAX_POOL_SIZE: num({
default: 500,
}),
Expand Down
1 change: 1 addition & 0 deletions apps/api/src/types/env.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ declare namespace NodeJS {
// eslint-disable-next-line @typescript-eslint/naming-convention
export interface ProcessEnv {
MONGO_URL: string;
MONGO_MIN_POOL_SIZE: number;
MONGO_MAX_POOL_SIZE: number;
REDIS_URL: string;
SYNC_PATH: string;
Expand Down
3 changes: 3 additions & 0 deletions apps/worker/src/config/env-validator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,9 @@ const validators: { [K in keyof any]: ValidatorSpec<any[K]> } = {
}),
REDIS_DB_INDEX: num(),
MONGO_URL: str(),
MONGO_MIN_POOL_SIZE: num({
default: 10,
}),
MONGO_MAX_POOL_SIZE: num({
default: 500,
}),
Expand Down
1 change: 1 addition & 0 deletions apps/worker/src/types/env.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ declare global {
REDIS_CACHE_KEY_PREFIX?: string;
REDIS_CACHE_SERVICE_TLS?: ConnectionOptions;
MONGO_URL: string;
MONGO_MIN_POOL_SIZE: number;
MONGO_MAX_POOL_SIZE: number;
NEW_RELIC_APP_NAME: string;
NEW_RELIC_LICENSE_KEY: string;
Expand Down
1 change: 1 addition & 0 deletions docker/kubernetes/helm/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -156,6 +156,7 @@ The command removes all the Kubernetes components associated with the chart and
| `api.env.API_ROOT_URL` | Define the required env for novu | `http://novu-api:3000` |
| `api.env.DISABLE_USER_REGISTRATION` | If users should not be able to create new accounts. Possible values are: true, false | `true` |
| `api.env.FRONT_BASE_URL` | The base url on which your frontend is accessible for the user. (e.g. web.novu.co) | `http://novu-web:4200` |
| `api.env.MONGO_MIN_POOL_SIZE` | The min pool size of the MongoDB connection | `10` |
| `api.env.MONGO_MAX_POOL_SIZE` | The max pool size of the MongoDB connection | `50` |
| `api.ingress.enabled` | Enable ingress record generation for novu api | `false` |
| `api.ingress.pathType` | Ingress path type | `ImplementationSpecific` |
Expand Down
2 changes: 2 additions & 0 deletions docker/kubernetes/helm/templates/api/deployment.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,8 @@ spec:
value : {{print .Values.api.port | quote }}
- name: FRONT_BASE_URL
value : {{print .Values.api.env.FRONT_BASE_URL | quote }}
- name: MONGO_MIN_POOL_SIZE
value : {{print .Values.api.env.MONGO_MIN_POOL_SIZE | quote }}
- name: MONGO_MAX_POOL_SIZE
value : {{print .Values.api.env.MONGO_MAX_POOL_SIZE | quote }}
- name: REDIS_HOST
Expand Down
2 changes: 2 additions & 0 deletions docker/kubernetes/helm/templates/worker/deployment.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,8 @@ spec:
value : {{print .Values.api.env.NODE_ENV | quote }}
- name: PORT
value : {{ print .Values.worker.port | quote }}
- name: MONGO_MIN_POOL_SIZE
value : {{print .Values.api.env.MONGO_MIN_POOL_SIZE | quote }}
- name: MONGO_MAX_POOL_SIZE
value : {{print .Values.api.env.MONGO_MAX_POOL_SIZE | quote }}
- name: REDIS_HOST
Expand Down
2 changes: 2 additions & 0 deletions docker/kubernetes/helm/templates/ws/deployment.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,8 @@ spec:
value : {{print .Values.api.env.NODE_ENV | quote }}
- name: PORT
value : {{print .Values.ws.port | quote }}
- name: MONGO_MIN_POOL_SIZE
value : {{print .Values.api.env.MONGO_MIN_POOL_SIZE | quote }}
- name: MONGO_MAX_POOL_SIZE
value : {{print .Values.api.env.MONGO_MAX_POOL_SIZE | quote }}
- name: REDIS_HOST
Expand Down
3 changes: 3 additions & 0 deletions docker/kubernetes/helm/values.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -365,6 +365,9 @@ api:
## @param api.env.FRONT_BASE_URL The base url on which your frontend is accessible for the user. (e.g. web.novu.co)
##
FRONT_BASE_URL: http://novu-web:4200
## @param api.env.MONGO_MIN_POOL_SIZE The min pool size of the MongoDB connection
##
MONGO_MIN_POOL_SIZE: 10
## @param api.env.MONGO_MAX_POOL_SIZE The max pool size of the MongoDB connection
##
MONGO_MAX_POOL_SIZE: 50
Expand Down
1 change: 1 addition & 0 deletions docker/kubernetes/kustomize/env-dev
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ STORE_ENCRYPTION_KEY=ekwUKf9yLjGPLOz939Y1GM0nJckVoVyF
## General
NODE_ENV=dev
MONGO_URL=mongodb://novu-mongodb-dev:27017/novu-db
MONGO_MIN_POOL_SIZE=200
MONGO_MAX_POOL_SIZE=500
REDIS_HOST=novu-redis-dev
REDIS_CACHE_SERVICE_HOST=novu-redis-dev
Expand Down
1 change: 1 addition & 0 deletions docker/local/deployment/docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ services:
PORT: ${API_PORT}
FRONT_BASE_URL: ${FRONT_BASE_URL}
MONGO_URL: ${MONGO_URL}
MONGO_MIN_POOL_SIZE: ${MONGO_MIN_POOL_SIZE}
MONGO_MAX_POOL_SIZE: ${MONGO_MAX_POOL_SIZE}
REDIS_HOST: ${REDIS_HOST}
REDIS_PORT: ${REDIS_PORT}
Expand Down
9 changes: 6 additions & 3 deletions docs/docs/community/run-locally.md
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,8 @@ However, if you want to test certain parts of Novu or run it in production mode,
- `JWT_SECRET`<br />The secret keybase which is used to encrypt / verify the tokens issued for authentication
- `SENDGRID_API_KEY`<br />The api key of the Sendgrid account used to send various emails
- `MONGO_URL`<br />The URL of your MongoDB instance
- `MONGO_MAX_POOL_SIZE`<br />The max pool size of the MongoDB connection
- `MONGO_MIN_POOL_SIZE`<br />The min pool size of the MongoDB connection
- `MONGO_MAX_POOL_SIZE`<br />The max pool size of the MongoDB connection. N.B. The API, web and the worker process have their own pool, so ensure that you have sufficient connection limit on your Mongo service to handle 3 X `MONGO_MAX_POOL_SIZE` connections
- `NOVU_API_KEY`<br />The api key of web.novu.co used to send various emails
- `SENTRY_DSN`<br />The DSN of sentry.io used to report errors happening in production

Expand Down Expand Up @@ -138,7 +139,8 @@ However, if you want to test certain parts of Novu or run it in production mode,
- `REDIS_CLUSTER_FAMILY`<br />Redis cluster IP stack version
- `REDIS_CLUSTER_KEY_PREFIX`<br />Redis cluster prefix prepend to all keys
- `MONGO_URL`<br />The URL of your MongoDB instance
- `MONGO_MAX_POOL_SIZE`<br />The max pool size of the MongoDB connection
- `MONGO_MIN_POOL_SIZE`<br />The min pool size of the MongoDB connection
- `MONGO_MAX_POOL_SIZE`<br />The max pool size of the MongoDB connection. N.B. The API, web and the worker process have their own pool, so ensure that you have sufficient connection limit on your Mongo service to handle 3 X `MONGO_MAX_POOL_SIZE` connections
- `NEW_RELIC_APP_NAME`<br />The New Relic app name
- `NEW_RELIC_LICENSE_KEY`<br />The New Relic license key
- `SEGMENT_TOKEN`<br />The Segment Analytics token
Expand Down Expand Up @@ -169,7 +171,8 @@ This will generate a file called `env-config.js` that will be copied inside of t
- `REDIS_PASSWORD`<br />Optional password of your redis instance
- `JWT_SECRET`<br />The secret keybase which is used to encrypt / verify the tokens issued for authentication
- `MONGO_URL`<br />The URL of your MongoDB instance
- `MONGO_MAX_POOL_SIZE`<br />The max pool size of the MongoDB connection
- `MONGO_MIN_POOL_SIZE`<br />The min pool size of the MongoDB connection
- `MONGO_MAX_POOL_SIZE`<br />The max pool size of the MongoDB connection. N.B. The API, web and the worker process have their own pool, so ensure that you have sufficient connection limit on your Mongo service to handle 3 X `MONGO_MAX_POOL_SIZE` connections
- `PORT`<br />The port on which the WebSocket service should listen on

</FAQItem>
Expand Down
2 changes: 1 addition & 1 deletion libs/dal/src/dal.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ export class DalService {
async connect(url: string, config: ConnectOptions = {}) {
const baseConfig: ConnectOptions = {
maxPoolSize: process.env.MONGO_MAX_POOL_SIZE || 500,
minPoolSize: process.env.NODE_ENV === 'production' ? 200 : 10,
minPoolSize: process.env.MONGO_MIN_POOL_SIZE || 10,
autoIndex: process.env.AUTO_CREATE_INDEXES === 'true',
};

Expand Down
1 change: 1 addition & 0 deletions libs/dal/src/types/env.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ declare namespace NodeJS {
REDIS_URL: string;
REDIS_ARENA_PORT: string;
NODE_ENV: 'test' | 'production' | 'dev';
MONGO_MIN_POOL_SIZE: number;
MONGO_MAX_POOL_SIZE: number;
}
}

0 comments on commit 244ecdc

Please sign in to comment.