P
Prisma•11mo ago
Jonathan

Using Prisma with Azure SQL Hyperscale

Is anyone using Prisma with Azure SQL Hyperscale? I am running into an issue that when the sql azure scales up or down there is a massive slow down in the application and errors from disconnections of the connection pooling. I am curious how others may have solved this. In .NET's EF they had automatic retry logic but is there an equavaliant in Prisma?
8 Replies
Prisma AI Help
Prisma AI Help•11mo 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!
Jonathan
JonathanOP•11mo ago
#ask-ai
Nurul
Nurul•11mo ago
Hey Jonathan 👋 I haven’t used Azure SQL Hyperscale personally. Based on your use case, I think you can use $connect explicitly, whenever you get a disconnection error. Would something like that work? https://www.prisma.io/docs/orm/prisma-client/setup-and-configuration/databases-connections/connection-management#calling-connect-explicitly
Connection management | Prisma Documentation
This page explains how database connections are handled with Prisma Client and how to manually connect and disconnect your database.
Jonathan
JonathanOP•8mo ago
I have struggled with this also, have you found a good solution? Wait, I just tried to post on my own question... what a day
Nurul
Nurul•8mo ago
I haven't used Hyperscale so I am not aware of a solution unfortunately. I would recommend opening a GitHub Issue if the error persists, so that our ORM team can provide their insights. https://github.com/prisma/prisma/issues/
GitHub
Issues · prisma/prisma
Next-generation ORM for Node.js & TypeScript | PostgreSQL, MySQL, MariaDB, SQL Server, SQLite, MongoDB and CockroachDB - Issues · prisma/prisma
Jonathan
JonathanOP•8mo ago
GitHub
Prisma Disconnecting from SQL Azure Hyperscale · Issue #27024 · p...
Bug description When using Azure SQL Hyperscale prisma will get disconnected from the connection pool and cause the application to crash as it is unable to connect to the database. This seems to ha...
Nurul
Nurul•8mo ago
Thank you! 🙌
Jonathan
JonathanOP•8mo ago
Could something like this work:
import { PrismaClient } from "@prisma/client";

const globalForPrisma = globalThis as unknown as {
prisma: PrismaClient;
};

export const prisma =
globalForPrisma.prisma ??
new PrismaClient({
log: ["query", "error", "warn"],
});

if (process.env.NODE_ENV !== "production") globalForPrisma.prisma = prisma;

// Retry logic wrapper
export async function withRetry<T>(
fn: () => Promise<T>,
maxRetries = 3,
delay = 1000
): Promise<T> {
let retries = 0;
while (true) {
try {
return await fn();
} catch (error) {
if (
retries >= maxRetries ||
!(error instanceof Error) ||
!isRetryableError(error)
) {
throw error;
}

console.warn(`Database operation failed, retrying (${retries + 1}/${maxRetries})`);
await new Promise(resolve => setTimeout(resolve, delay * Math.pow(2, retries)));
retries++;
}
}
}

// Helper to determine if an error is retryable
function isRetryableError(error: Error): boolean {
// Azure SQL connection errors that are typically retryable
const retryableErrors = [
'ECONNRESET',
'ETIMEDOUT',
'ECONNREFUSED',
'connect ETIMEDOUT',
'Connection terminated unexpectedly',
'40613', // Database is currently unavailable (Azure scaling)
'40143', // The service has encountered an error processing your request
'49918', // Cannot process request. Not enough resources
'40501', // The service is currently busy
];

return retryableErrors.some(errMsg =>
error.message.includes(errMsg) || (error.stack && error.stack.includes(errMsg))
);
}

// Usage example:
// const users = await withRetry(() => prisma.user.findMany());
import { PrismaClient } from "@prisma/client";

const globalForPrisma = globalThis as unknown as {
prisma: PrismaClient;
};

export const prisma =
globalForPrisma.prisma ??
new PrismaClient({
log: ["query", "error", "warn"],
});

if (process.env.NODE_ENV !== "production") globalForPrisma.prisma = prisma;

// Retry logic wrapper
export async function withRetry<T>(
fn: () => Promise<T>,
maxRetries = 3,
delay = 1000
): Promise<T> {
let retries = 0;
while (true) {
try {
return await fn();
} catch (error) {
if (
retries >= maxRetries ||
!(error instanceof Error) ||
!isRetryableError(error)
) {
throw error;
}

console.warn(`Database operation failed, retrying (${retries + 1}/${maxRetries})`);
await new Promise(resolve => setTimeout(resolve, delay * Math.pow(2, retries)));
retries++;
}
}
}

// Helper to determine if an error is retryable
function isRetryableError(error: Error): boolean {
// Azure SQL connection errors that are typically retryable
const retryableErrors = [
'ECONNRESET',
'ETIMEDOUT',
'ECONNREFUSED',
'connect ETIMEDOUT',
'Connection terminated unexpectedly',
'40613', // Database is currently unavailable (Azure scaling)
'40143', // The service has encountered an error processing your request
'49918', // Cannot process request. Not enough resources
'40501', // The service is currently busy
];

return retryableErrors.some(errMsg =>
error.message.includes(errMsg) || (error.stack && error.stack.includes(errMsg))
);
}

// Usage example:
// const users = await withRetry(() => prisma.user.findMany());

Did you find this page helpful?