Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement RTDB Query get #7110

Merged
merged 17 commits into from
Jan 6, 2021
1 change: 1 addition & 0 deletions FirebaseDatabase/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
# Unreleased
- [added] Made emulator connection API consistent between Auth, Database, Firestore, and Functions (#5916).
- [added] Implement RTDB Query get (#7110)

# v7.0.0
- [fixed] Disabled a deprecation warning. (#6502)
Expand Down
7 changes: 7 additions & 0 deletions FirebaseDatabase/Sources/Api/FIRDatabaseQuery.m
Original file line number Diff line number Diff line change
Expand Up @@ -581,6 +581,13 @@ - (void)keepSynced:(BOOL)keepSynced {
});
}

- (void)getDataWithCompletionBlock:(void (^)(NSError *__nullable error,
FIRDataSnapshot *snapshot))block {
dispatch_async([FIRDatabaseQuery sharedQueue], ^{
[self.repo getValue:self withCompletionBlock:block];
});
}

- (void)observeSingleEventOfType:(FIRDataEventType)eventType
withBlock:(fbt_void_datasnapshot)block {

Expand Down
2 changes: 2 additions & 0 deletions FirebaseDatabase/Sources/Api/Private/FTypedefs_Private.h
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,8 @@ typedef BOOL (^fbt_bool_nsstring_node)(NSString *, id<FNode>);
typedef void (^fbt_void_path_node_marray)(FPath *, id<FNode>, NSMutableArray *);
typedef BOOL (^fbt_bool_void)(void);
typedef void (^fbt_void_nsstring_nsstring)(NSString *str1, NSString *str2);
typedef void (^fbt_void_nsstring_id_nsstring)(NSString *str1, id dict1,
NSString *str2);
typedef void (^fbt_void_nsstring_nserror)(NSString *str, NSError *error);
typedef BOOL (^fbt_bool_path)(FPath *str);
typedef void (^fbt_void_id)(id data);
Expand Down
4 changes: 4 additions & 0 deletions FirebaseDatabase/Sources/Constants/FConstants.h
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ FOUNDATION_EXPORT NSString *const kFWPRequestAction;
FOUNDATION_EXPORT NSString *const kFWPResponseForRNData;
FOUNDATION_EXPORT NSString *const kFWPResponseForActionStatus;
FOUNDATION_EXPORT NSString *const kFWPResponseForActionStatusOk;
FOUNDATION_EXPORT NSString *const kFWPResponseForActionStatusFailed;
FOUNDATION_EXPORT NSString *const kFWPResponseForActionStatusDataStale;
FOUNDATION_EXPORT NSString *const kFWPResponseForActionData;
FOUNDATION_EXPORT NSString *const kFWPResponseDataWarnings;
Expand Down Expand Up @@ -75,6 +76,7 @@ FOUNDATION_EXPORT NSString *const kFWPAsyncServerControlMessageReset;

FOUNDATION_EXPORT NSString *const kFWPRequestActionPut;
FOUNDATION_EXPORT NSString *const kFWPRequestActionMerge;
FOUNDATION_EXPORT NSString *const kFWPRequestActionGet;
FOUNDATION_EXPORT NSString *const kFWPRequestActionTaggedListen;
FOUNDATION_EXPORT NSString *const kFWPRequestActionTaggedUnlisten;
FOUNDATION_EXPORT NSString
Expand Down Expand Up @@ -105,9 +107,11 @@ FOUNDATION_EXPORT NSString *const kFWPRequestStatus;
FOUNDATION_EXPORT NSString *const kWireProtocolVersionParam;
FOUNDATION_EXPORT NSString *const kWebsocketProtocolVersion;
FOUNDATION_EXPORT NSString *const kWebsocketServerKillPacket;
FOUNDATION_EXPORT NSString *const kPersistentConnOffline;
FOUNDATION_EXPORT const int kWebsocketMaxFrameSize;
FOUNDATION_EXPORT NSUInteger const kWebsocketKeepaliveInterval;
FOUNDATION_EXPORT NSUInteger const kWebsocketConnectTimeout;
FOUNDATION_EXPORT UInt64 const kPersistentConnGetConnectTimeout;

FOUNDATION_EXPORT float const kPersistentConnReconnectMinDelay;
FOUNDATION_EXPORT float const kPersistentConnReconnectMaxDelay;
Expand Down
4 changes: 4 additions & 0 deletions FirebaseDatabase/Sources/Constants/FConstants.m
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
NSString *const kFWPResponseForRNData = @"b";
NSString *const kFWPResponseForActionStatus = @"s";
NSString *const kFWPResponseForActionStatusOk = @"ok";
NSString *const kFWPResponseForActionStatusFailed = @"failed";
NSString *const kFWPResponseForActionStatusDataStale = @"datastale";
NSString *const kFWPResponseForActionData = @"d";
NSString *const kFWPResponseDataWarnings = @"w";
Expand Down Expand Up @@ -70,6 +71,7 @@

NSString *const kFWPRequestActionPut = @"p";
NSString *const kFWPRequestActionMerge = @"m";
NSString *const kFWPRequestActionGet = @"g";
NSString *const kFWPRequestActionListen =
@"l"; // {"t": "d", "d": {"r": 1, "a": "l", "b": { "p": "/" } } }
NSString *const kFWPRequestActionUnlisten = @"u";
Expand Down Expand Up @@ -99,9 +101,11 @@
NSString *const kWireProtocolVersionParam = @"v";
NSString *const kWebsocketProtocolVersion = @"5";
NSString *const kWebsocketServerKillPacket = @"kill";
NSString *const kPersistentConnOffline = @"Client is offline.";
const int kWebsocketMaxFrameSize = 16384;
NSUInteger const kWebsocketKeepaliveInterval = 45;
NSUInteger const kWebsocketConnectTimeout = 30;
UInt64 const kPersistentConnGetConnectTimeout = 3 * NSEC_PER_SEC;

float const kPersistentConnReconnectMinDelay = 1.0;
float const kPersistentConnReconnectMaxDelay = 30.0;
Expand Down
3 changes: 3 additions & 0 deletions FirebaseDatabase/Sources/Core/FPersistentConnection.h
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,9 @@
- (void)onDisconnectCancelPath:(FPath *)path
withCallback:(fbt_void_nsstring_nsstring)callback;
- (void)ackPuts;
- (void)get:(NSString *)pathString
withParams:(NSDictionary *)queryWireProtocolParams
withCallback:(fbt_void_nsstring_id_nsstring)onComplete;
- (void)purgeOutstandingWrites;

- (void)interruptForReason:(NSString *)reason;
Expand Down
102 changes: 102 additions & 0 deletions FirebaseDatabase/Sources/Core/FPersistentConnection.m
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,18 @@ @implementation FOutstandingPut

@end

@interface FOutstandingGet : NSObject

@property(nonatomic, strong) NSDictionary *request;
@property(nonatomic, copy) fbt_void_nsstring_id_nsstring onCompleteBlock;
@property(nonatomic) BOOL sent;

@end

@implementation FOutstandingGet

@end

typedef enum {
ConnectionStateDisconnected,
ConnectionStateGettingToken,
Expand Down Expand Up @@ -92,9 +104,11 @@ - (void)sendOnDisconnectAction:(NSString *)action
@property(nonatomic, strong) FConnection *realtime;
@property(nonatomic, strong) NSMutableDictionary *listens;
@property(nonatomic, strong) NSMutableDictionary *outstandingPuts;
@property(nonatomic, strong) NSMutableDictionary *outstandingGets;
@property(nonatomic, strong) NSMutableArray *onDisconnectQueue;
@property(nonatomic, strong) FRepoInfo *repoInfo;
@property(nonatomic, strong) FAtomicNumber *putCounter;
@property(nonatomic, strong) FAtomicNumber *getCounter;
@property(nonatomic, strong) FAtomicNumber *requestNumber;
@property(nonatomic, strong) NSMutableDictionary *requestCBHash;
@property(nonatomic, strong) FIRDatabaseConfig *config;
Expand Down Expand Up @@ -128,8 +142,10 @@ - (id)initWithRepoInfo:(FRepoInfo *)repoInfo

self.listens = [[NSMutableDictionary alloc] init];
self.outstandingPuts = [[NSMutableDictionary alloc] init];
self.outstandingGets = [[NSMutableDictionary alloc] init];
self.onDisconnectQueue = [[NSMutableArray alloc] init];
self.putCounter = [[FAtomicNumber alloc] init];
self.getCounter = [[FAtomicNumber alloc] init];
self.requestNumber = [[FAtomicNumber alloc] init];
self.requestCBHash = [[NSMutableDictionary alloc] init];
self.unackedListensCount = 0;
Expand Down Expand Up @@ -309,6 +325,9 @@ - (BOOL)canSendWrites {
return self->connectionState == ConnectionStateConnected;
}

- (BOOL)canSendReads {
return self->connectionState == ConnectionStateConnected;
}
#pragma mark -
#pragma mark FConnection delegate methods

Expand Down Expand Up @@ -707,6 +726,39 @@ - (void)sendPut:(NSNumber *)index {
}];
}

- (void)sendGet:(NSNumber *)index {
NSAssert([self canSendReads],
@"sendGet called when not able to send reads");
FOutstandingGet *get = self.outstandingGets[index];
assert(get != nil);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Prefer NSAssert.

if ([get sent]) {
return;
}
get.sent = YES;
[self sendAction:kFWPRequestActionGet
body:get.request
sensitive:NO
callback:^(NSDictionary *data) {
FOutstandingGet *currentGet = self.outstandingGets[index];
if (currentGet == get) {
[self.outstandingGets removeObjectForKey:index];
NSString *status =
[data objectForKey:kFWPResponseForActionStatus];
id resultData = [data objectForKey:kFWPResponseForActionData];
if ([status isEqualToString:kFWPResponseForActionStatusOk]) {
get.onCompleteBlock(status, resultData, nil);
return;
}
get.onCompleteBlock(status, nil, resultData);
} else {
FFLog(@"I-RDB034045",
@"Ignoring on complete for get %@ because it was "
@"already removed",
index);
}
}];
}

- (void)sendUnlisten:(FPath *)path
queryParams:(FQueryParams *)queryParams
tagId:(NSNumber *)tagId {
Expand Down Expand Up @@ -759,6 +811,45 @@ - (void)putInternal:(id)data
}
}

- (void)get:(NSString *)pathString
withParams:(NSDictionary *)queryWireProtocolParams
withCallback:(fbt_void_nsstring_id_nsstring)onComplete {
NSMutableDictionary *request = [NSMutableDictionary
dictionaryWithObjectsAndKeys:pathString, kFWPRequestPath,
queryWireProtocolParams,
kFWPRequestQueries, nil];
FOutstandingGet *get = [[FOutstandingGet alloc] init];
get.request = request;
get.onCompleteBlock = onComplete;
get.sent = NO;

NSNumber *index = [self.getCounter getAndIncrement];
self.outstandingGets[index] = get;

if (![self connected]) {
dispatch_after(
dispatch_time(DISPATCH_TIME_NOW, kPersistentConnGetConnectTimeout),
self.dispatchQueue, ^{
FOutstandingGet *get = self.outstandingGets[index];
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is missing the log statement from Android, which would also improve clarity here.

if ([get sent]) {
return;
}
get.sent = YES;
[self.outstandingGets removeObjectForKey:index];
get.onCompleteBlock(kFWPResponseForActionStatusFailed, nil,
kPersistentConnOffline);
});
return;
}

if ([self canSendReads]) {
FFLog(@"I-RDB034024", @"Was connected, and added as index: %@", index);
[self sendGet:index];
} else {
FFLog(@"I-RDB034025", @"Was connected, and added as index: %@", index);
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This seems to be superfluous.

}

- (void)sendListen:(FOutstandingQuery *)listenSpec {
FQuerySpec *query = listenSpec.query;
FFLog(@"I-RDB034026", @"Listen for %@", query);
Expand Down Expand Up @@ -1009,6 +1100,17 @@ - (void)restoreState {
}
}

NSArray *getKeys = [[self.outstandingGets allKeys]
sortedArrayUsingSelector:@selector(compare:)];
for (int i = 0; i < [getKeys count]; i++) {
if ([self.outstandingGets objectForKey:[keys objectAtIndex:i]] != nil) {
FFLog(@"I-RDB034037", @"Restoring get: %d", i);
[self sendGet:[keys objectAtIndex:i]];
} else {
FFLog(@"I-RDB034038", @"Restoring get: skipped nil: %d", i);
}
}

for (FTupleOnDisconnect *tuple in self.onDisconnectQueue) {
[self sendOnDisconnectAction:tuple.action
forPath:tuple.pathString
Expand Down
6 changes: 6 additions & 0 deletions FirebaseDatabase/Sources/Core/FRepo.h
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
#import "FirebaseDatabase/Sources/Core/FPersistentConnection.h"
#import "FirebaseDatabase/Sources/Core/FRepoInfo.h"
#import "FirebaseDatabase/Sources/Public/FirebaseDatabase/FIRDataEventType.h"
#import "FirebaseDatabase/Sources/Public/FirebaseDatabase/FIRDatabaseQuery.h"
#import "FirebaseDatabase/Sources/Utilities/Tuples/FTupleUserCallback.h"
#import <Foundation/Foundation.h>

Expand Down Expand Up @@ -45,6 +46,11 @@
withCallback:(fbt_void_nserror_ref)callback;
- (void)purgeOutstandingWrites;

- (void)getValue:(FIRDatabaseQuery *)query
withCompletionBlock:
(void (^_Nonnull)(NSError *__nullable error,
FIRDataSnapshot *__nullable snapshot))block;

- (void)addEventRegistration:(id<FEventRegistration>)eventRegistration
forQuery:(FQuerySpec *)query;
- (void)removeEventRegistration:(id<FEventRegistration>)eventRegistration
Expand Down
51 changes: 51 additions & 0 deletions FirebaseDatabase/Sources/Core/FRepo.m
Original file line number Diff line number Diff line change
Expand Up @@ -513,6 +513,57 @@ - (void)purgeOutstandingWrites {
[self.connection purgeOutstandingWrites];
}

- (void)getValue:(FIRDatabaseQuery *)query
withCompletionBlock:
(void (^_Nonnull)(NSError *__nullable error,
FIRDataSnapshot *__nullable snapshot))block {
FQuerySpec *querySpec = [query querySpec];
[self.persistenceManager setQueryActive:querySpec];
[self.connection
get:[query.path toString]
withParams:querySpec.params.wireProtocolParams
withCallback:^(NSString *status, id data, NSString *errorReason) {
id<FNode> node;
if (![status isEqualToString:kFWPResponseForActionStatusOk]) {
FFLog(@"I-RDB038024",
@"getValue for query %@ falling back to cache",
[querySpec.path toString]);
node = [self.serverSyncTree
calcCompleteEventCacheAtPath:querySpec.path
excludeWriteIds:@[]];
if ([node isEmpty]) {
FFWarn(@"I-RDB038025", @"getValue for query at %@ failed: %@",
[querySpec.path toString], status);
NSDictionary *errorDict = @{
NSLocalizedFailureReasonErrorKey : errorReason,
NSLocalizedDescriptionKey : [NSString
stringWithFormat:
@"Unable to get latest value for query %@, "
@"client offline and cache is empty",
querySpec]
};
block([NSError errorWithDomain:kFirebaseCoreErrorDomain
code:1
userInfo:errorDict],
nil);
return;
}
} else {
node = [FSnapshotUtilities nodeFrom:data];
}
[self.eventRaiser
raiseEvents:[self.serverSyncTree
applyServerOverwriteAtPath:[query path]
newData:node]];
block(nil, [[FIRDataSnapshot alloc]
initWithRef:query.ref
indexedNode:[FIndexedNode
indexedNodeWithNode:node
index:querySpec.index]]);
[self.persistenceManager setQueryInactive:querySpec];
}];
}

- (void)addEventRegistration:(id<FEventRegistration>)eventRegistration
forQuery:(FQuerySpec *)query {
NSArray *events = nil;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,10 @@ NS_SWIFT_NAME(DatabaseQuery)
withCancelBlock:
(nullable void (^)(NSError *error))cancelBlock;

- (void)getDataWithCompletionBlock:
(void (^_Nonnull)(NSError *__nullable error,
FIRDataSnapshot *snapshot))block;

/**
* This is equivalent to observeEventType:withBlock:, except the block is
* immediately canceled after the initial data is returned.
Expand Down
Loading