nodedb_physical/physical_plan/cluster_array.rs
1// SPDX-License-Identifier: Apache-2.0
2
3//! Control-Plane cluster array operations.
4//!
5//! These variants are emitted by the SQL converter when cluster mode is
6//! active and an array operation spans multiple vShards. They are handled
7//! exclusively by the Control-Plane dispatch loop (pgwire routing handler)
8//! which calls the `ArrayCoordinator` before anything touches the SPSC
9//! bridge. They are **never sent to the Data Plane**.
10//!
11//! The converter emits local `ArrayOp` variants in single-node mode and
12//! `ClusterArrayOp` variants in cluster mode. Downstream code that sees
13//! a `PhysicalPlan::ClusterArray(_)` knows it must call the coordinator,
14//! not the SPSC bridge.
15
16use nodedb_array::types::ArrayId;
17
18/// Cluster-mode array operations executed by the coordinator on the
19/// Control Plane.
20#[derive(
21 Debug,
22 Clone,
23 PartialEq,
24 serde::Serialize,
25 serde::Deserialize,
26 zerompk::ToMessagePack,
27 zerompk::FromMessagePack,
28)]
29pub enum ClusterArrayOp {
30 /// Fan-out a slice query across all target vShards and merge rows.
31 ///
32 /// `slice_hilbert_ranges` — pre-computed `(lo, hi)` Hilbert-prefix
33 /// ranges that determine which shards to contact. An empty vec means
34 /// "scan all shards".
35 ///
36 /// `prefix_bits` — routing granularity from the array catalog entry.
37 Slice {
38 array_id: ArrayId,
39 /// zerompk-encoded `Slice` predicate.
40 slice_msgpack: Vec<u8>,
41 /// Projected attribute indices (empty = all).
42 attr_projection: Vec<u32>,
43 /// Maximum rows returned. 0 = unlimited.
44 limit: u32,
45 /// Pre-computed Hilbert-prefix ranges for shard selection.
46 slice_hilbert_ranges: Vec<(u64, u64)>,
47 /// Routing granularity from the array catalog entry.
48 prefix_bits: u8,
49 /// Bitemporal system-time cutoff. `None` = live read.
50 system_as_of: Option<i64>,
51 /// Bitemporal valid-time point. `None` = no valid-time filter.
52 valid_at_ms: Option<i64>,
53 },
54
55 /// Fan-out an aggregate across all target vShards and reduce partials.
56 Agg {
57 array_id: ArrayId,
58 /// Index of the attribute to aggregate.
59 attr_idx: u32,
60 /// zerompk-encoded `ArrayReducer`.
61 reducer_msgpack: Vec<u8>,
62 /// Dimension index to group by (-1 = no grouping).
63 group_by_dim: i32,
64 /// Pre-computed Hilbert ranges for shard selection (empty = all).
65 slice_hilbert_ranges: Vec<(u64, u64)>,
66 /// Routing granularity.
67 prefix_bits: u8,
68 /// Bitemporal system-time cutoff. `None` = live read.
69 system_as_of: Option<i64>,
70 /// Bitemporal valid-time point. `None` = no valid-time filter.
71 valid_at_ms: Option<i64>,
72 },
73
74 /// Fan-out a cell write batch, partitioned by Hilbert tile.
75 ///
76 /// `cells` — list of `(hilbert_prefix, zerompk-encoded single-cell
77 /// bytes)`. The coordinator partitions these by prefix and sends each
78 /// shard's subset via `coord_put`.
79 Put {
80 array_id: ArrayId,
81 /// zerompk-encoded `ArrayId` bytes for the wire request.
82 array_id_msgpack: Vec<u8>,
83 /// Cells as `(hilbert_prefix, cell_msgpack)` pairs.
84 cells: Vec<(u64, Vec<u8>)>,
85 /// WAL LSN allocated by the Control Plane for this batch.
86 wal_lsn: u64,
87 /// Routing granularity.
88 prefix_bits: u8,
89 },
90
91 /// Fan-out a coord delete batch, partitioned by Hilbert tile.
92 ///
93 /// `coords` — `(hilbert_prefix, zerompk-encoded single-coord bytes)`.
94 Delete {
95 array_id: ArrayId,
96 /// zerompk-encoded `ArrayId` bytes for the wire request.
97 array_id_msgpack: Vec<u8>,
98 /// Coords as `(hilbert_prefix, coord_msgpack)` pairs.
99 coords: Vec<(u64, Vec<u8>)>,
100 /// WAL LSN.
101 wal_lsn: u64,
102 /// Routing granularity.
103 prefix_bits: u8,
104 },
105}