Migration Hints
PgShift is built on an honest premise: Postgres is the right tool for most early-stage infrastructure, but not forever. Every module collects latency metrics in the background and emits a migration hint when it detects that you are approaching the limits of what Postgres can handle efficiently.
The hint is not an alarm. It is a signal that you should start planning.
Configuration
Section titled “Configuration”const db = createClient({ url: process.env.DATABASE_URL, metrics: true, onMigrationHint(hint) { // Log it, send it to your observability platform, or open a Slack alert console.warn(`[pgshift] Migration hint for ${hint.module}`) console.warn(` Current adapter: ${hint.currentAdapter}`) console.warn(` Suggested: ${hint.suggestedAdapter}`) console.warn(` Reason: ${hint.reason}`) console.warn(` Urgency: ${hint.urgency}`) // 0 to 1 },})Metrics collection is enabled by default. Set metrics: false to disable it.
Each hint fires only once per process lifetime to avoid log noise.
Thresholds
Section titled “Thresholds”| Module | What is measured | Threshold | Suggested migration |
|---|---|---|---|
search | Average query latency | Over 200ms across 100 queries | Elasticsearch, Typesense |
cache | Average read latency | Over 50ms across 100 reads | Redis |
queue | Average job processing lag | Over 5s average wait time | BullMQ with Redis, SQS |
vector | Average query latency | Over 100ms across 100 queries | Pinecone, Weaviate |
workflow | Average step execution latency | Over 2s average step time across 50 runs | Temporal, AWS Step Functions |
Thresholds are conservative by design. A single slow query will not trigger a hint. The threshold must be consistently exceeded before PgShift considers it a signal worth surfacing.
The MigrationHint type
Section titled “The MigrationHint type”interface MigrationHint { module: 'search' | 'cache' | 'queue' | 'vector' | 'workflow' | 'realtime' currentAdapter: string suggestedAdapter: string reason: string urgency: number // 0 to 1 — how urgent the migration is learnMoreUrl?: string // link to docs for the suggested adapter}What migration actually involves
Section titled “What migration actually involves”PgShift does not automate infrastructure migrations. When a hint fires, the planning work is yours.
Search to Elasticsearch: provision a cluster, design index mappings, reindex existing data, and set up a sync pipeline from your database to Elasticsearch.
Cache to Redis: provision a Redis instance, decide on TTL strategy and eviction policy, and update the invalidation logic in your application.
Queue to BullMQ or SQS: provision Redis or an SQS queue, migrate existing pending jobs, and update your worker configuration.
Vector to Pinecone or Weaviate: provision the vector database, re-embed and reindex your data, and update your upsert pipeline.
Workflow to Temporal or AWS Step Functions: provision the orchestration backend, migrate existing workflow definitions and in-flight runs, and update your worker configuration to target the new runtime.
In every case, your application-level API stays exactly the same. The migration is infrastructure work, not application code.
// Before migration — Postgresconst results = await db.search('products').query('air max', { fuzzy: true })
// After migration — Elasticsearchconst results = await db.search('products').query('air max', { fuzzy: true })
// Identical. The adapter changes, your code does not.Sending hints to your observability stack
Section titled “Sending hints to your observability stack”const db = createClient({ url: process.env.DATABASE_URL, onMigrationHint(hint) { // Datadog datadog.increment('pgshift.migration_hint', 1, [ `module:${hint.module}`, `urgency:${Math.round(hint.urgency * 10) / 10}`, ])
// Sentry Sentry.captureMessage(`PgShift migration hint: ${hint.module}`, { level: hint.urgency > 0.7 ? 'warning' : 'info', extra: hint, })
// Slack await slack.send(`PgShift suggests migrating *${hint.module}* to *${hint.suggestedAdapter}*. Urgency: ${hint.urgency}.`) },})