feat(tasks): report priced sandbox compute credits - #73031
Conversation
|
Merging to
After your PR is submitted to the merge queue, this comment will be automatically updated with its status. If the PR fails, failure details will also be posted here |
|
Hey @adboio! 👋 It looks like your git author email on this PR isn't your
You can fix it for this repo with: git config user.email "you@posthog.com"Or set it globally with |
🦔 ReviewHog reviewed this pull requestFound 0 must fix, 1 should fix, 0 consider. Published 1 finding (view the review). |
|
ReviewHog Alpha 🦔 If you find any issues helpful - please reply "valid", "invalid", etc., for evaluation purposes 🙏 |
Prompt To Fix All With AIFix the following 2 code review issues. Work through them one at a time, proposing concise fixes.
---
### Issue 1 of 2
products/tasks/backend/logic/services/sandbox_usage.py:156
**Historical Loop Usage Becomes Unbillable**
When a non-internal loop is later marked internal or deleted, this annotation reads `True` or `NULL` instead of the loop state when the sandbox ran. Rebuilding an earlier report then drops credits for compute that was billable at execution time, so historical billing changes after the session is complete.
### Issue 2 of 2
products/tasks/backend/logic/services/sandbox_usage.py:156
**Internal Loop Usage Becomes Billable**
When an internal loop is later marked non-internal, this annotation reads the new `False` value and the billing gate charges its earlier sandbox sessions. Historical reports can therefore add credits for compute that was internal when it ran; the billing classification needs to be recorded with the session rather than resolved from mutable current state.
Reviews (1): Last reviewed commit: "fix(tasks): expand billable sandbox orig..." | Re-trigger Greptile |
| # Unscoped: the usage report aggregates across every team in the region. | ||
| sessions = ( | ||
| SandboxSession.objects.unscoped() | ||
| .annotate(task_loop_internal=F("task_run__task__loop__internal")) |
There was a problem hiding this comment.
Historical Loop Usage Becomes Unbillable
When a non-internal loop is later marked internal or deleted, this annotation reads True or NULL instead of the loop state when the sandbox ran. Rebuilding an earlier report then drops credits for compute that was billable at execution time, so historical billing changes after the session is complete.
Prompt To Fix With AI
This is a comment left during a code review.
Path: products/tasks/backend/logic/services/sandbox_usage.py
Line: 156
Comment:
**Historical Loop Usage Becomes Unbillable**
When a non-internal loop is later marked internal or deleted, this annotation reads `True` or `NULL` instead of the loop state when the sandbox ran. Rebuilding an earlier report then drops credits for compute that was billable at execution time, so historical billing changes after the session is complete.
How can I resolve this? If you propose a fix, please make it concise.There was a problem hiding this comment.
Fixed by snapshotting loop_internal onto SandboxSession at provision time and billing from that immutable value. The regression test flips both loop directions after provisioning.
| if session.burstable: | ||
| assert session.cpu_request_cores is not None | ||
| assert session.memory_request_mb is not None | ||
| cpu_cores = session.cpu_request_cores if session.burstable else session.cpu_cores |
There was a problem hiding this comment.
Medium: Burst usage is priced at reservation floors
A user can run CPU- or memory-intensive code in a default burstable sandbox while this calculation charges only the 0.5-core/1-GiB reservation floor. The sandbox can consume up to its configured limits and the provider bills the greater of reserved and actual usage, so credits can substantially underreport attacker-driven compute consumption. Use provider-reported actual usage, or conservatively price the configured limits when actual usage is unavailable.
PR overviewThis pull request adds reporting of priced compute-credit usage for task sandboxes, including CPU and memory consumption calculations. One issue has been addressed, but burstable sandbox usage can still be priced at reservation floors rather than actual consumption or configured limits. A user can deliberately drive CPU or memory usage above those floors, causing compute-credit reporting to substantially undercharge their activity while provider costs continue to accrue. Open issues (1)
Fixed/addressed: 1 · PR risk: 6/10 |
🤖 CI report✅ Django migration risk — migration analysis completeWe've analyzed your migrations for potential risks. Summary: 1 Safe | 0 Needs Review | 0 Blocked ✅ SafeBrief or no lock, backwards compatible 📚 How to Deploy These Changes SafelyAddField: This operation acquires a brief lock but doesn't rewrite the table. Deployment uses lock timeouts with automatic retries, so lock contention will cause retries rather than connection pile-up. Last updated: 2026-07-29 16:37 UTC (387599a) |
380ca41 to
f5d7435
Compare
🦔 Hogbox preview · ❌ build failedThe preview didn't come up for commit Previews are optional and never block merging. A failure here is often a hogland or tailnet hiccup rather than anything in your PR, so the check stays green and this comment is the status. |
ca874ed to
7484578
Compare
Generated-By: PostHog Code Task-Id: 5737ef14-4436-46b0-8568-8ae9143ca6c7
Generated-By: PostHog Code Task-Id: 5737ef14-4436-46b0-8568-8ae9143ca6c7
Bill user-created sandbox sessions only when their run was initiated through a verified PostHog Code OAuth application. Generated-By: PostHog Code Task-Id: e55f8ea4-cc41-4e76-ad48-896b396e95ce
Use the sandbox ledger's loop classification snapshot so later loop edits cannot change historical compute credits. Generated-By: PostHog Code Task-Id: e55f8ea4-cc41-4e76-ad48-896b396e95ce
Generated-By: PostHog Code Task-Id: e55f8ea4-cc41-4e76-ad48-896b396e95ce
4f4398d to
e984894
Compare
Generated-By: PostHog Code Task-Id: e55f8ea4-cc41-4e76-ad48-896b396e95ce
…hog-code/sandbox-compute-refresh Generated-By: PostHog Code Task-Id: e55f8ea4-cc41-4e76-ad48-896b396e95ce
Problem
Sandbox compute needs a priced usage counter without allowing a client-selected task origin to determine whether work is charged.
Changes
This PR is stacked on #74491. It calculates integer compute credits from the snapshotted sandbox ledger and applies the existing origin rules. User-created work is billable only when SandboxSession.compute_source is the server-verified posthog_desktop value. Signal-report work remains excluded, and loop billing uses the provision-time internal classification.
Resource quantities use the configured burstable request floors, durations round up to whole seconds, and pricing remains isolated behind provisional named constants.
How did you test this code?
Database-backed tests rely on CI because the configured Postgres hostname is unavailable locally.
Automatic notifications
Docs update
No user-facing documentation change.
🤖 Agent context
Autonomy: Human-driven (agent-assisted)
Codex implemented the counter with direct human design review. Repository testing and Django migration guidance shaped the validation. The key decision is to bill from immutable ledger provenance rather than the mutable task or a client assertion.
Created with PostHog Code