Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(react-components): improve loading reveal 3d resource by removing unnecessary caching #4874

Open
wants to merge 5 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,7 @@
"RGB": "RGB",
"RULESET_NO_SELECTION": "No Rule Set Selected",
"RULESET_SELECT_HEADER": "Select color overlay",
"RULESET_SELECT_HEADER_LOADING": "Color overlay loading",
"SCENE_SELECT_HEADER": "Select 3D location",
"SEARCH_PLACEHOLDER": "Search",
"SELECT_INDIVIDUAL_3D_MODELS": "Select individual models",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,10 @@ import {
type CogniteClient,
type AssetMapping3D,
type Node3D,
type CogniteInternalId
type CogniteInternalId,
type AssetMappings3DAssetFilter,
type AssetMappings3DNodeFilter,
type AssetMappings3DTreeIndexFilter
} from '@cognite/sdk';
import {
type ModelTreeIndexKey,
Expand All @@ -16,7 +19,7 @@ import {
type ChunkInCacheTypes,
type ModelAssetIdKey
} from './types';
import { chunk, maxBy } from 'lodash';
import { chunk, maxBy, uniqBy } from 'lodash';
import assert from 'assert';
import { isValidAssetMapping } from './utils';
import { modelRevisionNodesAssetToKey, createModelRevisionKey } from './idAndKeyTranslation';
Expand All @@ -29,6 +32,12 @@ import { AssetMappingPerModelCache } from './AssetMappingPerModelCache';
export type NodeAssetMappingResult = { node?: Node3D; mappings: AssetMapping[] };

export type AssetMapping = Required<AssetMapping3D>;

export type FiltersTypeForAssetMappings =
| AssetMappings3DAssetFilter
| AssetMappings3DNodeFilter
| AssetMappings3DTreeIndexFilter;

export class AssetMappingAndNode3DCache {
private readonly _sdk: CogniteClient;

Expand Down Expand Up @@ -108,9 +117,7 @@ export class AssetMappingAndNode3DCache {
const allAssetMappings = await Promise.all(allAssetMappingsReturned);
const assetMappings = allAssetMappings.flat();

const relevantAssetMappings = assetMappings.filter((mapping) =>
relevantAssetIds.has(mapping.assetId)
);
const relevantAssetMappings = uniqBy(assetMappings, 'assetId');

const nodes = await this.nodeIdsToNode3DCache.getNodesForNodeIds(
modelId,
Expand Down Expand Up @@ -140,25 +147,26 @@ export class AssetMappingAndNode3DCache {
await this.nodeIdsToNode3DCache.generateNode3DCachePerItem(modelId, revisionId, nodeIds);
}

public async generateAssetMappingsCachePerItemFromModelCache(
public generateAssetMappingsCachePerItemFromModelCache(
modelId: ModelId,
revisionId: RevisionId,
assetMappingsPerModel: ModelWithAssetMappings[] | undefined
): Promise<void> {
): void {
if (assetMappingsPerModel === undefined) {
return;
}
assetMappingsPerModel.forEach(async (modelMapping) => {
modelMapping.assetMappings.forEach(async (item) => {
const key = modelRevisionNodesAssetToKey(modelId, revisionId, item.assetId);
await this.assetIdsToAssetMappingCache.setAssetMappingsCacheItem(key, item);
this.assetIdsToAssetMappingCache.setAssetMappingsCacheItem(key, item);
});
});
}

public async getAssetMappingsForModel(
modelId: ModelId,
revisionId: RevisionId
revisionId: RevisionId,
filter: FiltersTypeForAssetMappings | undefined
): Promise<AssetMapping[]> {
const key = createModelRevisionKey(modelId, revisionId);
const cachedResult = await this.modelToAssetMappingsCache.getModelToAssetMappingCacheItems(key);
Expand All @@ -167,7 +175,11 @@ export class AssetMappingAndNode3DCache {
return cachedResult;
}

return await this.modelToAssetMappingsCache.fetchAndCacheMappingsForModel(modelId, revisionId);
return await this.modelToAssetMappingsCache.fetchAndCacheMappingsForModel(
modelId,
revisionId,
filter
);
}

private async splitChunkInCacheAssetMappings(
Expand All @@ -182,7 +194,7 @@ export class AssetMappingAndNode3DCache {
await Promise.all(
currentChunk.map(async (id) => {
const key = modelRevisionNodesAssetToKey(modelId, revisionId, id);
const cachedResult = await this.getItemCacheResult(type, key);
const cachedResult = this.getItemCacheResult(type, key);
if (cachedResult !== undefined) {
chunkInCache.push(...cachedResult);
} else {
Expand All @@ -194,21 +206,21 @@ export class AssetMappingAndNode3DCache {
return { chunkInCache, chunkNotInCache: chunkNotCached };
}

private async getItemCacheResult(
private getItemCacheResult(
type: string,
key: ModelTreeIndexKey | ModelAssetIdKey
): Promise<AssetMapping[] | undefined> {
): AssetMapping[] | undefined {
return type === 'nodeIds'
? await this.nodeIdsToAssetMappingCache.getNodeIdsToAssetMappingCacheItem(key)
: await this.assetIdsToAssetMappingCache.getAssetIdsToAssetMappingCacheItem(key);
? this.nodeIdsToAssetMappingCache.getNodeIdsToAssetMappingCacheItem(key)
: this.assetIdsToAssetMappingCache.getAssetIdsToAssetMappingCacheItem(key);
}

private setItemCacheResult(
type: string,
key: ModelTreeIndexKey | ModelAssetIdKey,
item: AssetMapping[] | undefined
): void {
const value = Promise.resolve(item ?? []);
const value = item ?? [];
type === 'nodeIds'
? this.nodeIdsToAssetMappingCache.setNodeIdsToAssetMappingCacheItem(key, value)
: this.assetIdsToAssetMappingCache.setAssetIdsToAssetMappingCacheItem(key, value);
Expand All @@ -220,22 +232,15 @@ export class AssetMappingAndNode3DCache {
modelId: ModelId,
revisionId: RevisionId
): Promise<AssetMapping[]> {
let assetMapping3D: AssetMapping3D[] = [];

if (currentChunk.length === 0) {
return [];
}
const filter =
filterType === 'nodeIds' ? { nodeIds: currentChunk } : { assetIds: currentChunk };

assetMapping3D = await this._sdk.assetMappings3D
.filter(modelId, revisionId, {
limit: 1000,
filter
})
.autoPagingToArray({ limit: Infinity });
const assetMappings3D = await this.getAssetMappingsForModel(modelId, revisionId, filter);

assetMapping3D.forEach(async (item) => {
assetMappings3D.forEach(async (item) => {
const keyAssetId: ModelAssetIdKey = modelRevisionNodesAssetToKey(
modelId,
revisionId,
Expand All @@ -246,20 +251,11 @@ export class AssetMappingAndNode3DCache {
revisionId,
item.nodeId
);
await this.assetIdsToAssetMappingCache.setAssetMappingsCacheItem(keyAssetId, item);
await this.nodeIdsToAssetMappingCache.setAssetMappingsCacheItem(keyNodeId, item);
});

currentChunk.forEach(async (id) => {
const key = modelRevisionNodesAssetToKey(modelId, revisionId, id);
const cachedResult = await this.getItemCacheResult(filterType, key);

if (cachedResult === undefined) {
this.setItemCacheResult(filterType, key, []);
}
this.assetIdsToAssetMappingCache.setAssetMappingsCacheItem(keyAssetId, item);
this.nodeIdsToAssetMappingCache.setAssetMappingsCacheItem(keyNodeId, item);
});

return assetMapping3D.filter(isValidAssetMapping);
return assetMappings3D.filter(isValidAssetMapping);
}

private async fetchMappingsInQueue(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,13 +40,23 @@ const useAssetMappingAndNode3DCache = (): AssetMappingAndNode3DCache => {
return content.cache;
};

export const useGenerateCadAssetMappingsCache = (
enabled: boolean,
assetMappings: ModelWithAssetMappings[] | undefined,
cadModelOptions: CadModelOptions[]
): void => {
useGenerateNode3DCache(enabled, cadModelOptions, assetMappings);
useGenerateAssetMappingCachePerItemFromModelCache(enabled, cadModelOptions, assetMappings);
};

export const useGenerateNode3DCache = (
enabled: boolean,
cadModelOptions: CadModelOptions[],
assetMappings: ModelWithAssetMappings[] | undefined
): void => {
const assetMappingAndNode3DCache = useAssetMappingAndNode3DCache();

useMemo(() => {
if (assetMappings === undefined || !enabled) return;
const assetMappingAndNode3DCache = useAssetMappingAndNode3DCache();
cadModelOptions.forEach(async ({ modelId, revisionId }) => {
const assetMapping = assetMappings?.filter(
(item) => item.model.modelId === modelId && item.model.revisionId === revisionId
Expand All @@ -63,28 +73,31 @@ export const useGenerateNode3DCache = (
nodeIdsFromAssetMappings
);
});
}, [cadModelOptions, assetMappings]);
}, [cadModelOptions.length, assetMappings?.length, enabled]);
};

export const useGenerateAssetMappingCachePerItemFromModelCache = (
enabled: boolean,
cadModelOptions: CadModelOptions[],
assetMappings: ModelWithAssetMappings[] | undefined
): void => {
const assetMappingAndNode3DCache = useAssetMappingAndNode3DCache();
useMemo(() => {
if (assetMappings === undefined || !enabled) return;
const assetMappingAndNode3DCache = useAssetMappingAndNode3DCache();

cadModelOptions.forEach(async ({ modelId, revisionId }) => {
const assetMapping = assetMappings?.filter(
(item) => item.model.modelId === modelId && item.model.revisionId === revisionId
);
if (assetMapping !== undefined && assetMapping.length > 0) {
await assetMappingAndNode3DCache.generateAssetMappingsCachePerItemFromModelCache(
assetMappingAndNode3DCache.generateAssetMappingsCachePerItemFromModelCache(
modelId,
revisionId,
assetMapping
);
}
});
}, [cadModelOptions, assetMappings]);
}, [cadModelOptions.length, assetMappings?.length, enabled]);
};

export const useAssetMappedNodesForRevisions = (
Expand All @@ -103,7 +116,7 @@ export const useAssetMappedNodesForRevisions = (
const fetchPromises = cadModels.map(
async (model) =>
await assetMappingAndNode3DCache
.getAssetMappingsForModel(model.modelId, model.revisionId)
.getAssetMappingsForModel(model.modelId, model.revisionId, undefined)
.then((assetMappings) => ({ model, assetMappings }))
);
return await Promise.all(fetchPromises);
Expand All @@ -128,6 +141,7 @@ export const useNodesForAssets = (
...assetIds.map((id) => `${id}`)
],
queryFn: async () => {
console.log('TEST RC useNodesForAssets models', models, assetIds.length);
const modelAndNodeMapPromises = models.map(async (model) => {
const nodeMap = await assetMappingAndNode3DCache.getNodesForAssetIds(
model.modelId,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,32 +6,31 @@ import { type ModelAssetIdKey } from './types';
import { type AssetMapping } from './AssetMappingAndNode3DCache';

export class AssetMappingPerAssetIdCache {
private readonly _assetIdsToAssetMappings = new Map<ModelAssetIdKey, Promise<AssetMapping[]>>();
private readonly _assetIdsToAssetMappings = new Map<ModelAssetIdKey, AssetMapping[]>();

public setAssetIdsToAssetMappingCacheItem(
key: ModelAssetIdKey,
item: Promise<Array<Required<AssetMapping3D>>>
item: Array<Required<AssetMapping3D>>
): void {
this._assetIdsToAssetMappings.set(key, Promise.resolve(item));
this._assetIdsToAssetMappings.set(key, item);
}

public async getAssetIdsToAssetMappingCacheItem(
key: ModelAssetIdKey
): Promise<AssetMapping[] | undefined> {
return await this._assetIdsToAssetMappings.get(key);
public getAssetIdsToAssetMappingCacheItem(key: ModelAssetIdKey): AssetMapping[] | undefined {
return this._assetIdsToAssetMappings.get(key);
}

public async setAssetMappingsCacheItem(key: ModelAssetIdKey, item: AssetMapping): Promise<void> {
public setAssetMappingsCacheItem(key: ModelAssetIdKey, item: AssetMapping): void {
const currentAssetMappings = this.getAssetIdsToAssetMappingCacheItem(key);
this.setAssetIdsToAssetMappingCacheItem(
key,
currentAssetMappings.then((value) => {
if (value === undefined) {
return [item];
}
value.push(item);
return value;
})
);
let assetMappings: Array<Required<AssetMapping3D>> | undefined = currentAssetMappings;

if (assetMappings === undefined) {
assetMappings = [item];
} else if (
assetMappings.find((assetMapping) => assetMapping.nodeId === item.nodeId) === undefined
) {
assetMappings.push(item);
}

this.setAssetIdsToAssetMappingCacheItem(key, assetMappings);
}
}
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
/*!
* Copyright 2024 Cognite AS
*/
import { type CogniteClient, type AssetMapping3D } from '@cognite/sdk/dist/src';
import { type CogniteClient, type AssetMapping3D } from '@cognite/sdk';
import { type ModelId, type RevisionId, type ModelRevisionKey } from './types';
import { type AssetMapping } from './AssetMappingAndNode3DCache';
import { type FiltersTypeForAssetMappings, type AssetMapping } from './AssetMappingAndNode3DCache';
import { isValidAssetMapping } from './utils';
import { createModelRevisionKey } from './idAndKeyTranslation';

Expand Down Expand Up @@ -31,21 +31,51 @@ export class AssetMappingPerModelCache {

public async fetchAndCacheMappingsForModel(
modelId: ModelId,
revisionId: RevisionId
revisionId: RevisionId,
filter: FiltersTypeForAssetMappings | undefined
): Promise<AssetMapping[]> {
const key = createModelRevisionKey(modelId, revisionId);
const assetMappings = this.fetchAssetMappingsForModel(modelId, revisionId);
const assetMappings = this.fetchAssetMappingsForModel(modelId, revisionId, filter);

this.setModelToAssetMappingCacheItems(key, assetMappings);
return await assetMappings;
}

private async fetchAssetMappingsForModel(
modelId: ModelId,
revisionId: RevisionId,
filter: FiltersTypeForAssetMappings | undefined
): Promise<AssetMapping[]> {
const assetMappings =
filter !== undefined
? this.fetchAssetMappingsForModelWithFilter(modelId, revisionId, filter)
: this.fetchAssetMappingsForModelWithoutFilter(modelId, revisionId);
return await assetMappings;
}

private async fetchAssetMappingsForModelWithoutFilter(
modelId: ModelId,
revisionId: RevisionId
): Promise<AssetMapping[]> {
const assetMapping3D = await this._sdk.assetMappings3D
.list(modelId, revisionId, { limit: 1000 })
.list(modelId, revisionId, {
limit: 1000
})
.autoPagingToArray({ limit: Infinity });

return assetMapping3D.filter(isValidAssetMapping);
}

private async fetchAssetMappingsForModelWithFilter(
modelId: ModelId,
revisionId: RevisionId,
filter: FiltersTypeForAssetMappings
): Promise<AssetMapping[]> {
const assetMapping3D = await this._sdk.assetMappings3D
.filter(modelId, revisionId, {
limit: 1000,
filter
})
.autoPagingToArray({ limit: Infinity });

return assetMapping3D.filter(isValidAssetMapping);
Expand Down
Loading