Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Concurrency / Thread-Safety Issue: Setting UrlEndpoint(...) in Parallel Triggers InvalidOperationException #67

Open
ahnv opened this issue Jan 6, 2025 · 1 comment

Comments

@ahnv
Copy link
Member

ahnv commented Jan 6, 2025

A user has reported that invoking UrlEndpoint(...) on a shared ServerImagekit (or Imagekit) instance from multiple threads can result in a System.InvalidOperationException. The error indicates that a non-concurrent internal collection or state is being mutated by multiple threads simultaneously.

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.
It appears the library is mutating some shared, non-thread-safe state (perhaps a collection or internal field) when UrlEndpoint(...) is invoked in parallel.

Note: Although one might typically set the endpoint once at initialization, some use cases require dynamically switching endpoints in parallel. In such scenarios, the SDK can throw the above exception.

Steps to Reproduce

  1. Create a single ServerImagekit instance with an initial endpoint.
var _imagekit = new ServerImagekit(
    "public_key_here",
    "private_key_here",
    "https://ik.imagekit.io/realedo/"
);
  1. In parallel (e.g., using Parallel.For or multiple threads), alternate calls to .UrlEndpoint(".../tera/") and .UrlEndpoint(".../another/").
  2. Generate URLs (e.g. kit.Url(...).Generate()) between these calls to simulate typical usage in a concurrent environment.

Here’s a minimal code snippet that triggers the error on many machines:

using System;
using System.Threading.Tasks;
using Imagekit; // or relevant namespace

public class Program
{
    public static void Main()
    {
        var _imagekit = new ServerImagekit("public_key", "private_key", "https://ik.imagekit.io/realedo/");
        
        Parallel.For(0, 10000, new ParallelOptions { MaxDegreeOfParallelism = 16 }, i =>
        {
            try
            {
                // Randomly switch endpoint
                if (i % 2 == 0)
                {
                    _imagekit.UrlEndpoint("https://ik.imagekit.io/realedo/tera/");
                }
                else
                {
                    _imagekit.UrlEndpoint("https://ik.imagekit.io/realedo/another/");
                }

                // Optional small delay to increase concurrency
                Task.Delay(1).Wait();

                var url = _imagekit.Url().Path($"test{i}.jpg").Generate();
                Console.WriteLine($"{i}: {url}");
            }
            catch (Exception ex)
            {
                Console.WriteLine($"{i}: {ex.GetType().Name} -> {ex.Message}");
            }
        });
        
        Console.WriteLine("Done.");
        Console.ReadKey();
    }
}

Observed Behavior

In some runs (especially with higher iteration counts and parallelism), System.InvalidOperationException is thrown, complaining about concurrent access to an internal, non-concurrent collection.

Expected Behavior

  • Either the SDK handles concurrent modifications safely (e.g., using thread-safe data structures or internal locking), or
  • The documentation explicitly states that changing UrlEndpoint(...) in parallel is unsupported, guiding users to create separate Imagekit instances or synchronize their calls.

Suggested Solutions

  1. Immutability: Make methods like UrlEndpoint(...) return a new ServerImagekit object instead of mutating the existing instance.
  2. Thread-Safe Mutations: Use lock statements or thread-safe collections internally so multiple threads can safely modify the endpoint configuration.
  3. Documentation Update: If dynamic endpoint changes aren’t intended to be thread-safe, clarify that in the docs, suggesting patterns like “create separate clients” or “lock around calls.”

Additional Context

Some users have valid scenarios requiring multiple endpoints (e.g., multi-tenant setups) and may switch them at runtime. If the SDK cannot support concurrent reconfiguration, it should at least guide developers toward safe usage patterns.

@ahnv
Copy link
Member Author

ahnv commented Jan 6, 2025

This issue has been observed when working with Path(...) function also, refer to #56

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant