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