apollo-invalidation-policies
Advanced tools
Comparing version
@@ -0,1 +1,6 @@ | ||
1.0.0.-beta5 (Dan Reynolds) | ||
- Adds support for a `renewalPolicy` type and global config option for specifying how type TTLs should be renewed on write vs access | ||
- Adds the `expire` API for evicting all entities that have expired in the cache based on their type's or the global TTL. | ||
1.0.0-beta4 (Dan Reynolds) | ||
@@ -2,0 +7,0 @@ |
@@ -13,2 +13,3 @@ "use strict"; | ||
const fragments_1 = require("@apollo/client/utilities/graphql/fragments"); | ||
const types_1 = require("../policies/types"); | ||
var ReadResultStatus; | ||
@@ -44,3 +45,3 @@ (function (ReadResultStatus) { | ||
processReadSubResult(parentResult, fieldNameOrIndex) { | ||
const { cache, invalidationPolicyManager } = this.config; | ||
const { cache, invalidationPolicyManager, entityTypeMap } = this.config; | ||
const result = lodash_1.default.isUndefined(fieldNameOrIndex) | ||
@@ -56,2 +57,7 @@ ? parentResult | ||
if (id) { | ||
const renewalPolicy = invalidationPolicyManager.getRenewalPolicyForType(__typename); | ||
if (renewalPolicy === types_1.RenewalPolicy.AccessAndWrite || | ||
renewalPolicy === types_1.RenewalPolicy.AccessOnly) { | ||
entityTypeMap.renewEntity(id); | ||
} | ||
const evicted = invalidationPolicyManager.runReadPolicy(__typename, id); | ||
@@ -107,2 +113,7 @@ if (evicted) { | ||
}); | ||
const renewalPolicy = invalidationPolicyManager.getRenewalPolicyForType(typename); | ||
if (renewalPolicy === types_1.RenewalPolicy.AccessAndWrite || | ||
renewalPolicy === types_1.RenewalPolicy.AccessOnly) { | ||
entityTypeMap.renewEntity(dataId, storeFieldName); | ||
} | ||
const evicted = invalidationPolicyManager.runReadPolicy(typename, dataId, fieldName, storeFieldName); | ||
@@ -127,3 +138,3 @@ if (evicted) { | ||
processWriteSubResult(result) { | ||
const { cache, invalidationPolicyManager } = this.config; | ||
const { cache, invalidationPolicyManager, entityTypeMap } = this.config; | ||
if (lodash_1.default.isPlainObject(result)) { | ||
@@ -135,2 +146,7 @@ const { __typename } = result; | ||
if (id) { | ||
const renewalPolicy = invalidationPolicyManager.getRenewalPolicyForType(__typename); | ||
if (renewalPolicy === types_1.RenewalPolicy.WriteOnly || | ||
renewalPolicy === types_1.RenewalPolicy.AccessAndWrite) { | ||
entityTypeMap.renewEntity(id); | ||
} | ||
invalidationPolicyManager.runWritePolicy(__typename, { | ||
@@ -172,2 +188,7 @@ parent: { | ||
entityTypeMap.write(typename, dataId, storeFieldName, fieldVariables); | ||
const renewalPolicy = invalidationPolicyManager.getRenewalPolicyForType(typename); | ||
if (renewalPolicy === types_1.RenewalPolicy.WriteOnly || | ||
renewalPolicy === types_1.RenewalPolicy.AccessAndWrite) { | ||
entityTypeMap.renewEntity(dataId, storeFieldName); | ||
} | ||
invalidationPolicyManager.runWritePolicy(typename, { | ||
@@ -188,2 +209,7 @@ parent: { | ||
if (typename) { | ||
const renewalPolicy = invalidationPolicyManager.getRenewalPolicyForType(typename); | ||
if (renewalPolicy === types_1.RenewalPolicy.WriteOnly || | ||
renewalPolicy === types_1.RenewalPolicy.AccessAndWrite) { | ||
entityTypeMap.renewEntity(dataId); | ||
} | ||
invalidationPolicyManager.runWritePolicy(typename, { | ||
@@ -190,0 +216,0 @@ parent: { |
@@ -24,2 +24,3 @@ import { InMemoryCache, Cache, NormalizedCacheObject, Reference, StoreObject } from "@apollo/client"; | ||
evict(options: Cache.EvictOptions): boolean; | ||
expire(): string[]; | ||
read<T>(options: Cache.ReadOptions<any>): T | null; | ||
@@ -26,0 +27,0 @@ diff<T>(options: Cache.DiffOptions): Cache.DiffResult<T>; |
@@ -127,3 +127,4 @@ "use strict"; | ||
// the policy will later be applied when the server data response is received. | ||
if (!this.invalidationPolicyManager.isPolicyActive(types_1.InvalidationPolicyEvent.Write) || | ||
if ((!this.invalidationPolicyManager.isPolicyActive(types_1.InvalidationPolicyEvent.Write) && | ||
!this.invalidationPolicyManager.isPolicyActive(types_1.InvalidationPolicyEvent.Read)) || | ||
!this.isOperatingOnRootData()) { | ||
@@ -171,2 +172,30 @@ return writeResult; | ||
} | ||
// Evicts all expired entities whose cache time exceeds their type's timeToLive or as a fallback | ||
// the global timeToLive if specified. Returns a list of entity IDs removed from the cache. | ||
expire() { | ||
const { entitiesById } = this.entityTypeMap.extract(); | ||
const expiredEntityIds = []; | ||
Object.keys(entitiesById).forEach((entityId) => { | ||
const entity = entitiesById[entityId]; | ||
const { storeFieldNames, dataId, fieldName, typename } = entity; | ||
if (helpers_2.isQuery(dataId) && storeFieldNames) { | ||
Object.keys(storeFieldNames.entries).forEach((storeFieldName) => { | ||
const evicted = this.invalidationPolicyManager.runReadPolicy(typename, dataId, fieldName, storeFieldName); | ||
if (evicted) { | ||
expiredEntityIds.push(helpers_2.makeEntityId(dataId, storeFieldName)); | ||
} | ||
}); | ||
} | ||
else { | ||
const evicted = this.invalidationPolicyManager.runReadPolicy(typename, dataId, fieldName); | ||
if (evicted) { | ||
expiredEntityIds.push(helpers_2.makeEntityId(dataId)); | ||
} | ||
} | ||
}); | ||
if (expiredEntityIds.length > 0) { | ||
this.broadcastWatches(); | ||
} | ||
return expiredEntityIds; | ||
} | ||
read(options) { | ||
@@ -191,3 +220,3 @@ const result = super.read(options); | ||
// as these are internal reads not reflective of client action and can lead to recursive recomputation of cached data which is an error. | ||
// Instead, diffs swill trigger the read policies for client-based reads like `readCache` invocations from watched queries outside | ||
// Instead, diffs will trigger the read policies for client-based reads like `readCache` invocations from watched queries outside | ||
// the scope of broadcasts. | ||
@@ -215,6 +244,5 @@ if (!this.invalidationPolicyManager.isPolicyActive(types_1.InvalidationPolicyEvent.Read) || | ||
if (withInvalidation) { | ||
extractedCache.invalidation = lodash_1.default.pick(this.entityTypeMap.extract(), | ||
// The entitiesById are sufficient alone for reconstructing the type map, so to | ||
// minimize payload size only inject the entitiesById object into the extracted cache | ||
"entitiesById"); | ||
extractedCache.invalidation = lodash_1.default.pick(this.entityTypeMap.extract(), "entitiesById"); | ||
} | ||
@@ -221,0 +249,0 @@ return extractedCache; |
@@ -98,2 +98,3 @@ import { EntitiesById, ExtractedTypeMap, TypeMapEntities, TypeMapEntity } from "./types"; | ||
readEntityById(entityId: string): TypeMapEntity | null; | ||
renewEntity(dataId: string, storeFieldName?: string): void; | ||
restore(entitiesById: EntitiesById): void; | ||
@@ -100,0 +101,0 @@ extract(): ExtractedTypeMap; |
@@ -108,7 +108,22 @@ "use strict"; | ||
const entityId = helpers_2.makeEntityId(dataId, fieldName); | ||
const typeMapEntity = this.readEntityById(entityId); | ||
let cacheTime = Date.now(); | ||
let newEntity = null; | ||
if (helpers_2.isQuery(dataId) && storeFieldName) { | ||
if (!typeMapEntity) { | ||
const existingTypeMapEntity = this.readEntityById(entityId); | ||
if (existingTypeMapEntity) { | ||
if (helpers_2.isQuery(dataId) && storeFieldName) { | ||
const storeFieldNameEntry = existingTypeMapEntity.storeFieldNames | ||
.entries[storeFieldName]; | ||
if (storeFieldNameEntry) { | ||
storeFieldNameEntry.variables = variables; | ||
} | ||
else { | ||
existingTypeMapEntity.storeFieldNames.entries[storeFieldName] = { | ||
variables, | ||
}; | ||
existingTypeMapEntity.storeFieldNames.__size++; | ||
} | ||
} | ||
} | ||
else { | ||
let newEntity; | ||
const cacheTime = Date.now(); | ||
if (helpers_2.isQuery(dataId) && storeFieldName) { | ||
newEntity = { | ||
@@ -121,3 +136,3 @@ dataId, | ||
entries: { | ||
[storeFieldName]: { cacheTime, variables }, | ||
[storeFieldName]: { variables, cacheTime }, | ||
}, | ||
@@ -128,24 +143,8 @@ }, | ||
else { | ||
const storeFieldNameEntry = typeMapEntity.storeFieldNames.entries[storeFieldName]; | ||
if (storeFieldNameEntry) { | ||
storeFieldNameEntry.cacheTime = cacheTime; | ||
storeFieldNameEntry.variables = variables; | ||
} | ||
else { | ||
typeMapEntity.storeFieldNames.entries[storeFieldName] = { | ||
cacheTime, | ||
variables, | ||
}; | ||
typeMapEntity.storeFieldNames.__size++; | ||
} | ||
newEntity = { | ||
dataId, | ||
typename, | ||
cacheTime, | ||
}; | ||
} | ||
} | ||
else { | ||
newEntity = { | ||
dataId, | ||
typename, | ||
cacheTime, | ||
}; | ||
} | ||
if (newEntity) { | ||
lodash_1.default.set(this.entitiesByType, [typename, entityId], newEntity); | ||
@@ -191,2 +190,17 @@ this.entitiesById[entityId] = newEntity; | ||
} | ||
renewEntity(dataId, storeFieldName) { | ||
const fieldName = storeFieldName | ||
? helpers_1.fieldNameFromStoreName(storeFieldName) | ||
: undefined; | ||
const entity = this.entitiesById[helpers_2.makeEntityId(dataId, fieldName)]; | ||
if (entity) { | ||
const cacheTime = Date.now(); | ||
if (helpers_2.isQuery(dataId) && storeFieldName) { | ||
entity.storeFieldNames.entries[storeFieldName].cacheTime = cacheTime; | ||
} | ||
else { | ||
entity.cacheTime = cacheTime; | ||
} | ||
} | ||
} | ||
restore(entitiesById) { | ||
@@ -204,6 +218,6 @@ this.entitiesById = entitiesById; | ||
const { entitiesById, entitiesByType } = this; | ||
return lodash_1.default.cloneDeep({ | ||
return { | ||
entitiesById, | ||
entitiesByType, | ||
}); | ||
}; | ||
} | ||
@@ -210,0 +224,0 @@ clear() { |
import { NormalizedCacheObject } from "@apollo/client"; | ||
import { InvalidationPolicies } from "../policies/types"; | ||
export interface EntityTypeMapConfig { | ||
policies: InvalidationPolicies; | ||
} | ||
export interface TypeMapEntity { | ||
@@ -11,3 +15,3 @@ dataId: string; | ||
[index: string]: { | ||
cacheTime: number; | ||
cacheTime?: number; | ||
variables?: Record<string, any>; | ||
@@ -14,0 +18,0 @@ }; |
export { InvalidationPolicyCache } from "./cache"; | ||
export { InvalidationPolicyCacheAuditor } from "./audit"; | ||
export { InvalidationPolicies, PolicyActionEntity, PolicyActionFields, } from "./policies/types"; | ||
export { InvalidationPolicies, PolicyActionEntity, PolicyActionFields, RenewalPolicy, } from "./policies/types"; | ||
export { InvalidationPolicyCacheConfig } from "./cache/types"; | ||
//# sourceMappingURL=index.d.ts.map |
@@ -7,2 +7,4 @@ "use strict"; | ||
exports.InvalidationPolicyCacheAuditor = audit_1.InvalidationPolicyCacheAuditor; | ||
var types_1 = require("./policies/types"); | ||
exports.RenewalPolicy = types_1.RenewalPolicy; | ||
//# sourceMappingURL=index.js.map |
import { InvalidationPolicyEvent, InvalidationPolicyManagerConfig, PolicyActionMeta } from "./types"; | ||
import { RenewalPolicy } from "./types"; | ||
/** | ||
@@ -14,2 +15,3 @@ * Executes invalidation policies for types when they are modified, evicted or read from the cache. | ||
private runPolicyEvent; | ||
getRenewalPolicyForType(typename: string): RenewalPolicy.AccessOnly | RenewalPolicy; | ||
runWritePolicy(typeName: string, policyMeta: PolicyActionMeta): void; | ||
@@ -16,0 +18,0 @@ runEvictPolicy(typeName: string, policyMeta: PolicyActionMeta): void; |
@@ -10,2 +10,3 @@ "use strict"; | ||
const client_1 = require("@apollo/client"); | ||
const types_2 = require("./types"); | ||
/** | ||
@@ -83,2 +84,7 @@ * Executes invalidation policies for types when they are modified, evicted or read from the cache. | ||
} | ||
getRenewalPolicyForType(typename) { | ||
var _a, _b, _c, _d; | ||
const { policies } = this.config; | ||
return ((_d = (_c = (_b = (_a = policies.types) === null || _a === void 0 ? void 0 : _a[typename]) === null || _b === void 0 ? void 0 : _b.renewalPolicy) !== null && _c !== void 0 ? _c : policies.renewalPolicy) !== null && _d !== void 0 ? _d : types_2.RenewalPolicy.WriteOnly); | ||
} | ||
runWritePolicy(typeName, policyMeta) { | ||
@@ -85,0 +91,0 @@ return this.runPolicyEvent(typeName, types_1.InvalidationPolicyEvent.Write, policyMeta); |
@@ -13,4 +13,11 @@ import { Cache, Reference, StoreObject, StoreValue } from "@apollo/client"; | ||
} | ||
export declare enum RenewalPolicy { | ||
AccessOnly = "access-only", | ||
AccessAndWrite = "access-and-write", | ||
WriteOnly = "write-only", | ||
None = "none" | ||
} | ||
export interface InvalidationPolicies { | ||
timeToLive?: number; | ||
renewalPolicy?: RenewalPolicy; | ||
types?: { | ||
@@ -38,2 +45,3 @@ [typeName: string]: InvalidationPolicy; | ||
timeToLive?: number; | ||
renewalPolicy?: RenewalPolicy; | ||
}; | ||
@@ -40,0 +48,0 @@ export declare type CacheOperations = { |
@@ -14,2 +14,9 @@ "use strict"; | ||
})(InvalidationPolicyLifecycleEvent = exports.InvalidationPolicyLifecycleEvent || (exports.InvalidationPolicyLifecycleEvent = {})); | ||
var RenewalPolicy; | ||
(function (RenewalPolicy) { | ||
RenewalPolicy["AccessOnly"] = "access-only"; | ||
RenewalPolicy["AccessAndWrite"] = "access-and-write"; | ||
RenewalPolicy["WriteOnly"] = "write-only"; | ||
RenewalPolicy["None"] = "none"; | ||
})(RenewalPolicy = exports.RenewalPolicy || (exports.RenewalPolicy = {})); | ||
//# sourceMappingURL=types.js.map |
{ | ||
"name": "apollo-invalidation-policies", | ||
"version": "1.0.0-beta4", | ||
"version": "1.0.0-beta5", | ||
"description": "An extension to the InMemoryCache from Apollo for type-based invalidation policies.", | ||
@@ -42,3 +42,3 @@ "main": "dist/index.js", | ||
"devDependencies": { | ||
"@apollo/client": "^3.1.1", | ||
"@apollo/client": "^3.2.0", | ||
"@types/jest": "^25.1.5", | ||
@@ -45,0 +45,0 @@ "jest": "^25.2.6", |
@@ -21,5 +21,7 @@  | ||
timeToLive: Number; | ||
renewalPolicy: RenewalPolicy; | ||
types: { | ||
Typename: { | ||
timeToLive: Number, | ||
renewalPolicy: RenewalPolicy, | ||
PolicyEvent: { | ||
@@ -34,7 +36,15 @@ Typename: (PolicyActionCacheOperation, PolicyActionEntity) => {} | ||
| Config | Description | Required | Default | | ||
| ---------------| -------------------------------------------------------------------------------------------|----------|---------| | ||
| `timeToLive` | The global time to live in milliseconds for all types in the cache | false | None | | ||
| `types` | The types for which invalidation policies have been defined | false | None | | ||
| Config | Description | Required | Default | | ||
| ----------------| -------------------------------------------------------------------------------------------|----------|-----------| | ||
| `timeToLive` | The global time to live in milliseconds for all types in the cache | false | None | | ||
| `types` | The types for which invalidation policies have been defined | false | None | | ||
| `renewalPolicy` | The policy for renewing an entity's time to live in the cache | false | WriteOnly | | ||
### Renewal policies: | ||
* **AccessOnly** - After first write, the entity in the cache will renew its TTL on read | ||
* **AccessAndWrite** - After first write, the entity will renew its TTL on read or write | ||
* **WriteOnly** - After first write, the entity in the cache will renew its TTL on write | ||
* **None** - After first write, the entity in the cache will never renew its TTL on reads or writes. | ||
| Policy Event | Description | Required | | ||
@@ -51,2 +61,6 @@ | ---------------| -------------------------------------------------------------------------------------------|----------| | ||
| Extended cache API | Description | Return Type | | ||
| -------------------| -------------------------------------------------------------------------------------------|-------------------------------------------------------| | ||
| `expire` | Evicts all expired entities from the cache based on their type's or the global timeToLive. | String[] - List of entity IDs evicted from the cache. | | ||
| Policy Action Entity | Description | Type | Example | | ||
@@ -53,0 +67,0 @@ | ---------------------| --------------------------------------------------------|--------------------| --------------------------------------------------------------------------------------------| |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
557737
2.55%1513
7%275
5.36%