Skip to content

Commit 395db9c

Browse files
authored
fix(eap): reliabilities should be updated even when value is null (#6863)
The reliabilities array has to be the same size as the result array. In the previous method, their lengths could be different if some of the aggregations returned null. Prior to the nullity overhaul, this wasn't a concern because the aggregation couldn't be null.
1 parent 0bdf9a9 commit 395db9c

File tree

3 files changed

+112
-24
lines changed

3 files changed

+112
-24
lines changed

snuba/web/rpc/v1/resolvers/common/aggregation.py

Lines changed: 5 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -43,16 +43,12 @@ class ExtrapolationContext(ABC):
4343
confidence_interval: Any
4444
average_sample_rate: float
4545
sample_count: int
46+
is_extrapolated: bool
4647

4748
@property
4849
def is_data_present(self) -> bool:
4950
return self.sample_count > 0
5051

51-
@property
52-
@abstractmethod
53-
def is_extrapolated(self) -> bool:
54-
raise NotImplementedError
55-
5652
@property
5753
@abstractmethod
5854
def reliability(self) -> Reliability.ValueType:
@@ -64,6 +60,7 @@ def from_row(
6460
row_data: Dict[str, Any],
6561
) -> ExtrapolationContext:
6662
value = row_data[column_label]
63+
is_extrapolated = False
6764

6865
confidence_interval = None
6966
average_sample_rate = 0
@@ -88,6 +85,7 @@ def from_row(
8885
continue
8986

9087
if custom_column_information.custom_column_id == "confidence_interval":
88+
is_extrapolated = True
9189
confidence_interval = col_value
9290

9391
is_percentile = custom_column_information.metadata.get(
@@ -116,25 +114,20 @@ def from_row(
116114
percentile=percentile,
117115
granularity=granularity,
118116
width=width,
117+
is_extrapolated=is_extrapolated,
119118
)
120119

121120
return GenericExtrapolationContext(
122121
value=value,
123122
confidence_interval=confidence_interval,
124123
average_sample_rate=average_sample_rate,
125124
sample_count=sample_count,
125+
is_extrapolated=is_extrapolated,
126126
)
127127

128128

129129
@dataclass(frozen=True)
130130
class GenericExtrapolationContext(ExtrapolationContext):
131-
@property
132-
def is_extrapolated(self) -> bool:
133-
# We infer if a column is extrapolated or not by the presence of the
134-
# confidence interval. It will be present for extrapolated aggregates
135-
# but not for non-extrapolated aggregates and scalars.
136-
return self.confidence_interval is not None
137-
138131
@cached_property
139132
def reliability(self) -> Reliability.ValueType:
140133
if not self.is_extrapolated or not self.is_data_present:
@@ -161,13 +154,6 @@ class PercentileExtrapolationContext(ExtrapolationContext):
161154
granularity: float
162155
width: float
163156

164-
@property
165-
def is_extrapolated(self) -> bool:
166-
# We infer if a column is extrapolated or not by the presence of the
167-
# confidence interval. It will be present for extrapolated aggregates
168-
# but not for non-extrapolated aggregates and scalars.
169-
return self.confidence_interval is not None
170-
171157
@cached_property
172158
def reliability(self) -> Reliability.ValueType:
173159
if not self.is_extrapolated or not self.is_data_present:

snuba/web/rpc/v1/resolvers/common/trace_item_table.py

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -44,14 +44,14 @@ def convert_results(
4444
extrapolation_context = ExtrapolationContext.from_row(column_name, row)
4545
res[column_name].attribute_name = column_name
4646
if value is None:
47-
4847
res[column_name].results.append(AttributeValue(is_null=True))
4948
else:
5049
res[column_name].results.append(converters[column_name](value))
51-
if extrapolation_context.is_extrapolated:
52-
res[column_name].reliabilities.append(
53-
extrapolation_context.reliability
54-
)
50+
51+
if extrapolation_context.is_extrapolated:
52+
res[column_name].reliabilities.append(
53+
extrapolation_context.reliability
54+
)
5555

5656
column_ordering = {column.label: i for i, column in enumerate(request.columns)}
5757

tests/web/rpc/v1/test_endpoint_trace_item_table/test_endpoint_trace_item_table_extrapolation.py

Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -784,3 +784,105 @@ def test_formula(self) -> None:
784784
],
785785
),
786786
]
787+
788+
def test_aggregation_with_nulls(self) -> None:
789+
spans_storage = get_storage(StorageKey("eap_spans"))
790+
start = BASE_TIME
791+
messages_a = [
792+
gen_message(
793+
start - timedelta(minutes=i),
794+
measurements={
795+
"custom_measurement": {"value": 1},
796+
"server_sample_rate": {"value": 1.0},
797+
},
798+
tags={"custom_tag": "a"},
799+
)
800+
for i in range(5)
801+
]
802+
messages_b = [
803+
gen_message(
804+
start - timedelta(minutes=i),
805+
measurements={
806+
"custom_measurement2": {"value": 1},
807+
"server_sample_rate": {"value": 1.0},
808+
},
809+
tags={"custom_tag": "b"},
810+
)
811+
for i in range(5)
812+
]
813+
write_raw_unprocessed_events(spans_storage, messages_a + messages_b) # type: ignore
814+
815+
ts = Timestamp(seconds=int(BASE_TIME.timestamp()))
816+
hour_ago = int((BASE_TIME - timedelta(hours=1)).timestamp())
817+
message = TraceItemTableRequest(
818+
meta=RequestMeta(
819+
project_ids=[1],
820+
organization_id=1,
821+
cogs_category="something",
822+
referrer="something",
823+
start_timestamp=Timestamp(seconds=hour_ago),
824+
end_timestamp=ts,
825+
trace_item_type=TraceItemType.TRACE_ITEM_TYPE_SPAN,
826+
),
827+
columns=[
828+
Column(
829+
key=AttributeKey(type=AttributeKey.TYPE_STRING, name="custom_tag")
830+
),
831+
Column(
832+
aggregation=AttributeAggregation(
833+
aggregate=Function.FUNCTION_SUM,
834+
key=AttributeKey(
835+
type=AttributeKey.TYPE_DOUBLE, name="custom_measurement"
836+
),
837+
label="sum(custom_measurement)",
838+
extrapolation_mode=ExtrapolationMode.EXTRAPOLATION_MODE_SAMPLE_WEIGHTED,
839+
)
840+
),
841+
Column(
842+
aggregation=AttributeAggregation(
843+
aggregate=Function.FUNCTION_SUM,
844+
key=AttributeKey(
845+
type=AttributeKey.TYPE_DOUBLE, name="custom_measurement2"
846+
),
847+
label="sum(custom_measurement2)",
848+
extrapolation_mode=ExtrapolationMode.EXTRAPOLATION_MODE_SAMPLE_WEIGHTED,
849+
)
850+
),
851+
],
852+
group_by=[
853+
AttributeKey(type=AttributeKey.TYPE_STRING, name="custom_tag"),
854+
],
855+
order_by=[
856+
TraceItemTableRequest.OrderBy(
857+
column=Column(
858+
key=AttributeKey(
859+
type=AttributeKey.TYPE_STRING, name="custom_tag"
860+
)
861+
),
862+
),
863+
],
864+
limit=5,
865+
)
866+
response = EndpointTraceItemTable().execute(message)
867+
assert response.column_values == [
868+
TraceItemColumnValues(
869+
attribute_name="custom_tag",
870+
results=[AttributeValue(val_str="a"), AttributeValue(val_str="b")],
871+
),
872+
TraceItemColumnValues(
873+
attribute_name="sum(custom_measurement)",
874+
results=[AttributeValue(val_double=5), AttributeValue(is_null=True)],
875+
reliabilities=[
876+
Reliability.RELIABILITY_LOW,
877+
Reliability.RELIABILITY_UNSPECIFIED,
878+
],
879+
),
880+
TraceItemColumnValues(
881+
attribute_name="sum(custom_measurement2)",
882+
results=[AttributeValue(is_null=True), AttributeValue(val_double=5)],
883+
reliabilities=[
884+
Reliability.RELIABILITY_UNSPECIFIED,
885+
Reliability.RELIABILITY_LOW,
886+
],
887+
),
888+
]

0 commit comments

Comments
 (0)