diff --git a/packages/prisma-client/migrations-cli/TROUBLESHOOTING.md b/packages/prisma-client/migrations-cli/TROUBLESHOOTING.md new file mode 100644 index 0000000000..aaad796e53 --- /dev/null +++ b/packages/prisma-client/migrations-cli/TROUBLESHOOTING.md @@ -0,0 +1,46 @@ +## A migration failed in PREVIEW, how do I fix the PREVIEW database + +1. Open a new PR where you'll do the fix +1. Go to https://vercel.com/webstudio-is/webstudio-designer/settings/environment-variables +1. Set APPLY_MIGRATIONS to "true" for your PR's branch +1. Fix the failed migration code +1. Fix the database mannually if the migration have been applied half-way +1. Temporarily change `ci:migrate` script to run `resolve` before applying migrations: `"ci:migrate": "migrations resolve rolled-back --force && migrations migrate --force"`. Use `applied` instead of `rolled-back` if you manually applied the migration at previous step. +1. Commit the changes and check Vercel logs to see if the migration is applied +1. Change `ci:migrate` script back to normal +1. Merge the PR +1. Remove APPLY_MIGRATIONS from Vercel settings for your branch + +## Transacation failed with a error "Transaction API error: Transaction already closed: Could not perform operation." + +Most likely reason Prisma has thrown this error is because transaction has timed out. +If that's the case, you have the following options: + +1. Optimize your migration to make it faster +2. Increase the timeout: + +```js +export default () => { + const client = new PrismaClient(); + return client.$transaction( + async (prisma) => { + // migration code... + }, + { + timeout: 1000 * 60 * 2, // in milliseconds, default 5000 + } + ); +}; +``` + +3. Do not wrap the migration body into a transaction: + +```js +export default async () => { + const prisma = new PrismaClient(); + // use `prisma` here without wrapping it into a transaction +}; +``` + +Note that in this case if the migration fails again, it will be harder to fix the database, +because the migration can end up half-way applied. diff --git a/packages/prisma-client/migrations-cli/commands.ts b/packages/prisma-client/migrations-cli/commands.ts index 32c8a7868d..d6f8099406 100644 --- a/packages/prisma-client/migrations-cli/commands.ts +++ b/packages/prisma-client/migrations-cli/commands.ts @@ -2,6 +2,7 @@ import fs from "node:fs"; import path from "node:path"; import { FileLocker, MigrationMeta } from "umzug"; import confirm from "@inquirer/confirm"; +import { inspect } from "node:util"; import * as prismaMigrations from "./prisma-migrations"; import { umzug } from "./umzug"; import * as logger from "./logger"; @@ -256,13 +257,18 @@ If you're sure no other process is running, please delete the lockfile: try { await umzug.up(); } catch (err) { + const originalError: unknown = err.cause || err; + const originalErrorString = + (originalError instanceof Error && originalError.stack) || + inspect(originalError); + logger.error(""); - logger.error(err.stack); + logger.error(originalErrorString); logger.error(""); const migrationName = (err.migration || undefined)?.name; if (typeof migrationName === "string") { - prismaMigrations.setFailed(migrationName, err.stack); + prismaMigrations.setFailed(migrationName, originalErrorString); } process.exitCode = 1; diff --git a/packages/prisma-client/prisma/migrations/20220909124542_builds-data/migration.ts b/packages/prisma-client/prisma/migrations/20220909124542_builds-data/migration.ts index 2df896a683..382918738f 100644 --- a/packages/prisma-client/prisma/migrations/20220909124542_builds-data/migration.ts +++ b/packages/prisma-client/prisma/migrations/20220909124542_builds-data/migration.ts @@ -23,46 +23,49 @@ export default () => { // Uncomment to see the queries in console as the migration runs // log: ["query", "info", "warn", "error"], }); - return client.$transaction(async (prisma) => { - const trees = await prisma.tree.findMany(); - const projects = await prisma.project.findMany(); + return client.$transaction( + async (prisma) => { + const trees = await prisma.tree.findMany(); + const projects = await prisma.project.findMany(); - const builds: Prisma.BuildCreateManyInput[] = []; + const builds: Prisma.BuildCreateManyInput[] = []; - for (const tree of trees) { - const project = projects.find( - (project) => - project.devTreeId === tree.id || - project.prodTreeId === tree.id || - TreeHistorySchema.parse( - JSON.parse(project.prodTreeIdHistory) - ).includes(tree.id) - ); + for (const tree of trees) { + const project = projects.find( + (project) => + project.devTreeId === tree.id || + project.prodTreeId === tree.id || + TreeHistorySchema.parse( + JSON.parse(project.prodTreeIdHistory) + ).includes(tree.id) + ); - if (project === undefined) { - continue; + if (project === undefined) { + continue; + } + + const pages = PagesSchema.parse({ + homePage: { + id: uuid(), + name: "Home", + path: "", + title: "Home", + meta: {}, + treeId: tree.id, + }, + pages: [], + }); + + builds.push({ + pages: JSON.stringify(pages), + isDev: project.devTreeId === tree.id, + isProd: project.prodTreeId === tree.id, + projectId: project.id, + }); } - const pages = PagesSchema.parse({ - homePage: { - id: uuid(), - name: "Home", - path: "", - title: "Home", - meta: {}, - treeId: tree.id, - }, - pages: [], - }); - - builds.push({ - pages: JSON.stringify(pages), - isDev: project.devTreeId === tree.id, - isProd: project.prodTreeId === tree.id, - projectId: project.id, - }); - } - - await prisma.build.createMany({ data: builds }); - }); + await prisma.build.createMany({ data: builds }); + }, + { timeout: 1000 * 60 } + ); }; diff --git a/packages/prisma-client/prisma/migrations/20220912142938_assets-meta-data/migration.ts b/packages/prisma-client/prisma/migrations/20220912142938_assets-meta-data/migration.ts index 15ad7112f7..43b7920027 100644 --- a/packages/prisma-client/prisma/migrations/20220912142938_assets-meta-data/migration.ts +++ b/packages/prisma-client/prisma/migrations/20220912142938_assets-meta-data/migration.ts @@ -1,4 +1,3 @@ -import { assert } from "console"; import { PrismaClient } from "./client"; // NOTE ON IMPORTS: @@ -12,22 +11,26 @@ import { PrismaClient } from "./client"; export default () => { const client = new PrismaClient(); - return client.$transaction(async (prisma) => { - const previousAssets = await prisma.asset.findMany(); - const update = previousAssets.map((asset) => { - return { - id: asset.id, - description: asset.alt, - meta: JSON.stringify({ - width: Number(asset.width), - height: Number(asset.height), - }), - }; - }); - await Promise.all( - update.map(({ id, ...data }) => - prisma.asset.update({ where: { id }, data }) - ) - ); - }); + + return client.$transaction( + async (prisma) => { + const previousAssets = await prisma.asset.findMany(); + const update = previousAssets.map((asset) => { + return { + id: asset.id, + description: asset.alt, + meta: JSON.stringify({ + width: Number(asset.width), + height: Number(asset.height), + }), + }; + }); + await Promise.all( + update.map(({ id, ...data }) => + prisma.asset.update({ where: { id }, data }) + ) + ); + }, + { timeout: 1000 * 60 } + ); }; diff --git a/packages/prisma-client/prisma/migrations/20220915144008_breakpoints-build_data/migration.ts b/packages/prisma-client/prisma/migrations/20220915144008_breakpoints-build_data/migration.ts index 0a7eefa820..e6f38ae448 100644 --- a/packages/prisma-client/prisma/migrations/20220915144008_breakpoints-build_data/migration.ts +++ b/packages/prisma-client/prisma/migrations/20220915144008_breakpoints-build_data/migration.ts @@ -20,34 +20,37 @@ export default () => { // Uncomment to see the queries in console as the migration runs // log: ["query", "info", "warn", "error"], }); - return client.$transaction(async (prisma) => { - const builds = await prisma.build.findMany(); - const breakpoints = await prisma.breakpoints.findMany(); + return client.$transaction( + async (prisma) => { + const builds = await prisma.build.findMany(); + const breakpoints = await prisma.breakpoints.findMany(); - const buildsParsed = builds.map((build) => ({ - id: build.id, - pages: PagesSchema.parse(JSON.parse(build.pages)), - })); + const buildsParsed = builds.map((build) => ({ + id: build.id, + pages: PagesSchema.parse(JSON.parse(build.pages)), + })); - await Promise.all( - breakpoints.map((breakpoint) => { - const build = buildsParsed.find( - (build) => build.pages.homePage.treeId === breakpoint.treeId - ); - - if (build === undefined) { - throw new Error( - `Build not found for breakpoint ${breakpoint.treeId}` + await Promise.all( + breakpoints.map((breakpoint) => { + const build = buildsParsed.find( + (build) => build.pages.homePage.treeId === breakpoint.treeId ); - } - return prisma.breakpoints.update({ - where: { treeId: breakpoint.treeId }, - data: { - buildId: build.id, - }, - }); - }) - ); - }); + if (build === undefined) { + throw new Error( + `Build not found for breakpoint ${breakpoint.treeId}` + ); + } + + return prisma.breakpoints.update({ + where: { treeId: breakpoint.treeId }, + data: { + buildId: build.id, + }, + }); + }) + ); + }, + { timeout: 1000 * 60 } + ); }; diff --git a/packages/prisma-client/prisma/migrations/template.txt b/packages/prisma-client/prisma/migrations/template.txt index b63e297a0f..c330e9dabd 100644 --- a/packages/prisma-client/prisma/migrations/template.txt +++ b/packages/prisma-client/prisma/migrations/template.txt @@ -33,5 +33,5 @@ export default () => { // NOTE: // Don't do any schema modifications here like adding a column to a table! // If you need to change schema, edit schema.prisma and create a schema migration. - }); + }, { timeout: 1000 * 60 }); };