notiz-dev / nestjs-prisma-starter

Starter template for NestJS 😻 includes GraphQL with Prisma Client, Passport-JWT authentication, Swagger Api and Docker
MIT License
2.36k stars 337 forks source link

This line opens a new connection for every service that includes prisma. #438

Closed raminnoodle closed 3 years ago

raminnoodle commented 3 years ago

https://github.com/fivethree-team/nestjs-prisma-starter/blob/c029a2a52ef3c9f21572629ac9b298a0d8c25594/src/services/prisma.service.ts#L12

I noticed at the start of the web server that it would take 5+ seconds and was taking longer and longer with every module I added to my project. It seems it tried to connect oninit for each service that uses this service. When I removed this line the project started under 1 second and Prisma has lazy loading connection that auto connects if no connection is established.

I see this in other docs and am wondering why its setup this way if it tries to front-load so many connections?

marcjulian commented 3 years ago

@raminnoodle This is what I have found on the docs about connection management in the Prisma docs.

It is not necessary to call prisma.$connect() thanks to the lazy connect behavior: The PrismaClient instance connects lazily when the first request is made to the API ($connect() is called for you under the hood).
If you need the first request to respond instantly and cannot wait for a lazy connection to be established, you can explicitly call prisma.$connect() to establish a connection to the data source: https://www.prisma.io/docs/concepts/components/prisma-client/working-with-prismaclient/connection-management#calling-connect-explicitly

It might be a good time to review the PrismaService and perhaps even remove the explicit connect call as it is slowing down the application. We added the explicit connect at the where it was still callled Photon and I am not sure anymore if lazy connect was already available (https://github.com/marcjulian/nest-prisma2/pull/1).

raminnoodle commented 3 years ago

My start up time went from 5630ms to 334ms and sql connections went from 17 at startup to 1 after removing it.

marcjulian commented 3 years ago

I added the log option info for the prisma client. Printing out all started connections.

import { Injectable, OnModuleInit, OnModuleDestroy } from '@nestjs/common';
import { PrismaClient } from '@prisma/client';

@Injectable()
export class PrismaService
  extends PrismaClient
  implements OnModuleInit, OnModuleDestroy {
  constructor() {
    super({log: ['info']});
  }
  async onModuleInit() {
    await this.$connect();
  }

  async onModuleDestroy() {
    await this.$disconnect();
  }
}

PrismaService provided for other modules

Start up with explicit connect. PrismaService added to providers in three modules. For each module a connection pool is started, because of the explicit OnModuleInit hook.

Screenshot 2021-06-09 at 09 29 34

Start up with lazy connect, commentted out await this.$connect(); . First query creates the connection. Only one connection pool is started for the first query, other queries are using the existing connection pool.

Screenshot 2021-06-09 at 09 57 35

PrismaModule imported in other modules

Refactored PrismaService to be exported in PrismaModule. Now PrismaModule can be added to the imports of other modules.

import { Module } from '@nestjs/common';
import { PrismaService } from './prisma.service';

@Module({
  providers: [PrismaService],
  exports: [PrismaService],
})
export class PrismaModule {}

Start up with explicit connect. PrismaModule added to imports in three modules. One connection pool is explicitly started in the OnModuleInit hook. It is only started once as the PrismaService is only added to the PrismaModule and the PrismaModule is imported in to other modules.

Screenshot 2021-06-09 at 10 01 32

Start up with lazy connect, commentted out await this.$connect(); . First query creates the connection. Only one connection pool is started for the first query, other queries are using the existing connection pool.

Screenshot 2021-06-09 at 10 06 03

Conclusion

Starting an explicit connection with PrismaClient slows down the start up time of an application and creates multiple connection pools, if the PrismaService is added to providers for multiple modules. However, if the PrismaClient is added to PrismaModule and this module is imported into other modules, than explicit connection will only create one connection pool for the whole application.

If you need the first request to respond instantly and cannot wait for a lazy connection to be established, you can explicitly call prisma.$connect() to establish a connection to the data source:

If you don't need an instant response for the first request you can rely on Prisma to etablish a lazy connection for you. This is how PrismaService would look like for a lazy connection

import { Injectable } from '@nestjs/common';
import { PrismaClient } from '@prisma/client';

@Injectable()
export class PrismaService extends PrismaClient {
  constructor() {
    super();
  }
}
mikeplis commented 3 years ago

Removing that line also fixes an open handlers issue I was having with my e2e tests