Merge pull request #114 from NearSocial/no-stale

- Add `cacheOptions` optional argument to the following methods:
  - `Social.get(keys, blockId|finality, options, cacheOptions)`
  - `Social.getr(keys, blockId|finality, options, cacheOptions)`
  - `Social.keys(keys, blockId|finality, options, cacheOptions)`
  - `Social.index(action, key, options, cacheOptions)`
  - `Near.view(contractName, methodName, args, blockId|finality, subscribe, cacheOptions)`
  - `Near.block(blockId|finality, subscribe, cacheOptions)`
The `cacheOptions` object is optional and may contain the following property:
  - `ignoreCache` - boolean, if true, the method will ignore the cached value in the local DB and fetch the data from the API server. This will only happen once per session. Default is false.

This is useful to avoid loading stale objects that are likely to change often. For example, the index of posts for the main feed, or notifications.
```jsx
const index = Social.index(
  "post",
  "main",
  {
    limit: 10,
    order: "desc",
  },
  {
    ignoreCache: true,
  }
);
```
This commit is contained in:
Evgeny Kuzyakov 2023-09-11 10:14:36 -07:00 committed by GitHub
commit 27ec908e57
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 138 additions and 52 deletions

View File

@ -2,6 +2,30 @@
## Pending
- Add `cacheOptions` optional argument to the following methods:
- `Social.get(keys, blockId|finality, options, cacheOptions)`
- `Social.getr(keys, blockId|finality, options, cacheOptions)`
- `Social.keys(keys, blockId|finality, options, cacheOptions)`
- `Social.index(action, key, options, cacheOptions)`
- `Near.view(contractName, methodName, args, blockId|finality, subscribe, cacheOptions)`
- `Near.block(blockId|finality, subscribe, cacheOptions)`
The `cacheOptions` object is optional and may contain the following property:
- `ignoreCache` - boolean, if true, the method will ignore the cached value in the local DB and fetch the data from the API server. This will only happen once per session. Default is false.
This is useful to avoid loading stale objects that are likely to change often. For example, the index of posts for the main feed, or notifications.
```jsx
const index = Social.index(
"post",
"main",
{
limit: 10,
order: "desc",
},
{
ignoreCache: true,
}
);
```
- Replace `lodash` dependency with `lodash.clonedeep` to reduce bundle size.
## 2.3.2

2
dist/index.js vendored

File diff suppressed because one or more lines are too long

View File

@ -65,7 +65,7 @@ class Cache {
return (await this.dbPromise).put(CacheDbObject, val, key);
}
cachedPromise(key, promise, invalidate, forceCachedValue) {
cachedPromise(key, promise, invalidate, cacheOptions) {
key = JSON.stringify(key);
const cached = this.cache[key] || {
status: CacheStatus.NotStarted,
@ -106,10 +106,13 @@ class Cache {
) {
return cached.result;
}
if (cached.status === CacheStatus.NotStarted) {
if (
cached.status === CacheStatus.NotStarted &&
!cacheOptions?.ignoreCache
) {
this.innerGet(key).then((cachedResult) => {
if (
(cachedResult || forceCachedValue) &&
(cachedResult || cacheOptions?.forceCachedValue) &&
cached.status === CacheStatus.InProgress
) {
CacheDebug && console.log("Cached value", key, cachedResult);
@ -200,18 +203,27 @@ class Cache {
});
}
cachedBlock(near, blockId, invalidate) {
cachedBlock(near, blockId, invalidate, cacheOptions) {
return this.cachedPromise(
{
action: Action.Block,
blockId,
},
() => near.block(blockId),
invalidate
invalidate,
cacheOptions
);
}
cachedViewCall(near, contractId, methodName, args, blockId, invalidate) {
cachedViewCall(
near,
contractId,
methodName,
args,
blockId,
invalidate,
cacheOptions
) {
return this.cachedPromise(
{
action: Action.ViewCall,
@ -221,7 +233,8 @@ class Cache {
blockId,
},
() => near.viewCall(contractId, methodName, args, blockId),
invalidate
invalidate,
cacheOptions
);
}
@ -266,7 +279,7 @@ class Cache {
}
}
cachedFetch(url, options, invalidate) {
cachedFetch(url, options, invalidate, cacheOptions) {
return this.cachedPromise(
{
action: Action.Fetch,
@ -274,22 +287,24 @@ class Cache {
options,
},
() => this.asyncFetch(url, options),
invalidate
invalidate,
cacheOptions
);
}
cachedCustomPromise(key, promise, invalidate) {
cachedCustomPromise(key, promise, invalidate, cacheOptions) {
return this.cachedPromise(
{
action: Action.CustomPromise,
key,
},
() => promise(),
invalidate
invalidate,
cacheOptions
);
}
socialGet(near, keys, recursive, blockId, options, invalidate) {
socialGet(near, keys, recursive, blockId, options, invalidate, cacheOptions) {
if (!near) {
return null;
}
@ -305,7 +320,8 @@ class Cache {
"get",
args,
blockId,
invalidate
invalidate,
cacheOptions
);
if (data === null) {
return null;
@ -325,7 +341,7 @@ class Cache {
return data;
}
socialIndex(near, action, key, options, invalidate) {
socialIndex(near, action, key, options, invalidate, cacheOptions) {
const res = this.cachedFetch(
`${near.config.apiUrl}/index`,
{
@ -339,7 +355,8 @@ class Cache {
options,
}),
},
invalidate
invalidate,
cacheOptions
);
return res?.ok ? res.body : null;
@ -354,7 +371,9 @@ class Cache {
},
undefined,
invalidate,
true
{
forceCachedValue: true,
}
);
}
@ -390,7 +409,7 @@ class Cache {
}
}
cachedEthersCall(ethersProvider, callee, args, invalidate) {
cachedEthersCall(ethersProvider, callee, args, invalidate, cacheOptions) {
if (!ethersProvider) {
return null;
}
@ -401,7 +420,8 @@ class Cache {
args,
},
() => ethersProvider[callee](...args),
invalidate
invalidate,
cacheOptions
);
}
}
@ -417,14 +437,14 @@ const useSecondaryCache = singletonHook(secondaryCache, () => {
return secondaryCache;
});
export const useCache = networkId => {
export const useCache = (networkId) => {
const near = useNear();
const defaultCache = useDefaultCache();
const secondaryCache = useSecondaryCache();
if(!networkId || networkId === near.config.networkId) {
if (!networkId || networkId === near.config.networkId) {
return defaultCache;
}
return secondaryCache;
}
};

View File

@ -738,7 +738,13 @@ class VmStack {
if (args.length < 1) {
throw new Error("Missing argument 'keys' for Social.getr");
}
return this.vm.cachedSocialGet(args[0], true, args[1], args[2]);
return this.vm.cachedSocialGet(
args[0],
true,
args[1],
args[2],
args[3]
);
} else if (
(keyword === "Social" && callee === "get") ||
callee === "socialGet"
@ -746,19 +752,25 @@ class VmStack {
if (args.length < 1) {
throw new Error("Missing argument 'keys' for Social.get");
}
return this.vm.cachedSocialGet(args[0], false, args[1], args[2]);
return this.vm.cachedSocialGet(
args[0],
false,
args[1],
args[2],
args[3]
);
} else if (keyword === "Social" && callee === "keys") {
if (args.length < 1) {
throw new Error("Missing argument 'keys' for Social.keys");
}
return this.vm.cachedSocialKeys(args[0], args[1], args[2]);
return this.vm.cachedSocialKeys(...args);
} else if (keyword === "Social" && callee === "index") {
if (args.length < 2) {
throw new Error(
"Missing argument 'action' and 'key` for Social.index"
);
}
return this.vm.cachedIndex(args[0], args[1], args[2]);
return this.vm.cachedIndex(...args);
} else if (keyword === "Social" && callee === "set") {
if (args.length < 1) {
throw new Error("Missing argument 'data' for Social.set");
@ -767,17 +779,25 @@ class VmStack {
} else if (keyword === "Near" && callee === "view") {
if (args.length < 2) {
throw new Error(
"Method: Near.view. Required arguments: 'contractName', 'methodName'. Optional: 'args', 'blockId/finality', 'subscribe'"
"Method: Near.view. Required arguments: 'contractName', 'methodName'. Optional: 'args', 'blockId/finality', 'subscribe', 'cacheOptions'"
);
}
const [contractName, methodName, viewArg, blockId, subscribe] = args;
const [
contractName,
methodName,
viewArg,
blockId,
subscribe,
cacheOptions,
] = args;
return this.vm.cachedNearView(
contractName,
methodName,
viewArg,
blockId,
maybeSubscribe(subscribe, blockId)
maybeSubscribe(subscribe, blockId),
cacheOptions
);
} else if (keyword === "Near" && callee === "asyncView") {
if (args.length < 2) {
@ -787,10 +807,11 @@ class VmStack {
}
return this.vm.asyncNearView(...args);
} else if (keyword === "Near" && callee === "block") {
const [blockId, subscribe] = args;
const [blockId, subscribe, cacheOptions] = args;
return this.vm.cachedNearBlock(
blockId,
maybeSubscribe(subscribe, blockId)
maybeSubscribe(subscribe, blockId),
cacheOptions
);
} else if (keyword === "Near" && callee === "call") {
if (args.length === 1) {
@ -1807,7 +1828,7 @@ export default class VM {
return deepCopy(promise(invalidate));
}
cachedSocialGet(keys, recursive, blockId, options) {
cachedSocialGet(keys, recursive, blockId, options, cacheOptions) {
keys = Array.isArray(keys) ? keys : [keys];
return this.cachedPromise(
(invalidate) =>
@ -1817,7 +1838,8 @@ export default class VM {
recursive,
blockId,
options,
invalidate
invalidate,
cacheOptions
),
options?.subscribe
);
@ -1833,7 +1855,7 @@ export default class VM {
return this.cache.localStorageSet(domain, key, value);
}
cachedSocialKeys(keys, blockId, options) {
cachedSocialKeys(keys, blockId, options, cacheOptions) {
keys = Array.isArray(keys) ? keys : [keys];
return this.cachedPromise(
(invalidate) =>
@ -1846,7 +1868,8 @@ export default class VM {
options,
},
blockId,
invalidate
invalidate,
cacheOptions
),
options?.subscribe
);
@ -1856,20 +1879,28 @@ export default class VM {
return this.near.viewCall(contractName, methodName, args, blockId);
}
cachedEthersCall(callee, args, subscribe) {
cachedEthersCall(callee, args, subscribe, cacheOptions) {
return this.cachedPromise(
(invalidate) =>
this.cache.cachedEthersCall(
this.ethersProvider,
callee,
args,
invalidate
invalidate,
cacheOptions
),
subscribe
);
}
cachedNearView(contractName, methodName, args, blockId, subscribe) {
cachedNearView(
contractName,
methodName,
args,
blockId,
subscribe,
cacheOptions
) {
return this.cachedPromise(
(invalidate) =>
this.cache.cachedViewCall(
@ -1878,15 +1909,17 @@ export default class VM {
methodName,
args,
blockId,
invalidate
invalidate,
cacheOptions
),
subscribe
);
}
cachedNearBlock(blockId, subscribe) {
cachedNearBlock(blockId, subscribe, cacheOptions) {
return this.cachedPromise(
(invalidate) => this.cache.cachedBlock(this.near, blockId, invalidate),
(invalidate) =>
this.cache.cachedBlock(this.near, blockId, invalidate, cacheOptions),
subscribe
);
}
@ -1895,22 +1928,30 @@ export default class VM {
return this.cache.asyncFetch(url, options);
}
cachedFetch(url, options) {
return this.cachedPromise(
(invalidate) => this.cache.cachedFetch(url, options, invalidate),
options?.subscribe
);
}
cachedIndex(action, key, options) {
cachedFetch(url, options, cacheOptions) {
return this.cachedPromise(
(invalidate) =>
this.cache.socialIndex(this.near, action, key, options, invalidate),
this.cache.cachedFetch(url, options, invalidate, cacheOptions),
options?.subscribe
);
}
useCache(promiseGenerator, dataKey, options) {
cachedIndex(action, key, options, cacheOptions) {
return this.cachedPromise(
(invalidate) =>
this.cache.socialIndex(
this.near,
action,
key,
options,
invalidate,
cacheOptions
),
options?.subscribe
);
}
useCache(promiseGenerator, dataKey, options, cacheOptions) {
return this.cachedPromise(
(invalidate) =>
this.cache.cachedCustomPromise(
@ -1919,7 +1960,8 @@ export default class VM {
dataKey,
},
promiseGenerator,
invalidate
invalidate,
cacheOptions
),
options?.subscribe
);