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 https://github.com/facebook/react-native/pull/5514

Reviewed By: svcscm

Differential Revision: D2908264

Pulled By: nicklockwood

fb-gh-sync-id: 60be1bce4acfc47083e4ae28bb8b63f9dfa56039
This commit is contained in:
Mark Vayngrib 2016-02-05 16:44:44 -08:00 committed by facebook-github-bot-0
parent 375abc3f06
commit 52755fdde2
2 changed files with 31 additions and 5 deletions

View File

@ -142,12 +142,30 @@ function testMerge() {
expectAsyncNoError('testMerge/setItem', err3); expectAsyncNoError('testMerge/setItem', err3);
expectEqual(JSON.parse(result), VAL_MERGE_EXPECT, 'testMerge'); expectEqual(JSON.parse(result), VAL_MERGE_EXPECT, 'testMerge');
updateMessage('objects deeply merged\nDone!'); updateMessage('objects deeply merged\nDone!');
runTestCase('multi set and get', testOptimizedMultiGet);
});
});
});
}
function testOptimizedMultiGet() {
let batch = [[KEY_1, VAL_1], [KEY_2, VAL_2]];
let keys = batch.map(([key, value]) => key);
AsyncStorage.multiSet(batch, (err1) => {
// yes, twice on purpose
;[1, 2].forEach((i) => {
expectAsyncNoError(`${i} testOptimizedMultiGet/multiSet`, err1);
AsyncStorage.multiGet(keys, (err2, result) => {
expectAsyncNoError(`${i} testOptimizedMultiGet/multiGet`, err2);
expectEqual(result, batch, `${i} testOptimizedMultiGet multiGet`);
updateMessage('multiGet([key_1, key_2]) correctly returned ' + JSON.stringify(result));
done(); done();
}); });
}); });
}); });
} }
var AsyncStorageTest = React.createClass({ var AsyncStorageTest = React.createClass({
getInitialState() { getInitialState() {
return { return {

View File

@ -180,14 +180,16 @@ var AsyncStorage = {
// Even though the runtime complexity of this is theoretically worse vs if we used a map, // Even though the runtime complexity of this is theoretically worse vs if we used a map,
// it's much, much faster in practice for the data sets we deal with (we avoid // it's much, much faster in practice for the data sets we deal with (we avoid
// allocating result pair arrays). This was heavily benchmarked. // allocating result pair arrays). This was heavily benchmarked.
//
// Is there a way to avoid using the map but fix the bug in this breaking test?
// https://github.com/facebook/react-native/commit/8dd8ad76579d7feef34c014d387bf02065692264
let map = {};
result.forEach(([key, value]) => map[key] = value);
const reqLength = getRequests.length; const reqLength = getRequests.length;
for (let i = 0; i < reqLength; i++) { for (let i = 0; i < reqLength; i++) {
const request = getRequests[i]; const request = getRequests[i];
const requestKeys = request.keys; const requestKeys = request.keys;
var requestResult = result.filter(function(resultPair) { let requestResult = requestKeys.map(key => [key, map[key]]);
return requestKeys.indexOf(resultPair[0]) !== -1;
});
request.callback && request.callback(null, requestResult); request.callback && request.callback(null, requestResult);
request.resolve && request.resolve(requestResult); request.resolve && request.resolve(requestResult);
} }
@ -214,6 +216,7 @@ var AsyncStorage = {
var getRequest = { var getRequest = {
keys: keys, keys: keys,
callback: callback, callback: callback,
// do we need this?
keyIndex: this._getKeys.length, keyIndex: this._getKeys.length,
resolve: null, resolve: null,
reject: null, reject: null,
@ -225,7 +228,12 @@ var AsyncStorage = {
}); });
this._getRequests.push(getRequest); this._getRequests.push(getRequest);
this._getKeys.push.apply(this._getKeys, keys); // avoid fetching duplicates
keys.forEach(key => {
if (this._getKeys.indexOf(key) === -1) {
this._getKeys.push(key);
}
});
return promiseResult; return promiseResult;
}, },