autopush_common/db/redis/
mod.rs

1/// This uses redis as a storage and management
2/// system for Autopush Notifications and Routing information.
3///
4/// Keys for the data are
5/// `autopush/user/{uaid}` String to store the user data
6/// `autopush/co/{uaid}` u64 to store the last time the user has interacted with the server
7/// `autopush/timestamp/{uaid}` u64 to store the last storage timestamp incremented by the server, once messages are delivered
8/// `autopush/channels/{uaid}` List to store the list of the channels of the user
9/// `autopush/msgs/{uaid}` SortedSet to store the list of the pending message ids for the user
10/// `autopush/msgs_exp/{uaid}` SortedSet to store the list of the pending message ids, ordered by expiry date, this is because SortedSet elements can't have independent expiry date
11/// `autopush/msg/{uaid}/{chidmessageid}`, with `{chidmessageid} == {chid}:{version}` String to store
12/// the content of the messages
13///
14mod redis_client;
15
16pub use redis_client::RedisClientImpl;
17
18use std::collections::HashMap;
19use std::time::Duration;
20
21use crate::db::error::DbError;
22use crate::notification::{default_ttl, Notification};
23use crate::util::deserialize_opt_u32_to_duration;
24
25use serde_derive::{Deserialize, Serialize};
26use uuid::Uuid;
27
28/// The settings for accessing the redis contents.
29#[derive(Clone, Debug, Deserialize)]
30pub struct RedisDbSettings {
31    #[serde(default)]
32    #[serde(deserialize_with = "deserialize_opt_u32_to_duration")]
33    pub create_timeout: Option<Duration>,
34    #[serde(default)]
35    #[serde(deserialize_with = "deserialize_opt_u32_to_duration")]
36    pub router_ttl: Option<Duration>,
37    #[serde(default)]
38    #[serde(deserialize_with = "deserialize_opt_u32_to_duration")]
39    pub notification_ttl: Option<Duration>,
40}
41
42#[allow(clippy::derivable_impls)]
43impl Default for RedisDbSettings {
44    fn default() -> Self {
45        Self {
46            create_timeout: Default::default(),
47            router_ttl: Some(Duration::from_secs(crate::MAX_ROUTER_TTL_SECS)),
48            notification_ttl: Some(Duration::from_secs(crate::MAX_NOTIFICATION_TTL_SECS)),
49        }
50    }
51}
52
53impl TryFrom<&str> for RedisDbSettings {
54    type Error = DbError;
55    fn try_from(setting_string: &str) -> Result<Self, Self::Error> {
56        let me: Self = match serde_json::from_str(setting_string) {
57            Ok(me) => me,
58            Err(e) if e.is_eof() => Self::default(),
59            Err(e) => Err(DbError::General(format!(
60                "Could not parse RedisDbSettings: {:?}",
61                e
62            )))?,
63        };
64        Ok(me)
65    }
66}
67
68#[derive(Serialize, Default, Deserialize, Clone, Debug)]
69/// A Publishable Notification record. This is a notification that is either
70/// received from a third party or is outbound to a UserAgent.
71///
72pub struct StorableNotification {
73    // Required values
74    #[serde(rename = "channelID")]
75    pub channel_id: Uuid,
76    pub version: String,
77    pub timestamp: u64,
78    // Possibly stored values, provided with a default.
79    #[serde(default = "default_ttl", skip_serializing)]
80    pub ttl: u64,
81    // Optional values, which imply a "None" default.
82    #[serde(skip_serializing)]
83    pub topic: Option<String>,
84    #[serde(skip_serializing_if = "Option::is_none")]
85    pub data: Option<String>,
86    #[serde(skip_serializing)]
87    pub sortkey_timestamp: Option<u64>,
88    #[serde(skip_serializing_if = "Option::is_none")]
89    pub headers: Option<HashMap<String, String>>,
90    #[cfg(feature = "reliable_report")]
91    #[serde(skip_serializing_if = "Option::is_none")]
92    pub reliability_id: Option<String>,
93    #[cfg(feature = "reliable_report")]
94    #[serde(skip_serializing_if = "Option::is_none")]
95    pub reliable_state: Option<crate::reliability::ReliabilityState>,
96}
97
98impl From<Notification> for StorableNotification {
99    fn from(notification: Notification) -> Self {
100        Self {
101            channel_id: notification.channel_id,
102            version: notification.version,
103            timestamp: notification.timestamp,
104            ttl: notification.ttl,
105            topic: notification.topic,
106            data: notification.data,
107            sortkey_timestamp: notification.sortkey_timestamp,
108            headers: notification.headers,
109            #[cfg(feature = "reliable_report")]
110            reliability_id: notification.reliability_id,
111            #[cfg(feature = "reliable_report")]
112            reliable_state: notification.reliable_state,
113        }
114    }
115}
116
117impl From<StorableNotification> for Notification {
118    fn from(storable: StorableNotification) -> Self {
119        Self {
120            channel_id: storable.channel_id,
121            version: storable.version,
122            timestamp: storable.timestamp,
123            ttl: storable.ttl,
124            topic: storable.topic,
125            data: storable.data,
126            sortkey_timestamp: storable.sortkey_timestamp,
127            headers: storable.headers,
128            #[cfg(feature = "reliable_report")]
129            reliability_id: storable.reliability_id,
130            #[cfg(feature = "reliable_report")]
131            reliable_state: storable.reliable_state,
132        }
133    }
134}
135
136#[cfg(test)]
137mod tests {
138
139    #[test]
140    fn test_settings_parse() -> Result<(), crate::db::error::DbError> {
141        let settings = super::RedisDbSettings::try_from("{\"timeout\": 123}")?;
142        assert_eq!(
143            settings.create_timeout,
144            Some(std::time::Duration::from_secs(123))
145        );
146        Ok(())
147    }
148}