diff --git a/src/Abstractions/Entities/EntityLockAcquisitionFailedException.cs b/src/Abstractions/Entities/EntityLockAcquisitionFailedException.cs new file mode 100644 index 00000000..785c1738 --- /dev/null +++ b/src/Abstractions/Entities/EntityLockAcquisitionFailedException.cs @@ -0,0 +1,41 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +namespace Microsoft.DurableTask.Entities; + +/// +/// Exception that gets thrown when an entity operation fails with an unhandled exception. +/// +/// +/// Detailed information associated with a particular operation failure, including exception details, can be found in the +/// property. +/// +public sealed class EntityLockAcquisitionFailedException : Exception +{ + /// + /// Initializes a new instance of the class. + /// + /// The entity IDs being locked. + /// The failure details. + public EntityLockAcquisitionFailedException(IEnumerable entityIds, TaskFailureDetails failureDetails) + : base(GetExceptionMessage(entityIds, failureDetails)) + { + this.EntityIds = entityIds; + this.FailureDetails = failureDetails; + } + + /// + /// Gets the IDs of the entities for which the lock request was issued. + /// + public IEnumerable EntityIds { get; } + + /// + /// Gets the details of the task failure, including exception information. + /// + public TaskFailureDetails FailureDetails { get; } + + static string GetExceptionMessage(IEnumerable entityIds, TaskFailureDetails failureDetails) + { + return $"Acquisition of locks for entities '{string.Join(",", entityIds)}' failed: {failureDetails.ErrorMessage}"; + } +} diff --git a/src/Worker/Core/Shims/TaskOrchestrationEntityContext.cs b/src/Worker/Core/Shims/TaskOrchestrationEntityContext.cs index a5483073..7847f941 100644 --- a/src/Worker/Core/Shims/TaskOrchestrationEntityContext.cs +++ b/src/Worker/Core/Shims/TaskOrchestrationEntityContext.cs @@ -78,8 +78,14 @@ public override async Task LockEntitiesAsync(IEnumerable(criticalSectionId.ToString()); - + OperationResult result = await this.wrapper.WaitForExternalEvent(criticalSectionId.ToString()); + + if (result.IsError) + { + this.EntityContext.AbandonAcquire(); + throw new EntityLockAcquisitionFailedException(entityIds, ConvertFailureDetails(result.FailureDetails!)); + } + this.EntityContext.CompleteAcquire(result, criticalSectionId); // return an IDisposable that releases the lock