Forward a consume request to the remote partition leader via the gateway. Routes the stream SELECT SQL through `gateway.execute_sql`, which plans it locally and dispatches it as an `ExecuteRequest` over QUIC to the correct leader node. The `leader_node` parameter is accepted for caller compatibility but is ignored — the gateway handles node selection.
(
state: &SharedState,
params: &ConsumeParams<'_>,
_leader_node: u64,
)
| 234 | /// leader node. The `leader_node` parameter is accepted for caller |
| 235 | /// compatibility but is ignored — the gateway handles node selection. |
| 236 | pub async fn consume_remote( |
| 237 | state: &SharedState, |
| 238 | params: &ConsumeParams<'_>, |
| 239 | _leader_node: u64, |
| 240 | ) -> Result<ConsumeResult, ConsumeError> { |
| 241 | let gateway = state |
| 242 | .gateway |
| 243 | .as_ref() |
| 244 | .ok_or(ConsumeError::NoClusterTransport)?; |
| 245 | |
| 246 | let sql = build_consume_sql(params); |
| 247 | let tenant_id = params.tenant_id; |
| 248 | |
| 249 | let gw_ctx = crate::control::gateway::core::QueryContext { |
| 250 | tenant_id: crate::types::TenantId::new(tenant_id), |
| 251 | trace_id: nodedb_types::TraceId::generate(), |
| 252 | database_id: nodedb_types::id::DatabaseId::DEFAULT, |
| 253 | }; |
| 254 | |
| 255 | let query_ctx = crate::control::planner::context::QueryContext::for_state(state); |
| 256 | |
| 257 | let payloads = gateway |
| 258 | .execute_sql(&gw_ctx, &sql, &[], || { |
| 259 | let tasks = tokio::task::block_in_place(|| { |
| 260 | tokio::runtime::Handle::current().block_on(query_ctx.plan_sql( |
| 261 | &sql, |
| 262 | crate::types::TenantId::new(tenant_id), |
| 263 | crate::types::DatabaseId::DEFAULT, |
| 264 | )) |
| 265 | }) |
| 266 | .map_err(|e| crate::Error::PlanError { |
| 267 | detail: e.to_string(), |
| 268 | })?; |
| 269 | // Take the first task's plan (stream reads are single-task). |
| 270 | tasks |
| 271 | .into_iter() |
| 272 | .next() |
| 273 | .map(|t| t.plan) |
| 274 | .ok_or_else(|| crate::Error::PlanError { |
| 275 | detail: "stream SELECT produced no physical tasks".into(), |
| 276 | }) |
| 277 | }) |
| 278 | .await |
| 279 | .map_err(|e| ConsumeError::RemoteError(e.to_string()))?; |
| 280 | |
| 281 | // Deserialize events from the response payloads. |
| 282 | // Payloads contain msgpack-serialised Vec<CdcEvent>. |
| 283 | let events: Vec<Arc<CdcEvent>> = if let Some(payload) = payloads.first() { |
| 284 | zerompk::from_msgpack::<Vec<CdcEvent>>(payload) |
| 285 | .unwrap_or_default() |
| 286 | .into_iter() |
| 287 | .map(Arc::new) |
| 288 | .collect() |
| 289 | } else { |
| 290 | Vec::new() |
| 291 | }; |
| 292 | |
| 293 | // Compute per-partition max LSN for the returned batch. |
no test coverage detected