risingwave_frontend/optimizer/property/
distribution.rs1use std::collections::HashMap;
46use std::fmt;
47use std::fmt::Debug;
48
49use fixedbitset::FixedBitSet;
50use generic::PhysicalPlanRef;
51use itertools::Itertools;
52use risingwave_batch::worker_manager::worker_node_manager::WorkerNodeSelector;
53use risingwave_common::catalog::{FieldDisplay, Schema, TableId};
54use risingwave_common::hash::WorkerSlotId;
55use risingwave_pb::batch_plan::ExchangeInfo;
56use risingwave_pb::batch_plan::exchange_info::{
57 ConsistentHashInfo, Distribution as PbDistribution, DistributionMode, HashInfo,
58};
59
60use super::super::plan_node::*;
61use crate::catalog::FragmentId;
62use crate::catalog::catalog_service::CatalogReader;
63use crate::error::Result;
64use crate::optimizer::property::Order;
65
66#[derive(Debug, Clone, PartialEq, Eq, Hash)]
68pub enum Distribution {
69 Single,
75 SomeShard,
78 HashShard(Vec<usize>),
82 UpstreamHashShard(Vec<usize>, TableId),
96 Broadcast,
98}
99
100#[derive(Debug, Clone, PartialEq)]
102pub enum RequiredDist {
103 Any,
105 AnyShard,
107 ShardByKey(FixedBitSet),
112 ShardByExactKey(FixedBitSet),
115 PhysicalDist(Distribution),
117}
118
119impl Distribution {
120 pub fn to_prost(
121 &self,
122 output_count: u32,
123 catalog_reader: &CatalogReader,
124 worker_node_manager: &WorkerNodeSelector,
125 batch_parallelism: usize,
126 ) -> Result<ExchangeInfo> {
127 let exchange_info = ExchangeInfo {
128 mode: match self {
129 Distribution::Single => DistributionMode::Single,
130 Distribution::HashShard(_) => DistributionMode::Hash,
131 Distribution::SomeShard => DistributionMode::Single,
133 Distribution::Broadcast => DistributionMode::Broadcast,
134 Distribution::UpstreamHashShard(_, _) => DistributionMode::ConsistentHash,
135 } as i32,
136 distribution: match self {
137 Distribution::Single => None,
138 Distribution::HashShard(key) => {
139 assert!(
140 !key.is_empty(),
141 "hash key should not be empty, use `Single` instead"
142 );
143 Some(PbDistribution::HashInfo(HashInfo {
144 output_count,
145 key: key.iter().map(|num| *num as u32).collect(),
146 }))
147 }
148 Distribution::SomeShard => None,
150 Distribution::Broadcast => None,
151 Distribution::UpstreamHashShard(key, table_id) => {
152 assert!(
153 !key.is_empty(),
154 "hash key should not be empty, use `Single` instead"
155 );
156
157 let vnode_mapping = worker_node_manager.fragment_mapping(
158 Self::get_fragment_id(catalog_reader, *table_id)?,
159 batch_parallelism,
160 )?;
161
162 let worker_slot_to_id_map: HashMap<WorkerSlotId, u32> = vnode_mapping
163 .iter_unique()
164 .enumerate()
165 .map(|(i, worker_slot_id)| (worker_slot_id, i as u32))
166 .collect();
167
168 Some(PbDistribution::ConsistentHashInfo(ConsistentHashInfo {
169 vmap: vnode_mapping
170 .iter()
171 .map(|id| worker_slot_to_id_map[&id])
172 .collect_vec(),
173 key: key.iter().map(|num| *num as u32).collect(),
174 }))
175 }
176 },
177 };
178 Ok(exchange_info)
179 }
180
181 pub fn satisfies(&self, required: &RequiredDist) -> bool {
183 match required {
184 RequiredDist::Any => true,
185 RequiredDist::AnyShard => {
186 matches!(
187 self,
188 Distribution::SomeShard
189 | Distribution::HashShard(_)
190 | Distribution::UpstreamHashShard(_, _)
191 | Distribution::Broadcast
192 )
193 }
194 RequiredDist::ShardByKey(required_key) => match self {
195 Distribution::HashShard(hash_key)
196 | Distribution::UpstreamHashShard(hash_key, _) => {
197 hash_key.iter().all(|idx| required_key.contains(*idx))
198 }
199 _ => false,
200 },
201 RequiredDist::ShardByExactKey(required_key) => match self {
202 Distribution::HashShard(hash_key)
203 | Distribution::UpstreamHashShard(hash_key, _) => {
204 hash_key.len() == required_key.count_ones(..)
205 && hash_key.iter().all(|idx| required_key.contains(*idx))
206 }
207 _ => false,
208 },
209 RequiredDist::PhysicalDist(other) => self == other,
210 }
211 }
212
213 pub fn dist_column_indices(&self) -> &[usize] {
215 self.dist_column_indices_opt()
216 .unwrap_or_else(|| panic!("cannot obtain distribution columns for {self:?}"))
217 }
218
219 pub fn dist_column_indices_opt(&self) -> Option<&[usize]> {
221 match self {
222 Distribution::Single => Some(&[]),
223 Distribution::HashShard(dists) | Distribution::UpstreamHashShard(dists, _) => {
224 Some(dists)
225 }
226 Distribution::SomeShard | Distribution::Broadcast => None,
227 }
228 }
229
230 #[inline(always)]
231 fn get_fragment_id(catalog_reader: &CatalogReader, table_id: TableId) -> Result<FragmentId> {
232 catalog_reader
233 .read_guard()
234 .get_any_table_by_id(table_id)
235 .map(|table| table.fragment_id)
236 .map_err(Into::into)
237 }
238}
239
240impl fmt::Display for Distribution {
241 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
242 f.write_str("[")?;
243 match self {
244 Self::Single => f.write_str("Single")?,
245 Self::SomeShard => f.write_str("SomeShard")?,
246 Self::Broadcast => f.write_str("Broadcast")?,
247 Self::HashShard(vec) | Self::UpstreamHashShard(vec, _) => {
248 for key in vec {
249 std::fmt::Debug::fmt(&key, f)?;
250 }
251 }
252 }
253 f.write_str("]")
254 }
255}
256
257pub struct DistributionDisplay<'a> {
258 pub distribution: &'a Distribution,
259 pub input_schema: &'a Schema,
260}
261
262impl DistributionDisplay<'_> {
263 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
264 let that = self.distribution;
265 match that {
266 Distribution::Single => f.write_str("Single"),
267 Distribution::SomeShard => f.write_str("SomeShard"),
268 Distribution::Broadcast => f.write_str("Broadcast"),
269 Distribution::HashShard(vec) | Distribution::UpstreamHashShard(vec, _) => {
270 if let Distribution::HashShard(_) = that {
271 f.write_str("HashShard(")?;
272 } else {
273 f.write_str("UpstreamHashShard(")?;
274 }
275 for (pos, key) in vec.iter().copied().with_position() {
276 std::fmt::Debug::fmt(
277 &FieldDisplay(self.input_schema.fields.get(key).unwrap()),
278 f,
279 )?;
280 match pos {
281 itertools::Position::First | itertools::Position::Middle => {
282 f.write_str(", ")?;
283 }
284 _ => {}
285 }
286 }
287 f.write_str(")")
288 }
289 }
290 }
291}
292
293impl fmt::Debug for DistributionDisplay<'_> {
294 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
295 self.fmt(f)
296 }
297}
298
299impl fmt::Display for DistributionDisplay<'_> {
300 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
301 self.fmt(f)
302 }
303}
304
305impl RequiredDist {
306 pub fn single() -> Self {
307 Self::PhysicalDist(Distribution::Single)
308 }
309
310 pub fn shard_by_key(tot_col_num: usize, key: &[usize]) -> Self {
311 let mut cols = FixedBitSet::with_capacity(tot_col_num);
312 for i in key {
313 cols.insert(*i);
314 }
315 assert!(!cols.is_clear());
316 Self::ShardByKey(cols)
317 }
318
319 pub fn shard_by_exact_key(tot_col_num: usize, key: &[usize]) -> Self {
320 let mut cols = FixedBitSet::with_capacity(tot_col_num);
321 for i in key {
322 cols.insert(*i);
323 }
324 assert!(!cols.is_clear());
325 Self::ShardByExactKey(cols)
326 }
327
328 pub fn hash_shard(key: &[usize]) -> Self {
329 assert!(!key.is_empty());
330 Self::PhysicalDist(Distribution::HashShard(key.to_vec()))
331 }
332
333 pub fn batch_enforce_if_not_satisfies(
334 &self,
335 mut plan: BatchPlanRef,
336 required_order: &Order,
337 ) -> Result<BatchPlanRef> {
338 plan = required_order.enforce_if_not_satisfies(plan)?;
339 if !plan.distribution().satisfies(self) {
340 Ok(self.batch_enforce(plan, required_order))
341 } else {
342 Ok(plan)
343 }
344 }
345
346 pub fn streaming_enforce_if_not_satisfies(&self, plan: StreamPlanRef) -> Result<StreamPlanRef> {
347 if !plan.distribution().satisfies(self) {
348 Ok(self.stream_enforce(plan))
349 } else {
350 Ok(plan)
351 }
352 }
353
354 pub fn no_shuffle(plan: StreamPlanRef) -> StreamPlanRef {
355 StreamExchange::new_no_shuffle(plan).into()
356 }
357
358 pub fn satisfies(&self, required: &RequiredDist) -> bool {
360 match self {
361 RequiredDist::Any => matches!(required, RequiredDist::Any),
362 RequiredDist::AnyShard => {
363 matches!(required, RequiredDist::Any | RequiredDist::AnyShard)
364 }
365 RequiredDist::ShardByKey(key) => match required {
366 RequiredDist::Any | RequiredDist::AnyShard => true,
367 RequiredDist::ShardByKey(required_key) => key.is_subset(required_key),
368 RequiredDist::ShardByExactKey(required_key) => {
369 key == required_key && key.count_ones(..) == 1
370 }
371 _ => false,
372 },
373 RequiredDist::ShardByExactKey(key) => match required {
374 RequiredDist::Any | RequiredDist::AnyShard => true,
375 RequiredDist::ShardByKey(required_key) => key.is_subset(required_key),
376 RequiredDist::ShardByExactKey(required_key) => key == required_key,
377 _ => false,
378 },
379 RequiredDist::PhysicalDist(dist) => dist.satisfies(required),
380 }
381 }
382
383 pub fn batch_enforce(&self, plan: BatchPlanRef, required_order: &Order) -> BatchPlanRef {
384 let dist = self.to_dist();
385 BatchExchange::new(plan, required_order.clone(), dist).into()
386 }
387
388 pub fn stream_enforce(&self, plan: StreamPlanRef) -> StreamPlanRef {
389 let dist = self.to_dist();
390 StreamExchange::new(plan, dist).into()
391 }
392
393 fn to_dist(&self) -> Distribution {
394 match self {
395 RequiredDist::Any => unreachable!(),
398 RequiredDist::AnyShard => todo!(),
400 RequiredDist::ShardByKey(required_keys) => {
401 Distribution::HashShard(required_keys.ones().collect())
402 }
403 RequiredDist::ShardByExactKey(required_keys) => {
404 Distribution::HashShard(required_keys.ones().collect())
405 }
406 RequiredDist::PhysicalDist(dist) => dist.clone(),
407 }
408 }
409}
410
411impl StreamPlanRef {
412 pub fn enforce_concrete_distribution(self) -> Self {
415 match self.distribution() {
416 Distribution::SomeShard => {
417 RequiredDist::shard_by_key(self.schema().len(), self.expect_stream_key())
418 .stream_enforce(self)
419 }
420 _ => self,
421 }
422 }
423}
424
425#[cfg(test)]
426mod tests {
427 use super::{Distribution, RequiredDist};
428
429 #[test]
430 fn hash_shard_satisfy() {
431 let d1 = Distribution::HashShard(vec![0, 1]);
432 let d2 = Distribution::HashShard(vec![1, 0]);
433 let d3 = Distribution::HashShard(vec![0]);
434 let d4 = Distribution::HashShard(vec![1]);
435
436 let r1 = RequiredDist::shard_by_key(2, &[0, 1]);
437 let r3 = RequiredDist::shard_by_key(2, &[0]);
438 let r4 = RequiredDist::shard_by_key(2, &[1]);
439 let r_exact = RequiredDist::shard_by_exact_key(2, &[0, 1]);
440 let r_exact_single = RequiredDist::shard_by_exact_key(2, &[0]);
441 assert!(d1.satisfies(&RequiredDist::PhysicalDist(d1.clone())));
442 assert!(d2.satisfies(&RequiredDist::PhysicalDist(d2.clone())));
443 assert!(d3.satisfies(&RequiredDist::PhysicalDist(d3.clone())));
444 assert!(d4.satisfies(&RequiredDist::PhysicalDist(d4.clone())));
445
446 assert!(!d2.satisfies(&RequiredDist::PhysicalDist(d1.clone())));
447 assert!(!d3.satisfies(&RequiredDist::PhysicalDist(d1.clone())));
448 assert!(!d4.satisfies(&RequiredDist::PhysicalDist(d1.clone())));
449
450 assert!(!d1.satisfies(&RequiredDist::PhysicalDist(d3.clone())));
451 assert!(!d2.satisfies(&RequiredDist::PhysicalDist(d3.clone())));
452 assert!(!d1.satisfies(&RequiredDist::PhysicalDist(d4.clone())));
453 assert!(!d2.satisfies(&RequiredDist::PhysicalDist(d4.clone())));
454
455 assert!(d1.satisfies(&r1));
456 assert!(d2.satisfies(&r1));
457 assert!(d3.satisfies(&r1));
458 assert!(d4.satisfies(&r1));
459
460 assert!(!d1.satisfies(&r3));
461 assert!(!d2.satisfies(&r3));
462 assert!(d3.satisfies(&r3));
463 assert!(!d4.satisfies(&r3));
464
465 assert!(!d1.satisfies(&r4));
466 assert!(!d2.satisfies(&r4));
467 assert!(!d3.satisfies(&r4));
468 assert!(d4.satisfies(&r4));
469
470 assert!(d1.satisfies(&r_exact));
471 assert!(d2.satisfies(&r_exact));
472 assert!(!d3.satisfies(&r_exact));
473 assert!(!d4.satisfies(&r_exact));
474
475 assert!(r3.satisfies(&r1));
476 assert!(r4.satisfies(&r1));
477 assert!(!r1.satisfies(&r3));
478 assert!(!r1.satisfies(&r4));
479 assert!(!r3.satisfies(&r4));
480 assert!(!r4.satisfies(&r3));
481
482 assert!(r_exact.satisfies(&r1));
483 assert!(!r1.satisfies(&r_exact));
484 assert!(!r3.satisfies(&r_exact));
485 assert!(!r_exact.satisfies(&r3));
486
487 assert!(r3.satisfies(&r_exact_single));
488 assert!(r_exact_single.satisfies(&r3));
489 }
490}