Qadam Roadmap
проектdocs/Agents/rules/data-read-write-replicas.md

data-read-write-replicas.md

Обновлён 1 апр. 2026 г., 12:41 · 0 комментариев


title: Use Read/Write Replica Separation impact: HIGH impactDescription: Reduces primary DB load and improves read performance tags: data, prisma, replicas, performance, postgresql

Паспорт документа

  • Статус документа: living standard
  • Актуально на: 28 марта 2026 года
  • Владелец: backend/platform-команда
  • Пересмотр: при изменении инженерной практики, CI/CD, архитектурных правил или локального workflow
  • Область применения: внутренние rule/reference-card документы для инженерной команды
  • Связанные документы:

Use Read/Write Replica Separation

Impact: HIGH

All read operations (SELECT) must use the read replica. Only write operations (INSERT, UPDATE, DELETE) use the primary database connection.

Implementation with Prisma:

// prisma/prisma.service.ts
import { Injectable, OnModuleInit, OnModuleDestroy } from '@nestjs/common';
import { PrismaClient } from '@prisma/client';

@Injectable()
export class PrismaService extends PrismaClient implements OnModuleInit, OnModuleDestroy {
  private replicaClient: PrismaClient | null = null;

  constructor() {
    super({ datasourceUrl: process.env.DATABASE_URL });

    if (process.env.DATABASE_REPLICA_URL) {
      this.replicaClient = new PrismaClient({
        datasourceUrl: process.env.DATABASE_REPLICA_URL,
      });
    }
  }

  $replica() {
    return this.replicaClient || this;
  }

  async onModuleInit() {
    await this.$connect();
    if (this.replicaClient) await this.replicaClient.$connect();
  }

  async onModuleDestroy() {
    await this.$disconnect();
    if (this.replicaClient) await this.replicaClient.$disconnect();
  }
}

Usage in repositories:

@Injectable()
export class CatalogRepository {
  constructor(private readonly prisma: PrismaService) {}

  // READ — use replica
  async findPublished(filters: CatalogFilters) {
    return this.prisma.$replica().item.findMany({
      where: this.buildWhereClause(filters),
      select: { id: true, name: true, slug: true, priceFrom: true },
    });
  }

  // WRITE — use primary
  async create(data: CreateItemDTO) {
    return this.prisma.item.create({ data });
  }

  // WRITE — use primary
  async updateStatus(id: string, status: ItemStatus) {
    return this.prisma.item.update({
      where: { id },
      data: { status },
    });
  }
}

Rules:

  • $replica() for all findMany, findFirst, findUnique, count, aggregate
  • Default this.prisma (primary) for all create, update, delete, upsert
  • In development with no replica configured, $replica() falls back to primary