From 9d1660270272180cd71147d6ddd53d1382643d2e Mon Sep 17 00:00:00 2001
From: wenxiangchao <wenxiangchao@ihengxin.com.cn>
Date: 星期一, 02 十一月 2026 18:00:14 +0800
Subject: [PATCH] merge: 并入远程 Initial commit 作为历史根节点
---
Aging.Infrastructure/Core/ResourceManager.cs | 258 +++++++++++++++++++++++++++++++++++++++++++++++++++
1 files changed, 258 insertions(+), 0 deletions(-)
diff --git a/Aging.Infrastructure/Core/ResourceManager.cs b/Aging.Infrastructure/Core/ResourceManager.cs
new file mode 100644
index 0000000..68f4c2b
--- /dev/null
+++ b/Aging.Infrastructure/Core/ResourceManager.cs
@@ -0,0 +1,258 @@
+using System.Collections.Concurrent;
+using Aging.Infrastructure.Contracts;
+using log4net;
+
+namespace Aging.Infrastructure.Core
+{
+ /// <summary>
+ /// 璧勬簮绠$悊瀹炵幇銆�
+ /// 鎻愪緵绾跨▼瀹夊叏鐨勮祫婧愰绾︺�佺绾﹁幏鍙栧拰閲婃斁鍔熻兘銆�
+ /// 浣跨敤 ConcurrentDictionary 瀛樺偍娲昏穬棰勭害锛屼娇鐢� SemaphoreSlim 瀹炵幇 per-resource 閿併��
+ /// </summary>
+ public sealed class ResourceManager : IResourceManager
+ {
+ private static readonly ILog Log = LogManager.GetLogger(typeof(ResourceManager));
+
+ /// <summary>
+ /// 娲昏穬棰勭害瀛楀吀锛坘ey = ResourceKey.ToString()锛�
+ /// </summary>
+ private readonly ConcurrentDictionary<string, ResourceReservation> _reservations = new();
+
+ /// <summary>
+ /// 姣忚祫婧愪俊鍙烽噺瀛楀吀锛岀敤浜庡疄鐜扮粏绮掑害閿�
+ /// </summary>
+ private readonly ConcurrentDictionary<string, SemaphoreSlim> _resourceLocks = new();
+
+ /// <summary>
+ /// 鍏ㄥ眬涓栦唬鍙凤紝姣忔鏂伴绾﹂�掑
+ /// </summary>
+ private long _generation;
+
+ /// <summary>
+ /// 榛樿绉熺害鏈夋晥鏈燂紙绉掞級
+ /// </summary>
+ private const int DefaultLeaseDurationSeconds = 30;
+
+ /// <inheritdoc/>
+ public async Task<ResourceReservation> ReserveAsync(
+ ResourceOwnerToken owner, ResourceKey resource, CancellationToken ct)
+ {
+ if (owner == null) throw new ArgumentNullException(nameof(owner));
+ if (resource == null) throw new ArgumentNullException(nameof(resource));
+
+ var resourceKey = resource.ToString();
+ var semaphore = _resourceLocks.GetOrAdd(resourceKey, _ => new SemaphoreSlim(1, 1));
+
+ try
+ {
+ await semaphore.WaitAsync(ct).ConfigureAwait(false);
+
+ try
+ {
+ // 妫�鏌ユ槸鍚﹀凡鏈� Active 棰勭害
+ if (_reservations.TryGetValue(resourceKey, out var existingReservation))
+ {
+ if (existingReservation.State == ReservationState.Active)
+ {
+ // 妫�鏌ユ槸鍚︿负鍚屼竴 Owner锛堢画鏈熷満鏅級
+ if (existingReservation.Owner.TokenId == owner.TokenId)
+ {
+ // 鍚屼竴鎵�鏈夎�呯画鏈燂細鏇存柊涓栦唬鍙�
+ var renewedGeneration = Interlocked.Increment(ref _generation);
+ owner.Generation = renewedGeneration;
+ existingReservation.Owner.Generation = renewedGeneration;
+
+ Log.Info($"[ResourceManager] 璧勬簮 {resourceKey} 缁湡鎴愬姛锛屾柊涓栦唬鍙�: {renewedGeneration}");
+ return existingReservation;
+ }
+
+ // 涓嶅悓鎵�鏈夎�呭啿绐�
+ var conflictMsg = $"璧勬簮 {resourceKey} 宸茶鎵�鏈夎�� {existingReservation.Owner.OwnerId} 棰勭害锛�" +
+ $"褰撳墠璇锋眰鑰�: {owner.OwnerId}";
+ Log.Warn($"[ResourceManager] 璧勬簮鍐茬獊: {conflictMsg}");
+ throw new ResourceConflictException(conflictMsg);
+ }
+ }
+
+ // 鍒涘缓鏂伴绾�
+ var newGeneration = Interlocked.Increment(ref _generation);
+ owner.Generation = newGeneration;
+
+ var reservation = new ResourceReservation(resource, owner)
+ {
+ State = ReservationState.Active
+ };
+
+ _reservations[resourceKey] = reservation;
+
+ Log.Info($"[ResourceManager] 璧勬簮 {resourceKey} 棰勭害鎴愬姛锛屾墍鏈夎��: {owner.OwnerId}锛屼笘浠e彿: {newGeneration}");
+ return reservation;
+ }
+ finally
+ {
+ semaphore.Release();
+ }
+ }
+ catch (OperationCanceledException)
+ {
+ Log.Warn($"[ResourceManager] 璧勬簮 {resourceKey} 棰勭害琚彇娑�");
+ throw;
+ }
+ catch (ResourceConflictException)
+ {
+ throw;
+ }
+ catch (Exception ex)
+ {
+ Log.Error($"[ResourceManager] 璧勬簮 {resourceKey} 棰勭害澶辫触: {ex.Message}", ex);
+ throw;
+ }
+ }
+
+ /// <inheritdoc/>
+ public async Task<IOperationLease> AcquireOperationAsync(
+ ResourceOwnerToken owner, ResourceKey resource, CancellationToken ct)
+ {
+ if (owner == null) throw new ArgumentNullException(nameof(owner));
+ if (resource == null) throw new ArgumentNullException(nameof(resource));
+
+ var resourceKey = resource.ToString();
+
+ try
+ {
+ // 妫�鏌ヨ祫婧愭槸鍚︽湁鏈夋晥棰勭害
+ if (!_reservations.TryGetValue(resourceKey, out var reservation))
+ {
+ throw new InvalidOperationException($"璧勬簮 {resourceKey} 鏈棰勭害锛屾棤娉曡幏鍙栨搷浣滅绾�");
+ }
+
+ if (reservation.State != ReservationState.Active)
+ {
+ throw new InvalidOperationException($"璧勬簮 {resourceKey} 棰勭害鐘舵�佷负 {reservation.State}锛屾棤娉曡幏鍙栨搷浣滅绾�");
+ }
+
+ // 楠岃瘉 Owner 鍖归厤锛圱okenId + Generation锛�
+ if (reservation.Owner.TokenId != owner.TokenId || reservation.Owner.Generation != owner.Generation)
+ {
+ throw new InvalidOperationException(
+ $"璧勬簮 {resourceKey} 鎵�鏈夎�呬笉鍖归厤锛屾湡鏈� TokenId: {reservation.Owner.TokenId}锛孏eneration: {reservation.Owner.Generation}");
+ }
+
+ // 鍒涘缓 OperationLease锛堢煭鏈燂紝榛樿30绉掕繃鏈燂級
+ var lease = new OperationLease(
+ resource,
+ owner,
+ onRelease: () => Log.Debug($"[ResourceManager] 绉熺害閲婃斁: {resourceKey}"),
+ leaseDurationSeconds: DefaultLeaseDurationSeconds);
+
+ Log.Info($"[ResourceManager] 鑾峰彇鎿嶄綔绉熺害: {resourceKey}锛屾墍鏈夎��: {owner.OwnerId}锛屾湁鏁堟湡: {DefaultLeaseDurationSeconds}绉�");
+
+ await Task.CompletedTask; // 淇濇寔寮傛绛惧悕
+ return lease;
+ }
+ catch (InvalidOperationException)
+ {
+ throw;
+ }
+ catch (Exception ex)
+ {
+ Log.Error($"[ResourceManager] 鑾峰彇鎿嶄綔绉熺害澶辫触: {ex.Message}", ex);
+ throw;
+ }
+ }
+
+ /// <inheritdoc/>
+ public async Task ReleaseReservationAsync(
+ ResourceOwnerToken owner, ResourceKey resource, CancellationToken ct)
+ {
+ if (owner == null) throw new ArgumentNullException(nameof(owner));
+ if (resource == null) throw new ArgumentNullException(nameof(resource));
+
+ var resourceKey = resource.ToString();
+ var semaphore = _resourceLocks.GetOrAdd(resourceKey, _ => new SemaphoreSlim(1, 1));
+
+ try
+ {
+ await semaphore.WaitAsync(ct).ConfigureAwait(false);
+
+ try
+ {
+ if (!_reservations.TryGetValue(resourceKey, out var reservation))
+ {
+ Log.Warn($"[ResourceManager] 灏濊瘯閲婃斁涓嶅瓨鍦ㄧ殑璧勬簮棰勭害: {resourceKey}");
+ return;
+ }
+
+ // 楠岃瘉 Owner 鍖归厤
+ if (reservation.Owner.TokenId != owner.TokenId)
+ {
+ throw new InvalidOperationException(
+ $"璧勬簮 {resourceKey} 閲婃斁澶辫触锛氭墍鏈夎�呬笉鍖归厤锛屾湡鏈� TokenId: {reservation.Owner.TokenId}");
+ }
+
+ // 鏍囪棰勭害涓� Released
+ reservation.State = ReservationState.Released;
+ _reservations.TryRemove(resourceKey, out _);
+
+ Log.Info($"[ResourceManager] 璧勬簮 {resourceKey} 棰勭害宸查噴鏀撅紝鎵�鏈夎��: {owner.OwnerId}");
+ }
+ finally
+ {
+ semaphore.Release();
+ }
+
+ await Task.CompletedTask; // 淇濇寔寮傛绛惧悕
+ }
+ catch (InvalidOperationException)
+ {
+ throw;
+ }
+ catch (OperationCanceledException)
+ {
+ Log.Warn($"[ResourceManager] 璧勬簮 {resourceKey} 閲婃斁鎿嶄綔琚彇娑�");
+ throw;
+ }
+ catch (Exception ex)
+ {
+ Log.Error($"[ResourceManager] 閲婃斁璧勬簮 {resourceKey} 澶辫触: {ex.Message}", ex);
+ throw;
+ }
+ }
+
+ /// <inheritdoc/>
+ public bool IsReserved(ResourceKey resource, out ResourceReservation? reservation)
+ {
+ if (resource == null) throw new ArgumentNullException(nameof(resource));
+
+ var resourceKey = resource.ToString();
+ var result = _reservations.TryGetValue(resourceKey, out var res);
+
+ if (result && res != null && res.State == ReservationState.Active)
+ {
+ reservation = res;
+ return true;
+ }
+
+ reservation = null;
+ return false;
+ }
+
+ /// <inheritdoc/>
+ public bool ValidateOwner(ResourceOwnerToken owner, ResourceKey resource)
+ {
+ if (owner == null) throw new ArgumentNullException(nameof(owner));
+ if (resource == null) throw new ArgumentNullException(nameof(resource));
+
+ var resourceKey = resource.ToString();
+
+ if (!_reservations.TryGetValue(resourceKey, out var reservation))
+ return false;
+
+ if (reservation.State != ReservationState.Active)
+ return false;
+
+ return reservation.Owner.TokenId == owner.TokenId &&
+ reservation.Owner.Generation == owner.Generation;
+ }
+ }
+}
--
Gitblit v1.9.1