Repository navigation
feat(outbox): report outbox messages that stay undispatched - #4598
guilhermeappel wants to merge 5 commits into
Conversation
Add spec 0038 for the telemetry part of issue BrighterCommand#4489: report how many outbox messages have stayed undispatched for longer than a set age, so an operator can see a message that was never sent, including in an application that runs without the outbox sweeper. The channel for failed clears, the other part of the issue, is not in this spec. Part of issue BrighterCommand#4489
A hosted service in Outbox.Hosting counts the undispatched messages older than the set age on a fixed schedule, one bounded call at a time, through the outbox's existing count. It runs on its own, so it keeps measuring when the sweeper or the posting path is stuck. A sealed meter in core, created by AddBrighterInstrumentation, publishes the last count as a gauge and how each scheduled measurement ended, skips included, as a counter. The mediator, the sweeper and the existing meter roles do not change. The ADR stays Proposed until it is agreed on the pull request. Regenerating docs/adr/index.md also adds the missing row for ADR 0082-isolate-azure-service-bus-subscription-retries. Part of issue BrighterCommand#4489
Because we can circuit break a topic, we should have an age per publication, otherwise we won't see that clearly |
| ## Proposed Solution | ||
|
|
||
| 1. The app turns the feature on with one explicit configuration step that names a **stale age**. It may also set a **ceiling**, a **measurement interval** and a **measurement timeout**. Each has a documented default. | ||
| 2. While measurement runs, Brighter counts the outbox's undispatched messages older than the stale age once per interval, up to the ceiling. |
There was a problem hiding this comment.
We might choose to support both this "overall outstanding count" and a "per topic outstanding count" to allow for topic circuit breakers to show up
|
Makes sense. While a topic's circuit is open, the sweeper skips that topic, so its messages stay in the outbox. One count over the whole outbox can't show which topic they belong to. I'll keep the overall count, as you suggest, and add a count per topic on an instrument of its own, tagged with |
A topic whose circuit breaker is open keeps its messages in the outbox, and one count over the whole outbox cannot show which topic they are on. Ask for a count per topic beside the overall count, with an age per publication that falls back to the overall stale age, and the smallest age winning when publications share a topic. The count per topic needs a new optional capability on the outbox, since no outbox can count several topics, each at its own age, and the outbox interfaces cannot gain a member. An outbox without it reports the overall count only. Part of issue BrighterCommand#4489
Revise ADR 0085-outbox-stale-message-telemetry for the two counts: the monitor makes the overall count and the count per topic at each due time, one cannot delay the other, and a second gauge reports one series per topic. A publication can set its own stale age. ADR 0086-outbox-per-topic-stale-count decides the optional outbox interface the count per topic goes through: one call for every topic, each topic at its own age and up to the ceiling, by the message header topic. Only InMemoryOutbox will implement it in this PR, and a follow-up issue will track each of the other outbox families. Part of issue BrighterCommand#4489
|
@iancooper I've pushed the revised requirements and ADRs for the age per publication and the count per topic. ADR 0085-outbox-stale-message-telemetry now covers both counts. The count per topic got an ADR of its own, ADR 0086-outbox-per-topic-stale-count, because the optional interface it adds to the outbox is a separate decision that each outbox family can then implement on its own. On the points I said the ADRs would cover: the ceiling applies to each topic on its own, so a busy topic can't hide a quiet one, and a topic shared by two publications uses the smaller age. In this PR only InMemoryOutbox will implement the interface. The database outboxes, MongoDb included, will report the overall count until their follow-ups land, with one issue per family. If you'd rather have one of them in this PR, tell me which and I'll add it. Running the two counts side by side touches most of the mechanism in ADR 0085-outbox-stale-message-telemetry. The new sections there are the ones on the smallest age, the age on |
Description
This is the telemetry part of issue #4489. As I said on the issue, I'm starting it as a draft. It has the requirements, spec 0038, and two ADRs, ADR 0085-outbox-stale-message-telemetry and ADR 0086-outbox-per-topic-stale-count, which stay Proposed until you're happy with them. If you'd change the design, now is the cheapest time to say so.
The feature reports how many outbox messages have stayed undispatched for longer than an age you set, so an operator can alert on a message that was never sent, even in an app that runs without the sweeper. A new hosted service in
Outbox.Hosting, registered withUseOutboxStaleMessageMonitorin the same way asUseOutboxArchiver, counts on a fixed interval, with a timeout. It makes two counts at each interval, and one never waits for the other. The overall count goes through the outbox's existing async count. The count per topic goes through a new optional outbox interface, which ADR 0086-outbox-per-topic-stale-count decides, so a topic whose circuit breaker is open shows up on its own. Each publication can set its own stale age, falling back to the monitor's, and a topic shared by several publications uses the smallest. A sealed meter in core, created byAddBrighterInstrumentationlikePublishConfirmationMeterfrom PR #4562, publishes each count as an observable gauge, one series per topic for the second, and a counter of how each scheduled measurement ended, so "no recent reading" looks different from zero. The mediator, the sweeper and the existing meter roles don't change.These are the answers I went with for the three questions in my last comment on the issue:
OutboxMeterfrom PR Add outbox metrics #4484. They come fromIMeterFactorylikePublishConfirmationMeter, so they don't depend on when the mediator is built, andOutboxMeterand its role stay as they are. Alternative 12 in ADR 0085-outbox-stale-message-telemetry has the details. If you'd rather the outbox had one meter, I'll move them. The new meter has no role interface, so moving it later breaks no implementer.After your review I added an age per publication and the count per topic, as described above. The count per topic only works on an outbox that implements the new interface. Only
InMemoryOutboxdoes in this PR, and each of the other outbox families gets a follow-up issue, so until then they report the overall count only. The gauges don't tell instances apart: each process measures its own outbox, the series carries the outbox type in a newparamore.brighter.outbox.implementationattribute, and the docs will say to combine instances that share a database with max rather than sum (alternative 17 in ADR 0085-outbox-stale-message-telemetry).I read "telemetry on the call that warns" as a metric you can alert on, counted on a schedule rather than on each Post or Clear, because a quiet app with no posts would never report a message that failed once. There's no Warning log for each count above zero either. Alternative 13 in ADR 0085-outbox-stale-message-telemetry has why, and alternative 23 there says why I didn't rely on added minus cleared from ADR 0084-outbox-metrics. The requirements still list my questions as Q-1 to Q-16, and the ADRs answer each of them. Q-7, Q-14 and Q-15 are the ones your review reopened.
Related Issues
Part of issue #4489. The channel for failed clears, the other part of the issue, isn't in this PR.
Type of Change
Checklist
Additional Notes
UseOutboxStaleMessageMonitor,TimedOutboxStaleMessageMonitorand its options inOutbox.Hosting, and in core the meter, a small readings type that the monitor writes and the meter reads, six names inBrighterSemanticConventions, the optionalIAmATopicCountingOutboxinterface and a nullableStaleMessageAgeproperty onPublication. The readings type is public only becauseOutbox.Hostingis a separate assembly, the same reasonAmbientScopeSuppressionis public.review-requirements.mdandreview-design.mdin the spec folder.docs/adr/index.mdalso adds the missing row for ADR 0082-isolate-azure-service-bus-subscription-retries.