You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Nestjs+Prisma+MongoDB偶发ConnectorError(os error 10054)求助

偶发Prisma ConnectorError(os error 10054)问题排查与解决

我正在使用NestJS和MongoDB开发API,遇到一个出现频率极低的偶发问题:调用this.prisma.people.findMany()时触发ConnectorError,错误信息如下:

{
  "status": "error",
  "message": "\nInvalid this.prisma.people.findMany() invocation in\nC:\\ima\\empresas\\datec\\api-official\\src\\modules\\Events\\services\\utils.service.ts:57: 28\n\n 54 }),\n 55 \n 56 // Get attendees by id\n→ 57 this.prisma.people.findMany(\nError in batch request 3: Error occurred during query execution:\nConnectorError(ConnectorError { user_facing_error: None, kind: RawDatabaseError { code: \"unknown\", message: \"An existing connection was forced to be terminated by the remote host. (os error 10054)\" } })\n"
}

我已按照NestJS集成Prisma的官方文档步骤操作,但仍未找到有效的解决方法,以下是相关代码实现:

Prisma Service实现

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

// Interfaces
import { INestApplication, OnModuleInit, OnModuleDestroy } from '@nestjs/common'

@Injectable()
export class PrismaService
  extends PrismaClient
  implements OnModuleInit, OnModuleDestroy
{
  async onModuleInit() {
    await this.$connect()
  }

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

  async enableShutdownHooks(app: INestApplication) {
    this.$on('beforeExit', async () => {
      await app.close()
    })
  }
}

应用启动文件

// Libraries
import { NestFactory } from '@nestjs/core'
import { ValidationPipe } from '@nestjs/common'
import { SwaggerModule, DocumentBuilder } from '@nestjs/swagger'
import { useContainer } from 'class-validator'

// Modules
import { MainModule } from './main.module'

// config
import pk from '@root/package.json'
import { corstOptions } from '@config/cors'
import { PORT, APP_NAME, PUBLIC_URL } from '@config/env'

// Exceptions
import { HttpExceptionFilter } from '@utils/HttpExceptionFilter'
import { PrismaService } from '@root/src/services/prisma'

async function bootstrap() {
  // Create application
  const app = await NestFactory.create(MainModule)

  // Enable cors
  app.enableCors(corstOptions)

  // Use global 'api' prefix, all calls will come after '/api/*'
  app.setGlobalPrefix('api')

  // Globally define custom response
  app.useGlobalFilters(new HttpExceptionFilter())

  // Enable prism on custom validations
  useContainer(app.select(MainModule), { fallbackOnErrors: true })

  // Get service from primsa and enable 'shutdowns'
  const prismaService = app.get(PrismaService)
  await prismaService.enableShutdownHooks(app)

  // Use 'pipe' validation to validate the 'body' structure
  app.useGlobalPipes(
    new ValidationPipe({
      whitelist: true,
      transform: true,
      forbidUnknownValues: true,
      forbidNonWhitelisted: true,
      transformOptions: { enableImplicitConversion: true }
    })
  )

  // Create API documentation
  const config = new DocumentBuilder()
    .addBearerAuth()
    .setTitle(`API | ${APP_NAME}`)
    .setContact(pk.author.name, pk.author.url, pk.author.email)
    .setDescription(pk.description)
    .setVersion(pk.version)
    .build()

  const document = SwaggerModule.createDocument(app, config)
  SwaggerModule.setup('/', app, document)

  // Listen to the application on the PORT defined in the environment variables
  await app.listen(PORT || 0, () => {
    // eslint-disable-next-line no-console
    console.log(
      '\x1b[33m%s\x1b[0m',
      `[INFO] The server has been started at '${PUBLIC_URL}'`
    )
  })
}

bootstrap() // Run application

Event模块UtilsService

// Librarys
import { Prisma } from '@prisma/client'
import { Inject, Injectable } from '@nestjs/common'

// Services
import { PrismaService } from '@services/prisma'

// DTO
import { EventBodyDTO } from '@modules/Events/events.dto'

// Arguments
import { EventsArgs } from '@modules/Events/events.args'

@Injectable()
export class UtilsService {
  @Inject(PrismaService)
  private readonly prisma: PrismaService

  private readonly selects = {
    'id.title': { id: true, title: true },
    'id.fullname': { id: true, fullname: true },
    'id.title.attributes': { id: true, title: true, attributes: true },
    'id.code.description.attributes': {
      id: true,
      code: true,
      description: true,
      attributes: true
    }
  }

  /**
   * Get reminders, types of activities, case, client and attendees of an event
   * @param {EventBodyDTO} payload Event data
   */
  async getEventFields(payload: EventBodyDTO) {
    const [activityType, eventCase, client, assistants, reminders] = 
      await Promise.all([
        // Get an activity type by id
        this.prisma.parameters.findUnique({
          select: this.selects['id.title'],
          where: { id: payload.activityType }
        }),

        // Get a case by id
        this.prisma.expedients.findUnique({
          where: { id: payload.case },
          select: this.selects['id.code.description.attributes']
        }),

        // Get a person by id
        this.prisma.people.findFirst({
          select: this.selects['id.fullname'],
          where: { isClient: true, id: payload.client }
        }),

        // Get attendees by id
        this.prisma.people.findMany({
          select: this.selects['id.fullname'],
          where: {
            isEmployee: true,
            id: { in: payload.assistants }
          }
        }),

        // Get reminders by id
        this.prisma.parameters.findMany({
          select: this.selects['id.title.attributes'],
          where: {
            id: { in: payload.reminders }
          }
        })
      ])

    return {
      reminders: reminders,
      assistants: assistants,
      client: client === null ? {} : client,
      case: eventCase === null ? {} : eventCase,
      activityType: activityType === null ? {} : activityType
    }
  }

  /**
   * Create filters to filter by customer or event attendees
   * @param {EventsArgs} args Arguments to filter
   * @returns {Promise<Prisma.EventsFindManyArgs['where']>} Returns an object with filters
   */
  async createEventFilters(
    args: EventsArgs
  ): Promise<Prisma.EventsFindManyArgs['where']> {
    const filters: Prisma.EventsFindManyArgs['where'] = {
      userId: args.user
    }

    // Filter by customer
    if (typeof args.client === 'string') {
      const clients = await this.prisma.people.findMany({
        where: {
          isClient: true,
          fullname: {
            mode: 'insensitive',
            contains: args.client
          }
        },
        select: {
          id: true,
          fullname: true
        }
      })

      filters.OR = []

      for (const client of clients) {
        filters.OR.push({
          client: { equals: client.id }
        })
      }
    }

    // Filter by attendees
    if (Array.isArray(args.assistants)) {
      const assistants = await this.prisma.people.findMany({
        where: {
          isEmployee: true,
          id: {
            in: args.assistants
          }
        },
        select: {
          id: true,
          fullname: true
        }
      })

      if (!Array.isArray(filters.OR)) {
        filters.OR = []
      }

      filters.OR.push({
        assistants: { hasSome: assistants.map(item => item.id) }
      })
    }

    return filters
  }
}

问题分析与解决建议

  1. 核心错误原因:os error 10054表示远程主机强制关闭了现有连接,通常是MongoDB主动断开了闲置的连接,而Prisma连接池中的连接未被及时回收或重建。

  2. 具体修复方案:

    • 调整Prisma连接池配置:在schema.prisma中添加连接超时和闲置超时参数,让Prisma主动回收闲置连接:
      datasource db {
        provider = "mongodb"
        url      = env("DATABASE_URL")
        pool_timeout = 30 // 连接池获取连接超时时间(秒)
        idle_timeout = 60 // 连接闲置超时时间(秒),需小于MongoDB的maxIdleTimeMS
      }
      
    • 添加连接重试机制:在全局异常过滤器中捕获ConnectorError,针对查询请求进行最多2-3次重试,避免偶发错误导致请求失败。
    • 修复代码语法错误:修正原代码中的拼写错误、括号缺失、查询条件匹配错误等问题,消除潜在的逻辑异常。
    • 检查MongoDB配置:确认MongoDB的maxIdleTimeMS、socketTimeoutMS等参数,确保Prisma的idle_timeout小于MongoDB的闲置超时阈值,避免连接被主动断开。

内容的提问来源于stack exchange,提问作者Husdady

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.31 16:30:46