Permalink
Browse files

Hooking Up Incremental Read

Summary: Adding `readRelayDiskCache`, which uses the recently added `findRelayQueryLeaves` to figure
which nodes we need to read out from disk cache. It's essentially a BFT of the queries nodes and reading nodes out from disk recursively as they are needed.
`readRelayDiskCache` will keep track of pending node reads and pending rootcalls.
The pending maps ensure we don't waste time trying to read the same node from cache multiple times.
I also added a bunch of `hasFailed` to ensure we short circuit as fast as possible if we realized we are missing
data to avoid doing extra work.

Reviewed By: josephsavona

Differential Revision: D2548754

fb-gh-sync-id: 75d30bf4331dcf217b8396357dcdf39257b8b8e9
  • Loading branch information...
yuzhi authored and facebook-github-bot-7 committed Oct 23, 2015
1 parent 1622cea commit cc7c0e5b16999e045937a5f75a4ef0fe05b4695e
@@ -30,6 +30,7 @@ var flattenSplitRelayQueries = require('flattenSplitRelayQueries');
var forEachObject = require('forEachObject');
var generateForceIndex = require('generateForceIndex');
var invariant = require('invariant');
var mapObject = require('mapObject');
var resolveImmediate = require('resolveImmediate');
var someObject = require('someObject');
var splitDeferredRelayQueries = require('splitDeferredRelayQueries');
@@ -276,10 +277,24 @@ function runQueries(
setReadyState({ready: true});
} else {
setReadyState({ready: false});
storeData.runWithDiskCache(() => {
if (hasItems(remainingRequiredFetchMap)) {
resolveImmediate(() => {
if (storeData.hasCacheManager()) {
var requiredQueryMap = mapObject(
remainingRequiredFetchMap,
value => value.getQuery()
);
storeData.readFromDiskCache(requiredQueryMap, {
onSuccess: () => {
if (hasItems(remainingRequiredFetchMap)) {
setReadyState({ready: true, stale: true});
}
},
});
} else {
if (everyObject(remainingRequiredFetchMap, canResolve)) {
setReadyState({ready: true, stale: true});
if (hasItems(remainingRequiredFetchMap)) {
setReadyState({ready: true, stale: true});
}
}
}
});
@@ -139,8 +139,6 @@ describe('GraphQLQueryRunner', () => {
it('is not ready if required data is being fetched', () => {
diffRelayQuery.mockImplementation(query => [query]);
checkRelayQueryData.mockImplementation(() => false);
RelayStoreData.prototype.runWithDiskCache =
jest.genMockFunction().mockImplementation(resolveImmediate);
mockSplitDeferredQueries();
GraphQLQueryRunner.run(mockQuerySet, mockCallback);
@@ -360,6 +358,25 @@ describe('GraphQLQueryRunner', () => {
]);
});
it('is ready if required data is in disk cache', () => {
diffRelayQuery.mockImplementation(query => [query]);
RelayStoreData.prototype.hasCacheManager =
jest.genMockFunction().mockImplementation(() => true);
RelayStoreData.prototype.readFromDiskCache =
jest.genMockFunction().mockImplementation((queries, callback) => {
callback.onSuccess();
});
mockSplitDeferredQueries();
GraphQLQueryRunner.run(mockQuerySet, mockCallback);
jest.runAllTimers();
expect(mockCallback.mock.calls).toEqual([
[{aborted: false, done: false, error: null, ready: false, stale: false}],
[{aborted: false, done: false, error: null, ready: true, stale: true}],
]);
});
it('adds query on `forceFetch` even if there are no diff queries', () => {
diffRelayQuery.mockImplementation(query => []);
mockSplitDeferredQueries();
@@ -376,10 +393,7 @@ describe('GraphQLQueryRunner', () => {
it('is completely ready on `forceFetch` when all data is available', () => {
diffRelayQuery.mockImplementation(() => []);
checkRelayQueryData.mockImplementation(() => true);
RelayStoreData.prototype.runWithDiskCache =
jest.genMockFunction().mockImplementation(resolveImmediate);
mockSplitDeferredQueries();
var singleMockQuery = {foo: mockQuerySet.foo};
GraphQLQueryRunner.forceFetch(singleMockQuery, mockCallback);
jest.runAllTimers();
@@ -23,6 +23,7 @@ import type {
DataID,
NodeRangeMap,
Records,
RelayQuerySet,
RootCallMap,
UpdateOptions
} from 'RelayInternalTypes';
@@ -33,11 +34,12 @@ var RelayQueryTracker = require('RelayQueryTracker');
var RelayQueryWriter = require('RelayQueryWriter');
var RelayRecordStore = require('RelayRecordStore');
var RelayStoreGarbageCollector = require('RelayStoreGarbageCollector');
import type {CacheManager} from 'RelayTypes';
import type {CacheManager, CacheReadCallbacks} from 'RelayTypes';
var forEachObject = require('forEachObject');
var invariant = require('invariant');
var generateForceIndex = require('generateForceIndex');
var readRelayDiskCache = require('readRelayDiskCache');
var refragmentRelayQuery = require('refragmentRelayQuery');
var resolveImmediate = require('resolveImmediate');
var warning = require('warning');
@@ -179,6 +181,34 @@ class RelayStoreData {
}
}
hasCacheManager(): boolean {
return !!this._cacheManager;
}
/**
* Reads data for queries incrementally from disk cache.
* It calls onSuccess when all the data has been loaded into memory.
* It calls onFailure when some data is unabled to be satisfied from disk.
*/
readFromDiskCache(
queries: RelayQuerySet,
callbacks: CacheReadCallbacks
): void {
invariant(
this._cacheManager,
'RelayStoreData: `readFromDiskCache` should only be called when cache ' +
'manager is available.'
);
readRelayDiskCache(
queries,
this._queuedStore,
this._cachedRecords,
this._cachedRootCalls,
this._cacheManager,
callbacks
);
}
/**
* Write the results of a query into the base record store.
*/
@@ -0,0 +1,14 @@
/**
* Copyright 2013-2015, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*/
'use strict';
module.exports = jest.genMockFunction().mockImplementation(
require.requireActual('readRelayDiskCache')
);
Oops, something went wrong.

0 comments on commit cc7c0e5

Please sign in to comment.