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

Feat/improved cache #4589

Merged
merged 1 commit into from
Oct 21, 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
71 changes: 22 additions & 49 deletions addon/-private/system/store.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

import Ember from 'ember';
import Model from 'ember-data/model';
import { instrument, assert, warn, runInDebug } from "ember-data/-private/debug";
import { instrument, assert, deprecate, 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';
Expand Down Expand Up @@ -97,7 +97,6 @@ const {
normalize,
peekAll,
peekRecord,
retrieveManagedInstance,
serializerFor,
typeMapFor,
typeMapFor_allocate
Expand All @@ -116,7 +115,6 @@ const {
'normalize',
'peekAll',
'peekRecord',
'retrieveManagedInstance',
'serializerFor',
'typeMapFor',
'typeMapFor_allocate'
Expand Down Expand Up @@ -210,7 +208,8 @@ Store = Service.extend({
store: this
});
this._pendingSave = [];
this._instanceCache = new ContainerInstanceCache(getOwner(this));
this._instanceCache = new ContainerInstanceCache(getOwner(this), this);

//Used to keep track of all the find requests that need to be coalesced
this._pendingFetch = Map.create();
},
Expand Down Expand Up @@ -267,13 +266,11 @@ Store = Service.extend({
@return DS.Adapter
*/
defaultAdapter: Ember.computed('adapter', function() {
var adapter = get(this, 'adapter');
let adapter = get(this, 'adapter');

assert('You tried to set `adapter` property to an instance of `DS.Adapter`, where it should be a name', typeof adapter === 'string');

adapter = this.retrieveManagedInstance('adapter', adapter);

return adapter;
return this.adapterFor(adapter);
}),

// .....................
Expand Down Expand Up @@ -2456,7 +2453,9 @@ Store = Service.extend({
assert("You need to pass a model name to the store's adapterFor method", isPresent(modelName));
assert(`Passing classes to store.adapterFor has been removed. Please pass a dasherized string instead of ${Ember.inspect(modelName)}`, typeof modelName === 'string');

return this.lookupAdapter(modelName);
let normalizedModelName = normalizeModelName(modelName);

return this._instanceCache.get('adapter', normalizedModelName);
},

_adapterRun(fn) {
Expand Down Expand Up @@ -2493,60 +2492,34 @@ Store = Service.extend({
assert("You need to pass a model name to the store's serializerFor method", isPresent(modelName));
assert(`Passing classes to store.serializerFor has been removed. Please pass a dasherized string instead of ${Ember.inspect(modelName)}`, typeof modelName === 'string');

var fallbacks = [
'application',
this.adapterFor(modelName).get('defaultSerializer'),
'-default'
];
let normalizedModelName = normalizeModelName(modelName);

var serializer = this.lookupSerializer(modelName, fallbacks);
return serializer;
},

/**
Retrieve a particular instance from the
container cache. If not found, creates it and
placing it in the cache.

Enabled a store to manage local instances of
adapters and serializers.

@method retrieveManagedInstance
@private
@param {String} modelName the object modelName
@param {String} name the object name
@param {Array} fallbacks the fallback objects to lookup if the lookup for modelName or 'application' fails
@return {Ember.Object}
*/
retrieveManagedInstance(type, modelName, fallbacks) {
heimdall.increment(retrieveManagedInstance);
var normalizedModelName = normalizeModelName(modelName);

var instance = this._instanceCache.get(type, normalizedModelName, fallbacks);
set(instance, 'store', this);
return instance;
return this._instanceCache.get('serializer', normalizedModelName);
},

lookupAdapter(name) {
return this.retrieveManagedInstance('adapter', name, this.get('_adapterFallbacks'));
deprecate(`Use of lookupAdapter is deprecated, use adapterFor instead.`, {
id: 'ds.store.lookupAdapter',
until: '3.0'
});
return this.adapterFor(name);
},

_adapterFallbacks: Ember.computed('adapter', function() {
var adapter = this.get('adapter');
return ['application', adapter, '-json-api'];
}),

lookupSerializer(name, fallbacks) {
return this.retrieveManagedInstance('serializer', name, fallbacks);
lookupSerializer(name) {
deprecate(`Use of lookupSerializer is deprecated, use serializerFor instead.`, {
id: 'ds.store.lookupSerializer',
until: '3.0'
});
return this.serializerFor(name);
},

willDestroy() {
this._super(...arguments);
this.recordArrayManager.destroy();
this._instanceCache.destroy();

this.unloadAll();
}

});

function deserializeRecordId(store, key, relationship, id) {
Expand Down
128 changes: 78 additions & 50 deletions addon/-private/system/store/container-instance-cache.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,15 @@
/* global heimdall */
import Ember from 'ember';
import EmptyObject from "ember-data/-private/system/empty-object";
const assign = Ember.assign || Ember.merge;
const { set } = Ember;

const {
__get,
_instanceFor
} = heimdall.registerMonitor('system.store.container-instance-cache',
'__get',
'_instanceFor'
);

/*
* The `ContainerInstanceCache` serves as a lazy cache for looking up
Copy link
Member

Choose a reason for hiding this comment

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

Please update the comments

Copy link
Contributor Author

Choose a reason for hiding this comment

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

updated

Expand All @@ -12,71 +20,86 @@ const assign = Ember.assign || Ember.merge;
* when the preferred lookup fails. For example, say you try to look up `adapter:post`,
* but there is no entry (app/adapters/post.js in EmberCLI) for `adapter:post` in the registry.
*
* The `fallbacks` array passed will then be used; the first entry in the fallbacks array
* that exists in the container will then be cached for `adapter:post`. So, the next time you
* look up `adapter:post`, you'll get the `adapter:application` instance (or whatever the fallback
* was if `adapter:application` doesn't exist).
* When an adapter or serializer is unfound, getFallbacks will be invoked with the current namespace
* ('adapter' or 'serializer') and the 'preferredKey' (usually a modelName). The method should return
* an array of keys to check against.
*
* The first entry in the fallbacks array that exists in the container will then be cached for
* `adapter:post`. So, the next time you look up `adapter:post`, you'll get the `adapter:application`
* instance (or whatever the fallback was if `adapter:application` doesn't exist).
*
* @private
* @class ContainerInstanceCache
*
*/
export default function ContainerInstanceCache(owner) {
this._owner = owner;
this._cache = new EmptyObject();
}
export default class ContainerInstanceCache {
constructor(owner, store) {
this._owner = owner;
this._store = store;
this._namespaces = {
adapter: new EmptyObject(),
serializer: new EmptyObject()
};
}

const {
__get,
instanceFor
} = heimdall.registerMonitor('system.store.container-instance-cache',
'get',
'instanceFor'
);
get(namespace, preferredKey) {
heimdall.increment(__get);
let cache = this._namespaces[namespace];

ContainerInstanceCache.prototype = new EmptyObject();
if (cache[preferredKey]) {
return cache[preferredKey];
}

assign(ContainerInstanceCache.prototype, {
get(type, preferredKey, fallbacks) {
heimdall.increment(__get);
let cache = this._cache;
let preferredLookupKey = `${type}:${preferredKey}`;
let preferredLookupKey = `${namespace}:${preferredKey}`;

if (!(preferredLookupKey in cache)) {
let instance = this.instanceFor(preferredLookupKey) || this._findInstance(type, fallbacks);
if (instance) {
cache[preferredLookupKey] = instance;
}
let instance = this._instanceFor(preferredLookupKey) || this._findInstance(namespace, this._fallbacksFor(namespace, preferredKey));
if (instance) {
cache[preferredKey] = instance;
set(instance, 'store', this._store);
}
return cache[preferredLookupKey];
},

_findInstance(type, fallbacks) {
return cache[preferredKey];
}

_fallbacksFor(namespace, preferredKey) {
if (namespace === 'adapter') {
return ['application', this._store.get('adapter'), '-json-api'];
}

// serializer
return [
'application',
this.get('adapter', preferredKey).get('defaultSerializer'),
'-default'
];
}

_findInstance(namespace, fallbacks) {
let cache = this._namespaces[namespace];

for (let i = 0, length = fallbacks.length; i < length; i++) {
let fallback = fallbacks[i];
let lookupKey = `${type}:${fallback}`;
let instance = this.instanceFor(lookupKey);

if (instance) {
return instance;
if (cache[fallback]) {
return cache[fallback];
}
}
},

instanceFor(key) {
heimdall.increment(instanceFor);
let cache = this._cache;
if (!cache[key]) {
let instance = this._owner.lookup(key);
let lookupKey = `${namespace}:${fallback}`;
let instance = this._instanceFor(lookupKey);

if (instance) {
cache[key] = instance;
cache[fallback] = instance;
return instance;
}
}
return cache[key];
},
}

destroy() {
let cache = this._cache;
_instanceFor(key) {
heimdall.increment(_instanceFor);
return this._owner.lookup(key);
}

destroyCache(cache) {
let cacheEntries = Object.keys(cache);

for (let i = 0, length = cacheEntries.length; i < length; i++) {
Expand All @@ -86,12 +109,17 @@ assign(ContainerInstanceCache.prototype, {
cacheEntry.destroy();
}
}
this._owner = null;
},
}

constructor: ContainerInstanceCache,
destroy() {
this.destroyCache(this._namespaces.adapter);
this.destroyCache(this._namespaces.serializer);
this._namespaces = null;
this._store = null;
this._owner = null;
}

toString() {
return 'ContainerInstanceCache';
}
});
}
2 changes: 1 addition & 1 deletion tests/helpers/store.js
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ export default function setupStore(options) {
return env;
}

export {setupStore};
export { setupStore };

export function createStore(options) {
return setupStore(options).store;
Expand Down