Skip to content

Commit

Permalink
[Rollouts] Implement insert and load logic for rollout metadata in RC (
Browse files Browse the repository at this point in the history
  • Loading branch information
ddnan authored Jan 18, 2024
1 parent 50b72c1 commit 822fcdf
Show file tree
Hide file tree
Showing 6 changed files with 303 additions and 47 deletions.
43 changes: 27 additions & 16 deletions FirebaseRemoteConfig/Sources/RCNConfigContent.m
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,10 @@ @implementation RCNConfigContent {
/// Pending Personalization metadata that is latest data from server that might or might not be
/// applied.
NSDictionary *_fetchedPersonalization;
/// Active Rollout metadata that is currently used.
NSArray<NSDictionary *> *_activeRolloutMetadata;
/// Pending Rollout metadata that is latest data from server that might or might not be applied.
NSArray<NSDictionary *> *_fetchedRolloutMetadata;
/// DBManager
RCNConfigDBManager *_DBManager;
/// Current bundle identifier;
Expand Down Expand Up @@ -80,6 +84,8 @@ - (instancetype)initWithDBManager:(RCNConfigDBManager *)DBManager {
_defaultConfig = [[NSMutableDictionary alloc] init];
_activePersonalization = [[NSDictionary alloc] init];
_fetchedPersonalization = [[NSDictionary alloc] init];
_activeRolloutMetadata = [[NSArray alloc] init];
_fetchedRolloutMetadata = [[NSArray alloc] init];
_bundleIdentifier = [[NSBundle mainBundle] bundleIdentifier];
if (!_bundleIdentifier) {
FIRLogNotice(kFIRLoggerRemoteConfig, @"I-RCN000038",
Expand Down Expand Up @@ -115,25 +121,30 @@ - (void)loadConfigFromMainTable {
_isDatabaseLoadAlreadyInitiated = true;

dispatch_group_enter(_dispatch_group);
[_DBManager
loadMainWithBundleIdentifier:_bundleIdentifier
completionHandler:^(BOOL success, NSDictionary *fetchedConfig,
NSDictionary *activeConfig, NSDictionary *defaultConfig) {
self->_fetchedConfig = [fetchedConfig mutableCopy];
self->_activeConfig = [activeConfig mutableCopy];
self->_defaultConfig = [defaultConfig mutableCopy];
dispatch_group_leave(self->_dispatch_group);
}];
[_DBManager loadMainWithBundleIdentifier:_bundleIdentifier
completionHandler:^(
BOOL success, NSDictionary *fetchedConfig, NSDictionary *activeConfig,
NSDictionary *defaultConfig, NSDictionary *rolloutMetadata) {
self->_fetchedConfig = [fetchedConfig mutableCopy];
self->_activeConfig = [activeConfig mutableCopy];
self->_defaultConfig = [defaultConfig mutableCopy];
self->_fetchedRolloutMetadata =
[rolloutMetadata[@RCNRolloutTableKeyFetchedMetadata] copy];
self->_activeRolloutMetadata =
[rolloutMetadata[@RCNRolloutTableKeyActiveMetadata] copy];
dispatch_group_leave(self->_dispatch_group);
}];

// TODO(karenzeng): Refactor personalization to be returned in loadMainWithBundleIdentifier above
dispatch_group_enter(_dispatch_group);
[_DBManager loadPersonalizationWithCompletionHandler:^(
BOOL success, NSDictionary *fetchedPersonalization,
NSDictionary *activePersonalization, NSDictionary *defaultConfig) {
self->_fetchedPersonalization = [fetchedPersonalization copy];
self->_activePersonalization = [activePersonalization copy];
dispatch_group_leave(self->_dispatch_group);
}];
[_DBManager
loadPersonalizationWithCompletionHandler:^(
BOOL success, NSDictionary *fetchedPersonalization, NSDictionary *activePersonalization,
NSDictionary *defaultConfig, NSDictionary *rolloutMetadata) {
self->_fetchedPersonalization = [fetchedPersonalization copy];
self->_activePersonalization = [activePersonalization copy];
dispatch_group_leave(self->_dispatch_group);
}];
}

/// Update the current config result to main table.
Expand Down
14 changes: 12 additions & 2 deletions FirebaseRemoteConfig/Sources/RCNConfigDBManager.h
Original file line number Diff line number Diff line change
Expand Up @@ -53,10 +53,12 @@ typedef void (^RCNDBCompletion)(BOOL success, NSDictionary *result);
/// @param fetchedConfig Return fetchedConfig loaded from DB
/// @param activeConfig Return activeConfig loaded from DB
/// @param defaultConfig Return defaultConfig loaded from DB
/// @param rolloutMetadata Return fetched and active RolloutMetadata loaded from DB
typedef void (^RCNDBLoadCompletion)(BOOL success,
NSDictionary *fetchedConfig,
NSDictionary *activeConfig,
NSDictionary *defaultConfig);
NSDictionary *defaultConfig,
NSDictionary *rolloutMetadata);

/// Returns the current version of the Remote Config database.
+ (NSString *)remoteConfigPathForDatabase;
Expand All @@ -78,7 +80,6 @@ typedef void (^RCNDBLoadCompletion)(BOOL success,
/// Load Personalization from table.
/// @param handler The callback when reading from DB is complete.
- (void)loadPersonalizationWithCompletionHandler:(RCNDBLoadCompletion)handler;

/// Insert a record in metadata table.
/// @param columnNameToValue The column name and its value to be inserted in metadata table.
/// @param handler The callback.
Expand Down Expand Up @@ -110,6 +111,15 @@ typedef void (^RCNDBLoadCompletion)(BOOL success,
/// Insert or update the data in Personalization config.
- (BOOL)insertOrUpdatePersonalizationConfig:(NSDictionary *)metadata fromSource:(RCNDBSource)source;

/// Insert rollout metadata in rollout table.
/// @param key Key indicating whether rollout metadata is fetched or active and defined in
/// RCNConfigDefines.h.
/// @param value The value that rollout metadata array.
/// @param handler The callback.
- (void)insertOrUpdateRolloutTableWithKey:(NSString *)key
value:(NSArray<NSDictionary *> *)value
completionHandler:(RCNDBCompletion)handler;

/// Clear the record of given namespace and package name
/// before updating the table.
- (void)deleteRecordFromMainTableWithNamespace:(NSString *)namespace_p
Expand Down
122 changes: 111 additions & 11 deletions FirebaseRemoteConfig/Sources/RCNConfigDBManager.m
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
#define RCNTableNameInternalMetadata "internal_metadata"
#define RCNTableNameExperiment "experiment"
#define RCNTableNamePersonalization "personalization"
#define RCNTableNameRollout "rollout"

static BOOL gIsNewDatabase;
/// SQLite file name in versions 0, 1 and 2.
Expand Down Expand Up @@ -284,11 +285,14 @@ - (BOOL)createTableSchema {
"create TABLE IF NOT EXISTS " RCNTableNamePersonalization
" (_id INTEGER PRIMARY KEY, key INTEGER, value BLOB)";

static const char *createTableRollout = "create TABLE IF NOT EXISTS " RCNTableNameRollout
" (_id INTEGER PRIMARY KEY, key TEXT, value BLOB)";

return [self executeQuery:createTableMain] && [self executeQuery:createTableMainActive] &&
[self executeQuery:createTableMainDefault] && [self executeQuery:createTableMetadata] &&
[self executeQuery:createTableInternalMetadata] &&
[self executeQuery:createTableExperiment] &&
[self executeQuery:createTablePersonalization];
[self executeQuery:createTablePersonalization] && [self executeQuery:createTableRollout];
}

- (void)removeDatabaseOnDatabaseQueueAtPath:(NSString *)path {
Expand Down Expand Up @@ -618,6 +622,52 @@ - (BOOL)insertOrUpdatePersonalizationConfig:(NSDictionary *)dataValue
return YES;
}

- (void)insertOrUpdateRolloutTableWithKey:(NSString *)key
value:(NSArray<NSDictionary *> *)value
completionHandler:(RCNDBCompletion)handler {
dispatch_async(_databaseOperationQueue, ^{
BOOL success = [self insertOrUpdateRolloutTableWithKey:key value:value];
if (handler) {
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
handler(success, nil);
});
}
});
}

- (BOOL)insertOrUpdateRolloutTableWithKey:(NSString *)key
value:(NSArray<NSDictionary *> *)arrayValue {
RCN_MUST_NOT_BE_MAIN_THREAD();
NSError *error;
NSData *dataValue = [NSJSONSerialization dataWithJSONObject:arrayValue
options:NSJSONWritingPrettyPrinted
error:&error];
const char *SQL =
"INSERT OR REPLACE INTO " RCNTableNameRollout
" (_id, key, value) values ((SELECT _id from " RCNTableNameRollout " WHERE key = ?), ?, ?)";
sqlite3_stmt *statement = [self prepareSQL:SQL];
if (!statement) {
return NO;
}
if (![self bindStringToStatement:statement index:1 string:key]) {
return [self logErrorWithSQL:SQL finalizeStatement:statement returnValue:NO];
}

if (![self bindStringToStatement:statement index:2 string:key]) {
return [self logErrorWithSQL:SQL finalizeStatement:statement returnValue:NO];
}

if (sqlite3_bind_blob(statement, 3, dataValue.bytes, (int)dataValue.length, NULL) != SQLITE_OK) {
return [self logErrorWithSQL:SQL finalizeStatement:statement returnValue:NO];
}

if (sqlite3_step(statement) != SQLITE_DONE) {
return [self logErrorWithSQL:SQL finalizeStatement:statement returnValue:NO];
}
sqlite3_finalize(statement);
return YES;
}

#pragma mark - update

- (void)updateMetadataWithOption:(RCNUpdateOption)option
Expand Down Expand Up @@ -852,7 +902,6 @@ - (void)loadExperimentWithCompletionHandler:(RCNDBCompletion)handler {
- (NSMutableArray<NSData *> *)loadExperimentTableFromKey:(NSString *)key {
RCN_MUST_NOT_BE_MAIN_THREAD();

NSMutableArray *results = [[NSMutableArray alloc] init];
const char *SQL = "SELECT value FROM " RCNTableNameExperiment " WHERE key = ?";
sqlite3_stmt *statement = [self prepareSQL:SQL];
if (!statement) {
Expand All @@ -861,12 +910,49 @@ - (void)loadExperimentWithCompletionHandler:(RCNDBCompletion)handler {

NSArray *params = @[ key ];
[self bindStringsToStatement:statement stringArray:params];
NSData *experimentData;
NSMutableArray *results = [self loadValuesFromStatement:statement];
return results;
}

- (NSArray<NSDictionary *> *)loadRolloutTableFromKey:(NSString *)key {
RCN_MUST_NOT_BE_MAIN_THREAD();
const char *SQL = "SELECT value FROM " RCNTableNameRollout " WHERE key = ?";
sqlite3_stmt *statement = [self prepareSQL:SQL];
if (!statement) {
return nil;
}
NSArray *params = @[ key ];
[self bindStringsToStatement:statement stringArray:params];
NSMutableArray *results = [self loadValuesFromStatement:statement];
// There should be only one entry in this table.
if (results.count != 1) {
return nil;
}
NSArray *rollout;
// Convert from NSData to NSArray
if (results[0]) {
NSError *error;
rollout = [NSJSONSerialization JSONObjectWithData:results[0] options:0 error:&error];
if (!rollout) {
FIRLogError(kFIRLoggerRemoteConfig, @"I-RCN000011",
@"Failed to convert NSData to NSAarry for Rollout Metadata with error %@.",
error);
}
}
if (!rollout) {
rollout = [[NSArray alloc] init];
}
return rollout;
}

- (NSMutableArray *)loadValuesFromStatement:(sqlite3_stmt *)statement {
NSMutableArray *results = [[NSMutableArray alloc] init];
NSData *value;
while (sqlite3_step(statement) == SQLITE_ROW) {
experimentData = [NSData dataWithBytes:(char *)sqlite3_column_blob(statement, 0)
length:sqlite3_column_bytes(statement, 0)];
if (experimentData) {
[results addObject:experimentData];
value = [NSData dataWithBytes:(char *)sqlite3_column_blob(statement, 0)
length:sqlite3_column_bytes(statement, 0)];
if (value) {
[results addObject:value];
}
}

Expand All @@ -880,7 +966,7 @@ - (void)loadPersonalizationWithCompletionHandler:(RCNDBLoadCompletion)handler {
RCNConfigDBManager *strongSelf = weakSelf;
if (!strongSelf) {
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
handler(NO, [NSMutableDictionary new], [NSMutableDictionary new], nil);
handler(NO, [NSMutableDictionary new], [NSMutableDictionary new], nil, nil);
});
return;
}
Expand Down Expand Up @@ -913,7 +999,7 @@ - (void)loadPersonalizationWithCompletionHandler:(RCNDBLoadCompletion)handler {

if (handler) {
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
handler(YES, fetchedPersonalization, activePersonalization, nil);
handler(YES, fetchedPersonalization, activePersonalization, nil, nil);
});
}
});
Expand Down Expand Up @@ -987,7 +1073,7 @@ - (void)loadMainWithBundleIdentifier:(NSString *)bundleIdentifier
RCNConfigDBManager *strongSelf = weakSelf;
if (!strongSelf) {
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
handler(NO, [NSDictionary new], [NSDictionary new], [NSDictionary new]);
handler(NO, [NSDictionary new], [NSDictionary new], [NSDictionary new], [NSDictionary new]);
});
return;
}
Expand All @@ -1000,12 +1086,26 @@ - (void)loadMainWithBundleIdentifier:(NSString *)bundleIdentifier
__block NSDictionary *defaultConfig =
[strongSelf loadMainTableWithBundleIdentifier:bundleIdentifier
fromSource:RCNDBSourceDefault];

__block NSArray<NSDictionary *> *fetchedRolloutMetadata =
[strongSelf loadRolloutTableFromKey:@RCNRolloutTableKeyFetchedMetadata];
__block NSArray<NSDictionary *> *activeRolloutMetadata =
[strongSelf loadRolloutTableFromKey:@RCNRolloutTableKeyActiveMetadata];

if (handler) {
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
fetchedConfig = fetchedConfig ? fetchedConfig : [[NSDictionary alloc] init];
activeConfig = activeConfig ? activeConfig : [[NSDictionary alloc] init];
defaultConfig = defaultConfig ? defaultConfig : [[NSDictionary alloc] init];
handler(YES, fetchedConfig, activeConfig, defaultConfig);
fetchedRolloutMetadata =
fetchedRolloutMetadata ? fetchedRolloutMetadata : [[NSArray alloc] init];
activeRolloutMetadata =
activeRolloutMetadata ? activeRolloutMetadata : [[NSArray alloc] init];
NSDictionary *rolloutMetadata = @{
@RCNRolloutTableKeyActiveMetadata : [activeRolloutMetadata copy],
@RCNRolloutTableKeyFetchedMetadata : [fetchedRolloutMetadata copy]
};
handler(YES, fetchedConfig, activeConfig, defaultConfig, rolloutMetadata);
});
}
});
Expand Down
2 changes: 2 additions & 0 deletions FirebaseRemoteConfig/Sources/RCNConfigDefines.h
Original file line number Diff line number Diff line change
Expand Up @@ -31,5 +31,7 @@
#define RCNExperimentTableKeyPayload "experiment_payload"
#define RCNExperimentTableKeyMetadata "experiment_metadata"
#define RCNExperimentTableKeyActivePayload "experiment_active_payload"
#define RCNRolloutTableKeyActiveMetadata "active_rollout_metadata"
#define RCNRolloutTableKeyFetchedMetadata "fetched_rollout_metadata"

#endif
4 changes: 2 additions & 2 deletions FirebaseRemoteConfig/Tests/Unit/RCNConfigContentTest.m
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ - (void)loadMainWithBundleIdentifier:(NSString *)bundleIdentifier
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(justSmallDelay * NSEC_PER_SEC)),
dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
self.isLoadMainCompleted = YES;
handler(YES, nil, nil, nil);
handler(YES, nil, nil, nil, nil);
});
}
- (void)loadPersonalizationWithCompletionHandler:(RCNDBLoadCompletion)handler {
Expand All @@ -53,7 +53,7 @@ - (void)loadPersonalizationWithCompletionHandler:(RCNDBLoadCompletion)handler {
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(justOtherSmallDelay * NSEC_PER_SEC)),
dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
self.isLoadPersonalizationCompleted = YES;
handler(YES, nil, nil, nil);
handler(YES, nil, nil, nil, nil);
});
}
@end
Expand Down
Loading

0 comments on commit 822fcdf

Please sign in to comment.