From 450cb3e55db67b2ae1c61d05a1c91f10dfff7da5 Mon Sep 17 00:00:00 2001 From: Emre KARA Date: Mon, 2 Jun 2025 15:55:05 +0300 Subject: [PATCH] Enhance TelemetryActivityStorage with mutex-based file access control and update activity storage path for improved concurrency and organization. --- .../Storage/TelemetryActivityStorage.cs | 67 ++++++++++++++++--- .../Abp/Telemetry/Constants/TelemetryPaths.cs | 2 +- 2 files changed, 59 insertions(+), 10 deletions(-) diff --git a/framework/src/Volo.Abp.Core/Volo/Abp/Telemetry/Activity/Storage/TelemetryActivityStorage.cs b/framework/src/Volo.Abp.Core/Volo/Abp/Telemetry/Activity/Storage/TelemetryActivityStorage.cs index 62eac8f7a1..6916f18748 100644 --- a/framework/src/Volo.Abp.Core/Volo/Abp/Telemetry/Activity/Storage/TelemetryActivityStorage.cs +++ b/framework/src/Volo.Abp.Core/Volo/Abp/Telemetry/Activity/Storage/TelemetryActivityStorage.cs @@ -4,6 +4,7 @@ using System.IO; using System.Linq; using System.Text; using System.Text.Json; +using System.Threading; using System.Threading.Tasks; using Microsoft.Extensions.Options; using Volo.Abp.DependencyInjection; @@ -16,9 +17,11 @@ public class TelemetryActivityStorage : ITelemetryActivityStorage, ISingletonDep { private const int MaxFileRetries = 5; private const int RetryDelayMs = 100; + private const int MutexTimeoutMs = 5000; // 5 seconds timeout private readonly TelemetryActivityStorageOptions _options; private TelemetryActivityStorageState? _cachedState; + private static readonly string MutexName = "Global\\TelemetryActivityStorage"; public TelemetryActivityStorage(IOptions options) { @@ -156,18 +159,65 @@ public class TelemetryActivityStorage : ITelemetryActivityStorage, ISingletonDep private async Task WithExclusiveFileLockAsync(Func> action) { + using var mutex = new Mutex(false, MutexName); + for (int i = 0; i < MaxFileRetries; i++) { try { - using var stream = new FileStream( - TelemetryPaths.ActivityStorage, - FileMode.OpenOrCreate, - FileAccess.ReadWrite, - FileShare.None - ); - - return await action(stream); + // Wait for mutex with timeout + if (!mutex.WaitOne(MutexTimeoutMs)) + { + if (i == MaxFileRetries - 1) + { + return default; + } + await Task.Delay(RetryDelayMs); + continue; + } + + try + { + using var stream = new FileStream( + TelemetryPaths.ActivityStorage, + FileMode.OpenOrCreate, + FileAccess.ReadWrite, + FileShare.Read // Allow read sharing since we have mutex protection + ); + + return await action(stream); + } + finally + { + mutex.ReleaseMutex(); + } + } + catch (AbandonedMutexException) + { + // Previous process crashed, mutex is now owned by us + try + { + using var stream = new FileStream( + TelemetryPaths.ActivityStorage, + FileMode.OpenOrCreate, + FileAccess.ReadWrite, + FileShare.Read + ); + + return await action(stream); + } + catch + { + if (i == MaxFileRetries - 1) + { + return default; + } + await Task.Delay(RetryDelayMs); + } + finally + { + try { mutex.ReleaseMutex(); } catch { } + } } catch (IOException) { @@ -175,7 +225,6 @@ public class TelemetryActivityStorage : ITelemetryActivityStorage, ISingletonDep { return default; } - await Task.Delay(RetryDelayMs); } catch diff --git a/framework/src/Volo.Abp.Core/Volo/Abp/Telemetry/Constants/TelemetryPaths.cs b/framework/src/Volo.Abp.Core/Volo/Abp/Telemetry/Constants/TelemetryPaths.cs index 87e394bb8b..e47c83b3a9 100644 --- a/framework/src/Volo.Abp.Core/Volo/Abp/Telemetry/Constants/TelemetryPaths.cs +++ b/framework/src/Volo.Abp.Core/Volo/Abp/Telemetry/Constants/TelemetryPaths.cs @@ -7,7 +7,7 @@ public static class TelemetryPaths { public static string AccessToken => Path.Combine(AbpRootPath, "cli", "access-token.bin"); public static string ComputerId => Path.Combine(AbpRootPath, "cli", "computer-id.bin"); - public static string ActivityStorage => Path.Combine(AbpRootPath, "activity-storage.json"); + public static string ActivityStorage => Path.Combine(AbpRootPath , "telemetry", "activity-storage.json"); public static string Studio => Path.Combine(AbpRootPath, "studio"); private readonly static string AbpRootPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.UserProfile), ".abp"); } \ No newline at end of file