@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":"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"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/cachingBackend/index.ts"],"names":[],"mappings":"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"}
@@ -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
- // grab a reference to the cache results as soon as possible in case they would get evicted while loading the ones with missing data in cache
827
- // then would might not be able to call cache.get again and be guaranteed to get the data
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 = isIdentifierRef(ref) ? ref.identifier : undefined;
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
- const [alreadyInCache, loadedFromServer] = await Promise.all([
840
- // await the stuff from cache, we need the data available (we cannot just return the promises)
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
- // reconstruct the original ordering
857
- const candidates = [...loadedFromServer, ...alreadyInCache];
858
- return outputRefs.map((ref) => {
859
- const match = candidates.find((item) => refMatchesMdObject(ref, item, "displayForm"));
860
- // if this bombs, some data got lost in the process
861
- invariant(match);
862
- return match;
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.49.0-alpha.1",
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-model": "11.49.0-alpha.1",
33
- "@gooddata/util": "11.49.0-alpha.1",
34
- "@gooddata/sdk-backend-spi": "11.49.0-alpha.1"
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.49.0-alpha.1",
60
- "@gooddata/oxlint-config": "11.49.0-alpha.1",
61
- "@gooddata/reference-workspace": "11.49.0-alpha.1"
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",