Skip to content

Commit 49f7104

Browse files
author
ci bot
committed
Merge branch 'events-list-performance' into 'enterprise'
refactor(be): remove coalesce when ordering events list See merge request dkinternal/observability/dataops-observability!24
2 parents 5d4b091 + 863e5e0 commit 49f7104

9 files changed

Lines changed: 96 additions & 56 deletions

File tree

common/entities/event.py

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
from enum import Enum, IntEnum
44

5-
from peewee import ForeignKeyField, Node, fn
5+
from peewee import ForeignKeyField
66
from playhouse.mysql_ext import JSONField
77

88
from common.peewee_extensions.fields import EnumIntField, EnumStrField, UTCTimestampField
@@ -44,10 +44,6 @@ class EventEntity(BaseEntity):
4444
instance_set = ForeignKeyField(InstanceSet, null=True, backref="events", on_delete="SET NULL")
4545
v2_payload = JSONField(null=False)
4646

47-
@classmethod
48-
def timestamp_coalesce(cls) -> Node:
49-
return fn.COALESCE(cls.timestamp, cls.created_timestamp)
50-
5147
@property
5248
def components(self) -> list[Component]:
5349
return [self.component] if self.component else []

common/entity_services/company_service.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ def get_organizations_with_rules(company_id: str, rules: ListRules) -> Page[Orga
2626

2727
@staticmethod
2828
def get_users_with_rules(company_id: str, rules: ListRules) -> Page[User]:
29-
query = User.select().join(Company).where(Company.id == company_id)
29+
query = User.select().join(Company).where(User.primary_company == company_id)
3030
return Page[User].get_paginated_results(query, User.name, rules)
3131

3232
@staticmethod

common/entity_services/event_service.py

Lines changed: 7 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -37,11 +37,12 @@ def get_events_with_rules(*, rules: ListRules, filters: ProjectEventFilters) ->
3737
filter_list: list[object] = [EventEntity.project << filters.project_ids]
3838

3939
if filters.instance_ids or filters.journey_ids:
40-
query = query.join(InstanceSet).join(InstancesInstanceSets).join(Instance).switch(EventEntity)
40+
instance_set_subquery = InstanceSet.select(InstanceSet.id).join(InstancesInstanceSets).join(Instance)
4141
if filters.journey_ids:
42-
filter_list.append(Instance.journey << filters.journey_ids)
42+
instance_set_subquery = instance_set_subquery.where(Instance.journey << filters.journey_ids)
4343
if filters.instance_ids:
44-
filter_list.append(Instance.id << filters.instance_ids)
44+
instance_set_subquery = instance_set_subquery.where(Instance.id << filters.instance_ids)
45+
query = query.where(EventEntity.instance_set.in_(instance_set_subquery))
4546
if filters.event_types:
4647
filter_list.append(EventEntity.type << filters.event_types)
4748
if filters.component_ids:
@@ -53,16 +54,12 @@ def get_events_with_rules(*, rules: ListRules, filters: ProjectEventFilters) ->
5354
if filters.task_ids:
5455
filter_list.append(EventEntity.task << filters.task_ids)
5556
if filters.date_range_start:
56-
filter_list.append(
57-
EventEntity.timestamp_coalesce() >= EventEntity.timestamp.db_value(filters.date_range_start)
58-
)
57+
filter_list.append(EventEntity.timestamp >= filters.date_range_start)
5958
if filters.date_range_end:
60-
filter_list.append(
61-
EventEntity.timestamp_coalesce() < EventEntity.timestamp.db_value(filters.date_range_end)
62-
)
59+
filter_list.append(EventEntity.timestamp < filters.date_range_end)
6360

6461
query = query.where(*filter_list)
65-
page = Page[EventEntity].get_paginated_results(query, EventEntity.timestamp_coalesce(), rules)
62+
page = Page[EventEntity].get_paginated_results(query, EventEntity.timestamp, rules)
6663

6764
# Using a single query to fetch the Instance and Journey data
6865
instance_set_ids = {e.instance_set_id for e in page.results}

common/entity_services/helpers/list_rules.py

Lines changed: 20 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,11 @@
88

99
from marshmallow import EXCLUDE, Schema
1010
from marshmallow.fields import Enum, Int
11-
from peewee import Field, Ordering, Select
11+
from peewee import JOIN, Field, Ordering, Select
1212
from werkzeug.datastructures import MultiDict
1313

14+
from common.entities import BaseEntity
15+
1416
DEFAULT_PAGE = 1
1517
DEFAULT_COUNT = 10
1618
T = TypeVar("T")
@@ -50,9 +52,24 @@ def __len__(self) -> int:
5052

5153
@classmethod
5254
def get_paginated_results(cls, query: Select, order_by: Field, list_rules: ListRules) -> Page[T]:
55+
model: BaseEntity = query.model
5356
ordering = list_rules.order_by_field(order_by)
54-
paginated_query = query.order_by(ordering).paginate(list_rules.page, list_rules.count)
55-
return cls(results=list(paginated_query), total=query.count())
57+
58+
paginated_subquery: Select = (
59+
model.select(model.id)
60+
.where(query._where)
61+
.order_by(ordering)
62+
.paginate(list_rules.page, list_rules.count)
63+
.alias("results")
64+
)
65+
66+
results_query = query.clone()
67+
results_query._where = None
68+
results_query = results_query.join(
69+
paginated_subquery, join_type=JOIN.INNER, on=(model.id == paginated_subquery.c.id)
70+
).order_by(ordering)
71+
72+
return cls(results=list(results_query), total=paginated_subquery.count(clear_limit=True))
5673

5774

5875
@dataclass

common/entity_services/journey_service.py

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -52,16 +52,18 @@ def get_action_by_implementation(journey_id: UUID, action_impl: str) -> Optional
5252

5353
@staticmethod
5454
def get_components_with_rules(journey_id: str, rules: ListRules, filters: ComponentFilters) -> Page[Component]:
55-
query = JourneyDagEdge.journey_id == journey_id
55+
join_on = (JourneyDagEdge.journey_id == journey_id) & (
56+
(JourneyDagEdge.left == Component.id) | (JourneyDagEdge.right == Component.id)
57+
)
58+
query = Component.select(Component).join(JourneyDagEdge, on=join_on)
5659
if rules.search is not None:
57-
query &= Component.key ** f"%{rules.search}%"
60+
query = query.where(Component.key ** f"%{rules.search}%")
5861
if filters.component_types:
59-
query &= Component.type.in_(filters.component_types)
62+
query = query.where(Component.type.in_(filters.component_types))
6063
if filters.tools:
61-
query &= Component.tool.in_(filters.tools)
62-
join_on = (JourneyDagEdge.left == Component.id) | (JourneyDagEdge.right == Component.id)
63-
query = Component.select(Component).join(JourneyDagEdge, on=join_on).where(query).distinct()
64-
return Page[Component].get_paginated_results(query, Component.key, rules)
64+
query = query.where(Component.tool.in_(filters.tools))
65+
66+
return Page[Component].get_paginated_results(query.distinct(), Component.key, rules)
6567

6668
@staticmethod
6769
def get_upstream_nodes(journey: Journey, component_id: UUID) -> set:

common/entity_services/project_service.py

Lines changed: 17 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -55,36 +55,37 @@ class ProjectService:
5555
def get_test_outcomes_with_rules(
5656
project: Project, rules: ListRules, filters: TestOutcomeItemFilters
5757
) -> Page[TestOutcome]:
58-
clauses = [TestOutcome.component.in_(project.components)]
58+
query = TestOutcome.select(TestOutcome).where(TestOutcome.component.in_(project.components))
5959
if rules.search is not None:
60-
clauses.append(
60+
query = query.where(
6161
((TestOutcome.name ** f"%{rules.search}%") | (TestOutcome.description ** f"%{rules.search}%"))
6262
)
63+
6364
if filters:
6465
if statuses := filters.statuses:
65-
clauses.append(TestOutcome.status.in_(statuses))
66+
query = query.where(TestOutcome.status.in_(statuses))
6667
if component_ids := filters.component_ids:
67-
clauses.append(TestOutcome.component << component_ids)
68+
query = query.where(TestOutcome.component << component_ids)
6869
if start_range_begin := filters.start_range_begin:
69-
clauses.append(TestOutcome.start_time >= start_range_begin)
70+
query = query.where(TestOutcome.start_time >= start_range_begin)
7071
if start_range_end := filters.start_range_end:
71-
clauses.append(TestOutcome.start_time < start_range_end)
72+
query = query.where(TestOutcome.start_time < start_range_end)
7273
if end_range_begin := filters.end_range_begin:
73-
clauses.append(TestOutcome.end_time >= end_range_begin)
74+
query = query.where(TestOutcome.end_time >= end_range_begin)
7475
if end_range_end := filters.end_range_end:
75-
clauses.append(TestOutcome.end_time < end_range_end)
76+
query = query.where(TestOutcome.end_time < end_range_end)
7677
if run_ids := filters.run_ids:
77-
clauses.append(TestOutcome.run.in_(run_ids))
78+
query = query.where(TestOutcome.run.in_(run_ids))
7879
if instance_ids := filters.instance_ids:
79-
clauses.append(InstancesInstanceSets.instance.in_(instance_ids))
80+
instance_set_subquery = (
81+
InstanceSet.select(InstanceSet.id)
82+
.join(InstancesInstanceSets)
83+
.where(InstancesInstanceSets.instance.in_(instance_ids))
84+
)
85+
query = query.where(TestOutcome.instance_set.in_(instance_set_subquery))
8086
if key := filters.key:
81-
clauses.append(TestOutcome.key == key)
87+
query = query.where(TestOutcome.key == key)
8288

83-
# If filtering on instance_ids we need to join the InstanceSet tables
84-
if filters and filters.instance_ids:
85-
query = TestOutcome.select(TestOutcome).join(InstanceSet).join(InstancesInstanceSets).where(*clauses)
86-
else:
87-
query = TestOutcome.select(TestOutcome).where(*clauses)
8889
return Page[TestOutcome].get_paginated_results(query, TestOutcome.start_time, rules)
8990

9091
@staticmethod

common/tests/unit/entity_services/helpers/test_list_rules.py

Lines changed: 35 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
from unittest.mock import Mock
1+
from unittest.mock import ANY, Mock, MagicMock
22

33
import pytest
44
from werkzeug.datastructures import MultiDict
@@ -20,21 +20,29 @@ def mocked_entity_class():
2020

2121
@pytest.fixture()
2222
def mocked_query():
23-
# .paginate() (and other query methods in general) doesn't return a list until iterated on, but this is good enough for the test
24-
paginate = Mock(return_value=list())
2523
query = Mock()
26-
query.paginate = paginate
27-
query.order_by = Mock(return_value=query)
24+
subquery = Mock()
25+
results_query = MagicMock()
26+
27+
query.model.select.return_value = subquery
28+
query.clone = Mock(return_value=results_query)
29+
30+
subquery.where.return_value = subquery
31+
subquery.order_by.return_value = subquery
32+
subquery.paginate.return_value = subquery
33+
subquery.alias.return_value = subquery
34+
35+
results_query.join.return_value = results_query
36+
results_query.order_by.return_value = results_query
37+
2838
yield query
2939

3040

3141
@pytest.fixture()
32-
def mocked_query_with_data():
33-
# .paginate() (and other query methods in general) doesn't return a list until iterated on, but this is good enough for the test
34-
paginate = Mock(return_value=[Mock(), Mock()])
35-
query = Mock()
36-
query.paginate = paginate
37-
query.order_by = Mock(return_value=query)
42+
def mocked_query_with_data(mocked_query):
43+
query = mocked_query
44+
results_query = query.clone.return_value
45+
results_query.__iter__.return_value = [Mock(), Mock()]
3846
yield query
3947

4048

@@ -80,11 +88,24 @@ def test_from_params_extras():
8088

8189
@pytest.mark.unit
8290
def test_page_get_paginated_results(mocked_field, mocked_entity_class, mocked_query):
91+
mocked_subquery = mocked_query.model.select.return_value
92+
mocked_results_query = mocked_query.clone.return_value
8393
rules = ListRules.from_params(MultiDict())
94+
8495
result = Page[mocked_entity_class].get_paginated_results(mocked_query, mocked_field, rules)
96+
8597
mocked_field.asc.assert_called_once()
86-
mocked_query.paginate.assert_called_once()
87-
mocked_query.count.assert_called_once()
98+
99+
mocked_subquery.where.assert_called_once_with(mocked_query._where)
100+
mocked_subquery.order_by.assert_called_once_with(rules.order_by_field(mocked_field))
101+
mocked_subquery.paginate.assert_called_once_with(rules.page, rules.count)
102+
mocked_subquery.alias.assert_called_once()
103+
mocked_subquery.count.assert_called_once_with(clear_limit=True)
104+
105+
assert mocked_results_query._where is None
106+
mocked_results_query.join.assert_called_once_with(mocked_subquery, join_type=ANY, on=ANY)
107+
mocked_results_query.order_by.assert_called_once_with(rules.order_by_field(mocked_field))
108+
88109
assert type(result) == Page
89110

90111

@@ -102,6 +123,6 @@ def test_page_len(mocked_field, mocked_entity_class, mocked_query_with_data):
102123
def test_page_iter(mocked_field, mocked_entity_class, mocked_query_with_data):
103124
rules = ListRules.from_params(MultiDict())
104125
result = Page[mocked_entity_class].get_paginated_results(mocked_query_with_data, mocked_field, rules)
105-
count = len(mocked_query_with_data.paginate())
126+
count = len(list(mocked_query_with_data.clone.return_value))
106127
r = [r for r in result]
107128
assert len(r) == count

deploy/charts/observability-app/templates/observability-ui.yaml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,11 @@ spec:
5151
protocol: TCP
5252
resources:
5353
{{- toYaml .Values.observability_ui.resources | nindent 12 }}
54+
{{- if .Values.observability_api.hostname }}
55+
env:
56+
- name: OBSERVABILITY_API_HOSTNAME
57+
value: {{ tpl .Values.observability_api.hostname . | quote }}
58+
{{- end }}
5459
{{- if .Values.observability_ui.environmentJson }}
5560
volumeMounts:
5661
- mountPath: /observability_ui/shell/environments

testlib/fixtures/entities.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -621,6 +621,7 @@ def event_entity_2(test_db, dataset):
621621
version=EventVersion.V2,
622622
type=ApiEventType.DATASET_OPERATION,
623623
created_timestamp=datetime(2024, 1, 20, 9, 55, 0, tzinfo=timezone.utc),
624+
timestamp=datetime(2024, 1, 20, 9, 55, 0, tzinfo=timezone.utc),
624625
project=dataset.project_id,
625626
component=dataset,
626627
v2_payload={},

0 commit comments

Comments
 (0)