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 statsig_types::Layer,
12 user::StatsigUserLoggable,
13 EvaluationDetails, SecondaryExposure,
14};
15
16use super::queued_event::{EnqueueOperation, QueuedEvent, QueuedExposure};
17
18pub enum EnqueueLayerParamExpoOp<'a> {
19 LayerRef(u64, &'a Layer, &'a str, ExposureTrigger),
20 LayerOwned(u64, Box<Layer>, String, ExposureTrigger),
21}
22
23impl<'a> EnqueueLayerParamExpoOp<'a> {
24 fn get_layer_ref(&'a self) -> &'a Layer {
25 match self {
26 EnqueueLayerParamExpoOp::LayerRef(_, layer, _, _) => layer,
27 EnqueueLayerParamExpoOp::LayerOwned(_, layer, _, _) => layer,
28 }
29 }
30
31 fn get_parameter_name_ref(&'a self) -> &'a str {
32 match self {
33 EnqueueLayerParamExpoOp::LayerRef(_, _, parameter_name, _) => parameter_name,
34 EnqueueLayerParamExpoOp::LayerOwned(_, _, parameter_name, _) => parameter_name.as_str(),
35 }
36 }
37}
38
39impl EnqueueOperation for EnqueueLayerParamExpoOp<'_> {
40 fn as_exposure(&self) -> Option<&impl QueuedExposure> {
41 Some(self)
42 }
43
44 fn into_queued_event(self, sampling_decision: EvtSamplingDecision) -> QueuedEvent {
45 let event = match self {
46 EnqueueLayerParamExpoOp::LayerRef(exposure_time, layer, parameter_name, trigger) => {
47 extract_from_layer_ref(
48 exposure_time,
49 layer,
50 parameter_name,
51 trigger,
52 sampling_decision,
53 )
54 }
55 EnqueueLayerParamExpoOp::LayerOwned(exposure_time, layer, parameter_name, trigger) => {
56 extract_from_layer_owned(
57 exposure_time,
58 layer,
59 parameter_name,
60 trigger,
61 sampling_decision,
62 )
63 }
64 };
65
66 QueuedEvent::LayerParamExposure(event)
67 }
68}
69
70impl<'a> QueuedExposure<'a> for EnqueueLayerParamExpoOp<'a> {
71 fn create_exposure_sampling_key(&self) -> ExposureSamplingKey {
72 let layer = self.get_layer_ref();
73
74 let user_data = &layer.__user.data;
75 let evaluation = layer.__evaluation.as_ref().map(|e| &e.base);
76
77 let pname = self.get_parameter_name_ref();
79 let pname_hash = ahash_str(pname);
80
81 ExposureSamplingKey::new(evaluation, user_data.as_ref(), pname_hash)
82 }
83
84 fn get_rule_id_ref(&'a self) -> &'a str {
85 &self.get_layer_ref().rule_id
86 }
87
88 fn get_extra_exposure_info_ref(&'a self) -> Option<&'a ExtraExposureInfo> {
89 self.get_layer_ref()
90 .__evaluation
91 .as_ref()?
92 .base
93 .exposure_info
94 .as_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<String>,
109 pub is_explicit: bool,
110 pub allocated_experiment: Option<String>,
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.unwrap_or_default(),
122 );
123 metadata.insert("parameterName".into(), self.parameter_name);
124 metadata.insert("isExplicitParameter".into(), self.is_explicit.to_string());
125
126 if let Some(version) = self.version {
127 metadata.insert("configVersion".into(), version.to_string());
128 }
129
130 if self.exposure_trigger == ExposureTrigger::Manual {
131 metadata.insert("isManualExposure".into(), "true".into());
132 }
133
134 if let Some(override_config_name) = self.override_config_name {
135 metadata.insert("overrideConfigName".into(), override_config_name);
136 }
137
138 let statsig_metadata = get_statsig_metadata_with_sampling_decision(self.sampling_decision);
139
140 let event = StatsigEvent {
141 event_name: LAYER_EXPOSURE_EVENT_NAME.into(),
142 value: None,
143 metadata: Some(metadata),
144 statsig_metadata: Some(statsig_metadata),
145 };
146
147 StatsigEventInternal::new(
148 self.exposure_time,
149 self.user,
150 event,
151 Some(self.secondary_exposures.unwrap_or_default()),
152 )
153 }
154}
155
156type ExtractFromEvaluationResult = (
157 bool,
158 Option<String>,
159 Option<Vec<SecondaryExposure>>,
160 Option<u32>,
161 Option<String>,
162);
163
164fn extract_exposure_info(layer: &Layer, parameter_name: &String) -> ExtractFromEvaluationResult {
165 let evaluation = match layer.__evaluation.as_ref() {
166 Some(eval) => eval,
167 None => return (false, None, None, None, None),
168 };
169
170 let is_explicit = evaluation.explicit_parameters.contains(parameter_name);
171 let secondary_exposures;
172 let mut allocated_experiment = None;
173
174 if is_explicit {
175 allocated_experiment = evaluation.allocated_experiment_name.clone();
176 secondary_exposures = Some(evaluation.base.secondary_exposures.clone());
177 } else {
178 secondary_exposures = evaluation.undelegated_secondary_exposures.clone();
179 }
180
181 let mut version = layer.__version;
183 let mut override_config_name = None;
184
185 if let Some(exposure_info) = evaluation.base.exposure_info.as_ref() {
186 version = exposure_info.version;
187 override_config_name = exposure_info.override_config_name.clone();
188 }
189
190 (
191 is_explicit,
192 allocated_experiment,
193 secondary_exposures,
194 version,
195 override_config_name,
196 )
197}
198
199fn extract_from_layer_ref(
200 exposure_time: u64,
201 layer: &Layer,
202 param_name: &str,
203 trigger: ExposureTrigger,
204 sampling_decision: EvtSamplingDecision,
205) -> QueuedLayerParamExposureEvent {
206 let parameter_name = param_name.to_string();
207 let (is_explicit, allocated_experiment, secondary_exposures, version, override_config_name) =
208 extract_exposure_info(layer, ¶meter_name);
209
210 QueuedLayerParamExposureEvent {
211 exposure_time,
212 user: layer.__user.clone(),
213 layer_name: layer.name.clone(),
214 rule_id: layer.rule_id.clone(),
215 parameter_name,
216 exposure_trigger: trigger,
217 evaluation_details: layer.details.clone(),
218 version,
219 sampling_decision,
220 override_config_name,
221 secondary_exposures,
222 is_explicit,
223 allocated_experiment,
224 }
225}
226
227fn extract_from_layer_owned(
228 exposure_time: u64,
229 layer: Box<Layer>,
230 parameter_name: String,
231 trigger: ExposureTrigger,
232 sampling_decision: EvtSamplingDecision,
233) -> QueuedLayerParamExposureEvent {
234 let (is_explicit, allocated_experiment, secondary_exposures, version, override_config_name) =
235 extract_exposure_info(&layer, ¶meter_name);
236
237 QueuedLayerParamExposureEvent {
238 exposure_time,
239 user: layer.__user,
240 layer_name: layer.name,
241 rule_id: layer.rule_id,
242 parameter_name,
243 exposure_trigger: trigger,
244 evaluation_details: layer.details,
245 version,
246 sampling_decision,
247 override_config_name,
248 secondary_exposures,
249 is_explicit,
250 allocated_experiment,
251 }
252}