@gooddata/sdk-backend-base 11.49.0-alpha.1 → 11.50.0-alpha.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/cachingBackend/index.ts"],"names":[],"mappings":"AAQA,OAAO,EAGH,KAAK,kBAAkB,EAgD1B,MAAM,2BAA2B,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/cachingBackend/index.ts"],"names":[],"mappings":"AAQA,OAAO,EAGH,KAAK,kBAAkB,EAgD1B,MAAM,2BAA2B,CAAC;AAwvFnC;;;;;GAKG;AACH,MAAM,MAAM,YAAY,GAAG;IACvB;;;;;OAKG;IACH,eAAe,EAAE,MAAM,IAAI,CAAC;IAE5B;;OAEG;IACH,aAAa,EAAE,MAAM,IAAI,CAAC;IAE1B;;OAEG;IACH,qBAAqB,EAAE,MAAM,IAAI,CAAC;IAElC;;OAEG;IACH,eAAe,EAAE,MAAM,IAAI,CAAC;IAE5B;;OAEG;IACH,UAAU,EAAE,MAAM,IAAI,CAAC;IAEvB;;OAEG;IACH,aAAa,EAAE,MAAM,IAAI,CAAC;IAE1B;;OAEG;IACH,sBAAsB,EAAE,MAAM,IAAI,CAAC;IAEnC;;OAEG;IACH,cAAc,EAAE,MAAM,IAAI,CAAC;IAE3B;;OAEG;IACH,oBAAoB,EAAE,MAAM,IAAI,CAAC;IAEjC;;OAEG;IACH,aAAa,EAAE,MAAM,IAAI,CAAC;IAE1B;;OAEG;IACH,aAAa,EAAE,MAAM,IAAI,CAAC;IAE1B;;OAEG;IACH,QAAQ,EAAE,MAAM,IAAI,CAAC;CACxB,CAAC;AAEF;;;;GAIG;AACH,MAAM,MAAM,oBAAoB,GAAG;IAC/B;;;;;;;;;;;;;;OAcG;IACH,aAAa,CAAC,EAAE,MAAM,CAAC;IAEvB;;;;;;;;;;;OAWG;IACH,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAE1B;;;;;;;;;;;OAWG;IACH,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAE1B;;;;;;;;;OASG;IACH,8BAA8B,CAAC,EAAE,MAAM,CAAC;IAExC;;;;;;;;;;;OAWG;IACH,WAAW,CAAC,EAAE,MAAM,CAAC;IAErB;;;;;;;;;;;;OAYG;IACH,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAE3B;;;;;OAKG;IACH,YAAY,CAAC,EAAE,CAAC,YAAY,EAAE,YAAY,KAAK,IAAI,CAAC;IAEpD;;;;;;;;;;;OAWG;IACH,uBAAuB,CAAC,EAAE,MAAM,CAAC;IAEjC;;;;;;;;;;;;OAYG;IACH,0BAA0B,CAAC,EAAE,MAAM,CAAC;IAEpC;;;;;;;;;;OAUG;IACH,6BAA6B,CAAC,EAAE,MAAM,CAAC;IAEvC;;;;;;;;;;;;OAYG;IACH,sBAAsB,CAAC,EAAE,MAAM,CAAC;IAEhC;;;;;;;;;;;OAWG;IACH,wBAAwB,CAAC,EAAE,MAAM,CAAC;IAElC;;;;;;;;;;;;;OAaG;IACH,uBAAuB,CAAC,EAAE,MAAM,CAAC;IAEjC;;;;;;;;;;;;OAYG;IACH,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAE5B;;;;;;;;;;;;;;OAcG;IACH,oBAAoB,CAAC,EAAE,MAAM,CAAC;IAE9B;;;;;;;;;;;;OAYG;IACH,qBAAqB,CAAC,EAAE,MAAM,CAAC;IAE/B;;;;;;;;;;;OAWG;IACH,uBAAuB,CAAC,EAAE,MAAM,CAAC;IAEjC;;;;;;;;;;;;OAYG;IACH,oBAAoB,CAAC,EAAE,MAAM,CAAC;IAE9B;;;;;;;;;;;OAWG;IACH,uBAAuB,CAAC,EAAE,MAAM,CAAC;IAEjC;;;;;OAKG;IACH,sBAAsB,CAAC,EAAE,MAAM,CAAC;IAEhC;;;;;;;;;;OAUG;IACH,4BAA4B,CAAC,EAAE,MAAM,CAAC;IAEtC;;;;;;;;;;;OAWG;IACH,oCAAoC,CAAC,EAAE,MAAM,CAAC;IAE9C;;;;;;;;;;;OAWG;IACH,yBAAyB,CAAC,EAAE,MAAM,CAAC;IAEnC;;;;;;;;;;;;OAYG;IACH,+BAA+B,CAAC,EAAE,MAAM,CAAC;IAEzC;;;;;;;;;;;;OAYG;IACH,kCAAkC,CAAC,EAAE,MAAM,CAAC;IAE5C;;;;;;;;;;;;;OAaG;IACH,sCAAsC,CAAC,EAAE,MAAM,CAAC;IAEhD;;;;;;;;;;OAUG;IACH,oBAAoB,CAAC,EAAE,MAAM,CAAC;IAE9B;;;;;;;;;;OAUG;IACH,cAAc,CAAC,EAAE,OAAO,CAAC;CAC5B,CAAC;AASF;;;;;;;GAOG;AACH,eAAO,MAAM,+BAA+B,EAAE,oBA2B7C,CAAC;AAEF;;;;;;;;GAQG;AACH,wBAAgB,WAAW,CACvB,WAAW,EAAE,kBAAkB,EAC/B,MAAM,EAAE,oBAAoB,GAC7B,kBAAkB,CA+GpB"}
|
|
@@ -823,43 +823,88 @@ class WithAttributesCaching extends DecoratedWorkspaceAttributesService {
|
|
|
823
823
|
};
|
|
824
824
|
getAttributeDisplayForms = async (refs) => {
|
|
825
825
|
const cache = getOrCreateAttributeCache(this.ctx, this.workspace).displayForms;
|
|
826
|
-
//
|
|
827
|
-
// then
|
|
826
|
+
// Grab a reference to the cache results as soon as possible in case they would get evicted while
|
|
827
|
+
// loading the ones missing from the cache - then we might not be able to call cache.get again and
|
|
828
|
+
// be guaranteed to get the data.
|
|
828
829
|
const refsWithCacheResults = refs.map((ref) => {
|
|
829
|
-
const idCacheKey =
|
|
830
|
-
const uriCacheKey = isUriRef(ref) ? ref.uri : undefined;
|
|
830
|
+
const { idCacheKey, uriCacheKey } = refCacheKeys(ref);
|
|
831
831
|
const cacheHit = firstDefined([idCacheKey, uriCacheKey].map((key) => key && cache.get(key)));
|
|
832
832
|
return {
|
|
833
833
|
ref,
|
|
834
|
+
idCacheKey,
|
|
835
|
+
uriCacheKey,
|
|
834
836
|
cacheHit: cacheHit,
|
|
835
837
|
};
|
|
836
838
|
});
|
|
837
839
|
const [withCacheHits, withoutCacheHits] = partition(refsWithCacheResults, ({ cacheHit }) => !!cacheHit);
|
|
838
840
|
const refsToLoad = withoutCacheHits.map((item) => item.ref);
|
|
839
|
-
|
|
840
|
-
|
|
841
|
+
// Use a single shared load promise so that concurrent callers asking for the same uncached refs
|
|
842
|
+
// collapse onto one underlying request. Per-ref promises are derived from it and registered into
|
|
843
|
+
// the cache synchronously - before any await - so a concurrent caller sees the in-flight promise.
|
|
844
|
+
const loadPromise = refsToLoad.length
|
|
845
|
+
? Promise.resolve(this.decorated.getAttributeDisplayForms(refsToLoad))
|
|
846
|
+
: Promise.resolve([]);
|
|
847
|
+
withoutCacheHits.forEach((item) => {
|
|
848
|
+
const perRefPromise = loadPromise
|
|
849
|
+
.then((loaded) => {
|
|
850
|
+
const match = loaded.find((df) => refMatchesMdObject(item.ref, df, "displayForm"));
|
|
851
|
+
if (!match) {
|
|
852
|
+
// The server omitted this ref; evict so a later call can retry it and so a
|
|
853
|
+
// resolved-undefined is never cached.
|
|
854
|
+
if (item.idCacheKey) {
|
|
855
|
+
cache.delete(item.idCacheKey);
|
|
856
|
+
}
|
|
857
|
+
if (item.uriCacheKey) {
|
|
858
|
+
cache.delete(item.uriCacheKey);
|
|
859
|
+
}
|
|
860
|
+
}
|
|
861
|
+
return match;
|
|
862
|
+
})
|
|
863
|
+
.catch((e) => {
|
|
864
|
+
if (item.idCacheKey) {
|
|
865
|
+
cache.delete(item.idCacheKey);
|
|
866
|
+
}
|
|
867
|
+
if (item.uriCacheKey) {
|
|
868
|
+
cache.delete(item.uriCacheKey);
|
|
869
|
+
}
|
|
870
|
+
throw e;
|
|
871
|
+
});
|
|
872
|
+
// Prevent an unhandled rejection if nobody else awaits this per-ref promise (the primary caller
|
|
873
|
+
// below awaits `loadPromise` directly). Concurrent callers awaiting the cached promise still see
|
|
874
|
+
// the rejection through their own reference.
|
|
875
|
+
perRefPromise.catch(() => undefined);
|
|
876
|
+
// Reuse the per-ref promise as this call's own cache hit so the assembled result awaits it, and
|
|
877
|
+
// register it so later callers share it. Cast is safe: an omitted ref resolves to undefined but
|
|
878
|
+
// is compacted out of the result below (and never survives in the cache).
|
|
879
|
+
item.cacheHit = perRefPromise;
|
|
880
|
+
if (item.idCacheKey) {
|
|
881
|
+
cache.set(item.idCacheKey, item.cacheHit);
|
|
882
|
+
}
|
|
883
|
+
if (item.uriCacheKey) {
|
|
884
|
+
cache.set(item.uriCacheKey, item.cacheHit);
|
|
885
|
+
}
|
|
886
|
+
});
|
|
887
|
+
const [loadedFromServer, alreadyInCache] = await Promise.all([
|
|
888
|
+
loadPromise,
|
|
841
889
|
Promise.all(withCacheHits.map((item) => item.cacheHit)),
|
|
842
|
-
// load items not in cache using the bulk operation
|
|
843
|
-
this.decorated.getAttributeDisplayForms(refsToLoad),
|
|
844
890
|
]);
|
|
845
|
-
// save newly loaded to cache for future reference
|
|
846
|
-
loadedFromServer.forEach((loaded) => {
|
|
847
|
-
const promisifiedResult = Promise.resolve(loaded);
|
|
848
|
-
// save the cache item for both types of refs
|
|
849
|
-
cache.set(loaded.id, promisifiedResult);
|
|
850
|
-
cache.set(loaded.uri, promisifiedResult);
|
|
851
|
-
});
|
|
852
891
|
const loadedRefs = loadedFromServer.map((item) => item.ref);
|
|
853
892
|
const outputRefs = this.ctx.capabilities.allowsInconsistentRelations
|
|
854
893
|
? skipMissingReferences(refs, refsToLoad, loadedRefs)
|
|
855
894
|
: refs;
|
|
856
|
-
//
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
895
|
+
// Reconstruct the original ordering. Derive the output from what actually resolved (compact) rather
|
|
896
|
+
// than from refsToLoad, so a concurrent caller sharing the in-flight promise of a ref the server
|
|
897
|
+
// omitted does not trip the invariant below.
|
|
898
|
+
const candidates = compact([...loadedFromServer, ...alreadyInCache]);
|
|
899
|
+
return outputRefs
|
|
900
|
+
.map((ref) => candidates.find((item) => refMatchesMdObject(ref, item, "displayForm")))
|
|
901
|
+
.filter((match) => {
|
|
902
|
+
// When the backend guarantees consistent relations, every requested ref must resolve.
|
|
903
|
+
if (!match) {
|
|
904
|
+
invariant(this.ctx.capabilities.allowsInconsistentRelations);
|
|
905
|
+
return false;
|
|
906
|
+
}
|
|
907
|
+
return true;
|
|
863
908
|
});
|
|
864
909
|
};
|
|
865
910
|
getAttributeByDisplayForm = async (ref) => {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@gooddata/sdk-backend-base",
|
|
3
|
-
"version": "11.
|
|
3
|
+
"version": "11.50.0-alpha.0",
|
|
4
4
|
"description": "GoodData.UI SDK - Base for backend implementations",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "GoodData",
|
|
@@ -29,9 +29,9 @@
|
|
|
29
29
|
"ts-invariant": "0.10.3",
|
|
30
30
|
"tslib": "2.8.1",
|
|
31
31
|
"uuid": "11.1.1",
|
|
32
|
-
"@gooddata/sdk-
|
|
33
|
-
"@gooddata/
|
|
34
|
-
"@gooddata/
|
|
32
|
+
"@gooddata/sdk-backend-spi": "11.50.0-alpha.0",
|
|
33
|
+
"@gooddata/sdk-model": "11.50.0-alpha.0",
|
|
34
|
+
"@gooddata/util": "11.50.0-alpha.0"
|
|
35
35
|
},
|
|
36
36
|
"devDependencies": {
|
|
37
37
|
"@microsoft/api-documenter": "^7.17.0",
|
|
@@ -56,9 +56,9 @@
|
|
|
56
56
|
"oxlint-tsgolint": "0.15.0",
|
|
57
57
|
"typescript": "5.9.3",
|
|
58
58
|
"vitest": "4.1.8",
|
|
59
|
-
"@gooddata/eslint-config": "11.
|
|
60
|
-
"@gooddata/oxlint-config": "11.
|
|
61
|
-
"@gooddata/reference-workspace": "11.
|
|
59
|
+
"@gooddata/eslint-config": "11.50.0-alpha.0",
|
|
60
|
+
"@gooddata/oxlint-config": "11.50.0-alpha.0",
|
|
61
|
+
"@gooddata/reference-workspace": "11.50.0-alpha.0"
|
|
62
62
|
},
|
|
63
63
|
"scripts": {
|
|
64
64
|
"_phase:build": "npm run build",
|