Skip to content

Commit

Permalink
Merge pull request #4868 from emberjs/simplifiy-record-array-managers…
Browse files Browse the repository at this point in the history
…-internal-storage

[PERF] make filtered/liveRecord array storage a simple Object.create(…
  • Loading branch information
stefanpenner authored Mar 16, 2017
2 parents 42d3c08 + 96c147b commit 89588f5
Show file tree
Hide file tree
Showing 2 changed files with 39 additions and 31 deletions.
66 changes: 37 additions & 29 deletions addon/-private/system/record-array-manager.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@ import { assert } from 'ember-data/-private/debug';

const {
get,
MapWithDefault,
run: emberRun
} = Ember;

Expand All @@ -25,6 +24,7 @@ const {
createFilteredRecordArray,
createRecordArray,
liveRecordArrayFor,
filteredRecordArraysFor,
populateLiveRecordArray,
recordDidChange,
registerFilteredRecordArray,
Expand All @@ -40,6 +40,7 @@ const {
'createFilteredRecordArray',
'createRecordArray',
'liveRecordArrayFor',
'filteredRecordArraysFor',
'populateLiveRecordArray',
'recordDidChange',
'registerFilteredRecordArray',
Expand All @@ -59,17 +60,8 @@ export default class RecordArrayManager {
this.store = options.store;
this.isDestroying = false;
this.isDestroyed = false;
this.filteredRecordArrays = MapWithDefault.create({
defaultValue() { return []; }
});

this.liveRecordArrays = MapWithDefault.create({
defaultValue: modelName => {
assert(`liveRecordArrays.get expects modelName not modelClass as the param`, typeof modelName === 'string');
return this.createRecordArray(modelName);
}
});

this._filteredRecordArrays = Object.create(null);
this._liveRecordArrays = Object.create(null);
this._pending = Object.create(null);
this._adapterPopulatedRecordArrays = [];
}
Expand Down Expand Up @@ -113,42 +105,42 @@ export default class RecordArrayManager {
this._pending = Object.create(null);
let modelsToRemove = [];

Object.keys(pending).forEach(modelName => {
for (let modelName in pending) {
let internalModels = pending[modelName];

internalModels.forEach(internalModel => {
for (let j = 0; j < internalModels.length; j++) {
let internalModel = internalModels[j];
// mark internalModels, so they can once again be processed by the
// recordArrayManager
internalModel._pendingRecordArrayManagerFlush = false;
// build up a set of models to ensure we have purged correctly;
if (internalModel.isHiddenFromRecordArrays()) {
modelsToRemove.push(internalModel);
}
});
}

// process filteredRecordArrays
if (this.filteredRecordArrays.has(modelName)) {
let recordArrays = this.filteredRecordArrays.get(modelName);
if (this._filteredRecordArrays[modelName]) {
let recordArrays = this.filteredRecordArraysFor(modelName);
for (let i = 0; i < recordArrays.length; i++) {
this.updateFilterRecordArray(recordArrays[i], modelName, internalModels);
}
}

// TODO: skip if it only changed
// process liveRecordArrays
if (this.liveRecordArrays.has(modelName)) {
if (this._liveRecordArrays[modelName]) {
this.updateLiveRecordArray(modelName, internalModels);
}

// process adapterPopulatedRecordArrays
if (modelsToRemove.length > 0) {
this.removeFromAdapterPopulatedRecordArrays(modelsToRemove);
}
});
}
}

updateLiveRecordArray(modelName, internalModels) {
let array = this.liveRecordArrays.get(modelName);
let array = this.liveRecordArrayFor(modelName);

let modelsToAdd = [];
let modelsToRemove = [];
Expand Down Expand Up @@ -298,9 +290,25 @@ export default class RecordArrayManager {
assert(`recordArrayManger.liveRecordArrayFor expects modelName not modelClass as the param`, typeof modelName === 'string');

heimdall.increment(liveRecordArrayFor);
return this.liveRecordArrays.get(modelName);

return this._liveRecordArrays[modelName] || (this._liveRecordArrays[modelName] = this.createRecordArray(modelName))
}

/**
Get the `DS.RecordArray` for a modelName, which contains all loaded records of
given modelName.
@method filteredRecordArraysFor
@param {String} modelName
@return {DS.RecordArray}
*/
filteredRecordArraysFor(modelName) {
assert(`recordArrayManger.filteredRecordArraysFor expects modelName not modelClass as the param`, typeof modelName === 'string');

heimdall.increment(filteredRecordArraysFor);

return this._filteredRecordArrays[modelName] || (this._filteredRecordArrays[modelName] = []);
}
/**
Create a `DS.RecordArray` for a modelName.
Expand Down Expand Up @@ -387,7 +395,7 @@ export default class RecordArrayManager {
heimdall.increment(registerFilteredRecordArray);
assert(`recordArrayManger.registerFilteredRecordArray expects modelName not modelClass as the second param, received ${modelName}`, typeof modelName === 'string');

this.filteredRecordArrays.get(modelName).push(array);
this.filteredRecordArraysFor(modelName).push(array);
this.updateFilter(array, modelName, filter);
}

Expand All @@ -404,27 +412,27 @@ export default class RecordArrayManager {
let modelName = array.modelName;

// unregister filtered record array
let recordArrays = this.filteredRecordArrays.get(modelName);
let recordArrays = this.filteredRecordArraysFor(modelName);
let removedFromFiltered = remove(recordArrays, array);

// remove from adapter populated record array
let removedFromAdapterPopulated = remove(this._adapterPopulatedRecordArrays, array);

if (!removedFromFiltered && !removedFromAdapterPopulated) {

let liveRecordArrayForType = this._liveRecordArrays[modelName];
// unregister live record array
if (this.liveRecordArrays.has(modelName)) {
let liveRecordArrayForType = this.liveRecordArrayFor(modelName);
if (liveRecordArrayForType) {
if (array === liveRecordArrayForType) {
this.liveRecordArrays.delete(modelName);
delete this._liveRecordArrays[modelName];
}
}
}
}

willDestroy() {
this.filteredRecordArrays.forEach(value => flatten(value).forEach(destroy));
this.liveRecordArrays.forEach(destroy);
Object.keys(this._filteredRecordArrays).forEach(modelName => flatten(this._filteredRecordArrays[modelName]).forEach(destroy));
Object.keys(this._liveRecordArrays).forEach(modelName => this._liveRecordArrays[modelName].destroy());
this._adapterPopulatedRecordArrays.forEach(destroy);
this.isDestroyed = true;
}
Expand Down
4 changes: 2 additions & 2 deletions tests/integration/record-array-manager-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -117,13 +117,13 @@ test('destroying the store correctly cleans everything up', function(assert) {
assert.equal(internalPersonModel._recordArrays.size, 2, 'expected the person to be a member of 2 recordArrays');
assert.equal(filterd2Summary.called.length, 1);

assert.equal(manager.liveRecordArrays.has('person'), true);
assert.equal('person' in manager._liveRecordArrays, true);

Ember.run(all, all.destroy);

assert.equal(internalPersonModel._recordArrays.size, 1, 'expected the person to be a member of 1 recordArrays');
assert.equal(allSummary.called.length, 1);
assert.equal(manager.liveRecordArrays.has('person'), false);
assert.equal('person' in manager._liveRecordArrays, false);

Ember.run(manager, manager.destroy);

Expand Down

0 comments on commit 89588f5

Please sign in to comment.