Skip to content

A newly set-up instance runs without its MongoDB indexes until the API restarts, because setup drops the database after boot built them #1882

Open

Description

@joshunrau

A newly set-up instance runs with no MongoDB indexes until the API restarts. ensureDatabaseIndexes builds every index in DATABASE_INDEXES once, when the Prisma client is created at boot. SetupService.initApp, which serves POST /v1/setup, then drops the whole database before it creates the admin. Dropping a database also drops its indexes. Every collection written after that is recreated on first insert with only _id_.

Until the next restart, the instance enforces none of the schema's unique constraints: UserModel.username, GroupModel.name, InstrumentRepoModel.url and PermissionSetModel.name, plus the InstrumentRecordModel.assignmentId index. Every query those indexes exist for scans the whole collection. The services check for duplicates before they insert (for example GroupsService.create calls exists({ name })), so a duplicate needs two concurrent requests. The index is the only thing that stops that race. A Docker deployment that is set up once and never restarted stays in this state indefinitely. The same thing happens on every re-initialisation in development, which initApp allows when NODE_ENV=development.

The Playwright stack runs this way too: testing/src/global/setup.spec.ts posts /v1/setup after the API has booted, so no e2e test ever runs against the indexes.

Where

apps/api/src/setup/setup.service.ts:99 (initApp) and :49 (delete, test-only) both call:

private async dropDatabase(): Promise<void> {
  const result = await this.prismaClient.$runCommandRaw({ dropDatabase: 1 });
  ...
}

apps/api/src/core/prisma.ts:190: the only call to ensureDatabaseIndexes, in the client factory, right after $connect():

await client.$connect();
await ensureDatabaseIndexes(client);

Introduced with startup index creation in 672eaab (v2.2.3). Every instance set up since then started without indexes and got them only at its first restart.

Reproduce

  1. Start the API on an empty database (pnpm dev:test in apps/api will do).
  2. Before setup, list the indexes on UserModel: the unique UserModel_username_key is there.
  3. POST /v1/setup with valid $InitAppOptions.
  4. List the indexes on UserModel again.

Actual: only _id_. The same holds for every other collection in DATABASE_INDEXES.
Expected: setup leaves the database with every index ensureDatabaseIndexes builds at boot.

Tests

  • apps/api/src/setup/__tests__/setup.service.test.ts: it('should rebuild the database indexes after dropping the database, so a newly set-up instance enforces its unique constraints'). Assert the order: drop, then indexes, then the admin is created.
  • apps/api/test/suites/: boot the app, POST /v1/setup, then run listIndexes on UserModel and GroupModel through the Prisma client and expect the unique indexes. Unit tests mock Prisma, so only this harness shows what a real dropDatabase does.
  • E2E: no route exposes indexes. One option is a spec that sends two concurrent POST /v1/groups requests with the same name after the global setup and expects exactly one group to be created. Today both can succeed. Whether that is reliable enough, or whether the integration suite counts as the e2e coverage here, is the implementer's call to raise in the PR.

Suggested fix

Rebuild the indexes inside dropDatabase, right after the drop succeeds, so every path that drops the database, delete included, restores them. src/core/prisma.ts already exports ensureDatabaseIndexes, and it takes a RawCommandRunner, which the injected client satisfies. Running it before createAdmin also means the admin and demo inserts happen with the indexes in place.

Found while working on #1571 (PR #1878).

Activity

  1. added
    BugType: existing behavior is wrong
    Difficulty: LowIsolated change, about 2 hours or less
    Area: APIapps/api (NestJS backend, Prisma models, CASL permissions)
    PerformanceSpeed, memory or scalability
    on Nov 9, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    Area: APIapps/api (NestJS backend, Prisma models, CASL permissions)BugType: existing behavior is wrongDifficulty: LowIsolated change, about 2 hours or lessPerformanceSpeed, memory or scalabilityPriority: MediumReal value; schedule normally

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions