autopush_common/db/bigtable/
mod.rs1mod bigtable_client;
23mod pool;
24
25pub use bigtable_client::BigTableClientImpl;
26pub use bigtable_client::error::BigTableError;
27
28use serde::Deserialize;
29use std::time::Duration;
30use tonic::metadata::MetadataMap;
31
32use crate::db::bigtable::bigtable_client::MetadataBuilder;
33use crate::db::error::DbError;
34use crate::util::deserialize_opt_u32_to_duration;
35
36fn retry_default() -> usize {
37 bigtable_client::RETRY_COUNT
38}
39
40#[derive(Clone, Debug, Deserialize)]
42pub struct BigTableDbSettings {
43 #[serde(default)]
50 pub table_name: String,
51 #[serde(default)]
54 pub app_profile_id: String,
55 #[serde(default)]
56 pub router_family: String,
57 #[serde(default)]
58 pub message_family: String,
59 #[serde(default)]
60 pub message_topic_family: String,
61 #[serde(default)]
62 pub database_pool_max_size: Option<u32>,
63 #[serde(default)]
65 #[serde(deserialize_with = "deserialize_opt_u32_to_duration")]
66 pub database_pool_create_timeout: Option<Duration>,
67 #[serde(default)]
69 #[serde(deserialize_with = "deserialize_opt_u32_to_duration")]
70 pub database_pool_wait_timeout: Option<Duration>,
71 #[serde(default)]
73 #[serde(deserialize_with = "deserialize_opt_u32_to_duration")]
74 pub database_pool_recycle_timeout: Option<Duration>,
75 #[serde(default)]
77 #[serde(deserialize_with = "deserialize_opt_u32_to_duration")]
78 pub database_pool_connection_ttl: Option<Duration>,
79 #[serde(default)]
81 #[serde(deserialize_with = "deserialize_opt_u32_to_duration")]
82 pub database_pool_max_idle: Option<Duration>,
83 #[serde(default)]
85 pub route_to_leader: bool,
86 #[serde(default = "retry_default")]
88 pub retry_count: usize,
89 #[serde(default)]
91 #[serde(deserialize_with = "deserialize_opt_u32_to_duration")]
92 pub max_router_ttl: Option<Duration>,
93}
94
95#[allow(clippy::derivable_impls)]
97#[cfg(test)]
98impl Default for BigTableDbSettings {
99 fn default() -> Self {
100 use crate::MAX_ROUTER_TTL_SECS;
101
102 Self {
103 table_name: Default::default(),
104 router_family: Default::default(),
105 message_family: Default::default(),
106 message_topic_family: Default::default(),
107 database_pool_max_size: Default::default(),
108 database_pool_create_timeout: Default::default(),
109 database_pool_wait_timeout: Default::default(),
110 database_pool_recycle_timeout: Default::default(),
111 database_pool_connection_ttl: Default::default(),
112 database_pool_max_idle: Default::default(),
113 route_to_leader: Default::default(),
114 retry_count: Default::default(),
115 app_profile_id: Default::default(),
116 max_router_ttl: Some(Duration::from_secs(MAX_ROUTER_TTL_SECS)),
117 }
118 }
119}
120
121impl BigTableDbSettings {
122 pub fn metadata(&self) -> Result<MetadataMap, BigTableError> {
123 MetadataBuilder::with_prefix(&self.table_name)
124 .routing_param("table_name", &self.table_name)
125 .route_to_leader(self.route_to_leader)
126 .build()
127 }
128
129 pub fn health_metadata(&self) -> Result<MetadataMap, BigTableError> {
131 self.metadata()
132 }
133
134 pub fn get_instance_name(&self) -> Result<String, BigTableError> {
135 let parts: Vec<&str> = self.table_name.split('/').collect();
136 if parts.len() < 4 || parts[0] != "projects" || parts[2] != "instances" {
137 return Err(BigTableError::Config(
138 "Invalid table name specified. Cannot parse instance".to_owned(),
139 ));
140 }
141 Ok(parts[0..4].join("/"))
142 }
143}
144
145impl TryFrom<&str> for BigTableDbSettings {
146 type Error = DbError;
147 fn try_from(setting_string: &str) -> Result<Self, Self::Error> {
148 let mut me: Self = serde_json::from_str(setting_string)
149 .map_err(|e| DbError::General(format!("Could not parse DdbSettings: {e:?}")))?;
150
151 if me.table_name.starts_with('/') {
152 return Err(DbError::ConnectionError(
153 "Table name path begins with a '/'".to_owned(),
154 ));
155 };
156
157 if me.app_profile_id.is_empty() {
161 "default".clone_into(&mut me.app_profile_id);
162 }
163
164 Ok(me)
165 }
166}
167
168mod tests {
169
170 #[test]
171 fn test_settings_parse() -> Result<(), crate::db::error::DbError> {
172 let settings =
173 super::BigTableDbSettings::try_from("{\"database_pool_create_timeout\": 123}")?;
174 assert_eq!(
175 settings.database_pool_create_timeout,
176 Some(std::time::Duration::from_secs(123))
177 );
178 Ok(())
179 }
180 #[test]
181 fn test_get_instance() -> Result<(), super::BigTableError> {
182 let settings = super::BigTableDbSettings {
183 table_name: "projects/foo/instances/bar/tables/gorp".to_owned(),
184 ..Default::default()
185 };
186 let res = settings.get_instance_name()?;
187 assert_eq!(res.as_str(), "projects/foo/instances/bar");
188
189 let settings = super::BigTableDbSettings {
190 table_name: "projects/foo/".to_owned(),
191 ..Default::default()
192 };
193 assert!(settings.get_instance_name().is_err());
194
195 let settings = super::BigTableDbSettings {
196 table_name: "protect/foo/instances/bar/tables/gorp".to_owned(),
197 ..Default::default()
198 };
199 assert!(settings.get_instance_name().is_err());
200
201 let settings = super::BigTableDbSettings {
202 table_name: "project/foo/instance/bar/tables/gorp".to_owned(),
203 ..Default::default()
204 };
205 assert!(settings.get_instance_name().is_err());
206
207 Ok(())
208 }
209}