Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Heimdall instrumentation #4510

Merged
merged 10 commits into from
Sep 30, 2016
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .eslintrc.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,9 @@ module.exports = {
env: {
'browser': true,
},
globals: {
'heimdall': true
},
rules: {
'no-unused-vars': ['error', {
'args': 'none',
Expand Down
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ dist/
.idea
*.iml

benchmarks/results/*.json

.DS_Store
.project

Expand Down
21 changes: 21 additions & 0 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,27 @@ production builds automatically.
Be sure to leave a description of the feature and possible example of how to
use it (if necessary).

## Benchmarking

Ember Data is instrumented with [heimdalljs](https://github.com/heimdalljs/heimdalljs-lib)
Top level scenarios for benchmarking are available via the `query` route in
the dummy app, and desired scenarios to be run can be configured via `benchmarks/config.js`.

The scenarios are configured to interop with [heimdall-query](https://github.com/heimdalljs/heimdall-query)
for analysis. To run scenarios:

1. Start the dummy app with instrumentation on: `ember s --instrument`

2. Configure `benchmarks/config.js` with desired scenarios

3. To run both the benchmarks and the analysis: `node ./benchmarks`

a.) To just collect data (no analysis): `node ./benchmarks/bash-run.js`
b.) To just run analysis (w/cached data): `node ./benchmarks/bash-analyze.js`
c.) To cache a data set or use a cached data set, all commands accept `-c ./path/to/cache/dir`

4. Do not commit cached data results, these should be git ignored already.

# Pull Requests

We love pull requests. Here's a quick guide:
Expand Down
4 changes: 4 additions & 0 deletions addon/-private/debug.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,10 @@ export function runInDebug() {
return Ember.runInDebug(...arguments);
}

export function instrument(method) {
return method();
}

export function warn() {
return Ember.warn(...arguments);
}
Expand Down
39 changes: 39 additions & 0 deletions addon/-private/system/model/internal-model.js
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,34 @@ function retrieveFromCurrentState(key) {
};
}

// this (and all heimdall instrumentation) will be stripped by a babel transform
// https://github.com/heimdalljs/babel5-plugin-strip-heimdall
const {
_triggerDeferredTriggers,
changedAttributes,
createSnapshot,
flushChangedAttributes,
hasChangedAttributes,
materializeRecord,
new_InternalModel,
send,
setupData,
transitionTo,
updateChangedAttributes
} = heimdall.registerMonitor('InternalModel',
'_triggerDeferredTriggers',
'changedAttributes',
'createSnapshot',
'flushChangedAttributes',
'hasChangedAttributes',
'materializeRecord',
'new_InternalModel',
'send',
'setupData',
'transitionTo',
'updateChangedAttributes'
);
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@stefanpenner style preference here? Expanded like this or moved onto fewer lines?


/*
`InternalModel` is the Model class that we use internally inside Ember Data to represent models.
Internal ED methods should only deal with `InternalModel` objects. It is a fast, plain Javascript class.
Expand All @@ -61,6 +89,7 @@ function retrieveFromCurrentState(key) {
*/

export default function InternalModel(type, id, store, _, data) {
heimdall.increment(new_InternalModel);
this.type = type;
this.id = id;
this.store = store;
Expand Down Expand Up @@ -124,6 +153,7 @@ InternalModel.prototype = {

constructor: InternalModel,
materializeRecord() {
heimdall.increment(materializeRecord);
assert("Materialized " + this.modelName + " record with id:" + this.id + "more than once", this.record === null || this.record === undefined);

// lookupFactory should really return an object that creates
Expand Down Expand Up @@ -221,6 +251,7 @@ InternalModel.prototype = {
},

setupData(data) {
heimdall.increment(setupData);
var changedKeys = this._changedKeys(data.attributes);
assign(this._data, data.attributes);
this.pushedData();
Expand Down Expand Up @@ -252,6 +283,7 @@ InternalModel.prototype = {
@private
*/
createSnapshot(options) {
heimdall.increment(createSnapshot);
return new Snapshot(this, options);
},

Expand Down Expand Up @@ -290,11 +322,13 @@ InternalModel.prototype = {
},

flushChangedAttributes() {
heimdall.increment(flushChangedAttributes);
this._inFlightAttributes = this._attributes;
this._attributes = new EmptyObject();
},

hasChangedAttributes() {
heimdall.increment(hasChangedAttributes);
return Object.keys(this._attributes).length > 0;
},

Expand All @@ -309,6 +343,7 @@ InternalModel.prototype = {
@private
*/
updateChangedAttributes() {
heimdall.increment(updateChangedAttributes);
var changedAttributes = this.changedAttributes();
var changedAttributeNames = Object.keys(changedAttributes);

Expand All @@ -330,6 +365,7 @@ InternalModel.prototype = {
@private
*/
changedAttributes() {
heimdall.increment(changedAttributes);
var oldData = this._data;
var currentData = this._attributes;
var inFlightData = this._inFlightAttributes;
Expand Down Expand Up @@ -370,6 +406,7 @@ InternalModel.prototype = {
@param {Object} context
*/
send(name, context) {
heimdall.increment(send);
var currentState = get(this, 'currentState');

if (!currentState[name]) {
Expand Down Expand Up @@ -441,6 +478,7 @@ InternalModel.prototype = {
@param {String} name
*/
transitionTo(name) {
heimdall.increment(transitionTo);
// POSSIBLE TODO: Remove this code and replace with
// always having direct reference to state objects

Expand Down Expand Up @@ -509,6 +547,7 @@ InternalModel.prototype = {
},

_triggerDeferredTriggers() {
heimdall.increment(_triggerDeferredTriggers);
//TODO: Before 1.0 we want to remove all the events that happen on the pre materialized record,
//but for now, we queue up all the events triggered before the record was materialized, and flush
//them once we have the record
Expand Down
2 changes: 2 additions & 0 deletions addon/-private/system/model/states.js
Original file line number Diff line number Diff line change
Expand Up @@ -248,11 +248,13 @@ var DirtyState = {
},

pushedData(internalModel) {
let token = heimdall.start('stats.uncommitted.pushedData');
internalModel.updateChangedAttributes();

if (!internalModel.hasChangedAttributes()) {
internalModel.transitionTo('loaded.saved');
}
heimdall.stop(token);
},

becomeDirty: Ember.K,
Expand Down
61 changes: 61 additions & 0 deletions addon/-private/system/record-array-manager.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,48 @@ var MapWithDefault = Ember.MapWithDefault;
import OrderedSet from "ember-data/-private/system/ordered-set";
var get = Ember.get;

const {
_addRecordToRecordArray,
_recordWasChanged,
_recordWasDeleted,
array_flatten,
array_remove,
create,
createAdapterPopulatedRecordArray,
createFilteredRecordArray,
createRecordArray,
liveRecordArrayFor,
populateLiveRecordArray,
recordArraysForRecord,
recordDidChange,
recordWasLoaded,
registerFilteredRecordArray,
unregisterRecordArray,
updateFilter,
updateFilterRecordArray,
updateRecordArrays
} = heimdall.registerMonitor('recordArrayManager',
'_addRecordToRecordArray',
'_recordWasChanged',
'_recordWasDeleted',
'array_fatten',
'array_remove',
'create',
'createAdapterPopulatedRecordArray',
'createFilteredRecordArray',
'createRecordArray',
'liveRecordArrayFor',
'populateLiveRecordArray',
'recordArraysForRecord',
'recordDidChange',
'recordWasLoaded',
'registerFilteredRecordArray',
'unregisterRecordArray',
'updateFilter',
'updateFilterRecordArray',
'updateRecordArrays'
);

/**
@class RecordArrayManager
@namespace DS
Expand All @@ -20,6 +62,7 @@ var get = Ember.get;
*/
export default Ember.Object.extend({
init() {
heimdall.increment(create);
this.filteredRecordArrays = MapWithDefault.create({
defaultValue() { return []; }
});
Expand All @@ -35,12 +78,14 @@ export default Ember.Object.extend({
},

recordDidChange(record) {
heimdall.increment(recordDidChange);
if (this.changedRecords.push(record) !== 1) { return; }

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

recordArraysForRecord(record) {
heimdall.increment(recordArraysForRecord);
record._recordArrays = record._recordArrays || OrderedSet.create();
return record._recordArrays;
},
Expand All @@ -56,6 +101,7 @@ export default Ember.Object.extend({
@method updateRecordArrays
*/
updateRecordArrays() {
heimdall.increment(updateRecordArrays);
this.changedRecords.forEach((internalModel) => {
if (get(internalModel, 'record.isDestroyed') || get(internalModel, 'record.isDestroying') ||
(get(internalModel, 'currentState.stateName') === 'root.deleted.saved')) {
Expand All @@ -69,6 +115,7 @@ export default Ember.Object.extend({
},

_recordWasDeleted(record) {
heimdall.increment(_recordWasDeleted);
var recordArrays = record._recordArrays;

if (!recordArrays) { return; }
Expand All @@ -80,6 +127,7 @@ export default Ember.Object.extend({


_recordWasChanged(record) {
heimdall.increment(_recordWasChanged);
var typeClass = record.type;
var recordArrays = this.filteredRecordArrays.get(typeClass);
var filter;
Expand All @@ -91,6 +139,7 @@ export default Ember.Object.extend({

//Need to update live arrays on loading
recordWasLoaded(record) {
heimdall.increment(recordWasLoaded);
var typeClass = record.type;
var recordArrays = this.filteredRecordArrays.get(typeClass);
var filter;
Expand All @@ -115,6 +164,7 @@ export default Ember.Object.extend({
@param {InternalModel} record
*/
updateFilterRecordArray(array, filter, typeClass, record) {
heimdall.increment(updateFilterRecordArray);
var shouldBeInArray = filter(record.getRecord());
var recordArrays = this.recordArraysForRecord(record);
if (shouldBeInArray) {
Expand All @@ -126,6 +176,7 @@ export default Ember.Object.extend({
},

_addRecordToRecordArray(array, record) {
heimdall.increment(_addRecordToRecordArray);
var recordArrays = this.recordArraysForRecord(record);
if (!recordArrays.has(array)) {
array.addInternalModel(record);
Expand All @@ -134,6 +185,7 @@ export default Ember.Object.extend({
},

populateLiveRecordArray(array, modelName) {
heimdall.increment(populateLiveRecordArray);
var typeMap = this.store.typeMapFor(modelName);
var records = typeMap.records;
var record;
Expand All @@ -160,6 +212,7 @@ export default Ember.Object.extend({
@param {Function} filter
*/
updateFilter(array, modelName, filter) {
heimdall.increment(updateFilter);
var typeMap = this.store.typeMapFor(modelName);
var records = typeMap.records;
var record;
Expand All @@ -182,6 +235,7 @@ export default Ember.Object.extend({
@return {DS.RecordArray}
*/
liveRecordArrayFor(typeClass) {
heimdall.increment(liveRecordArrayFor);
return this.liveRecordArrays.get(typeClass);
},

Expand All @@ -193,6 +247,7 @@ export default Ember.Object.extend({
@return {DS.RecordArray}
*/
createRecordArray(typeClass) {
heimdall.increment(createRecordArray);
var array = RecordArray.create({
type: typeClass,
content: Ember.A(),
Expand All @@ -214,6 +269,7 @@ export default Ember.Object.extend({
@return {DS.FilteredRecordArray}
*/
createFilteredRecordArray(typeClass, filter, query) {
heimdall.increment(createFilteredRecordArray);
var array = FilteredRecordArray.create({
query: query,
type: typeClass,
Expand All @@ -237,6 +293,7 @@ export default Ember.Object.extend({
@return {DS.AdapterPopulatedRecordArray}
*/
createAdapterPopulatedRecordArray(typeClass, query) {
heimdall.increment(createAdapterPopulatedRecordArray);
var array = AdapterPopulatedRecordArray.create({
type: typeClass,
query: query,
Expand All @@ -262,6 +319,7 @@ export default Ember.Object.extend({
@param {Function} filter
*/
registerFilteredRecordArray(array, typeClass, filter) {
heimdall.increment(registerFilteredRecordArray);
var recordArrays = this.filteredRecordArrays.get(typeClass);
recordArrays.push(array);

Expand All @@ -276,6 +334,7 @@ export default Ember.Object.extend({
@param {DS.RecordArray} array
*/
unregisterRecordArray(array) {
heimdall.increment(unregisterRecordArray);
var typeClass = array.type;

// unregister filtered record array
Expand Down Expand Up @@ -312,6 +371,7 @@ function destroy(entry) {
}

function flatten(list) {
heimdall.increment(array_flatten);
var length = list.length;
var result = Ember.A();

Expand All @@ -323,6 +383,7 @@ function flatten(list) {
}

function remove(array, item) {
heimdall.increment(array_remove);
const index = array.indexOf(item);

if (index !== -1) {
Expand Down
Loading