Skip to content

feat: Cloud cost anomaly detection (backport #7251) - #7258

Open
mergify[bot] wants to merge 5 commits into
masterfrom
mergify/bp/master/pr-7251
Open

feat: Cloud cost anomaly detection (backport #7251)#7258
mergify[bot] wants to merge 5 commits into
masterfrom
mergify/bp/master/pr-7251

Conversation

@mergify

@mergify mergify Bot commented Aug 18, 2026

Copy link
Copy Markdown
Contributor

This is an automatic backport of pull request #7251 done by [Mergify](https://mergify.com).

saurabh6790 and others added 5 commits August 18, 2026 10:00
The newest month in the window is only billed up to today, so measuring it
against a full previous month reported every service as collapsing and the
notable-change flag fired on all of them. The change column now compares the
two most recent complete months, and the part month is labelled MTD.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
(cherry picked from commit 16840b2)
Cost Explorer was only ever read live, by month, grouped by service. That says
"S3 is up" three weeks after it went up, which is neither early nor a lead.

Daily cost and usage are now stored per service, usage type and region, so the
history can be walked without paying Cost Explorer for every look. Alongside it
we count what the business was doing each day from our own records: active
sites, running machines, volume and snapshot size, and the bytes written to the
backup buckets. Remote File keeps its rows after deletion, so the upload series
has real history rather than starting from today.

Two detectors run over each series. A spike is one loud day against the same
weekday's history. A level shift is the day a series settled at a new number and
stayed there, which is the shape behind a bill that is a third higher than last
month without any single day looking wrong. The shift runs first, because
reporting "yesterday was high" when the answer is "it changed on the twelfth"
is the failure being fixed.

Growth its driver kept up with is the product working and is recorded quietly.
Growth its driver did not follow is filed as unexplained, with the day it
started, the money it costs, and the teams or machines behind it, and goes to
Telegram.

Cloud Cost Drilldown walks service to usage type to region to day, all from the
stored table. Off by default; enable it and add an account on Cloud Cost
Settings.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
(cherry picked from commit 7852660)
The four providers answer the cost question in three different ways, so the
adapters admit that instead of pretending otherwise.

OCI meters like AWS does. Its Usage API gives daily cost and quantity per
service and SKU, so it is read the same way and marked Billed.

Hetzner has no billing API at all, and DigitalOcean issues invoices monthly,
which is too late to warn anyone. For those two the daily figure is our own
inventory priced at the provider's rate card, marked Accrued so nobody reads a
model of the bill as the bill. Hetzner's rates come from its live pricing
endpoint; DigitalOcean prices droplets from the sizes API, and block storage and
snapshots from rates on Cloud Cost Settings, because it publishes none.

Hetzner traffic is the reason this was worth doing. Every server reports its own
outgoing bytes against its included allowance, so overage is priced the day it
starts instead of turning up on an invoice a month later.

Drivers and verdicts are now per provider. Hetzner volumes growing says nothing
about whether AWS storage should have grown, so each series is judged against
its own provider's count and only falls back to the fleet when that provider has
none. A series with no driver mapped now reaches the queue and the alert too:
unexplainable is not the same as fine, and staying quiet made an unmapped series
the safest place for a leak to hide.

Providers bill in their own currency and there is no exchange rate in Press
worth trusting, so currency is stored per row and the report shows one at a time
rather than summing euros into dollars.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
(cherry picked from commit 2cf42c6)
Three from review.

Accrued providers were detected a day late. Hetzner and DigitalOcean have no
cost API, so their rows are today's inventory priced now, but detection ended at
yesterday because that is the last complete day a metered provider has billed.
Each account now gets the window its own source can actually fill, so the two
providers with a live reading are judged the day it is taken.

An account that failed to ingest was quietly reported as fine. Ingest isolates
each account so one bad token does not discard three healthy providers, which
means one account can be left short of today while the rest are current. Its
series then end in a gap, gaps are read as zeros, and a run of trailing zeros
makes a real problem look like calm. Those accounts are now skipped and named in
the error log, and the healthy ones are still judged.

Making the whole pipeline atomic instead would trade a quiet failure for a
louder one: a single provider outage would throw away every other provider's
day, and four external APIs plus a full driver scan in one transaction is a long
way to hold a lock.

Also split the summary sentences into their own strings. They were implicitly
concatenated inside a list literal, which reads like a missing comma.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
(cherry picked from commit 5ee4005)
Six doctypes for one feature was too many, and two of them held data nothing
ever queried.

Cloud Cost Account listed which accounts to pull from. Press already knows: AWS
billing credentials live in Press Settings, and OCI, Hetzner and DigitalOcean
each keep their token on the cluster. Accounts are worked out from that now, so
there is no list to configure and none to keep in step with the clusters. AWS
stays a single payer on purpose, because per-cluster keys run machines rather
than read bills, and querying a member the payer already covers would count its
spend twice.

Cloud Usage Anomaly Contributor held rows that were only ever read. They render
into one field on the finding, and the links still open the record.

Settings went from thirteen fields to eight. The statistical ones, baseline
length, the spike threshold, the level shift minimum and the organic tolerance,
are the shape of detection rather than something operations tunes, so they are
constants worth arguing about in a pull request. What is left is the two money
floors that decide how much a change has to be worth before anyone hears about
it, retention, and the DigitalOcean rates.

Nothing about detection changed. Same 82 tests, four more of them covering how
accounts are now derived.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
(cherry picked from commit 6a16953)
@mergify

mergify Bot commented Aug 18, 2026

Copy link
Copy Markdown
Contributor Author

Tick the box to add this pull request to the merge queue (same as @mergifyio queue).

  • Queue this pull request

@codecov-commenter

codecov-commenter commented Aug 18, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 87.41770% with 172 lines in your changes missing coverage. Please review.
✅ Project coverage is 59.55%. Comparing base (9d610fb) to head (1ad1836).

Files with missing lines Patch % Lines
...press/doctype/cloud_cost_daily/cloud_cost_daily.py 44.92% 38 Missing ⚠️
...s/doctype/cloud_usage_driver/cloud_usage_driver.py 65.51% 30 Missing ⚠️
...ess/press/doctype/cloud_cost_daily/adapters/aws.py 60.52% 15 Missing ⚠️
...doctype/cloud_usage_anomaly/cloud_usage_anomaly.py 92.97% 13 Missing ⚠️
.../press/doctype/cloud_usage_anomaly/contributors.py 82.89% 13 Missing ⚠️
...ss/press/doctype/cloud_cost_daily/adapters/base.py 57.14% 12 Missing ⚠️
...ess/press/doctype/cloud_cost_daily/adapters/oci.py 55.55% 12 Missing ⚠️
...press/doctype/cloud_cost_daily/adapters/hetzner.py 82.53% 11 Missing ⚠️
...doctype/cloud_cost_settings/cloud_cost_settings.py 50.00% 10 Missing ⚠️
.../doctype/cloud_cost_daily/adapters/digitalocean.py 82.60% 8 Missing ⚠️
... and 3 more
Additional details and impacted files
@@            Coverage Diff             @@
##           master    #7258      +/-   ##
==========================================
+ Coverage   59.08%   59.55%   +0.47%     
==========================================
  Files        1026     1046      +20     
  Lines       93867    95233    +1366     
  Branches     1210     1211       +1     
==========================================
+ Hits        55462    56718    +1256     
- Misses      38382    38492     +110     
  Partials       23       23              
Flag Coverage Δ
dashboard 87.30% <ø> (ø)

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ 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.

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