@itwin/core-backend
Version:
iTwin.js backend components
198 lines • 9.17 kB
JavaScript
/*---------------------------------------------------------------------------------------------
* Copyright (c) Bentley Systems, Incorporated. All rights reserved.
* See LICENSE.md in the project root for license terms and full copyright notice.
*--------------------------------------------------------------------------------------------*/
/** @packageDocumentation
* @module RpcInterface
*/
Object.defineProperty(exports, "__esModule", { value: true });
exports.IModelTileRpcImpl = void 0;
exports.cancelTileContentRequests = cancelTileContentRequests;
const core_bentley_1 = require("@itwin/core-bentley");
const core_common_1 = require("@itwin/core-common");
const BackendLoggerCategory_1 = require("../BackendLoggerCategory");
const IModelDb_1 = require("../IModelDb");
const IModelHost_1 = require("../IModelHost");
const PromiseMemoizer_1 = require("../PromiseMemoizer");
const tracing_1 = require("../rpc/tracing");
const RpcBriefcaseUtility_1 = require("./RpcBriefcaseUtility");
const NativePlatform_1 = require("../internal/NativePlatform");
const Symbols_1 = require("../internal/Symbols");
function generateTileRequestKey(props) {
const token = props.tokenProps;
return `${JSON.stringify({
key: token.key,
iTwinId: token.iTwinId,
iModelId: token.iModelId,
changesetId: token.changeset?.id,
})}:${props.treeId}`;
}
class TileRequestMemoizer extends PromiseMemoizer_1.PromiseMemoizer {
_loggerCategory = BackendLoggerCategory_1.BackendLoggerCategory.IModelTileRequestRpc;
makeMetadata(props) {
const meta = { ...props.tokenProps };
this.addMetadata(meta, props);
return meta;
}
constructor(memoizeFn, generateKeyFn) {
super(memoizeFn, generateKeyFn);
}
memoize(props) {
return super.memoize(props);
}
deleteMemoized(props) {
super.deleteMemoized(props);
}
log(status, props) {
const descr = `${this._operationName}(${this.stringify(props)})`;
core_bentley_1.Logger.logTrace(this._loggerCategory, `Backend ${status} ${descr}`, () => this.makeMetadata(props));
}
async perform(props) {
this.log("received", props);
const tileQP = this.memoize(props);
await core_bentley_1.BeDuration.race(this._timeoutMilliseconds, tileQP.promise).catch(() => { });
// Note: Rejections must be caught so that the memoization entry can be deleted
if (tileQP.isPending) {
this.log("issuing pending status for", props);
throw new core_common_1.RpcPendingResponse(); // eslint-disable-line @typescript-eslint/only-throw-error
}
this.deleteMemoized(props);
if (tileQP.isFulfilled) {
this.log("completed", props);
(0, core_bentley_1.assert)(undefined !== tileQP.result);
return tileQP.result;
}
(0, core_bentley_1.assert)(tileQP.isRejected);
this.log("rejected", props);
throw tileQP.error;
}
}
async function getTileTreeProps(props) {
(0, core_bentley_1.assert)(undefined !== props.accessToken);
const db = await RpcBriefcaseUtility_1.RpcBriefcaseUtility.findOpenIModel(props.accessToken, props.tokenProps);
return db.tiles.requestTileTreeProps(props.treeId);
}
class RequestTileTreePropsMemoizer extends TileRequestMemoizer {
get _timeoutMilliseconds() { return IModelHost_1.IModelHost.tileTreeRequestTimeout; }
get _operationName() { return "requestTileTreeProps"; }
stringify(props) { return props.treeId; }
addMetadata(meta, props) {
meta.treeId = props.treeId;
}
static _instance;
constructor() {
super(getTileTreeProps, generateTileRequestKey);
IModelHost_1.IModelHost.onBeforeShutdown.addOnce(() => {
this[Symbol.dispose]();
RequestTileTreePropsMemoizer._instance = undefined;
});
}
static async perform(props) {
if (undefined === this._instance)
this._instance = new RequestTileTreePropsMemoizer();
return this._instance.perform(props);
}
}
async function getTileContent(props) {
(0, core_bentley_1.assert)(undefined !== props.accessToken);
const db = await RpcBriefcaseUtility_1.RpcBriefcaseUtility.findOpenIModel(props.accessToken, props.tokenProps);
const tile = await db.tiles.requestTileContent(props.treeId, props.contentId);
// ###TODO: Verify the guid supplied by the front-end matches the guid stored in the model?
if (IModelHost_1.IModelHost.usingExternalTileCache) {
const tileMetadata = {
backendName: IModelHost_1.IModelHost.applicationId,
tileGenerationTime: tile.elapsedSeconds.toString(),
tileSize: tile.content.byteLength.toString(),
};
await IModelHost_1.IModelHost.tileStorage?.uploadTile(props.tokenProps.iModelId ?? db.iModelId, props.tokenProps.changeset?.id ?? db.changeset.id, props.treeId, props.contentId, tile.content, props.guid, tileMetadata);
const { accessToken: _, ...safeProps } = props;
core_bentley_1.Logger.logInfo(BackendLoggerCategory_1.BackendLoggerCategory.IModelTileRequestRpc, "Generated and uploaded tile", { tileMetadata, ...safeProps });
return core_common_1.TileContentSource.ExternalCache;
}
return core_common_1.TileContentSource.Backend;
}
function generateTileContentKey(props) {
return `${generateTileRequestKey(props)}:${props.contentId}`;
}
class RequestTileContentMemoizer extends TileRequestMemoizer {
get _timeoutMilliseconds() { return IModelHost_1.IModelHost.tileContentRequestTimeout; }
get _operationName() { return "requestTileContent"; }
stringify(props) { return `${props.treeId}:${props.contentId}`; }
addMetadata(meta, props) {
meta.treeId = props.treeId;
meta.contentId = props.contentId;
}
static _instance;
constructor() {
super(getTileContent, generateTileContentKey);
IModelHost_1.IModelHost.onBeforeShutdown.addOnce(() => {
this[Symbol.dispose]();
RequestTileContentMemoizer._instance = undefined;
});
}
static get instance() {
if (undefined === this._instance)
this._instance = new RequestTileContentMemoizer();
return this._instance;
}
static async perform(props) {
return this.instance.perform(props);
}
}
function currentActivity() {
return tracing_1.RpcTrace.expectCurrentActivity;
}
/** @internal */
class IModelTileRpcImpl extends core_common_1.RpcInterface {
static register() { core_common_1.RpcManager.registerImpl(core_common_1.IModelTileRpcInterface, IModelTileRpcImpl); }
async requestTileTreeProps(tokenProps, treeId) {
return RequestTileTreePropsMemoizer.perform({ accessToken: currentActivity().accessToken, tokenProps, treeId });
}
async purgeTileTrees(tokenProps, modelIds) {
// `undefined` gets forwarded as `null`...
if (null === modelIds)
modelIds = undefined;
const db = await RpcBriefcaseUtility_1.RpcBriefcaseUtility.findOpenIModel(currentActivity().accessToken, tokenProps);
if (!db.isOpen) {
return;
}
return db[Symbols_1._nativeDb].purgeTileTrees(modelIds);
}
async generateTileContent(tokenProps, treeId, contentId, guid) {
return RequestTileContentMemoizer.perform({ accessToken: currentActivity().accessToken, tokenProps, treeId, contentId, guid });
}
async retrieveTileContent(tokenProps, key) {
const db = await RpcBriefcaseUtility_1.RpcBriefcaseUtility.findOpenIModel(currentActivity().accessToken, tokenProps);
return db.tiles.getTileContent(key.treeId, key.contentId);
}
async getTileCacheConfig(tokenProps) {
if (IModelHost_1.IModelHost.tileStorage === undefined)
return undefined;
const iModelId = tokenProps.iModelId ?? (await RpcBriefcaseUtility_1.RpcBriefcaseUtility.findOpenIModel(currentActivity().accessToken, tokenProps)).iModelId;
return IModelHost_1.IModelHost.tileStorage.getDownloadConfig(iModelId);
}
async queryVersionInfo() {
return NativePlatform_1.IModelNative.platform.getTileVersionInfo();
}
/** @internal */
async requestElementGraphics(rpcProps, request) {
const iModel = await RpcBriefcaseUtility_1.RpcBriefcaseUtility.findOpenIModel(currentActivity().accessToken, rpcProps);
return iModel.generateElementGraphics(request);
}
}
exports.IModelTileRpcImpl = IModelTileRpcImpl;
/** @internal */
async function cancelTileContentRequests(tokenProps, contentIds) {
const iModel = IModelDb_1.IModelDb.findByKey(tokenProps.key);
const props = { tokenProps, treeId: "", contentId: "" };
for (const entry of contentIds) {
props.treeId = entry.treeId;
for (const contentId of entry.contentIds) {
props.contentId = contentId;
RequestTileContentMemoizer.instance.deleteMemoized(props);
}
iModel[Symbols_1._nativeDb].cancelTileContentRequests(entry.treeId, entry.contentIds);
}
}
//# sourceMappingURL=IModelTileRpcImpl.js.map
;