Skip to content

Commit e6e6199

Browse files
authored
Merge 6b04339 into aefb2a4
2 parents aefb2a4 + 6b04339 commit e6e6199

File tree

7 files changed

+197
-14
lines changed

7 files changed

+197
-14
lines changed

FirebaseDatabase/Sources/Core/FRepo.m

Lines changed: 16 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -533,7 +533,7 @@ - (void)getData:(FIRDatabaseQuery *)query
533533
}];
534534
return;
535535
}
536-
[self.persistenceManager setQueryActive:querySpec];
536+
NSNumber *tag = [self.serverSyncTree registerQuery:querySpec];
537537
[self.connection
538538
getDataAtPath:[query.path toString]
539539
withParams:querySpec.params.wireProtocolParams
@@ -561,6 +561,7 @@ - (void)getData:(FIRDatabaseQuery *)query
561561
userInfo:errorDict],
562562
nil);
563563
}];
564+
[self.serverSyncTree unregisterQuery:[query querySpec]];
564565
return;
565566
}
566567
[self.eventRaiser raiseCallback:^{
@@ -569,10 +570,19 @@ - (void)getData:(FIRDatabaseQuery *)query
569570
}];
570571
} else {
571572
node = [FSnapshotUtilities nodeFrom:data];
572-
[self.eventRaiser
573-
raiseEvents:[self.serverSyncTree
574-
applyServerOverwriteAtPath:[query path]
575-
newData:node]];
573+
if ([query.querySpec loadsAllData]) {
574+
[self.eventRaiser
575+
raiseEvents:[self.serverSyncTree
576+
applyServerOverwriteAtPath:[query path]
577+
newData:node]];
578+
} else {
579+
[self.eventRaiser
580+
raiseEvents:[self.serverSyncTree
581+
applyTaggedQueryOverwriteAtPath:[query
582+
path]
583+
newData:node
584+
tagId:tag]];
585+
}
576586
[self.eventRaiser raiseCallback:^{
577587
block(
578588
nil,
@@ -583,7 +593,7 @@ - (void)getData:(FIRDatabaseQuery *)query
583593
index:querySpec.index]]);
584594
}];
585595
}
586-
[self.persistenceManager setQueryInactive:querySpec];
596+
[self.serverSyncTree unregisterQuery:querySpec];
587597
}];
588598
}
589599

FirebaseDatabase/Sources/Core/FSyncPoint.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,5 +70,9 @@
7070
- (BOOL)viewExistsForQuery:(FQuerySpec *)query;
7171
- (BOOL)hasCompleteView;
7272
- (FView *)completeView;
73+
- (void)registerQuery:(FQuerySpec *)query
74+
writesCache:(FWriteTreeRef *)writesCache
75+
serverCache:(FCacheNode *)serverCache;
76+
- (BOOL)unregisterQuery:(FQuerySpec *)query;
7377

7478
@end

FirebaseDatabase/Sources/Core/FSyncPoint.m

Lines changed: 23 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -161,14 +161,9 @@ - (FView *)getView:(FQuerySpec *)query
161161
return [[FView alloc] initWithQuery:query initialViewCache:viewCache];
162162
}
163163

164-
/**
165-
* Add an event callback for the specified query
166-
* Returns an array of events to raise.
167-
*/
168-
- (NSArray *)addEventRegistration:(id<FEventRegistration>)eventRegistration
169-
forNonExistingViewForQuery:(FQuerySpec *)query
170-
writesCache:(FWriteTreeRef *)writesCache
171-
serverCache:(FCacheNode *)serverCache {
164+
- (void)registerQuery:(FQuerySpec *)query
165+
writesCache:(FWriteTreeRef *)writesCache
166+
serverCache:(FCacheNode *)serverCache {
172167
NSAssert(self.views[query.params] == nil, @"Found view for query: %@",
173168
query.params);
174169
// TODO: make writesCache take flag for complete server node
@@ -187,6 +182,17 @@ - (NSArray *)addEventRegistration:(id<FEventRegistration>)eventRegistration
187182
[self.persistenceManager setTrackedQueryKeys:allKeys forQuery:query];
188183
}
189184
self.views[query.params] = view;
185+
}
186+
187+
/**
188+
* Add an event callback for the specified query
189+
* Returns an array of events to raise.
190+
*/
191+
- (NSArray *)addEventRegistration:(id<FEventRegistration>)eventRegistration
192+
forNonExistingViewForQuery:(FQuerySpec *)query
193+
writesCache:(FWriteTreeRef *)writesCache
194+
serverCache:(FCacheNode *)serverCache {
195+
[self registerQuery:query writesCache:writesCache serverCache:serverCache];
190196
return [self addEventRegistration:eventRegistration
191197
forExistingViewForQuery:query];
192198
}
@@ -199,6 +205,15 @@ - (NSArray *)addEventRegistration:(id<FEventRegistration>)eventRegistration
199205
return [view initialEvents:eventRegistration];
200206
}
201207

208+
- (BOOL)unregisterQuery:(FQuerySpec *)query {
209+
FView *view = self.views[query.params];
210+
NSAssert(view != nil, @"no view for query %@", query);
211+
if ([view isEmpty]) {
212+
[self.views removeObjectForKey:query.params];
213+
}
214+
return [view isEmpty];
215+
}
216+
202217
/**
203218
* Remove event callback(s). Return cancelEvents if a cancelError is specified.
204219
*

FirebaseDatabase/Sources/Core/FSyncTree.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,5 +81,7 @@
8181
- (id<FNode>)getServerValue:(FQuerySpec *)query;
8282
- (id<FNode>)calcCompleteEventCacheAtPath:(FPath *)path
8383
excludeWriteIds:(NSArray *)writeIdsToExclude;
84+
- (NSNumber *)registerQuery:(FQuerySpec *)querySpec;
85+
- (void)unregisterQuery:(FQuerySpec *)querySpec;
8486

8587
@end

FirebaseDatabase/Sources/Core/FSyncTree.m

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -164,6 +164,15 @@ - (id)initWithPersistenceManager:(FPersistenceManager *)persistenceManager
164164
return self;
165165
}
166166

167+
- (NSNumber *)tagQuery:(FQuerySpec *)query {
168+
NSAssert(self.queryToTagMap[query] == nil,
169+
@"View does not exist, but we have a tag");
170+
NSNumber *tagId = [self.queryTagCounter getAndIncrement];
171+
self.queryToTagMap[query] = tagId;
172+
self.tagToQueryMap[tagId] = query;
173+
return tagId;
174+
}
175+
167176
#pragma mark -
168177
#pragma mark Apply Operations
169178

@@ -448,6 +457,42 @@ - (NSArray *)applyTaggedServerRangeMergeAtPath:(FPath *)path
448457
}
449458
}
450459

460+
- (NSNumber *)registerQuery:(FQuerySpec *)query {
461+
FPath *path = query.path;
462+
463+
__block BOOL foundAncestorDefaultView = NO;
464+
[self.syncPointTree
465+
forEachOnPath:query.path
466+
whileBlock:^BOOL(FPath *pathToSyncPoint, FSyncPoint *syncPoint) {
467+
foundAncestorDefaultView =
468+
foundAncestorDefaultView || [syncPoint hasCompleteView];
469+
return !foundAncestorDefaultView;
470+
}];
471+
472+
[self.persistenceManager setQueryActive:query];
473+
474+
FSyncPoint *syncPoint = [self.syncPointTree valueAtPath:path];
475+
if (syncPoint == nil) {
476+
syncPoint = [[FSyncPoint alloc]
477+
initWithPersistenceManager:self.persistenceManager];
478+
self.syncPointTree = [self.syncPointTree setValue:syncPoint
479+
atPath:path];
480+
}
481+
NSNumber *tag = nil;
482+
BOOL viewAlreadyExists = [syncPoint viewExistsForQuery:query];
483+
if (!viewAlreadyExists) {
484+
FWriteTreeRef *writesCache =
485+
[self.pendingWriteTree childWritesForPath:path];
486+
FCacheNode *serverCache = [self serverCacheForQuery:query];
487+
[syncPoint registerQuery:query
488+
writesCache:writesCache
489+
serverCache:serverCache];
490+
} else if (![query loadsAllData]) {
491+
tag = [self tagQuery:query];
492+
}
493+
return tag;
494+
}
495+
451496
/**
452497
* Add an event callback for the specified query
453498
* @return NSArray of FEvent to raise.
@@ -575,6 +620,23 @@ - (FCacheNode *)serverCacheForQuery:(FQuerySpec *)query {
575620
return serverCache;
576621
}
577622

623+
- (void)unregisterQuery:(FQuerySpec *)query {
624+
FPath *path = query.path;
625+
FSyncPoint *maybeSyncPoint = [self.syncPointTree valueAtPath:path];
626+
627+
if (maybeSyncPoint &&
628+
([query isDefault] || [maybeSyncPoint viewExistsForQuery:query])) {
629+
BOOL removed = [maybeSyncPoint unregisterQuery:query];
630+
if ([maybeSyncPoint isEmpty]) {
631+
self.syncPointTree = [self.syncPointTree removeValueAtPath:path];
632+
}
633+
if (removed) {
634+
[self removeTags:@[ query ]];
635+
[self.persistenceManager setQueryInactive:query];
636+
}
637+
}
638+
}
639+
578640
/**
579641
* Remove event callback(s).
580642
*
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
/*
2+
* Copyright 2017 Google
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
#import <Foundation/Foundation.h>
18+
#import "FirebaseDatabase/Tests/Helpers/FTestBase.h"
19+
20+
@interface FIRDatabaseGetTests : FTestBase
21+
22+
@end
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
/*
2+
* Copyright 2017 Google
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
#import "FirebaseDatabase/Tests/Integration/FIRDatabaseGetTests.h"
18+
#import "FirebaseCore/Sources/Public/FirebaseCore/FIROptions.h"
19+
#import "FirebaseDatabase/Sources/Api/Private/FIRDatabaseQuery_Private.h"
20+
#import "FirebaseDatabase/Sources/Constants/FConstants.h"
21+
#import "FirebaseDatabase/Sources/Core/FQuerySpec.h"
22+
#import "FirebaseDatabase/Sources/Utilities/FUtilities.h"
23+
#import "FirebaseDatabase/Tests/Helpers/FEventTester.h"
24+
#import "FirebaseDatabase/Tests/Helpers/FIRFakeApp.h"
25+
#import "FirebaseDatabase/Tests/Helpers/FTestExpectations.h"
26+
#import "FirebaseDatabase/Tests/Helpers/FTupleEventTypeString.h"
27+
28+
@implementation FIRDatabaseGetTests
29+
30+
- (void)testGetDoesntTriggerExtraListens {
31+
FIRDatabaseReference* ref = [FTestHelpers getRandomNode];
32+
FIRDatabaseReference* root = [ref root];
33+
FIRDatabaseReference* list = [root child:@"list"];
34+
35+
__block BOOL removeDone = NO;
36+
[root removeValueWithCompletionBlock:^(NSError* error, FIRDatabaseReference* ref) {
37+
removeDone = YES;
38+
}];
39+
WAIT_FOR(removeDone);
40+
41+
[self waitForCompletionOf:[list childByAutoId] setValue:@{@"name" : @"child1"}];
42+
[self waitForCompletionOf:[list childByAutoId] setValue:@{@"name" : @"child2"}];
43+
44+
// The original report of this issue makes a listen call first, and then
45+
// performs a getData. However, in the testing environment, if the listen
46+
// is made first, it will round trip and cache results before get has a
47+
// chance to run, at which point it reads from cache.
48+
// https://github.com/firebase/firebase-ios-sdk/issues/8286
49+
__block BOOL getDone = NO;
50+
[[[list queryOrderedByChild:@"name"] queryEqualToValue:@"child2"]
51+
getDataWithCompletionBlock:^(NSError* error, FIRDataSnapshot* snapshot) {
52+
XCTAssertNil(error);
53+
XCTAssertEqual(snapshot.childrenCount, 1L);
54+
getDone = YES;
55+
}];
56+
__block NSInteger numListenEvents = 0L;
57+
FIRDatabaseHandle handle = [list observeEventType:FIRDataEventTypeValue
58+
withBlock:^(FIRDataSnapshot* snapshot) {
59+
XCTAssertEqual(snapshot.childrenCount, 2L);
60+
numListenEvents += 1;
61+
}];
62+
WAIT_FOR(getDone);
63+
[NSThread sleepForTimeInterval:1];
64+
XCTAssertEqual(numListenEvents, 1);
65+
[list removeObserverWithHandle:handle];
66+
}
67+
68+
@end

0 commit comments

Comments
 (0)