statsig_rust/event_logging/event_queue/
queued_layer_param_expo.rs1use crate::{
2 evaluation::evaluation_types::ExtraExposureInfo,
3 event_logging::{
4 event_logger::ExposureTrigger,
5 exposure_sampling::{EvtSamplingDecision, ExposureSamplingKey},
6 exposure_utils::{get_metadata_with_details, get_statsig_metadata_with_sampling_decision},
7 statsig_event::StatsigEvent,
8 statsig_event_internal::{StatsigEventInternal, LAYER_EXPOSURE_EVENT_NAME},
9 },
10 hashing::ahash_str,
11 interned_string::InternedString,
12 statsig_types::Layer,
13 user::StatsigUserLoggable,
14 EvaluationDetails, SecondaryExposure,
15};
16
17use super::queued_event::{EnqueueOperation, QueuedEvent, QueuedExposure};
18use crate::event_logging::statsig_event::string_metadata_to_value_metadata;
19
20pub enum EnqueueLayerParamExpoOp<'a> {
21 LayerRef(u64, &'a Layer, &'a str, ExposureTrigger),
22 LayerOwned(u64, Box<Layer>, String, ExposureTrigger),
23}
24
25impl<'a> EnqueueLayerParamExpoOp<'a> {
26 fn get_layer_ref(&'a self) -> &'a Layer {
27 match self {
28 EnqueueLayerParamExpoOp::LayerRef(_, layer, _, _) => layer,
29 EnqueueLayerParamExpoOp::LayerOwned(_, layer, _, _) => layer,
30 }
31 }
32
33 fn get_parameter_name_ref(&'a self) -> &'a str {
34 match self {
35 EnqueueLayerParamExpoOp::LayerRef(_, _, parameter_name, _) => parameter_name,
36 EnqueueLayerParamExpoOp::LayerOwned(_, _, parameter_name, _) => parameter_name.as_str(),
37 }
38 }
39}
40
41impl EnqueueOperation for EnqueueLayerParamExpoOp<'_> {
42 fn as_exposure(&self) -> Option<&impl QueuedExposure<'_>> {
43 Some(self)
44 }
45
46 fn into_queued_event(self, sampling_decision: EvtSamplingDecision) -> QueuedEvent {
47 let event = match self {
48 EnqueueLayerParamExpoOp::LayerRef(exposure_time, layer, parameter_name, trigger) => {
49 extract_from_layer_ref(
50 exposure_time,
51 layer,
52 parameter_name,
53 trigger,
54 sampling_decision,
55 )
56 }
57 EnqueueLayerParamExpoOp::LayerOwned(exposure_time, layer, parameter_name, trigger) => {
58 extract_from_layer_owned(
59 exposure_time,
60 layer,
61 parameter_name,
62 trigger,
63 sampling_decision,
64 )
65 }
66 };
67
68 QueuedEvent::LayerParamExposure(event)
69 }
70}
71
72impl<'a> QueuedExposure<'a> for EnqueueLayerParamExpoOp<'a> {
73 fn create_exposure_sampling_key(&self) -> ExposureSamplingKey {
74 let layer = self.get_layer_ref();
75
76 let user_data = &layer.__user.data;
77 let evaluation = layer.__evaluation.as_ref().map(|e| &e.base);
78 let unit_id_type = layer
79 .__evaluation
80 .as_ref()
81 .and_then(|e| e.id_type.as_ref())
82 .map(|id| id.as_str());
83 let pname = self.get_parameter_name_ref();
85 let pname_hash = ahash_str(pname);
86 ExposureSamplingKey::new(evaluation, user_data, pname_hash, unit_id_type)
87 }
88
89 fn get_rule_id_ref(&'a self) -> &'a str {
90 &self.get_layer_ref().rule_id
91 }
92
93 fn get_extra_exposure_info_ref(&'a self) -> Option<&'a ExtraExposureInfo> {
94 get_layer_exposure_info(self.get_layer_ref())
95 }
96}
97
98pub struct QueuedLayerParamExposureEvent {
99 pub user: StatsigUserLoggable,
100 pub layer_name: String,
101 pub rule_id: String,
102 pub parameter_name: String,
103 pub secondary_exposures: Option<Vec<SecondaryExposure>>,
104 pub evaluation_details: EvaluationDetails,
105 pub version: Option<u32>,
106 pub exposure_trigger: ExposureTrigger,
107 pub sampling_decision: EvtSamplingDecision,
108 pub override_config_name: Option<InternedString>,
109 pub is_explicit: bool,
110 pub allocated_experiment: Option<InternedString>,
111 pub exposure_time: u64,
112}
113
114impl QueuedLayerParamExposureEvent {
115 pub fn into_statsig_event_internal(self) -> StatsigEventInternal {
116 let mut metadata = get_metadata_with_details(self.evaluation_details);
117 metadata.insert("config".into(), self.layer_name);
118 metadata.insert("ruleID".into(), self.rule_id);
119 metadata.insert(
120 "allocatedExperiment".into(),
121 self.allocated_experiment
122 .unwrap_or_default()
123 .unperformant_to_string(),
124 );
125 metadata.insert("parameterName".into(), self.parameter_name);
126 metadata.insert("isExplicitParameter".into(), self.is_explicit.to_string());
127
128 if let Some(version) = self.version {
129 metadata.insert("configVersion".into(), version.to_string());
130 }
131
132 if self.exposure_trigger == ExposureTrigger::Manual {
133 metadata.insert("isManualExposure".into(), "true".into());
134 }
135
136 if let Some(override_config_name) = self.override_config_name {
137 metadata.insert(
138 "overrideConfigName".into(),
139 override_config_name.unperformant_to_string(),
140 );
141 }
142
143 let statsig_metadata = get_statsig_metadata_with_sampling_decision(self.sampling_decision);
144
145 let event = StatsigEvent {
146 event_name: LAYER_EXPOSURE_EVENT_NAME.into(),
147 value: None,
148 metadata: Some(string_metadata_to_value_metadata(metadata)),
149 statsig_metadata: Some(statsig_metadata),
150 };
151
152 StatsigEventInternal::new(
153 self.exposure_time,
154 self.user,
155 event,
156 Some(self.secondary_exposures.unwrap_or_default()),
157 )
158 }
159}
160
161type ExtractFromEvaluationResult = (
162 bool,
163 Option<InternedString>,
164 Option<Vec<SecondaryExposure>>,
165 Option<u32>,
166 Option<InternedString>,
167);
168
169fn extract_exposure_info(layer: &Layer, parameter_name: &str) -> ExtractFromEvaluationResult {
170 let evaluation = match layer.__evaluation.as_ref() {
171 Some(eval) => eval,
172 None => return (false, None, None, None, None),
173 };
174
175 let is_explicit = evaluation.explicit_parameters.contains(parameter_name);
176 let secondary_exposures;
177 let mut allocated_experiment = None;
178
179 if is_explicit {
180 allocated_experiment = evaluation.allocated_experiment_name.clone();
181 secondary_exposures = Some(evaluation.base.secondary_exposures.clone());
182 } else {
183 secondary_exposures = evaluation.undelegated_secondary_exposures.clone();
184 }
185
186 let mut version = layer.__version;
188 let mut override_config_name = None;
189
190 if let Some(exposure_info) = get_layer_exposure_info(layer) {
191 version = exposure_info.version;
192 override_config_name = exposure_info.override_config_name.clone();
193 }
194
195 (
196 is_explicit,
197 allocated_experiment,
198 secondary_exposures,
199 version,
200 override_config_name,
201 )
202}
203
204fn get_layer_exposure_info(layer: &Layer) -> Option<&ExtraExposureInfo> {
205 layer
206 .__evaluation
207 .as_ref()
208 .and_then(|eval| eval.base.exposure_info.as_ref())
209 .or(layer.__exposure_info.as_ref())
210}
211
212fn extract_from_layer_ref(
213 exposure_time: u64,
214 layer: &Layer,
215 param_name: &str,
216 trigger: ExposureTrigger,
217 sampling_decision: EvtSamplingDecision,
218) -> QueuedLayerParamExposureEvent {
219 let parameter_name = param_name.to_string();
220 let (is_explicit, allocated_experiment, secondary_exposures, version, override_config_name) =
221 extract_exposure_info(layer, ¶meter_name);
222
223 let rule_id = match layer.__parameter_rule_ids {
224 Some(ref rule_ids) => rule_ids
225 .get(&InternedString::from_str_ref(param_name))
226 .map(|s| s.unperformant_to_string())
227 .unwrap_or_else(|| layer.rule_id.clone()),
228 None => layer.rule_id.clone(),
229 };
230
231 QueuedLayerParamExposureEvent {
232 exposure_time,
233 user: layer.__user.clone(),
234 layer_name: layer.name.clone(),
235 rule_id,
236 parameter_name,
237 exposure_trigger: trigger,
238 evaluation_details: layer.details.clone(),
239 version,
240 sampling_decision,
241 override_config_name,
242 secondary_exposures,
243 is_explicit,
244 allocated_experiment,
245 }
246}
247
248fn extract_from_layer_owned(
249 exposure_time: u64,
250 layer: Box<Layer>,
251 parameter_name: String,
252 trigger: ExposureTrigger,
253 sampling_decision: EvtSamplingDecision,
254) -> QueuedLayerParamExposureEvent {
255 let (is_explicit, allocated_experiment, secondary_exposures, version, override_config_name) =
256 extract_exposure_info(&layer, ¶meter_name);
257
258 let rule_id = match layer.__parameter_rule_ids {
259 Some(ref rule_ids) => rule_ids
260 .get(&InternedString::from_str_ref(parameter_name.as_str()))
261 .map(|s| s.unperformant_to_string())
262 .unwrap_or_else(|| layer.rule_id.clone()),
263 None => layer.rule_id.clone(),
264 };
265
266 QueuedLayerParamExposureEvent {
267 exposure_time,
268 user: layer.__user,
269 layer_name: layer.name,
270 rule_id,
271 parameter_name,
272 exposure_trigger: trigger,
273 evaluation_details: layer.details,
274 version,
275 sampling_decision,
276 override_config_name,
277 secondary_exposures,
278 is_explicit,
279 allocated_experiment,
280 }
281}