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: Store.cache and Store.createCache #8458

Merged
merged 9 commits into from
Mar 12, 2023
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
2 changes: 2 additions & 0 deletions ember-data-types/q/ds-model.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ export interface DSModel extends EmberObject {
eachRelationship<T>(callback: (this: T, key: string, meta: RelationshipSchema) => void, binding?: T): void;
eachAttribute<T>(callback: (this: T, key: string, meta: AttributeSchema) => void, binding?: T): void;
invalidErrorsChanged(errors: JsonApiValidationError[]): void;
rollbackAttributes(): void;
changedAttributes(): Record<string, [unknown, unknown]>;
[key: string]: unknown;
isDeleted: boolean;
deleteRecord(): void;
Expand Down
65 changes: 42 additions & 23 deletions packages/json-api/src/-private/cache.ts
Original file line number Diff line number Diff line change
Expand Up @@ -87,22 +87,29 @@ export default class SingletonCache implements Cache {
}

/**
* Private method used when the store's `createRecordDataFor` hook is called
* to populate an entry for the identifier into the singleton.
* Private method used to populate an entry for the identifier
*
* @method createCache
* @method _createCache
* @private
* @param identifier
*/
createCache(identifier: StableRecordIdentifier): void {
this.__cache.set(identifier, makeCache());
_createCache(identifier: StableRecordIdentifier): CachedResource {
assert(`Expected no resource data to yet exist in the cache`, !this.__cache.has(identifier));
const cache = makeCache();
this.__cache.set(identifier, cache);
return cache;
}

__peek(identifier: StableRecordIdentifier, allowDestroyed = false): CachedResource {
__safePeek(identifier: StableRecordIdentifier, allowDestroyed: boolean): CachedResource | undefined {
let resource = this.__cache.get(identifier);
if (!resource && allowDestroyed) {
resource = this.__destroyedCache.get(identifier);
}
return resource;
}

__peek(identifier: StableRecordIdentifier, allowDestroyed: boolean): CachedResource {
let resource = this.__safePeek(identifier, allowDestroyed);
assert(
`Expected Cache to have a resource entry for the identifier ${String(identifier)} but none was found`,
resource
Expand All @@ -116,9 +123,9 @@ export default class SingletonCache implements Cache {
calculateChanges?: boolean | undefined
): void | string[] {
let changedKeys: string[] | undefined;
const peeked = this.__peek(identifier);
const peeked = this.__safePeek(identifier, false);
const existed = !!peeked;
const cached = peeked || this.createCache(identifier);
const cached = peeked || this._createCache(identifier);

const isLoading = _isLoading(peeked, this.__storeWrapper, identifier) || !recordIsLoaded(peeked);
let isUpdate = !_isEmpty(peeked) && !isLoading;
Expand All @@ -136,6 +143,7 @@ export default class SingletonCache implements Cache {

if (cached.isNew) {
cached.isNew = false;
this.__storeWrapper.notifyChange(identifier, 'identity');
this.__storeWrapper.notifyChange(identifier, 'state');
}

Expand Down Expand Up @@ -211,7 +219,7 @@ export default class SingletonCache implements Cache {
console.log(`EmberData | Mutation - clientDidCreate ${identifier.lid}`, options);
}
}
const cached = this.__peek(identifier);
const cached = this._createCache(identifier);
cached.isNew = true;
let createOptions = {};

Expand Down Expand Up @@ -273,12 +281,12 @@ export default class SingletonCache implements Cache {
return createOptions;
}
willCommit(identifier: StableRecordIdentifier): void {
const cached = this.__peek(identifier);
const cached = this.__peek(identifier, false);
cached.inflightAttrs = cached.localAttrs;
cached.localAttrs = null;
}
didCommit(identifier: StableRecordIdentifier, data: JsonApiResource | null): void {
const cached = this.__peek(identifier);
const cached = this.__peek(identifier, false);
if (cached.isDeleted) {
graphFor(this.__storeWrapper).push({
op: 'deleteRecord',
Expand Down Expand Up @@ -337,7 +345,7 @@ export default class SingletonCache implements Cache {
}

commitWasRejected(identifier: StableRecordIdentifier, errors?: JsonApiValidationError[] | undefined): void {
const cached = this.__peek(identifier);
const cached = this.__peek(identifier, false);
if (cached.inflightAttrs) {
let keys = Object.keys(cached.inflightAttrs);
if (keys.length > 0) {
Expand All @@ -357,9 +365,17 @@ export default class SingletonCache implements Cache {
}

unloadRecord(identifier: StableRecordIdentifier): void {
// TODO this is necessary because
// we maintain memebership inside InstanceCache
// for peekAll, so even though we haven't created
// any data we think this exists.
// TODO can we eliminate that membership now?
if (!this.__cache.has(identifier)) {
return;
}
const removeFromRecordArray = !this.isDeletionCommitted(identifier);
let removed = false;
const cached = this.__peek(identifier);
const cached = this.__peek(identifier, false);
const storeWrapper = this.__storeWrapper;
peekGraph(storeWrapper)?.unload(identifier);

Expand Down Expand Up @@ -422,7 +438,7 @@ export default class SingletonCache implements Cache {
}
}
setAttr(identifier: StableRecordIdentifier, attr: string, value: unknown): void {
const cached = this.__peek(identifier);
const cached = this.__peek(identifier, false);
const existing =
cached.inflightAttrs && attr in cached.inflightAttrs
? cached.inflightAttrs[attr]
Expand All @@ -443,7 +459,7 @@ export default class SingletonCache implements Cache {
}
changedAttrs(identifier: StableRecordIdentifier): ChangedAttributesHash {
// TODO freeze in dev
return this.__peek(identifier).changes || Object.create(null);
return this.__peek(identifier, false).changes || Object.create(null);
}
hasChangedAttrs(identifier: StableRecordIdentifier): boolean {
const cached = this.__peek(identifier, true);
Expand All @@ -454,7 +470,7 @@ export default class SingletonCache implements Cache {
);
}
rollbackAttrs(identifier: StableRecordIdentifier): string[] {
const cached = this.__peek(identifier);
const cached = this.__peek(identifier, false);
let dirtyKeys: string[] | undefined;
cached.isDeleted = false;

Expand Down Expand Up @@ -498,7 +514,7 @@ export default class SingletonCache implements Cache {
}

setIsDeleted(identifier: StableRecordIdentifier, isDeleted: boolean): void {
const cached = this.__peek(identifier);
const cached = this.__peek(identifier, false);
cached.isDeleted = isDeleted;
if (cached.isNew) {
// TODO can we delete this since we will do this in unload?
Expand All @@ -514,17 +530,20 @@ export default class SingletonCache implements Cache {
return this.__peek(identifier, true).errors || [];
}
isEmpty(identifier: StableRecordIdentifier): boolean {
const cached = this.__peek(identifier, true);
return cached.remoteAttrs === null && cached.inflightAttrs === null && cached.localAttrs === null;
const cached = this.__safePeek(identifier, true);
return cached ? cached.remoteAttrs === null && cached.inflightAttrs === null && cached.localAttrs === null : true;
}
isNew(identifier: StableRecordIdentifier): boolean {
return this.__peek(identifier, true).isNew;
// TODO can we assert here?
return this.__safePeek(identifier, true)?.isNew || false;
}
isDeleted(identifier: StableRecordIdentifier): boolean {
return this.__peek(identifier, true).isDeleted;
// TODO can we assert here?
return this.__safePeek(identifier, true)?.isDeleted || false;
}
isDeletionCommitted(identifier: StableRecordIdentifier): boolean {
return this.__peek(identifier, true).isDeletionCommitted;
// TODO can we assert here?
return this.__safePeek(identifier, true)?.isDeletionCommitted || false;
}
}

Expand Down Expand Up @@ -654,7 +673,7 @@ function recordIsLoaded(cached: CachedResource | undefined, filterDeleted: boole
}

function _isLoading(
peeked: CachedResource,
peeked: CachedResource | undefined,
storeWrapper: CacheStoreWrapper,
identifier: StableRecordIdentifier
): boolean {
Expand Down
2 changes: 2 additions & 0 deletions packages/private-build-infra/addon/current-deprecations.ts
Original file line number Diff line number Diff line change
Expand Up @@ -61,4 +61,6 @@ export default {
DEPRECATE_ARRAY_LIKE: '4.7',
DEPRECATE_COMPUTED_CHAINS: '5.0',
DEPRECATE_NON_EXPLICIT_POLYMORPHISM: '4.7',
DEPRECATE_INSTANTIATE_RECORD_ARGS: '4.12',
DEPRECATE_CREATE_RECORD_DATA_FOR_HOOK: '4.12',
};
2 changes: 2 additions & 0 deletions packages/private-build-infra/addon/deprecations.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,3 +30,5 @@ export const DEPRECATE_PROMISE_PROXIES = deprecationState('DEPRECATE_PROMISE_PRO
export const DEPRECATE_ARRAY_LIKE = deprecationState('DEPRECATE_ARRAY_LIKE');
export const DEPRECATE_COMPUTED_CHAINS = deprecationState('DEPRECATE_COMPUTED_CHAINS');
export const DEPRECATE_NON_EXPLICIT_POLYMORPHISM = deprecationState('DEPRECATE_NON_EXPLICIT_POLYMORPHISM');
export const DEPRECATE_INSTANTIATE_RECORD_ARGS = deprecationState('DEPRECATE_INSTANTIATE_RECORD_ARGS');
export const DEPRECATE_CREATE_RECORD_DATA_FOR_HOOK = deprecationState('DEPRECATE_CREATE_RECORD_DATA_FOR_HOOK');
34 changes: 34 additions & 0 deletions packages/store/src/-private/caches/cache-utils.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import { assert } from '@ember/debug';

import type { Cache } from '@ember-data/types/q/cache';
import type { StableRecordIdentifier } from '@ember-data/types/q/identifier';
import type { RecordInstance } from '@ember-data/types/q/record-instance';

/*
* Returns the Cache instance associated with a given
* Model or Identifier
*/

export const CacheForIdentifierCache = new Map<StableRecordIdentifier | RecordInstance, Cache>();

export function setCacheFor(identifier: StableRecordIdentifier | RecordInstance, recordData: Cache): void {
assert(
`Illegal set of identifier`,
!CacheForIdentifierCache.has(identifier) || CacheForIdentifierCache.get(identifier) === recordData
);
CacheForIdentifierCache.set(identifier, recordData);
}

export function removeRecordDataFor(identifier: StableRecordIdentifier | RecordInstance): void {
CacheForIdentifierCache.delete(identifier);
}

export default function peekCache(instance: StableRecordIdentifier): Cache | null;
export default function peekCache(instance: RecordInstance): Cache;
export default function peekCache(instance: StableRecordIdentifier | RecordInstance): Cache | null {
if (CacheForIdentifierCache.has(instance as StableRecordIdentifier)) {
return CacheForIdentifierCache.get(instance as StableRecordIdentifier) as Cache;
}

return null;
}
Loading