Skip to main content

datanode/region_server/
remote_dyn_filter.rs

1// Copyright 2023 Greptime Team
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//     http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15use std::pin::Pin;
16use std::sync::{Arc, Weak};
17use std::task::{Context, Poll};
18
19use api::region::RegionResponse;
20use api::v1::region::RemoteDynFilterRequest;
21use api::v1::region::remote_dyn_filter_request::Action;
22use common_base::Plugins;
23use common_recordbatch::adapter::RecordBatchMetrics;
24use common_recordbatch::{OrderOption, RecordBatch, RecordBatchStream, SendableRecordBatchStream};
25use common_telemetry::{debug, warn};
26use datafusion_expr::LogicalPlan;
27use futures_util::Stream;
28use query::dist_plan::{
29    RemoteDynFilterReceiverInjector, RemoteDynFilterReceiverInjectorRef,
30    RemoteDynFilterReceiverLogicalPlan,
31};
32use query::options::remote_dyn_filter_pushdown_enabled_from_extensions;
33use query::promql::plan_contains_promql_extension;
34use session::context::QueryContextRef;
35use session::query_id::QueryId;
36use snafu::OptionExt;
37use store_api::storage::RegionId;
38
39use crate::error::{self, Result, UnexpectedSnafu};
40use crate::region_server::registrations::{
41    RemoteDynFilterId, RemoteDynFilterUpdateOutcome, apply_remote_dyn_filter_update,
42    initial_dyn_filter_regs_from_query_ctx, register_initial_dyn_filter_regs,
43    remote_dyn_filter_exprs_for_initial_regs, remove_initial_dyn_filter_regs,
44    unregister_remote_dyn_filter,
45};
46use crate::region_server::{RegionServer, RegionServerInner};
47
48fn remote_dyn_filter_receiver_plan(
49    server: &Weak<RegionServerInner>,
50    origin: LogicalPlan,
51    ctx: QueryContextRef,
52) -> LogicalPlan {
53    if !remote_dyn_filter_pushdown_enabled(&ctx) {
54        return origin;
55    }
56
57    if plan_contains_promql_extension(&origin) {
58        return origin;
59    }
60
61    let Some(query_id) = ctx.remote_query_id_value() else {
62        return origin;
63    };
64
65    let Some(initial_regs) = initial_dyn_filter_regs_from_query_ctx(&ctx) else {
66        return origin;
67    };
68
69    let Some(server) = server.upgrade() else {
70        return origin;
71    };
72
73    let dyn_filters = remote_dyn_filter_exprs_for_initial_regs(
74        &server.initial_remote_dyn_filter_registrations,
75        &query_id,
76        &initial_regs,
77        origin.schema().as_arrow(),
78    );
79
80    if dyn_filters.is_empty() {
81        return origin;
82    }
83
84    RemoteDynFilterReceiverLogicalPlan::new(origin, dyn_filters).into_logical_plan()
85}
86
87fn remote_dyn_filter_pushdown_enabled(query_ctx: &QueryContextRef) -> bool {
88    match remote_dyn_filter_pushdown_enabled_from_extensions(&query_ctx.extensions()) {
89        Ok(enabled) => enabled,
90        Err(error) => {
91            warn!(error; "Remote dynamic filter pushdown is disabled because the query option is invalid");
92            false
93        }
94    }
95}
96
97impl RegionServer {
98    pub fn install_remote_dyn_filter_receiver_injector(&self, plugins: &Plugins) {
99        let server = Arc::downgrade(&self.inner);
100        plugins.insert::<RemoteDynFilterReceiverInjectorRef>(Arc::new(
101            RemoteDynFilterReceiverInjector::new(move |origin, ctx| {
102                remote_dyn_filter_receiver_plan(&server, origin, ctx)
103            }),
104        ));
105    }
106
107    pub(super) fn register_initial_remote_dyn_filter_cleanup(
108        &self,
109        query_ctx: &QueryContextRef,
110        region_id: RegionId,
111    ) -> Option<RemoteDynFilterRegistrationGuard> {
112        if !remote_dyn_filter_pushdown_enabled(query_ctx) {
113            return None;
114        }
115
116        let initial_dyn_filter_regs = initial_dyn_filter_regs_from_query_ctx(query_ctx);
117        let query_id = query_ctx.remote_query_id_value();
118        let registered_filter_ids = if let (Some(query_id), Some(regs)) =
119            (query_id.as_ref(), initial_dyn_filter_regs.as_ref())
120        {
121            register_initial_dyn_filter_regs(
122                &self.inner.initial_remote_dyn_filter_registrations,
123                query_id,
124                region_id,
125                regs,
126            )
127        } else {
128            Vec::new()
129        };
130
131        match (query_id, registered_filter_ids.is_empty()) {
132            (Some(query_id), false) => Some(RemoteDynFilterRegistrationGuard::new(
133                self.clone(),
134                query_id,
135                region_id,
136                registered_filter_ids,
137            )),
138            _ => None,
139        }
140    }
141
142    pub(super) async fn handle_remote_dyn_filter_request(
143        &self,
144        request: &RemoteDynFilterRequest,
145    ) -> Result<RegionResponse> {
146        if request.query_id.is_empty() {
147            return error::MissingRequiredFieldSnafu { name: "query_id" }.fail();
148        }
149
150        let query_id = request.query_id.parse::<QueryId>().map_err(|_| {
151            UnexpectedSnafu {
152                violated: "remote dynamic filter query_id must be a valid QueryId",
153            }
154            .build()
155        })?;
156
157        match request
158            .action
159            .as_ref()
160            .context(error::MissingRequiredFieldSnafu { name: "action" })?
161        {
162            Action::Update(update) => {
163                self.handle_remote_dyn_filter_update(&query_id, update)
164                    .await
165            }
166            Action::Unregister(unregister) => {
167                self.handle_remote_dyn_filter_unregister(&query_id, unregister)
168                    .await
169            }
170        }
171    }
172
173    async fn handle_remote_dyn_filter_update(
174        &self,
175        query_id: &QueryId,
176        request: &api::v1::region::RemoteDynFilterUpdate,
177    ) -> Result<RegionResponse> {
178        if request.filter_id.is_empty() {
179            return error::MissingRequiredFieldSnafu { name: "filter_id" }.fail();
180        }
181
182        if request.payload.is_empty() {
183            return error::MissingRequiredFieldSnafu { name: "payload" }.fail();
184        }
185
186        let filter_id = RemoteDynFilterId::new(request.filter_id.clone());
187        let outcome = apply_remote_dyn_filter_update(
188            &self.inner.initial_remote_dyn_filter_registrations,
189            query_id,
190            &filter_id,
191            &request.payload,
192            request.generation,
193            request.is_complete,
194        );
195        self.log_remote_dyn_filter_update_outcome(query_id, &filter_id, outcome);
196
197        Ok(RegionResponse::new(0))
198    }
199
200    async fn handle_remote_dyn_filter_unregister(
201        &self,
202        query_id: &QueryId,
203        request: &api::v1::region::RemoteDynFilterUnregister,
204    ) -> Result<RegionResponse> {
205        if request.filter_id.is_empty() {
206            return error::MissingRequiredFieldSnafu { name: "filter_id" }.fail();
207        }
208
209        let filter_id = RemoteDynFilterId::new(request.filter_id.clone());
210        let outcome = unregister_remote_dyn_filter(
211            &self.inner.initial_remote_dyn_filter_registrations,
212            query_id,
213            &filter_id,
214        );
215        self.log_remote_dyn_filter_update_outcome(query_id, &filter_id, outcome);
216
217        Ok(RegionResponse::new(0))
218    }
219
220    fn log_remote_dyn_filter_update_outcome(
221        &self,
222        query_id: &QueryId,
223        filter_id: &RemoteDynFilterId,
224        outcome: RemoteDynFilterUpdateOutcome,
225    ) {
226        if matches!(
227            outcome,
228            RemoteDynFilterUpdateOutcome::MissingRegistration
229                | RemoteDynFilterUpdateOutcome::AlreadyComplete
230                | RemoteDynFilterUpdateOutcome::PayloadTooLarge
231                | RemoteDynFilterUpdateOutcome::DecodeFailed
232        ) {
233            warn!(
234                "Remote dynamic filter update outcome, query_id: {}, filter_id: {}, outcome: {:?}",
235                query_id, filter_id, outcome
236            );
237        } else {
238            debug!(
239                "Remote dynamic filter update outcome, query_id: {}, filter_id: {}, outcome: {:?}",
240                query_id, filter_id, outcome
241            );
242        }
243    }
244}
245
246pub(super) fn wrap_remote_dyn_filter_guarded_stream(
247    stream: SendableRecordBatchStream,
248    cleanup: RemoteDynFilterRegistrationGuard,
249) -> SendableRecordBatchStream {
250    Box::pin(RemoteDynFilterGuardedStream { stream, cleanup })
251}
252
253/// Removes query-scoped remote dynamic filter subscriptions unless ownership is moved elsewhere.
254pub(super) struct RemoteDynFilterRegistrationGuard {
255    server: RegionServer,
256    query_id: QueryId,
257    region_id: RegionId,
258    filter_ids: Vec<RemoteDynFilterId>,
259    cleaned: bool,
260}
261
262impl RemoteDynFilterRegistrationGuard {
263    fn new(
264        server: RegionServer,
265        query_id: QueryId,
266        region_id: RegionId,
267        filter_ids: Vec<RemoteDynFilterId>,
268    ) -> Self {
269        Self {
270            server,
271            query_id,
272            region_id,
273            filter_ids,
274            cleaned: false,
275        }
276    }
277
278    fn cleanup_once(&mut self) {
279        if self.cleaned {
280            return;
281        }
282
283        remove_initial_dyn_filter_regs(
284            &self.server.inner.initial_remote_dyn_filter_registrations,
285            &self.query_id,
286            self.region_id,
287            &self.filter_ids,
288        );
289        self.cleaned = true;
290    }
291}
292
293impl Drop for RemoteDynFilterRegistrationGuard {
294    fn drop(&mut self) {
295        self.cleanup_once();
296    }
297}
298
299/// Removes query-scoped remote dynamic filter subscriptions when a remote read stream is done.
300struct RemoteDynFilterGuardedStream {
301    stream: SendableRecordBatchStream,
302    cleanup: RemoteDynFilterRegistrationGuard,
303}
304
305impl RecordBatchStream for RemoteDynFilterGuardedStream {
306    fn name(&self) -> &str {
307        self.stream.name()
308    }
309
310    fn schema(&self) -> datatypes::schema::SchemaRef {
311        self.stream.schema()
312    }
313
314    fn output_ordering(&self) -> Option<&[OrderOption]> {
315        self.stream.output_ordering()
316    }
317
318    fn metrics(&self) -> Option<RecordBatchMetrics> {
319        self.stream.metrics()
320    }
321}
322
323impl Stream for RemoteDynFilterGuardedStream {
324    type Item = common_recordbatch::error::Result<RecordBatch>;
325
326    fn size_hint(&self) -> (usize, Option<usize>) {
327        self.stream.size_hint()
328    }
329
330    fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
331        match Pin::new(&mut self.stream).poll_next(cx) {
332            Poll::Ready(None) => {
333                self.cleanup.cleanup_once();
334                Poll::Ready(None)
335            }
336            other => other,
337        }
338    }
339}
340
341#[cfg(test)]
342mod tests {
343    use std::assert_matches;
344    use std::collections::{HashMap, HashSet};
345    use std::sync::Arc;
346    use std::time::Duration;
347
348    use api::v1::region::{
349        RemoteDynFilterRequest, RemoteDynFilterUnregister, RemoteDynFilterUpdate,
350        remote_dyn_filter_request,
351    };
352    use common_query::request::{
353        DynFilterPayload, INITIAL_REMOTE_DYN_FILTER_REGISTRATIONS_EXTENSION_KEY,
354        InitialDynFilterReg, InitialDynFilterRegs, InitialDynFilterSnapshot,
355        REMOTE_DYN_FILTER_PAYLOAD_MAX_BYTES,
356    };
357    use common_recordbatch::RecordBatches;
358    use datafusion::arrow::datatypes::Schema as ArrowSchema;
359    use datafusion::physical_plan::PhysicalExpr;
360    use datafusion::physical_plan::expressions::{DynamicFilterPhysicalExpr, lit as physical_lit};
361    use datafusion_common::DFSchema;
362    use datafusion_expr::EmptyRelation;
363    use datatypes::prelude::{ConcreteDataType, VectorRef};
364    use datatypes::schema::{ColumnSchema, Schema};
365    use datatypes::vectors::Int32Vector;
366    use futures_util::StreamExt;
367    use query::options::QUERY_ENABLE_REMOTE_DYNAMIC_FILTER_PUSHDOWN;
368    use session::context::QueryContext;
369    use session::hints::REMOTE_QUERY_ID_EXTENSION_KEY;
370    use session::query_id::QueryId;
371    use store_api::storage::RegionId;
372
373    use super::*;
374    use crate::region_server::registrations::RemoteDynFilterRegistry;
375    use crate::tests::mock_region_server;
376
377    #[derive(Debug, Clone)]
378    struct RegisteredDynFilterSnapshot {
379        filter_id: RemoteDynFilterId,
380        child_exprs_datafusion_proto: Vec<Vec<u8>>,
381        subscriber_regions: HashSet<RegionId>,
382    }
383
384    fn query_regs(
385        regs_by_query: &RemoteDynFilterRegistry,
386        query_id: &QueryId,
387    ) -> Option<HashMap<RemoteDynFilterId, RegisteredDynFilterSnapshot>> {
388        regs_by_query.inspect_query(query_id, |query_regs| {
389            query_regs
390                .iter()
391                .map(|(filter_id, registered)| {
392                    (
393                        filter_id.clone(),
394                        RegisteredDynFilterSnapshot {
395                            filter_id: registered.filter_id.clone(),
396                            child_exprs_datafusion_proto: registered
397                                .child_exprs_datafusion_proto
398                                .clone(),
399                            subscriber_regions: registered.subscriber_regions.clone(),
400                        },
401                    )
402                })
403                .collect()
404        })
405    }
406
407    fn test_remote_query_id() -> QueryId {
408        QueryId::new()
409    }
410
411    fn test_remote_dyn_filter_region_id() -> RegionId {
412        RegionId::new(1024, 7)
413    }
414
415    fn single_value_stream() -> common_recordbatch::SendableRecordBatchStream {
416        let schema = Arc::new(Schema::new(vec![ColumnSchema::new(
417            "v",
418            ConcreteDataType::int32_datatype(),
419            false,
420        )]));
421        let values: VectorRef = Arc::new(Int32Vector::from_slice([1]));
422        let batch = common_recordbatch::RecordBatch::new(schema.clone(), vec![values]).unwrap();
423        RecordBatches::try_new(schema, vec![batch])
424            .unwrap()
425            .as_stream()
426    }
427
428    fn empty_logical_plan() -> LogicalPlan {
429        LogicalPlan::EmptyRelation(EmptyRelation {
430            produce_one_row: false,
431            schema: Arc::new(DFSchema::empty()),
432        })
433    }
434
435    fn query_context_with_initial_regs(query_id: QueryId) -> QueryContext {
436        let mut query_ctx = QueryContext::with("greptime", "public");
437        query_ctx.set_extension(REMOTE_QUERY_ID_EXTENSION_KEY, query_id.to_string());
438        query_ctx.set_extension(
439            INITIAL_REMOTE_DYN_FILTER_REGISTRATIONS_EXTENSION_KEY,
440            InitialDynFilterRegs::new(vec![InitialDynFilterReg::new("filter-1", vec![])])
441                .to_extension_value()
442                .unwrap(),
443        );
444        query_ctx
445    }
446
447    #[test]
448    fn remote_dyn_filter_receiver_plan_respects_disabled_query_option() {
449        let mock_region_server = mock_region_server();
450        let query_id = test_remote_query_id();
451        let mut query_ctx = query_context_with_initial_regs(query_id);
452        query_ctx.set_extension(QUERY_ENABLE_REMOTE_DYNAMIC_FILTER_PUSHDOWN, "false");
453
454        let plan = remote_dyn_filter_receiver_plan(
455            &Arc::downgrade(&mock_region_server.inner),
456            empty_logical_plan(),
457            Arc::new(query_ctx),
458        );
459
460        assert!(matches!(plan, LogicalPlan::EmptyRelation(_)));
461    }
462
463    #[test]
464    fn remote_dyn_filter_cleanup_registration_respects_disabled_query_option() {
465        let mock_region_server = mock_region_server();
466        let query_id = test_remote_query_id();
467        let mut query_ctx = query_context_with_initial_regs(query_id);
468        query_ctx.set_extension(QUERY_ENABLE_REMOTE_DYNAMIC_FILTER_PUSHDOWN, "false");
469        let query_ctx = Arc::new(query_ctx);
470
471        let cleanup = mock_region_server.register_initial_remote_dyn_filter_cleanup(
472            &query_ctx,
473            test_remote_dyn_filter_region_id(),
474        );
475
476        assert!(cleanup.is_none());
477        assert!(
478            mock_region_server
479                .inner
480                .initial_remote_dyn_filter_registrations
481                .inspect_query(&query_id, |_| ())
482                .is_none()
483        );
484    }
485
486    #[test]
487    fn initial_dyn_filter_regs_can_be_read_from_query_context() {
488        let mut query_ctx = QueryContext::with("greptime", "public");
489        query_ctx.set_extension(
490            INITIAL_REMOTE_DYN_FILTER_REGISTRATIONS_EXTENSION_KEY,
491            InitialDynFilterRegs::new(vec![InitialDynFilterReg::new(
492                "filter-1",
493                vec![vec![1, 2, 3]],
494            )])
495            .to_extension_value()
496            .unwrap(),
497        );
498
499        let regs = initial_dyn_filter_regs_from_query_ctx(&Arc::new(query_ctx)).unwrap();
500
501        assert_eq!(regs.regs.len(), 1);
502        assert_eq!(regs.regs[0].filter_id, "filter-1");
503    }
504
505    #[test]
506    fn initial_dyn_filter_regs_from_query_context_rejects_duplicate_filter_ids() {
507        let mut query_ctx = QueryContext::with("greptime", "public");
508        query_ctx.set_extension(
509            INITIAL_REMOTE_DYN_FILTER_REGISTRATIONS_EXTENSION_KEY,
510            InitialDynFilterRegs::new(vec![
511                InitialDynFilterReg::new("filter-1", vec![vec![1, 2, 3]]),
512                InitialDynFilterReg::new("filter-1", vec![vec![4, 5, 6]]),
513            ])
514            .to_extension_value()
515            .unwrap(),
516        );
517
518        let regs = initial_dyn_filter_regs_from_query_ctx(&Arc::new(query_ctx));
519
520        assert!(regs.is_none());
521    }
522
523    #[test]
524    fn register_initial_dyn_filter_regs_creates_query_scoped_entries() {
525        let regs_by_query = RemoteDynFilterRegistry::new();
526        let regs = InitialDynFilterRegs::new(vec![
527            InitialDynFilterReg::new("filter-1", vec![vec![1, 2, 3]]),
528            InitialDynFilterReg::new("filter-2", vec![vec![4, 5, 6]]),
529        ]);
530        let query_id = test_remote_query_id();
531        let region_id = test_remote_dyn_filter_region_id();
532
533        let registered_filter_ids =
534            register_initial_dyn_filter_regs(&regs_by_query, &query_id, region_id, &regs);
535
536        let query_regs = query_regs(&regs_by_query, &query_id).unwrap();
537        assert_eq!(query_regs.len(), 2);
538        assert_eq!(
539            registered_filter_ids,
540            vec![
541                RemoteDynFilterId::new("filter-1"),
542                RemoteDynFilterId::new("filter-2")
543            ]
544        );
545        let registered = query_regs.get(&RemoteDynFilterId::new("filter-1")).unwrap();
546        assert_eq!(registered.filter_id, RemoteDynFilterId::new("filter-1"));
547        assert_eq!(registered.child_exprs_datafusion_proto, vec![vec![1, 2, 3]]);
548        assert_eq!(registered.subscriber_regions.len(), 1);
549        assert!(registered.subscriber_regions.contains(&region_id));
550    }
551
552    #[test]
553    fn register_initial_dyn_filter_regs_same_region_duplicate_is_idempotent() {
554        let regs_by_query = RemoteDynFilterRegistry::new();
555        let regs = InitialDynFilterRegs::new(vec![InitialDynFilterReg::new(
556            "filter-1",
557            vec![vec![1, 2, 3]],
558        )]);
559        let query_id = test_remote_query_id();
560        let region_id = test_remote_dyn_filter_region_id();
561
562        let first = register_initial_dyn_filter_regs(&regs_by_query, &query_id, region_id, &regs);
563        let duplicate =
564            register_initial_dyn_filter_regs(&regs_by_query, &query_id, region_id, &regs);
565
566        let query_regs = query_regs(&regs_by_query, &query_id).unwrap();
567        assert_eq!(query_regs.len(), 1);
568        assert_eq!(first, vec![RemoteDynFilterId::new("filter-1")]);
569        assert!(duplicate.is_empty());
570        let registered = query_regs.get(&RemoteDynFilterId::new("filter-1")).unwrap();
571        assert_eq!(registered.subscriber_regions.len(), 1);
572        assert!(registered.subscriber_regions.contains(&region_id));
573    }
574
575    #[test]
576    fn register_initial_dyn_filter_regs_ignores_invalid_duplicate_payload_set() {
577        let regs_by_query = RemoteDynFilterRegistry::new();
578        let regs = InitialDynFilterRegs::new(vec![
579            InitialDynFilterReg::new("filter-1", vec![vec![1, 2, 3]]),
580            InitialDynFilterReg::new("filter-1", vec![vec![4, 5, 6]]),
581        ]);
582        let query_id = test_remote_query_id();
583        let region_id = test_remote_dyn_filter_region_id();
584
585        register_initial_dyn_filter_regs(&regs_by_query, &query_id, region_id, &regs);
586
587        assert!(query_regs(&regs_by_query, &query_id).is_none());
588    }
589
590    #[test]
591    fn register_initial_dyn_filter_regs_tracks_different_region_subscribers_for_same_filter() {
592        let regs_by_query = RemoteDynFilterRegistry::new();
593        let regs = InitialDynFilterRegs::new(vec![InitialDynFilterReg::new(
594            "filter-1",
595            vec![vec![1, 2, 3]],
596        )]);
597        let query_id = test_remote_query_id();
598        let first_region_id = RegionId::new(1024, 7);
599        let second_region_id = RegionId::new(1024, 8);
600
601        register_initial_dyn_filter_regs(&regs_by_query, &query_id, first_region_id, &regs);
602        register_initial_dyn_filter_regs(&regs_by_query, &query_id, second_region_id, &regs);
603
604        let query_regs = query_regs(&regs_by_query, &query_id).unwrap();
605        assert_eq!(query_regs.len(), 1);
606        let registered = query_regs.get(&RemoteDynFilterId::new("filter-1")).unwrap();
607        assert_eq!(registered.subscriber_regions.len(), 2);
608        assert!(registered.subscriber_regions.contains(&first_region_id));
609        assert!(registered.subscriber_regions.contains(&second_region_id));
610    }
611
612    #[test]
613    fn remove_initial_dyn_filter_regs_removes_registered_filter_entries() {
614        let regs_by_query = RemoteDynFilterRegistry::new();
615        let query_id = test_remote_query_id();
616        let other_query_id = test_remote_query_id();
617        let region_id = test_remote_dyn_filter_region_id();
618
619        let registered_filter_ids = register_initial_dyn_filter_regs(
620            &regs_by_query,
621            &query_id,
622            region_id,
623            &InitialDynFilterRegs::new(vec![InitialDynFilterReg::new(
624                "filter-1",
625                vec![vec![1, 2, 3]],
626            )]),
627        );
628        register_initial_dyn_filter_regs(
629            &regs_by_query,
630            &other_query_id,
631            region_id,
632            &InitialDynFilterRegs::new(vec![InitialDynFilterReg::new(
633                "filter-2",
634                vec![vec![4, 5, 6]],
635            )]),
636        );
637
638        remove_initial_dyn_filter_regs(
639            &regs_by_query,
640            &query_id,
641            region_id,
642            &registered_filter_ids,
643        );
644
645        assert!(query_regs(&regs_by_query, &query_id).is_none());
646        let other_query_regs = query_regs(&regs_by_query, &other_query_id).unwrap();
647        assert_eq!(other_query_regs.len(), 1);
648    }
649
650    #[test]
651    fn remove_initial_dyn_filter_regs_keeps_other_subscribers() {
652        let regs_by_query = RemoteDynFilterRegistry::new();
653        let query_id = test_remote_query_id();
654        let regs = InitialDynFilterRegs::new(vec![InitialDynFilterReg::new(
655            "filter-1",
656            vec![vec![1, 2, 3]],
657        )]);
658        let first_region_id = RegionId::new(1024, 7);
659        let second_region_id = RegionId::new(1024, 8);
660
661        let first_subscription =
662            register_initial_dyn_filter_regs(&regs_by_query, &query_id, first_region_id, &regs);
663        register_initial_dyn_filter_regs(&regs_by_query, &query_id, second_region_id, &regs);
664
665        remove_initial_dyn_filter_regs(
666            &regs_by_query,
667            &query_id,
668            first_region_id,
669            &first_subscription,
670        );
671
672        let query_regs = query_regs(&regs_by_query, &query_id).unwrap();
673        assert_eq!(query_regs.len(), 1);
674        let registered = query_regs.get(&RemoteDynFilterId::new("filter-1")).unwrap();
675        assert_eq!(registered.subscriber_regions.len(), 1);
676        assert!(registered.subscriber_regions.contains(&second_region_id));
677    }
678
679    #[tokio::test]
680    async fn test_handle_remote_dyn_filter_request_requires_query_id() {
681        let mock_region_server = mock_region_server();
682
683        let err = mock_region_server
684            .handle_remote_dyn_filter_request(&RemoteDynFilterRequest {
685                query_id: String::new(),
686                action: Some(remote_dyn_filter_request::Action::Unregister(
687                    RemoteDynFilterUnregister {
688                        filter_id: "filter-1".to_string(),
689                    },
690                )),
691            })
692            .await
693            .unwrap_err();
694
695        assert_matches!(
696            err,
697            crate::error::Error::MissingRequiredField { ref name, .. } if name == "query_id"
698        );
699    }
700
701    #[tokio::test]
702    async fn test_handle_remote_dyn_filter_request_requires_action() {
703        let mock_region_server = mock_region_server();
704
705        let err = mock_region_server
706            .handle_remote_dyn_filter_request(&RemoteDynFilterRequest {
707                query_id: test_remote_query_id().to_string(),
708                action: None,
709            })
710            .await
711            .unwrap_err();
712
713        assert_matches!(
714            err,
715            crate::error::Error::MissingRequiredField { ref name, .. } if name == "action"
716        );
717    }
718
719    #[tokio::test]
720    async fn test_handle_remote_dyn_filter_update_requires_filter_id() {
721        let mock_region_server = mock_region_server();
722
723        let err = mock_region_server
724            .handle_remote_dyn_filter_request(&RemoteDynFilterRequest {
725                query_id: test_remote_query_id().to_string(),
726                action: Some(remote_dyn_filter_request::Action::Update(
727                    RemoteDynFilterUpdate {
728                        filter_id: String::new(),
729                        payload: vec![1],
730                        generation: 1,
731                        is_complete: false,
732                    },
733                )),
734            })
735            .await
736            .unwrap_err();
737
738        assert_matches!(
739            err,
740            crate::error::Error::MissingRequiredField { ref name, .. } if name == "filter_id"
741        );
742    }
743
744    #[tokio::test]
745    async fn test_handle_remote_dyn_filter_update_requires_payload() {
746        let mock_region_server = mock_region_server();
747
748        let err = mock_region_server
749            .handle_remote_dyn_filter_request(&RemoteDynFilterRequest {
750                query_id: test_remote_query_id().to_string(),
751                action: Some(remote_dyn_filter_request::Action::Update(
752                    RemoteDynFilterUpdate {
753                        filter_id: "filter-1".to_string(),
754                        payload: Vec::new(),
755                        generation: 1,
756                        is_complete: false,
757                    },
758                )),
759            })
760            .await
761            .unwrap_err();
762
763        assert_matches!(
764            err,
765            crate::error::Error::MissingRequiredField { ref name, .. } if name == "payload"
766        );
767    }
768
769    #[test]
770    fn test_apply_remote_dyn_filter_update_missing_registration() {
771        let regs_by_query = RemoteDynFilterRegistry::new();
772        let query_id = test_remote_query_id();
773        let filter_id = RemoteDynFilterId::new("filter-1");
774
775        let outcome =
776            apply_remote_dyn_filter_update(&regs_by_query, &query_id, &filter_id, &[1], 1, false);
777        assert_eq!(outcome, RemoteDynFilterUpdateOutcome::MissingRegistration);
778
779        let outcome = unregister_remote_dyn_filter(&regs_by_query, &query_id, &filter_id);
780        assert_eq!(outcome, RemoteDynFilterUpdateOutcome::MissingRegistration);
781    }
782
783    #[test]
784    fn test_apply_remote_dyn_filter_update_buffering_before_scan() {
785        let regs_by_query = RemoteDynFilterRegistry::new();
786        let regs = InitialDynFilterRegs::new(vec![InitialDynFilterReg::new(
787            "filter-1",
788            vec![vec![1, 2, 3]],
789        )]);
790        let query_id = test_remote_query_id();
791        let filter_id = RemoteDynFilterId::new("filter-1");
792
793        register_initial_dyn_filter_regs(
794            &regs_by_query,
795            &query_id,
796            test_remote_dyn_filter_region_id(),
797            &regs,
798        );
799
800        // First update with generation 1: should be Buffered (no runtime installed yet)
801        let outcome =
802            apply_remote_dyn_filter_update(&regs_by_query, &query_id, &filter_id, &[1], 1, false);
803        assert_eq!(outcome, RemoteDynFilterUpdateOutcome::Buffered);
804
805        // Update with generation 0: should be Stale (older than pending generation 1)
806        let outcome =
807            apply_remote_dyn_filter_update(&regs_by_query, &query_id, &filter_id, &[2], 0, false);
808        assert_eq!(outcome, RemoteDynFilterUpdateOutcome::Stale);
809
810        // Update with generation 1 again: should be Idempotent (same generation)
811        let outcome =
812            apply_remote_dyn_filter_update(&regs_by_query, &query_id, &filter_id, &[3], 1, false);
813        assert_eq!(outcome, RemoteDynFilterUpdateOutcome::Idempotent);
814    }
815
816    fn datafusion_payload_bytes(expr: Arc<dyn PhysicalExpr>) -> Vec<u8> {
817        match DynFilterPayload::from_datafusion_expr(&expr, REMOTE_DYN_FILTER_PAYLOAD_MAX_BYTES)
818            .unwrap()
819        {
820            DynFilterPayload::Datafusion(bytes) => bytes,
821            _ => unreachable!(
822                "DynFilterPayload::from_datafusion_expr only returns datafusion payloads"
823            ),
824        }
825    }
826
827    fn empty_arrow_schema() -> ArrowSchema {
828        ArrowSchema::empty()
829    }
830
831    fn register_empty_remote_dyn_filter(
832        regs_by_query: &RemoteDynFilterRegistry,
833        query_id: &QueryId,
834    ) -> Vec<RemoteDynFilterId> {
835        register_empty_remote_dyn_filter_for_region(
836            regs_by_query,
837            query_id,
838            test_remote_dyn_filter_region_id(),
839        )
840    }
841
842    fn register_empty_remote_dyn_filter_for_region(
843        regs_by_query: &RemoteDynFilterRegistry,
844        query_id: &QueryId,
845        region_id: RegionId,
846    ) -> Vec<RemoteDynFilterId> {
847        register_initial_dyn_filter_regs(
848            regs_by_query,
849            query_id,
850            region_id,
851            &InitialDynFilterRegs::new(vec![InitialDynFilterReg::new("filter-1", vec![])]),
852        )
853    }
854
855    #[test]
856    fn initial_remote_dyn_filter_snapshot_initializes_runtime_filter() {
857        let regs_by_query = RemoteDynFilterRegistry::new();
858        let query_id = test_remote_query_id();
859        let payload = DynFilterPayload::Datafusion(datafusion_payload_bytes(physical_lit(false)));
860        let regs = InitialDynFilterRegs::new(vec![
861            InitialDynFilterReg::new("filter-1", vec![])
862                .with_initial_snapshot(InitialDynFilterSnapshot::new(payload, 7, false)),
863        ]);
864
865        register_initial_dyn_filter_regs(
866            &regs_by_query,
867            &query_id,
868            test_remote_dyn_filter_region_id(),
869            &regs,
870        );
871        let exprs = remote_dyn_filter_exprs_for_initial_regs(
872            &regs_by_query,
873            &query_id,
874            &regs,
875            &empty_arrow_schema(),
876        );
877
878        assert_eq!(exprs.len(), 1);
879        assert_eq!(format!("{}", exprs[0]), "DynamicFilter [ false ]");
880    }
881
882    fn only_remote_dyn_filter(
883        regs_by_query: &RemoteDynFilterRegistry,
884        query_id: &QueryId,
885    ) -> Arc<DynamicFilterPhysicalExpr> {
886        let initial_regs =
887            InitialDynFilterRegs::new(vec![InitialDynFilterReg::new("filter-1", vec![])]);
888        let exprs = remote_dyn_filter_exprs_for_initial_regs(
889            regs_by_query,
890            query_id,
891            &initial_regs,
892            &empty_arrow_schema(),
893        );
894        assert_eq!(1, exprs.len());
895        let expr = exprs.into_iter().next().unwrap();
896        let expr = expr as Arc<dyn std::any::Any + Send + Sync>;
897        expr.downcast::<DynamicFilterPhysicalExpr>().unwrap()
898    }
899
900    #[test]
901    fn test_remote_dyn_filter_rejects_oversized_payload_before_buffering() {
902        let regs_by_query = RemoteDynFilterRegistry::new();
903        let query_id = test_remote_query_id();
904        let filter_id = RemoteDynFilterId::new("filter-1");
905        register_empty_remote_dyn_filter(&regs_by_query, &query_id);
906
907        let oversized = vec![0; REMOTE_DYN_FILTER_PAYLOAD_MAX_BYTES + 1];
908        let outcome = apply_remote_dyn_filter_update(
909            &regs_by_query,
910            &query_id,
911            &filter_id,
912            &oversized,
913            1,
914            false,
915        );
916        assert_eq!(outcome, RemoteDynFilterUpdateOutcome::PayloadTooLarge);
917
918        // The rejected generation must not become the pending generation.
919        let outcome =
920            apply_remote_dyn_filter_update(&regs_by_query, &query_id, &filter_id, &[1], 0, false);
921        assert_eq!(outcome, RemoteDynFilterUpdateOutcome::Buffered);
922    }
923
924    #[test]
925    fn test_remote_dyn_filter_generation_zero_applies_as_first_update() {
926        let regs_by_query = RemoteDynFilterRegistry::new();
927        let query_id = test_remote_query_id();
928        let filter_id = RemoteDynFilterId::new("filter-1");
929        register_empty_remote_dyn_filter(&regs_by_query, &query_id);
930
931        // Installing the wrapper before the update exercises the runtime apply path directly.
932        let dyn_filter = only_remote_dyn_filter(&regs_by_query, &query_id);
933        let payload = datafusion_payload_bytes(physical_lit(false));
934        let outcome = apply_remote_dyn_filter_update(
935            &regs_by_query,
936            &query_id,
937            &filter_id,
938            &payload,
939            0,
940            false,
941        );
942
943        assert_eq!(outcome, RemoteDynFilterUpdateOutcome::Applied);
944        assert!(dyn_filter.snapshot_generation() > 1);
945        assert_eq!(format!("{}", dyn_filter.current().unwrap()), "false");
946    }
947
948    #[test]
949    fn test_buffered_update_applies_when_scan_installs_wrapper() {
950        let regs_by_query = RemoteDynFilterRegistry::new();
951        let query_id = test_remote_query_id();
952        let filter_id = RemoteDynFilterId::new("filter-1");
953        register_empty_remote_dyn_filter(&regs_by_query, &query_id);
954
955        let payload = datafusion_payload_bytes(physical_lit(false));
956        let outcome = apply_remote_dyn_filter_update(
957            &regs_by_query,
958            &query_id,
959            &filter_id,
960            &payload,
961            1,
962            false,
963        );
964        assert_eq!(outcome, RemoteDynFilterUpdateOutcome::Buffered);
965
966        let dyn_filter = only_remote_dyn_filter(&regs_by_query, &query_id);
967        assert!(dyn_filter.snapshot_generation() > 1);
968        assert_eq!(format!("{}", dyn_filter.current().unwrap()), "false");
969    }
970
971    #[tokio::test]
972    async fn test_unregister_completes_installed_remote_dyn_filter_without_relaxing() {
973        let regs_by_query = RemoteDynFilterRegistry::new();
974        let query_id = test_remote_query_id();
975        let filter_id = RemoteDynFilterId::new("filter-1");
976        register_empty_remote_dyn_filter(&regs_by_query, &query_id);
977
978        let dyn_filter = only_remote_dyn_filter(&regs_by_query, &query_id);
979        let payload = datafusion_payload_bytes(physical_lit(false));
980        let outcome = apply_remote_dyn_filter_update(
981            &regs_by_query,
982            &query_id,
983            &filter_id,
984            &payload,
985            1,
986            false,
987        );
988        assert_eq!(outcome, RemoteDynFilterUpdateOutcome::Applied);
989        assert_eq!(format!("{}", dyn_filter.current().unwrap()), "false");
990
991        let outcome = unregister_remote_dyn_filter(&regs_by_query, &query_id, &filter_id);
992        assert_eq!(outcome, RemoteDynFilterUpdateOutcome::Applied);
993        assert!(query_regs(&regs_by_query, &query_id).is_none());
994        tokio::time::timeout(Duration::from_secs(1), dyn_filter.wait_complete())
995            .await
996            .unwrap();
997        assert_eq!(format!("{}", dyn_filter.current().unwrap()), "false");
998    }
999
1000    #[tokio::test]
1001    async fn test_remote_dyn_filter_unregister_removes_all_region_subscribers_for_filter() {
1002        let regs_by_query = RemoteDynFilterRegistry::new();
1003        let query_id = test_remote_query_id();
1004        let filter_id = RemoteDynFilterId::new("filter-1");
1005        let first_region_id = RegionId::new(1024, 7);
1006        let second_region_id = RegionId::new(1024, 8);
1007        let regs = InitialDynFilterRegs::new(vec![InitialDynFilterReg::new("filter-1", vec![])]);
1008
1009        let first_subscription =
1010            register_initial_dyn_filter_regs(&regs_by_query, &query_id, first_region_id, &regs);
1011        let second_subscription =
1012            register_initial_dyn_filter_regs(&regs_by_query, &query_id, second_region_id, &regs);
1013        let dyn_filter = only_remote_dyn_filter(&regs_by_query, &query_id);
1014
1015        let payload = datafusion_payload_bytes(physical_lit(false));
1016        let outcome = apply_remote_dyn_filter_update(
1017            &regs_by_query,
1018            &query_id,
1019            &filter_id,
1020            &payload,
1021            1,
1022            false,
1023        );
1024        assert_eq!(outcome, RemoteDynFilterUpdateOutcome::Applied);
1025
1026        let outcome = unregister_remote_dyn_filter(&regs_by_query, &query_id, &filter_id);
1027        assert_eq!(outcome, RemoteDynFilterUpdateOutcome::Applied);
1028        assert!(query_regs(&regs_by_query, &query_id).is_none());
1029        tokio::time::timeout(Duration::from_secs(1), dyn_filter.wait_complete())
1030            .await
1031            .unwrap();
1032        assert_eq!(format!("{}", dyn_filter.current().unwrap()), "false");
1033
1034        // Stream-local cleanup may run after FE's peer-deduplicated unregister. It should be benign.
1035        remove_initial_dyn_filter_regs(
1036            &regs_by_query,
1037            &query_id,
1038            first_region_id,
1039            &first_subscription,
1040        );
1041        remove_initial_dyn_filter_regs(
1042            &regs_by_query,
1043            &query_id,
1044            second_region_id,
1045            &second_subscription,
1046        );
1047        assert!(query_regs(&regs_by_query, &query_id).is_none());
1048    }
1049
1050    #[test]
1051    fn test_remote_dyn_filter_unregister_keeps_other_filters_for_same_query() {
1052        let regs_by_query = RemoteDynFilterRegistry::new();
1053        let query_id = test_remote_query_id();
1054        let region_id = test_remote_dyn_filter_region_id();
1055
1056        register_initial_dyn_filter_regs(
1057            &regs_by_query,
1058            &query_id,
1059            region_id,
1060            &InitialDynFilterRegs::new(vec![InitialDynFilterReg::new("filter-1", vec![])]),
1061        );
1062        register_initial_dyn_filter_regs(
1063            &regs_by_query,
1064            &query_id,
1065            region_id,
1066            &InitialDynFilterRegs::new(vec![InitialDynFilterReg::new("filter-2", vec![])]),
1067        );
1068
1069        let outcome = unregister_remote_dyn_filter(
1070            &regs_by_query,
1071            &query_id,
1072            &RemoteDynFilterId::new("filter-1"),
1073        );
1074        assert_eq!(outcome, RemoteDynFilterUpdateOutcome::Applied);
1075
1076        let query_regs = query_regs(&regs_by_query, &query_id).unwrap();
1077        assert!(!query_regs.contains_key(&RemoteDynFilterId::new("filter-1")));
1078        assert!(query_regs.contains_key(&RemoteDynFilterId::new("filter-2")));
1079    }
1080
1081    #[tokio::test]
1082    async fn test_remote_dyn_filter_guarded_stream_removes_on_eof() {
1083        let mock_region_server = mock_region_server();
1084        let query_id = test_remote_query_id();
1085        let region_id = test_remote_dyn_filter_region_id();
1086        let registered_filter_ids = register_empty_remote_dyn_filter(
1087            &mock_region_server
1088                .inner
1089                .initial_remote_dyn_filter_registrations,
1090            &query_id,
1091        );
1092        let cleanup = RemoteDynFilterRegistrationGuard::new(
1093            mock_region_server.clone(),
1094            query_id,
1095            region_id,
1096            registered_filter_ids,
1097        );
1098
1099        let stream = wrap_remote_dyn_filter_guarded_stream(single_value_stream(), cleanup);
1100        let mut pinned = Box::pin(stream);
1101        while pinned.next().await.is_some() {}
1102
1103        assert!(
1104            mock_region_server
1105                .inner
1106                .initial_remote_dyn_filter_registrations
1107                .inspect_query(&query_id, |_| ())
1108                .is_none()
1109        );
1110    }
1111
1112    #[tokio::test]
1113    async fn test_remote_dyn_filter_multi_region_subscription_cleanup() {
1114        let regs_by_query = RemoteDynFilterRegistry::new();
1115        let regs = InitialDynFilterRegs::new(vec![InitialDynFilterReg::new("filter-1", vec![])]);
1116        let query_id = test_remote_query_id();
1117        let first_region_id = RegionId::new(1024, 7);
1118        let second_region_id = RegionId::new(1024, 8);
1119
1120        // Register the same logical filter for two regions on the same datanode.
1121        let first_subscription =
1122            register_initial_dyn_filter_regs(&regs_by_query, &query_id, first_region_id, &regs);
1123        let second_subscription =
1124            register_initial_dyn_filter_regs(&regs_by_query, &query_id, second_region_id, &regs);
1125        let dyn_filter = only_remote_dyn_filter(&regs_by_query, &query_id);
1126
1127        // Verify only one filter entry exists and both region subscribers are tracked explicitly.
1128        {
1129            let query_regs = query_regs(&regs_by_query, &query_id).unwrap();
1130            assert_eq!(query_regs.len(), 1);
1131            let registered = query_regs.get(&RemoteDynFilterId::new("filter-1")).unwrap();
1132            assert_eq!(registered.subscriber_regions.len(), 2);
1133            assert!(registered.subscriber_regions.contains(&first_region_id));
1134            assert!(registered.subscriber_regions.contains(&second_region_id));
1135        }
1136
1137        // One region cleanup should not drop the entry while another region is subscribed.
1138        remove_initial_dyn_filter_regs(
1139            &regs_by_query,
1140            &query_id,
1141            first_region_id,
1142            &first_subscription,
1143        );
1144        assert!(query_regs(&regs_by_query, &query_id).is_some());
1145        assert!(
1146            tokio::time::timeout(Duration::from_millis(50), dyn_filter.wait_complete())
1147                .await
1148                .is_err()
1149        );
1150        {
1151            let query_regs = query_regs(&regs_by_query, &query_id).unwrap();
1152            let registered = query_regs.get(&RemoteDynFilterId::new("filter-1")).unwrap();
1153            assert_eq!(registered.subscriber_regions.len(), 1);
1154            assert!(registered.subscriber_regions.contains(&second_region_id));
1155        }
1156
1157        // Last region cleanup should drop the entry.
1158        remove_initial_dyn_filter_regs(
1159            &regs_by_query,
1160            &query_id,
1161            second_region_id,
1162            &second_subscription,
1163        );
1164        assert!(query_regs(&regs_by_query, &query_id).is_none());
1165        tokio::time::timeout(Duration::from_secs(1), dyn_filter.wait_complete())
1166            .await
1167            .unwrap();
1168    }
1169}