Thanks to visit codestin.com
Credit goes to deepwiki.com

Menu

Database Migrations

Relevant source files

Purpose and Scope

This document describes the database migration system in Trigger.dev, focusing on how schema changes are managed, applied, and deployed across different environments. The system uses Prisma as the primary ORM and migration tool for the PostgreSQL database, with additional migration handling for the Graphile Worker job queue infrastructure.

For information about the overall database schema structure, see Database Schema Overview. For details on specific data models, see Task and Run Models, Organization and Project Models, and Deployment and Worker Models.


Prisma Migration System

Trigger.dev uses Prisma as its Object-Relational Mapping (ORM) tool and database migration manager. Prisma provides type-safe database access and a declarative migration workflow.

Schema Definition

The database schema is defined in schema.prisma files using Prisma's schema language. The schema includes:

  • Data source configuration: Connection to PostgreSQL with support for read replicas
  • Generator configuration: Prisma Client generation settings
  • Model definitions: All database tables, relationships, and constraints
  • Indexes and unique constraints: Performance optimizations and data integrity rules

The Prisma client is instantiated and used throughout the codebase for database operations, as seen in apps/webapp/app/services/worker.server.ts4 where both prisma and $replica are imported for write and read operations respectively.

Migration Generation

Prisma migrations are generated using the Prisma CLI:

When a migration is generated, Prisma:

  1. Compares the current schema with the database state
  2. Generates SQL migration files in the migrations directory
  3. Creates a readable description of the changes
  4. Updates the Prisma Client types

Migration Application

Migrations are applied differently depending on the environment:

Development Environment:

  • Migrations run automatically during local development setup
  • The prisma migrate dev command applies pending migrations
  • Schema changes can be iterated rapidly with automatic type regeneration

Production Environment:

  • Migrations are applied as part of the deployment process
  • Uses prisma migrate deploy to apply pending migrations
  • Runs before application startup to ensure schema compatibility

Sources: apps/webapp/app/services/worker.server.ts1-131


Graphile Worker Migration System

In addition to Prisma migrations for the main database schema, Trigger.dev uses Graphile Worker for background job processing, which maintains its own database schema and migration system.

GraphileMigrationHelperService

The GraphileMigrationHelperService handles migrations specific to the Graphile Worker infrastructure. This service is invoked during worker initialization to ensure the Graphile Worker schema is up-to-date.

Initialization Flow:

The migration helper is called early in the application lifecycle:

apps/webapp/app/services/worker.server.ts124-131

async function init() {
  const migrationHelper = new GraphileMigrationHelperService();
  await migrationHelper.call();

  if (env.WORKER_ENABLED === "true") {
    await workerQueue.initialize();
  }
}

This ensures that:

  1. Graphile Worker schema is created/updated before workers start
  2. Job queue tables exist before attempting to enqueue jobs
  3. Migration failures prevent worker initialization

Graphile Worker Schema Components

The Graphile Worker maintains several database objects in a separate schema:

  • Job queue tables: Store pending and completed jobs
  • Job state tracking: Track job execution status and attempts
  • Scheduled jobs: Handle recurring task scheduling
  • Worker coordination: Manage worker locks and concurrency

These migrations are separate from Prisma migrations because Graphile Worker manages its own schema versioning and migration system.

Sources: apps/webapp/app/services/worker.server.ts124-131


Migration Execution Workflow

Development Workflow

  1. Schema Modification: Developer updates schema.prisma with new models, fields, or relationships
  2. Migration Generation: Run prisma migrate dev --name <description>
  3. Local Testing: Test migration on local development database
  4. Type Regeneration: Prisma Client types are automatically updated
  5. Code Updates: Update application code to use new schema

Production Deployment Workflow

  1. Pre-deployment: Migration files are committed to version control
  2. Build Phase: Docker image includes migration files
  3. Deployment Phase:
    • prisma migrate deploy applies pending migrations
    • GraphileMigrationHelperService applies Graphile Worker migrations
    • Application starts only after successful migrations
  4. Rollback Strategy: Keep migration files backward-compatible when possible

Sources: apps/webapp/app/services/worker.server.ts124-131


Database Client Configuration

The codebase maintains multiple database client instances for different purposes:

Primary Client and Replica

Client Instantiation:

  • prisma: Main client for write operations and transactions
  • $replica: Read-only client pointing to database replica for improved performance
  • Both clients share the same schema but connect to different database instances

The ZodWorker in apps/webapp/app/services/worker.server.ts136-137 is configured with both clients:

prisma,
replica: $replica,

Connection Pooling

The worker queue uses connection pooling to manage database connections efficiently:

apps/webapp/app/services/worker.server.ts138-145

runnerOptions: {
  connectionString: env.DATABASE_URL,
  concurrency: env.WORKER_CONCURRENCY,
  pollInterval: env.WORKER_POLL_INTERVAL,
  noPreparedStatements: env.DATABASE_URL !== env.DIRECT_URL,
  schema: env.WORKER_SCHEMA,
  maxPoolSize: env.WORKER_CONCURRENCY + 1,
}

The maxPoolSize is set to WORKER_CONCURRENCY + 1 to ensure adequate connections for concurrent job processing.

Sources: apps/webapp/app/services/worker.server.ts134-145


Migration Considerations

Transaction Safety

Migrations should be designed with transactional safety in mind:

  • Additive changes (adding columns, tables): Generally safe
  • Destructive changes (dropping columns, tables): Require careful planning
  • Data migrations: May need to be split into multiple steps

Zero-Downtime Migrations

For production deployments, follow these patterns:

  1. Adding columns: Add as nullable first, populate data, then add constraints
  2. Removing columns: Mark as deprecated in code, remove in subsequent deployment
  3. Renaming columns: Create new column, migrate data, remove old column
  4. Schema changes: Use database views or application-layer abstractions during transition

Backward Compatibility

The migration system supports rolling deployments by ensuring:

  • New migrations are compatible with the previous application version
  • Old application code can operate on the new schema temporarily
  • Database constraints don't break existing functionality

Multi-Schema Coordination

When both Prisma and Graphile Worker schemas require updates:

  1. Prisma migrations apply to the main schema (typically public)
  2. Graphile Worker migrations apply to the graphile_worker schema
  3. Both run sequentially during initialization
  4. Application waits for both to complete before starting

Sources: apps/webapp/app/services/worker.server.ts124-131


Migration History and Versioning

Prisma Migration History

Prisma maintains migration history in the _prisma_migrations table:

  • Each migration has a unique identifier
  • Tracks applied migrations and timestamps
  • Prevents re-applying completed migrations
  • Enables migration status queries

Schema Versioning

The schema version is determined by:

  • The set of applied Prisma migrations
  • The Graphile Worker schema version
  • Application code compatibility level

Migration Rollback

While Prisma supports migration rollback in development:

Production rollbacks are more complex and typically involve:

  1. Deploying code compatible with the previous schema
  2. Running a compensating migration to undo changes
  3. Careful coordination to avoid data loss

Sources: apps/webapp/app/services/worker.server.ts1-131


Environment-Specific Configuration

Database connection configuration varies by environment:

ConfigurationDevelopmentProduction
Primary URLDATABASE_URLDATABASE_URL
Direct ConnectionDIRECT_URLDIRECT_URL
Read ReplicaOptionalDATABASE_REPLICA_URL
Migration Strategymigrate devmigrate deploy
Worker Schemaenv.WORKER_SCHEMAenv.WORKER_SCHEMA
Connection PoolingLower limitsHigher limits

The distinction between DATABASE_URL and DIRECT_URL is important:

  • DATABASE_URL: May point to a connection pooler (e.g., PgBouncer)
  • DIRECT_URL: Direct connection for migrations and schema operations

The noPreparedStatements flag is set when using connection poolers that don't support prepared statements:

apps/webapp/app/services/worker.server.ts142

noPreparedStatements: env.DATABASE_URL !== env.DIRECT_URL

Sources: apps/webapp/app/services/worker.server.ts138-145


Key Migration Files and Locations

Key locations:

  • Schema Definition: apps/webapp/prisma/schema.prisma
  • Migration Files: apps/webapp/prisma/migrations/
  • Graphile Helper: apps/webapp/app/services/db/graphileMigrationHelper.server.ts
  • Database Client: apps/webapp/app/db.server.ts

Sources: apps/webapp/app/services/worker.server.ts29 apps/webapp/app/services/worker.server.ts4