typeorm: Proper reconnect to database after unexpected loss of connection
Issue type:
[x] question [ ] bug report [ ] feature request [ ] documentation issue
Database system/driver:
[ ] cordova
[ ] mongodb
[ ] mssql
[x] mysql / mariadb
[ ] oracle
[ ] postgres
[ ] cockroachdb
[ ] sqlite
[ ] sqljs
[ ] react-native
[ ] expo
TypeORM version:
[ ] latest
[ ] @next
[x] 0.2.22
Steps to reproduce or a small repository showing the problem:
Establish a connection to your database. Then do some inserts and selects, whatever. And while TypeORM does all that, cut the connection by stopping the MariaDB server. That should throw an error (when logging is turned on) that says either PROTOCOL_CONNECTION_LOST and/or ER_SERVER_SHUTDOWN in the code property of the error.
Actual question
How can I catch that? How can I catch these errors to perform reconnect tries. I want to have access to the error object that looks like that in the console:
error: Error: ER_SERVER_SHUTDOWN: Server shutdown in progress
[TRACE]
--------------------
[TRACE]
code: 'ER_SERVER_SHUTDOWN',
errno: 1053,
sqlMessage: 'Server shutdown in progress',
sqlState: '08S01',
index: 0,
sql: 'ALTER TABLE `TABLE_NAME` ADD CONSTRAINT `FK_62c53a5b672adbef1ccb69f5a4b` FOREIGN KEY (`TABLE_NAME`) REFERENCES `TABLE_NAME`(`id`) ON DELETE NO ACTION ON UPDATE NO ACTION'
error: Error: Connection lost: The server closed the connection.
[STACK]
fatal: true,
code: 'PROTOCOL_CONNECTION_LOST'
My goal is to catch that event and the SQL query, so that when a new connection is established, I can execute these cached SQL queries, so no data is lost. Maybe TypeORM as an built-in way or event to detect such things.
Solving attempts
Through unhandled rejection handler
process.on('unhandledRejection', listener => {
if (listener!.toString().match(/ECONNREFUSED/) ||
listener!.toString().match(/Connection is not established/)) {
logger(`Connection to ${bold(target_database.database)} has been lost. Retry to connect ...`, _PRIORITY.CRITICAL, null, _MODULE.MAIN);
conn?.close()
conn?.connect()
}
})
The problem is that I don’t get enough data. I can manually close the connection (since TypeORM still thinks that the connection is there) and then I can try a reconnect. Problem is just that I’m not able to cache queries that were made while the database wasn’t available. And another problem is that this might trigger on an other event which isn’t related to the database at all.
Directly using the underlying MySQL driver
// I also tried to use the 'enqueue' event, which doesn't trigger either.
// @ts-ignore: Since `pool` doesn't exist in the type definition
conn.driver.pool.on('error', (error) => {
logger(`Connection to ${bold(target_database.database)} has been lost. Retry to connect ...`, _PRIORITY.CRITICAL, null, _MODULE.MAIN);
})
Here I get the driver property of my TypeORM connection and of that the pool object. I don’t get an runtime error, so I assume that this object exists. The problem is just that this never triggers.
My config
{
name: "development",
type: "mysql",
host: "localhost",
port: 3306,
username: "****-backend-dev",
password: "*****",
database: "****-backend-dev",
synchronize: true,
logging: true,
entities: [
"src/models/**/*.ts"
],
migrations: [
"src/migration/**/*.ts"
],
subscribers: [
"src/subscriber/**/*.ts"
],
extra: {
connectionTimeoutMillis: 1000,
idleTimeoutMillis: 1500
}
}
About this issue
- Original URL
- State: open
- Created 4 years ago
- Reactions: 26
- Comments: 19 (3 by maintainers)
@alexey2baranov I’ve tried
canRetry, it only retry the initial connection. Once your connection Pool established, then it’s disconnected for any reason, TypeORM still won’t be able to reconnect your broken connection - leaving all queries failed.Faced kind of the same problem with
replicationconfiguredPool does Not existsalways 😨On the other hand, what I managed to implement yesterday was to handle this error. of course, this is limited to my use case. what I did was to implement a GlobalExceptionHandler which actually lets you catch the ECONNREFUSED exception, whereas in its absence Nest would not let you catch it.
DISCLAIMER: this is not related to typeorm so and is rather just a workaround so excuse me if I’m posting it here, I just supposed it might help someone since at first i was perplexed by the fact that i couldn’t catch this error and handle it, but then i discovered that implementing a gloablExceptionHandler gives you control over it (which seems obvious i know but whatever)
inside the handleDatabaseConnectionIssue:
my use case was to redo the call to the database therefore i retried doing the http call that i had put inside the failedRequests array and then retry them using axios:
it goes without saying that there are other workarounds such as using a queuing framework that could redo the http calls instead of doing them internally in your application. for example, configuring it to redo the http calls to your application in case of a certain error code, using master/slave replication options which I tried but really didn’t get into since it was a pain and wasn’t really my use case nor had I the possibility. passing retryAttempts and retryDelay only serves when the application is restarted, instead, this use case is more directed towards databases that might break under certain conditions and there is the need to just wait it out for a minute or so and you want the user to not loose connection, of course, client-side timeouts should be considered.