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:
master/src/error
ORM for TypeScript and JavaScript (ES7, ES6, ES5). Supports MySQL, PostgreSQL, MariaDB, SQLite, MS SQL Server, Oracle, SAP Hana, WebSQL databases. Works in NodeJS, Browser, Ionic, Cordova and Elect...
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