Skip to content

Commit

Permalink
Add ConflictingLocksError class (#6852)
Browse files Browse the repository at this point in the history
Co-authored-by: Paul Connelly <22944042+pmconne@users.noreply.github.com>
  • Loading branch information
nick4598 and pmconne committed Jun 24, 2024
1 parent ae97a42 commit 20cd3cd
Show file tree
Hide file tree
Showing 14 changed files with 126 additions and 33 deletions.
11 changes: 6 additions & 5 deletions common/api/core-backend.api.md
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,7 @@ import { LineStyleProps } from '@itwin/core-common';
import { LocalBriefcaseProps } from '@itwin/core-common';
import { LocalDirName } from '@itwin/core-common';
import { LocalFileName } from '@itwin/core-common';
import { LockState as LockState_2 } from '@itwin/core-common';
import { LogLevel } from '@itwin/core-bentley';
import { LowAndHighXYZ } from '@itwin/core-geometry';
import { MarkRequired } from '@itwin/core-bentley';
Expand Down Expand Up @@ -4049,15 +4050,15 @@ export interface LockControl {
}

// @internal (undocumented)
export type LockMap = Map<Id64String, LockState>;
export type LockMap = Map<Id64String, LockState_2>;

// @beta
export interface LockProps {
readonly id: Id64String;
readonly state: LockState;
readonly state: LockState_2;
}

// @public
// @public @deprecated
export enum LockState {
Exclusive = 2,
None = 0,
Expand All @@ -4071,7 +4072,7 @@ export interface LockStatusExclusive {
// (undocumented)
lastCsIndex?: ChangesetIndex;
// (undocumented)
state: LockState.Exclusive;
state: LockState_2.Exclusive;
}

// @internal
Expand All @@ -4081,7 +4082,7 @@ export interface LockStatusShared {
// (undocumented)
sharedBy: Set<BriefcaseId>;
// (undocumented)
state: LockState.Shared;
state: LockState_2.Shared;
}

// @internal
Expand Down
21 changes: 21 additions & 0 deletions common/api/core-common.api.md
Original file line number Diff line number Diff line change
Expand Up @@ -1640,6 +1640,20 @@ export namespace ConcreteEntityTypes {
export function toBisCoreRootClassFullName(type: ConcreteEntityTypes): string;
}

// @public
export interface ConflictingLock {
briefcaseIds: number[];
objectId: string;
state: LockState;
}

// @public
export class ConflictingLocksError extends IModelError {
constructor(message: string, getMetaData?: LoggingMetaData, conflictingLocks?: ConflictingLock[]);
// (undocumented)
conflictingLocks?: ConflictingLock[];
}

// @alpha
export enum ContentFlags {
// (undocumented)
Expand Down Expand Up @@ -5533,6 +5547,13 @@ export interface Localization {
unregisterNamespace(namespace: string): void;
}

// @public
export enum LockState {
Exclusive = 2,
None = 0,
Shared = 1
}

export { LogFunction }

export { LoggingMetaData }
Expand Down
1 change: 1 addition & 0 deletions common/api/summary/core-backend.exports.csv
Original file line number Diff line number Diff line change
Expand Up @@ -266,6 +266,7 @@ beta;LockControl
internal;LockMap = Map
beta;LockProps
public;LockState
deprecated;LockState
internal;LockStatusExclusive
internal;LockStatusShared
internal;MetaDataRegistry
Expand Down
3 changes: 3 additions & 0 deletions common/api/summary/core-common.exports.csv
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,8 @@ internal;computeTileChordTolerance(tile: TileMetadata, is3d: boolean, tileScreen
alpha;ConcreteEntityTypes
alpha;ConcreteEntityTypes
internal;toBisCoreRootClassFullName(type: ConcreteEntityTypes): string
public;ConflictingLock
public;ConflictingLocksError
alpha;ContentFlags
internal;class ContentIdProvider
public;ContextRealityModel
Expand Down Expand Up @@ -460,6 +462,7 @@ public;LocalBriefcaseProps
public;LocalDirName = string
public;LocalFileName = string
public;Localization
public;LockState
public;MapImageryProps
public;MapImagerySettings
public;MapLayerKey
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
{
"changes": [
{
"packageName": "@itwin/core-backend",
"comment": "",
"type": "none"
}
],
"packageName": "@itwin/core-backend"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
{
"changes": [
{
"packageName": "@itwin/core-common",
"comment": "add ConflictingLocksError",
"type": "none"
}
],
"packageName": "@itwin/core-common"
}
37 changes: 20 additions & 17 deletions core/backend/src/BackendHubAccess.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,25 +9,12 @@
import { AccessToken, GuidString, Id64String, IModelHubStatus } from "@itwin/core-bentley";
import {
BriefcaseId, ChangesetFileProps, ChangesetIdWithIndex, ChangesetIndex, ChangesetIndexAndId, ChangesetIndexOrId, ChangesetProps, ChangesetRange,
IModelError, IModelVersion, LocalDirName, LocalFileName,
LockState as CommonLockState, IModelError, IModelVersion,
LocalDirName, LocalFileName,
} from "@itwin/core-common";
import { CheckpointProps, DownloadRequest, ProgressFunction } from "./CheckpointManager";
import type { TokenArg } from "./IModelDb";

/** The state of a lock.
* @public
*/
export enum LockState {
/** The element is not locked */
None = 0,
/** Holding a shared lock on an element blocks other users from acquiring the Exclusive lock it. More than one user may acquire the shared lock. */
Shared = 1,
/** A Lock that permits modifications to an element and blocks other users from making modifications to it.
* Holding an exclusive lock on an "owner" (a model or a parent element), implicitly exclusively locks all its members.
*/
Exclusive = 2,
}

/** Exception thrown if lock cannot be acquired.
* @beta
*/
Expand All @@ -43,6 +30,21 @@ export class LockConflict extends IModelError {
}
}

/** The state of a lock. See [Acquiring locks on elements.]($docs/learning/backend/ConcurrencyControl.md#acquiring-locks-on-elements).
* @deprecated in 4.7 Use [LockState]($common)
* @public
*/
export enum LockState {
/** The element is not locked */
None = 0,
/** Holding a shared lock on an element blocks other users from acquiring the Exclusive lock it. More than one user may acquire the shared lock. */
Shared = 1,
/** A Lock that permits modifications to an element and blocks other users from making modifications to it.
* Holding an exclusive lock on an "owner" (a model or a parent element), implicitly exclusively locks all its members.
*/
Exclusive = 2,
}

/**
* The properties to access a V2 checkpoint through a daemon.
* @internal
Expand All @@ -61,7 +63,7 @@ export interface V2CheckpointAccessProps {
}

/** @internal */
export type LockMap = Map<Id64String, LockState>;
export type LockMap = Map<Id64String, CommonLockState>;

/**
* The properties of a lock that may be obtained from a lock server.
Expand All @@ -71,7 +73,7 @@ export interface LockProps {
/** The elementId for the lock */
readonly id: Id64String;
/** the lock state */
readonly state: LockState;
readonly state: CommonLockState;
}

/**
Expand Down Expand Up @@ -232,6 +234,7 @@ export interface BackendHubAccess {
/**
* acquire one or more locks. Throws if unsuccessful. If *any* lock cannot be obtained, no locks are acquired
* @internal
* @throws ConflictingLocksError if one or more requested locks are held by other briefcases.
*/
acquireLocks: (arg: BriefcaseDbArg, locks: LockMap) => Promise<void>;

Expand Down
1 change: 1 addition & 0 deletions core/backend/src/IModelDb.ts
Original file line number Diff line number Diff line change
Expand Up @@ -167,6 +167,7 @@ export interface LockControl {
* If any required lock is not available, this method throws an exception and *none* of the requested locks are acquired.
* > Note: acquiring the exclusive lock on an element requires also obtaining a shared lock on all its owner elements. This method will
* attempt to acquire all necessary locks for both sets of input ids.
* @throws ConflictingLocksError if one or more requested locks are held by other briefcases.
*/
acquireLocks(arg: {
/** if present, one or more elements to obtain shared lock */
Expand Down
4 changes: 2 additions & 2 deletions core/backend/src/LocalHub.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,9 @@ import { join } from "path";
import { DbResult, GuidString, Id64String, IModelHubStatus, IModelStatus, OpenMode } from "@itwin/core-bentley";
import {
BriefcaseId, BriefcaseIdValue, ChangesetFileProps, ChangesetId, ChangesetIdWithIndex, ChangesetIndex, ChangesetIndexOrId, ChangesetProps,
ChangesetRange, IModelError, LocalDirName, LocalFileName,
ChangesetRange, IModelError, LocalDirName, LocalFileName, LockState,
} from "@itwin/core-common";
import { LockConflict, LockMap, LockProps, LockState } from "./BackendHubAccess";
import { LockConflict, LockMap, LockProps } from "./BackendHubAccess";
import { BriefcaseManager } from "./BriefcaseManager";
import { BriefcaseLocalValue, IModelDb, SnapshotDb } from "./IModelDb";
import { IModelJsFs } from "./IModelJsFs";
Expand Down
4 changes: 2 additions & 2 deletions core/backend/src/ServerBasedLocks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@
*/

import { DbResult, Id64, Id64Arg, Id64String, IModelStatus, OpenMode } from "@itwin/core-bentley";
import { IModel, IModelError } from "@itwin/core-common";
import { LockMap, LockState } from "./BackendHubAccess";
import { IModel, IModelError, LockState } from "@itwin/core-common";
import { LockMap } from "./BackendHubAccess";
import { BriefcaseDb, LockControl } from "./IModelDb";
import { IModelHost } from "./IModelHost";
import { SQLiteDb } from "./SQLiteDb";
Expand Down
4 changes: 2 additions & 2 deletions core/backend/src/test/standalone/HubMock.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@
import { assert, expect } from "chai";
import { join } from "path";
import { AccessToken, Guid, Mutable } from "@itwin/core-bentley";
import { ChangesetFileProps, ChangesetType } from "@itwin/core-common";
import { LockProps, LockState } from "../../BackendHubAccess";
import { ChangesetFileProps, ChangesetType, LockState } from "@itwin/core-common";
import { LockProps } from "../../BackendHubAccess";
import { BriefcaseManager } from "../../BriefcaseManager";
import { IModelHost } from "../../IModelHost";
import { IModelJsFs } from "../../IModelJsFs";
Expand Down
4 changes: 2 additions & 2 deletions core/backend/src/test/standalone/IModelWrite.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
import { AccessToken, DbResult, GuidString, Id64, Id64String } from "@itwin/core-bentley";
import {
Code, ColorDef,
GeometricElement2dProps, GeometryStreamProps, IModel, QueryRowFormat, RequestNewBriefcaseProps, SchemaState, SubCategoryAppearance,
GeometricElement2dProps, GeometryStreamProps, IModel, LockState, QueryRowFormat, RequestNewBriefcaseProps, SchemaState, SubCategoryAppearance,
} from "@itwin/core-common";
import { Arc3d, IModelJson, Point2d, Point3d } from "@itwin/core-geometry";
import * as chai from "chai";
Expand All @@ -24,7 +24,7 @@ import {
BriefcaseManager,
ChannelControl,
CodeService,
DefinitionModel, DictionaryModel, DocumentListModel, Drawing, DrawingGraphic, LockState, OpenBriefcaseArgs, SpatialCategory, Subject,
DefinitionModel, DictionaryModel, DocumentListModel, Drawing, DrawingGraphic, OpenBriefcaseArgs, SpatialCategory, Subject,
} from "../../core-backend";
import { IModelTestUtils, TestUserType } from "../IModelTestUtils";
import { ServerBasedLocks } from "../../ServerBasedLocks";
Expand Down
3 changes: 1 addition & 2 deletions core/backend/src/test/standalone/ServerBasedLocks.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,7 @@
import { assert, expect } from "chai";
import { restore as sinonRestore, spy as sinonSpy } from "sinon";
import { AccessToken, GuidString, Id64, Id64Arg } from "@itwin/core-bentley";
import { Code, IModel, IModelError, LocalBriefcaseProps, PhysicalElementProps, RequestNewBriefcaseProps } from "@itwin/core-common";
import { LockState } from "../../BackendHubAccess";
import { Code, IModel, IModelError, LocalBriefcaseProps, LockState, PhysicalElementProps, RequestNewBriefcaseProps } from "@itwin/core-common";
import { BriefcaseManager } from "../../BriefcaseManager";
import { PhysicalObject } from "../../domains/GenericElements";
import { PhysicalElement } from "../../Element";
Expand Down
46 changes: 45 additions & 1 deletion core/common/src/IModelError.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
*/

import {
BentleyError, BentleyStatus, BriefcaseStatus, ChangeSetStatus, DbResult, IModelStatus, LoggingMetaData, RepositoryStatus,
BentleyError, BentleyStatus, BriefcaseStatus, ChangeSetStatus, DbResult, IModelHubStatus, IModelStatus, LoggingMetaData, RepositoryStatus,
} from "@itwin/core-bentley";

export {
Expand All @@ -32,6 +32,50 @@ export class IModelError extends BentleyError {
}
}

/** The state of a lock. See [Acquiring locks on elements.]($docs/learning/backend/ConcurrencyControl.md#acquiring-locks-on-elements).
* @public
*/
export enum LockState {
/** The element is not locked */
None = 0,
/** Holding a shared lock on an element blocks other users from acquiring the Exclusive lock it. More than one user may acquire the shared lock. */
Shared = 1,
/** A Lock that permits modifications to an element and blocks other users from making modifications to it.
* Holding an exclusive lock on an "owner" (a model or a parent element), implicitly exclusively locks all its members.
*/
Exclusive = 2,
}

/** Detailed information about a particular object Lock that is causing the Lock update conflict.
* An example of a lock update conflict would be attempting to use [LockControl.acquireLocks]($backend) on an object that is already locked by another Briefcase.
* @public
*/
export interface ConflictingLock {
/** Id of the object that is causing conflict. */
objectId: string;
/**
* The level of conflicting lock. Possible values are {@link LockState.Shared}, {@link LockState.Exclusive}.
* See {@link LockState}.
*/
state: LockState;
/** An array of Briefcase ids that hold this lock. */
briefcaseIds: number[];
}

/**
* An error raised when there is a lock conflict detected.
* Typically this error would be thrown by [LockControl.acquireLocks]($backend) when you are requesting a lock on an element that is already held by another briefcase.
* @public
*/
export class ConflictingLocksError extends IModelError {
public conflictingLocks?: ConflictingLock[];
constructor(message: string, getMetaData?: LoggingMetaData, conflictingLocks?: ConflictingLock[]) {
super(IModelHubStatus.LockOwnedByAnotherBriefcase, message, getMetaData);
this.conflictingLocks = conflictingLocks;
}

}

/** @public */
export class ServerError extends IModelError {
public constructor(errorNumber: number, message: string) {
Expand Down

0 comments on commit 20cd3cd

Please sign in to comment.