Skip to content

Commit 511f795

Browse files
antiguruclaude
andcommitted
fixup: report a refusal without the controller frontier
The refusal report named the controller's last compaction frontier, which the registry no longer records. The published `since` already says how far the trace compacted, so the report keeps `since`, `as_of`, and the standing hold. Tests compact through the writer's own agents where they used to note the controller's frontier on the registry, and keep the agents alive for as long as they read. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VDm7opomJLxbNUEP3r9BLk
1 parent 293b006 commit 511f795

4 files changed

Lines changed: 134 additions & 118 deletions

File tree

src/compute/src/render.rs

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -563,11 +563,10 @@ pub fn build_compute_dataflow(
563563
/// Reports a publication point refusing to serve `as_of`, and aborts.
564564
///
565565
/// A refusal is a protocol-ordering failure: the controller promises an index's `since` never
566-
/// passes the `as_of` of a dataflow importing it. The diagnostics say which side moved. A controller
567-
/// frontier beyond `as_of` means maintenance applied a compaction ahead of this render. A standing
568-
/// hold at the refusing `since` means this runtime had already applied it, so the create was ordered
569-
/// behind it on this runtime's own stream. A standing hold below the `since` means the publisher
570-
/// escaped its bound.
566+
/// passes the `as_of` of a dataflow importing it. The diagnostics say which side moved. A standing
567+
/// hold at the refusing `since` means this runtime had already applied the compaction, so the
568+
/// create was ordered behind it on this runtime's own stream. A standing hold below the `since`
569+
/// means the trace compacted past its bound.
571570
fn report_compacted_past(
572571
idx_id: GlobalId,
573572
part: &str,
@@ -577,10 +576,9 @@ fn report_compacted_past(
577576
) -> ! {
578577
panic!(
579578
"Index {idx_id} ({part}) has been allowed to compact beyond the dataflow as_of: \
580-
since {:?}, as_of {:?}, controller allow_compaction {:?}, standing hold {:?}",
579+
since {:?}, as_of {:?}, standing hold {:?}",
581580
since.elements(),
582581
as_of.elements(),
583-
diagnostics.writer_logical.as_ref().map(|f| f.elements()),
584582
diagnostics.standing_hold.elements(),
585583
)
586584
}

src/compute/src/render/tests.rs

Lines changed: 40 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -46,12 +46,17 @@ fn test_rows() -> Vec<(Row, Row)> {
4646
/// of `scope`. The updates are written at time 0 and sealed by advancing the inputs to 1.
4747
///
4848
/// The `InputSession` handles drop at the end of this call, buffering the sealed updates for the
49-
/// worker to process on later steps, mirroring `sharing.rs`'s `publish_index_into`.
49+
/// worker to process on later steps, mirroring `sharing.rs`'s `publish_index_into`. Returns the
50+
/// trace agents: the point closes with the trace, and the trace lives as long as an agent does, so
51+
/// the caller keeps them for as long as it reads.
5052
fn publish_index(
5153
scope: Scope<'_, Timestamp>,
5254
registry: &ArrangementSharingRegistry,
5355
id: GlobalId,
5456
rows: Vec<(Row, Row)>,
57+
) -> (
58+
RowRowAgent<Timestamp, Diff>,
59+
crate::typedefs::ErrAgent<Timestamp, Diff>,
5560
) {
5661
let (mut oks_input, oks_collection) = scope.new_collection::<(Row, Row), Diff>();
5762
let oks = oks_collection.mz_arrange::<
@@ -69,8 +74,8 @@ fn publish_index(
6974
);
7075

7176
let slot = registry.get_or_create(id, 0, 1);
72-
PublishArrangement::adopt(&oks, &slot.oks, "test oks", || {});
73-
PublishArrangement::adopt(&errs, &slot.errs, "test errs", || {});
77+
PublishArrangement::adopt(&oks, &slot.oks, || {});
78+
PublishArrangement::adopt(&errs, &slot.errs, || {});
7479
registry.notify(id, 0);
7580

7681
for (k, v) in rows {
@@ -80,6 +85,7 @@ fn publish_index(
8085
oks_input.flush();
8186
errs_input.advance_to(Timestamp::from(1_u64));
8287
errs_input.flush();
88+
(oks.trace.clone(), errs.trace.clone())
8389
}
8490

8591
/// The interactive import path imports a maintenance-published arrangement into a second
@@ -103,8 +109,8 @@ fn interactive_import_replays_rows_and_holds_at_as_of() {
103109

104110
timely::execute_directly(move |worker| {
105111
// Maintenance runtime: publish the index into the shared registry.
106-
worker.dataflow::<Timestamp, _, _>(|scope| {
107-
publish_index(scope, &registry_in, id, rows.clone());
112+
let _keep = worker.dataflow::<Timestamp, _, _>(|scope| {
113+
publish_index(scope, &registry_in, id, rows.clone())
108114
});
109115

110116
// Interactive runtime: a temporary dataflow imports the published arrangement via the
@@ -170,6 +176,7 @@ fn publish_index_with_writer(
170176
InputSession<Timestamp, (Row, Row), Diff>,
171177
InputSession<Timestamp, crate::render::errors::DataflowErrorSer, Diff>,
172178
RowRowAgent<Timestamp, Diff>,
179+
crate::typedefs::ErrAgent<Timestamp, Diff>,
173180
) {
174181
let (mut oks_input, oks_collection) = scope.new_collection::<(Row, Row), Diff>();
175182
let oks = oks_collection.mz_arrange::<
@@ -188,8 +195,8 @@ fn publish_index_with_writer(
188195
);
189196

190197
let slot = registry.get_or_create(id, 0, 1);
191-
PublishArrangement::adopt(&oks, &slot.oks, "test oks", || {});
192-
PublishArrangement::adopt(&errs, &slot.errs, "test errs", || {});
198+
PublishArrangement::adopt(&oks, &slot.oks, || {});
199+
PublishArrangement::adopt(&errs, &slot.errs, || {});
193200
registry.notify(id, 0);
194201

195202
for (k, v) in rows {
@@ -200,14 +207,13 @@ fn publish_index_with_writer(
200207
errs_input.advance_to(Timestamp::from(1_u64));
201208
errs_input.flush();
202209

203-
(oks_input, errs_input, oks_writer)
210+
(oks_input, errs_input, oks_writer, errs.trace.clone())
204211
}
205212

206213
/// Feeds `oks_input` a filler update at `at`, advances it to `next`, and steps `worker` a few
207214
/// times, mirroring the `tick` helper in `differential-dataflow`'s own `sharing.rs` test suite.
208-
/// The publisher operator only recomputes its forwarded compaction when a batch runs through
209-
/// it, so a bare `set_logical_compaction`/`set_physical_compaction` call on a writer handle is
210-
/// invisible to the published `since` until the next such tick.
215+
/// A reader's hold reaches the trace on the arrange operator's next activation, which an idle
216+
/// dataflow never gets, so tests tick after moving one.
211217
fn tick(
212218
worker: &mut timely::worker::Worker,
213219
oks_input: &mut InputSession<Timestamp, (Row, Row), Diff>,
@@ -261,8 +267,8 @@ fn interactive_import_hold_releases_on_drop() {
261267
// Maintenance runtime: publish the index, keeping the `oks` `InputSession` (so we can
262268
// tick the dataflow afterward) and a plain writer trace handle (so we can request
263269
// compaction on it directly, as a controller would) alive across the whole closure.
264-
let (mut oks_input, _errs_input, mut oks_writer) =
265-
worker.dataflow::<Timestamp, _, _>(|scope| {
270+
let (mut oks_input, _errs_input, mut oks_writer, _errs_keep) = worker
271+
.dataflow::<Timestamp, _, _>(|scope| {
266272
publish_index_with_writer(scope, &registry, id, rows.clone())
267273
});
268274

@@ -283,14 +289,12 @@ fn interactive_import_hold_releases_on_drop() {
283289
(oks_arranged.trace, errs_arranged.trace)
284290
});
285291

286-
// The controller requests compaction well past `as_of`, and both runtimes apply it:
287-
// `note_allow_compaction` forwards the writer floor into the published slot and
292+
// The controller requests compaction well past `as_of`, and both runtimes apply it: the
293+
// writer handle advances, which the trace mirrors into the published `since` at once, and
288294
// `note_standing_hold` advances the importing runtime's own position, exactly as
289-
// `handle_allow_compaction` does on each side. The writer handle advances too so the trace
290-
// can physically compact. A filler tick reactivates the publisher so it recomputes its
291-
// forwarded `since` (still pinned to `as_of` here by the live reader hold).
295+
// `handle_allow_compaction` does on each side. The `since` stays pinned to `as_of` here by
296+
// the live reader hold.
292297
let target = Antichain::from_elem(Timestamp::from(10_u64));
293-
registry.note_allow_compaction(id, 0, &target);
294298
registry.note_standing_hold(id, 0, &target);
295299
oks_writer.set_logical_compaction(target.borrow());
296300
oks_writer.set_physical_compaction(target.borrow());
@@ -359,8 +363,8 @@ fn interactive_import_holds_after_construction() {
359363
let registry = ArrangementSharingRegistry::new();
360364

361365
timely::execute_directly(move |worker| {
362-
let (mut oks_input, _errs_input, _oks_writer) =
363-
worker.dataflow::<Timestamp, _, _>(|scope| {
366+
let (mut oks_input, _errs_input, _oks_writer, _errs_keep) = worker
367+
.dataflow::<Timestamp, _, _>(|scope| {
364368
publish_index_with_writer(scope, &registry, id, rows.clone())
365369
});
366370

@@ -430,9 +434,10 @@ fn published_since_does_not_chase_reader_holds() {
430434
let registry = ArrangementSharingRegistry::new();
431435

432436
timely::execute_directly(move |worker| {
433-
let (mut oks_input, _errs_input, _w) = worker.dataflow::<Timestamp, _, _>(|scope| {
434-
publish_index_with_writer(scope, &registry, id, rows.clone())
435-
});
437+
let (mut oks_input, _errs_input, _w, _errs_keep) =
438+
worker.dataflow::<Timestamp, _, _>(|scope| {
439+
publish_index_with_writer(scope, &registry, id, rows.clone())
440+
});
436441
// A reader at the higher as_of. Its handles go out of scope with the builder; the
437442
// import operator's own hold remains.
438443
worker.dataflow::<Timestamp, _, _>(|scope| {
@@ -454,8 +459,7 @@ fn published_since_does_not_chase_reader_holds() {
454459
);
455460
}
456461

457-
// No `note_allow_compaction` has been called: the controller has allowed nothing, so a
458-
// read at the lower time is still legal.
462+
// The writer has compacted nothing, so a read at the lower time is still legal.
459463
let (probe_oks, _) = registry.handles(&id, 0).expect("published");
460464
let since = probe_oks.frontiers().0;
461465
assert!(
@@ -484,9 +488,10 @@ fn import_reports_physical_within_chain_coverage() {
484488
let registry = ArrangementSharingRegistry::new();
485489

486490
timely::execute_directly(move |worker| {
487-
let (mut oks_input, _errs_input, _w) = worker.dataflow::<Timestamp, _, _>(|scope| {
488-
publish_index_with_writer(scope, &registry, id, rows.clone())
489-
});
491+
let (mut oks_input, _errs_input, _w, _errs_keep) =
492+
worker.dataflow::<Timestamp, _, _>(|scope| {
493+
publish_index_with_writer(scope, &registry, id, rows.clone())
494+
});
490495
tick(
491496
worker,
492497
&mut oks_input,
@@ -547,8 +552,8 @@ fn interactive_import_hold_downgrades_while_live() {
547552
let registry = ArrangementSharingRegistry::new();
548553

549554
timely::execute_directly(move |worker| {
550-
let (mut oks_input, _errs_input, mut oks_writer) =
551-
worker.dataflow::<Timestamp, _, _>(|scope| {
555+
let (mut oks_input, _errs_input, mut oks_writer, _errs_keep) = worker
556+
.dataflow::<Timestamp, _, _>(|scope| {
552557
publish_index_with_writer(scope, &registry, id, rows.clone())
553558
});
554559

@@ -567,7 +572,6 @@ fn interactive_import_hold_downgrades_while_live() {
567572
// The controller allows compaction well past `as_of`, both runtimes apply it, and the
568573
// writer applies it to the trace.
569574
let target = Antichain::from_elem(Timestamp::from(10_u64));
570-
registry.note_allow_compaction(id, 0, &target);
571575
registry.note_standing_hold(id, 0, &target);
572576
oks_writer.set_logical_compaction(target.borrow());
573577
oks_writer.set_physical_compaction(target.borrow());
@@ -640,8 +644,8 @@ fn import_asserts_since_at_most_as_of() {
640644
let registry = ArrangementSharingRegistry::new();
641645

642646
timely::execute_directly(move |worker| {
643-
let (mut oks_input, _errs_input, mut oks_writer) =
644-
worker.dataflow::<Timestamp, _, _>(|scope| {
647+
let (mut oks_input, _errs_input, mut oks_writer, _errs_keep) = worker
648+
.dataflow::<Timestamp, _, _>(|scope| {
645649
publish_index_with_writer(scope, &registry, id, rows.clone())
646650
});
647651

@@ -650,7 +654,6 @@ fn import_asserts_since_at_most_as_of() {
650654
// `as_of` on the next tick: no reader hold pins it, and the standing hold has moved with
651655
// the writer floor.
652656
let target = Antichain::from_elem(Timestamp::from(10_u64));
653-
registry.note_allow_compaction(id, 0, &target);
654657
registry.note_standing_hold(id, 0, &target);
655658
oks_writer.set_logical_compaction(target.borrow());
656659
oks_writer.set_physical_compaction(target.borrow());
@@ -698,16 +701,15 @@ fn standing_hold_pins_until_the_importing_runtime_applies() {
698701
let registry = ArrangementSharingRegistry::new();
699702

700703
timely::execute_directly(move |worker| {
701-
let (mut oks_input, _errs_input, mut oks_writer) =
702-
worker.dataflow::<Timestamp, _, _>(|scope| {
704+
let (mut oks_input, _errs_input, mut oks_writer, _errs_keep) = worker
705+
.dataflow::<Timestamp, _, _>(|scope| {
703706
publish_index_with_writer(scope, &registry, id, rows.clone())
704707
});
705708

706709
// The maintenance runtime applies `AllowCompaction(10)` in full: the writer floor moves and
707710
// its own trace handle compacts. The interactive runtime has not applied the broadcast copy
708711
// of that command, so its standing hold does not move.
709712
let target = Antichain::from_elem(Timestamp::from(10_u64));
710-
registry.note_allow_compaction(id, 0, &target);
711713
oks_writer.set_logical_compaction(target.borrow());
712714
oks_writer.set_physical_compaction(target.borrow());
713715
tick(

src/compute/src/shared_trace/publish.rs

Lines changed: 2 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -23,10 +23,9 @@ use crate::shared_trace::handle::SharedTraceHandle;
2323
/// Why a publication point refused an `as_of`.
2424
///
2525
/// Read off the point rather than off a handle, so a failure path registers no hold on its way to a
26-
/// panic, and so the caller reports the point that actually refused rather than a sibling.
26+
/// panic, and so the caller reports the point that actually refused rather than a sibling. The
27+
/// refusing `since` is already in [`Published::handle_at`]'s `Err`, so it is not repeated here.
2728
pub(crate) struct Diagnostics<T> {
28-
/// The published `since`, the meet of the writer's own compaction frontier and every hold.
29-
pub(crate) since: Antichain<T>,
3029
/// The frontier the importing runtime has applied.
3130
///
3231
/// A refusal with this AT the refusing `since` means that runtime had already applied the
@@ -122,16 +121,9 @@ where
122121
self.shared.upper()
123122
}
124123

125-
/// The published seal frontier.
126-
pub(crate) fn upper(&self) -> Antichain<Tr::Time> {
127-
let state = self.shared.state.lock().expect("shared trace poisoned");
128-
state.upper.clone()
129-
}
130-
131124
/// Why this point would refuse an `as_of`. See [`Diagnostics`].
132125
pub(crate) fn diagnostics(&self) -> Diagnostics<Tr::Time> {
133126
Diagnostics {
134-
since: self.shared.since(),
135127
standing_hold: self.standing_hold(),
136128
}
137129
}

0 commit comments

Comments
 (0)