autoconnect_ws_sm/identified/
on_server_notif.rs1#[cfg(feature = "reliable_report")]
2use std::mem;
3
4use cadence::{Counted, Timed};
5
6use autoconnect_common::protocol::{ServerMessage, ServerNotification};
7use autopush_common::{
8 db::CheckStorageResponse, metric_name::MetricName, metrics::StatsdClientExt,
9 notification::Notification, util::sec_since_epoch,
10};
11
12use super::WebPushClient;
13use crate::error::{SMError, SMErrorKind};
14
15#[derive(Clone, Copy, PartialEq, Eq)]
18enum SendSource {
19 Direct,
21 Stored,
23}
24
25impl SendSource {
26 fn as_tag(&self) -> &'static str {
28 match self {
29 SendSource::Direct => "Direct",
30 SendSource::Stored => "Stored",
31 }
32 }
33}
34
35impl WebPushClient {
36 pub async fn on_server_notif(
44 &mut self,
45 snotif: ServerNotification,
46 ) -> Result<Vec<ServerMessage>, SMError> {
47 match snotif {
48 ServerNotification::Notification(notif) => Ok(vec![self.notif(notif)?]),
49 ServerNotification::CheckStorage => self.check_storage().await,
50 ServerNotification::Disconnect => Err(SMErrorKind::Ghost.into()),
51 }
52 }
53
54 pub fn on_server_notif_shutdown(&mut self, snotif: ServerNotification) {
58 trace!("WebPushClient::on_server_notif_shutdown");
59 if let ServerNotification::Notification(notif) = snotif {
60 let key = notif.version.clone();
61 self.ack_state.unacked_direct_notifs.insert(key, notif);
62 }
63 }
64
65 fn notif(&mut self, notif: Notification) -> Result<ServerMessage, SMError> {
67 trace!("WebPushClient::notif Sending a direct notif");
68 let response = notif.clone();
71 if notif.ttl != 0 {
72 let key = notif.version.clone();
75 self.ack_state.unacked_direct_notifs.insert(key, notif);
76 }
77 self.emit_send_metrics(&response, SendSource::Direct);
78 Ok(ServerMessage::Notification(response))
79 }
80
81 pub(super) async fn check_storage(&mut self) -> Result<Vec<ServerMessage>, SMError> {
86 trace!("🗄️ WebPushClient::check_storage");
87 self.flags.check_storage = true;
88 self.flags.include_topic = true;
89 self.check_storage_loop().await
90 }
91
92 pub(super) async fn check_storage_loop(&mut self) -> Result<Vec<ServerMessage>, SMError> {
97 trace!("🗄️ WebPushClient::check_storage_loop");
98 while self.flags.check_storage {
99 let smsgs = self.check_storage_advance().await?;
100 if !smsgs.is_empty() {
101 self.check_msg_limit().await?;
102 return Ok(smsgs);
103 }
104 }
105 if self.flags.increment_storage {
109 debug!("🗄️ WebPushClient::check_storage_loop increment_storage");
110 self.increment_storage().await?;
111 }
112 Ok(vec![])
113 }
114
115 async fn check_storage_advance(&mut self) -> Result<Vec<ServerMessage>, SMError> {
121 trace!("🗄️ WebPushClient::check_storage_advance");
122 let CheckStorageResponse {
123 include_topic,
124 mut messages,
125 timestamp,
126 } = self.do_check_storage().await?;
127
128 debug!(
129 "🗄️ WebPushClient::check_storage_advance \
130 include_topic: {} -> {} \
131 unacked_stored_highest: {:?} -> {:?}",
132 self.flags.include_topic,
133 include_topic,
134 self.ack_state.unacked_stored_highest,
135 timestamp
136 );
137 self.flags.include_topic = include_topic;
138 self.ack_state.unacked_stored_highest = timestamp;
139
140 if messages.is_empty() {
141 trace!("🗄️ WebPushClient::check_storage_advance finished");
142 self.flags.check_storage = false;
143 self.sent_from_storage = 0;
144 return Ok(vec![]);
145 }
146
147 let now_sec = sec_since_epoch();
149 let mut expired_messages = vec![];
151 messages.retain(|msg| {
154 if !msg.expired(now_sec) {
155 return true;
156 }
157 if msg.sortkey_timestamp.is_none() {
158 expired_messages.push(msg.clone());
159 }
160 false
161 });
162 #[allow(unused_mut)]
164 for mut msg in expired_messages {
165 let chidmessageid = msg.chidmessageid();
166 trace!("🉑 removing expired topic chidmessageid: {chidmessageid}");
167 self.app_state
168 .db
169 .remove_message(&self.uaid, &chidmessageid)
170 .await?;
171 #[cfg(feature = "reliable_report")]
172 msg.record_reliability(
173 &self.app_state.reliability,
174 autopush_common::reliability::ReliabilityState::Expired,
175 )
176 .await;
177 }
178
179 self.flags.increment_storage = !include_topic && timestamp.is_some();
180
181 if messages.is_empty() {
182 trace!("🗄️ WebPushClient::check_storage_advance empty response (filtered expired)");
183 return Ok(vec![]);
184 }
185
186 for msg in messages.iter() {
187 let key = msg.version.clone();
188 self.ack_state
189 .unacked_stored_notifs
190 .insert(key, msg.clone());
191 }
192 let smsgs: Vec<_> = messages
193 .into_iter()
194 .inspect(|msg| {
195 trace!("🗄️ WebPushClient::check_storage_advance Sending stored");
196 self.emit_send_metrics(msg, SendSource::Stored)
197 })
198 .map(ServerMessage::Notification)
199 .collect();
200
201 let count = smsgs.len() as u32;
202 debug!(
203 "🗄️ WebPushClient::check_storage_advance: sent_from_storage: {}, +{}",
204 self.sent_from_storage, count
205 );
206 self.sent_from_storage += count;
207 Ok(smsgs)
208 }
209
210 #[cfg(feature = "reliable_report")]
211 async fn record_state(
213 &self,
214 messages: &mut Vec<Notification>,
215 state: autopush_common::reliability::ReliabilityState,
216 ) {
217 for message in messages {
220 message
221 .record_reliability(&self.app_state.reliability, state)
222 .await;
223 }
224 }
225
226 async fn do_check_storage(&self) -> Result<CheckStorageResponse, SMError> {
237 let timestamp = self
239 .ack_state
240 .unacked_stored_highest
241 .or(self.current_timestamp);
242 trace!("🗄️ WebPushClient::do_check_storage {:?}", ×tamp);
243 let topic_resp = if self.flags.include_topic {
247 trace!("🗄️ WebPushClient::do_check_storage: fetch_topic_messages");
248 #[allow(unused_mut)]
250 let mut messages = self
251 .app_state
252 .db
253 .fetch_topic_messages(&self.uaid, 11)
254 .await?;
255 #[cfg(feature = "reliable_report")]
256 self.record_state(
258 &mut messages.messages,
259 autopush_common::reliability::ReliabilityState::Retrieved,
260 )
261 .await;
262 messages
263 } else {
264 Default::default()
265 };
266 if !topic_resp.messages.is_empty() {
268 trace!(
269 "🗄️ WebPushClient::do_check_storage: Topic message returns: {:#?}",
270 topic_resp.messages
271 );
272 self.app_state
273 .metrics
274 .count_with_tags(
275 "notification.message.retrieved",
276 topic_resp.messages.len() as i64,
277 )
278 .with_tag("topic", "true")
279 .send();
280 return Ok(CheckStorageResponse {
281 include_topic: true,
282 messages: topic_resp.messages,
283 timestamp: topic_resp.timestamp,
284 });
285 }
286 let timestamp = if self.flags.include_topic {
288 topic_resp.timestamp.or(self.current_timestamp)
292 } else {
293 timestamp
294 };
295 trace!(
296 "🗄️ WebPushClient::do_check_storage: fetch_timestamp_messages timestamp: {:?}",
297 timestamp
298 );
299 #[allow(unused_mut)]
300 let mut timestamp_resp = self
301 .app_state
302 .db
303 .fetch_timestamp_messages(&self.uaid, timestamp, 10)
304 .await?;
305 if !timestamp_resp.messages.is_empty() {
306 trace!(
307 "🗄️ WebPushClient::do_check_storage: Timestamp message returns: {:#?}",
308 timestamp_resp.messages
309 );
310 self.app_state
311 .metrics
312 .count_with_tags(
313 "notification.message.retrieved",
314 timestamp_resp.messages.len() as i64,
315 )
316 .with_tag("topic", "false")
317 .send();
318 #[cfg(feature = "reliable_report")]
319 self.record_state(
321 &mut timestamp_resp.messages,
322 autopush_common::reliability::ReliabilityState::Retrieved,
323 )
324 .await;
325 }
326
327 Ok(CheckStorageResponse {
328 include_topic: false,
329 messages: timestamp_resp.messages,
330 timestamp: timestamp_resp.timestamp.or(timestamp),
333 })
334 }
335
336 pub(super) async fn increment_storage(&mut self) -> Result<(), SMError> {
342 trace!(
343 "🗄️ WebPushClient::increment_storage: unacked_stored_highest: {:?}",
344 self.ack_state.unacked_stored_highest
345 );
346 let Some(timestamp) = self.ack_state.unacked_stored_highest else {
347 return Err(SMErrorKind::Internal(
348 "increment_storage w/ no unacked_stored_highest".to_owned(),
349 )
350 .into());
351 };
352 self.current_timestamp = Some(timestamp);
353 self.app_state
354 .db
355 .increment_storage(&self.uaid, timestamp)
356 .await?;
357 #[cfg(feature = "reliable_report")]
358 {
359 let mut notifs = mem::take(&mut self.ack_state.acked_stored_timestamp_notifs);
360 self.record_state(
361 &mut notifs,
362 autopush_common::reliability::ReliabilityState::Delivered,
363 )
364 .await;
365 }
366 self.flags.increment_storage = false;
367 Ok(())
368 }
369
370 async fn check_msg_limit(&mut self) -> Result<(), SMError> {
376 trace!(
377 "WebPushClient::check_msg_limit: sent_from_storage: {} msg_limit: {}",
378 self.sent_from_storage, self.app_state.settings.msg_limit
379 );
380 if self.sent_from_storage > self.app_state.settings.msg_limit {
381 self.app_state
384 .metrics
385 .incr_with_tags(MetricName::UaExpiration)
386 .with_tag("reason", "too_many_messages")
387 .send();
388 self.app_state.db.remove_user(&self.uaid).await?;
389 return Err(SMErrorKind::UaidReset.into());
390 }
391 Ok(())
392 }
393
394 fn emit_send_metrics(&self, notif: &Notification, source: SendSource) {
396 let metrics = &self.app_state.metrics;
397 let ua_info = &self.ua_info;
398 metrics
399 .incr_with_tags(MetricName::UaNotificationSent)
400 .with_tag("source", source.as_tag())
401 .with_tag("topic", ¬if.topic.is_some().to_string())
402 .with_tag("os", &ua_info.metrics_os)
403 .send();
405 metrics
406 .count_with_tags(
407 "ua.message_data",
408 notif.data.as_ref().map_or(0, |data| data.len() as i64),
409 )
410 .with_tag("source", source.as_tag())
411 .with_tag("os", &ua_info.metrics_os)
412 .send();
413 if source == SendSource::Stored {
416 let stored_ms = sec_since_epoch().saturating_sub(notif.timestamp) * 1000;
417 metrics
418 .time_with_tags(MetricName::NotificationStorageTime.as_ref(), stored_ms)
419 .with_tag("topic", ¬if.topic.is_some().to_string())
420 .with_tag("os", &ua_info.metrics_os)
421 .send();
422 }
423 }
424}