Skip to content

Slow performance during create with CITEXT column #26400

@Christian-Barnowski

Description

@Christian-Barnowski

Bug description

Not sure if I'm doing something wrong but create and createMany on a table with a CITEXT[] column is much slower than with a TEXT[] column.
On my machine it's about 10 times slower (~30 times slower when the database is running in Docker). I did expect CITEXT to be a bit slower but not by that much. I see the same with CITEXT columns that are not arrays but not quite as severe.

There is barely any performance difference for queries and updates between CITEXT and TEXT.

I tried executing the SQL generated by Prisma in node-postgres directly and didn't see this difference in performance there.

When CITEXT property is undefined and the column is nullable the difference disappears.
When I set @default([]) on the column in the Prisma schema and do the same I observe decreased performance again.
Which is not that surprising but when I don't set a default in the Prisma schema and then manually set one in the database, the performance improves again.

The SQL generated by Prisma looks fine to me.

BEGIN
DEALLOCATE ALL
INSERT INTO "public"."Foo" ("name","tags") VALUES ($1,$2), ($3,$4), ($5,$6), ($7,$8), ($9,$10), ($11,$12), ($13,$14), ($15,$16), ($17,$18), ($19,$20)
COMMIT

How to reproduce

  1. have a model with a Citext property and one without
  2. citext extension installed in the database
  3. do something like createMany({ data: [{ citextProperty: [] }, ...] }) and measure the time it takes
  4. do the same for the model where the property is just a regular text
    See "Prisma information" for an example

Expected behavior

No response

Prisma information

generator client {
  provider = "prisma-client-js"
}

datasource db {
  provider = "postgresql"
  url      = env("DATABASE_URL")
}

model Foo {
  id   Int      @id @default(autoincrement())
  name String
  tags String[] @db.Citext
}

model Bar {
  id   Int      @id @default(autoincrement())
  name String
  tags String[]
}
import { PrismaClient } from '@prisma/client';
import { performance } from 'perf_hooks';

const prisma = new PrismaClient();
const elementCount = 1000;

async function main() {
  const foos: any[] = [];
  const bars: any[] = [];

  for (let i = 0; i < elementCount; i++) {
    foos.push({ name: `foo${i}`, tags: [] });
    bars.push({ name: `bar${i}`, tags: [] });
  }

  const createFoos = () => prisma.foo.createMany({ data: foos });
  const createBars = () => prisma.bar.createMany({ data: bars });
  await stopwatch(createFoos);
  await stopwatch(createBars);

  await prisma.foo.deleteMany();
  await prisma.bar.deleteMany();
}

async function stopwatch(fn: () => Promise<any>) {
  const start = performance.now();
  await fn();
  const end = performance.now();
  console.log(`Time ${fn.name}: ${end - start}ms`);
}

main()
  .then(async () => {
    await prisma.$disconnect();
  })
  .catch(async (e) => {
    console.error(e);
    await prisma.$disconnect();
    process.exit(1);
  });

(order of createFoos and createBars can also be reversed to rule out that the additional time is caused by overhead of initializing the connection at the first call)

Environment & setup

  • OS: Windows 10
  • Database: PostgreSQL 17.3
  • Node.js version: v22.12.0

Prisma Version

prisma                  : 6.3.1
@prisma/client          : 6.3.1

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions