does anybody know why i can't insert multiple docu...
# mongodb
p
does anybody know why i can't insert multiple documents at once? i have such an error:
Copy code
125 await Promise.all([
  → 126   prisma.post.create(
    Error in connector: Database error. error code: unknown, error message: Command failed (WriteConflict): WriteConflict error: this operation conflicted with another operation. Please retry your operation or multi-document transaction.)

    123 |   });
I'm trying to insert multiple documents:
Copy code
await Promise.all([
  prisma.post.create({
    data: mockPost1,
  }),
  prisma.post.create({
    data: mockPost2,
  }),
  prisma.post.create({
    data: mockPost3,
  }),
]);
which have such a shape:
Copy code
export const mockPost1 = {
  id: '507f1f77bcf86cd799439011',
  name: 'testPost1',
  description: 'testPost1 description',
  author: {
    connect: {
      id: mockUser1.id,
    },
  },
};

export const mockPost2 = {
  id: '507f1f77bcf86cd799439012',
  name: 'testPost2',
  description: 'testPost2 description',
  author: {
    connect: {
      id: mockUser1.id,
    },
  },
};

export const mockPost3 = {
  id: '507f1f77bcf86cd799439013',
  name: 'testPost3',
  description: 'testPost3 description',
  author: {
    connect: {
      id: mockUser1.id,
    },
  },
};
j
Can you quickly turn this into an issue on GitHub? Would love to take a look but that is usually easier via GitHub.
d
I can only assume the datamodel here, but if author is a to-one connection on Post, and the
author_id
is stored on the Post, then can you try writing the
author_id
directly instead of
connect
? I'm interested if the transactions stop overlapping in that case.
m
I'm unable to reproduce this via
Copy code
import { PrismaClient } from ".prisma/client"
import crypto from 'crypto'

export const user = {
    email: 'email',
}

async function main() {
    const prisma = new PrismaClient()

    // await prisma.user.deleteMany()
    // await prisma.post.deleteMany()
    // await prisma.user.create({data: user})

    let posts = [] as any[]
    for (let i = 0; i < 100; i++) {
        posts.push({
            id: crypto.randomBytes(12).toString('hex'),
            title: 'title',
            published: true,
            author: {
                connect: {
                    email: user.email
                }
            }
        })
    }

    await Promise.all(posts.map(post => prisma.post.create({ data: post })))
}

void main().catch((e) => {
    console.log(e.message)
    process.exit(1)
})
and schema
Copy code
generator client {
  provider        = "prisma-client-js"
  output          = "../node_modules/.prisma/client"
  previewFeatures = ["mongodb"]
}

datasource db {
  provider = "mongodb"
  url      = "<mongodb://root:prisma@localhost:27018/tests2?authSource=admin>"
}

model User {
  id    String  @default(auto()) @map("_id") @id @db.ObjectId
  email String  @unique
  name  String?
  posts Post[]
}

model Post {
  id        String   @default(auto()) @map("_id") @id @db.ObjectId
  createdAt DateTime @default(now())
  updatedAt DateTime @updatedAt
  published Boolean
  title     String
  content   String?
  authorId  String?
  author    User?    @relation(fields: [authorId], references: [id])
}
p
switching to
author_id
didn't help, same issue. but what is more interesting, I never have this issue locally, but I always have it on CI (github actions). but i have the same docker container for mongo and the tests setup is exactly the same.
i used this container
prismagraphql/mongo-single-replica:5.0.3
so i'm not even sure if i can provide a reproducible example 😞
but that's something related to running multiple requests at once, if i change
Copy code
await Promise.all([
  prisma.post.create({
    data: mockPost1,
  }),
  prisma.post.create({
    data: mockPost2,
  }),
  prisma.post.create({
    data: mockPost3,
  }),
]);
to
Copy code
await prisma.post.create({
    data: mockPost1,
  });
  await prisma.post.create({
    data: mockPost2,
  });
  await prisma.post.create({
    data: mockPost3,
  });
it works fine even on CI
m
Hey @PinkiePie would you mind sharing your schema?
If you have the time, would you mind putting up a full reproduction?
The only way I found to get this error was to write the same id concurrently
Copy code
import { PrismaClient } from ".prisma/client"
import crypto from 'crypto'

const post = {
    id: crypto.randomBytes(12).toString('hex'),
    title: 'title',
    published: true,
}

async function main() {
    const prisma1 = new PrismaClient()
    const prisma2 = new PrismaClient()

    void prisma1.post.create({ data: post }).then()
    void prisma2.post.create({ data: post }).then()
}

void main().catch((e) => {
    console.log(e.message)
    process.exit(1)
})
p
will probably do it later this week. i switched to
crypto.randomBytes(12).toString('hex')
from the hardcoded ids - the result is the same, works fine locally, WriteConflict on the CI
as for the schema - i have something like that (removed all unrelated collections from it)
Copy code
generator client {
  provider        = "prisma-client-js"
  previewFeatures = ["mongoDb"]
}

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

model Post {
  id          String @id @default(auto()) @map("_id") @db.ObjectId
  authorId    String @db.ObjectId
  author      User   @relation(fields: [authorId], references: [id], onDelete: Cascade)
  description String
  name        String
}

model User {
  id    String @id @default(auto()) @map("_id") @db.ObjectId
  login String @unique
  name  String

  posts Post[]
}
and the code which fails in tests:
Copy code
export const insertMockData = async () => {
  await prisma.user.deleteMany({});
  await prisma.post.deleteMany({});

  await prisma.user.create({
    data: mockUser1,
  });

  await Promise.all([
    prisma.post.create({
      data: mockPost1,
    }),
    prisma.post.create({
      data: mockPost2,
    }),
    prisma.post.create({
      data: mockPost3,
    }),
  ]);
};
and i also set
--maxWorkers=1
for jest, so it can't be that some other test tries to insert mocks at the same time as well and interfere with it
j
Where are you running your CI @PinkiePie? Can you maybe create a tiny repository with a CI setup that reproduces this? We are seeing internally similar things, but for us it is flaky - so it happens sometimes, and then doesn't when we look at it 😕 We would really love to understand this further and properly.
p
github actions with
prismagraphql/mongo-single-replica:5.0.3
container. here is my config:
Copy code
name: Backend tests pipeline

on: [push]

jobs:
  test_mongo_action:
    runs-on: ubuntu-latest
    name: Test Server/GraphQL
    strategy:
      matrix:
        work-dir: [./lambdas]
        node-version: [16.x]
    steps:
      - name: Checkout repo
        uses: actions/checkout@v2
      - name: Create mongo Docker container
        run: sudo docker run --name test_mongo -d -p 27017:27017 --env MONGO_INITDB_ROOT_USERNAME=prisma --env MONGO_INITDB_ROOT_PASSWORD=prisma --env INIT_WAIT_SEC="10" prismagraphql/mongo-single-replica:5.0.3
      - name: Use Node.js ${{ matrix.node-version }}
        uses: actions/setup-node@v1
        with:
          node-version: ${{ matrix.node-version }}
      - name: Installing Node Modules
        uses: bahmutov/npm-install@v1
      - name: create env file
        run: |
          touch .env
          echo ... >> .env
          echo ... >> .env
          echo ... >> .env
      - name: Generate Prisma Client
        run: yarn build:prisma
        working-directory: ${{ matrix.work-dir }}
      - name: Run backend tests
        id: run_backend_tests
        run: yarn test --silent
        working-directory: ${{ matrix.work-dir }}
I will have time to create a repo no earlier than the end of this week though 😞 I realised one more thing: I use
insertMockData
function from the snippet above in the tests like that:
Copy code
describe('Unlogged user', () => {
  beforeAll(async () => insertMockedData());
I have multiple such sections in the file. And the only first
describe
fails, the rest of the
describe
in the same file works fine! I tried to re-order them, and it is always like that - the first
describe
always fails, and the rest always works fine. So as for now I have temporary workaround:
Copy code
describe('Prisma WriteConflict', () => {
  beforeAll(async () => insertMockedData().catch(() => {}));
  it('workaround', () => {});
});
at the beginning of the file. It's a mystery for me,
insertMockData
cleans up the database in every
describe
and all mocks have exactly the same shape, ids, and so on, why does it fail on the first run, but the same commands work fine later... At least I have a workaround for now
j
That is indeed super weird - but then should be actually a reproduction. WIth the GH Actions workflow above, I think you might only need a minimal test setup in the repo to show this.