Skip to content

Commit d8b2505

Browse files
obs(wave-2): NR alerts + Prom rules + reliability dashboard for 12 new metrics
Wave 2 observability sweep — every new metric shipped between 2026-05-15 and 2026-05-20 now has a P0/P1/P2 alert AND a dashboard tile AND a row in observability/METRICS-CATALOG.md. Per project memory `feedback-nr-observability-per-change` and CLAUDE.md Rule 25 ("Every new metric ships with its alert + dashboard tile in the same PR"). NR alerts added (newrelic/alerts/*.json — 11 files): - propagation-dead-lettered.json → P0 (paid-customer regrade fell through) - propagation-unknown-kind.json → P1 (api/worker image skew) - propagation-unexpected-skip.json → P0 (CHAOS F1 sentinel) - orphan-sweep-no-db-row.json → P0 (P0-3 atomic-provision symptom) - orphan-sweep-stuck-build-spike.json → P1 (build pipeline degraded) - orphan-sweep-reap-failed.json → P1 (reap path itself broken) - brevo-send-errors-spike.json → P1 (SEND-side email pipeline) - magic-link-email-rate-limited.json → P2 (abuse / NAT throttle) - provisioner-circuit-open.json → P1 (backend down, fail-fast active) - email-missing-renderer.json → P0 (silent customer-facing breakage) - worker-migration-mismatch.json → P1 (api/worker schema disagreement) Prometheus rules mirrored into k8s/prometheus-rules.yaml (8 new rule groups): - instant-worker-propagation-skip (PropagationUnexpectedSkip) - instant-worker-brevo-send (BrevoSendErrorsSpike + Warning) - instant-api-magic-link (MagicLinkEmailRateLimited) - instant-provisioner-circuit (ProvisionerCircuitOpen + HalfOpen) - instant-worker-email-renderer (EmailMissingRenderer) - instant-readyz (ReadyzCheckFailed + Degraded — mirror of prometheus/alert-rules.yml) - instant-api-brevo-webhook (BrevoDeliveryRatioLow + Warn — mirror of NR email-delivery-ratio) - instant-worker-billing (BillingChargeUndeliverable) Dashboard (newrelic/dashboards/instanode-reliability.json): 15 tiles spanning /readyz across 3 services, propagation queue depth + dead-letter rate + per-kind retry distribution, orphan sweep activity, magic-link rate-limit, Brevo send/error/webhook funnel, provisioner circuit-breaker state, missing-renderer events, storage presign rate, billing charge undeliverable, worker migration drift. METRICS-CATALOG (observability/METRICS-CATALOG.md): New doc enumerating every metric, its labels, emit timing (eager vs lazy *Vec), NR alert path, Prom rule name, and dashboard tile title. Lazy-emit gotcha documented so operators don't panic when a fresh deploy's /metrics page lacks an instant_*_total{label=...} series until the codepath fires. CLAUDE.md Rule 25 strengthened: Was "alert OR dashboard"; now requires BOTH plus METRICS-CATALOG row in the same PR. The Wave 2 sweep found 14 metrics with zero monitoring — that's the failure mode this rule prevents. DEPLOY: No auto-apply for infra repo. Operator applies via `infra/newrelic/apply.sh` (NerdGraph create/update mutations) and `kubectl apply -f infra/k8s/prometheus-rules.yaml`. The shell apply is idempotent — delete-then-create per existing alert pattern. COVERAGE BLOCK (per CLAUDE.md rule 17): Symptom: 14 metrics shipped 2026-05-15..2026-05-20 with no NR alert / no Prom rule / no dashboard tile. Enumeration: `grep -nE 'instant_.*_total|brevo_.*_total|readyz_check_status|circuit_state|email_missing_renderer' api/internal/metrics/*.go worker/internal/metrics/*.go provisioner/internal/circuit/*.go` Sites found: 13 unique Prometheus metrics + 1 healthz log field Sites touched: 14 (all covered) — see METRICS-CATALOG.md table Coverage test: jq empty over newrelic/alerts/*.json + python3 yaml-safe-load over k8s/prometheus-rules.yaml + jq empty over newrelic/dashboards/instanode-reliability.json — all PASS Live verified: N/A for declarative configs — verification is `bash newrelic/apply.sh` (operator) + observing the new alert NRQL evaluating against live Metric stream within ~2 min of apply. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent 1b65d9c commit d8b2505

14 files changed

Lines changed: 940 additions & 0 deletions

k8s/prometheus-rules.yaml

Lines changed: 256 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -279,3 +279,259 @@ spec:
279279
clean it — a k8s API outage or a DB write failure. Single transient events
280280
are fine; a sustained rate means the reap path itself is broken. Check
281281
instant-worker pod logs for `jobs.orphan_sweep.*_delete_failed` lines.
282+
283+
# CHAOS F1 (2026-05-20) — propagation_runner used to silently mark APPLIED on
284+
# any row whose target resource was missing/in an unexpected state. The fix
285+
# added unexpected_skip counting AND treats those rows as Failure (counts
286+
# against maxAttempts). This rule pages if the counter ticks more than once
287+
# — every unexpected_skip is one class of customer-facing nondeterminism
288+
# that the operator should investigate, not just the eventual dead-letter.
289+
- name: instant-worker-propagation-skip
290+
rules:
291+
- alert: PropagationUnexpectedSkip
292+
expr: |
293+
sum(rate(instant_propagation_unexpected_skip_total[15m])) by (kind, resource_type, skip_reason) > 0
294+
for: 15m
295+
labels:
296+
severity: critical
297+
service: worker
298+
annotations:
299+
summary: "propagation_runner unexpected_skip {{ $labels.kind }} / {{ $labels.resource_type }} / {{ $labels.skip_reason }}"
300+
description: |
301+
instant_propagation_unexpected_skip_total{kind="{{ $labels.kind }}",resource_type="{{ $labels.resource_type }}",skip_reason="{{ $labels.skip_reason }}"} > 0 for >15m.
302+
The post-CHAOS-F1 fix means the row is now counted as a Failure (will eventually dead-letter and fire PropagationDeadLettered above) — but the operator should investigate the schema/state drift NOW. Find the row in pending_propagations, walk back to the api caller, and decide whether the missing resource should be inserted, the propagation row should be aborted, or the underlying race condition should be patched.
303+
304+
# Email pipeline SEND-side (worker → Brevo /v3/smtp/email). RECEIVE-side
305+
# (Brevo → /webhooks/brevo) is covered by the NR email-delivery-ratio
306+
# alert. The classification label separates fixable-by-key-rotation
307+
# (permanent: 401/403) from fixable-by-backoff (transient: 5xx, 429) so the
308+
# operator knows which runbook to follow.
309+
- name: instant-worker-brevo-send
310+
rules:
311+
- alert: BrevoSendErrorsSpike
312+
expr: |
313+
sum(rate(brevo_send_errors_total[10m])) by (classification, status_code) * 600 > 10
314+
for: 10m
315+
labels:
316+
severity: critical
317+
service: worker
318+
annotations:
319+
summary: "brevo_send_errors_total > 10 in 10m (classification={{ $labels.classification }} status={{ $labels.status_code }})"
320+
description: |
321+
Worker → Brevo /v3/smtp/email is failing at >10 errors / 10m. Brevo
322+
returns 201 the instant it accepts a POST, so a degraded send path
323+
never affects the delivery-ratio alert — the messages simply never
324+
queue at Brevo. Check classification: `permanent` (401/403) means
325+
rotate BREVO_API_KEY; `transient` (5xx/429) means upstream
326+
brown-out, the worker's exponential backoff will catch up.
327+
328+
- alert: BrevoSendErrorsWarning
329+
expr: |
330+
sum(rate(brevo_send_errors_total[10m])) by (classification, status_code) * 600 > 3
331+
for: 10m
332+
labels:
333+
severity: warning
334+
service: worker
335+
annotations:
336+
summary: "brevo_send_errors_total > 3 in 10m (early warning) classification={{ $labels.classification }}"
337+
description: |
338+
Lower-severity early-warning twin of BrevoSendErrorsSpike. Lets the
339+
operator catch a slow upstream brown-out before it crosses the page
340+
threshold. No paging — just investigate during business hours.
341+
342+
# Login throttle — magic-link rate limiter denial counter. Noisier than a
343+
# page; fires WARNING on >10 events in 10m so the operator can correlate
344+
# against a /auth/login spike (legitimate users locked out by NAT, dashboard
345+
# retry bug, bot sweep).
346+
- name: instant-api-magic-link
347+
rules:
348+
- alert: MagicLinkEmailRateLimited
349+
expr: |
350+
sum(rate(instant_magic_link_email_rate_limited_total[10m])) * 600 > 10
351+
for: 10m
352+
labels:
353+
severity: warning
354+
service: api
355+
annotations:
356+
summary: "magic-link rate-limiter denied > 10 requests in 10m"
357+
description: |
358+
instant_magic_link_email_rate_limited_total > 10 events in 10m.
359+
Per-email/per-IP magic-link throttle is denying real users at a
360+
meaningful rate. Cross-check /auth/login traffic — a spike in
361+
denials with no spike in requests means the dashboard is firing
362+
retry storms; a spike in both means a bot sweep is wedging the
363+
per-IP quota for legitimate users behind the same NAT.
364+
365+
# Provisioner circuit breaker state. 0=CLOSED (healthy), 1=HALF_OPEN
366+
# (probing), 2=OPEN (fail-fast). Sustained OPEN means the provisioner
367+
# stopped attempting calls to a backend after N consecutive failures —
368+
# better than a hung call, but the customer flow is degraded.
369+
- name: instant-provisioner-circuit
370+
rules:
371+
- alert: ProvisionerCircuitOpen
372+
expr: |
373+
max(instant_provisioner_circuit_state) by (backend) == 2
374+
for: 5m
375+
labels:
376+
severity: critical
377+
service: provisioner
378+
annotations:
379+
summary: "provisioner circuit breaker OPEN for backend={{ $labels.backend }}"
380+
description: |
381+
instant_provisioner_circuit_state{backend="{{ $labels.backend }}"} = 2
382+
(OPEN) for >5m. The provisioner has stopped attempting calls to
383+
this backend; all /db/new, /cache/new, /nosql/new, or /queue/new
384+
requests targeting this backend are failing fast. Check the
385+
backend's own /healthz, kubectl logs the upstream pod, and decide:
386+
roll back the failing change, restart the backend pod, or wait
387+
for the half-open probe to close the breaker naturally.
388+
389+
- alert: ProvisionerCircuitHalfOpen
390+
expr: |
391+
max(instant_provisioner_circuit_state) by (backend) == 1
392+
for: 10m
393+
labels:
394+
severity: warning
395+
service: provisioner
396+
annotations:
397+
summary: "provisioner circuit breaker HALF_OPEN sustained 10m for backend={{ $labels.backend }}"
398+
description: |
399+
Backend hasn't closed back to CLOSED after 10 minutes in
400+
HALF_OPEN. Probes are still failing intermittently; investigate
401+
the upstream before the breaker flips back to OPEN.
402+
403+
# Go-rendered email registry sentinel. After 2026-05-15 expiry-email retro
404+
# the platform moved 18 email kinds off Brevo templates onto Go renderers.
405+
# This counter ticks every time the worker tries to send an email kind
406+
# that isn't in the eventEmailBuilders registry — a silent user-facing
407+
# breakage that page-priority must be P0.
408+
- name: instant-worker-email-renderer
409+
rules:
410+
- alert: EmailMissingRenderer
411+
expr: |
412+
sum(rate(email_missing_renderer_total[5m])) by (kind) > 0
413+
for: 5m
414+
labels:
415+
severity: critical
416+
service: worker
417+
annotations:
418+
summary: "email_missing_renderer kind={{ $labels.kind }} — silent user-facing breakage"
419+
description: |
420+
email_missing_renderer_total{kind="{{ $labels.kind }}"} > 0 for >5m.
421+
The worker tried to send an email with a kind that has no Go
422+
renderer registered in eventEmailBuilders — the message body
423+
dropped silently on the floor. Add the renderer to
424+
worker/internal/email/renderers/ AND add the kind to the
425+
coverage test that iterates eventEmailBuilders so this can't
426+
happen again for the same kind.
427+
428+
# /readyz deep-health gauge, exposed by api, worker, and provisioner via
429+
# readyz_check_status{service,check}. 1=ok, 0.5=degraded, 0=failed. This
430+
# rule mirrors the NR alert readyz-component-failed.json (which depends
431+
# on the OTLP forwarder) so a Prometheus-only operator gets the same
432+
# coverage. Same thresholds. Same labels.
433+
- name: instant-readyz
434+
rules:
435+
- alert: ReadyzCheckFailed
436+
expr: |
437+
max(readyz_check_status) by (service, check) == 0
438+
for: 5m
439+
labels:
440+
severity: critical
441+
annotations:
442+
summary: "{{ $labels.service }} /readyz check {{ $labels.check }} is FAILED"
443+
description: |
444+
readyz_check_status{service="{{ $labels.service }}",check="{{ $labels.check }}"} == 0 for >5m.
445+
The upstream this check probes is unreachable or rejecting calls.
446+
Open the failing pod's /readyz body for last_error, cross-check
447+
against the upstream status page, rotate keys if 401, file an
448+
incident if 5xx.
449+
450+
- alert: ReadyzCheckDegraded
451+
expr: |
452+
max(readyz_check_status) by (service, check) == 0.5
453+
for: 10m
454+
labels:
455+
severity: warning
456+
annotations:
457+
summary: "{{ $labels.service }} /readyz check {{ $labels.check }} is DEGRADED"
458+
description: |
459+
readyz_check_status{service="{{ $labels.service }}",check="{{ $labels.check }}"} == 0.5 for >10m.
460+
Upstream reachable but creds/policy off (the exact pattern that
461+
would have caught the 2026-05-20 Brevo silent-rejection bug weeks
462+
earlier — Brevo /v3/account returns 401 on every probe and the
463+
brevo check goes 0.5). Rotate the credential / re-validate the
464+
policy before the breaker escalates to failed.
465+
466+
# Brevo RECEIVE-side delivery-ratio alert (Prom mirror of NR
467+
# email-delivery-ratio-low.json). Webhook receiver counter
468+
# brevo_webhook_events_total{event} — `delivered` / total over 1h.
469+
# The 2026-05-20 sender-domain dropout would have shown here within an
470+
# hour as a falling ratio. Closed-set event labels keep cardinality
471+
# bounded.
472+
- name: instant-api-brevo-webhook
473+
rules:
474+
- alert: BrevoDeliveryRatioLow
475+
expr: |
476+
(
477+
sum(rate(brevo_webhook_events_total{event="delivered"}[1h]))
478+
)
479+
/
480+
clamp_min(
481+
sum(rate(brevo_webhook_events_total{event=~"delivered|bounced_hard|bounced_soft|rejected|complaint|deferred|unsubscribed|error"}[1h])),
482+
1e-9
483+
)
484+
< 0.95
485+
for: 1h
486+
labels:
487+
severity: critical
488+
service: api
489+
annotations:
490+
summary: "Brevo delivery ratio < 95% over 1h"
491+
description: |
492+
<95% of webhook-confirmed Brevo POSTs are landing as `delivered`.
493+
Common causes: sender domain dropped from validated list (the
494+
2026-05-20 launch incident), DKIM record stale, IP on a blocklist,
495+
or a broad bounce/rejected event class spiking.
496+
497+
- alert: BrevoDeliveryRatioWarn
498+
expr: |
499+
(
500+
sum(rate(brevo_webhook_events_total{event="delivered"}[1h]))
501+
)
502+
/
503+
clamp_min(
504+
sum(rate(brevo_webhook_events_total{event=~"delivered|bounced_hard|bounced_soft|rejected|complaint|deferred|unsubscribed|error"}[1h])),
505+
1e-9
506+
)
507+
< 0.98
508+
for: 1h
509+
labels:
510+
severity: warning
511+
service: api
512+
annotations:
513+
summary: "Brevo delivery ratio < 98% over 1h (early warning)"
514+
515+
# Make-good worklist signal — billing.charge_undeliverable. Customer paid
516+
# via Razorpay, platform could NOT translate the charge into a delivered
517+
# upgrade (team unresolvable, or resolved plan tier not in plans.yaml).
518+
# Each occurrence is a real customer charged but not upgraded; operator
519+
# must reconcile in the Razorpay dashboard.
520+
- name: instant-worker-billing
521+
rules:
522+
- alert: BillingChargeUndeliverable
523+
expr: |
524+
sum(rate(instant_billing_charge_undeliverable_total[1h])) > 0
525+
for: 5m
526+
labels:
527+
severity: critical
528+
service: worker
529+
annotations:
530+
summary: "billing.charge_undeliverable > 0 — paid customer not upgraded"
531+
description: |
532+
instant_billing_charge_undeliverable_total > 0 in the last hour.
533+
A subscription.charged Razorpay webhook confirmed a real card
534+
charge that the platform could NOT translate into a delivered
535+
upgrade. Find the team in audit_log where audit_kind = 'billing.charge_undeliverable',
536+
cross-check against the Razorpay dashboard, refund or hand-grant
537+
the tier.
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
{
2+
"name": "instant-worker — brevo_send_errors_total spike (SEND-side of email pipeline broken)",
3+
"type": "NRQL",
4+
"description": "P1 page. The SEND-side complement to email-delivery-ratio-low.json: that alert measures Brevo → /webhooks/brevo (RECEIVE), this one measures worker → Brevo /v3/smtp/email (SEND). Brevo's transactional API returns 201 the instant it accepts the POST, so a degraded send path (auth failure, rate limit, network) NEVER affects the delivery-ratio alert — the messages simply never queue at Brevo. The classification label separates fixable-by-key-rotation (permanent: 401/403) from fixable-by-backoff (transient: 5xx, 429) so the operator knows which runbook to follow. Source: worker/internal/metrics/metrics.go (BrevoSendErrorsTotal), emit site worker/internal/email/brevo/brevo.go.",
5+
"enabled": true,
6+
"nrql": {
7+
"query": "SELECT sum(brevo_send_errors_total) FROM Metric WHERE metricName = 'brevo_send_errors_total' FACET classification, status_code"
8+
},
9+
"terms": [
10+
{
11+
"priority": "CRITICAL",
12+
"operator": "ABOVE",
13+
"threshold": 10,
14+
"thresholdDuration": 600,
15+
"thresholdOccurrences": "ALL"
16+
},
17+
{
18+
"priority": "WARNING",
19+
"operator": "ABOVE",
20+
"threshold": 3,
21+
"thresholdDuration": 600,
22+
"thresholdOccurrences": "ALL"
23+
}
24+
],
25+
"signal": {
26+
"aggregationWindow": 60,
27+
"aggregationMethod": "EVENT_FLOW",
28+
"aggregationDelay": 120,
29+
"fillOption": "STATIC",
30+
"fillValue": 0
31+
},
32+
"expiration": {
33+
"expirationDuration": 3600,
34+
"openViolationOnExpiration": false,
35+
"closeViolationsOnExpiration": true
36+
},
37+
"violationTimeLimitSeconds": 86400
38+
}
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
{
2+
"name": "instant-worker — email_missing_renderer (no Go renderer for an email kind — silent user-facing breakage)",
3+
"type": "NRQL",
4+
"description": "P0 page. email_missing_renderer_total{kind} ticks every time the worker tries to send an email whose `kind` is not in the eventEmailBuilders registry. After the 2026-05-15 expiry-email retro the platform moved 18 email kinds off Brevo templates and onto Go-side renderers; this metric is the registry-iteration sentinel for that decision. ANY tick means a customer-facing email path is broken silently — no body content, no fallback, just a worker ERROR log + a missed message. Runbook: search NR Logs for `worker.email.missing_renderer` with the offending kind, then add the renderer to worker/internal/email/renderers/ AND add the kind to the coverage test that iterates eventEmailBuilders. Source: worker/internal/metrics/metrics.go (EmailMissingRendererTotal), emit site worker/internal/email/forwarder.go.",
5+
"enabled": true,
6+
"nrql": {
7+
"query": "SELECT sum(email_missing_renderer_total) FROM Metric WHERE metricName = 'email_missing_renderer_total' FACET kind"
8+
},
9+
"terms": [
10+
{
11+
"priority": "CRITICAL",
12+
"operator": "ABOVE",
13+
"threshold": 0,
14+
"thresholdDuration": 300,
15+
"thresholdOccurrences": "AT_LEAST_ONCE"
16+
}
17+
],
18+
"signal": {
19+
"aggregationWindow": 60,
20+
"aggregationMethod": "EVENT_FLOW",
21+
"aggregationDelay": 120,
22+
"fillOption": "STATIC",
23+
"fillValue": 0
24+
},
25+
"expiration": {
26+
"expirationDuration": 3600,
27+
"openViolationOnExpiration": false,
28+
"closeViolationsOnExpiration": true
29+
},
30+
"violationTimeLimitSeconds": 86400
31+
}
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
{
2+
"name": "instant-api — magic_link_email_rate_limited spike (legitimate user being throttled)",
3+
"type": "NRQL",
4+
"description": "P2 warning. instant_magic_link_email_rate_limited_total ticks every time the magic-link rate limiter denies a /auth/login request (per-email or per-IP). Modal harmless cause: a user double-clicking the login button. Modal harmful cause: an upstream bot sweep wedging the per-IP quota for legitimate users sharing the same NAT or office network, OR a bug in the dashboard re-firing the magic-link request on a hot retry path (BugBash 2026-05-20 found one such case). Threshold here is intentionally noisier than a page — fire WARNING on >10 events in 10 minutes so the operator can correlate against a /auth/login spike. Source: api/internal/metrics/metrics.go (MagicLinkEmailRateLimitedTotal), emit site api/internal/handlers/auth.go.",
5+
"enabled": true,
6+
"nrql": {
7+
"query": "SELECT sum(instant_magic_link_email_rate_limited_total) FROM Metric WHERE metricName = 'instant_magic_link_email_rate_limited_total'"
8+
},
9+
"terms": [
10+
{
11+
"priority": "WARNING",
12+
"operator": "ABOVE",
13+
"threshold": 10,
14+
"thresholdDuration": 600,
15+
"thresholdOccurrences": "ALL"
16+
}
17+
],
18+
"signal": {
19+
"aggregationWindow": 60,
20+
"aggregationMethod": "EVENT_FLOW",
21+
"aggregationDelay": 120,
22+
"fillOption": "STATIC",
23+
"fillValue": 0
24+
},
25+
"expiration": {
26+
"expirationDuration": 3600,
27+
"openViolationOnExpiration": false,
28+
"closeViolationsOnExpiration": true
29+
},
30+
"violationTimeLimitSeconds": 86400
31+
}
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
{
2+
"name": "instant-worker — orphan_sweep reaped instant-deploy-* ns with no backing deployments row (P0-3 atomic-provision)",
3+
"type": "NRQL",
4+
"description": "P0 page. instant_orphan_sweep_reaped_total{reason='no_db_row'} > 0 sustained for >1h. A no_db_row event means the orphan_sweep reconciler found an instant-deploy-<appID> namespace in the live cluster with NO matching deployments row in the platform DB. That can only happen if the api created the k8s namespace but the INSERT into deployments never committed — the P0-3 atomic-provision symptom surfacing in prod. Runbook: search NR Logs for `jobs.orphan_sweep.proposed_reap` with reason=no_db_row, capture the app_id, then trace back through the api POST /deploy/new logs for the same time window to find the partial-commit path that needs the atomic-rollback fix. Source: worker/internal/metrics/metrics.go (OrphanSweepReapedTotal), emit site worker/internal/jobs/orphan_sweep_reconciler.go.",
5+
"enabled": true,
6+
"nrql": {
7+
"query": "SELECT sum(instant_orphan_sweep_reaped_total) FROM Metric WHERE metricName = 'instant_orphan_sweep_reaped_total' AND reason = 'no_db_row'"
8+
},
9+
"terms": [
10+
{
11+
"priority": "CRITICAL",
12+
"operator": "ABOVE",
13+
"threshold": 0,
14+
"thresholdDuration": 3600,
15+
"thresholdOccurrences": "AT_LEAST_ONCE"
16+
}
17+
],
18+
"signal": {
19+
"aggregationWindow": 300,
20+
"aggregationMethod": "EVENT_FLOW",
21+
"aggregationDelay": 120,
22+
"fillOption": "STATIC",
23+
"fillValue": 0
24+
},
25+
"expiration": {
26+
"expirationDuration": 7200,
27+
"openViolationOnExpiration": false,
28+
"closeViolationsOnExpiration": true
29+
},
30+
"violationTimeLimitSeconds": 86400
31+
}

0 commit comments

Comments
 (0)