diff --git a/src/Discovery/src/Eureka/EurekaDiscoveryClient.cs b/src/Discovery/src/Eureka/EurekaDiscoveryClient.cs index 5e286261f2..db5a32a0f5 100644 --- a/src/Discovery/src/Eureka/EurekaDiscoveryClient.cs +++ b/src/Discovery/src/Eureka/EurekaDiscoveryClient.cs @@ -37,6 +37,7 @@ public sealed partial class EurekaDiscoveryClient : IDiscoveryClient private readonly Timer? _cacheRefreshTimer; private readonly SemaphoreSlim _registerUnregisterAsyncLock = new(1); private readonly SemaphoreSlim _registryFetchAsyncLock = new(1); + private volatile bool _hasRegistered; private volatile bool _hasFirstHeartbeatCompleted; private volatile ApplicationInfoCollection _remoteApps; @@ -195,7 +196,7 @@ public async Task ShutdownAsync(CancellationToken cancellationToken) try { - if (!ReferenceEquals(_appInfoManager.Instance, InstanceInfo.Disabled)) + if (_hasRegistered && !ReferenceEquals(_appInfoManager.Instance, InstanceInfo.Disabled)) { await DeregisterAsync(cancellationToken); } @@ -268,6 +269,7 @@ internal async Task RegisterAsync(bool requireDirtyInstance, CancellationToken c LogRegistrationSucceeded(snapshot.AppName, snapshot.InstanceId); snapshot.IsDirty = false; + _hasRegistered = true; } } finally diff --git a/src/Discovery/test/Eureka.Test/EurekaDiscoveryClientTest.cs b/src/Discovery/test/Eureka.Test/EurekaDiscoveryClientTest.cs index f98e97c2e0..590e979a1c 100644 --- a/src/Discovery/test/Eureka.Test/EurekaDiscoveryClientTest.cs +++ b/src/Discovery/test/Eureka.Test/EurekaDiscoveryClientTest.cs @@ -417,6 +417,63 @@ public async Task UnRegisterAsync_Succeeds_WhenOKStatusReturned() handler.Mock.VerifyNoOutstandingExpectation(); } + [Fact] + public async Task ShutdownAsync_Unregisters_WhenRegistered() + { + var appSettings = new Dictionary + { + ["Eureka:Client:ShouldFetchRegistry"] = "false", + ["Eureka:Client:ShouldRegisterWithEureka"] = "true", + ["Eureka:Instance:AppName"] = "FOO", + ["Eureka:Instance:InstanceId"] = "localhost:foo" + }; + + WebApplicationBuilder builder = TestWebApplicationBuilderFactory.Create(); + builder.Configuration.AddInMemoryCollection(appSettings); + builder.Services.AddEurekaDiscoveryClient(); + + var handler = new DelegateToMockHttpClientHandler(); + handler.Mock.Expect(HttpMethod.Post, "http://localhost:8761/eureka/apps/FOO").Respond(HttpStatusCode.OK); + handler.Mock.Expect(HttpMethod.Delete, "http://localhost:8761/eureka/apps/FOO/localhost%3Afoo").Respond(HttpStatusCode.OK); + + await using WebApplication webApplication = builder.Build(); + webApplication.Services.GetRequiredService().Using(handler); + + var discoveryClient = webApplication.Services.GetRequiredService(); + + await discoveryClient.ShutdownAsync(TestContext.Current.CancellationToken); + + handler.Mock.VerifyNoOutstandingExpectation(); + } + + [Fact] + public async Task ShutdownAsync_DoesNotUnregister_WhenNotRegistered() + { + var appSettings = new Dictionary + { + ["Eureka:Client:ShouldFetchRegistry"] = "false", + ["Eureka:Client:ShouldRegisterWithEureka"] = "true", + ["Eureka:Instance:AppName"] = "FOO", + ["Eureka:Instance:InstanceId"] = "localhost:foo" + }; + + WebApplicationBuilder builder = TestWebApplicationBuilderFactory.Create(); + builder.Configuration.AddInMemoryCollection(appSettings); + builder.Services.AddEurekaDiscoveryClient(); + + var handler = new DelegateToMockHttpClientHandler(); + handler.Mock.Expect(HttpMethod.Post, "http://localhost:8761/eureka/apps/FOO").Respond(HttpStatusCode.NotFound); + + await using WebApplication webApplication = builder.Build(); + webApplication.Services.GetRequiredService().Using(handler); + + var discoveryClient = webApplication.Services.GetRequiredService(); + + await discoveryClient.ShutdownAsync(TestContext.Current.CancellationToken); + + handler.Mock.VerifyNoOutstandingExpectation(); + } + [Fact] public async Task GetInstancesAsync_ReturnsExpected() {