oxigeo_distributed/lib.rs
1//! OxiGeo Distributed Processing
2//!
3//! This crate provides distributed processing capabilities for large-scale geospatial
4//! workflows using Apache Arrow Flight for zero-copy data transfer.
5//!
6//! # Features
7//!
8//! - **Arrow Flight RPC**: Zero-copy data transfer between nodes
9//! - **Worker Nodes**: Execute processing tasks with resource management
10//! - **Coordinator**: Schedule and manage distributed execution
11//! - **Data Partitioning**: Spatial, hash, range, and load-balanced partitioning
12//! - **Shuffle Operations**: Efficient data redistribution for group-by and joins
13//! - **Fault Tolerance**: Automatic retry and failure recovery
14//! - **Progress Monitoring**: Real-time tracking of distributed execution
15//!
16//! # Architecture
17//!
18//! ```text
19//! ┌─────────────┐
20//! │ Coordinator │ ──── Schedules tasks
21//! └──────┬──────┘
22//! │
23//! ┌────┴────┐
24//! │ Flight │
25//! │ Server │
26//! └────┬────┘
27//! │
28//! ┌────┴────────────────┐
29//! │ │
30//! ┌─▼──────┐ ┌───▼─────┐
31//! │ Worker │ │ Worker │
32//! │ Node 1 │ │ Node 2 │
33//! └────────┘ └─────────┘
34//! ```
35//!
36//! # Example: Distributed NDVI Calculation
37//!
38//! ```rust,no_run
39//! use oxigeo_distributed::*;
40//! # async fn example() -> std::result::Result<(), Box<dyn std::error::Error>> {
41//!
42//! // Create coordinator
43//! let config = CoordinatorConfig::new("localhost:50051".to_string());
44//! let coordinator = Coordinator::new(config);
45//!
46//! // Add workers
47//! coordinator.add_worker("worker-1".to_string(), "localhost:50052".to_string())?;
48//! coordinator.add_worker("worker-2".to_string(), "localhost:50053".to_string())?;
49//!
50//! // Partition data spatially
51//! let extent = SpatialExtent::new(0.0, 0.0, 1000.0, 1000.0)?;
52//! let partitioner = TilePartitioner::new(extent, 4, 4)?;
53//! let partitions = partitioner.partition();
54//!
55//! // Submit tasks for each partition
56//! for partition in partitions {
57//! coordinator.submit_task(
58//! partition.id,
59//! TaskOperation::CalculateIndex {
60//! index_type: "NDVI".to_string(),
61//! bands: vec![3, 4], // Red and NIR
62//! },
63//! )?;
64//! }
65//!
66//! // Monitor progress
67//! while !coordinator.is_complete() {
68//! let progress = coordinator.get_progress()?;
69//! println!(
70//! "Progress: {}/{} completed",
71//! progress.completed_tasks,
72//! progress.total_tasks()
73//! );
74//! tokio::time::sleep(tokio::time::Duration::from_secs(1)).await;
75//! }
76//!
77//! // Collect results
78//! let results = coordinator.collect_results()?;
79//! println!("Processing complete: {} results", results.len());
80//! # Ok(())
81//! # }
82//! ```
83//!
84//! # Example: Custom Processing with Workers
85//!
86//! ```rust,no_run
87//! use oxigeo_distributed::*;
88//! # async fn example() -> std::result::Result<(), Box<dyn std::error::Error>> {
89//!
90//! // Create worker
91//! let config = WorkerConfig::new("worker-1".to_string())
92//! .with_max_concurrent_tasks(4)
93//! .with_memory_limit(8 * 1024 * 1024 * 1024); // 8 GB
94//!
95//! let worker = Worker::new(config);
96//!
97//! // Serve the worker over Arrow Flight so a coordinator can dispatch tasks to
98//! // it end-to-end via `Coordinator::dispatch_task_to_worker` (which ships the
99//! // task + its input partition through the `execute_task` Flight action):
100//! let flight_server = FlightServer::new().with_worker(std::sync::Arc::new(worker));
101//! let _service = flight_server.into_service();
102//! // tonic::transport::Server::builder().add_service(_service).serve(addr).await?;
103//! # Ok(())
104//! # }
105//! ```
106//!
107//! # Example: Data Shuffle
108//!
109//! ```rust,no_run
110//! use oxigeo_distributed::*;
111//! use arrow::array::{Int32Array, StringArray};
112//! use arrow::datatypes::{DataType, Field, Schema};
113//! use arrow::record_batch::RecordBatch;
114//! use std::sync::Arc;
115//!
116//! # fn example() -> std::result::Result<(), Box<dyn std::error::Error>> {
117//! // Create test data
118//! let schema = Arc::new(Schema::new(vec![
119//! Field::new("id", DataType::Int32, false),
120//! Field::new("name", DataType::Utf8, false),
121//! ]));
122//!
123//! let batch = RecordBatch::try_new(
124//! schema,
125//! vec![
126//! Arc::new(Int32Array::from(vec![1, 2, 3, 4, 5])),
127//! Arc::new(StringArray::from(vec!["a", "b", "c", "d", "e"])),
128//! ],
129//! )?;
130//!
131//! // Hash shuffle by ID column
132//! let shuffle = HashShuffle::new("id".to_string(), 2)?;
133//! let partitions = shuffle.shuffle(&batch)?;
134//!
135//! println!("Data shuffled into {} partitions", partitions.len());
136//! # Ok(())
137//! # }
138//! ```
139
140#![deny(clippy::unwrap_used)]
141#![deny(clippy::panic)]
142#![warn(missing_docs)]
143#![warn(clippy::expect_used)]
144
145pub mod coordinator;
146pub mod error;
147pub mod flight;
148pub mod operations;
149pub mod partition;
150pub mod shuffle;
151pub mod task;
152pub mod worker;
153
154// Re-export main types
155pub use coordinator::{Coordinator, CoordinatorConfig, CoordinatorProgress, WorkerInfo};
156pub use error::{DistributedError, Result};
157pub use flight::{FlightClient, FlightServer};
158pub use partition::{
159 HashPartitioner, LoadBalancedPartitioner, Partition, PartitionStrategy, RangePartitioner,
160 SpatialExtent, StripPartitioner, TilePartitioner,
161};
162pub use shuffle::{
163 BroadcastShuffle, HashShuffle, RangeShuffle, ShuffleConfig, ShuffleKey, ShuffleResult,
164 ShuffleStats, ShuffleType,
165};
166pub use task::{
167 PartitionId, Task, TaskContext, TaskId, TaskOperation, TaskResult, TaskScheduler, TaskStatus,
168};
169pub use worker::{Worker, WorkerConfig, WorkerHealthCheck, WorkerMetrics, WorkerStatus};
170
171#[cfg(test)]
172mod tests {
173 use super::*;
174
175 #[test]
176 fn test_exports() {
177 // Verify that main types are exported
178 let _config: CoordinatorConfig;
179 let _worker_config: WorkerConfig;
180 let _task_id: TaskId;
181 let _partition_id: PartitionId;
182 }
183}