src/store/RelayGarbageCollection.js
/**
* Copyright (c) 2013-present, 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.
*
* @providesModule RelayGarbageCollection
* @flow
*/
'use strict';
import type {DataID} from 'RelayInternalTypes';
const RelayStore = require('RelayStore');
const invariant = require('invariant');
const warning = require('warning');
let _stepLength = -1; // collect in a single pass by default
/**
* Public API for controlling garbage collection of `RelayStoreData`.
*
* Provides methods to control the garbage collection of records in
* `RelayStoreData`.
*/
const RelayGarbageCollection = {
/**
* Initializes garbage collection: must be called before any records are
* fetched. When records are collected after calls to `scheduleCollection` or
* `scheduleCollectionFromNode`, records are collected in steps, with a
* maximum of `stepLength` records traversed in a step. Steps are scheduled
* via the `RelayStore` task queue (using the injected scheduler).
*/
initialize(stepLength: number): void {
invariant(
stepLength > 0,
'RelayGarbageCollection: step length must be greater than zero, got ' +
'`%s`.',
stepLength
);
_stepLength = stepLength;
RelayStore.getStoreData().initializeGarbageCollector(scheduler);
},
/**
* Collects any un-referenced records in the store.
*/
scheduleCollection(): void {
const garbageCollector = RelayStore.getStoreData().getGarbageCollector();
if (garbageCollector) {
garbageCollector.collect();
}
},
/**
* Collects any un-referenced records reachable from the given record via
* graph traversal of fields.
*
* NOTE: If the given record is still referenced, no records are collected.
*/
scheduleCollectionFromNode(dataID: DataID): void {
const garbageCollector = RelayStore.getStoreData().getGarbageCollector();
if (garbageCollector) {
garbageCollector.collectFromNode(dataID);
}
},
};
function scheduler(run: () => boolean): void {
const pendingQueryTracker =
RelayStore.getStoreData().getPendingQueryTracker();
const runIteration = () => {
// TODO: #9366746: integrate RelayRenderer/Container with GC hold
warning(
!pendingQueryTracker.hasPendingQueries(),
'RelayGarbageCollection: GC is executing during a fetch, but the ' +
'pending query may rely on data that is collected.'
);
let iterations = 0;
let hasNext = true;
while (hasNext && (_stepLength < 0 || iterations < _stepLength)) {
hasNext = run();
iterations++;
}
// This is effectively a (possibly async) `while` loop
if (hasNext) {
enqueue(runIteration);
}
};
enqueue(runIteration);
}
function enqueue(fn: () => void): void {
RelayStore.getStoreData().getTaskQueue().enqueue(fn);
}
module.exports = RelayGarbageCollection;