Overview

Projects using Prisma Client can be deployed to many different cloud platforms. Given the variety of cloud platforms and different names, it's noteworthy to mention the different deployment paradigms, as they affect the way you deploy an application using Prisma Client.

Deployment Paradigms

Here are two common deployment paradigms for a Node.js based project:

  • Long running process (PaaS): Your Node.js process is continuously running and handles multiple requests. Your application can be deployed to a Platform-as-a-Service like Heroku, as a Docker container to Kubernetes, or as a Node.js process on a virtual machine.
  • Serverless (FaaS): Node.js processes of your application (or subsets of it broken into functions) are started as requests come in.

Each paradigm has different tradeoffs that affect the performance, scalability, and operational costs of your application.

Moreover, the user traffic pattern of your application is also an important factor to consider. For example, any application with consistent user traffic may be better suited for a continuously running paradigm whereas an application with sudden spikes may be better suited for serverless.

Connection handling

Databases have a limit on the number of open connections they can handle. A higher connection limit allows more processes to connect, however, this comes with a significant performance cost on the database side. Moreover, many databases recommended not overpassing a certain connection number. Several approaches can be used to mitigate such problems. Choosing the right approach depends on the deployment paradigm.

From a high level, the following approaches can prevent database connection issues:

  • Limiting the connection pool of the query engine with the database as described in connection management
  • Using an external connection pooler like PgBouncer.

Both approaches prevent exhausting the connection limit of the database.

Setting the connection limit of the Prisma query engine

When the Prisma Client connects to the database, the query engine immediately creates a connection pool with the number of connections that were specified as the connection_limit parameter in your database connection URL.

For example, with the following datasource configuration in your Prisma schema the connection pool will have exactly five connections:

datasource db {
provider = "postgresql"
url = "postgresql://johndoe:mypassword@localhost:5432/mydb?connection_limit=5"
}

If the connection_limit argument is omitted, the default number of connections is calculated according to this formula: num_physical_cpus * 2 + 1 where num_physical_cpus represents the number of physical CPUs on your machine.

datasource db {
provider = "postgresql"
url = "postgresql://johndoe:mypassword@localhost:5432/mydb"
}

If your machine four physical CPUs, your connection pool will contain nine connections (4 * 2 + 1 = 9).

  • Long running process (PaaS): It's recommended to use the default of num_physical_cpus * 2 + 1. That number should be multiplied by the number of application instances to ensure it's under the database's limit.
  • Serverless (FaaS): It's recommended to set the connection limit to 1 because each incoming request will start a short-lived Node.js process. This can cause the database connection pool to be quickly exhausted from a short spike in user traffic.

PostgreSQL

postgresql://USER:PASSWORD@HOST:PORT/DATABASE?connection_limit=1

MySQL

mysql://USER:PASSWORD@HOST:PORT/DATABASE?connection_limit=1

Serverless (FaaS)

Serverless environments have the concept of warm starts which means that for subsequent invocations of the same function it may use an already existing container that has the allocated processes, memory, file system (/tmp is writable on AWS Lambda), and even DB connection still available.

Typically, any piece of code outside the handler remains initialized. This is a great place for PrismaClient to call connect or at least call PrismaClient constructor so that subsequent invocations can share a connection. There are some implications though that are not directly related to Prisma Client JS but any system that would require a DB connection from serverless environment:

ImplicationDescriptionPotential Solution
Container reuseIt is not guaranteed that subsequent nearby invocations of a function will hit the same container. AWS can choose to create a new container at any time.Code should assume the container to be stateless and create a connection only if it does not exist. Prisma Client JS already implements that logic.
Zombie connectionsThe containers that are marked to be removed and are not being reused still keep a connection open and can stay in that state for some time (unknown and not documented from AWS), this can lead to sub-optimal utilization of the DB connectionsOne potential solution is to use a lower idle connection timeout. Another solution can be to clean up the idle connections in a separate service1, 2.
Connection pooling issuesConcurrent requests might spin up separate containers i.e. new connections. This makes connection pooling a bit difficult to manage because if there is a pool of size N and C concurrent containers, the effective number of connections is N * C. It is very easy to exhaust max_connection limits of the underlying data sourceCan be handled by limiting the concurrency levels of a Lambda function.

1. Note that these are recommendations and not best practices. These would vary from system to system.
2. serverless-mysql is a library that implements this idea.

Note that depending on your serverless concurrency limit (the number of serverless functions running), you might still exhaust your database's connection limit. This can happen when too many functions are invoked concurrently (i.e. the number of concurrent Lambdas that each hold a DB connection exceeds the connection limit of your database). To prevent this, set your serverless concurrency limit to a number lower than the connection limit of your database (as you might want to be able to connect from another client for other purposes).

Query engine binary

Prisma Client depends on the query engine that is running as a binary on the same host as your application.

The query engine is implemented in Rust and is used by Prisma in the form of executable binary files. The binary is downloaded when prisma generate is called. If the call happens on a different environment when deploying your Prisma-based application to production, you need to ensure that the binaryTarget in the Prisma schema is compatible with your production environment as described in binary targets.

Edit this page on GitHub