Implement incremental sync and offline recovery

This commit is contained in:
diyaa
2026-06-15 22:31:23 +02:00
parent fa7727d572
commit 295c6c1d9b
25 changed files with 2293 additions and 494 deletions
@@ -0,0 +1,128 @@
ALTER TABLE "users"
ADD COLUMN "library_cursor" BIGINT NOT NULL DEFAULT 0;
ALTER TABLE "library_events"
ADD COLUMN "cursor" BIGINT,
ADD COLUMN "payload" JSONB;
WITH ranked_events AS (
SELECT
"id",
ROW_NUMBER() OVER (
PARTITION BY "user_id"
ORDER BY "created_at" ASC, "id" ASC
)::BIGINT AS next_cursor
FROM "library_events"
)
UPDATE "library_events" AS "event"
SET "cursor" = ranked_events.next_cursor
FROM ranked_events
WHERE ranked_events."id" = "event"."id";
UPDATE "library_events" AS "event"
SET "payload" = CASE
WHEN track."status" = 'DELETED'::"TrackStatus" THEN jsonb_build_object(
'deletedTrackId',
track."id"
)
WHEN audio_asset."id" IS NOT NULL THEN jsonb_build_object(
'track',
jsonb_build_object(
'trackId',
track."id",
'title',
track."title",
'artist',
track."artist",
'durationSeconds',
GREATEST(
0,
ROUND(COALESCE(track."duration_ms", audio_asset."duration_ms", 0)::numeric / 1000.0)
)::INTEGER,
'sha256',
audio_asset."sha256",
'assetId',
audio_asset."id",
'createdAt',
to_char(track."created_at" AT TIME ZONE 'UTC', 'YYYY-MM-DD"T"HH24:MI:SS.MS"Z"'),
'updatedAt',
to_char(track."updated_at" AT TIME ZONE 'UTC', 'YYYY-MM-DD"T"HH24:MI:SS.MS"Z"'),
'artwork',
CASE
WHEN artwork_asset."id" IS NULL THEN NULL
ELSE jsonb_build_object(
'artworkId',
artwork_asset."id",
'sha256',
artwork_asset."sha256",
'mimeType',
artwork_asset."mime_type",
'width',
artwork_asset."width",
'height',
artwork_asset."height"
)
END
)
)
ELSE '{}'::jsonb
END
FROM "tracks" AS track
LEFT JOIN "audio_assets" AS audio_asset
ON audio_asset."id" = track."primary_audio_asset_id"
LEFT JOIN "artwork_assets" AS artwork_asset
ON artwork_asset."id" = track."artwork_asset_id"
WHERE "event"."entity_type" = 'TRACK'::"EntityType"
AND "event"."entity_id" = track."id";
UPDATE "library_events"
SET "payload" = '{}'::jsonb
WHERE "payload" IS NULL;
ALTER TABLE "library_events"
ALTER COLUMN "cursor" SET NOT NULL,
ALTER COLUMN "payload" SET NOT NULL;
CREATE UNIQUE INDEX "library_events_user_id_cursor_key"
ON "library_events"("user_id", "cursor");
CREATE INDEX "library_events_user_id_cursor_idx"
ON "library_events"("user_id", "cursor");
UPDATE "users" AS "user"
SET "library_cursor" = COALESCE(cursor_summary."max_cursor", 0)
FROM (
SELECT
"user_id",
MAX("cursor") AS "max_cursor"
FROM "library_events"
GROUP BY "user_id"
) AS cursor_summary
WHERE cursor_summary."user_id" = "user"."id";
ALTER TABLE "device_sync_cursors"
ADD COLUMN "user_id" UUID,
ADD COLUMN "cursor" BIGINT NOT NULL DEFAULT 0;
UPDATE "device_sync_cursors" AS "sync_cursor"
SET
"user_id" = device."user_id",
"cursor" = "sync_cursor"."last_event_id"
FROM "devices" AS device
WHERE device."id" = "sync_cursor"."device_id";
ALTER TABLE "device_sync_cursors"
ALTER COLUMN "user_id" SET NOT NULL;
ALTER TABLE "device_sync_cursors"
DROP COLUMN "last_event_id",
DROP COLUMN "last_full_sync_at";
CREATE INDEX "device_sync_cursors_user_id_idx"
ON "device_sync_cursors"("user_id");
ALTER TABLE "device_sync_cursors"
ADD CONSTRAINT "device_sync_cursors_user_id_fkey"
FOREIGN KEY ("user_id") REFERENCES "users"("id")
ON DELETE CASCADE
ON UPDATE CASCADE;