Skip to content

Commit

Permalink
feat: Store.cache and Store.createCache (#8458)
Browse files Browse the repository at this point in the history
* cleanup

* add docs

* fixes

* more fixes

* more progress

* fixes

* all the fixes

* fix docs

* fix more tests
  • Loading branch information
runspired authored Mar 12, 2023
1 parent e294c79 commit 9da3d1b
Show file tree
Hide file tree
Showing 21 changed files with 661 additions and 1,154 deletions.
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

0 comments on commit 9da3d1b

Please sign in to comment.