Skip to content

Commit 822fcdf

Browse files
authored
[Rollouts] Implement insert and load logic for rollout metadata in RC (#12262)
1 parent 50b72c1 commit 822fcdf

File tree

6 files changed

+303
-47
lines changed

6 files changed

+303
-47
lines changed

FirebaseRemoteConfig/Sources/RCNConfigContent.m

Lines changed: 27 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,10 @@ @implementation RCNConfigContent {
3838
/// Pending Personalization metadata that is latest data from server that might or might not be
3939
/// applied.
4040
NSDictionary *_fetchedPersonalization;
41+
/// Active Rollout metadata that is currently used.
42+
NSArray<NSDictionary *> *_activeRolloutMetadata;
43+
/// Pending Rollout metadata that is latest data from server that might or might not be applied.
44+
NSArray<NSDictionary *> *_fetchedRolloutMetadata;
4145
/// DBManager
4246
RCNConfigDBManager *_DBManager;
4347
/// Current bundle identifier;
@@ -80,6 +84,8 @@ - (instancetype)initWithDBManager:(RCNConfigDBManager *)DBManager {
8084
_defaultConfig = [[NSMutableDictionary alloc] init];
8185
_activePersonalization = [[NSDictionary alloc] init];
8286
_fetchedPersonalization = [[NSDictionary alloc] init];
87+
_activeRolloutMetadata = [[NSArray alloc] init];
88+
_fetchedRolloutMetadata = [[NSArray alloc] init];
8389
_bundleIdentifier = [[NSBundle mainBundle] bundleIdentifier];
8490
if (!_bundleIdentifier) {
8591
FIRLogNotice(kFIRLoggerRemoteConfig, @"I-RCN000038",
@@ -115,25 +121,30 @@ - (void)loadConfigFromMainTable {
115121
_isDatabaseLoadAlreadyInitiated = true;
116122

117123
dispatch_group_enter(_dispatch_group);
118-
[_DBManager
119-
loadMainWithBundleIdentifier:_bundleIdentifier
120-
completionHandler:^(BOOL success, NSDictionary *fetchedConfig,
121-
NSDictionary *activeConfig, NSDictionary *defaultConfig) {
122-
self->_fetchedConfig = [fetchedConfig mutableCopy];
123-
self->_activeConfig = [activeConfig mutableCopy];
124-
self->_defaultConfig = [defaultConfig mutableCopy];
125-
dispatch_group_leave(self->_dispatch_group);
126-
}];
124+
[_DBManager loadMainWithBundleIdentifier:_bundleIdentifier
125+
completionHandler:^(
126+
BOOL success, NSDictionary *fetchedConfig, NSDictionary *activeConfig,
127+
NSDictionary *defaultConfig, NSDictionary *rolloutMetadata) {
128+
self->_fetchedConfig = [fetchedConfig mutableCopy];
129+
self->_activeConfig = [activeConfig mutableCopy];
130+
self->_defaultConfig = [defaultConfig mutableCopy];
131+
self->_fetchedRolloutMetadata =
132+
[rolloutMetadata[@RCNRolloutTableKeyFetchedMetadata] copy];
133+
self->_activeRolloutMetadata =
134+
[rolloutMetadata[@RCNRolloutTableKeyActiveMetadata] copy];
135+
dispatch_group_leave(self->_dispatch_group);
136+
}];
127137

128138
// TODO(karenzeng): Refactor personalization to be returned in loadMainWithBundleIdentifier above
129139
dispatch_group_enter(_dispatch_group);
130-
[_DBManager loadPersonalizationWithCompletionHandler:^(
131-
BOOL success, NSDictionary *fetchedPersonalization,
132-
NSDictionary *activePersonalization, NSDictionary *defaultConfig) {
133-
self->_fetchedPersonalization = [fetchedPersonalization copy];
134-
self->_activePersonalization = [activePersonalization copy];
135-
dispatch_group_leave(self->_dispatch_group);
136-
}];
140+
[_DBManager
141+
loadPersonalizationWithCompletionHandler:^(
142+
BOOL success, NSDictionary *fetchedPersonalization, NSDictionary *activePersonalization,
143+
NSDictionary *defaultConfig, NSDictionary *rolloutMetadata) {
144+
self->_fetchedPersonalization = [fetchedPersonalization copy];
145+
self->_activePersonalization = [activePersonalization copy];
146+
dispatch_group_leave(self->_dispatch_group);
147+
}];
137148
}
138149

139150
/// Update the current config result to main table.

FirebaseRemoteConfig/Sources/RCNConfigDBManager.h

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -53,10 +53,12 @@ typedef void (^RCNDBCompletion)(BOOL success, NSDictionary *result);
5353
/// @param fetchedConfig Return fetchedConfig loaded from DB
5454
/// @param activeConfig Return activeConfig loaded from DB
5555
/// @param defaultConfig Return defaultConfig loaded from DB
56+
/// @param rolloutMetadata Return fetched and active RolloutMetadata loaded from DB
5657
typedef void (^RCNDBLoadCompletion)(BOOL success,
5758
NSDictionary *fetchedConfig,
5859
NSDictionary *activeConfig,
59-
NSDictionary *defaultConfig);
60+
NSDictionary *defaultConfig,
61+
NSDictionary *rolloutMetadata);
6062

6163
/// Returns the current version of the Remote Config database.
6264
+ (NSString *)remoteConfigPathForDatabase;
@@ -78,7 +80,6 @@ typedef void (^RCNDBLoadCompletion)(BOOL success,
7880
/// Load Personalization from table.
7981
/// @param handler The callback when reading from DB is complete.
8082
- (void)loadPersonalizationWithCompletionHandler:(RCNDBLoadCompletion)handler;
81-
8283
/// Insert a record in metadata table.
8384
/// @param columnNameToValue The column name and its value to be inserted in metadata table.
8485
/// @param handler The callback.
@@ -110,6 +111,15 @@ typedef void (^RCNDBLoadCompletion)(BOOL success,
110111
/// Insert or update the data in Personalization config.
111112
- (BOOL)insertOrUpdatePersonalizationConfig:(NSDictionary *)metadata fromSource:(RCNDBSource)source;
112113

114+
/// Insert rollout metadata in rollout table.
115+
/// @param key Key indicating whether rollout metadata is fetched or active and defined in
116+
/// RCNConfigDefines.h.
117+
/// @param value The value that rollout metadata array.
118+
/// @param handler The callback.
119+
- (void)insertOrUpdateRolloutTableWithKey:(NSString *)key
120+
value:(NSArray<NSDictionary *> *)value
121+
completionHandler:(RCNDBCompletion)handler;
122+
113123
/// Clear the record of given namespace and package name
114124
/// before updating the table.
115125
- (void)deleteRecordFromMainTableWithNamespace:(NSString *)namespace_p

FirebaseRemoteConfig/Sources/RCNConfigDBManager.m

Lines changed: 111 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131
#define RCNTableNameInternalMetadata "internal_metadata"
3232
#define RCNTableNameExperiment "experiment"
3333
#define RCNTableNamePersonalization "personalization"
34+
#define RCNTableNameRollout "rollout"
3435

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

288+
static const char *createTableRollout = "create TABLE IF NOT EXISTS " RCNTableNameRollout
289+
" (_id INTEGER PRIMARY KEY, key TEXT, value BLOB)";
290+
287291
return [self executeQuery:createTableMain] && [self executeQuery:createTableMainActive] &&
288292
[self executeQuery:createTableMainDefault] && [self executeQuery:createTableMetadata] &&
289293
[self executeQuery:createTableInternalMetadata] &&
290294
[self executeQuery:createTableExperiment] &&
291-
[self executeQuery:createTablePersonalization];
295+
[self executeQuery:createTablePersonalization] && [self executeQuery:createTableRollout];
292296
}
293297

294298
- (void)removeDatabaseOnDatabaseQueueAtPath:(NSString *)path {
@@ -618,6 +622,52 @@ - (BOOL)insertOrUpdatePersonalizationConfig:(NSDictionary *)dataValue
618622
return YES;
619623
}
620624

625+
- (void)insertOrUpdateRolloutTableWithKey:(NSString *)key
626+
value:(NSArray<NSDictionary *> *)value
627+
completionHandler:(RCNDBCompletion)handler {
628+
dispatch_async(_databaseOperationQueue, ^{
629+
BOOL success = [self insertOrUpdateRolloutTableWithKey:key value:value];
630+
if (handler) {
631+
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
632+
handler(success, nil);
633+
});
634+
}
635+
});
636+
}
637+
638+
- (BOOL)insertOrUpdateRolloutTableWithKey:(NSString *)key
639+
value:(NSArray<NSDictionary *> *)arrayValue {
640+
RCN_MUST_NOT_BE_MAIN_THREAD();
641+
NSError *error;
642+
NSData *dataValue = [NSJSONSerialization dataWithJSONObject:arrayValue
643+
options:NSJSONWritingPrettyPrinted
644+
error:&error];
645+
const char *SQL =
646+
"INSERT OR REPLACE INTO " RCNTableNameRollout
647+
" (_id, key, value) values ((SELECT _id from " RCNTableNameRollout " WHERE key = ?), ?, ?)";
648+
sqlite3_stmt *statement = [self prepareSQL:SQL];
649+
if (!statement) {
650+
return NO;
651+
}
652+
if (![self bindStringToStatement:statement index:1 string:key]) {
653+
return [self logErrorWithSQL:SQL finalizeStatement:statement returnValue:NO];
654+
}
655+
656+
if (![self bindStringToStatement:statement index:2 string:key]) {
657+
return [self logErrorWithSQL:SQL finalizeStatement:statement returnValue:NO];
658+
}
659+
660+
if (sqlite3_bind_blob(statement, 3, dataValue.bytes, (int)dataValue.length, NULL) != SQLITE_OK) {
661+
return [self logErrorWithSQL:SQL finalizeStatement:statement returnValue:NO];
662+
}
663+
664+
if (sqlite3_step(statement) != SQLITE_DONE) {
665+
return [self logErrorWithSQL:SQL finalizeStatement:statement returnValue:NO];
666+
}
667+
sqlite3_finalize(statement);
668+
return YES;
669+
}
670+
621671
#pragma mark - update
622672

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

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

862911
NSArray *params = @[ key ];
863912
[self bindStringsToStatement:statement stringArray:params];
864-
NSData *experimentData;
913+
NSMutableArray *results = [self loadValuesFromStatement:statement];
914+
return results;
915+
}
916+
917+
- (NSArray<NSDictionary *> *)loadRolloutTableFromKey:(NSString *)key {
918+
RCN_MUST_NOT_BE_MAIN_THREAD();
919+
const char *SQL = "SELECT value FROM " RCNTableNameRollout " WHERE key = ?";
920+
sqlite3_stmt *statement = [self prepareSQL:SQL];
921+
if (!statement) {
922+
return nil;
923+
}
924+
NSArray *params = @[ key ];
925+
[self bindStringsToStatement:statement stringArray:params];
926+
NSMutableArray *results = [self loadValuesFromStatement:statement];
927+
// There should be only one entry in this table.
928+
if (results.count != 1) {
929+
return nil;
930+
}
931+
NSArray *rollout;
932+
// Convert from NSData to NSArray
933+
if (results[0]) {
934+
NSError *error;
935+
rollout = [NSJSONSerialization JSONObjectWithData:results[0] options:0 error:&error];
936+
if (!rollout) {
937+
FIRLogError(kFIRLoggerRemoteConfig, @"I-RCN000011",
938+
@"Failed to convert NSData to NSAarry for Rollout Metadata with error %@.",
939+
error);
940+
}
941+
}
942+
if (!rollout) {
943+
rollout = [[NSArray alloc] init];
944+
}
945+
return rollout;
946+
}
947+
948+
- (NSMutableArray *)loadValuesFromStatement:(sqlite3_stmt *)statement {
949+
NSMutableArray *results = [[NSMutableArray alloc] init];
950+
NSData *value;
865951
while (sqlite3_step(statement) == SQLITE_ROW) {
866-
experimentData = [NSData dataWithBytes:(char *)sqlite3_column_blob(statement, 0)
867-
length:sqlite3_column_bytes(statement, 0)];
868-
if (experimentData) {
869-
[results addObject:experimentData];
952+
value = [NSData dataWithBytes:(char *)sqlite3_column_blob(statement, 0)
953+
length:sqlite3_column_bytes(statement, 0)];
954+
if (value) {
955+
[results addObject:value];
870956
}
871957
}
872958

@@ -880,7 +966,7 @@ - (void)loadPersonalizationWithCompletionHandler:(RCNDBLoadCompletion)handler {
880966
RCNConfigDBManager *strongSelf = weakSelf;
881967
if (!strongSelf) {
882968
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
883-
handler(NO, [NSMutableDictionary new], [NSMutableDictionary new], nil);
969+
handler(NO, [NSMutableDictionary new], [NSMutableDictionary new], nil, nil);
884970
});
885971
return;
886972
}
@@ -913,7 +999,7 @@ - (void)loadPersonalizationWithCompletionHandler:(RCNDBLoadCompletion)handler {
913999

9141000
if (handler) {
9151001
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
916-
handler(YES, fetchedPersonalization, activePersonalization, nil);
1002+
handler(YES, fetchedPersonalization, activePersonalization, nil, nil);
9171003
});
9181004
}
9191005
});
@@ -987,7 +1073,7 @@ - (void)loadMainWithBundleIdentifier:(NSString *)bundleIdentifier
9871073
RCNConfigDBManager *strongSelf = weakSelf;
9881074
if (!strongSelf) {
9891075
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
990-
handler(NO, [NSDictionary new], [NSDictionary new], [NSDictionary new]);
1076+
handler(NO, [NSDictionary new], [NSDictionary new], [NSDictionary new], [NSDictionary new]);
9911077
});
9921078
return;
9931079
}
@@ -1000,12 +1086,26 @@ - (void)loadMainWithBundleIdentifier:(NSString *)bundleIdentifier
10001086
__block NSDictionary *defaultConfig =
10011087
[strongSelf loadMainTableWithBundleIdentifier:bundleIdentifier
10021088
fromSource:RCNDBSourceDefault];
1089+
1090+
__block NSArray<NSDictionary *> *fetchedRolloutMetadata =
1091+
[strongSelf loadRolloutTableFromKey:@RCNRolloutTableKeyFetchedMetadata];
1092+
__block NSArray<NSDictionary *> *activeRolloutMetadata =
1093+
[strongSelf loadRolloutTableFromKey:@RCNRolloutTableKeyActiveMetadata];
1094+
10031095
if (handler) {
10041096
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
10051097
fetchedConfig = fetchedConfig ? fetchedConfig : [[NSDictionary alloc] init];
10061098
activeConfig = activeConfig ? activeConfig : [[NSDictionary alloc] init];
10071099
defaultConfig = defaultConfig ? defaultConfig : [[NSDictionary alloc] init];
1008-
handler(YES, fetchedConfig, activeConfig, defaultConfig);
1100+
fetchedRolloutMetadata =
1101+
fetchedRolloutMetadata ? fetchedRolloutMetadata : [[NSArray alloc] init];
1102+
activeRolloutMetadata =
1103+
activeRolloutMetadata ? activeRolloutMetadata : [[NSArray alloc] init];
1104+
NSDictionary *rolloutMetadata = @{
1105+
@RCNRolloutTableKeyActiveMetadata : [activeRolloutMetadata copy],
1106+
@RCNRolloutTableKeyFetchedMetadata : [fetchedRolloutMetadata copy]
1107+
};
1108+
handler(YES, fetchedConfig, activeConfig, defaultConfig, rolloutMetadata);
10091109
});
10101110
}
10111111
});

FirebaseRemoteConfig/Sources/RCNConfigDefines.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,5 +31,7 @@
3131
#define RCNExperimentTableKeyPayload "experiment_payload"
3232
#define RCNExperimentTableKeyMetadata "experiment_metadata"
3333
#define RCNExperimentTableKeyActivePayload "experiment_active_payload"
34+
#define RCNRolloutTableKeyActiveMetadata "active_rollout_metadata"
35+
#define RCNRolloutTableKeyFetchedMetadata "fetched_rollout_metadata"
3436

3537
#endif

FirebaseRemoteConfig/Tests/Unit/RCNConfigContentTest.m

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ - (void)loadMainWithBundleIdentifier:(NSString *)bundleIdentifier
4444
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(justSmallDelay * NSEC_PER_SEC)),
4545
dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
4646
self.isLoadMainCompleted = YES;
47-
handler(YES, nil, nil, nil);
47+
handler(YES, nil, nil, nil, nil);
4848
});
4949
}
5050
- (void)loadPersonalizationWithCompletionHandler:(RCNDBLoadCompletion)handler {
@@ -53,7 +53,7 @@ - (void)loadPersonalizationWithCompletionHandler:(RCNDBLoadCompletion)handler {
5353
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(justOtherSmallDelay * NSEC_PER_SEC)),
5454
dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
5555
self.isLoadPersonalizationCompleted = YES;
56-
handler(YES, nil, nil, nil);
56+
handler(YES, nil, nil, nil, nil);
5757
});
5858
}
5959
@end

0 commit comments

Comments
 (0)