Skip to content

Commit

Permalink
feat(mongoose): force secondaryPreferred if no secondaries in cluster
Browse files Browse the repository at this point in the history
- Trigger early load of mongoose before any other import and implicit
  load of mongoose model
- Add a global schema plugin that inspects schema for read preference
  - if `secondary`, check the cluster using mongoose connection
  - if cluster without secondaries, force read pref to be
    `secondaryPreferred`
  • Loading branch information
LoneRifle committed Sep 13, 2024
1 parent cf52953 commit 5982b64
Show file tree
Hide file tree
Showing 2 changed files with 59 additions and 24 deletions.
56 changes: 32 additions & 24 deletions replacements/src/app/loaders/mongoose.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,38 @@ import mongoose, { Connection } from 'mongoose'
import config from '../config/config'
import { createLoggerWithLabel } from '../config/logger'

// Invoke this at module load time
mongoose.plugin((schema) => {
if (schema.get('read') === 'secondary') {
// Inspect cluster topology from client
const client = mongoose.connection.getClient()
const {
description: { servers, type },
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
} = client.topology

if (
type === 'ReplicaSetWithPrimary' &&
!servers
.values()
.some(({ type }: { type: string }) => type === 'RSSecondary')
) {
// There are no secondary nodes in ReplicaSetWithPrimary cluster.
// Queries with `secondary` read preferences will fail, so rewrite
// those to be `secondaryPreferred`.
logger.warn({
message: 'Forcing secondary read preference to secondaryPreferred.',
meta: {
action: 'schema',
},
})

schema.set('read', 'secondaryPreferred')
}
}
})

const logger = createLoggerWithLabel(module)

export default async (): Promise<Connection> => {
Expand Down Expand Up @@ -90,30 +122,6 @@ export default async (): Promise<Connection> => {
})
})

// Seed db with initial agency if we have none
if (
process.env.INIT_AGENCY_DOMAIN &&
process.env.INIT_AGENCY_SHORTNAME &&
process.env.INIT_AGENCY_FULLNAME
) {
const Agency = mongoose.model('Agency')
const agencyCount = await Agency.count()
if (agencyCount === 0) {
await mongoose.connection.collection(Agency.collection.name).updateOne(
{ shortName: process.env.INIT_AGENCY_SHORTNAME },
{
$setOnInsert: {
shortName: process.env.INIT_AGENCY_SHORTNAME,
fullName: process.env.INIT_AGENCY_FULLNAME,
emailDomain: [process.env.INIT_AGENCY_DOMAIN],
logo: '/logo192.png',
},
},
{ upsert: true },
)
}
}

// Seed the db with govtech agency if using the mocked db
if (usingMockedDb) {
const Agency = mongoose.model('Agency')
Expand Down
27 changes: 27 additions & 0 deletions replacements/src/app/server.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import './loaders/datadog-tracer'
import './loaders/mongoose'

import config from './config/config'
import { createLoggerWithLabel } from './config/logger'
import loadApp from './loaders'

const logger = createLoggerWithLabel(module)

const initServer = async () => {
const app = await loadApp()

// Configure aws-sdk based on environment
// sdk is later used to upload images to S3
await config.configureAws()

app.listen(config.port)

logger.info({
message: `[${config.nodeEnv}] Connected to port ${config.port}`,
meta: {
action: 'initServer',
},
})
}

void initServer()

0 comments on commit 5982b64

Please sign in to comment.