Skip to content

Commit 01d9e03

Browse files
authored
feat: add account webhook metrics and metrics-only mode (#2378)
This commmit adds two statsd counters in the account webhooks endpoint for - number of events received - number of events processed, with event type labels A metrics-only mode for the endpoint is also introduced.
1 parent e508549 commit 01d9e03

5 files changed

Lines changed: 56 additions & 3 deletions

File tree

docs/src/config.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,7 @@ The following configuration options are available.
106106
| <span id="SYNC_TOKENSERVER__STATSD_LABEL"></span>SYNC_TOKENSERVER__STATSD_LABEL | syncstorage.tokenserver | StatsD metrics label prefix |
107107
| <span id="SYNC_TOKENSERVER__TOKEN_DURATION"></span>SYNC_TOKENSERVER__TOKEN_DURATION | 3600 | Token TTL (1 hour) |
108108
| <span id="SYNC_TOKENSERVER__FXA_WEBHOOK_ENABLED"></span>SYNC_TOKENSERVER__FXA_WEBHOOK_ENABLED | false | Enable the FxA webhook endpoint. When disabled, the route is not registered. |
109+
| <span id="SYNC_TOKENSERVER__FXA_WEBHOOK_METRICS_ONLY"></span>SYNC_TOKENSERVER__FXA_WEBHOOK_METRICS_ONLY | false | Run the FxA webhook handler in metrics-only mode. Received events are counted but not processed. Only used if `FXA_WEBHOOK_ENABLED` is true. |
109110

110111
### Tokenserver+FxA Integration
111112

syncserver/src/tokenserver/extractors.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1345,6 +1345,7 @@ mod tests {
13451345
token_duration: TOKEN_DURATION,
13461346
set_verifiers: Vec::new(),
13471347
fxa_webhook_enabled: false,
1348+
fxa_webhook_metrics_only: false,
13481349
}
13491350
}
13501351

@@ -1367,6 +1368,7 @@ mod tests {
13671368
token_duration: TOKEN_DURATION,
13681369
set_verifiers,
13691370
fxa_webhook_enabled: true,
1371+
fxa_webhook_metrics_only: false,
13701372
}
13711373
}
13721374

syncserver/src/tokenserver/handlers.rs

Lines changed: 46 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ use serde_json::Value;
88
use tokio::time::timeout;
99
use utoipa::ToSchema;
1010

11+
use syncserver_common::Metrics;
1112
use tokenserver_auth::{MakeTokenPlaintext, Tokenlib, TokenserverOrigin};
1213
use tokenserver_common::{NodeType, TokenserverError};
1314
use tokenserver_db::{
@@ -277,27 +278,42 @@ async fn update_user(
277278
}
278279
}
279280

281+
// statsd counter names
282+
const METRIC_EVENTS_RECEIVED: &str = "webhook.events_received";
283+
const METRIC_EVENTS_PROCESSED: &str = "webhook.events_processed";
284+
280285
pub async fn handle_fxa_events(
281286
FxaWebhookToken(claims): FxaWebhookToken,
282287
DbWrapper(mut db): DbWrapper,
283288
state: Data<super::ServerState>,
284289
) -> Result<HttpResponse, TokenserverError> {
290+
let metrics = Metrics::from(&state.metrics);
291+
292+
let Some(events) = claims.events.as_object() else {
293+
return Ok(HttpResponse::Ok().finish());
294+
};
295+
296+
// Count every event received.
297+
metrics.count(METRIC_EVENTS_RECEIVED, events.len() as i64);
298+
299+
if state.fxa_webhook_metrics_only {
300+
return Ok(HttpResponse::Ok().finish());
301+
}
302+
285303
let service_id = db
286304
.get_service_id(tokenserver_db::params::GetServiceId {
287305
service: SYNC_SERVICE_NAME.to_owned(),
288306
})
289307
.await?
290308
.id;
291-
let Some(events) = claims.events.as_object() else {
292-
return Ok(HttpResponse::Ok().finish());
293-
};
294309

295310
for event_type in events.keys() {
296311
let email = format!("{}@{}", claims.sub, state.fxa_email_domain);
297312
match event_type.as_str() {
298313
"https://schemas.accounts.firefox.com/event/delete-user" => {
299314
info!("Processing account delete for {}", email);
300315
db.retire_user(RetireUser { service_id, email }).await?;
316+
metrics.incr_with_tag(METRIC_EVENTS_PROCESSED, "event_type", "delete_user");
301317
}
302318
"https://schemas.accounts.firefox.com/event/password-change" => {
303319
if let Some(change_time_ms) = events[event_type]
@@ -312,6 +328,7 @@ pub async fn handle_fxa_events(
312328
keys_changed_at: None,
313329
})
314330
.await?;
331+
metrics.incr_with_tag(METRIC_EVENTS_PROCESSED, "event_type", "password_change");
315332
}
316333
}
317334
_ => {
@@ -444,6 +461,7 @@ mod tests {
444461
token_duration: 3600,
445462
set_verifiers,
446463
fxa_webhook_enabled: true,
464+
fxa_webhook_metrics_only: false,
447465
}
448466
}
449467

@@ -580,6 +598,31 @@ mod tests {
580598
assert_eq!(retire_user_calls[0].email, "quux@api.accounts.firefox.com");
581599
}
582600

601+
#[actix_web::test]
602+
async fn test_metrics_only_mode_skips_processing() {
603+
let verifier =
604+
SETVerifierImpl::new(&test_jwk(), "testo", "https://accounts.firefox.com/").unwrap();
605+
let (pool, call_log) = MockDbPool::with_capture();
606+
let mut state = make_state_with_db_pool(vec![verifier], Box::new(pool));
607+
state.fxa_webhook_metrics_only = true;
608+
let app = make_app_from_state(state).await;
609+
let token = make_set(
610+
"quux",
611+
"testo",
612+
json!({"https://schemas.accounts.firefox.com/event/delete-user": {}}),
613+
3600,
614+
TEST_PRIVATE_KEY_PEM,
615+
);
616+
let req = TestRequest::post()
617+
.uri("/1.0/webhooks/fxa/events")
618+
.insert_header(("Authorization", format!("Bearer {token}")))
619+
.to_request();
620+
let resp = test::call_service(&app, req).await;
621+
// no database mutation in metrics-only mode
622+
assert_eq!(resp.status(), 200);
623+
assert_eq!(call_log.retire_user.lock().unwrap().len(), 0);
624+
}
625+
583626
#[actix_web::test]
584627
async fn test_password_change_event() {
585628
let verifier =

syncserver/src/tokenserver/mod.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ pub struct ServerState {
3434
pub token_duration: u64,
3535
pub set_verifiers: Vec<SETVerifierImpl>,
3636
pub fxa_webhook_enabled: bool,
37+
pub fxa_webhook_metrics_only: bool,
3738
}
3839

3940
impl ServerState {
@@ -115,6 +116,7 @@ impl ServerState {
115116
token_duration: settings.token_duration,
116117
set_verifiers,
117118
fxa_webhook_enabled: settings.fxa_webhook_enabled,
119+
fxa_webhook_metrics_only: settings.fxa_webhook_metrics_only,
118120
})
119121
}
120122

tokenserver-settings/src/lib.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,10 @@ pub struct Settings {
7171
/// Whether to enable the FxA webhook endpoint.
7272
/// Defaults to false.
7373
pub fxa_webhook_enabled: bool,
74+
/// Whether the FxA webhook handler runs in metrics-only mode. When enabled, received events
75+
/// are counted but not processed.
76+
/// Defaults to false.
77+
pub fxa_webhook_metrics_only: bool,
7478
}
7579

7680
impl Default for Settings {
@@ -100,6 +104,7 @@ impl Default for Settings {
100104
init_node_url: None,
101105
init_node_capacity: 100000,
102106
fxa_webhook_enabled: false,
107+
fxa_webhook_metrics_only: false,
103108
}
104109
}
105110
}

0 commit comments

Comments
 (0)