HTTP POST alert event to a webhook URL using a shared `reqwest::Client`. Retries with exponential backoff (3 attempts, 100ms base). Reusing the client avoids re-building the connection pool / TLS session cache per notification — `CREATE ALERT` rules firing at high rate would otherwise cause a SYN flood and TLS-handshake-dominated CPU.
(
client: &reqwest::Client,
url: &str,
event: &AlertEvent,
per_request_timeout: Duration,
)
| 102 | /// notification — `CREATE ALERT` rules firing at high rate would otherwise |
| 103 | /// cause a SYN flood and TLS-handshake-dominated CPU. |
| 104 | pub async fn notify_webhook_with_client( |
| 105 | client: &reqwest::Client, |
| 106 | url: &str, |
| 107 | event: &AlertEvent, |
| 108 | per_request_timeout: Duration, |
| 109 | ) { |
| 110 | let body = match sonic_rs::to_string(event) { |
| 111 | Ok(b) => b, |
| 112 | Err(e) => { |
| 113 | warn!(alert = event.alert_name, error = %e, "failed to serialize alert event"); |
| 114 | return; |
| 115 | } |
| 116 | }; |
| 117 | |
| 118 | let max_retries = 3u32; |
| 119 | for attempt in 0..max_retries { |
| 120 | match client |
| 121 | .post(url) |
| 122 | .header("Content-Type", "application/json") |
| 123 | .timeout(per_request_timeout) |
| 124 | .body(body.clone()) |
| 125 | .send() |
| 126 | .await |
| 127 | { |
| 128 | Ok(resp) if resp.status().is_success() => { |
| 129 | info!( |
| 130 | alert = event.alert_name, |
| 131 | url, |
| 132 | status = event.status, |
| 133 | "alert webhook delivered" |
| 134 | ); |
| 135 | return; |
| 136 | } |
| 137 | Ok(resp) if resp.status().is_client_error() && resp.status().as_u16() != 429 => { |
| 138 | // 4xx (except 429) is permanent failure. |
| 139 | warn!( |
| 140 | alert = event.alert_name, |
| 141 | url, |
| 142 | status_code = resp.status().as_u16(), |
| 143 | "alert webhook permanently rejected" |
| 144 | ); |
| 145 | return; |
| 146 | } |
| 147 | Ok(resp) => { |
| 148 | warn!( |
| 149 | alert = event.alert_name, |
| 150 | url, |
| 151 | status_code = resp.status().as_u16(), |
| 152 | attempt, |
| 153 | "alert webhook delivery failed, retrying" |
| 154 | ); |
| 155 | } |
| 156 | Err(e) => { |
| 157 | warn!( |
| 158 | alert = event.alert_name, |
| 159 | url, |
| 160 | attempt, |
| 161 | error = %e, |