Skip to main content

data-dataflow-coordination

Coordination patterns for distributed dataflow systems including barriers, epochs, and distributed snapshots

معلومات المصدر

المستودع
tools-only/X-Skills
آخر نشاط في المصدر
١ مارس ٢٠٢٦ في ٠٣:٣٧
لغة SKILL.md المكتشفة
الإنجليزية
النجوم
٧
التفرعات
١

خيارات التثبيت

يُحدَّد Prompt الذي يراجع المصدر أولًا بشكل افتراضي. يمكنك التبديل إلى أمر مباشر أو تنزيل نسخة محلية.

مراجعة ملفات المصدر

اقرأ SKILL.md وأي ملفات مرافقة يعرضها SkillsMP قبل أن تقرر التثبيت.

عرض SKILL.md

SKILL.md
تعليمات المصدر · معاينة للقراءة فقط
name
data-dataflow-coordination
description
Coordination patterns for distributed dataflow systems including barriers, epochs, and distributed snapshots
# Dataflow Coordination **Scope**: Coordination primitives, barrier synchronization, epoch markers, distributed snapshots, consistency **Lines**: 370 **Last Updated**: 2025-10-27 **Format Version**: 1.0 (Atomic) ## When to Use This Skill Use this skill when: - Coordinating multiple parallel dataflow streams - Implementing barrier synchronization across workers - Managing epochs and checkpointing in distributed systems - Building consistent snapshots of distributed state - Coordinating multi-stage pipeline computations - Implementing exactly-once semantics with coordination - Synchronizing external systems with dataflow progress - Handling backpressure across distributed workers ## Core Concepts ### Coordination Mechanisms ``` Barriers → Synchronization points across parallel streams → All workers must reach barrier before proceeding → Use for: Global aggregations, checkpointing → Cost: Latency spike at barrier Epochs → Logical time units for batching → Watermarks: "No data before T will arrive" → Use for: Windowing, progress tracking → Cost: Buffering until epoch closes Snapshots → Consistent global state capture → Chandy-Lamport algorithm → Use for: Fault recovery, migration → Cost: Storage and I/O overhead ``` ### Progress Tracking Models ``` Global Frontier → Minimum timestamp across all workers → Conservative: Waits for slowest worker → Guarantees: Completeness at timestamp Per-Worker Frontiers → Independent progress per worker → Optimistic: Faster workers proceed → Requires: Careful synchronization Hierarchical Frontiers → Per-operator, per-worker tracking → Fine-grained progress visibility → Complex but enables optimization ``` ### Consistency Models ``` Strong Consistency → All workers see same state → Requires: Global coordination → Use for: Financial transactions Eventual Consistency → Workers converge over time → Minimal coordination → Use for: Analytics, monitoring Causal Consistency → Respects causal relationships → Vector clocks, happens-before → Use for: Distributed collaboration ``` ## Patterns ### Pattern 1: Barrier Synchronization (Rust/Timely) ```rust use timely::dataflow::{Scope, Stream}; use timely::dataflow::channels::pact::Pipeline; use timely::dataflow::operators::generic::operator::Operator; use std::collections::HashMap; // Barrier that emits when all inputs reach same timestamp fn barrier<G: Scope>( streams: Vec<&Stream<G, i32>>, ) -> Stream<G, Vec<i32>> { assert!(!streams.is_empty()); let mut builder = timely::dataflow::operators::generic::builder_rc::OperatorBuilder::new( "Barrier".to_string(), streams[0].scope(), ); // Create inputs for all streams let mut inputs: Vec<_> = streams.iter() .map(|stream| builder.new_input(stream, Pipeline)) .collect(); let (mut output, stream) = builder.new_output(); builder.build(move |_capability| { let num_inputs = inputs.len(); let mut buffers: HashMap<G::Timestamp, Vec<Vec<i32>>> = HashMap::new(); move |_frontiers| { // Collect data from all inputs for (idx, input) in inputs.iter_mut().enumerate() { input.for_each(|time, data| { let entry = buffers.entry(time.time().clone()) .or_insert_with(|| vec![Vec::new(); num_inputs]); entry[idx].extend(data.iter().cloned()); }); } // Emit when all inputs have data at timestamp let ready: Vec<G::Timestamp> = buffers.iter() .filter(|(_, vecs)| vecs.iter().all(|v| !v.is_empty())) .map(|(t, _)| t.clone()) .collect(); for time in ready { if let Some(mut data_vecs) = buffers.remove(&time) { let mut session = output.session(&time); // Combine data from all inputs let combined: Vec<i32> = data_vecs.into_iter() .flat_map(|v| v) .collect(); session.give(combined); } } } }); stream } // Usage fn main() { use timely::dataflow::operators::{ToStream, Inspect}; timely::execute_from_args(std::env::args(), |worker| { worker.dataflow::<u64, _, _>(|scope| { let stream1 = (0..5).to_stream(scope); let stream2 = (10..15).to_stream(scope); barrier(vec![&stream1, &stream2]) .inspect(|data| println!("Barrier output: {:?}", data)); }); }).expect("Execution failed"); } ``` ### Pattern 2: Epoch Markers (Go) ```go package main import ( "fmt" "sync" "time" ) // EpochCoordinator manages epoch transitions across workers type EpochCoordinator struct { currentEpoch int64 numWorkers int barriers map[int64]*sync.WaitGroup mu sync.Mutex } func NewEpochCoordinator(numWorkers int) *EpochCoordinator { return &EpochCoordinator{ currentEpoch: 0, numWorkers: numWorkers, barriers: make(map[int64]*sync.WaitGroup), } } // ArriveAtEpoch signals that worker has reached epoch func (ec *EpochCoordinator) ArriveAtEpoch(workerID int, epoch int64) { ec.mu.Lock() defer ec.mu.Unlock() // Create barrier for this epoch if needed if _, exists := ec.barriers[epoch]; !exists { ec.barriers[epoch] = &sync.WaitGroup{} ec.barriers[epoch].Add(ec.numWorkers) } fmt.Printf("Worker %d arrived at epoch %d\n", workerID, epoch) ec.barriers[epoch].Done() } // WaitForEpoch blocks until all workers reach epoch func (ec *EpochCoordinator) WaitForEpoch(epoch int64) { ec.mu.Lock() barrier := ec.barriers[epoch] ec.mu.Unlock() if barrier != nil { barrier.Wait() fmt.Printf("Epoch %d complete\n", epoch) // Clean up old barriers ec.mu.Lock() delete(ec.barriers, epoch) ec.mu.Unlock() } } // AdvanceEpoch moves to next epoch func (ec *EpochCoordinator) AdvanceEpoch() int64 { ec.mu.Lock() defer ec.mu.Unlock() ec.currentEpoch++ return ec.currentEpoch } // Worker simulates dataflow worker with epoch coordination func worker(id int, coordinator *EpochCoordinator, wg *sync.WaitGroup) { defer wg.Done() for epoch := int64(0); epoch < 5; epoch++ { // Simulate work time.Sleep(time.Duration(id*100) * time.Millisecond) fmt.Printf("Worker %d processing epoch %d\n", id, epoch) // Signal arrival at epoch barrier coordinator.ArriveAtEpoch(id, epoch) // Wait for all workers to reach epoch coordinator.WaitForEpoch(epoch) } } func main() { numWorkers := 3 coordinator := NewEpochCoordinator(numWorkers) var wg sync.WaitGroup wg.Add(numWorkers) for i := 0; i < numWorkers; i++ { go worker(i, coordinator, &wg) } wg.Wait() fmt.Println("All workers complete") } ``` ### Pattern 3: Distributed Snapshot (Chandy-Lamport) ```python import threading import queue from dataclasses import dataclass from typing import Dict, List, Set from enum import Enum class MessageType(Enum): DATA = 1 MARKER = 2 @dataclass class Message: msg_type: MessageType data: any snapshot_id: int = 0 class SnapshotWorker: """Implements Chandy-Lamport snapshot algorithm""" def __init__(self, worker_id: int, neighbors: List[int]): self.worker_id = worker_id self.neighbors = neighbors self.state = {} # Local state self.channels: Dict[int, queue.Queue] = {} # Input channels # Snapshot state self.recording: Dict[int, bool] = {} # Per snapshot self.recorded_state: Dict[int, dict] = {} # Snapshot ID -> state self.recorded_messages: Dict[int, Dict[int, List]] = {} # Snapshot -> channel -> messages self.markers_received: Dict[int, Set[int]] = {} # Snapshot -> set of channels # Initialize channels for neighbor in neighbors: self.channels[neighbor] = queue.Queue() def start_snapshot(self, snapshot_id: int): """Initiate snapshot (only by coordinator)""" print(f"Worker {self.worker_id}: Starting snapshot {snapshot_id}") # Record local state self.recorded_state[snapshot_id] = self.state.copy() self.recording[snapshot_id] = True self.recorded_messages[snapshot_id] = {n: [] for n in self.neighbors} self.markers_received[snapshot_id] = set() # Send markers to all neighbors for neighbor in self.neighbors: self.send_message(neighbor, Message(MessageType.MARKER, None, snapshot_id)) def receive_marker(self, snapshot_id: int, from_channel: int): """Handle marker message""" if snapshot_id not in self.recording: # First marker for this snapshot print(f"Worker {self.worker_id}: Received first marker for snapshot {snapshot_id}") # Record state self.recorded_state[snapshot_id] = self.state.copy() self.recording[snapshot_id] = True self.recorded_messages[snapshot_id] = {n: [] for n in self.neighbors} self.markers_received[snapshot_id] = {from_channel} # Channel from which marker arrived is empty self.recorded_messages[snapshot_id][from_channel] = [] # Send markers to all neighbors for neighbor in self.neighbors: self.send_message(neighbor, Message(MessageType.MARKER, None, snapshot_id)) else: # Subsequent marker print(f"Worker {self.worker_id}: Received marker from channel {from_channel}") self.markers_received[snapshot_id].add(from_channel) # Check if snapshot complete if len(self.markers_received[snapshot_id]) == len(self.neighbors): self.finalize_snapshot(snapshot_id) def receive_data(self, data: any, from_channel: int): """Handle data message""" # Process data (update state) self.state[f'key_{len(self.state)}'] = data # Record message if snapshot in progress for snapshot_id, recording in self.recording.items(): if recording and from_channel not in self.markers_received.get(snapshot_id, set()): # Recording messages on this channel self.recorded_messages[snapshot_id][from_channel].append(data) def finalize_snapshot(self, snapshot_id: int): """Snapshot complete for this worker""" print(f"Worker {self.worker_id}: Snapshot {snapshot_id} complete") print(f" State: {self.recorded_state[snapshot_id]}") print(f" Messages: {self.recorded_messages[snapshot_id]}") self.recording[snapshot_id] = False def send_message(self, to_worker: int, message: Message): """Send message to another worker (simulated)""" # In real system, this would send over network pass # Example usage def main(): # Create 3 workers in ring topology: 0 -> 1 -> 2 -> 0 workers = [ SnapshotWorker(0, [1, 2]), SnapshotWorker(1, [0, 2]), SnapshotWorker(2, [0, 1]), ] # Simulate some processing workers[0].state = {'count': 10} workers[1].state = {'count': 20} workers[2].state = {'count': 30} # Initiate snapshot from worker 0 workers[0].start_snapshot(snapshot_id=1) # Simulate marker propagation workers[1].receive_marker(snapshot_id=1, from_channel=0) workers[2].receive_marker(snapshot_id=1, from_channel=1) workers[0].receive_marker(snapshot_id=1, from_channel=2) workers[1].receive_marker(snapshot_id=1, from_channel=2) workers[2].receive_marker(snapshot_id=1, from_channel=0) if __name__ == '__main__': main() ``` ### Pattern 4: Backpressure Coordination (Python) ```python import asyncio from dataclasses import dataclass from typing import Optional import time @dataclass class Watermark: """Progress indicator for backpressure""" timestamp: int worker_id: int class BackpressureCoordinator: """Coordinates flow control across pipeline stages""" def __init__(self, num_workers: int, buffer_size: int = 100): self.num_workers = num_workers self.buffer_size = buffer_size self.worker_watermarks = [0] * num_workers self.global_watermark = 0 self.lock = asyncio.Lock() async def update_watermark(self, worker_id: int, timestamp: int): """Update watermark for worker""" async with self.lock: self.worker_watermarks[worker_id] = timestamp old_global = self.global_watermark self.global_watermark = min(self.worker_watermarks) if self.global_watermark > old_global: print(f"Global watermark advanced to {self.global_watermark}") async def can_proceed(self, worker_id: int, timestamp: int) -> bool: """Check if worker can proceed without overwhelming downstream""" async with self.lock: # Allow if within buffer_size of slowest worker
عرض على GitHub
ملف SKILL.md هذا كبير جدا، لذلك يعرض SkillsMP القسم الاول فقط هنا. عرض على GitHub