Repository navigation
Description
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
- Start the API on an empty database (
pnpm dev:testinapps/apiwill do). - Before setup, list the indexes on
UserModel: the uniqueUserModel_username_keyis there. POST /v1/setupwith valid$InitAppOptions.- List the indexes on
UserModelagain.
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 runlistIndexesonUserModelandGroupModelthrough the Prisma client and expect the unique indexes. Unit tests mock Prisma, so only this harness shows what a realdropDatabasedoes.- E2E: no route exposes indexes. One option is a spec that sends two concurrent
POST /v1/groupsrequests 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.
Activity