Skip to content

feat: support typeorm slave option - #694

Open
jamesacres wants to merge 2 commits into
Papooch:mainfrom
jamesacres:feature/typeorm-slave-mode
Open

jamesacres wants to merge 2 commits into
Papooch:mainfrom
jamesacres:feature/typeorm-slave-mode

Conversation

@jamesacres

Copy link
Copy Markdown

What

Adds an optional mode: 'slave' to the TypeORM adapter's transaction options so a whole transaction can be pinned to a read replica. When set, every read in the callback — including custom query-builder reads, which TypeORM would otherwise send to the master — runs on a replica query runner. Default behaviour (mode: 'master') is unchanged, so this is purely additive for existing users.

Why

TypeORM's replication only routes plain find-style reads to a replica; anything built with the query builder still hits the master. We need some custom query-builder reads to go to the replica.

Worth a close look

  • transactional-adapter-typeorm.ts — wrapWithReplicaTransaction is the only new runtime path. It manages the query runner's lifecycle by hand (connect → startTransaction → commit/rollback → release in finally) because dataSource.transaction() always uses the master and can't be told to use a replica. Worth confirming the release/rollback ordering is what you'd expect.
  • Nested transactions in slave mode throw rather than silently falling back to the master. This is a deliberate limitation (a replica transaction can't host a nested write), but it is a behavioural choice worth a second opinion — see the wrapWithNestedTransaction guard.
  • The rest of the adapter diff (the master path) is untouched; the two if (options?.mode === 'slave') guards are the only insertions into existing methods.
  • The spec changes are large but mostly additive: the DataSource now uses replication config, and there's a new Slave mode describe block. The existing tests and their setup are unchanged apart from the datasource config.

Verified

  • yarn test in the typeorm adapter package: 12/12 pass, including the 4 new slave-mode tests. Tests run against the existing dockerised Postgres on localhost:5446.
  • yarn build for core → transactional → typeorm adapter: clean.
  • yarn format / format:check: clean.
  • Slave-mode tests assert routing (createQueryRunner('slave')), isolation-level pass-through (startTransaction('SERIALIZABLE') when both mode and isolationLevel are given), query-runner release on success and on error, and the nested-transaction rejection.
  • Not tested with real streaming replication. The test DataSource points both master and slave at the same database (there's a single test instance), so the tests verify that the slave connection/query runner is used and options flow through it, not that reads physically land on a distinct replica. Behaviour against a genuinely separate replica relies on TypeORM's own createQueryRunner('slave') routing.

Notes

  • Also included a small pre-existing fix in the spec: Transaction is now a type-only import. It was imported as a value from a export type re-export, which fails under the repo's Jest ESM runtime — the suite couldn't load at all before this. Independent of the feature, but the suite can't run without it. (If you'd prefer, I can split this into its own commit/PR.)
  • No changelog entry added — the repo uses monoweave and generates CHANGES.md from commits, so this should flow from the commit message. Flag if you want it done manually.
  • Docs updated: new "Running on a read replica" section in the TypeORM adapter page, covering the decorator and function-wrapper forms and the nested-transaction limitation.
  • No breaking changes. mode is optional and defaults to the existing master behaviour.

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant