From 213460d0d629ed0e73f4d3877204a4fe164931cc Mon Sep 17 00:00:00 2001 From: Kevin Hahn Date: Mon, 21 Apr 2025 14:13:35 +0700 Subject: [PATCH] make MigrateDb threadsafe --- .../FwLite/LcmCrdt/CurrentProjectService.cs | 30 ++++++++++++------- 1 file changed, 20 insertions(+), 10 deletions(-) diff --git a/backend/FwLite/LcmCrdt/CurrentProjectService.cs b/backend/FwLite/LcmCrdt/CurrentProjectService.cs index 88fd0c41f2..a07f32b637 100644 --- a/backend/FwLite/LcmCrdt/CurrentProjectService.cs +++ b/backend/FwLite/LcmCrdt/CurrentProjectService.cs @@ -1,4 +1,5 @@ -using Microsoft.EntityFrameworkCore; +using System.Collections.Concurrent; +using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Caching.Memory; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; @@ -82,18 +83,27 @@ public async ValueTask RefreshProjectData() return projectData; } - private async Task MigrateDb() + private static readonly ConcurrentDictionary> MigrationTasks = []; + private Task MigrateDb() { - try + //ensure we only execute once, otherwise we'll have a conflict as Migrate is not thread safe. + //design based on https://andrewlock.net/making-getoradd-on-concurrentdictionary-thread-safe-using-lazy/ +#pragma warning disable VSTHRD011 + return MigrationTasks.GetOrAdd(Project.DbPath, _ => new Lazy(Execute)).Value; +#pragma warning restore VSTHRD011 + async Task Execute() { - - await DbContext.Database.MigrateAsync(); - } - catch (Exception e) - { - logger.LogError(e, "Failed to migrate database for project '{Project}'", Project.Name); - throw; + try + { + await DbContext.Database.MigrateAsync(); + } + catch (Exception e) + { + logger.LogError(e, "Failed to migrate database for project '{Project}'", Project.Name); + throw; + } } + } public async Task SetProjectSyncOrigin(Uri? domain, Guid? id)