New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Proper reconnect to database after unexpected loss of connection #6350
Comments
Update: I'm actually able to get queries after they got executed with a custom logger: export class CustomTypeORMLogger implements Logger {
logQueryError(error: string, query: string, parameters?: any[] | undefined, queryRunner?: QueryRunner | undefined) {
if (this.isDown) {
logger(`Query "${query}" ${chalk.grey(`-- [${parameters?.join(", ")}`)}] will be cached!`, _PRIORITY.NORMAL, null, _MODULE.DB)
} else {
this.isDown = true
logger(`Query "${query}" ${chalk.grey(`-- [${parameters?.join(", ")}]`)}
failed because ${chalk.bold(error)}"
=== [DATABASE IS OFFLINE] ===
`, _PRIORITY.ERROR, null, _MODULE.DB)
}
}
logQuery(query: string, parameters?: any[] | undefined, queryRunner?: QueryRunner | undefined) {
logger(`Query "${query}" ${chalk.grey(`-- [${parameters?.join(", ")}]`)} was ${chalk.bold("successful")}`, _PRIORITY.DEBUG, null, _MODULE.DB)
}
[...] Which leads to that output:
So far, so good. But it's still weird. Every time TypeORM syncs the database (creating tables, sets up foreign keys) and it loses connection in that period, the logger receives a message that get's logged:
Now I start the MariaDB server again and the backend and stop the database again after the backend finished syncing and proceeds with seeding the DB with dummy data (after the connection was successful) I get ECONNREFUSED errors:
But Another approachI also tried using subscribers with the Maybe this isn't really possible and I have to use Redis which takes care of that? I really don't know. |
hi guys |
I also have the same problem, i'm trying to repurpose the default connection to connect to another server (after closing it). |
This seems to be a common problem if already 8 people showed interest. As for myself: This issue is now more then a year old and I don't work on the project anymore where I required a solution to this problem. It would be great if someone could experiment on it's own to solve this issue but it would be much easier if the dev team could implement a exposed handler or internal mechanic to solve this. It's very important to not lose queries if the connection drops for e. g. 30secs. |
I am also facing the same problem:
|
in my current project we use TypeORM to interact with Aurora AWS. Sometimes Aurora shutdowns server and instantiates another one. Previous team used Sequelize internal |
TypeORM has internal custom logger feature which I guess may be utilized for this purpose. Currently we utilize it to collect SQL, start time, end time and parameters of each query to send to AWS XRay. It works perfect. And I believe we can utilize this logger for retrying as well. Perhaps all we need is to introduce a cache to store store all this data and perform broken query later after reconnect. |
BTW: doesn't this canRetry do the job? https://typeorm.io/multiple-data-sources#replication
|
@alexey2baranov I've tried |
Faced kind of the same problem with
|
Any updates on this ? kind of a deal breaker... |
Any update on this. eagerly waiting since we get this issue in production |
I'm facing exactly the same problem |
i will migrate to other orm if nothing resolve here |
Any solution ? I mean it's mandatory for production so... EDIT SOLUTION: // seems to be mandatory otherwise poolErrorHandler doesn't get called if database dies
process.on('unhandledRejection', () => {});
const databaseConfigModule = TypeOrmModule.forRootAsync({
imports: [ConfigModule],
inject: [ConfigService],
useFactory: (config: ConfigService<Environment>) => ({
type: 'postgres',
host: config.get<DatabaseConfig>('database').host,
port: config.get<DatabaseConfig>('database').port,
username: config.get<DatabaseConfig>('database').user,
password: config.get<DatabaseConfig>('database').password,
database: config.get<DatabaseConfig>('database').database,
entities: [],
synchronize: true,
autoLoadEntities: true,
poolErrorHandler: async (err) => {
Logger.error(err);
const retryDelayMs = 1000;
const reconnection = setInterval(async () => {
Logger.log(
`DB Connection lost. Retry every ${retryDelayMs}ms...`,
);
const datasource = new DataSource(databaseOptions);
const db = await datasource.initialize();
if (db && db.isInitialized) {
Logger.log('Connection restored.');
clearInterval(reconnection);
}
}, retryDelayMs);
},
},
}),
}); |
@theocerutti thanks for your solution, Although I tried implementing it but couldn't really do it, Maybe you could clear things up for me, What troubles me is that the databaseOptions that you are going to pass to the new instance, firstly Correct me if I'm wrong is DataSource a method that is used is a factory method and the databaseOptions is a const that you pass to it? since I'm not sure that there is a native class that would do it, moreover; the biggest problem I'm facing is the "recursive" manner in which you are doing this, since if the new instance of the DataSource you are creating is going to have the poolErrorHandler you would have to pass a databaseOptions containing that handler into it again and then in that, again and again, and again. this is my interpretation of course, If you could provide more details regarding my doubts that would be great. |
On the other hand, what I managed to implement yesterday was to handle this error. DISCLAIMER:
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. |
Have the same problem but see nothing from that There are any plan to add any solution to hanlde next errors?
|
I have the same problem. I used docker for running mysql database. |
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/orER_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:
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
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
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
The text was updated successfully, but these errors were encountered: