Skip to content

feat: Keep host on aggregated API usage buckets - #8471

Open
gagantrivedi wants to merge 8 commits into
mainfrom
feat/host-in-usage-buckets
Open

gagantrivedi wants to merge 8 commits into
mainfrom
feat/host-in-usage-buckets

Conversation

@gagantrivedi

Copy link
Copy Markdown
Member

Changes

Contributes to Flagsmith/flagsmith-private#256

host is written to APIUsageRaw on every tracked request, but the hourly rollup dropped it — aggregated buckets carried no host, so the dimension only survived the raw-data retention window (30 days). With the Edge Proxy reporting its served requests with host="edge-proxy", that dimension is how proxy-served traffic stays distinguishable from direct traffic long-term.

  • APIUsageBucket gains a host column (default "" for existing rows).
  • The rollup (_get_api_usage_source_data) groups by host on both the raw→bucket and bucket→bucket paths, and populate_api_usage_bucket writes it.
  • The overlap check treats host as a dimension alongside resource.
  • Read paths unchanged: usage aggregations still sum across hosts, so the dashboards and usage API behave exactly as before — the data is just no longer thrown away.

FeatureEvaluationBucket is untouched (no host on its raw rows).

How did you test this code?

New test: raw events from two hosts in one bucket window roll up into separate buckets, each keeping its host. Full test_tasks.py (20) and the bucket read-path tests (test_analytics_db_service.py, test_commands.py, 29) pass against Postgres; ruff + mypy clean.

host was written to APIUsageRaw and then dropped by the hourly rollup,
so it only survived the raw-data retention window. Buckets now carry
it: the rollup groups by host and the overlap check treats it as a
dimension. Read paths are unchanged — aggregations still sum across
hosts — so dashboards behave exactly as before while the data stays
available.
@gagantrivedi
gagantrivedi requested a review from a team as a code owner September 5, 2026 02:36
@gagantrivedi
gagantrivedi requested review from matthewelwell and removed request for a team September 5, 2026 02:36
@vercel

vercel Bot commented Sep 5, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

3 Skipped Deployments
Project Deployment Actions Updated
docs Ignored Ignored Preview Sep 19, 2026 7:01am UTC
flagsmith-frontend-preview Ignored Ignored Preview Sep 19, 2026 7:01am UTC
flagsmith-frontend-staging Ignored Ignored Preview Sep 19, 2026 7:01am UTC

Request Review

@github-actions github-actions Bot added the api Issue related to the REST API label Sep 5, 2026
@github-actions

github-actions Bot commented Sep 5, 2026

Copy link
Copy Markdown
Contributor

Docker builds report

Image Build Status Security report
ghcr.io/flagsmith/flagsmith-e2e:pr-8471 Finished ✅ Skipped
ghcr.io/flagsmith/flagsmith-api-test:pr-8471 Finished ✅ Skipped
ghcr.io/flagsmith/flagsmith-frontend:pr-8471 Finished ✅ Results
ghcr.io/flagsmith/flagsmith-api:pr-8471 Finished ✅ Results
ghcr.io/flagsmith/flagsmith:pr-8471 Finished ✅ Results
ghcr.io/flagsmith/flagsmith-private-cloud:pr-8471 Finished ✅ Results

@github-actions github-actions Bot added the feature New feature or request label Sep 5, 2026
@coderabbitai

coderabbitai Bot commented Sep 5, 2026

Copy link
Copy Markdown

Review Change StackReview Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Advanced

Run ID: b545e341-2d3d-4224-9b7c-f8926f92a454

📥 Commits

Reviewing files that changed from the base of the PR and between f0083b9 and 23ce94f.

📒 Files selected for processing (4)
  • api/app_analytics/migrations/0009_apiusagebucket_host.py
  • api/app_analytics/tasks.py
  • api/tests/unit/app_analytics/test_migrations.py
  • api/tests/unit/app_analytics/test_tasks.py

Included review availability: Your plan provides up to 8 included reviews per hour; 7 remain after this review.


📝 Walkthrough

Walkthrough

The API usage bucket model and migration add a host field. Overlap checks and usage aggregation now use resource and host. Bucket creation stores the host and removes recent legacy rows during recomputation. Tests cover separate host buckets, legacy row replacement, empty-host rows, and host-specific totals.

Priority: ⬇️ Low

Estimated code review effort: 2 (Simple) | ~10 minutes


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@codecov

codecov Bot commented Sep 5, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 98.82%. Comparing base (2ce9498) to head (3953790).
⚠️ Report is 53 commits behind head on main.

Additional details and impacted files
@@           Coverage Diff            @@
##             main    #8471    +/-   ##
========================================
  Coverage   98.81%   98.82%            
========================================
  Files        1621     1632    +11     
  Lines       66299    66700   +401     
========================================
+ Hits        65514    65915   +401     
  Misses        785      785            

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@github-actions

github-actions Bot commented Sep 5, 2026

Copy link
Copy Markdown
Contributor
✅ private-cloud · depot-ubuntu-latest-arm-16 — run #20528 (attempt 2)

Playwright Test Results (private-cloud - depot-ubuntu-latest-arm-16)

passed  1 passed

Details

stats  1 test across 1 suite
duration  58.6 seconds
commit  3953790
info  🔄 Run: #20528 (attempt 2)

🗂️ Previous results
❌ private-cloud · depot-ubuntu-latest-arm-16 — run #20528 (attempt 1)

Playwright Test Results (private-cloud - depot-ubuntu-latest-arm-16)

failed  2 failed

Details

stats  2 tests across 2 suites
duration  1 minute, 12 seconds
commit  3953790
info  📦 Artifacts: View test results and HTML report
🔄 Run: #20528 (attempt 1)

Failed tests

firefox › tests/environment-permission-test.pw.ts › Environment Permission Tests › Environment-level permissions control access to features, identities, and segments @enterprise
firefox › tests/project-permission-test.pw.ts › Project Permission Tests › Project-level permissions control access to features, environments, audit logs, and segments @enterprise

✅ private-cloud · depot-ubuntu-latest-16 — run #20528 (attempt 1)

Playwright Test Results (private-cloud - depot-ubuntu-latest-16)

passed  3 passed

Details

stats  3 tests across 3 suites
duration  33.2 seconds
commit  3953790
info  🔄 Run: #20528 (attempt 1)

✅ oss · depot-ubuntu-latest-arm-16 — run #20528 (attempt 1)

Playwright Test Results (oss - depot-ubuntu-latest-arm-16)

passed  1 passed

Details

stats  1 test across 1 suite
duration  39.3 seconds
commit  3953790
info  🔄 Run: #20528 (attempt 1)

✅ oss · depot-ubuntu-latest-16 — run #20528 (attempt 1)

Playwright Test Results (oss - depot-ubuntu-latest-16)

passed  1 passed

Details

stats  1 test across 1 suite
duration  34.2 seconds
commit  3953790
info  🔄 Run: #20528 (attempt 1)

✅ private-cloud · depot-ubuntu-latest-arm-16 — run #20527 (attempt 1)

Playwright Test Results (private-cloud - depot-ubuntu-latest-arm-16)

passed  4 passed

Details

stats  4 tests across 4 suites
duration  4.2 seconds
commit  23ce94f
info  🔄 Run: #20527 (attempt 1)

✅ private-cloud · depot-ubuntu-latest-16 — run #20527 (attempt 1)

Playwright Test Results (private-cloud - depot-ubuntu-latest-16)

passed  4 passed

Details

stats  4 tests across 4 suites
duration  55.8 seconds
commit  23ce94f
info  🔄 Run: #20527 (attempt 1)

✅ oss · depot-ubuntu-latest-arm-16 — run #20527 (attempt 1)

Playwright Test Results (oss - depot-ubuntu-latest-arm-16)

passed  1 passed

Details

stats  1 test across 1 suite
duration  37.5 seconds
commit  23ce94f
info  🔄 Run: #20527 (attempt 1)

✅ oss · depot-ubuntu-latest-16 — run #20527 (attempt 1)

Playwright Test Results (oss - depot-ubuntu-latest-16)

passed  1 passed

Details

stats  1 test across 1 suite
duration  33.7 seconds
commit  23ce94f
info  🔄 Run: #20527 (attempt 1)

✅ private-cloud · depot-ubuntu-latest-arm-16 — run #20526 (attempt 1)

Playwright Test Results (private-cloud - depot-ubuntu-latest-arm-16)

passed  1 passed

Details

stats  1 test across 1 suite
duration  1 minute, 13 seconds
commit  2a81cb7
info  🔄 Run: #20526 (attempt 1)

✅ private-cloud · depot-ubuntu-latest-16 — run #20526 (attempt 1)

Playwright Test Results (private-cloud - depot-ubuntu-latest-16)

passed  2 passed

Details

stats  2 tests across 2 suites
duration  48.1 seconds
commit  2a81cb7
info  🔄 Run: #20526 (attempt 1)

✅ oss · depot-ubuntu-latest-16 — run #20526 (attempt 1)

Playwright Test Results (oss - depot-ubuntu-latest-16)

passed  1 passed

Details

stats  1 test across 1 suite
duration  33.7 seconds
commit  2a81cb7
info  🔄 Run: #20526 (attempt 1)

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 2


ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Team

Run ID: ccf868b1-727f-4ea5-a929-b42caf2afa63

📥 Commits

Reviewing files that changed from the base of the PR and between 2ce9498 and 2f7671e.

📒 Files selected for processing (4)
  • api/app_analytics/migrations/0009_apiusagebucket_host.py
  • api/app_analytics/models.py
  • api/app_analytics/tasks.py
  • api/tests/unit/app_analytics/test_tasks.py

Included review availability: Your plan provides up to 8 included reviews per hour; 7 remain after this review.

Comment thread api/app_analytics/tasks.py
Comment thread api/tests/unit/app_analytics/test_tasks.py
@github-actions

github-actions Bot commented Sep 5, 2026

Copy link
Copy Markdown
Contributor

Visual Regression

19 screenshots compared. See report for details.
View full report

…computed

Buckets written before host existed hold every host's count under "".
Recomputing such a window from raw data now removes that row before
writing the per-host rows, instead of adding to it and double counting.
Also covers host grouping on the bucket-to-bucket path.
@github-actions github-actions Bot added feature New feature or request and removed feature New feature or request labels Sep 12, 2026

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 1


ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Advanced

Run ID: d0e764ab-4c02-4955-8026-e83b172f8c0d

📥 Commits

Reviewing files that changed from the base of the PR and between 2f7671e and 95b2988.

📒 Files selected for processing (2)
  • api/app_analytics/tasks.py
  • api/tests/unit/app_analytics/test_tasks.py

Included review availability: Your plan provides up to 8 included reviews per hour; 7 remain after this review.

Comment thread api/app_analytics/tasks.py Outdated
@github-actions github-actions Bot added feature New feature or request and removed feature New feature or request labels Sep 12, 2026
A raw group with host "" processed before a real host would have had
its freshly written bucket deleted as legacy.
@github-actions github-actions Bot added feature New feature or request and removed feature New feature or request labels Sep 12, 2026

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 1


ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Advanced

Run ID: ad885eef-d3e0-446d-923c-d2ab71903f2c

📥 Commits

Reviewing files that changed from the base of the PR and between c4ada09 and f0083b9.

📒 Files selected for processing (2)
  • api/app_analytics/tasks.py
  • api/tests/unit/app_analytics/test_tasks.py

Included review availability: Your plan provides up to 8 included reviews per hour; 5 remain after this review.

Comment thread api/app_analytics/tasks.py Outdated

class APIUsageBucket(AbstractBucket):
resource = models.IntegerField(choices=Resource.choices)
host = models.CharField(max_length=255, default="")

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.

Should we make this field nullable to make the migration easier?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

That used to be the case up to Postgres 11. For Postgres 11 and later, adding a column with a default value that's the same for every row only changes the table metadata

Comment thread api/app_analytics/tasks.py Outdated
Comment on lines +191 to +195
# Buckets created before the `host` column existed hold the
# window's whole count under host "". Raw data recomputes that
# window per host with the same total, so the old row is removed
# rather than left to double the window. Removed before any upsert,
# so a row this pass writes under host "" is never taken for old.

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.

This reads as slop to me... Can we rewrite it for human consumption? It also feels like we're handling something in runtime code that maybe would be better handled in a migration (if that's possible)?

Thinking about it, I guess the main thing that's missing here for me, is why old buckets need to be affected at all. As I understood, once data was bucketed, it shouldn't need to be touched again?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

okay, this is a bit complicated okay so the thing is since our bucketing task runs every 60 mins and we create buckets for every 15 mins now for a normal case each run rebuilds the last four completed 15 min buckets, so the next run recomputes most of the buckets the previous run already wrote and just overwrites them the problem that we are facing now with host is that since we are bucketing on host as well now those buckets will be counted twice(once for the actual host and once for host="") if we did not have this janky code now I dont know if that's acceptable or not I lean on the side of not so now I have moved it to migration that will delete the buckets of the last two hours so that we dont count them twice it can loose some data but it will not count it twice and it's out of the code path let me know if that makes any sense

@gagantrivedi gagantrivedi Sep 19, 2026

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

I guess the main thing that's missing here for me, is why old buckets need to be affected at all. As I understood, once data was bucketed, it shouldn't need to be touched again?

In principle yes, but the task is deliberately stateless: it doesn't remember what it processed, it just rebuilds the last hour of completed 15 min buckets whenever it runs. That means a late or failed run heals itself, and raw rows that land a bit late get picked up on the next rebuild.

) -> None:
# Given a window already bucketed before `host` existed, and its raw data
environment_id = 1
when = timezone.now() - timedelta(minutes=90)

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.

Other than now, I much prefer being explicit with vars like this.

Suggested change
when = timezone.now() - timedelta(minutes=90)
ninety_minutes_ago = timezone.now() - timedelta(minutes=90)

…t at runtime

Buckets written before the host column hold a window's whole count under
host "". The rollup recomputes the trailing hour on every run, so a
recomputed window would gain per-host rows next to the old one and count
twice. Only rows within two hours of the migration can ever be recomputed,
so the migration deletes those; the next run rebuilds them from raw data.
Django drops the column default after AddField, so an old task processor
still running cannot insert new host-less rows.
@github-actions github-actions Bot added feature New feature or request and removed feature New feature or request labels Sep 19, 2026
@github-actions github-actions Bot added feature New feature or request and removed feature New feature or request labels Sep 19, 2026
@github-actions github-actions Bot added feature New feature or request and removed feature New feature or request labels Sep 19, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

api Issue related to the REST API feature New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants