-- Physical objects stored in S3. Keys are opaque and never encode album, photo -- or any other metadata entity. The row is the ledger entry that governs the -- lifecycle of the corresponding blob. CREATE TABLE IF NOT EXISTS objects ( id text PRIMARY KEY, storage_key text NOT NULL UNIQUE, mime_type text NOT NULL DEFAULT '', size bigint NOT NULL DEFAULT 0, width integer NOT NULL DEFAULT 0, height integer NOT NULL DEFAULT 0, checksum text NOT NULL DEFAULT '', state text NOT NULL DEFAULT 'pending' CHECK (state IN ('pending', 'ready', 'deleting')), created_at timestamptz NOT NULL DEFAULT now() ); CREATE INDEX IF NOT EXISTS objects_state_created_idx ON objects (state, created_at); -- Binding of a photo to its objects by role. The role is a property of the -- binding, not of the object itself. CREATE TABLE IF NOT EXISTS photo_objects ( photo_id text NOT NULL REFERENCES photos(id) ON DELETE CASCADE, object_id text NOT NULL REFERENCES objects(id) ON DELETE RESTRICT, role text NOT NULL CHECK (role IN ('original', 'thumb', 'preview')), PRIMARY KEY (photo_id, role) ); CREATE INDEX IF NOT EXISTS photo_objects_object_idx ON photo_objects (object_id); -- Durable queue of pending variant generation work, one job per photo. CREATE TABLE IF NOT EXISTS variant_jobs ( photo_id text PRIMARY KEY REFERENCES photos(id) ON DELETE CASCADE, attempts integer NOT NULL DEFAULT 0, next_attempt_at timestamptz NOT NULL DEFAULT now(), last_error text NOT NULL DEFAULT '', created_at timestamptz NOT NULL DEFAULT now() ); CREATE INDEX IF NOT EXISTS variant_jobs_due_idx ON variant_jobs (next_attempt_at);