From e8cc90b34cb782073e6f1fb4bd02c89167f87955 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Padez?= Date: Sun, 23 Aug 2026 14:16:31 +0000 Subject: [PATCH] name the over-long foreign keys so db:push can be a no-op MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Drizzle generates names past Postgres's 63-character identifier limit. Postgres stores the truncated form, drizzle compares against its own longer one, and every push on every host drops and re-adds constraints that are already identical. Deferred as harmless noise since 2026-08-19. What ended that was a deploy plan saying "expect no changes, stop if it wants to alter something" — which this made impossible to satisfy, so the deploy agent had to overrule its own instructions. Verified by pushing twice against a scratch database: the fleet-wide second-push churn went from ten statements to two, and the two that remain are a composite primary key that cannot be fixed either way. Naming a foreign key means removing the inline .references() or drizzle creates both. Co-Authored-By: Claude Opus 5 --- sidecar/db/collections/schema.ts | 29 ++++++++++++++++++++++------- 1 file changed, 22 insertions(+), 7 deletions(-) diff --git a/sidecar/db/collections/schema.ts b/sidecar/db/collections/schema.ts index e83ffbd..cffd7d5 100644 --- a/sidecar/db/collections/schema.ts +++ b/sidecar/db/collections/schema.ts @@ -1,4 +1,4 @@ -import { pgTable, uuid, text, timestamp, index, primaryKey } from 'drizzle-orm/pg-core'; +import { pgTable, uuid, text, timestamp, index, primaryKey, foreignKey} from 'drizzle-orm/pg-core'; import { generations } from '../generations/schema'; /** @@ -29,15 +29,30 @@ export const collections = pgTable('atelier_collections', { */ export const collectionItems = pgTable('atelier_collection_items', { - collectionId: uuid('collection_id') - .notNull() - .references(() => collections.id, { onDelete: 'cascade' }), - generationId: uuid('generation_id') - .notNull() - .references(() => generations.id, { onDelete: 'cascade' }), + collectionId: uuid('collection_id').notNull(), + generationId: uuid('generation_id').notNull(), addedAt: timestamp('added_at', { withTimezone: true }).notNull().defaultNow(), }, (table) => [ + /* + Both foreign keys are NAMED, because drizzle generates names over Postgres's 63-character identifier + limit — 65 and 64. Postgres stores the truncated form, drizzle keeps comparing against its own longer + one, and every `db:push` drops and re-adds constraints that are already identical. Four statements per + push, on every host, for ever. + + It read as harmless noise until a deploy plan said "expect no changes" and this made that impossible + to satisfy on all five hosts, forcing the deploy agent to overrule its own instructions. + */ + foreignKey({ + name: 'fk_atelier_collection_items_collection', + columns: [table.collectionId], + foreignColumns: [collections.id], + }).onDelete('cascade'), + foreignKey({ + name: 'fk_atelier_collection_items_generation', + columns: [table.generationId], + foreignColumns: [generations.id], + }).onDelete('cascade'), // The composite key is what makes adding an image twice a no-op instead of a duplicate. primaryKey({ columns: [table.collectionId, table.generationId] }), index('atelier_collection_items_generation_idx').on(table.generationId),