pollux
April 5, 2022, 6:35pm
1
Hi,
I just started setting up Postgres multiregion for typeorm.
So I’m confused on how to handle the fly-replay header?
how do I detect for database read replica write errors?
I’m using TypeORM for my db calls. Also using Graphql.
I was thinking I can lisn to lisners on every “Before Insert” or “Before Update” event? this is just a thought, havnt tried it out https://typeorm.io/listeners-and-subscribers
Has anybody implemented this with typeorm?
Thanks
greg
April 5, 2022, 9:10pm
2
I haven’t but in case nobody else has either (and says how they did it) I would think you could either:
have a listener on any request that might change the data. Like an insert/update/delete (as you suggest). And return a response including the fly-replay
header
or
listen for a database error caused by the write to a read replica. It’s not entirely clear what the recommended approach is to do that but here’s one approach:
opened 09:22AM - 07 Nov 19 UTC
closed 05:40AM - 06 Oct 20 UTC
question
**Issue type:**
[x] question
[ ] bug report
[ ] feature request
[ ] docume… ntation issue
**Database system/driver:**
[ ] `cordova`
[ ] `mongodb`
[ ] `mssql`
[x] `mysql` / `mariadb`
[ ] `oracle`
[ ] `postgres`
[ ] `cockroachdb`
[ ] `sqlite`
[ ] `sqljs`
[ ] `react-native`
[ ] `expo`
**TypeORM version:**
[ ] `latest`
[ ] `@next`
[x] `0.x.x` (0.2.20)
**Steps to reproduce or a small repository showing the problem:**
I see no hint of being able to handle database calls error properly. In my case, I have to catch duplicate entry error as follow:
#### // user.repository.ts
```ts
const result = this.save(user)
.catch((err: any) => {
console.log(err);
});
```
...and this is what I got
```js
{
message: 'ER_DUP_ENTRY: Duplicate entry ' +
"'a.xxxxxxxx@xxxxx.com' for key " +
"'IDX_97672ac88f789774dd47f7c8be'",
code: 'ER_DUP_ENTRY',
errno: 1062,
sqlMessage: "Duplicate entry 'a.xxxxxxxx@xxxxx.com' " +
"for key 'IDX_97672ac88f789774dd47f7c8be'",
sqlState: '23000',
index: 0,
sql: 'INSERT INTO `users`(`id`, `email`, `password`, `firstname`, ' +
'`lastname`, `status`, `token`) VALUES ' +
"('3433c6a6-6a64-4071-bf77-6a752a09879f', 'a.xxxxxxxx@xxxxx.com', " +
"'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx', " +
"'Aldee', 'XXXXXX', 'active', 'aa86b69a0b1f5')",
name: 'QueryFailedError',
query: 'INSERT INTO `users`(`id`, `email`, `password`, `firstname`, ' +
'`lastname`, `status`, `token`) VALUES (?, ?, ?, ?, ?, ?, ?)',
parameters: [
'3433c6a6-6a64-4071-bf77-6a752a09879f',
'a.xxxxxxxx@xxxxx.com',
'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx',
'Aldee',
'XXXXXX',
'active',
'aa86b69a0b1f5'
]
}
```
As you can see, the `code` property is available as well as the `errno`.
The exception thrown says that it is an instance of `QueryFailedError`.
Ok, so now, in the case above, I can modify my code and handle duplicate error exception like this:
```ts
const result = this.save(user)
.catch((err: any) => {
if (err instanceof QueryFailedError) {
switch (err.code) {
case: 'ER_DUP_ENTRY':
// do something
break;
}
}
});
```
But not so fast! The argument of the `switch` throws an error because the `QueryFailedError` class doesn't contain `code` property.
To bypass that issue, I just omitted the `instanceof` checking and `switch` part.
```ts
const result = this.save(user)
.catch((err: any) => {
switch (err.code) {
case 'ER_DUP_ENTRY':
// do something....
break;
}
});
```
It's working if I bypass the typing (using `any`) but it adds some risk if the API changes in the future. Is there any neat way to do this?
These being the available errors:
ORM for TypeScript and JavaScript. Supports MySQL, PostgreSQL, MariaDB, SQLite, MS SQL Server, Oracle, SAP Hana, WebSQL databases. Works in NodeJS, Browser, Ionic, Cordova and Electron platforms. -...
Not sure which it would throw … I would guess a QueryFailedError
as I can’t see a write-to-a-read-replica one
And return a fly-replay
header which Fly’s proxy will see. That should contain the region the primary database is in e.g
res.setHeader('Fly-Replay', 'region=lhr')
1 Like
pollux
April 5, 2022, 9:26pm
3
OMG! Yes this is what I was looking for.
Let me give it a try. I think this is a better apporach. Just to look for errors rather than handle all write requests.
1 Like
pollux
April 6, 2022, 6:40pm
4
Okay I implemnted it.
I dint use fly-replay request. TypeORM has a replication strategy here https://typeorm.io/multiple-data-sources#replication
here is a gist of my code if anybody needs for TypeORM
const databaseUrl = process.env.DATABASE_URL as string;
let options: ConnectionOptions = {
type: 'postgres',
name: 'default',
logging: false,
synchronize: false,
entities: [__dirname + '/../modules/**/*.js'],
migrations: [__dirname + '/../migration/*.js'],
};
if (process.env.PRIMARY_REGION !== process.env.FLY_REGION) {
options = {
...options,
replication: {
master: {
url: databaseUrl,
},
slaves: [
{
url: databaseUrl.replace('5432', '5433'),
},
],
},
};
} else {
options = {
...options,
url: databaseUrl,
};
}
return createConnection(options)
use this while you are creating a connection for produciton
1 Like