You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: readme.md
+62Lines changed: 62 additions & 0 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -56,6 +56,68 @@ To read email datasets from the database use this class: [QueryEmail.java](src%2
56
56
57
57
If you want to receive a report after each run of the scheduler, create a Bean implementing [EmailSchedulerCallback.java](src%2Fmain%2Fjava%2Fit%2Faboutbits%2Fspringboot%2Femailservice%2Flib%2FEmailSchedulerCallback.java)
58
58
59
+
### Metrics
60
+
61
+
The library records Micrometer metrics for both schedulers. Micrometer is an optional dependency: if the
62
+
application provides a `MeterRegistry` the metrics are recorded, otherwise the library falls back to a no-op
63
+
and nothing changes. Nothing has to be enabled in this library.
64
+
65
+
To scrape them, the application needs `spring-boot-starter-actuator` and `micrometer-registry-prometheus`,
| `app_email_send_duration_seconds` | timer | `mode`: `scheduled`, `direct`<br/>`outcome`: `sent`, `retry`, `error` | One observation per send attempt. `retry` is an attempt that failed but is scheduled for another one, `error` an email that has given up. |
90
+
| `app_email_cleanup_duration_seconds` | timer | `outcome`: `cleaned`, `error` | One observation per attachment cleanup attempt. |
91
+
| `app_email_pass_duration_seconds` | timer | `job`: `send`, `cleanup`<br/>`status`: `success`, `failed` | One observation per scheduler pass. `failed` means the pass itself broke, e.g. the database was unreachable. |
92
+
| `app_email_last_run_timestamp_seconds` | gauge | `job`: `send`, `cleanup` | When the scheduler last fired. Stalls if the scheduler is dead or the pod is down. |
93
+
| `app_email_last_success_timestamp_seconds` | gauge | `job`: `send`, `cleanup` | When a pass last got through. Stalls while passes keep failing. |
94
+
| `app_email_queue` | gauge | `state`: `pending`, `sending` | Emails per state, read after each send pass. The terminal states `SENT` and `ERROR` are left out: they only ever grow. Errors are counted by `app_email_send_duration_seconds_count{outcome="error"}`. |
95
+
| `app_email_queue_oldest_due_age_seconds` | gauge | `state`: `pending`| How long the oldest email that is already due has been waiting. `0` if nothing is due. |
96
+
97
+
Two things to keep in mind when querying them:
98
+
99
+
- **Aggregate the gauges with `max`, never `sum`.** The queue gauges are read from the database, so every pod
100
+
reports the same numbers - summing them multiplies the backlog by the number of pods.
101
+
- The queue gauges are only written by the send scheduler. With
102
+
`aboutbits.emailservice.scheduling.enabled=false`they are never registered, and the series are absent
103
+
rather than zero.
104
+
105
+
Both timestamp gauges are registered on first use, so a pod that has not completed a pass since starting has
106
+
no series at all. This is deliberate: an alert reads an absent series the same way it reads a `NaN` starting
107
+
value, while a `0` starting value would look like decades of staleness after every deploy.
108
+
109
+
The dashboards and alerts themselves belong to the consuming application, since they depend on how it is
110
+
deployed. As a starting point, a scheduler that has stopped firing and a backlog that is not being kept up
111
+
with read as:
112
+
113
+
```promql
114
+
time() - max by (job) (app_email_last_run_timestamp_seconds) > 300
115
+
max(app_email_queue_oldest_due_age_seconds) > 600
116
+
```
117
+
118
+
If the application replaces the metrics sink with its own `EmailMetrics` bean, the library's one steps back;
119
+
whatever the application provides is called fail-safe, so a failing sink can never break a send.
120
+
59
121
### Configuration
60
122
61
123
To enable this service just add `@EnableEmailService` to your main class. You must also enable `@EnableScheduling` to allow the email queue to be processed.
0 commit comments