From 6df3e23e0726fd396cf1b6e76b991c340a0e7170 Mon Sep 17 00:00:00 2001 From: Frank McSherry Date: Wed, 2 Sep 2026 09:49:11 -0400 Subject: [PATCH 1/3] Spike: adapt to TotalOrder-bounded InputCapability::time()/retain() - Collection::inspect_batch / inspect_container: T: TotalOrder (they reveal one time). - capture.rs CDC progress operator: hold a CapabilitySet via retain_stamp. - dogsdogsdogs half_join: insert each element of retain_stamp. - tests: dynamic.rs asserts stamp().len() == 1 explicitly; wcoj_partial_order uses inspect. Co-Authored-By: Claude Fable 5.1 Claude-Session: https://claude.ai/code/session_012k2GSwxmvD2LvckkoXi6GK --- differential-dataflow/src/capture.rs | 8 ++++---- differential-dataflow/src/collection.rs | 2 ++ differential-dataflow/tests/dynamic.rs | 4 ++-- dogsdogsdogs/src/operators/half_join.rs | 3 +-- dogsdogsdogs/tests/wcoj_partial_order.rs | 5 ++--- 5 files changed, 11 insertions(+), 11 deletions(-) diff --git a/differential-dataflow/src/capture.rs b/differential-dataflow/src/capture.rs index ba1522aca..4b194c091 100644 --- a/differential-dataflow/src/capture.rs +++ b/differential-dataflow/src/capture.rs @@ -227,7 +227,7 @@ pub mod source { use std::rc::Rc; use std::marker::{Send, Sync}; use std::sync::Arc; - use timely::dataflow::{Scope, Stream, operators::{Capability, CapabilitySet}}; + use timely::dataflow::{Scope, Stream, operators::CapabilitySet}; use timely::dataflow::operators::generic::OutputBuilder; use timely::progress::Timestamp; use timely::scheduling::SyncActivator; @@ -462,17 +462,17 @@ pub mod source { // If the frontier changes we need a capability to express that. // Any capability should work; the downstream listener doesn't care. - let mut capability: Option> = None; + let mut capability: Option> = None; // Drain all relevant update counts in to the mutable antichain tracking its frontier. counts.for_each(|cap, counts| { updates_frontier.update_iter(counts.iter().cloned()); - capability = Some(cap.retain(0)); + capability = Some(cap.retain_stamp(0)); }); // Drain all progress statements into the queue out of which we will work. input.for_each(|cap, progress| { progress_queue.extend(progress.iter().map(|x| (x.1).clone())); - capability = Some(cap.retain(0)); + capability = Some(cap.retain_stamp(0)); }); // Extract and act on actionable progress messages. diff --git a/differential-dataflow/src/collection.rs b/differential-dataflow/src/collection.rs index 3500aa9cb..4f1fa951a 100644 --- a/differential-dataflow/src/collection.rs +++ b/differential-dataflow/src/collection.rs @@ -134,6 +134,7 @@ impl<'scope, T: Timestamp, C: Container> Collection<'scope, T, C> { pub fn inspect_container(self, func: F) -> Self where F: FnMut(Result<(&T, &C), &[T]>)+'static, + T: timely::order::TotalOrder, { self.inner .inspect_container(func) @@ -628,6 +629,7 @@ pub mod vec { pub fn inspect_batch(self, mut func: F) -> Collection<'scope, T, D, R> where F: FnMut(&T, &[(D, T, R)])+'static, + T: timely::order::TotalOrder, { self.inner .inspect_batch(move |time, data| func(time, data)) diff --git a/differential-dataflow/tests/dynamic.rs b/differential-dataflow/tests/dynamic.rs index 05e896bb7..5ed3c9dec 100644 --- a/differential-dataflow/tests/dynamic.rs +++ b/differential-dataflow/tests/dynamic.rs @@ -48,7 +48,7 @@ fn leave_dynamic_on_a_message_over_two_epochs() { let mut output = output.activate(); input.for_each(|cap, data| { let Some(root) = root.as_ref() else { return }; - let epoch = cap.time().outer; + let epoch = cap.stamp().iter().map(|t| t.outer).min().unwrap(); let t1 = Product::new(epoch, PointStamp::new([3].into_iter().collect())); let t2 = Product::new(epoch + 1, PointStamp::new([0].into_iter().collect())); let caps: CapabilitySet