Add healthchecks for all candles + Claim ui fee button
This commit is contained in:
142
src/Managing.Api/HealthChecks/CandleDataDetailedHealthCheck.cs
Normal file
142
src/Managing.Api/HealthChecks/CandleDataDetailedHealthCheck.cs
Normal file
@@ -0,0 +1,142 @@
|
||||
using Managing.Application.Abstractions.Services;
|
||||
using Managing.Common;
|
||||
using Microsoft.Extensions.Diagnostics.HealthChecks;
|
||||
using static Managing.Common.Enums;
|
||||
|
||||
namespace Managing.Api.HealthChecks
|
||||
{
|
||||
public class CandleDataDetailedHealthCheck : IHealthCheck
|
||||
{
|
||||
private readonly IExchangeService _exchangeService;
|
||||
private readonly TradingExchanges _exchangeToCheck = TradingExchanges.Evm;
|
||||
|
||||
public CandleDataDetailedHealthCheck(IExchangeService exchangeService)
|
||||
{
|
||||
_exchangeService = exchangeService;
|
||||
}
|
||||
|
||||
public async Task<HealthCheckResult> CheckHealthAsync(HealthCheckContext context, CancellationToken cancellationToken = default)
|
||||
{
|
||||
try
|
||||
{
|
||||
var now = DateTime.UtcNow;
|
||||
|
||||
// Define timeframes to check with their appropriate start dates and expected freshness
|
||||
var timeframeChecks = new[]
|
||||
{
|
||||
(timeframe: Timeframe.FiveMinutes, startDate: now.AddDays(-1), maxAgeMins: 7),
|
||||
(timeframe: Timeframe.FifteenMinutes, startDate: now.AddDays(-1), maxAgeMins: 17),
|
||||
(timeframe: Timeframe.OneHour, startDate: now.AddDays(-3), maxAgeMins: 62),
|
||||
(timeframe: Timeframe.FourHour, startDate: now.AddDays(-7), maxAgeMins: 242),
|
||||
(timeframe: Timeframe.OneDay, startDate: now.AddDays(-30), maxAgeMins: 1442)
|
||||
};
|
||||
|
||||
var supportedTickers = Constants.GMX.Config.SupportedTickers;
|
||||
var tickerResults = new Dictionary<string, object>();
|
||||
var isHealthy = true;
|
||||
var totalChecks = 0;
|
||||
var healthyChecks = 0;
|
||||
var degradedChecks = 0;
|
||||
|
||||
foreach (var ticker in supportedTickers)
|
||||
{
|
||||
var degradedTimeframeResults = new List<Dictionary<string, object>>();
|
||||
|
||||
foreach (var (timeframe, startDate, maxAgeMins) in timeframeChecks)
|
||||
{
|
||||
totalChecks++;
|
||||
|
||||
// Fetch candles for this ticker and timeframe
|
||||
var candles = await _exchangeService.GetCandlesInflux(
|
||||
_exchangeToCheck,
|
||||
ticker,
|
||||
startDate,
|
||||
timeframe);
|
||||
|
||||
if (candles == null || !candles.Any())
|
||||
{
|
||||
var checkResult = new Dictionary<string, object>
|
||||
{
|
||||
["Timeframe"] = timeframe.ToString(),
|
||||
["StartDate"] = startDate,
|
||||
["Status"] = "Degraded",
|
||||
["Message"] = $"No candle data found for {ticker} - {timeframe}"
|
||||
};
|
||||
degradedTimeframeResults.Add(checkResult);
|
||||
isHealthy = false;
|
||||
degradedChecks++;
|
||||
}
|
||||
else
|
||||
{
|
||||
var latestCandle = candles.OrderByDescending(c => c.Date).FirstOrDefault();
|
||||
var timeDiff = now - latestCandle.Date;
|
||||
|
||||
if (timeDiff.TotalMinutes > maxAgeMins)
|
||||
{
|
||||
var checkResult = new Dictionary<string, object>
|
||||
{
|
||||
["Timeframe"] = timeframe.ToString(),
|
||||
["StartDate"] = startDate,
|
||||
["LatestCandleDate"] = latestCandle.Date,
|
||||
["TimeDifference"] = $"{timeDiff.TotalMinutes:F2} minutes",
|
||||
["CandleCount"] = candles.Count(),
|
||||
["Status"] = "Degraded",
|
||||
["Message"] = $"Data for {ticker} - {timeframe} is outdated. Latest candle is from {latestCandle.Date:yyyy-MM-dd HH:mm:ss} UTC"
|
||||
};
|
||||
degradedTimeframeResults.Add(checkResult);
|
||||
isHealthy = false;
|
||||
degradedChecks++;
|
||||
}
|
||||
else
|
||||
{
|
||||
healthyChecks++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Only add ticker to results if it has degraded checks
|
||||
if (degradedTimeframeResults.Any())
|
||||
{
|
||||
tickerResults[ticker.ToString()] = degradedTimeframeResults;
|
||||
}
|
||||
}
|
||||
|
||||
// Build summary
|
||||
var summary = new Dictionary<string, object>
|
||||
{
|
||||
["TotalChecks"] = totalChecks,
|
||||
["HealthyChecks"] = healthyChecks,
|
||||
["DegradedChecks"] = degradedChecks,
|
||||
["CheckedTickers"] = supportedTickers.Select(t => t.ToString()).ToArray(),
|
||||
["CheckedTimeframes"] = timeframeChecks.Select(tc => tc.timeframe.ToString()).ToArray(),
|
||||
["TickerResults"] = tickerResults
|
||||
};
|
||||
|
||||
if (isHealthy)
|
||||
{
|
||||
return HealthCheckResult.Healthy(
|
||||
$"All candle data is up-to-date for {supportedTickers.Length} tickers across {timeframeChecks.Length} timeframes",
|
||||
data: summary);
|
||||
}
|
||||
else
|
||||
{
|
||||
return HealthCheckResult.Degraded(
|
||||
$"{degradedChecks} out of {totalChecks} candle checks are outdated or missing",
|
||||
data: summary);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return HealthCheckResult.Unhealthy(
|
||||
"Error checking detailed candle data health",
|
||||
ex,
|
||||
data: new Dictionary<string, object>
|
||||
{
|
||||
["ErrorMessage"] = ex.Message,
|
||||
["ErrorType"] = ex.GetType().Name
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,6 +4,10 @@ using static Managing.Common.Enums;
|
||||
|
||||
namespace Managing.Api.HealthChecks
|
||||
{
|
||||
/// <summary>
|
||||
/// Lightweight health check for candle data - only checks ETH across all timeframes
|
||||
/// For comprehensive checks across all tickers, see CandleDataDetailedHealthCheck
|
||||
/// </summary>
|
||||
public class CandleDataHealthCheck : IHealthCheck
|
||||
{
|
||||
private readonly IExchangeService _exchangeService;
|
||||
@@ -90,13 +94,13 @@ namespace Managing.Api.HealthChecks
|
||||
if (isHealthy)
|
||||
{
|
||||
return HealthCheckResult.Healthy(
|
||||
"All candle timeframes are up-to-date",
|
||||
$"All {_tickerToCheck} candle timeframes are up-to-date",
|
||||
data: resultsDictionary);
|
||||
}
|
||||
else
|
||||
{
|
||||
return HealthCheckResult.Degraded(
|
||||
"One or more candle timeframes are outdated or missing",
|
||||
$"One or more {_tickerToCheck} candle timeframes are outdated or missing",
|
||||
data: resultsDictionary);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -144,6 +144,7 @@ builder.Services.AddHealthChecks()
|
||||
.AddUrlGroup(new Uri($"{influxUrl}/health"), name: "influxdb", tags: ["database"])
|
||||
.AddCheck<Web3ProxyHealthCheck>("web3proxy", tags: ["api", "external"])
|
||||
.AddCheck<CandleDataHealthCheck>("candle-data", tags: ["database", "candles"])
|
||||
.AddCheck<CandleDataDetailedHealthCheck>("candle-data-detailed", tags: ["database", "candles-detailed"])
|
||||
.AddCheck<GmxConnectivityHealthCheck>("gmx-connectivity", tags: ["api", "external"])
|
||||
.AddCheck<OrleansHealthCheck>("orleans-cluster", tags: ["orleans", "cluster"]);
|
||||
|
||||
@@ -337,6 +338,13 @@ app.UseEndpoints(endpoints =>
|
||||
|
||||
endpoints.MapHealthChecks("/health", new HealthCheckOptions
|
||||
{
|
||||
Predicate = r => !r.Tags.Contains("candles-detailed"),
|
||||
ResponseWriter = UIResponseWriter.WriteHealthCheckUIResponse
|
||||
});
|
||||
|
||||
endpoints.MapHealthChecks("/health-candles", new HealthCheckOptions
|
||||
{
|
||||
Predicate = r => r.Tags.Contains("candles-detailed"),
|
||||
ResponseWriter = UIResponseWriter.WriteHealthCheckUIResponse
|
||||
});
|
||||
|
||||
|
||||
Reference in New Issue
Block a user