Skip to content

Commit 4ad141d

Browse files
mvayngribbestander
authored andcommitted
multiGet breaking test and fix
Summary: the flush + optimized multiGet result in an obscure bug that results when two multiGet requests with overlapping key sets get issued. The result array for both requests ends up bigger than the key array (because it has duplicates) Closes react/react-native#5514 Reviewed By: svcscm Differential Revision: D2908264 Pulled By: nicklockwood fb-gh-sync-id: 60be1bce4acfc47083e4ae28bb8b63f9dfa56039
1 parent 6cce770 commit 4ad141d

2 files changed

Lines changed: 31 additions & 5 deletions

File tree

IntegrationTests/AsyncStorageTest.js

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -142,12 +142,30 @@ function testMerge() {
142142
expectAsyncNoError('testMerge/setItem', err3);
143143
expectEqual(JSON.parse(result), VAL_MERGE_EXPECT, 'testMerge');
144144
updateMessage('objects deeply merged\nDone!');
145+
runTestCase('multi set and get', testOptimizedMultiGet);
146+
});
147+
});
148+
});
149+
}
150+
151+
function testOptimizedMultiGet() {
152+
let batch = [[KEY_1, VAL_1], [KEY_2, VAL_2]];
153+
let keys = batch.map(([key, value]) => key);
154+
AsyncStorage.multiSet(batch, (err1) => {
155+
// yes, twice on purpose
156+
;[1, 2].forEach((i) => {
157+
expectAsyncNoError(`${i} testOptimizedMultiGet/multiSet`, err1);
158+
AsyncStorage.multiGet(keys, (err2, result) => {
159+
expectAsyncNoError(`${i} testOptimizedMultiGet/multiGet`, err2);
160+
expectEqual(result, batch, `${i} testOptimizedMultiGet multiGet`);
161+
updateMessage('multiGet([key_1, key_2]) correctly returned ' + JSON.stringify(result));
145162
done();
146163
});
147164
});
148165
});
149166
}
150167

168+
151169
var AsyncStorageTest = React.createClass({
152170
getInitialState() {
153171
return {

Libraries/Storage/AsyncStorage.js

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -180,14 +180,16 @@ var AsyncStorage = {
180180
// Even though the runtime complexity of this is theoretically worse vs if we used a map,
181181
// it's much, much faster in practice for the data sets we deal with (we avoid
182182
// allocating result pair arrays). This was heavily benchmarked.
183+
//
184+
// Is there a way to avoid using the map but fix the bug in this breaking test?
185+
// https://github.com/facebook/react-native/commit/8dd8ad76579d7feef34c014d387bf02065692264
186+
let map = {};
187+
result.forEach(([key, value]) => map[key] = value);
183188
const reqLength = getRequests.length;
184189
for (let i = 0; i < reqLength; i++) {
185190
const request = getRequests[i];
186191
const requestKeys = request.keys;
187-
var requestResult = result.filter(function(resultPair) {
188-
return requestKeys.indexOf(resultPair[0]) !== -1;
189-
});
190-
192+
let requestResult = requestKeys.map(key => [key, map[key]]);
191193
request.callback && request.callback(null, requestResult);
192194
request.resolve && request.resolve(requestResult);
193195
}
@@ -214,6 +216,7 @@ var AsyncStorage = {
214216
var getRequest = {
215217
keys: keys,
216218
callback: callback,
219+
// do we need this?
217220
keyIndex: this._getKeys.length,
218221
resolve: null,
219222
reject: null,
@@ -225,7 +228,12 @@ var AsyncStorage = {
225228
});
226229

227230
this._getRequests.push(getRequest);
228-
this._getKeys.push.apply(this._getKeys, keys);
231+
// avoid fetching duplicates
232+
keys.forEach(key => {
233+
if (this._getKeys.indexOf(key) === -1) {
234+
this._getKeys.push(key);
235+
}
236+
});
229237

230238
return promiseResult;
231239
},

0 commit comments

Comments
 (0)