Skip to content

Instantly share code, notes, and snippets.

@mlni
Created March 31, 2020 17:41
Show Gist options
  • Save mlni/a9c2f5624564dd47d85cb44317e90182 to your computer and use it in GitHub Desktop.
Save mlni/a9c2f5624564dd47d85cb44317e90182 to your computer and use it in GitHub Desktop.
A monkey-patched error handler for Typeorm to support failovers on AWS RDS with Postgres. Works around https://github.com/typeorm/typeorm/issues/5112
import { createConnection } from 'typeorm';
import { PostgresDriver } from 'typeorm/driver/postgres/PostgresDriver';
import { PostgresQueryRunner } from 'typeorm/driver/postgres/PostgresQueryRunner';
import { EventEmitter } from 'events';
class ErrorHandlingPostgresQueryRunner extends PostgresQueryRunner {
constructor(driver: any, mode: 'master' | 'slave' = 'master') {
super(driver, mode);
}
query(query: string, parameters?: any[]): Promise<any> {
return super.query(query, parameters).catch(err => {
if (this.releaseCallback) {
const originalReleaseCallback = this.releaseCallback;
this.releaseCallback = () => {
originalReleaseCallback(err); // pass the error back to 'pg' connection
};
}
throw err;
});
}
}
function errorHandler(err: Error) {
console.log(`Error on postgres connection: ${err.message}`);
}
const addErrorHandler = (client: EventEmitter) => client.on('error', errorHandler);
const removeErrorHandler = (client: EventEmitter) => client.removeListener('error', errorHandler);
export function configureErrorHandling(driver: PostgresDriver) {
const connectionPool = driver.master;
// Add error handlers to all new database connections
connectionPool.on('connect', addErrorHandler);
connectionPool.on('remove', removeErrorHandler);
// TypeORM creates a connection on startup to verify connection parameters. Add an error handler to that too.
// eslint-disable-next-line no-underscore-dangle
connectionPool._clients.forEach(addErrorHandler);
/* When a connection is killed during db failover, it starts throwing errors for all consecutive queries.
* TypeORM doesn't pass those errors back to underlying connection pool, which is in general the right thing
* to do. But in this case, the connection remains broken indefinitely.
*
* Monkey-patch the TypeORM query runner to pass any connection-level errors back to the pool.
* This has the unfortunate side effect of closing the connection when a technical error occurs during query
* (eg typo in query, duplicate unique value etc), but that is deemed a smaller evil compared to breaking down
* during maintenance fail-overs.
*/
// eslint-disable-next-line no-param-reassign
driver.createQueryRunner = mode => {
return new ErrorHandlingPostgresQueryRunner(driver, mode);
};
}
// Use it like this:
createConnection().then(connection => {
configureErrorHandling(connection.driver as PostgresDriver);
// do your db connection stuff
connection.createQueryBuilder().insert().into(User).values([{ name: 'Bill' }]).execute();
});
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment