Skip to content

Commit

Permalink
perf(internal-model): lazily allocation internal state.
Browse files Browse the repository at this point in the history
This will introduce a performance regression because of the use of ES2015 Class, the transpiled form of which relies on an expensive classCallCheck. @runspired will be adding a babel plugin to strip this classCallCheck at which time we will regain any lost instantiation performance.
  • Loading branch information
runspired committed Oct 19, 2016
1 parent fd59ce9 commit 374ea89
Show file tree
Hide file tree
Showing 9 changed files with 329 additions and 220 deletions.
442 changes: 277 additions & 165 deletions addon/-private/system/model/internal-model.js

Large diffs are not rendered by default.

14 changes: 9 additions & 5 deletions addon/-private/system/model/model.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,15 @@ import { DidDefinePropertyMixin, RelationshipsClassMethodsMixin, RelationshipsIn
import { AttrClassMethodsMixin, AttrInstanceMethodsMixin } from 'ember-data/-private/system/model/attr';
import isEnabled from 'ember-data/-private/features';

const {
get,
computed
} = Ember;

/**
@module ember-data
*/

var get = Ember.get;

function intersection (array1, array2) {
var result = [];
array1.forEach((element) => {
Expand All @@ -30,7 +33,7 @@ var RESERVED_MODEL_PROPS = [
'currentState', 'data', 'store'
];

var retrieveFromCurrentState = Ember.computed('currentState', function(key) {
var retrieveFromCurrentState = computed('currentState', function(key) {
return get(this._internalModel.currentState, key);
}).readOnly();

Expand Down Expand Up @@ -122,7 +125,7 @@ var Model = Ember.Object.extend(Ember.Evented, {
@type {Boolean}
@readOnly
*/
hasDirtyAttributes: Ember.computed('currentState.isDirty', function() {
hasDirtyAttributes: computed('currentState.isDirty', function() {
return this.get('currentState.isDirty');
}),
/**
Expand Down Expand Up @@ -305,6 +308,7 @@ var Model = Ember.Object.extend(Ember.Evented, {
@private
@type {Object}
*/
currentState: null,

/**
When the record is in the `invalid` state this object will contain
Expand Down Expand Up @@ -357,7 +361,7 @@ var Model = Ember.Object.extend(Ember.Evented, {
@property errors
@type {DS.Errors}
*/
errors: Ember.computed(function() {
errors: computed(function() {
let errors = Errors.create();

errors._registerHandlers(this._internalModel,
Expand Down
14 changes: 7 additions & 7 deletions addon/-private/system/model/states.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,10 @@
import Ember from 'ember';
import { assert } from "ember-data/-private/debug";

const { get } = Ember;
const {
K
} = Ember;

/*
This file encapsulates the various states that a record can transition
through during its lifecycle.
Expand Down Expand Up @@ -257,7 +260,7 @@ const DirtyState = {
heimdall.stop(token);
},

becomeDirty() { },
becomeDirty() {},

willCommit(internalModel) {
internalModel.transitionTo('inFlight');
Expand Down Expand Up @@ -430,7 +433,7 @@ createdState.uncommitted.pushedData = function(internalModel) {
internalModel.triggerLater('didLoad');
};

createdState.uncommitted.propertyWasReset = Ember.K;
createdState.uncommitted.propertyWasReset = K;

function assertAgainstUnloadRecord(internalModel) {
assert("You can only unload a record which is not inFlight. `" + internalModel + "`", false);
Expand Down Expand Up @@ -581,9 +584,7 @@ const RootState = {
internalModel.transitionTo('deleted.saved');
},

didCommit(internalModel) {
internalModel.send('invokeLifecycleCallbacks', get(internalModel, 'lastDirtyType'));
},
didCommit() {},

// loaded.saved.notFound would be triggered by a failed
// `reload()` on an unchanged record
Expand Down Expand Up @@ -715,7 +716,6 @@ const RootState = {
deleteRecord() { },
willCommit() { },


rolledBack(internalModel) {
internalModel.clearErrorMessages();
internalModel.transitionTo('loaded.saved');
Expand Down
9 changes: 7 additions & 2 deletions addon/-private/system/record-array-manager.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,14 @@ import {
FilteredRecordArray,
AdapterPopulatedRecordArray
} from "ember-data/-private/system/record-arrays";
const { get, MapWithDefault } = Ember;
import OrderedSet from "ember-data/-private/system/ordered-set";

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

const {
_addRecordToRecordArray,
_recordWasChanged,
Expand Down Expand Up @@ -78,7 +83,7 @@ export default Ember.Object.extend({
heimdall.increment(recordDidChange);
if (this.changedRecords.push(record) !== 1) { return; }

Ember.run.schedule('actions', this, this.updateRecordArrays);
emberRun.schedule('actions', this, this.updateRecordArrays);
},

recordArraysForRecord(record) {
Expand Down
59 changes: 22 additions & 37 deletions addon/-private/system/store.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,7 @@ import Model from 'ember-data/model';
import { instrument, assert, warn, runInDebug } from "ember-data/-private/debug";
import _normalizeLink from "ember-data/-private/system/normalize-link";
import normalizeModelName from "ember-data/-private/system/normalize-model-name";
import {
InvalidError
} from 'ember-data/adapters/errors';
import { InvalidError } from 'ember-data/adapters/errors';

import {
promiseArray,
Expand All @@ -22,13 +20,8 @@ import {
_objectIsAlive
} from "ember-data/-private/system/store/common";

import {
normalizeResponseHelper
} from "ember-data/-private/system/store/serializer-response";

import {
serializerForAdapter
} from "ember-data/-private/system/store/serializers";
import { normalizeResponseHelper } from "ember-data/-private/system/store/serializer-response";
import { serializerForAdapter } from "ember-data/-private/system/store/serializers";

import {
_find,
Expand All @@ -40,25 +33,27 @@ import {
_queryRecord
} from "ember-data/-private/system/store/finders";

import {
getOwner
} from 'ember-data/-private/utils';

import { getOwner } from 'ember-data/-private/utils';
import coerceId from "ember-data/-private/system/coerce-id";

import RecordArrayManager from "ember-data/-private/system/record-array-manager";
import ContainerInstanceCache from 'ember-data/-private/system/store/container-instance-cache';

import InternalModel from "ember-data/-private/system/model/internal-model";

import EmptyObject from "ember-data/-private/system/empty-object";

import isEnabled from 'ember-data/-private/features';

export let badIdFormatAssertion = '`id` passed to `findRecord()` has to be non-empty string or number';

const Backburner = Ember._Backburner;
var Map = Ember.Map;
const {
_Backburner: Backburner,
copy,
get,
isNone,
isPresent,
Map,
run: emberRun,
set,
Service
} = Ember;

//Get the materialized model from the internalModel/promise that returns
//an internal model and return it in a promiseObject. Useful for returning
Expand All @@ -68,19 +63,9 @@ function promiseRecord(internalModel, label) {
return promiseObject(toReturn, label);
}

var once = Ember.run.once;
var Promise = Ember.RSVP.Promise;
var Store;
const Promise = Ember.RSVP.Promise;

const {
copy,
get,
GUID_KEY,
isNone,
isPresent,
set,
Service
} = Ember;
let Store;

// Implementors Note:
//
Expand Down Expand Up @@ -807,7 +792,7 @@ Store = Service.extend({
} else {
this._pendingFetch.get(typeClass).push(pendingFetchItem);
}
Ember.run.scheduleOnce('afterRender', this, this.flushAllPendingFetches);
emberRun.scheduleOnce('afterRender', this, this.flushAllPendingFetches);

return promise;
},
Expand Down Expand Up @@ -1784,7 +1769,7 @@ Store = Service.extend({
snapshot: snapshot,
resolver: resolver
});
once(this, 'flushPendingSave');
emberRun.once(this, this.flushPendingSave);
},

/**
Expand Down Expand Up @@ -1839,7 +1824,7 @@ Store = Service.extend({
}
if (data) {
// normalize relationship IDs into records
this._backburner.schedule('normalizeRelationships', this, '_setupRelationships', internalModel, data);
this._backburner.schedule('normalizeRelationships', this, this._setupRelationships, internalModel, data);
this.updateId(internalModel, data);
} else {
assert(`Your ${internalModel.type.modelName} record was saved to the server, but the response does not have an id and no id has been set client side. Records must have ids. Please update the server response to provide an id in the response or generate the id on the client side either before saving the record or while normalizing the response.`, internalModel.id);
Expand Down Expand Up @@ -1893,7 +1878,7 @@ Store = Service.extend({
var id = coerceId(data.id);

// ID absolutely can't be missing if the oldID is empty (missing Id in response for a new record)
assert(`'${internalModel.type.modelName}:${internalModel[GUID_KEY]}' was saved to the server, but the response does not have an id and your record does not either.`, !(id === null && oldId === null));
assert(`'${internalModel.type.modelName}' was saved to the server, but the response does not have an id and your record does not either.`, !(id === null && oldId === null));

// ID absolutely can't be different than oldID if oldID is not null
assert(`'${internalModel.type.modelName}:${oldId}' was saved to the server, but the response returned the new id '${id}'. The store cannot assign a new id to a record that already has an id.`, !(oldId !== null && id !== oldId));
Expand Down Expand Up @@ -2262,7 +2247,7 @@ Store = Service.extend({
var internalModel = this._load(data);

this._backburner.join(() => {
this._backburner.schedule('normalizeRelationships', this, '_setupRelationships', internalModel, data);
this._backburner.schedule('normalizeRelationships', this, this._setupRelationships, internalModel, data);
});

return internalModel;
Expand Down
2 changes: 1 addition & 1 deletion ember-cli-build.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ module.exports = function(defaults) {
babel: {
plugins: [
// while ember-data strips itself, ember does not currently
{transformer: stripClassCallCheck, position: 'after'}
{ transformer: stripClassCallCheck, position: 'after' }
]
}
});
Expand Down
3 changes: 2 additions & 1 deletion lib/babel-build.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,8 @@ function babelOptions(libraryName, _options) {
'es6.properties.shorthand',
'es6.blockScoping',
'es6.constants',
'es6.modules'
'es6.modules',
'es6.classes'
],
sourceMaps: false,
modules: 'amdStrict',
Expand Down
4 changes: 3 additions & 1 deletion lib/stripped-build-plugins.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ var path = require('path');
var filterImports = require('babel-plugin-filter-imports');
var featureFlags = require('babel-plugin-feature-flags');
var stripHeimdall = require('babel5-plugin-strip-heimdall');
var stripClassCallCheck = require('babel5-plugin-strip-class-callcheck');

function uniqueAdd(obj, key, values) {
var a = obj[key] = obj[key] || [];
Expand Down Expand Up @@ -32,7 +33,8 @@ module.exports = function(environment) {
featureFlags({
import: { module: 'ember-data/-private/features' },
features: features
})
}),
{ transformer: stripClassCallCheck, position: 'after' }
];

if (process.env.INSTRUMENT_HEIMDALL === 'false') {
Expand Down
2 changes: 1 addition & 1 deletion tests/unit/store/unload-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ testInDebug("unload a dirty record asserts", function(assert) {

assert.expectAssertion(function() {
record.unloadRecord();
}, "You can only unload a record which is not inFlight. `" + Ember.inspect(record) + "`", "can not unload dirty record");
}, "You can only unload a record which is not inFlight. `" + record._internalModel.toString() + "`", "can not unload dirty record");

// force back into safe to unload mode.
run(function() {
Expand Down

0 comments on commit 374ea89

Please sign in to comment.