1use std::collections::hash_map::Entry;
16use std::collections::{HashMap, HashSet};
17use std::fmt::{Display, Formatter};
18use std::sync::{Arc, Mutex, OnceLock};
19
20use common_query::request::{
21 DynFilterPayload, INITIAL_REMOTE_DYN_FILTER_REGISTRATIONS_EXTENSION_KEY, InitialDynFilterReg,
22 InitialDynFilterRegs, REMOTE_DYN_FILTER_PAYLOAD_MAX_BYTES,
23};
24use common_telemetry::warn;
25use dashmap::DashMap;
26use datafusion::arrow::datatypes::{Schema, SchemaRef};
27use datafusion::execution::{SessionStateBuilder, TaskContext};
28use datafusion::physical_plan::PhysicalExpr;
29use datafusion::physical_plan::expressions::{DynamicFilterPhysicalExpr, lit};
30use datafusion_common::Result as DataFusionResult;
31use session::context::QueryContextRef;
32use session::query_id::QueryId;
33use store_api::storage::RegionId;
34
35use crate::metrics::{
36 REMOTE_DYN_FILTER_PAYLOAD_BYTES, REMOTE_DYN_FILTER_REGISTRY_ENTRIES,
37 REMOTE_DYN_FILTER_UNREGISTER_TOTAL, REMOTE_DYN_FILTER_UPDATE_DROP_TOTAL,
38 REMOTE_DYN_FILTER_UPDATE_OUTCOME_TOTAL,
39};
40
41type QueryRemoteDynFilterRegs = HashMap<RemoteDynFilterId, RegisteredDynFilter>;
42
43#[derive(Debug, Default)]
44pub(super) struct RemoteDynFilterRegistry {
45 queries: DashMap<QueryId, Arc<Mutex<QueryRemoteDynFilterRegs>>>,
48}
49
50impl RemoteDynFilterRegistry {
51 pub(super) fn new() -> Self {
52 Self::default()
53 }
54
55 fn get_or_insert_query(&self, query_id: QueryId) -> Arc<Mutex<QueryRemoteDynFilterRegs>> {
56 self.queries
57 .entry(query_id)
58 .or_insert_with(|| Arc::new(Mutex::new(HashMap::new())))
59 .clone()
60 }
61
62 fn get_query(&self, query_id: &QueryId) -> Option<Arc<Mutex<QueryRemoteDynFilterRegs>>> {
63 self.queries
64 .get(query_id)
65 .map(|query_regs| query_regs.clone())
66 }
67
68 fn remove_query_if_empty(
69 &self,
70 query_id: &QueryId,
71 expected: &Arc<Mutex<QueryRemoteDynFilterRegs>>,
72 ) {
73 self.queries.remove_if(query_id, |_, query_regs| {
77 Arc::ptr_eq(query_regs, expected) && query_regs.lock().unwrap().is_empty()
78 });
79 }
80
81 #[cfg(test)]
82 pub(super) fn inspect_query<R>(
83 &self,
84 query_id: &QueryId,
85 inspect: impl FnOnce(&QueryRemoteDynFilterRegs) -> R,
86 ) -> Option<R> {
87 self.get_query(query_id)
88 .map(|query_regs| inspect(&query_regs.lock().unwrap()))
89 }
90}
91
92#[derive(Clone, Debug, PartialEq, Eq, Hash)]
93pub(super) struct RemoteDynFilterId(String);
94
95impl RemoteDynFilterId {
96 pub(super) fn new(value: impl Into<String>) -> Self {
97 Self(value.into())
98 }
99}
100
101impl Display for RemoteDynFilterId {
102 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
103 self.0.fmt(f)
104 }
105}
106
107#[derive(Debug, Clone, Copy, PartialEq, Eq)]
108pub(super) enum RemoteDynFilterUpdateOutcome {
109 MissingRegistration,
110 Buffered,
111 Applied,
112 Idempotent,
113 Stale,
114 AlreadyComplete,
115 PayloadTooLarge,
116 DecodeFailed,
117}
118
119impl RemoteDynFilterUpdateOutcome {
120 pub fn as_str(&self) -> &'static str {
121 match self {
122 Self::MissingRegistration => "missing_registration",
123 Self::Buffered => "buffered",
124 Self::Applied => "applied",
125 Self::Idempotent => "idempotent",
126 Self::Stale => "stale",
127 Self::AlreadyComplete => "already_complete",
128 Self::PayloadTooLarge => "payload_too_large",
129 Self::DecodeFailed => "decode_failed",
130 }
131 }
132
133 fn is_drop(&self) -> bool {
134 matches!(
135 self,
136 Self::MissingRegistration
137 | Self::Stale
138 | Self::AlreadyComplete
139 | Self::PayloadTooLarge
140 | Self::DecodeFailed
141 )
142 }
143}
144
145fn record_update_outcome(outcome: RemoteDynFilterUpdateOutcome) {
146 REMOTE_DYN_FILTER_UPDATE_OUTCOME_TOTAL
147 .with_label_values(&[outcome.as_str()])
148 .inc();
149 if outcome.is_drop() {
150 REMOTE_DYN_FILTER_UPDATE_DROP_TOTAL
151 .with_label_values(&[outcome.as_str()])
152 .inc();
153 }
154}
155
156fn record_unregister_outcome(outcome: RemoteDynFilterUpdateOutcome) {
157 REMOTE_DYN_FILTER_UNREGISTER_TOTAL
158 .with_label_values(&[outcome.as_str()])
159 .inc();
160}
161
162#[derive(Debug, Clone)]
163struct PendingDynFilterUpdate {
164 payload: Vec<u8>,
165 generation: u64,
166 is_complete: bool,
167}
168
169impl PendingDynFilterUpdate {
170 fn from_initial_reg(reg: &InitialDynFilterReg) -> Option<Self> {
171 let snapshot = reg.initial_snapshot.as_ref()?;
172 match &snapshot.payload {
173 DynFilterPayload::Datafusion(payload) => Some(Self {
174 payload: payload.clone(),
175 generation: snapshot.generation,
176 is_complete: snapshot.is_complete,
177 }),
178 _ => None,
179 }
180 }
181}
182
183#[derive(Debug)]
184struct RemoteDynFilterEpochState {
185 generation: Option<u64>,
186 is_complete: bool,
187}
188
189#[derive(Debug)]
190struct RemoteDynFilterState {
191 filter: Arc<DynamicFilterPhysicalExpr>,
192 input_schema: SchemaRef,
193 epoch: Mutex<RemoteDynFilterEpochState>,
194}
195
196impl RemoteDynFilterState {
197 fn new(filter: Arc<DynamicFilterPhysicalExpr>, input_schema: SchemaRef) -> Self {
198 Self {
199 filter,
200 input_schema,
201 epoch: Mutex::new(RemoteDynFilterEpochState {
202 generation: None,
203 is_complete: false,
204 }),
205 }
206 }
207
208 fn filter(&self) -> Arc<DynamicFilterPhysicalExpr> {
209 self.filter.clone()
210 }
211
212 fn apply_update(
213 &self,
214 payload: &[u8],
215 generation: u64,
216 is_complete: bool,
217 ) -> RemoteDynFilterUpdateOutcome {
218 if !validate_update_payload_size(payload) {
219 return RemoteDynFilterUpdateOutcome::PayloadTooLarge;
220 }
221
222 let mut epoch = self.epoch.lock().unwrap();
223 if let Some(current_generation) = epoch.generation {
224 if generation < current_generation {
225 return RemoteDynFilterUpdateOutcome::Stale;
226 }
227
228 if generation == current_generation {
229 if is_complete && !epoch.is_complete {
230 self.filter.mark_complete();
231 epoch.is_complete = true;
232 return RemoteDynFilterUpdateOutcome::Applied;
233 }
234 return RemoteDynFilterUpdateOutcome::Idempotent;
235 }
236 }
237
238 if epoch.is_complete {
239 return RemoteDynFilterUpdateOutcome::AlreadyComplete;
240 }
241
242 let expr = match decode_update_payload(payload, self.input_schema.as_ref()) {
243 Ok(expr) => expr,
244 Err(error) => {
245 warn!(error; "Failed to decode remote dynamic filter update payload");
246 return RemoteDynFilterUpdateOutcome::DecodeFailed;
247 }
248 };
249
250 if let Err(error) = self.filter.update(expr) {
251 warn!(error; "Failed to apply remote dynamic filter update");
252 return RemoteDynFilterUpdateOutcome::DecodeFailed;
253 }
254
255 epoch.generation = Some(generation);
256 if is_complete {
257 self.filter.mark_complete();
258 epoch.is_complete = true;
259 }
260
261 RemoteDynFilterUpdateOutcome::Applied
262 }
263}
264
265#[derive(Debug)]
266pub(super) struct RegisteredDynFilter {
267 pub(super) filter_id: RemoteDynFilterId,
268 pub(super) child_exprs_datafusion_proto: Vec<Vec<u8>>,
269 pub(super) subscriber_regions: HashSet<RegionId>,
270 runtime: Option<Arc<RemoteDynFilterState>>,
271 pending_update: Option<PendingDynFilterUpdate>,
272}
273
274impl RegisteredDynFilter {
275 fn new(
276 filter_id: RemoteDynFilterId,
277 child_exprs_datafusion_proto: Vec<Vec<u8>>,
278 pending_update: Option<PendingDynFilterUpdate>,
279 region_id: RegionId,
280 ) -> Self {
281 let mut subscriber_regions = HashSet::new();
282 subscriber_regions.insert(region_id);
283
284 Self {
285 filter_id,
286 child_exprs_datafusion_proto,
287 subscriber_regions,
288 runtime: None,
289 pending_update,
290 }
291 }
292
293 fn apply_initial_snapshot(
294 &mut self,
295 reg: &InitialDynFilterReg,
296 ) -> RemoteDynFilterUpdateOutcome {
297 let Some(snapshot) = PendingDynFilterUpdate::from_initial_reg(reg) else {
298 return RemoteDynFilterUpdateOutcome::Idempotent;
299 };
300
301 self.apply_or_buffer_update(&snapshot.payload, snapshot.generation, snapshot.is_complete)
302 }
303
304 fn register_subscriber(&mut self, region_id: RegionId) -> bool {
305 if !self.subscriber_regions.insert(region_id) {
306 warn!(
307 "Duplicate remote dynamic filter subscriber region, filter_id: {}, region_id: {}",
308 self.filter_id, region_id
309 );
310 return false;
311 }
312
313 true
314 }
315
316 fn has_subscribers(&self) -> bool {
317 !self.subscriber_regions.is_empty()
318 }
319
320 fn should_drop_after_remove(&mut self, region_id: RegionId) -> bool {
321 self.subscriber_regions.remove(®ion_id);
322 !self.has_subscribers()
323 }
324
325 fn buffer_update(
326 &mut self,
327 payload: &[u8],
328 generation: u64,
329 is_complete: bool,
330 ) -> RemoteDynFilterUpdateOutcome {
331 if !validate_update_payload_size(payload) {
332 return RemoteDynFilterUpdateOutcome::PayloadTooLarge;
333 }
334
335 if let Some(pending) = self.pending_update.as_mut() {
336 if generation < pending.generation {
337 return RemoteDynFilterUpdateOutcome::Stale;
338 }
339
340 if generation == pending.generation {
341 pending.is_complete |= is_complete;
342 return RemoteDynFilterUpdateOutcome::Idempotent;
343 }
344
345 if pending.is_complete {
346 return RemoteDynFilterUpdateOutcome::AlreadyComplete;
347 }
348 }
349
350 self.pending_update = Some(PendingDynFilterUpdate {
351 payload: payload.to_vec(),
352 generation,
353 is_complete,
354 });
355 RemoteDynFilterUpdateOutcome::Buffered
356 }
357
358 fn apply_or_buffer_update(
359 &mut self,
360 payload: &[u8],
361 generation: u64,
362 is_complete: bool,
363 ) -> RemoteDynFilterUpdateOutcome {
364 if let Some(runtime) = &self.runtime {
365 return runtime.apply_update(payload, generation, is_complete);
366 }
367
368 self.buffer_update(payload, generation, is_complete)
369 }
370
371 fn decode_children(
372 &self,
373 input_schema: &Schema,
374 ) -> DataFusionResult<Vec<Arc<dyn PhysicalExpr>>> {
375 InitialDynFilterReg::new(
376 self.filter_id.to_string(),
377 self.child_exprs_datafusion_proto.clone(),
378 )
379 .decode_children(
380 remote_dyn_filter_task_context(),
381 input_schema,
382 REMOTE_DYN_FILTER_PAYLOAD_MAX_BYTES,
383 )
384 }
385
386 fn dyn_filter(&mut self, input_schema: &Schema) -> Option<Arc<dyn PhysicalExpr>> {
387 let children = match self.decode_children(input_schema) {
388 Ok(children) => children,
389 Err(error) => {
390 warn!(error; "Failed to decode remote dynamic filter initial children");
391 return None;
392 }
393 };
394
395 let runtime = match &self.runtime {
396 Some(runtime) => runtime.clone(),
397 None => {
398 let filter = Arc::new(DynamicFilterPhysicalExpr::new(children.clone(), lit(true)));
399 let runtime = Arc::new(RemoteDynFilterState::new(
400 filter,
401 Arc::new(input_schema.clone()),
402 ));
403 if let Some(pending) = self.pending_update.take() {
404 let outcome = runtime.apply_update(
405 &pending.payload,
406 pending.generation,
407 pending.is_complete,
408 );
409 if matches!(outcome, RemoteDynFilterUpdateOutcome::DecodeFailed) {
410 warn!(
411 "Dropped buffered remote dynamic filter update after decode failure, filter_id: {}, generation: {}",
412 self.filter_id, pending.generation
413 );
414 }
415 }
416 self.runtime = Some(runtime.clone());
417 runtime
418 }
419 };
420
421 match runtime.filter().with_new_children(children) {
422 Ok(expr) => Some(expr),
423 Err(error) => {
424 warn!(error; "Failed to remap remote dynamic filter children for scan");
425 None
426 }
427 }
428 }
429
430 fn deactivate(&self) {
431 if let Some(runtime) = &self.runtime {
432 runtime.filter.mark_complete();
433 }
434 }
435}
436
437impl Drop for RegisteredDynFilter {
438 fn drop(&mut self) {
439 self.deactivate();
440 }
441}
442
443pub(super) fn initial_dyn_filter_regs_from_query_ctx(
444 query_ctx: &QueryContextRef,
445) -> Option<InitialDynFilterRegs> {
446 let registrations =
447 query_ctx.extension(INITIAL_REMOTE_DYN_FILTER_REGISTRATIONS_EXTENSION_KEY)?;
448 match InitialDynFilterRegs::from_extension_value(registrations) {
449 Ok(registrations) => match registrations.validate_default_bounds() {
450 Ok(()) => Some(registrations),
451 Err(error) => {
452 warn!(error; "Initial remote dyn filter registrations exceeded Task 03 bounds");
453 None
454 }
455 },
456 Err(error) => {
457 warn!(error; "Failed to decode initial remote dyn filter registrations from query context");
458 None
459 }
460 }
461}
462
463pub(super) fn register_initial_dyn_filter_regs(
464 regs_by_query: &RemoteDynFilterRegistry,
465 query_id: &QueryId,
466 region_id: RegionId,
467 regs: &InitialDynFilterRegs,
468) -> Vec<RemoteDynFilterId> {
469 if regs.is_empty() {
470 return Vec::new();
471 }
472
473 if let Err(error) = regs.validate_default_bounds() {
474 warn!(error; "Ignored invalid initial dyn filter registrations for query_id {}", query_id);
475 return Vec::new();
476 }
477
478 let query_regs = regs_by_query.get_or_insert_query(*query_id);
479 let mut query_regs = query_regs.lock().unwrap();
480 let mut registered_filter_ids = Vec::with_capacity(regs.regs.len());
481
482 for reg in ®s.regs {
483 let filter_id = RemoteDynFilterId::new(reg.filter_id.clone());
484 match query_regs.entry(filter_id.clone()) {
485 Entry::Occupied(mut entry) => {
486 let registered = entry.get_mut();
487 if registered.child_exprs_datafusion_proto != reg.child_exprs_datafusion_proto {
488 warn!(
489 "Remote dynamic filter registration reused filter_id with different children, query_id: {}, filter_id: {}, region_id: {}",
490 query_id, filter_id, region_id
491 );
492 }
493 if registered.register_subscriber(region_id) {
494 registered_filter_ids.push(filter_id);
495 }
496 let _ = registered.apply_initial_snapshot(reg);
497 }
498 Entry::Vacant(entry) => {
499 entry.insert(RegisteredDynFilter::new(
500 filter_id.clone(),
501 reg.child_exprs_datafusion_proto.clone(),
502 PendingDynFilterUpdate::from_initial_reg(reg),
503 region_id,
504 ));
505 REMOTE_DYN_FILTER_REGISTRY_ENTRIES.inc();
506 registered_filter_ids.push(filter_id);
507 }
508 }
509 }
510
511 registered_filter_ids
512}
513
514pub(super) fn remote_dyn_filter_exprs_for_initial_regs(
515 regs_by_query: &RemoteDynFilterRegistry,
516 query_id: &QueryId,
517 initial_regs: &InitialDynFilterRegs,
518 input_schema: &Schema,
519) -> Vec<Arc<dyn PhysicalExpr>> {
520 let Some(query_regs) = regs_by_query.get_query(query_id) else {
521 return Vec::new();
522 };
523
524 let mut query_regs = query_regs.lock().unwrap();
525 initial_regs
526 .regs
527 .iter()
528 .filter_map(|reg| {
529 let filter_id = RemoteDynFilterId::new(reg.filter_id.clone());
530 let registered = query_regs.get_mut(&filter_id)?;
531 registered.dyn_filter(input_schema)
532 })
533 .collect()
534}
535
536pub(super) fn apply_remote_dyn_filter_update(
537 regs_by_query: &RemoteDynFilterRegistry,
538 query_id: &QueryId,
539 filter_id: &RemoteDynFilterId,
540 payload: &[u8],
541 generation: u64,
542 is_complete: bool,
543) -> RemoteDynFilterUpdateOutcome {
544 if !validate_update_payload_size(payload) {
545 warn!(
546 "Ignored oversized remote dynamic filter update, query_id: {}, filter_id: {}, payload_size: {}, max_payload_size: {}",
547 query_id,
548 filter_id,
549 payload.len(),
550 REMOTE_DYN_FILTER_PAYLOAD_MAX_BYTES
551 );
552 let outcome = RemoteDynFilterUpdateOutcome::PayloadTooLarge;
553 record_update_outcome(outcome);
554 return outcome;
555 }
556
557 REMOTE_DYN_FILTER_PAYLOAD_BYTES.observe(payload.len() as f64);
558
559 let Some(query_regs) = regs_by_query.get_query(query_id) else {
560 warn!(
561 "Ignored remote dynamic filter update without query registration, query_id: {}, filter_id: {}",
562 query_id, filter_id
563 );
564 let outcome = RemoteDynFilterUpdateOutcome::MissingRegistration;
565 record_update_outcome(outcome);
566 return outcome;
567 };
568
569 let mut query_regs = query_regs.lock().unwrap();
570 let Some(registered) = query_regs.get_mut(filter_id) else {
571 warn!(
572 "Ignored remote dynamic filter update without filter registration, query_id: {}, filter_id: {}",
573 query_id, filter_id
574 );
575 let outcome = RemoteDynFilterUpdateOutcome::MissingRegistration;
576 record_update_outcome(outcome);
577 return outcome;
578 };
579
580 let outcome = registered.apply_or_buffer_update(payload, generation, is_complete);
581 record_update_outcome(outcome);
582
583 outcome
584}
585
586pub(super) fn unregister_remote_dyn_filter(
587 regs_by_query: &RemoteDynFilterRegistry,
588 query_id: &QueryId,
589 filter_id: &RemoteDynFilterId,
590) -> RemoteDynFilterUpdateOutcome {
591 let Some(query_regs) = regs_by_query.get_query(query_id) else {
592 warn!(
593 "Ignored remote dynamic filter unregister without query registration, query_id: {}, filter_id: {}",
594 query_id, filter_id
595 );
596 let outcome = RemoteDynFilterUpdateOutcome::MissingRegistration;
597 record_unregister_outcome(outcome);
598 return outcome;
599 };
600
601 let (registered, should_remove_query) = {
602 let mut locked = query_regs.lock().unwrap();
603 let Some(registered) = locked.remove(filter_id) else {
604 warn!(
605 "Ignored remote dynamic filter unregister without filter registration, query_id: {}, filter_id: {}",
606 query_id, filter_id
607 );
608 let outcome = RemoteDynFilterUpdateOutcome::MissingRegistration;
609 record_unregister_outcome(outcome);
610 return outcome;
611 };
612 REMOTE_DYN_FILTER_REGISTRY_ENTRIES.dec();
613 let should_remove_query = locked.is_empty();
614 (registered, should_remove_query)
615 };
616
617 drop(registered);
618 if should_remove_query {
619 regs_by_query.remove_query_if_empty(query_id, &query_regs);
620 }
621
622 let outcome = RemoteDynFilterUpdateOutcome::Applied;
623 record_unregister_outcome(outcome);
624
625 outcome
626}
627
628pub(super) fn remove_initial_dyn_filter_regs(
629 regs_by_query: &RemoteDynFilterRegistry,
630 query_id: &QueryId,
631 region_id: RegionId,
632 filter_ids: &[RemoteDynFilterId],
633) {
634 if filter_ids.is_empty() {
635 return;
636 }
637
638 let Some(query_regs) = regs_by_query.get_query(query_id) else {
639 return;
640 };
641
642 let (removed_filters, should_remove_query) = {
643 let mut locked = query_regs.lock().unwrap();
644 let mut removed_filters = Vec::new();
645
646 for filter_id in filter_ids {
647 let should_remove_filter = locked
648 .get_mut(filter_id)
649 .map(|registered| registered.should_drop_after_remove(region_id))
650 .unwrap_or(false);
651
652 if should_remove_filter && let Some(registered) = locked.remove(filter_id) {
653 REMOTE_DYN_FILTER_REGISTRY_ENTRIES.dec();
654 removed_filters.push(registered);
655 }
656 }
657
658 let should_remove_query = locked.is_empty();
659 (removed_filters, should_remove_query)
660 };
661
662 drop(removed_filters);
663 if should_remove_query {
664 regs_by_query.remove_query_if_empty(query_id, &query_regs);
665 }
666}
667
668fn decode_update_payload(
669 payload: &[u8],
670 input_schema: &Schema,
671) -> DataFusionResult<Arc<dyn PhysicalExpr>> {
672 DynFilterPayload::Datafusion(payload.to_vec()).decode_datafusion_expr(
673 remote_dyn_filter_task_context(),
674 input_schema,
675 REMOTE_DYN_FILTER_PAYLOAD_MAX_BYTES,
676 )
677}
678
679fn remote_dyn_filter_task_context() -> &'static TaskContext {
680 static TASK_CONTEXT: OnceLock<TaskContext> = OnceLock::new();
681
682 TASK_CONTEXT.get_or_init(|| {
683 let session_state = SessionStateBuilder::new().with_default_features().build();
688 TaskContext::from(&session_state)
689 })
690}
691
692fn validate_update_payload_size(payload: &[u8]) -> bool {
693 payload.len() <= REMOTE_DYN_FILTER_PAYLOAD_MAX_BYTES
694}
695
696#[cfg(test)]
697mod tests {
698 use super::*;
699
700 #[test]
701 fn remote_dyn_filter_same_query_reuses_one_inner_lock() {
702 let regs_by_query = RemoteDynFilterRegistry::new();
703 let query_id = QueryId::new();
704
705 let first = regs_by_query.get_or_insert_query(query_id);
706 let second = regs_by_query.get_or_insert_query(query_id);
707
708 assert!(Arc::ptr_eq(&first, &second));
709 }
710
711 #[test]
712 fn remote_dyn_filter_stale_query_remove_does_not_remove_new_query_state() {
713 let regs_by_query = RemoteDynFilterRegistry::new();
714 let query_id = QueryId::new();
715 let stale_query_regs = regs_by_query.get_or_insert_query(query_id);
716
717 regs_by_query.remove_query_if_empty(&query_id, &stale_query_regs);
718 assert!(regs_by_query.get_query(&query_id).is_none());
719
720 let new_query_regs = regs_by_query.get_or_insert_query(query_id);
721 let filter_id = RemoteDynFilterId::new("filter-1");
722 let region_id = RegionId::new(1024, 7);
723 new_query_regs.lock().unwrap().insert(
724 filter_id.clone(),
725 RegisteredDynFilter::new(filter_id.clone(), vec![], None, region_id),
726 );
727
728 regs_by_query.remove_query_if_empty(&query_id, &stale_query_regs);
729
730 let current_query_regs = regs_by_query.get_query(&query_id).unwrap();
731 assert!(Arc::ptr_eq(¤t_query_regs, &new_query_regs));
732 assert_eq!(current_query_regs.lock().unwrap().len(), 1);
733 }
734
735 #[test]
736 fn remote_dyn_filter_register_uses_entry_to_merge_same_filter_subscribers() {
737 let regs_by_query = RemoteDynFilterRegistry::new();
738 let query_id = QueryId::new();
739 let first_region_id = RegionId::new(1024, 7);
740 let second_region_id = RegionId::new(1024, 8);
741 let regs = InitialDynFilterRegs::new(vec![InitialDynFilterReg::new("filter-1", vec![])]);
742
743 register_initial_dyn_filter_regs(®s_by_query, &query_id, first_region_id, ®s);
744 register_initial_dyn_filter_regs(®s_by_query, &query_id, second_region_id, ®s);
745
746 let query_regs = regs_by_query.get_query(&query_id).unwrap();
747 let query_regs = query_regs.lock().unwrap();
748 assert_eq!(query_regs.len(), 1);
749 let registered = query_regs.get(&RemoteDynFilterId::new("filter-1")).unwrap();
750 assert_eq!(registered.subscriber_regions.len(), 2);
751 assert!(registered.subscriber_regions.contains(&first_region_id));
752 assert!(registered.subscriber_regions.contains(&second_region_id));
753 }
754}