Skip to content

A concurrent update was performed on this collection and corrupted its state. The collection's state is no longer correct. #56

@codingyourlife

Description

@codingyourlife

We get this error log:
System.InvalidOperationException
Operations that change non-concurrent collections must have exclusive access. A concurrent update was performed on this collection and corrupted its state. The collection's state is no longer correct.

In this line:
return Imagekit.Url(new Transformation()).Path(fileDownloadRequest.ObjectName).Generate();

ChatGPT suggests:

lock (_lockObject)
{
    return Imagekit.Url(new Transformation()).Path(fileDownloadRequest.ObjectName).Generate();
}

But ChatGPT also says:
It seems like you're facing a concurrency issue with the Imagekit.Url method or the underlying collection that it uses. This is common in multi-threaded applications where multiple threads attempt to modify a non-concurrent collection simultaneously.

-> To me it sounds like this is an issue about multithreading inside of your library. The lock statement might have a big performance downside.

I also pasted the content of your ImageKitParams.cs to ChatGPT and asked if it can spot the error. It suggests this part:

if (this.options.ContainsKey(key))
{
    this.options[key] = value;
    if (key == "transformation")
    {
        this.options.Remove("path");
        this.options.Remove("src");
    }
}
else
{
    this.options.Add(key, value);
}

Solution suggestions:

  1. You can make the this.options dictionary thread-safe by using ConcurrentDictionary instead of a regular Dictionary. This would entail modifying all dictionary operations to use methods provided by ConcurrentDictionary like TryAdd, TryRemove, etc.
  2. Another way is to use locking (as I showed in my previous answer) to synchronize access to the this.options dictionary.

Proposed solution in code:
using System.Collections.Concurrent;

namespace Imagekit
{
    public partial class BaseImagekit<T>
    {
        // Assuming this is declared somewhere in your class
        private ConcurrentDictionary<string, object> options = new ConcurrentDictionary<string, object>();

        public T Add(string key, object value)
        {
            options.AddOrUpdate(key, value, (existingKey, existingValue) => 
            {
                if (key == "transformation")
                {
                    options.TryRemove("path", out _);
                    options.TryRemove("src", out _);
                }
                return value;
            });

            return (T)this;
        }

        // ... rest of the class remains unchanged
    }
}

For us the issue is happening pretty much every day in the background.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions