postcrate_core/db/
chaos_configs.rs1use serde::{Deserialize, Serialize};
4use sqlx::{Row, SqlitePool};
5
6use crate::error::Result;
7
8#[derive(Debug, Clone, Serialize, Deserialize)]
9#[cfg_attr(feature = "specta", derive(specta::Type))]
10#[serde(rename_all = "camelCase")]
11pub struct ChaosConfig {
12 #[serde(default)]
13 pub enabled: bool,
14 #[serde(default)]
15 pub reject_4xx_prob: f32,
16 #[serde(default)]
17 pub reject_5xx_prob: f32,
18 #[serde(default)]
19 pub delay_ms_min: u32,
20 #[serde(default)]
21 pub delay_ms_max: u32,
22 #[serde(default)]
23 pub drop_during_data_prob: f32,
24 #[serde(default)]
25 pub malformed_resp_prob: f32,
26 #[serde(default)]
27 pub seed: Option<u64>,
28}
29
30impl Default for ChaosConfig {
31 fn default() -> Self {
32 Self {
33 enabled: false,
34 reject_4xx_prob: 0.0,
35 reject_5xx_prob: 0.0,
36 delay_ms_min: 0,
37 delay_ms_max: 0,
38 drop_during_data_prob: 0.0,
39 malformed_resp_prob: 0.0,
40 seed: None,
41 }
42 }
43}
44
45pub(crate) async fn get(pool: &SqlitePool, mailbox_id: &str) -> Result<ChaosConfig> {
46 let row = sqlx::query(
47 r"SELECT enabled, reject_4xx_prob, reject_5xx_prob, delay_ms_min, delay_ms_max,
48 drop_during_data_prob, malformed_resp_prob, seed
49 FROM chaos_configs WHERE mailbox_id = ?",
50 )
51 .bind(mailbox_id)
52 .fetch_optional(pool)
53 .await?;
54
55 let Some(row) = row else {
56 return Ok(ChaosConfig::default());
57 };
58
59 Ok(ChaosConfig {
60 enabled: row.try_get::<i64, _>("enabled").unwrap_or(0) != 0,
61 reject_4xx_prob: row.try_get::<f64, _>("reject_4xx_prob").unwrap_or(0.0) as f32,
62 reject_5xx_prob: row.try_get::<f64, _>("reject_5xx_prob").unwrap_or(0.0) as f32,
63 delay_ms_min: row.try_get::<i64, _>("delay_ms_min").unwrap_or(0) as u32,
64 delay_ms_max: row.try_get::<i64, _>("delay_ms_max").unwrap_or(0) as u32,
65 drop_during_data_prob: row
66 .try_get::<f64, _>("drop_during_data_prob")
67 .unwrap_or(0.0) as f32,
68 malformed_resp_prob: row.try_get::<f64, _>("malformed_resp_prob").unwrap_or(0.0) as f32,
69 seed: row.try_get::<i64, _>("seed").ok().map(|x| x as u64),
70 })
71}
72
73pub(crate) async fn upsert(
74 pool: &SqlitePool,
75 mailbox_id: &str,
76 cfg: &ChaosConfig,
77) -> Result<()> {
78 sqlx::query(
79 r"INSERT INTO chaos_configs
80 (mailbox_id, enabled, reject_4xx_prob, reject_5xx_prob,
81 delay_ms_min, delay_ms_max, drop_during_data_prob,
82 malformed_resp_prob, seed)
83 VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)
84 ON CONFLICT(mailbox_id) DO UPDATE SET
85 enabled = excluded.enabled,
86 reject_4xx_prob = excluded.reject_4xx_prob,
87 reject_5xx_prob = excluded.reject_5xx_prob,
88 delay_ms_min = excluded.delay_ms_min,
89 delay_ms_max = excluded.delay_ms_max,
90 drop_during_data_prob = excluded.drop_during_data_prob,
91 malformed_resp_prob = excluded.malformed_resp_prob,
92 seed = excluded.seed",
93 )
94 .bind(mailbox_id)
95 .bind(i64::from(cfg.enabled))
96 .bind(f64::from(cfg.reject_4xx_prob))
97 .bind(f64::from(cfg.reject_5xx_prob))
98 .bind(i64::from(cfg.delay_ms_min))
99 .bind(i64::from(cfg.delay_ms_max))
100 .bind(f64::from(cfg.drop_during_data_prob))
101 .bind(f64::from(cfg.malformed_resp_prob))
102 .bind(cfg.seed.map(|s| s as i64))
103 .execute(pool)
104 .await?;
105 Ok(())
106}