From 5dc9dc5979cb73f43de4cc42f2f5aa5ab8a3da20 Mon Sep 17 00:00:00 2001 From: movingsam Date: Sun, 8 Mar 2026 10:29:30 +0800 Subject: [PATCH] =?UTF-8?q?IMPL-6:=20=E9=9B=86=E6=88=90=20ConfigNotificati?= =?UTF-8?q?onService=20=E9=80=9A=E7=9F=A5=E7=BD=91=E5=85=B3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 在 Confirm 方法中添加事务包装,确保数据库写入的原子性 - 注入 ConsoleDbContext 用于事务管理 - 添加详细的日志记录: - 配置确认开始/结束 - 数据库写入成功 - 事务提交/回滚 - NOTIFY 发送成功/失败 - 缓存移除成功/失败 - 事务处理逻辑:先提交数据库事务,再发送 NOTIFY - NOTIFY 失败不影响数据一致性(仅记录错误) --- src/Controllers/PendingConfigController.cs | 63 ++++++++++++++++++---- 1 file changed, 53 insertions(+), 10 deletions(-) diff --git a/src/Controllers/PendingConfigController.cs b/src/Controllers/PendingConfigController.cs index 294a233..a7edd94 100644 --- a/src/Controllers/PendingConfigController.cs +++ b/src/Controllers/PendingConfigController.cs @@ -1,10 +1,12 @@ using System.Security.Claims; using System.Text.Json; using System.Text.Json.Nodes; +using Fengling.Console.Data; using Fengling.Console.Models.Dtos; using Fengling.Console.Models.Entities; using Fengling.Console.Models.K8s; using Fengling.Platform.Domain.AggregatesModel.GatewayAggregate; +using Microsoft.EntityFrameworkCore; namespace Fengling.Console.Controllers; @@ -21,6 +23,7 @@ public class PendingConfigController : ControllerBase private readonly INotificationService _notificationService; private readonly IClusterStore _clusterStore; private readonly IRouteStore _routeStore; + private readonly ConsoleDbContext _dbContext; private readonly ILogger _logger; public PendingConfigController( @@ -28,12 +31,14 @@ public class PendingConfigController : ControllerBase INotificationService notificationService, IClusterStore clusterStore, IRouteStore routeStore, + ConsoleDbContext dbContext, ILogger logger) { _pendingConfigCache = pendingConfigCache; _notificationService = notificationService; _clusterStore = clusterStore; _routeStore = routeStore; + _dbContext = dbContext; _logger = logger; } @@ -188,11 +193,14 @@ public class PendingConfigController : ControllerBase [ProducesResponseType(typeof(object), StatusCodes.Status500InternalServerError)] public async Task Confirm(string id, [FromBody] ConfirmPendingConfigRequest? request) { + _logger.LogInformation("Starting config confirmation process for ConfigId: {ConfigId}", id); + try { var config = FindConfigById(id); if (config == null) { + _logger.LogWarning("Pending config not found: {ConfigId}", id); return NotFound(new { message = $"Pending config {id} not found" }); } @@ -204,27 +212,62 @@ public class PendingConfigController : ControllerBase if (configModel == null) { + _logger.LogError("Invalid config format for ConfigId: {ConfigId}", id); return BadRequest(new { message = "Invalid config format" }); } // 获取当前用户 var confirmedBy = User.FindFirst(ClaimTypes.Name)?.Value ?? User.Identity?.Name ?? "system"; + _logger.LogInformation("Config {ConfigId} confirmation initiated by user: {ConfirmedBy}", id, confirmedBy); - // 写入数据库 - await SaveConfigToDatabaseAsync(configModel); + // 使用事务包装数据库写入和通知 + await using var transaction = await _dbContext.Database.BeginTransactionAsync(); + try + { + // 写入数据库 + await SaveConfigToDatabaseAsync(configModel); + _logger.LogInformation("Database write completed successfully for ConfigId: {ConfigId}", id); - // 发送 PostgreSQL NOTIFY - await _notificationService.PublishConfigChangeAsync( - "route", - "create", - new { configId = id, serviceName = config.ServiceName, clusterId = config.ClusterId } - ); + // 提交事务 + await transaction.CommitAsync(); + _logger.LogInformation("Database transaction committed for ConfigId: {ConfigId}", id); + } + catch (Exception ex) + { + await transaction.RollbackAsync(); + _logger.LogError(ex, "Database transaction failed and rolled back for ConfigId: {ConfigId}", id); + throw; + } + + // 发送 PostgreSQL NOTIFY(在事务外,因为 NOTIFY 使用独立连接) + try + { + await _notificationService.PublishConfigChangeAsync( + "route", + "create", + new { configId = id, serviceName = config.ServiceName, clusterId = config.ClusterId } + ); + _logger.LogInformation("NOTIFY sent successfully for ConfigId: {ConfigId}, Channel: gateway_config_changed", id); + } + catch (Exception ex) + { + // NOTIFY 失败不应影响已提交的数据,仅记录错误 + _logger.LogError(ex, "Failed to send NOTIFY for ConfigId: {ConfigId}, but database changes are already committed", id); + } // 从缓存中移除 - _pendingConfigCache.Remove(config.SourceId); + var removed = _pendingConfigCache.Remove(config.SourceId); + if (removed) + { + _logger.LogInformation("Config removed from cache successfully: {ConfigId}, SourceId: {SourceId}", id, config.SourceId); + } + else + { + _logger.LogWarning("Failed to remove config from cache: {ConfigId}, SourceId: {SourceId}", id, config.SourceId); + } _logger.LogInformation( - "Pending config {ConfigId} confirmed by {ConfirmedBy}. Service: {ServiceName}", + "Pending config {ConfigId} confirmed successfully by {ConfirmedBy}. Service: {ServiceName}", id, confirmedBy, config.ServiceName); return Ok(new { message = "Config confirmed successfully", configId = id });