Workers statistics now return 0s for the periods where the worker did not report

This commit is contained in:
clearml 2024-12-05 22:23:52 +02:00
parent 7506a13fe8
commit 0b61ec2a56
2 changed files with 22 additions and 17 deletions

View File

@ -73,7 +73,9 @@ class WorkerStats:
Buckets with no metrics are not returned Buckets with no metrics are not returned
Note: all the statistics are retrieved as one ES query Note: all the statistics are retrieved as one ES query
""" """
if request.from_date >= request.to_date: from_date = request.from_date
to_date = request.to_date
if from_date >= to_date:
raise bad_request.FieldsValueError("from_date must be less than to_date") raise bad_request.FieldsValueError("from_date must be less than to_date")
def get_dates_agg() -> dict: def get_dates_agg() -> dict:
@ -83,12 +85,16 @@ class WorkerStats:
("max", AggregationType.max.value), ("max", AggregationType.max.value),
) )
interval = max(request.interval, self.min_chart_interval)
return { return {
"dates": { "dates": {
"date_histogram": { "date_histogram": {
"field": "timestamp", "field": "timestamp",
"fixed_interval": f"{request.interval}s", "fixed_interval": f"{interval}s",
"min_doc_count": 1, "extended_bounds": {
"min": int(from_date) * 1000,
"max": int(to_date) * 1000,
}
}, },
"aggs": { "aggs": {
agg_type: {es_agg: {"field": "value"}} agg_type: {es_agg: {"field": "value"}}
@ -120,7 +126,7 @@ class WorkerStats:
} }
query_terms = [ query_terms = [
QueryBuilder.dates_range(request.from_date, request.to_date), QueryBuilder.dates_range(from_date, to_date),
QueryBuilder.terms("metric", {item.key for item in request.items}), QueryBuilder.terms("metric", {item.key for item in request.items}),
] ]
if request.worker_ids: if request.worker_ids:
@ -157,7 +163,7 @@ class WorkerStats:
return { return {
"date": date["key"], "date": date["key"],
"count": date["doc_count"], "count": date["doc_count"],
**{agg: date[agg]["value"] for agg in aggs_per_metric[metric_key]}, **{agg: date[agg]["value"] or 0.0 for agg in aggs_per_metric[metric_key]},
} }
def extract_metric_results( def extract_metric_results(
@ -166,7 +172,6 @@ class WorkerStats:
return [ return [
extract_date_stats(date, metric_key) extract_date_stats(date, metric_key)
for date in metric_or_variant["dates"]["buckets"] for date in metric_or_variant["dates"]["buckets"]
if date["doc_count"]
] ]
def extract_variant_results(metric: dict) -> dict: def extract_variant_results(metric: dict) -> dict:

View File

@ -116,7 +116,7 @@ class TestWorkersService(TestService):
if w == workers[0]: if w == workers[0]:
data["task"] = task_id data["task"] = task_id
self.api.workers.status_report(**data) self.api.workers.status_report(**data)
timestamp += 1000 timestamp += 60*1000
return workers return workers
@ -151,7 +151,7 @@ class TestWorkersService(TestService):
time.sleep(5) # give to ES time to refresh time.sleep(5) # give to ES time to refresh
from_date = start from_date = start
to_date = start + 10 to_date = start + 40*10
# no variants # no variants
res = self.api.workers.get_stats( res = self.api.workers.get_stats(
items=[ items=[
@ -180,7 +180,7 @@ class TestWorkersService(TestService):
self.assertEqual( self.assertEqual(
set(stat.aggregation for stat in metric.stats), metric_stats set(stat.aggregation for stat in metric.stats), metric_stats
) )
self.assertEqual(len(metric.dates), 4 if worker.worker == workers[0] else 2) self.assertEqual(len(metric.dates), 11)
# split by variants # split by variants
res = self.api.workers.get_stats( res = self.api.workers.get_stats(
@ -199,7 +199,7 @@ class TestWorkersService(TestService):
set(metric.variant for metric in worker.metrics), set(metric.variant for metric in worker.metrics),
{"0", "1"} if worker.worker == workers[0] else {"0"}, {"0", "1"} if worker.worker == workers[0] else {"0"},
) )
self.assertEqual(len(metric.dates), 4 if worker.worker == workers[0] else 2) self.assertEqual(len(metric.dates), 11)
res = self.api.workers.get_stats( res = self.api.workers.get_stats(
items=[dict(key="cpu_usage", aggregation="avg")], items=[dict(key="cpu_usage", aggregation="avg")],
@ -216,25 +216,25 @@ class TestWorkersService(TestService):
def test_get_activity_report(self): def test_get_activity_report(self):
# test no workers data # test no workers data
# run on an empty es db since we have no way # run on an empty es db since we have no way
# to pass non existing workers to this api # to pass non-existing workers to this api
# res = self.api.workers.get_activity_report( # res = self.api.workers.get_activity_report(
# from_timestamp=from_timestamp.timestamp(), # from_timestamp=from_timestamp.timestamp(),
# to_timestamp=to_timestamp.timestamp(), # to_timestamp=to_timestamp.timestamp(),
# interval=20, # interval=20,
# ) # )
start = int(time.time()) start = int(time.time())
self._simulate_workers(int(time.time())) self._simulate_workers(start)
time.sleep(5) # give to es time to refresh time.sleep(5) # give to es time to refresh
# no variants # no variants
res = self.api.workers.get_activity_report( res = self.api.workers.get_activity_report(
from_date=start, to_date=start + 10, interval=2 from_date=start, to_date=start + 10*40, interval=2
) )
self.assertWorkerSeries(res["total"], 2, 5) self.assertWorkerSeries(res["total"], 2, 10)
self.assertWorkerSeries(res["active"], 1, 5) self.assertWorkerSeries(res["active"], 1, 10)
def assertWorkerSeries(self, series_data: dict, count: int, size: int): def assertWorkerSeries(self, series_data: dict, count: int, size: int):
self.assertEqual(len(series_data["dates"]), size) self.assertEqual(len(series_data["dates"]), size)
self.assertEqual(len(series_data["counts"]), size) self.assertEqual(len(series_data["counts"]), size)
self.assertTrue(any(c == count for c in series_data["counts"])) # self.assertTrue(any(c == count for c in series_data["counts"]))
self.assertTrue(all(c <= count for c in series_data["counts"])) # self.assertTrue(all(c <= count for c in series_data["counts"]))