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
Merged
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] Added `DatabaseQuery#getData` which returns data from the server when cache is stale (#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 getData: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 kPersistentConnectionOffline;
FOUNDATION_EXPORT const int kWebsocketMaxFrameSize;
FOUNDATION_EXPORT NSUInteger const kWebsocketKeepaliveInterval;
FOUNDATION_EXPORT NSUInteger const kWebsocketConnectTimeout;
FOUNDATION_EXPORT UInt64 const kPersistentConnectionGetConnectTimeout;

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 kPersistentConnectionOffline = @"Client is offline.";
const int kWebsocketMaxFrameSize = 16384;
NSUInteger const kWebsocketKeepaliveInterval = 45;
NSUInteger const kWebsocketConnectTimeout = 30;
UInt64 const kPersistentConnectionGetConnectTimeout = 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)getDataAtPath:(NSString *)pathString
withParams:(NSDictionary *)queryWireProtocolParams
withCallback:(fbt_void_nsstring_id_nsstring)onComplete;
- (void)purgeOutstandingWrites;

- (void)interruptForReason:(NSString *)reason;
Expand Down
118 changes: 114 additions & 4 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,10 @@ - (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 +727,43 @@ - (void)sendPut:(NSNumber *)index {
}];
}

- (void)sendGet:(NSNumber *)index {
NSAssert([self canSendReads],
@"sendGet called when not able to send reads");
FOutstandingGet *get = self.outstandingGets[index];
NSAssert(get != nil, @"sendGet found no outstanding get at index %@",
index);
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 (resultData == (id)[NSNull null]) {
resultData = nil;
}
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 +816,46 @@ - (void)putInternal:(id)data
}
}

- (void)getDataAtPath:(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,
kPersistentConnectionGetConnectTimeout),
self.dispatchQueue, ^{
FOutstandingGet *get = self.outstandingGets[index];
if ([get sent]) {
return;
}
FFLog(@"I-RDB034045",
@"get %@ timed out waiting for a connection",
index);
get.sent = YES;
[self.outstandingGets removeObjectForKey:index];
get.onCompleteBlock(kFWPResponseForActionStatusFailed,
nil, kPersistentConnectionOffline);
});
return;
}

if ([self canSendReads]) {
FFLog(@"I-RDB034024", @"Sending get: %@", index);
[self sendGet:index];
}
}

- (void)sendListen:(FOutstandingQuery *)listenSpec {
FQuerySpec *query = listenSpec.query;
FFLog(@"I-RDB034026", @"Listen for %@", query);
Expand Down Expand Up @@ -998,17 +1095,30 @@ - (void)restoreState {
[self sendListen:outstandingListen];
}];

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

NSArray *getKeys = [[self.outstandingGets allKeys]
sortedArrayUsingSelector:@selector(compare:)];
for (int i = 0; i < [getKeys count]; i++) {
if ([self.outstandingGets objectForKey:[getKeys objectAtIndex:i]] !=
nil) {
FFLog(@"I-RDB034037", @"Restoring get: %d", i);
[self sendGet:[getKeys 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
81 changes: 45 additions & 36 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 All @@ -31,63 +32,71 @@

@interface FRepo : NSObject <FPersistentConnectionDelegate>

@property(nonatomic, strong) FIRDatabaseConfig *config;
@property(nonatomic, strong) FIRDatabaseConfig *_Nullable config;

- (id)initWithRepoInfo:(FRepoInfo *)info
config:(FIRDatabaseConfig *)config
database:(FIRDatabase *)database;
- (id _Nonnull)initWithRepoInfo:(FRepoInfo *_Nullable)info
config:(FIRDatabaseConfig *_Nullable)config
database:(FIRDatabase *_Nullable)database;

- (void)set:(FPath *)path
withNode:(id)node
withCallback:(fbt_void_nserror_ref)onComplete;
- (void)update:(FPath *)path
withNodes:(FCompoundWrite *)compoundWrite
withCallback:(fbt_void_nserror_ref)callback;
- (void)set:(FPath *_Nullable)path
withNode:(id _Nullable)node
withCallback:(fbt_void_nserror_ref _Nullable)onComplete;
- (void)update:(FPath *_Nullable)path
withNodes:(FCompoundWrite *_Nullable)compoundWrite
withCallback:(fbt_void_nserror_ref _Nullable)callback;
- (void)purgeOutstandingWrites;

- (void)addEventRegistration:(id<FEventRegistration>)eventRegistration
forQuery:(FQuerySpec *)query;
- (void)removeEventRegistration:(id<FEventRegistration>)eventRegistration
forQuery:(FQuerySpec *)query;
- (void)keepQuery:(FQuerySpec *)query synced:(BOOL)synced;
- (void)getData:(FIRDatabaseQuery *_Nullable)query
withCompletionBlock:
(void (^_Nonnull)(NSError *_Nullable error,
FIRDataSnapshot *_Nullable snapshot))block;

- (NSString *)name;
- (void)addEventRegistration:(id<FEventRegistration> _Nullable)eventRegistration
forQuery:(FQuerySpec *_Nullable)query;
- (void)removeEventRegistration:
(id<FEventRegistration> _Nullable)eventRegistration
forQuery:(FQuerySpec *_Nullable)query;
- (void)keepQuery:(FQuerySpec *_Nullable)query synced:(BOOL)synced;

- (NSString *_Nullable)name;
- (NSTimeInterval)serverTime;

- (void)onDataUpdate:(FPersistentConnection *)fpconnection
forPath:(NSString *)pathString
message:(id)message
- (void)onDataUpdate:(FPersistentConnection *_Nullable)fpconnection
forPath:(NSString *_Nullable)pathString
message:(id _Nullable)message
isMerge:(BOOL)isMerge
tagId:(NSNumber *)tagId;
- (void)onConnect:(FPersistentConnection *)fpconnection;
- (void)onDisconnect:(FPersistentConnection *)fpconnection;
tagId:(NSNumber *_Nullable)tagId;
- (void)onConnect:(FPersistentConnection *_Nullable)fpconnection;
- (void)onDisconnect:(FPersistentConnection *_Nullable)fpconnection;

// Disconnect methods
- (void)onDisconnectCancel:(FPath *)path
withCallback:(fbt_void_nserror_ref)callback;
- (void)onDisconnectSet:(FPath *)path
withNode:(id<FNode>)node
withCallback:(fbt_void_nserror_ref)callback;
- (void)onDisconnectUpdate:(FPath *)path
withNodes:(FCompoundWrite *)compoundWrite
withCallback:(fbt_void_nserror_ref)callback;
- (void)onDisconnectCancel:(FPath *_Nullable)path
withCallback:(fbt_void_nserror_ref _Nullable)callback;
- (void)onDisconnectSet:(FPath *_Nullable)path
withNode:(id<FNode> _Nullable)node
withCallback:(fbt_void_nserror_ref _Nullable)callback;
- (void)onDisconnectUpdate:(FPath *_Nullable)path
withNodes:(FCompoundWrite *_Nullable)compoundWrite
withCallback:(fbt_void_nserror_ref _Nullable)callback;

// Connection Management.
- (void)interrupt;
- (void)resume;

// Transactions
- (void)startTransactionOnPath:(FPath *)path
update:(fbt_transactionresult_mutabledata)update
onComplete:(fbt_void_nserror_bool_datasnapshot)onComplete
- (void)startTransactionOnPath:(FPath *_Nullable)path
update:
(fbt_transactionresult_mutabledata _Nullable)update
onComplete:
(fbt_void_nserror_bool_datasnapshot _Nullable)onComplete
withLocalEvents:(BOOL)applyLocally;

// Testing methods
- (NSDictionary *)dumpListens;
- (NSDictionary *_Nullable)dumpListens;
- (void)dispose;
- (void)setHijackHash:(BOOL)hijack;

@property(nonatomic, strong, readonly) FAuthenticationManager *auth;
@property(nonatomic, strong, readonly) FIRDatabase *database;
@property(nonatomic, strong, readonly) FAuthenticationManager *_Nullable auth;
@property(nonatomic, strong, readonly) FIRDatabase *_Nullable database;

@end
Loading