Skip to content

Instantly share code, notes, and snippets.

@gustavopch
Last active April 18, 2019 17:25
Show Gist options
  • Save gustavopch/a5c280079ce6b178952c6ddd8c0dc404 to your computer and use it in GitHub Desktop.
Save gustavopch/a5c280079ce6b178952c6ddd8c0dc404 to your computer and use it in GitHub Desktop.
A function that provides a shared/cached connection to prevent creating new connections every time on Now 2.0
import { MongoClient } from 'mongodb'
import MongoMemoryServer from 'mongodb-memory-server'
import { config } from '../src/config'
import { getSharedConnection } from '../src/infra/database'
import { spinUpInMemoryMongo } from './spin-up-mongo'
let mongod: MongoMemoryServer
beforeAll(async () => {
mongod = await spinUpInMemoryMongo()
// Mock config
config.mongo.uri = await mongod.getConnectionString()
})
afterAll(async () => {
await mongod.stop()
})
describe('getSharedConnection', () => {
let client: MongoClient
it('should connect and return exact same client for all concurrent calls', async () => {
const clients = await Promise.all([await getSharedConnection(), await getSharedConnection(), await getSharedConnection()])
expect(clients[0]).toBeInstanceOf(MongoClient)
expect(clients[0]).toBe(clients[1])
expect(clients[0]).toBe(clients[2])
client = clients[0]
})
it('should still return exact same client for consecutive calls', async () => {
expect(await getSharedConnection()).toBe(client)
expect(await getSharedConnection()).toBe(client)
expect(await getSharedConnection()).toBe(client)
})
it('should close the connection', () => {
client.close()
})
})
import { MongoClient } from 'mongodb'
import { log } from '../log'
import { connect } from './connect'
let initiating = false
let client: MongoClient | null = null
/**
* Queue of `resolve` and `reject` functions waiting for the result
* of the on-going connection attempt.
*/
let deferred: Array<{
resolve: (client: MongoClient) => void
reject: (value: Error) => void
}> = []
export const getSharedConnection = (): Promise<MongoClient> => {
return new Promise(async (resolve, reject) => {
// In case a connection is being initiated by another call...
if (initiating) {
// Defer resolving/rejecting to the end of that other execution
log('mongo', 'already initiating, defer')
deferred.push({ resolve, reject })
return
}
// In case there's already a connection established...
if (client) {
if (client.isConnected()) {
// Client is connected, quick return
resolve(client)
return
} else {
// Client is not connected anymore, discard it and proceed
client = null
}
}
// No connection established or being established, so (re)connect
initiating = true
log('mongo', 'client init')
try {
client = await connect()
// Connected with success
log('mongo', 'connected')
resolve(client)
deferred.forEach(promise => promise.resolve(client!))
deferred = []
initiating = false
} catch (error) {
// Error
client = null
console.error('[mongo] client error:', error)
reject(error)
deferred.forEach(promise => promise.reject(error))
deferred = []
initiating = false
}
})
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment