-
Notifications
You must be signed in to change notification settings - Fork 804
/
DestinationHealthUpdater.cs
149 lines (127 loc) · 6.61 KB
/
DestinationHealthUpdater.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using Microsoft.Extensions.Logging;
using Yarp.ReverseProxy.Model;
namespace Yarp.ReverseProxy.Health;
internal sealed class DestinationHealthUpdater : IDestinationHealthUpdater, IDisposable
{
private readonly EntityActionScheduler<(ClusterState Cluster, DestinationState Destination)> _scheduler;
private readonly IClusterDestinationsUpdater _clusterUpdater;
private readonly ILogger<DestinationHealthUpdater> _logger;
public DestinationHealthUpdater(
TimeProvider timeProvider,
IClusterDestinationsUpdater clusterDestinationsUpdater,
ILogger<DestinationHealthUpdater> logger)
{
_scheduler = new EntityActionScheduler<(ClusterState Cluster, DestinationState Destination)>(d => Reactivate(d.Cluster, d.Destination), autoStart: true, runOnce: true, timeProvider);
_clusterUpdater = clusterDestinationsUpdater ?? throw new ArgumentNullException(nameof(clusterDestinationsUpdater));
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
}
public void SetActive(ClusterState cluster, IEnumerable<NewActiveDestinationHealth> newHealthPairs)
{
var changed = false;
foreach (var newHealthPair in newHealthPairs)
{
var destination = newHealthPair.Destination;
var newHealth = newHealthPair.NewActiveHealth;
var healthState = destination.Health;
if (newHealth != healthState.Active)
{
healthState.Active = newHealth;
changed = true;
if (newHealth == DestinationHealth.Unhealthy)
{
Log.ActiveDestinationHealthStateIsSetToUnhealthy(_logger, destination.DestinationId, cluster.ClusterId);
}
else
{
Log.ActiveDestinationHealthStateIsSet(_logger, destination.DestinationId, cluster.ClusterId, newHealth);
}
}
}
if (changed)
{
_clusterUpdater.UpdateAvailableDestinations(cluster);
}
}
public void SetPassive(ClusterState cluster, DestinationState destination, DestinationHealth newHealth, TimeSpan reactivationPeriod)
{
_ = SetPassiveAsync(cluster, destination, newHealth, reactivationPeriod);
}
internal Task SetPassiveAsync(ClusterState cluster, DestinationState destination, DestinationHealth newHealth, TimeSpan reactivationPeriod)
{
var healthState = destination.Health;
if (newHealth != healthState.Passive)
{
healthState.Passive = newHealth;
ScheduleReactivation(cluster, destination, newHealth, reactivationPeriod);
return Task.Factory.StartNew(c => UpdateDestinations(c!), cluster, TaskCreationOptions.RunContinuationsAsynchronously);
}
return Task.CompletedTask;
}
private void UpdateDestinations(object cluster)
{
_clusterUpdater.UpdateAvailableDestinations((ClusterState)cluster);
}
private void ScheduleReactivation(ClusterState cluster, DestinationState destination, DestinationHealth newHealth, TimeSpan reactivationPeriod)
{
if (newHealth == DestinationHealth.Unhealthy)
{
_scheduler.ScheduleEntity((cluster, destination), reactivationPeriod);
Log.UnhealthyDestinationIsScheduledForReactivation(_logger, destination.DestinationId, reactivationPeriod);
}
}
public void Dispose()
{
_scheduler.Dispose();
}
private Task Reactivate(ClusterState cluster, DestinationState destination)
{
var healthState = destination.Health;
if (healthState.Passive == DestinationHealth.Unhealthy)
{
healthState.Passive = DestinationHealth.Unknown;
Log.PassiveDestinationHealthResetToUnkownState(_logger, destination.DestinationId);
_clusterUpdater.UpdateAvailableDestinations(cluster);
}
return Task.CompletedTask;
}
private static class Log
{
private static readonly Action<ILogger, string, TimeSpan, Exception?> _unhealthyDestinationIsScheduledForReactivation = LoggerMessage.Define<string, TimeSpan>(
LogLevel.Warning,
EventIds.UnhealthyDestinationIsScheduledForReactivation,
"Destination `{destinationId}` marked as 'Unhealthy` by the passive health check is scheduled for a reactivation in `{reactivationPeriod}`.");
private static readonly Action<ILogger, string, Exception?> _passiveDestinationHealthResetToUnkownState = LoggerMessage.Define<string>(
LogLevel.Information,
EventIds.PassiveDestinationHealthResetToUnkownState,
"Passive health state of the destination `{destinationId}` is reset to 'Unknown`.");
private static readonly Action<ILogger, string, string, Exception?> _activeDestinationHealthStateIsSetToUnhealthy = LoggerMessage.Define<string, string>(
LogLevel.Warning,
EventIds.ActiveDestinationHealthStateIsSetToUnhealthy,
"Active health state of destination `{destinationId}` on cluster `{clusterId}` is set to 'Unhealthy'.");
private static readonly Action<ILogger, string, string, DestinationHealth, Exception?> _activeDestinationHealthStateIsSet = LoggerMessage.Define<string, string, DestinationHealth>(
LogLevel.Information,
EventIds.ActiveDestinationHealthStateIsSet,
"Active health state of destination `{destinationId}` on cluster `{clusterId}` is set to '{newHealthState}'.");
public static void ActiveDestinationHealthStateIsSetToUnhealthy(ILogger logger, string destinationId, string clusterId)
{
_activeDestinationHealthStateIsSetToUnhealthy(logger, destinationId, clusterId, null);
}
public static void ActiveDestinationHealthStateIsSet(ILogger logger, string destinationId, string clusterId, DestinationHealth newHealthState)
{
_activeDestinationHealthStateIsSet(logger, destinationId, clusterId, newHealthState, null);
}
public static void UnhealthyDestinationIsScheduledForReactivation(ILogger logger, string destinationId, TimeSpan reactivationPeriod)
{
_unhealthyDestinationIsScheduledForReactivation(logger, destinationId, reactivationPeriod, null);
}
public static void PassiveDestinationHealthResetToUnkownState(ILogger logger, string destinationId)
{
_passiveDestinationHealthResetToUnkownState(logger, destinationId, null);
}
}
}