Created
March 31, 2020 17:41
-
-
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
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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