Skip to content

Batch pointer fetch by 25 objects on querying from LDS. #605

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

Merged
merged 2 commits into from
Nov 30, 2015
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
123 changes: 87 additions & 36 deletions Parse/Internal/LocalDataStore/OfflineStore/PFOfflineStore.m
Original file line number Diff line number Diff line change
Expand Up @@ -520,30 +520,34 @@ - (BFTask *)findAsyncForQueryState:(PFQueryState *)queryState
@strongify(self);
PFConstraintMatcherBlock matcherBlock = [self.offlineQueryLogic createMatcherForQueryState:queryState user:user];

BFTask *checkAllObjectsTask = [BFTask taskWithResult:nil];
for (NSString *uuid in task.result) {
__block PFObject *object = nil;

checkAllObjectsTask = [[[[checkAllObjectsTask continueWithSuccessBlock:^id(BFTask *task) {
return [self _getPointerAsyncWithUUID:uuid database:database];
}] continueWithSuccessBlock:^id(BFTask *task) {
object = task.result;
return [self fetchObjectLocallyAsync:object database:database];
}] continueWithSuccessBlock:^id(BFTask *task) {
if (!object.dataAvailable) {
return @NO;
BFTask *checkAllTask = [BFTask taskWithResult:nil];
NSArray *uuidBatches = [PFInternalUtils arrayBySplittingArray:task.result withMaximumComponentsPerSegment:64];
for (NSArray *uuids in uuidBatches) {
checkAllTask = [[checkAllTask continueWithSuccessBlock:^id(BFTask *_) {
return [self _getObjectPointersAsyncWithUUIDs:uuids fromDatabase:database];
}] continueWithSuccessBlock:^id(BFTask PF_GENERIC(NSArray<PFObject *> *)*task) {
BFTask *checkBatchTask = [BFTask taskWithResult:nil];
for (PFObject *object in task.result) {
checkBatchTask = [[[checkBatchTask continueWithSuccessBlock:^id(BFTask *_) {
return [self fetchObjectLocallyAsync:object database:database];
}] continueWithSuccessBlock:^id(BFTask *_) {
if (!object.dataAvailable) {
return nil;
}
return matcherBlock(object, database);
}] continueWithSuccessBlock:^id(BFTask *task) {
if ([task.result boolValue]) {
[mutableResults addObject:object];
}
return nil;
}];
}
return matcherBlock(object, database);
}] continueWithSuccessBlock:^id(BFTask *task) {
if ([task.result boolValue]) {
[mutableResults addObject:object];
}
return nil;
return checkBatchTask;
}];
}
return checkAllObjectsTask;
return checkAllTask;
}];
}] continueWithSuccessBlock:^id(BFTask *task) {
}] continueWithSuccessBlock:^id(BFTask *_) {
@strongify(self);

// Sort, Apply Skip and Limit
Expand All @@ -561,7 +565,7 @@ - (BFTask *)findAsyncForQueryState:(PFQueryState *)queryState
ofQueryState:queryState
inDatabase:database];

return [fetchIncludesTask continueWithSuccessBlock:^id(BFTask *task) {
return [fetchIncludesTask continueWithSuccessBlock:^id(BFTask *_) {
return results;
}];
}];
Expand Down Expand Up @@ -867,6 +871,8 @@ - (BFTask *)getOrCreateUUIDAsyncForObject:(PFObject *)object
return tcs.task;
}

#pragma mark Pointers

/*!
Gets an unfetched pointer to an object in the database, based on its uuid. The object may or may
not be in memory, but it must be in database. If it is already in memory, the instance will be
Expand All @@ -877,8 +883,7 @@ - (BFTask *)getOrCreateUUIDAsyncForObject:(PFObject *)object
@param database The database instance to retrieve from.
@returns The object with that UUID.
*/
- (BFTask *)_getPointerAsyncWithUUID:(NSString *)uuid
database:(PFSQLiteDatabase *)database {
- (BFTask *)_getPointerAsyncWithUUID:(NSString *)uuid database:(PFSQLiteDatabase *)database {
@synchronized (self.lock) {
PFObject *existing = [self.UUIDToObjectMap objectForKey:uuid];
if (existing) {
Expand All @@ -905,24 +910,70 @@ - (BFTask *)_getPointerAsyncWithUUID:(NSString *)uuid

return nil;
}] continueWithSuccessBlock:^id(BFTask *_) {
PFObject *pointer = nil;
@synchronized (self.lock) {
pointer = [self.UUIDToObjectMap objectForKey:uuid];
if (!pointer) {
pointer = [PFObject objectWithoutDataWithClassName:className objectId:objectId];

// If it doesn't have objectId, we don't really need the UUID, and this simplifies some
// other logic elsewhere if we only update the map for new objects.
if (!objectId) {
[self.UUIDToObjectMap setObject:pointer forKey:uuid];
[self.objectToUUIDMap setObject:[BFTask taskWithResult:uuid] forKey:pointer];
}
return [self _getOrCreateInMemoryPointerForObjectWithUUID:uuid parseClassName:className objectId:objectId];
}];
}

- (BFTask PF_GENERIC(NSArray<PFObject *> *)*)_getObjectPointersAsyncWithUUIDs:(NSArray PF_GENERIC(NSString *)*)uuids
fromDatabase:(PFSQLiteDatabase *)database {
NSMutableArray PF_GENERIC(PFObject *)*objects = [NSMutableArray array];
NSMutableArray PF_GENERIC(NSString *)*missingUUIDs = [NSMutableArray array];
@synchronized(self.lock) {
for (NSString *uuid in uuids) {
PFObject *object = [self.UUIDToObjectMap objectForKey:uuid];
if (object) {
[objects addObject:object];
} else {
[missingUUIDs addObject:uuid];
}
}
return pointer;
}
NSString *queryString = [NSString stringWithFormat:@"SELECT %@, %@, %@ FROM %@ WHERE %@ IN ('%@');",
PFOfflineStoreKeyOfUUID, PFOfflineStoreKeyOfObjectId, PFOfflineStoreKeyOfClassName,
PFOfflineStoreTableOfObjects, PFOfflineStoreKeyOfUUID,
[missingUUIDs componentsJoinedByString:@"','"]];
NSMutableArray PF_GENERIC(BFTask <PFObject *>*)*fetchPointersTasks = [NSMutableArray arrayWithCapacity:missingUUIDs.count];
return [[database executeQueryAsync:queryString withArgumentsInArray:nil block:^id(PFSQLiteDatabaseResult *result) {
while ([result next]) {
NSString *uuid = [result stringForColumnIndex:0];
NSString *objectId = [result stringForColumnIndex:1];
NSString *parseClassName = [result stringForColumnIndex:2];
BFTask *task = [BFTask taskFromExecutor:[BFExecutor defaultPriorityBackgroundExecutor] withBlock:^id{
return [self _getOrCreateInMemoryPointerForObjectWithUUID:uuid parseClassName:parseClassName objectId:objectId];
}];
[fetchPointersTasks addObject:task];
}
return [BFTask taskForCompletionOfAllTasks:fetchPointersTasks];
}] continueWithSuccessBlock:^id(BFTask *_) {
for (BFTask PF_GENERIC(PFObject *)*task in fetchPointersTasks) {
[objects addObject:task.result];
}
return objects;
}];
}

- (BFTask PF_GENERIC(PFObject *)*)_getOrCreateInMemoryPointerForObjectWithUUID:(NSString *)uuid
parseClassName:(NSString *)parseClassName
objectId:(NSString *)objectId {
PFObject *pointer = nil;
@synchronized (self.lock) {
pointer = [self.UUIDToObjectMap objectForKey:uuid];
if (!pointer) {
pointer = [PFObject objectWithoutDataWithClassName:parseClassName objectId:objectId];

// If it doesn't have objectId, we don't really need the UUID, and this simplifies some
// other logic elsewhere if we only update the map for new objects.
if (!objectId) {
[self.UUIDToObjectMap setObject:pointer forKey:uuid];
[self.objectToUUIDMap setObject:[BFTask taskWithResult:uuid] forKey:pointer];
}
}
}
return [BFTask taskWithResult:pointer];
}

#pragma mark Else

- (PFObject *)getOrCreateObjectWithoutDataWithClassName:(NSString *)className
objectId:(NSString *)objectId {
PFParameterAssert(objectId, @"objectId cannot be nil.");
Expand Down