feat(ab-testing): add indexes to experiment-variant entity (#1223) - #1382
Open
devgbmuyiwa wants to merge 1 commit into
Open
feat(ab-testing): add indexes to experiment-variant entity (#1223)#1382devgbmuyiwa wants to merge 1 commit into
devgbmuyiwa wants to merge 1 commit into
Conversation
…#1223) `experiment-variant.entity.ts` declared no `@Index`, so every lookup on `experiment_variants` forced a sequential scan regardless of table size. Query-path review (ab-testing.service.ts, experiments/experiment.service.ts, analysis/statistical-analysis.service.ts, automation/automated-decision.service.ts, reporting/ab-testing-reports.service.ts) shows every real access to variant data goes through the `experiment` relation (`experimentRepository.findOne({ relations: [...] })`), then filters the loaded array in-memory for the control variant (`isControl`) or the winning variant (`isWinner`). There is no direct `variantRepository.find({ where })` call on any other column. Changes: - src/ab-testing/entities/experiment-variant.entity.ts: add two class-level composite indexes, `@Index(['experiment', 'isControl'])` and `@Index(['experiment', 'isWinner'])`, matching that access shape. A plain `experiment`-only index is intentionally omitted: leftmost-prefix lookup means either composite already serves a bare "all variants for this experiment" query, so a third index would be a redundant duplicate per the issue's acceptance criteria. A standalone index on `isControl` or `isWinner` alone would also be poor: both are low-cardinality booleans with no selectivity without the `experiment` prefix. - src/migrations/1802000000000-add-experiment-variant-indexes.ts: new migration creating the same two indexes via `CREATE INDEX IF NOT EXISTS ... ("experimentId", "isControl"/"isWinner")` (idempotent, matching this repo's established index-migration style, e.g. 1801000000000-add-achievement-indexes.ts), with a `down()` that drops them. Timestamp/class name follow migration-timestamps.spec.ts's rules (unique timestamp, class name suffixed with it). `experimentId` is the column name confirmed by TypeORM's default FK-naming convention, verified against an already-applied migration in this codebase (1750000000000-add-gamification-indexes.ts) since this entity has no custom NamingStrategy or explicit @joincolumn. - Migrations are auto-discovered via the `src/migrations/[0-9]*.{ts,js}` glob in src/config/datasource.ts, so no manual registration was needed. `synchronize` is `false` there, so the migration — not the `@Index` decorators — is what actually creates the indexes on a real database; the decorators keep the entity's schema intent documented and in sync. Validation: node_modules is not installed in this sandbox (only a single stray entry under node_modules/, `jest`/`tsc` not resolvable), so `npm test`, `npm run typecheck`, and `npm run lint` could not be run here. Both files were reviewed by hand against this repo's real conventions: the migration's timestamp/class-name were checked against migration-timestamps.spec.ts's actual assertions, the FK column name was cross-checked against a real applied migration rather than assumed, and the entity's decorator/import syntax mirrors user-achievement.entity.ts's established `@Index([...])` composite-index pattern in this same codebase. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
|
@devgbmuyiwa Great news! 🎉 Based on an automated assessment of this PR, the linked Wave issue(s) no longer count against your application limits. You can now already apply to more issues while waiting for a review of this PR. Keep up the great work! 🚀 |
Contributor
|
Well done on the job done so far! |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #1223
experiment-variant.entity.tsdeclared no@Index, so every lookup onexperiment_variantsforced a sequential scan regardless of table size.Query-path review (ab-testing.service.ts, experiments/experiment.service.ts, analysis/statistical-analysis.service.ts,
automation/automated-decision.service.ts,
reporting/ab-testing-reports.service.ts) shows every real access to variant data goes through the
experimentrelation(
experimentRepository.findOne({ relations: [...] })), then filters the loaded array in-memory for the control variant (isControl) or the winning variant (isWinner). There is no directvariantRepository.find({ where })call on any other column.Changes:
@Index(['experiment', 'isControl'])and@Index(['experiment', 'isWinner']), matching that access shape. A plainexperiment-only index is intentionally omitted: leftmost-prefix lookup means either composite already serves a bare "all variants for this experiment" query, so a third index would be a redundant duplicate per the issue's acceptance criteria. A standalone index onisControlorisWinneralone would also be poor: both are low-cardinality booleans with no selectivity without theexperimentprefix.CREATE INDEX IF NOT EXISTS ... ("experimentId", "isControl"/"isWinner")(idempotent, matching this repo's established index-migration style, e.g. 1801000000000-add-achievement-indexes.ts), with adown()that drops them. Timestamp/class name follow migration-timestamps.spec.ts's rules (unique timestamp, class name suffixed with it).experimentIdis the column name confirmed by TypeORM's default FK-naming convention, verified against an already-applied migration in this codebase (1750000000000-add-gamification-indexes.ts) since this entity has no custom NamingStrategy or explicit @joincolumn.src/migrations/[0-9]*.{ts,js}glob in src/config/datasource.ts, so no manual registration was needed.synchronizeisfalsethere, so the migration — not the@Indexdecorators — is what actually creates the indexes on a real database; the decorators keep the entity's schema intent documented and in sync.Validation: node_modules is not installed in this sandbox (only a single stray entry under node_modules/,
jest/tscnot resolvable), sonpm test,npm run typecheck, andnpm run lintcould not be run here. Both files were reviewed by hand against this repo's real conventions: the migration's timestamp/class-name were checked against migration-timestamps.spec.ts's actual assertions, the FK column name was cross-checked against a real applied migration rather than assumed, and the entity's decorator/import syntax mirrors user-achievement.entity.ts's established@Index([...])composite-index pattern in this same codebase.