30
30
#include " database/src/desktop/core/listen_provider.h"
31
31
#include " database/src/desktop/core/server_values.h"
32
32
#include " database/src/desktop/core/sync_point.h"
33
+ #include " database/src/desktop/core/tag.h"
33
34
#include " database/src/desktop/core/tree.h"
34
35
#include " database/src/desktop/util_desktop.h"
35
36
#include " database/src/desktop/view/event.h"
@@ -190,6 +191,14 @@ std::vector<Event> SyncTree::AddEventRegistration(
190
191
// Now that we have the sync point, see if there is an existing view of the
191
192
// database, and if there isn't, then set one up.
192
193
bool view_already_exists = sync_point->ViewExistsForQuery (query_spec);
194
+ if (!view_already_exists && !QuerySpecLoadsAllData (query_spec)) {
195
+ // We need to track a tag for this query
196
+ FIREBASE_DEV_ASSERT_MESSAGE (!query_spec_to_tag_map_.count (query_spec),
197
+ " View does not exist but we have a tag" );
198
+ Tag tag = GetNextQueryTag ();
199
+ query_spec_to_tag_map_[query_spec] = tag;
200
+ tag_to_query_spec_map_[*tag] = query_spec;
201
+ }
193
202
WriteTreeRef writes_cache = pending_write_tree_->ChildWrites (path);
194
203
events = sync_point->AddEventRegistration (Move (event_registration),
195
204
writes_cache, server_cache,
@@ -203,6 +212,86 @@ std::vector<Event> SyncTree::AddEventRegistration(
203
212
return events;
204
213
}
205
214
215
+ // Apply a listen complete to a path.
216
+ std::vector<Event> SyncTree::ApplyTaggedListenComplete (const Tag& tag) {
217
+ std::vector<Event> results;
218
+ persistence_manager_->RunInTransaction ([&, this ]() -> bool {
219
+ const QuerySpec* query_spec = this ->QuerySpecForTag (tag);
220
+ if (query_spec != nullptr ) {
221
+ this ->persistence_manager_ ->SetQueryComplete (*query_spec);
222
+ Operation op = Operation::ListenComplete (
223
+ OperationSource::ForServerTaggedQuery (query_spec->params ), Path ());
224
+ results = this ->ApplyTaggedOperation (*query_spec, op);
225
+ return true ;
226
+ } else {
227
+ // We've already removed the query. No big deal, ignore the update
228
+ return true ;
229
+ }
230
+ });
231
+ return results;
232
+ }
233
+
234
+ std::vector<Event> SyncTree::ApplyTaggedOperation (const QuerySpec& query_spec,
235
+ const Operation& operation) {
236
+ const Path& query_path = query_spec.path ;
237
+ SyncPoint* sync_point = sync_point_tree_.GetValueAt (query_path);
238
+ FIREBASE_DEV_ASSERT_MESSAGE (
239
+ sync_point != nullptr ,
240
+ " Missing sync point for query tag that we're tracking" );
241
+ WriteTreeRef writes_cache = pending_write_tree_->ChildWrites (query_path);
242
+ return sync_point->ApplyOperation (operation, writes_cache, nullptr ,
243
+ persistence_manager_.get ());
244
+ }
245
+
246
+ // Apply new server data for the specified tagged query.
247
+ std::vector<Event> SyncTree::ApplyTaggedQueryOverwrite (const Path& path,
248
+ const Variant& snap,
249
+ const Tag& tag) {
250
+ std::vector<Event> results;
251
+ persistence_manager_->RunInTransaction ([&, this ]() -> bool {
252
+ const QuerySpec* query_spec = this ->QuerySpecForTag (tag);
253
+ if (query_spec != nullptr ) {
254
+ Optional<Path> relative_path = Path::GetRelative (query_spec->path , path);
255
+ QuerySpec query_to_overwrite =
256
+ relative_path->empty () ? *query_spec : QuerySpec (path);
257
+ this ->persistence_manager_ ->UpdateServerCache (query_to_overwrite, snap);
258
+ Operation op = Operation::Overwrite (
259
+ OperationSource::ForServerTaggedQuery (query_spec->params ),
260
+ *relative_path, snap);
261
+ results = this ->ApplyTaggedOperation (*query_spec, op);
262
+ return true ;
263
+ } else {
264
+ // Query must have been removed already
265
+ return true ;
266
+ }
267
+ });
268
+ return results;
269
+ }
270
+
271
+ std::vector<Event> SyncTree::ApplyTaggedQueryMerge (
272
+ const Path& path, const std::map<Path, Variant>& changed_children,
273
+ const Tag& tag) {
274
+ std::vector<Event> results;
275
+ persistence_manager_->RunInTransaction ([&, this ]() -> bool {
276
+ const QuerySpec* query_spec = QuerySpecForTag (tag);
277
+ if (query_spec != nullptr ) {
278
+ Optional<Path> relative_path = Path::GetRelative (query_spec->path , path);
279
+ FIREBASE_DEV_ASSERT (relative_path.has_value ());
280
+ CompoundWrite merge = CompoundWrite::FromPathMerge (changed_children);
281
+ this ->persistence_manager_ ->UpdateServerCache (path, merge);
282
+ Operation op = Operation::Merge (
283
+ OperationSource::ForServerTaggedQuery (query_spec->params ),
284
+ *relative_path, merge);
285
+ results = ApplyTaggedOperation (*query_spec, op);
286
+ return true ;
287
+ } else {
288
+ // We've already removed the query. No big deal, ignore the update
289
+ return true ;
290
+ }
291
+ });
292
+ return results;
293
+ }
294
+
206
295
std::vector<Event> SyncTree::ApplyListenComplete (const Path& path) {
207
296
std::vector<Event> results;
208
297
persistence_manager_->RunInTransaction ([&, this ]() -> bool {
@@ -457,30 +546,40 @@ static QuerySpec QuerySpecForListening(const QuerySpec& query_spec) {
457
546
458
547
void SyncTree::SetupListener (const QuerySpec& query_spec, const View* view) {
459
548
const Path& path = query_spec.path ;
460
- listen_provider_->StartListening (QuerySpecForListening (query_spec), view);
549
+ const Tag& tag = TagForQuerySpec (query_spec);
550
+ listen_provider_->StartListening (QuerySpecForListening (query_spec), tag,
551
+ view);
461
552
462
553
Tree<SyncPoint>* subtree = sync_point_tree_.GetChild (path);
463
554
464
555
// The root of this subtree has our query. We're here because we definitely
465
556
// need to send a listen for that, but we may need to shadow other listens
466
557
// as well.
467
-
468
- // Shadow everything at or below this location, this is a default listener.
469
- subtree->CallOnEach (path, [this ](const Path& relative_path,
470
- const SyncPoint& child_sync_point) {
471
- if (!relative_path.empty () && child_sync_point.HasCompleteView ()) {
472
- const QuerySpec& query_spec =
473
- child_sync_point.GetCompleteView ()->query_spec ();
474
- listen_provider_->StopListening (MakeDefaultQuerySpec (query_spec));
475
- } else {
476
- // No default listener here.
477
- for (const View* sync_point_view :
478
- child_sync_point.GetIncompleteQueryViews ()) {
479
- const QuerySpec& child_query_spec = sync_point_view->query_spec ();
480
- listen_provider_->StopListening (MakeDefaultQuerySpec (child_query_spec));
558
+ if (tag.has_value ()) {
559
+ FIREBASE_DEV_ASSERT_MESSAGE (
560
+ !subtree->value ()->HasCompleteView (),
561
+ " If we're adding a query, it shouldn't be shadowed" );
562
+ } else {
563
+ // Shadow everything at or below this location, this is a default listener.
564
+ subtree->CallOnEach (path, [this ](const Path& relative_path,
565
+ const SyncPoint& child_sync_point) {
566
+ if (!relative_path.empty () && child_sync_point.HasCompleteView ()) {
567
+ const QuerySpec& query_spec =
568
+ child_sync_point.GetCompleteView ()->query_spec ();
569
+ listen_provider_->StopListening (QuerySpecForListening (query_spec),
570
+ TagForQuerySpec (query_spec));
571
+ } else {
572
+ // No default listener here.
573
+ for (const View* sync_point_view :
574
+ child_sync_point.GetIncompleteQueryViews ()) {
575
+ const QuerySpec& child_query_spec = sync_point_view->query_spec ();
576
+ listen_provider_->StopListening (
577
+ QuerySpecForListening (child_query_spec),
578
+ TagForQuerySpec (child_query_spec));
579
+ }
481
580
}
482
- }
483
- });
581
+ });
582
+ }
484
583
}
485
584
486
585
static void CollectDistinctViewsForSubTree (Tree<SyncPoint>* subtree,
@@ -561,7 +660,7 @@ std::vector<Event> SyncTree::RemoveEventRegistration(
561
660
for (const View* view : new_views) {
562
661
QuerySpec new_query = view->query_spec ();
563
662
listen_provider_->StartListening (QuerySpecForListening (new_query),
564
- view);
663
+ TagForQuerySpec (new_query), view);
565
664
}
566
665
} else {
567
666
// There's nothing below us, so nothing we need to start listening on
@@ -578,14 +677,19 @@ std::vector<Event> SyncTree::RemoveEventRegistration(
578
677
// other queries here. Just cancel the one default. Otherwise, we need
579
678
// to iterate through and cancel each individual query
580
679
if (removing_default) {
581
- listen_provider_->StopListening (QuerySpecForListening (query_spec));
680
+ listen_provider_->StopListening (QuerySpecForListening (query_spec),
681
+ Tag ());
582
682
} else {
583
683
for (QuerySpec query_to_remove : removed) {
684
+ Tag tag = TagForQuerySpec (query_to_remove);
685
+ FIREBASE_DEV_ASSERT (tag.has_value ());
584
686
listen_provider_->StopListening (
585
- QuerySpecForListening (query_to_remove));
687
+ QuerySpecForListening (query_to_remove), tag );
586
688
}
587
689
}
588
690
}
691
+ // Now, clear all of the tags we're tracking for the removed listens.
692
+ RemoveTags (removed);
589
693
} else {
590
694
// No-op, this listener must've been already removed.
591
695
}
@@ -594,6 +698,29 @@ std::vector<Event> SyncTree::RemoveEventRegistration(
594
698
return cancel_events;
595
699
}
596
700
701
+ void SyncTree::RemoveTags (const std::vector<QuerySpec>& queries) {
702
+ for (const QuerySpec& removed_query : queries) {
703
+ if (!QuerySpecLoadsAllData (removed_query)) {
704
+ // We should have a tag for this
705
+ Tag tag = TagForQuerySpec (removed_query);
706
+ FIREBASE_DEV_ASSERT (tag.has_value ());
707
+ query_spec_to_tag_map_.erase (removed_query);
708
+ tag_to_query_spec_map_.erase (*tag);
709
+ }
710
+ }
711
+ }
712
+
713
+ const QuerySpec* SyncTree::QuerySpecForTag (const Tag& tag) {
714
+ return MapGet (&tag_to_query_spec_map_, *tag);
715
+ }
716
+
717
+ Tag SyncTree::TagForQuerySpec (const QuerySpec& query_spec) {
718
+ const Tag* tag_ptr = MapGet (&query_spec_to_tag_map_, query_spec);
719
+ return tag_ptr ? *tag_ptr : Tag ();
720
+ }
721
+
722
+ Tag SyncTree::GetNextQueryTag () { return Tag (next_query_tag_++); }
723
+
597
724
} // namespace internal
598
725
} // namespace database
599
726
} // namespace firebase
0 commit comments