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
79 let pname = self.get_parameter_name_ref();
81 let pname_hash = ahash_str(pname);
82
83 ExposureSamplingKey::new(evaluation, user_data.as_ref(), pname_hash)
84 }
85
86 fn get_rule_id_ref(&'a self) -> &'a str {
87 &self.get_layer_ref().rule_id
88 }
89
90 fn get_extra_exposure_info_ref(&'a self) -> Option<&'a ExtraExposureInfo> {
91 self.get_layer_ref()
92 .__evaluation
93 .as_ref()?
94 .base
95 .exposure_info
96 .as_ref()
97 }
98}
99
100pub struct QueuedLayerParamExposureEvent {
101 pub user: StatsigUserLoggable,
102 pub layer_name: String,
103 pub rule_id: String,
104 pub parameter_name: String,
105 pub secondary_exposures: Option<Vec<SecondaryExposure>>,
106 pub evaluation_details: EvaluationDetails,
107 pub version: Option<u32>,
108 pub exposure_trigger: ExposureTrigger,
109 pub sampling_decision: EvtSamplingDecision,
110 pub override_config_name: Option<InternedString>,
111 pub is_explicit: bool,
112 pub allocated_experiment: Option<InternedString>,
113 pub exposure_time: u64,
114}
115
116impl QueuedLayerParamExposureEvent {
117 pub fn into_statsig_event_internal(self) -> StatsigEventInternal {
118 let mut metadata = get_metadata_with_details(self.evaluation_details);
119 metadata.insert("config".into(), self.layer_name);
120 metadata.insert("ruleID".into(), self.rule_id);
121 metadata.insert(
122 "allocatedExperiment".into(),
123 self.allocated_experiment
124 .unwrap_or_default()
125 .unperformant_to_string(),
126 );
127 metadata.insert("parameterName".into(), self.parameter_name);
128 metadata.insert("isExplicitParameter".into(), self.is_explicit.to_string());
129
130 if let Some(version) = self.version {
131 metadata.insert("configVersion".into(), version.to_string());
132 }
133
134 if self.exposure_trigger == ExposureTrigger::Manual {
135 metadata.insert("isManualExposure".into(), "true".into());
136 }
137
138 if let Some(override_config_name) = self.override_config_name {
139 metadata.insert(
140 "overrideConfigName".into(),
141 override_config_name.unperformant_to_string(),
142 );
143 }
144
145 let statsig_metadata = get_statsig_metadata_with_sampling_decision(self.sampling_decision);
146
147 let event = StatsigEvent {
148 event_name: LAYER_EXPOSURE_EVENT_NAME.into(),
149 value: None,
150 metadata: Some(string_metadata_to_value_metadata(metadata)),
151 statsig_metadata: Some(statsig_metadata),
152 };
153
154 StatsigEventInternal::new(
155 self.exposure_time,
156 self.user,
157 event,
158 Some(self.secondary_exposures.unwrap_or_default()),
159 )
160 }
161}
162
163type ExtractFromEvaluationResult = (
164 bool,
165 Option<InternedString>,
166 Option<Vec<SecondaryExposure>>,
167 Option<u32>,
168 Option<InternedString>,
169);
170
171fn extract_exposure_info(layer: &Layer, parameter_name: &str) -> ExtractFromEvaluationResult {
172 let evaluation = match layer.__evaluation.as_ref() {
173 Some(eval) => eval,
174 None => return (false, None, None, None, None),
175 };
176
177 let is_explicit = evaluation.explicit_parameters.contains(parameter_name);
178 let secondary_exposures;
179 let mut allocated_experiment = None;
180
181 if is_explicit {
182 allocated_experiment = evaluation.allocated_experiment_name.clone();
183 secondary_exposures = Some(evaluation.base.secondary_exposures.clone());
184 } else {
185 secondary_exposures = evaluation.undelegated_secondary_exposures.clone();
186 }
187
188 let mut version = layer.__version;
190 let mut override_config_name = None;
191
192 if let Some(exposure_info) = evaluation.base.exposure_info.as_ref() {
193 version = exposure_info.version;
194 override_config_name = exposure_info.override_config_name.clone();
195 }
196
197 (
198 is_explicit,
199 allocated_experiment,
200 secondary_exposures,
201 version,
202 override_config_name,
203 )
204}
205
206fn extract_from_layer_ref(
207 exposure_time: u64,
208 layer: &Layer,
209 param_name: &str,
210 trigger: ExposureTrigger,
211 sampling_decision: EvtSamplingDecision,
212) -> QueuedLayerParamExposureEvent {
213 let parameter_name = param_name.to_string();
214 let (is_explicit, allocated_experiment, secondary_exposures, version, override_config_name) =
215 extract_exposure_info(layer, ¶meter_name);
216
217 let rule_id = match layer.__parameter_rule_ids {
218 Some(ref rule_ids) => rule_ids
219 .get(&InternedString::from_str_ref(param_name))
220 .map(|s| s.unperformant_to_string())
221 .unwrap_or_else(|| layer.rule_id.clone()),
222 None => layer.rule_id.clone(),
223 };
224
225 QueuedLayerParamExposureEvent {
226 exposure_time,
227 user: layer.__user.clone(),
228 layer_name: layer.name.clone(),
229 rule_id,
230 parameter_name,
231 exposure_trigger: trigger,
232 evaluation_details: layer.details.clone(),
233 version,
234 sampling_decision,
235 override_config_name,
236 secondary_exposures,
237 is_explicit,
238 allocated_experiment,
239 }
240}
241
242fn extract_from_layer_owned(
243 exposure_time: u64,
244 layer: Box<Layer>,
245 parameter_name: String,
246 trigger: ExposureTrigger,
247 sampling_decision: EvtSamplingDecision,
248) -> QueuedLayerParamExposureEvent {
249 let (is_explicit, allocated_experiment, secondary_exposures, version, override_config_name) =
250 extract_exposure_info(&layer, ¶meter_name);
251
252 let rule_id = match layer.__parameter_rule_ids {
253 Some(ref rule_ids) => rule_ids
254 .get(&InternedString::from_str_ref(parameter_name.as_str()))
255 .map(|s| s.unperformant_to_string())
256 .unwrap_or_else(|| layer.rule_id.clone()),
257 None => layer.rule_id.clone(),
258 };
259
260 QueuedLayerParamExposureEvent {
261 exposure_time,
262 user: layer.__user,
263 layer_name: layer.name,
264 rule_id,
265 parameter_name,
266 exposure_trigger: trigger,
267 evaluation_details: layer.details,
268 version,
269 sampling_decision,
270 override_config_name,
271 secondary_exposures,
272 is_explicit,
273 allocated_experiment,
274 }
275}