Conversation
113f8be to
3f23cab
Compare
Problem: A curated metric sink keys its gauges on its stable definition name, so every incarnation shares one prometheus Desc id. When a restarted environmentd reconciles against a still-running replica, the old incarnation and the new one live in the same process, and nothing sequences the old dataflow's teardown (which drops its registration) before the new one renders. Registration collided and register_collector_with_dropper soft-panicked. Solution: Add MetricsRegistry::try_register_collector_with_dropper, a fallible variant that returns the prometheus error, and re-base the soft-panicking one on it so its callers keep the backstop. The sink operator owns a PendingRegistration and registers at build time, before the first activation, so a sink with a slow input still publishes its series promptly. On a Desc-id collision it retries once a second, self-scheduling via its activator so a quiescent input still gets another try. The guard lives in the operator closure, so it drops with the dataflow, freeing the Desc id for the retrying incarnation. A new counter, mz_compute_metric_sink_registration_retries_total, makes retries visible.
3f23cab to
8051e57
Compare
QA LLM Review1. MEDIUM -- Moving the registration guard into the operator closure unregisters the collector when the operator shuts down, not when the sink is dropped
DetailsThe operator is built with Concretely reachable: The same mechanism also means a registration that is still retrying when the input closes never completes: the operator is gone before the next Suggested fix: keep the guard's lifetime tied to the collection rather than the operator. Share the |
| // Drain so the operator isn't rescheduled forever. There is no state to | ||
| // fold into on this worker. | ||
| ok_input.for_each(|_, _| {}); | ||
| err_input.for_each(|_, _| {}); | ||
| return; |
There was a problem hiding this comment.
If we don't have a registration, do we not have to store the data locally so that we can publish it subsequently? Our inputs should be changes to metrics, not the metrics on each tick, so here we'd only start recording metrics once they change, right?
There was a problem hiding this comment.
I'm not sure I 100% understand your concern.
I think there are a few things going on:
-
The sink reads its source with
with_snapshot: true. So my understanding is that it means that it gets the entire current contents at theas_of(as diffs), and then subsequent changes. So there isn't only recording of new changes. -
If we don't have a registration, that means we are a none active worker (only one worker receives data in the model). So a non active worker has nothing to store regardless of whether the registration is sucessful or not.
-
The active worker accumulates regardless of registration outcome. After try_register, we don't have early return. Failed attempts schedules a retry, but execution continues. So the diffs arriving while registration is pending are accumulated in theSinkState. And then a scrape after registration exposes the snapshot
So I don't quite follow where we would miss metrics.
antiguru
left a comment
There was a problem hiding this comment.
Left a comment; I think we can miss metrics with this approach. Do you mind checking this?
Problem:
A curated metric sink keys its gauges on its stable definition
name, so every incarnation shares one prometheus Desc id. When a
restarted environmentd reconciles against a still-running replica, the
old incarnation and the new one live in the same process, and nothing
sequences the old dataflow's teardown (which drops its registration)
before the new one renders. Registration collided and
register_collector_with_dropper soft-panicked.
Solution:
Add MetricsRegistry::try_register_collector_with_dropper, a
fallible variant that returns the prometheus error, and re-base the
soft-panicking one on it so its callers keep the backstop. The sink
operator owns a PendingRegistration and retries once a second,
self-scheduling via its activator so a quiescent input still gets
another try. The guard lives in the operator closure, so it drops with
the dataflow, freeing the Desc id for the retrying incarnation.
A new counter, mz_compute_metric_sink_registration_retries_total,
makes retries visible.