P
Prisma•2mo ago
Marius Nowak

onDelete: Cascade slower with Generated PrismaClient (PlanetScale Adapter)

Hello 👋 Performance Issue with PlanetScale Adapter: Generated PrismaClient vs Direct @prisma/client Problem Description We're experiencing significant performance degradation with PlanetScale adapter when using the generated PrismaClient compared to the direct @prisma/client for cascade delete operations. Context - Database: PlanetScale (MySQL) - Prisma Version: 6.16.3 - Adapter: @prisma/adapter-planetscale 6.16.3 - Operation: db.user.delete() with cascade deletes - Environment: Local test (Vitest) and CI/CD (GitHub Actions) Performance Comparison in local - Direct @prisma/client: ~7 seconds - Generated PrismaClient: ~12 seconds
11 Replies
Prisma AI Help
Prisma AI Help•2mo ago
You're in no rush, so we'll let a dev step in. Enjoy your coffee, or drop into #ask-ai if you get antsy for a second opinion!
Marius Nowak
Marius NowakOP•2mo ago
Code Comparison Fast version (direct @prisma/client):
import { Client } from "@planetscale/database";
import { PrismaPlanetScale } from "@prisma/adapter-planetscale";
import { PrismaClient } from "@prisma/client";
import { fetch as undiciFetch } from "undici";

declare global {
var cachedPrisma: PrismaClient;
}

const client = new Client({
url: process.env.DATABASE_URL ?? "",
fetch: undiciFetch,
});
const adapter = new PrismaPlanetScale(client);
const prisma = global.cachedPrisma || new PrismaClient({ adapter });

if (process.env.NODE_ENV === "development") {
global.cachedPrisma = prisma;
}

export const db = prisma;



// inside the function called by user.test.ts
const deletedUser = await db.user.delete({
where: { id },
include: { documents: true }
});
import { Client } from "@planetscale/database";
import { PrismaPlanetScale } from "@prisma/adapter-planetscale";
import { PrismaClient } from "@prisma/client";
import { fetch as undiciFetch } from "undici";

declare global {
var cachedPrisma: PrismaClient;
}

const client = new Client({
url: process.env.DATABASE_URL ?? "",
fetch: undiciFetch,
});
const adapter = new PrismaPlanetScale(client);
const prisma = global.cachedPrisma || new PrismaClient({ adapter });

if (process.env.NODE_ENV === "development") {
global.cachedPrisma = prisma;
}

export const db = prisma;



// inside the function called by user.test.ts
const deletedUser = await db.user.delete({
where: { id },
include: { documents: true }
});
Slow version (generated) (the only change is the import of PrismaClient):
import { Client } from "@planetscale/database";
import { PrismaPlanetScale } from "@prisma/adapter-planetscale";
import { PrismaClient } from "generated/prisma/client"; // only change
import { fetch as undiciFetch } from "undici";

declare global {
var cachedPrisma: PrismaClient;
}

const client = new Client({
url: process.env.DATABASE_URL ?? "",
fetch: undiciFetch,
});
const adapter = new PrismaPlanetScale(client);
const prisma = global.cachedPrisma || new PrismaClient({ adapter });

if (process.env.NODE_ENV === "development") {
global.cachedPrisma = prisma;
}

export const db = prisma;



// inside the function called by user.test.ts
const deletedUser = await db.user.delete({
where: { id },
include: { documents: true }
});
import { Client } from "@planetscale/database";
import { PrismaPlanetScale } from "@prisma/adapter-planetscale";
import { PrismaClient } from "generated/prisma/client"; // only change
import { fetch as undiciFetch } from "undici";

declare global {
var cachedPrisma: PrismaClient;
}

const client = new Client({
url: process.env.DATABASE_URL ?? "",
fetch: undiciFetch,
});
const adapter = new PrismaPlanetScale(client);
const prisma = global.cachedPrisma || new PrismaClient({ adapter });

if (process.env.NODE_ENV === "development") {
global.cachedPrisma = prisma;
}

export const db = prisma;



// inside the function called by user.test.ts
const deletedUser = await db.user.delete({
where: { id },
include: { documents: true }
});
Schema Context The User model has multiple cascade relationships:
model User {
id Int @id @default(autoincrement())
// ... other fields

documents UserDocument[]
comments UserComment[]
// ... other relations with onDelete: Cascade
}

model UserDocument {
id Int @id @default(autoincrement())
userId Int
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
// ... other fields
}
model User {
id Int @id @default(autoincrement())
// ... other fields

documents UserDocument[]
comments UserComment[]
// ... other relations with onDelete: Cascade
}

model UserDocument {
id Int @id @default(autoincrement())
userId Int
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
// ... other fields
}
Environment Details - Local: Both versions work, but generated is slower - CI/CD: generated version times out at 20s (PlanetScale limit)
Marius Nowak
Marius NowakOP•2mo ago
------------------------------------------------ Screen Perfs (Local tests)
No description
Marius Nowak
Marius NowakOP•2mo ago
the test to look at is user creation, editing and deleting tests > should create a user and delete it and its dependencies 1st one : using PrismaClient from generated/prisma/client -> 12 043 ms 2nd one : using PrismaClient from @prisma/client -> 6935 ms Questions - Do you know something about this performance difference ? - Is there anything I did wrong ? - What takes much longer is the single call db.user.delete so I think the problem is the onDelete: Cascade that is much longer with PrismaClient from generated/prisma/client compared to PrismaClient from @prisma/client. What do you think ? Is this a bug ?
Nurul
Nurul•2mo ago
I haven't seen anyone else report this kind of issue before. Do you observe this degraded performance only for delete queries? Are other queries faster in generated PrismaClient?
Marius Nowak
Marius NowakOP•2mo ago
Hello, I share with you the perf differences between new_prisma (generated) & old_prisma (@prisma/client)
Marius Nowak
Marius NowakOP•2mo ago
I asked CURSOR to do a complete analysis about those tests : --------------------------------------------------------------------------- Cursor... Based on my comprehensive analysis of the test results, schema, and codebase, here's my detailed analysis: Test Performance Analysis: Old vs New Prisma Executive Summary The analysis confirms that tests involving onDelete: Cascade operations are significantly slower in the new Prisma version compared to the old version. This performance degradation is most pronounced in deletion tests that trigger complex cascade relationships. Key Findings
Marius Nowak
Marius NowakOP•2mo ago
No description
Marius Nowak
Marius NowakOP•2mo ago
No description
Marius Nowak
Marius NowakOP•2mo ago
3. Cascade Relationship Analysis From the schema analysis, I identified 308 onDelete: Cascade relationships. The most complex cascades involve: User Model (Most Complex) - Direct cascades: 15+ relationships including documents, comments, history, clocking sessions, primes, time off periods, etc. - Indirect cascades: Through related entities like approaches, instances, etc. Fitter Model (Very Complex) - Direct cascades: 20+ relationships including establishments, employees, documents, comments, history, approach pricing, etc. - Indirect cascades: Through establishments → certifications → documents Organism Models (Moderate Complexity) - CityHall/Connector: Relations, comments, documents, history - Fewer cascade levels compared to User/Fitter 4. Why Some Tests Are Unaffected: 1. No Cascade Operations: Tests that only create/edit without deletion 2. Simple Operations: Tests with minimal relationship complexity 3. Different Query Patterns: Read/update operations may not be affected by the cascade deletion changes 5. Specific Test Analysis User Deletion Test (85% slower) - Operation: deleteUser() → db.user.delete() - Cascades: Documents, comments, history, clocking sessions, primes, time off periods, approach types, etc. - Impact: Most complex cascade tree in the system Fitter Deletion Test (208% slower) - Operation: deleteFitterAndReturnEstablishments() → db.fitter.delete() - Cascades: Establishments, employees, documents, comments, history, approach pricing, approach types, etc. - Impact: Second most complex cascade tree Organism Deletion Tests (91% slower for CityHall, minimal for Connector) - CityHall: Relations, comments, documents, history - Connector: Similar structure but fewer relationships 6. Statistics Summary - Total Tests: 70 tests across 23 test files - Tests with Performance Issues: 4 deletion tests (5.7% of total tests) - Average Performance Loss: 96% slower for cascade deletion tests - Tests Unaffected: 66 tests (94.3% of total tests) - Cascade Relationships: 308 total onDelete: Cascade relationships in schema 7. Conclusion The performance degradation is specifically and exclusively related to onDelete: Cascade operations in the new Prisma version. This suggests: 1. Targeted Issue: The problem is not general Prisma performance but specifically cascade deletion logic 2. Scalability Concern: As the database grows, cascade deletions will become increasingly slow 3. Production Impact: This could significantly impact user experience for deletion operations 4. Investigation Needed: The root cause likely lies in how Prisma handles cascade operations in the newer version Would it be interesting if I try to create a little repository that focus on the onDelete cascade with new & old prisma client ? That I can give you so you can easily dig the issue (if the issue does not come from my codebase)
Marius Nowak
Marius NowakOP•2mo ago
GitHub
onDelete slower with new generated Prisma (planetscale adapter) · ...
Bug description I have a model User with multiple relations deleted onCascade when I delete user. Exactly the same action of deleting the user takes twice more time with new Rust-free Prisma than w...

Did you find this page helpful?