Skip to content

Commit

Permalink
Set replication role to replica to disable FK checks when receiving s…
Browse files Browse the repository at this point in the history
…ubscription data and TXs when using Postgres on the client.
  • Loading branch information
kevin-dp committed Apr 23, 2024
1 parent 67b8609 commit 044dc96
Show file tree
Hide file tree
Showing 5 changed files with 61 additions and 1 deletion.
5 changes: 5 additions & 0 deletions clients/typescript/src/migrators/query-builder/builder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,11 @@ export abstract class QueryBuilder {
*/
abstract readonly getVersion: string

/**
* Disables foreign key checks.
*/
abstract readonly disableForeignKeys: string

/**
* Returns the given query if the current SQL dialect is PostgreSQL.
*/
Expand Down
3 changes: 3 additions & 0 deletions clients/typescript/src/migrators/query-builder/pgBuilder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,9 @@ class PgBuilder extends QueryBuilder {
readonly paramSign = '$'
readonly defaultNamespace = 'public'

/** **Disables** FKs for the duration of the transaction */
readonly disableForeignKeys = 'SET LOCAL session_replication_role = replica;'

pgOnly(query: string) {
return query
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ class SqliteBuilder extends QueryBuilder {
'sqlite_temp_schema',
]

readonly disableForeignKeys = 'PRAGMA foreign_keys = OFF;'

pgOnly(_query: string) {
return ''
}
Expand Down
23 changes: 22 additions & 1 deletion clients/typescript/src/satellite/process.ts
Original file line number Diff line number Diff line change
Expand Up @@ -475,7 +475,17 @@ export class SatelliteProcess implements Satellite {
) {
const namespace = this.builder.defaultNamespace
const stmts: Statement[] = []
stmts.push({ sql: this.builder.deferForeignKeys })

if (this.builder.dialect === 'Postgres') {
// disable FK checks because order of inserts
// may not respect referential integrity
// and Postgres doesn't let us defer FKs
// that were not originally defined as deferrable
stmts.push({ sql: this.builder.disableForeignKeys })
} else {
// Defer FKs on SQLite
stmts.push({ sql: this.builder.deferForeignKeys })
}

// It's much faster[1] to do less statements to insert the data instead of doing an insert statement for each row
// so we're going to do just that, but with a caveat: SQLite has a max number of parameters in prepared statements,
Expand Down Expand Up @@ -1294,6 +1304,17 @@ export class SatelliteProcess implements Satellite {
const lsn = transaction.lsn
let firstDMLChunk = true

if (this.builder.dialect === 'Postgres') {
// Temporarily disable FK checks because order of inserts
// may not respect referential integrity
// and Postgres doesn't let us defer FKs
// that were not originally defined as deferrable
stmts.push({ sql: this.builder.disableForeignKeys })
} else {
// Defer FKs on SQLite
stmts.push({ sql: this.builder.deferForeignKeys })
}

// update lsn.
stmts.push(this.updateLsnStmt(lsn))
stmts.push(this._resetSeenAdditionalDataStmt())
Expand Down
29 changes: 29 additions & 0 deletions clients/typescript/test/satellite/process.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1148,6 +1148,14 @@ export const processTests = (test: TestFn<ContextType>) => {
builder,
namespace,
} = t.context
if (builder.dialect === 'Postgres') {
// Ignore this unit test for Postgres
// because we don't defer FK checks
// but completely disable them for incoming transactions
t.pass()
return
}

await runMigrations()

if (builder.dialect === 'SQLite') {
Expand Down Expand Up @@ -1269,6 +1277,18 @@ export const processTests = (test: TestFn<ContextType>) => {
builder,
namespace,
} = t.context
// since this test disables compensations
// by putting the flag on 0
// it is expecting a FK violation
if (builder.dialect === 'Postgres') {
// if we're running Postgres
// we are not deferring FK checks
// but completely disabling them for incoming transactions
// so the FK violation will not occur
t.pass()
return
}

await runMigrations()

if (builder.dialect === 'SQLite') {
Expand Down Expand Up @@ -1912,7 +1932,16 @@ export const processTests = (test: TestFn<ContextType>) => {
authState,
token,
namespace,
builder,
} = t.context
if (builder.dialect === 'Postgres') {
// Ignore this unit test for Postgres
// because we don't defer FK checks
// but completely disable them for incoming transactions
t.pass()
return
}

await runMigrations()

const tablename = 'child'
Expand Down

0 comments on commit 044dc96

Please sign in to comment.