Skip to content

fetch exposures from counter in db instead of scanning and counting - #3420

Open
danoswaltCL wants to merge 1 commit into
devfrom
wip/feature-flag-exposure-count
Open

danoswaltCL wants to merge 1 commit into
devfrom
wip/feature-flag-exposure-count

Conversation

@danoswaltCL

Copy link
Copy Markdown
Collaborator
  • creates Feature Flag Exposure Count table with just flag reference and a counter
  • increments counter on any new successful insert to the exposures table
  • rewires ff paginated call to use this for gathering the count instead of scanning the table to count exposures each time

The migration backfills existing counts.

Also to note this should have minimal impact even though it moves a db operation to a frequent call; only first-time callers will be registering an exposure and thus incrementing the counter, which can't be much added work, but we should keep it in my for performance tests.

@danoswaltCL
danoswaltCL requested review from bcb37 and a balanced review from Copilot November 11, 2026 19:42

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

Exposure counters can permanently undercount because synchronized databases skip the backfill and legacy writers can add exposures during or after backfilling without updating the counter.

2 open issues
What changed in this PR

Replaces exposure-table scans in paginated feature-flag reads with maintained per-flag counters to reduce list latency.

Changes:

  • Adds a counter entity and migration that backfills existing exposure totals.
  • Atomically increments counters for newly inserted exposures and uses them in paginated responses.
  • Updates unit tests and documents counter consistency and repair procedures.
File Description
packages/​backend/​test/​unit/​services/​FeatureFlagService.test.ts Tests count attachment and zero defaults.
packages/​backend/​test/​unit/​repositories/​FeatureFlagExposureRepository.test.ts Tests SQL construction, counter lookup, and empty inputs.
packages/​backend/​src/​database/​migrations/​1794418860625-featureFlagExposureCount.ts Creates and backfills the counter table with cascade deletion.
packages/​backend/​src/​api/​services/​FeatureFlagService.ts Reads maintained counts for paginated flags.
packages/​backend/​src/​api/​repositories/​FeatureFlagExposureRepository.ts Adds atomic exposure/count writes and counter lookups.
packages/​backend/​src/​api/​models/​FeatureFlagExposureCount.ts Defines the separate per-flag counter entity.
packages/​backend/​src/​api/​models/​FeatureFlag.ts Converts exposureCount to a plain response property.
packages/​backend/​CLAUDE.md Documents counter maintenance, invariants, and reconciliation.

Review effort: Balanced

return {
...featureFlag,
// A flag with no exposures yet has no counter row
exposureCount: exposureCounts.get(featureFlag.id) ?? 0,
Comment on lines +25 to +28
INSERT INTO "feature_flag_exposure_count" ("featureFlagId", "count")
SELECT "featureFlagId", COUNT(*)::int
FROM "feature_flag_exposure"
GROUP BY "featureFlagId"

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

wut

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants