skip to Main Content

please help.
Description of the problem:
I have a microservice which contains caching logic in Redis. On the server, many instances of this service are raised and they use a single cache.
If all instances receive a request to get data from the cache and there is no data there, then ALL instances send a request to get data to the database, thereby loading it.

Solution.
The solution to the problem is to establish a lock between containers via Mutex when requesting the database. So that while one instance receives data from the database and places it in the cache, other instances wait on a locked mutex and then take it from the cache.

All the logic has already been implemented, the only problem is that a single mutex is created for all containers.
Paul Google rummaged – zero information.
Help me please.

So far, I’m testing the creation of a single mutex on a console application.

Below provided the most standard DockerFile.

FROM mcr.microsoft.com/dotnet/runtime:7.0 AS base
WORKDIR /app
FROM mcr.microsoft.com/dotnet/sdk:7.0 AS build
WORKDIR /src
COPY ["MutexDockerTest/MutexDockerTest.csproj", "MutexDockerTest/"]
RUN dotnet restore "MutexDockerTest/MutexDockerTest.csproj"
COPY . .
WORKDIR "/src/MutexDockerTest"
RUN dotnet build "MutexDockerTest.csproj" -c Release -o /app/build

FROM build AS publish
RUN dotnet publish "MutexDockerTest.csproj" -c Release -o /app/publish

FROM base AS final
WORKDIR /app
COPY --from=publish /app/publish .
ENTRYPOINT ["dotnet", "MutexDockerTest.dll"]

Below is the code from Program.cs

Console.WriteLine("Start");
using (var mutex = new Mutex (false, "mutextUltra"))
{
    if (!mutex.WaitOne (TimeSpan.FromSeconds (1), false))
    {
        Console.WriteLine ("Another app instance is running. Bye!");
        Thread.Sleep(1000);
        return;
    }
    RunProgram();
}
void RunProgram()
{
    while (true)
    {
        Thread.Sleep(2000);
        Console.WriteLine("Hello");
    }
}

2

Answers


  1. Chosen as BEST ANSWER

    I found a solution to the problem. The fact is that each container acts as a separate session and Mutex is created individually for each container separately. To create a single mutex for all containers, you need to create a Global Mutex by adding the Global prefix to its name.

    Here is the corrected code

    Console.WriteLine("Start");
    
    
    using (var mutex = new Mutex (false, "Global\mutex")) //Create Global Mutex
    {
        if (!mutex.WaitOne (TimeSpan.FromSeconds (3), false))
        {
            Console.WriteLine ("Another app instance is running. Bye!");
    
            Thread.Sleep(1000);
            return;
        }
        RunProgram();
    }
    
    void RunProgram()
    {
        while (true)
        {
            Thread.Sleep(5000);
            Console.WriteLine("Hello");
        }
    }

    Here is the command to run docker containers. It is necessary to set the same --pid --ipc and --volume. The volume directory of the containers must be exactly /tmp/.dotnet/shm/. the global mutex is created there

    docker run --name mutexContainer1 --pid=host --ipc=host --volume=C:/mutex:/tmp/.dotnet/shm/ mutexImage


  2. The scope of a mutex is usally your local machine. Using docker images, I would assume the scope of the mutex would be within that container.

    I think you need to look for a solution, that suppports a wider scope. If you host one image in Azure, and another in AWS how would they know its should be the same mutex?

    If you are in Azure, some people are using a blob as the mutex, and aquiring and releasing the "mutex" using BlobLeaseClient

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search