perf: Add Model.replica getter for opt-in read replica queries

Allows Sequelize ORM queries to explicitly opt-in to using the read
replica connection via a static getter on the base Model class.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
Tom Moor
2026-01-31 14:47:20 -05:00
parent 446a0e1071
commit e2bedd2b0f
2 changed files with 34 additions and 7 deletions
+31
View File
@@ -13,6 +13,7 @@ import type {
SaveOptions,
} from "sequelize";
import { DataTypes, UniqueConstraintError } from "sequelize";
import type { Sequelize } from "sequelize-typescript";
import {
AfterCreate,
AfterDestroy,
@@ -27,6 +28,12 @@ import type { Replace, APIContext } from "@server/types";
import { getChangsetSkipped } from "../decorators/Changeset";
import { InternalError } from "@server/errors";
/**
* Lazy reference to the read-only Sequelize instance.
* Imported dynamically to avoid circular dependencies.
*/
let _sequelizeReadOnly: Sequelize | undefined;
type EventOverrideOptions = {
/** Override the default event name. */
name?: string;
@@ -56,6 +63,30 @@ class Model<
*/
static eventNamespace: string | undefined;
/**
* Returns this model bound to the read-only database connection (read replica).
* Use this for queries that can tolerate slight replication lag and don't need
* to read-your-writes consistency.
*
* @example
* // Opt-in to read replica
* const documents = await Document.replica.findAll({ where: { teamId } });
*
* // Default behavior uses primary
* const document = await Document.findOne({ where: { id } });
*
* @returns the model bound to the read replica connection.
*/
static get replica(): typeof this {
if (!_sequelizeReadOnly) {
// Lazy import to avoid circular dependencies
// eslint-disable-next-line @typescript-eslint/no-require-imports
_sequelizeReadOnly =
require("@server/storage/database").sequelizeReadOnly;
}
return _sequelizeReadOnly!.models[this.name] as typeof this;
}
/**
* Validates this instance, and if the validation passes, persists it to the database.
*/
+3 -7
View File
@@ -256,13 +256,9 @@ export const sequelize = createDatabaseInstance(databaseConfig, models);
* Falls back to the main connection if DATABASE_URL_READ_ONLY is not set.
*/
export const sequelizeReadOnly = env.DATABASE_URL_READ_ONLY
? createDatabaseInstance(
env.DATABASE_URL_READ_ONLY,
{},
{
readOnly: true,
}
)
? createDatabaseInstance(env.DATABASE_URL_READ_ONLY, models, {
readOnly: true,
})
: sequelize;
export const migrations = createMigrationRunner(sequelize, [