@@ -21,7 +21,8 @@ use relay_event_schema::protocol::{
21
21
SpanStatus , Tags , Timestamp , TraceContext , User , VALID_PLATFORMS ,
22
22
} ;
23
23
use relay_protocol:: {
24
- Annotated , Empty , Error , ErrorKind , FromValue , Getter , Meta , Object , Remark , RemarkType , Value ,
24
+ Annotated , Empty , Error , ErrorKind , FiniteF64 , FromValue , Getter , Meta , Object , Remark ,
25
+ RemarkType , TryFromFloatError , Value ,
25
26
} ;
26
27
use smallvec:: SmallVec ;
27
28
use uuid:: Uuid ;
@@ -866,10 +867,9 @@ pub fn normalize_measurements(
866
867
normalize_mobile_measurements ( measurements) ;
867
868
normalize_units ( measurements) ;
868
869
869
- let duration_millis = match ( start_timestamp, end_timestamp) {
870
- ( Some ( start) , Some ( end) ) => relay_common:: time:: chrono_to_positive_millis ( end - start) ,
871
- _ => 0.0 ,
872
- } ;
870
+ let duration_millis = start_timestamp. zip ( end_timestamp) . and_then ( |( start, end) | {
871
+ FiniteF64 :: new ( relay_common:: time:: chrono_to_positive_millis ( end - start) )
872
+ } ) ;
873
873
874
874
compute_measurements ( duration_millis, measurements) ;
875
875
if let Some ( measurements_config) = measurements_config {
@@ -910,8 +910,8 @@ pub fn normalize_performance_score(
910
910
// a measurement with weight is missing.
911
911
continue ;
912
912
}
913
- let mut score_total = 0.0f64 ;
914
- let mut weight_total = 0.0f64 ;
913
+ let mut score_total = FiniteF64 :: ZERO ;
914
+ let mut weight_total = FiniteF64 :: ZERO ;
915
915
for component in & profile. score_components {
916
916
// Skip optional components if they are not present on the event.
917
917
if component. optional
@@ -921,44 +921,55 @@ pub fn normalize_performance_score(
921
921
}
922
922
weight_total += component. weight ;
923
923
}
924
- if weight_total. abs ( ) < f64 :: EPSILON {
924
+ if weight_total. abs ( ) < FiniteF64 :: EPSILON {
925
925
// All components are optional or have a weight of `0`. We cannot compute
926
926
// component weights, so we bail.
927
927
continue ;
928
928
}
929
929
for component in & profile. score_components {
930
930
// Optional measurements that are not present are given a weight of 0.
931
- let mut normalized_component_weight = 0.0 ;
931
+ let mut normalized_component_weight = FiniteF64 :: ZERO ;
932
+
932
933
if let Some ( value) = measurements. get_value ( component. measurement . as_str ( ) ) {
933
- normalized_component_weight = component. weight / weight_total;
934
+ normalized_component_weight = component. weight . saturating_div ( weight_total) ;
934
935
let cdf = utils:: calculate_cdf_score (
935
- value. max ( 0.0 ) , // Webvitals can't be negative, but we need to clamp in case of bad data.
936
- component. p10 ,
937
- component. p50 ,
936
+ value. to_f64 ( ) . max ( 0.0 ) , // Webvitals can't be negative, but we need to clamp in case of bad data.
937
+ component. p10 . to_f64 ( ) ,
938
+ component. p50 . to_f64 ( ) ,
938
939
) ;
939
940
941
+ let cdf = Annotated :: try_from ( cdf) ;
942
+
940
943
measurements. insert (
941
944
format ! ( "score.ratio.{}" , component. measurement) ,
942
945
Measurement {
943
- value : cdf. into ( ) ,
946
+ value : cdf. clone ( ) ,
944
947
unit : ( MetricUnit :: Fraction ( FractionUnit :: Ratio ) ) . into ( ) ,
945
948
}
946
949
. into ( ) ,
947
950
) ;
948
951
949
- let component_score = cdf * normalized_component_weight;
950
- score_total += component_score;
951
- should_add_total = true ;
952
+ let component_score =
953
+ cdf. and_then ( |cdf| match cdf * normalized_component_weight {
954
+ Some ( v) => Annotated :: new ( v) ,
955
+ None => Annotated :: from_error ( TryFromFloatError , None ) ,
956
+ } ) ;
957
+
958
+ if let Some ( component_score) = component_score. value ( ) {
959
+ score_total += * component_score;
960
+ should_add_total = true ;
961
+ }
952
962
953
963
measurements. insert (
954
964
format ! ( "score.{}" , component. measurement) ,
955
965
Measurement {
956
- value : component_score. into ( ) ,
966
+ value : component_score,
957
967
unit : ( MetricUnit :: Fraction ( FractionUnit :: Ratio ) ) . into ( ) ,
958
968
}
959
969
. into ( ) ,
960
970
) ;
961
971
}
972
+
962
973
measurements. insert (
963
974
format ! ( "score.weight.{}" , component. measurement) ,
964
975
Measurement {
@@ -1042,7 +1053,10 @@ impl MutMeasurements for Span {
1042
1053
/// frames_frozen_rate := measurements.frames_frozen / measurements.frames_total
1043
1054
/// stall_percentage := measurements.stall_total_time / transaction.duration
1044
1055
/// ```
1045
- fn compute_measurements ( transaction_duration_ms : f64 , measurements : & mut Measurements ) {
1056
+ fn compute_measurements (
1057
+ transaction_duration_ms : Option < FiniteF64 > ,
1058
+ measurements : & mut Measurements ,
1059
+ ) {
1046
1060
if let Some ( frames_total) = measurements. get_value ( "frames_total" ) {
1047
1061
if frames_total > 0.0 {
1048
1062
if let Some ( frames_frozen) = measurements. get_value ( "frames_frozen" ) {
@@ -1063,22 +1077,25 @@ fn compute_measurements(transaction_duration_ms: f64, measurements: &mut Measure
1063
1077
}
1064
1078
1065
1079
// Get stall_percentage
1066
- if transaction_duration_ms > 0.0 {
1067
- if let Some ( stall_total_time) = measurements
1068
- . get ( "stall_total_time" )
1069
- . and_then ( Annotated :: value)
1070
- {
1071
- if matches ! (
1072
- stall_total_time. unit. value( ) ,
1073
- // Accept milliseconds or None, but not other units
1074
- Some ( & MetricUnit :: Duration ( DurationUnit :: MilliSecond ) | & MetricUnit :: None ) | None
1075
- ) {
1076
- if let Some ( stall_total_time) = stall_total_time. value . 0 {
1077
- let stall_percentage = Measurement {
1078
- value : ( stall_total_time / transaction_duration_ms) . into ( ) ,
1079
- unit : ( MetricUnit :: Fraction ( FractionUnit :: Ratio ) ) . into ( ) ,
1080
- } ;
1081
- measurements. insert ( "stall_percentage" . to_owned ( ) , stall_percentage. into ( ) ) ;
1080
+ if let Some ( transaction_duration_ms) = transaction_duration_ms {
1081
+ if transaction_duration_ms > 0.0 {
1082
+ if let Some ( stall_total_time) = measurements
1083
+ . get ( "stall_total_time" )
1084
+ . and_then ( Annotated :: value)
1085
+ {
1086
+ if matches ! (
1087
+ stall_total_time. unit. value( ) ,
1088
+ // Accept milliseconds or None, but not other units
1089
+ Some ( & MetricUnit :: Duration ( DurationUnit :: MilliSecond ) | & MetricUnit :: None )
1090
+ | None
1091
+ ) {
1092
+ if let Some ( stall_total_time) = stall_total_time. value . 0 {
1093
+ let stall_percentage = Measurement {
1094
+ value : ( stall_total_time / transaction_duration_ms) . into ( ) ,
1095
+ unit : ( MetricUnit :: Fraction ( FractionUnit :: Ratio ) ) . into ( ) ,
1096
+ } ;
1097
+ measurements. insert ( "stall_percentage" . to_owned ( ) , stall_percentage. into ( ) ) ;
1098
+ }
1082
1099
}
1083
1100
}
1084
1101
}
@@ -1409,7 +1426,7 @@ fn remove_invalid_measurements(
1409
1426
// Check if this is a builtin measurement:
1410
1427
for builtin_measurement in measurements_config. builtin_measurement_keys ( ) {
1411
1428
if builtin_measurement. name ( ) == name {
1412
- let value = measurement. value . value ( ) . unwrap_or ( & 0.0 ) ;
1429
+ let value = measurement. value . value ( ) . unwrap_or ( & FiniteF64 :: ZERO ) ;
1413
1430
// Drop negative values if the builtin measurement does not allow them.
1414
1431
if !builtin_measurement. allow_negative ( ) && * value < 0.0 {
1415
1432
meta. add_error ( Error :: invalid ( format ! (
@@ -2051,7 +2068,7 @@ mod tests {
2051
2068
fn test_keeps_valid_measurement ( ) {
2052
2069
let name = "lcp" ;
2053
2070
let measurement = Measurement {
2054
- value : Annotated :: new ( 420.69 ) ,
2071
+ value : Annotated :: new ( 420.69 . try_into ( ) . unwrap ( ) ) ,
2055
2072
unit : Annotated :: new ( MetricUnit :: Duration ( DurationUnit :: MilliSecond ) ) ,
2056
2073
} ;
2057
2074
@@ -2062,7 +2079,7 @@ mod tests {
2062
2079
fn test_drops_too_long_measurement_names ( ) {
2063
2080
let name = "lcpppppppppppppppppppppppppppp" ;
2064
2081
let measurement = Measurement {
2065
- value : Annotated :: new ( 420.69 ) ,
2082
+ value : Annotated :: new ( 420.69 . try_into ( ) . unwrap ( ) ) ,
2066
2083
unit : Annotated :: new ( MetricUnit :: Duration ( DurationUnit :: MilliSecond ) ) ,
2067
2084
} ;
2068
2085
@@ -2073,7 +2090,7 @@ mod tests {
2073
2090
fn test_drops_measurements_with_invalid_characters ( ) {
2074
2091
let name = "i æm frøm nørwåy" ;
2075
2092
let measurement = Measurement {
2076
- value : Annotated :: new ( 420.69 ) ,
2093
+ value : Annotated :: new ( 420.69 . try_into ( ) . unwrap ( ) ) ,
2077
2094
unit : Annotated :: new ( MetricUnit :: Duration ( DurationUnit :: MilliSecond ) ) ,
2078
2095
} ;
2079
2096
0 commit comments