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