@@ -538,9 +538,14 @@ public Node getServerValue(QuerySpec query) {
538
538
});
539
539
}
540
540
541
- /** Add an event callback for the specified query. */
542
541
public List <? extends Event > addEventRegistration (
543
542
@ NotNull final EventRegistration eventRegistration ) {
543
+ return addEventRegistration (eventRegistration , false );
544
+ }
545
+
546
+ /** Add an event callback for the specified query. */
547
+ public List <? extends Event > addEventRegistration (
548
+ @ NotNull final EventRegistration eventRegistration , final boolean skipListenerSetup ) {
544
549
return persistenceManager .runInTransaction (
545
550
new Callable <List <? extends Event >>() {
546
551
@ Override
@@ -635,7 +640,7 @@ public List<? extends Event> call() {
635
640
WriteTreeRef writesCache = pendingWriteTree .childWrites (path );
636
641
List <? extends Event > events =
637
642
syncPoint .addEventRegistration (eventRegistration , writesCache , serverCache );
638
- if (!viewAlreadyExists && !foundAncestorDefaultView ) {
643
+ if (!viewAlreadyExists && !foundAncestorDefaultView && ! skipListenerSetup ) {
639
644
View view = syncPoint .viewForQuery (query );
640
645
setupListener (query , view );
641
646
}
@@ -650,7 +655,14 @@ public List<? extends Event> call() {
650
655
* <p>If query is the default query, we'll check all queries for the specified eventRegistration.
651
656
*/
652
657
public List <Event > removeEventRegistration (@ NotNull EventRegistration eventRegistration ) {
653
- return this .removeEventRegistration (eventRegistration .getQuerySpec (), eventRegistration , null );
658
+ return this .removeEventRegistration (
659
+ eventRegistration .getQuerySpec (), eventRegistration , null , false );
660
+ }
661
+
662
+ public List <Event > removeEventRegistration (
663
+ @ NotNull EventRegistration eventRegistration , boolean skipDedup ) {
664
+ return this .removeEventRegistration (
665
+ eventRegistration .getQuerySpec (), eventRegistration , null , skipDedup );
654
666
}
655
667
656
668
/**
@@ -660,13 +672,14 @@ public List<Event> removeEventRegistration(@NotNull EventRegistration eventRegis
660
672
*/
661
673
public List <Event > removeAllEventRegistrations (
662
674
@ NotNull QuerySpec query , @ NotNull DatabaseError error ) {
663
- return this .removeEventRegistration (query , null , error );
675
+ return this .removeEventRegistration (query , null , error , false );
664
676
}
665
677
666
678
private List <Event > removeEventRegistration (
667
679
final @ NotNull QuerySpec query ,
668
680
final @ Nullable EventRegistration eventRegistration ,
669
- final @ Nullable DatabaseError cancelError ) {
681
+ final @ Nullable DatabaseError cancelError ,
682
+ final boolean skipDedup ) {
670
683
return persistenceManager .runInTransaction (
671
684
new Callable <List <Event >>() {
672
685
@ Override
@@ -688,6 +701,7 @@ public List<Event> call() {
688
701
if (maybeSyncPoint .isEmpty ()) {
689
702
syncPointTree = syncPointTree .remove (path );
690
703
}
704
+
691
705
List <QuerySpec > removed = removedAndEvents .getFirst ();
692
706
cancelEvents = removedAndEvents .getSecond ();
693
707
// We may have just removed one of many listeners and can short-circuit this whole
@@ -701,6 +715,25 @@ public List<Event> call() {
701
715
persistenceManager .setQueryInactive (query );
702
716
removingDefault = removingDefault || queryRemoved .loadsAllData ();
703
717
}
718
+
719
+ /**
720
+ * This is to handle removeRegistration by {@link Repo#getValue(Query)}. Specifically
721
+ * to avoid the scenario where: A listener is attached at a child path, and {@link
722
+ * Repo#getValue(Query)} is called on the parent path. Normally, when a listener is
723
+ * attached on a child path and then a parent path has a listener attached to it, to
724
+ * reduce the number of listeners, the listen() function will unlisten to the child
725
+ * path and listen instead on the parent path. And then, when removeRegistration is
726
+ * called on the parent path, the child path will get listened to, since it doesn't
727
+ * have anything covering its path. However, for {@link Repo#getValue(Query)}, we do
728
+ * not call listen on the parent path, and the child path is still listened to and so
729
+ * when the deduping happens below, the SyncTree assumes that the child listener has
730
+ * been removed and attempts to call listen again, but since we are still listening on
731
+ * that location, listen would be called twice on the same query. skipDedup allows us
732
+ * to skip this deduping process altogether.
733
+ */
734
+ if (skipDedup ) {
735
+ return null ;
736
+ }
704
737
ImmutableTree <SyncPoint > currentTree = syncPointTree ;
705
738
boolean covered =
706
739
currentTree .getValue () != null && currentTree .getValue ().hasCompleteView ();
@@ -915,7 +948,7 @@ private QuerySpec queryForTag(Tag tag) {
915
948
}
916
949
917
950
/** Return the tag associated with the given query. */
918
- private Tag tagForQuery (QuerySpec query ) {
951
+ public Tag tagForQuery (QuerySpec query ) {
919
952
return this .queryToTagMap .get (query );
920
953
}
921
954
0 commit comments