Multiple/Recursive locks are not allowed error (IM...
# help-with-umbraco
l
Ah yes, Umbraco scoping... I need some help with this one. I simply do not know how to fix this and it's just all trial and error. I think the issue is that there are too many action performed on the database, but what I'm doing doesn't seem so special or difficult. This is Umbracoi 13(.5.2) by the way. First in short the main flow before I give some code: In multiple packages, the following happens: - Package B (Umbraco NuGet package) contains a webhook and receives a post with some data (JSON) - Package B passes the data to Package A (non-Umbraco package) to handle and A create a nice deserialized model of it and invokes an event. - Package B (Umbraco package) listens to the event and when it fires, Package B creates a Media node in Umbraco and puts a file and some meta data in there. - Package B also has a custom Umbraco notification it fires, so that consumers of the package can do some custom action if required. These steps al seem to work all the time. Now in an Umbraco instance, the following happens: - Umbraco handles the custom notification that contains a reference to the media item - Umbraco updates a few fields on the media item and then saves it. This is the step that goes sometimes wrong. Possible issues might be that there are more than 1000 webhook posts done too fast. It could also be a combination of async an non-async stuff. Or I shouldn't give a reference to the media node itself in the notification perhaps. Any ideas? I'll create another post with some minimal and simplified code.
These code examples are really minimal and I left out a lot that I think it not relevant. So once again: * Package A: Non Umbraco NuGet package * Package B: Umbraco NuGet package Package B (Umbraco) contains this webhook:
Copy code
csharp
public class Controller (...): UmbracoApiController
{
    public async Task<IActionResult> Webhook()
    {
        try
        {
            using var reader = new StreamReader(Request.Body);
            var body = await reader.ReadToEndAsync();
            A.DoSomething(body);
            return Ok("OK");
        }
        catch (Exception e)
        {
            return BadRequest();
        }
    }
}
When called, an action is performed on package A (non-Umbraco) and A dispatches an event:
Copy code
csharp
public void HandleNotification(string body)
{
    var bodyModel = JsonSerializer.Deserialize<Contract>(body);
    eventService.OnEvent(new EventModel {Details = bodyModel});
}
Package B (Umbraco) listens to the event:
Copy code
csharp
internal class NotificationsHandler(...) : INotificationsHandler
{
    public void Initialize()
    {
        notificationManager.Event += NotificationService_Event;
    }

    private void NotificationService_Event(object? sender, Args e)
    {
        HandleEventAsync(e).ConfigureAwait(false);
    }

    private async Task HandleEventAsync(PdfChangedEventArgs e)
    {
        await SomeService.HandleUpdate(e);
    }
}
On that event, a mediaNode is found (or created if it didn't exist), the file is uploaded to the media node and after that a notification is published:
Copy code
csharp
private async Task HandleUpdate(eventdata)
{
    var mediaNode = GetOrCreateDocumentMediaNode(eventdata.someId, eventdata.documentName);
    _ = await UpdateMediaItemAsync(mediaNode, eventdata.title, eventdata.documentUrl);
    await eventAggregator.PublishAsync(new CustomNotification(mediaNode, eventdata.someId, eventdata));
}
Maybe the UpdateMediaItemAsync is interresting:
Copy code
csharp
private async Task<string?> UpdateMediaItemAsync(IMedia mediaItem, string title, string fileUrl)
{
    using var httpClient = new HttpClient();

    try
    {
        // Update the file
        var fileBytes = await httpClient.GetByteArrayAsync(mcmFileUrl);
        var fileName = Path.GetFileName(new Uri(fileUrl).LocalPath);
        using var stream = new MemoryStream(fileBytes);
        mediaItem.SetValue(mediaFileManager, mediaUrlGeneratorCollection, shortStringHelper,
            contentTypeBaseServiceProvider, MediaAliases.PlusDocumentPropertyAlias, fileName, stream);

        //Update other fields
        mediaItem.SetValue(MediaAliases.TitlePropertyAlias, title);
        mediaItem.SetValue(MediaAliases.LastUpdatePropertyAlias, DateTime.Now);

        //Save the changes
        mediaService.Save(mediaItem);

        return fileName;
    }
    catch (Exception ex)
    {
        logger.LogError(ex, "Error uploading file from URL: {Url}", mcmFileUrl);
        throw;
    }
}
Now the consuming Umbraco instance can handle the notification. In this case it simply takes the IMedia from the notification and updates a few fields and saves it.
Copy code
csharp
public void Handle(CustomNotification notification)
{
  var mediaNode = notification.mediaNode;
  mediaNode.SetValue(propertyAlias, $"[{string.Join(",", matchingNodes.ToArray())}]");

  var result = mediaService.Save(mediaNode);
  //This last line causes the errors....
}
Ok I changed the order of things and that seems to really work well: * Create the media node * Upload the file and set properties on the media node, but don't save it * Publish the event and don't perform any save in the event handler * After the event, save the media node once This prevents multiple saves. I was under the impression that publishing the event would be a fire and forget type of thing, but now, the notification works like the ContentSaved notification in Umbraco: any changes you make during the handling of the notifications are also saved now.
Hmm, it seems that the issue is way less an...well...issue than it was, but it's not gone. Still have the occasional 'Recursive locks not allowed' errors in the log on the media save. This really sucks. Any idea what I can check next? Or something you see wrong in the code?
122 Views