-
Notifications
You must be signed in to change notification settings - Fork 244
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[batch] Show cost breakdown in the UI #13491
Changes from all commits
2b6c7fc
740a695
cc8c92c
300d9da
6956613
c0da08e
a221ffd
67eefb1
dbf9c8d
b599298
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -133,14 +133,14 @@ def parse_list_batches_query_v2(user: str, q: str, last_batch_id: Optional[int]) | |
batches_n_jobs_in_complete_states.n_succeeded, | ||
batches_n_jobs_in_complete_states.n_failed, | ||
batches_n_jobs_in_complete_states.n_cancelled, | ||
cost_t.cost | ||
cost_t.cost, cost_t.cost_breakdown | ||
FROM batches | ||
LEFT JOIN billing_projects ON batches.billing_project = billing_projects.name | ||
LEFT JOIN batches_n_jobs_in_complete_states ON batches.id = batches_n_jobs_in_complete_states.id | ||
LEFT JOIN batches_cancelled ON batches.id = batches_cancelled.id | ||
STRAIGHT_JOIN billing_project_users ON batches.billing_project = billing_project_users.billing_project | ||
LEFT JOIN LATERAL ( | ||
SELECT COALESCE(SUM(`usage` * rate), 0) AS cost | ||
SELECT COALESCE(SUM(`usage` * rate), 0) AS cost, JSON_OBJECTAGG(resources.resource, COALESCE(`usage` * rate, 0)) AS cost_breakdown | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This looks great. |
||
FROM ( | ||
SELECT batch_id, resource_id, CAST(COALESCE(SUM(`usage`), 0) AS SIGNED) AS `usage` | ||
FROM aggregated_batch_resources_v2 | ||
|
@@ -269,7 +269,8 @@ def parse_batch_jobs_query_v2(batch_id: int, q: str, last_job_id: Optional[int]) | |
attempts_table_join_str = '' | ||
|
||
sql = f''' | ||
SELECT jobs.*, batches.user, batches.billing_project, batches.format_version, job_attributes.value AS name, cost_t.cost | ||
SELECT jobs.*, batches.user, batches.billing_project, batches.format_version, job_attributes.value AS name, cost_t.cost, | ||
cost_t.cost_breakdown | ||
FROM jobs | ||
INNER JOIN batches ON jobs.batch_id = batches.id | ||
INNER JOIN batch_updates ON jobs.batch_id = batch_updates.batch_id AND jobs.update_id = batch_updates.update_id | ||
|
@@ -279,7 +280,7 @@ def parse_batch_jobs_query_v2(batch_id: int, q: str, last_job_id: Optional[int]) | |
job_attributes.`key` = 'name' | ||
{attempts_table_join_str} | ||
LEFT JOIN LATERAL ( | ||
SELECT COALESCE(SUM(`usage` * rate), 0) AS cost | ||
SELECT COALESCE(SUM(`usage` * rate), 0) AS cost, JSON_OBJECTAGG(resources.resource, COALESCE(`usage` * rate, 0)) AS cost_breakdown | ||
FROM (SELECT aggregated_job_resources_v2.batch_id, aggregated_job_resources_v2.job_id, resource_id, CAST(COALESCE(SUM(`usage`), 0) AS SIGNED) AS `usage` | ||
FROM aggregated_job_resources_v2 | ||
WHERE aggregated_job_resources_v2.batch_id = jobs.batch_id AND aggregated_job_resources_v2.job_id = jobs.job_id | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -15,6 +15,28 @@ <h2>Properties</h2> | |
<li>Cost: {% if 'cost' in job and job['cost'] is not none %}{{ job['cost'] }}{% endif %}</li> | ||
</ul> | ||
|
||
<h2>Cost Breakdown</h2> | ||
{% if job['cost_breakdown'] %} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. What causes this to be None? It seems like the DB queries always return it. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It's None if the job hasn't started running yet. Same with batches with no jobs running yet. |
||
<table class="data-table"> | ||
<thead> | ||
<tr> | ||
<th>Resource</th> | ||
<th>Cost</th> | ||
</tr> | ||
</thead> | ||
<tbody> | ||
{% for resource_cost in job['cost_breakdown'] %} | ||
<tr> | ||
<td>{{ resource_cost['resource'] }}</td> | ||
<td>{{ resource_cost['cost'] }}</td> | ||
</tr> | ||
{% endfor %} | ||
</tbody> | ||
</table> | ||
{% else %} | ||
<p>No accrued costs</p> | ||
{% endif %} | ||
|
||
<h2>Attempts</h2> | ||
{% if attempts %} | ||
<table class="data-table"> | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Food for thought for a possible clean up PR: The fact that we repeat this query in a few places makes me wonder if we're better off using two DB queries in a transaction. I suspect the DB round-trip-latency is far from our main bottleneck and it makes it easier to understand the code if we had something like:
That way we can abstract the cost calculation out from the query code or the UI code.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
These queries are a mess anyways. They need to be rewritten to use lateral joins without the
WITH base_t
anyways. I was trying not to do that in this PR. The issue why the queries needed to be changed more drastically is because doingJSON_OBJECTAGG()
the way we had it before was returning{null: ...}
if there were no records which was causing lots of downstream problems.Should I go ahead and simplify the queries to what is optimal here in this PR?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
no, I was just noodling on how we could make this more readable for the future. let's keep this PR simple and to the point. A separate PR to explore alternative organization of the SQL seems fine.