Skip to content

Commit

Permalink
feat(mongoRepo): Adds retries to connections. (#30)
Browse files Browse the repository at this point in the history
  • Loading branch information
ryasmi committed Feb 6, 2018
1 parent 2efd73d commit c1bea1d
Show file tree
Hide file tree
Showing 12 changed files with 144 additions and 19 deletions.
16 changes: 8 additions & 8 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions package.json
Expand Up @@ -34,6 +34,7 @@
},
"dependencies": {
"aws-sdk": "^2.80.0",
"bluebird": "^3.5.1",
"body-parser": "^1.17.2",
"boolean": "^0.1.3",
"cors": "^2.8.3",
Expand All @@ -55,6 +56,7 @@
"devDependencies": {
"@ht2-labs/semantic-release": "1.0.26",
"@ht2-labs/typescript-project": "1.0.8",
"@types/bluebird": "^3.5.20",
"@types/body-parser": "1.16.8",
"@types/cors": "2.8.3",
"@types/express": "4.11.1",
Expand Down
11 changes: 0 additions & 11 deletions src/mongoRepo/utils/connectToDb.ts

This file was deleted.

11 changes: 11 additions & 0 deletions src/mongoRepo/utils/connectToDb/Config.ts
@@ -0,0 +1,11 @@
import { LoggerInstance } from 'winston';
import Connection from './Connection';

export default interface Config {
readonly maxRetries: number;
readonly retryGapMS: number;
readonly logger: LoggerInstance;
readonly url: string;
readonly dbName: string;
readonly setConnection: (connection: Promise<Connection>) => void;
}
6 changes: 6 additions & 0 deletions src/mongoRepo/utils/connectToDb/Connection.ts
@@ -0,0 +1,6 @@
import { Db, MongoClient } from 'mongodb';

export default interface Connection {
readonly client: MongoClient;
readonly db: Db;
}
20 changes: 20 additions & 0 deletions src/mongoRepo/utils/connectToDb/createConnection.ts
@@ -0,0 +1,20 @@
import { MongoClient } from 'mongodb';
import Config from './Config';
import Connection from './Connection';
import handleJoiningReplica from './handleJoiningReplica';
import handleLeavingReplica from './handleLeavingReplica';
import retryConnection from './retryConnection';

export default async (config: Config, retries = 0): Promise<Connection> => {
try {
const client = await MongoClient.connect(config.url);
const db = client.db(config.dbName);
config.logger.info(`Created new Mongo connection`);
(db as any).s.topology.once('left', handleLeavingReplica(config));
(db as any).s.topology.once('joined', handleJoiningReplica(config));
return { client, db };
} catch (err) {
config.logger.error(`Failed Mongo connection: ${err.message}`);
return retryConnection(config, retries + 1);
}
};
15 changes: 15 additions & 0 deletions src/mongoRepo/utils/connectToDb/getConnection.ts
@@ -0,0 +1,15 @@
import Config from './Config';
import Connection from './Connection';
import createConnection from './createConnection';
import hasConnection from './hasConnection';

export default async (
config: Config,
existingConnection?: Promise<Connection>,
): Promise<Connection> => {
const isConnected = await hasConnection(config, existingConnection);
if (!isConnected) {
return createConnection(config);
}
return existingConnection as Promise<Connection>;
};
5 changes: 5 additions & 0 deletions src/mongoRepo/utils/connectToDb/handleJoiningReplica.ts
@@ -0,0 +1,5 @@
import Config from './Config';

export default (config: Config) => (role: string, server: any) => {
config.logger.info(`A ${role} server joined the replica set`, server.me);
};
11 changes: 11 additions & 0 deletions src/mongoRepo/utils/connectToDb/handleLeavingReplica.ts
@@ -0,0 +1,11 @@
import Config from './Config';
import createConnection from './createConnection';

export default (config: Config) => (role: string, server: any) => {
config.logger.info(`A ${role} server (${server.ismaster.me}) left the replica set`);
const connection = createConnection(config);
connection.catch((err) => {
config.logger.error(`Failed Mongo connection after server left the replica: ${err.message}`);
});
config.setConnection(connection);
};
15 changes: 15 additions & 0 deletions src/mongoRepo/utils/connectToDb/hasConnection.ts
@@ -0,0 +1,15 @@
import * as mongodb from 'mongodb'; // tslint:disable-line:no-unused
import Config from './Config';
import Connection from './Connection';

export default async (config: Config, connection: Promise<Connection> | undefined) => {
if (connection === undefined) {
return false;
}
try {
const { client } = await connection;
return client.isConnected(config.dbName);
} catch (err) {
return false;
}
};
38 changes: 38 additions & 0 deletions src/mongoRepo/utils/connectToDb/index.ts
@@ -0,0 +1,38 @@
import { Db } from 'mongodb';
import { LoggerInstance } from 'winston';
import Config from './Config';
import Connection from './Connection';
import getConnection from './getConnection';

export interface Opts {
readonly maxRetries?: number;
readonly retryGapMS?: number;
readonly logger: LoggerInstance;
readonly url: string;
readonly dbName: string;
}

const defaultMaxRetries = 3;
const defaultRetryGapMS = 1000;

export default ({
maxRetries = defaultMaxRetries,
retryGapMS = defaultRetryGapMS,
logger,
url,
dbName,
}: Opts) => {
let connection: Promise<Connection>; // tslint:disable-line:no-let
const setConnection = (newConnection: Promise<Connection>) => {
connection = newConnection;
};
const config: Config = { maxRetries, retryGapMS, logger, url, dbName, setConnection };
const connectToDb = async (): Promise<Db> => {
setConnection(getConnection(config, connection));
return (await connection).db;
};
connectToDb().catch((err) => {
logger.error(`Failed initial mongo connection: ${err.message}`);
});
return connectToDb;
};
13 changes: 13 additions & 0 deletions src/mongoRepo/utils/connectToDb/retryConnection.ts
@@ -0,0 +1,13 @@
import { delay } from 'bluebird';
import Config from './Config';
import Connection from './Connection';
import createConnection from './createConnection';

export default async (config: Config, retries: number): Promise<Connection> => {
if (retries > config.maxRetries) {
throw new Error('Too many retries');
}
config.logger.info(`Starting retry ${retries} in ${config.retryGapMS}ms`);
await Promise.resolve(delay(config.retryGapMS));
return createConnection(config, retries);
};

0 comments on commit c1bea1d

Please sign in to comment.