From 05e088fdcdefa3533e5e52a889f4f6a91aceb709 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Krzysztof=20=C5=81abu=C5=9B?= Date: Thu, 28 Sep 2023 11:24:48 +0200 Subject: [PATCH 01/14] Added missing observer call (metrics) in LedgerTransactionsProcessor --- CHANGELOG.md | 8 +++++--- Directory.Build.props | 2 +- .../Services/LedgerTransactionsProcessor.cs | 5 +++-- 3 files changed, 9 insertions(+), 6 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3c02a62c6..efff06014 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,8 +1,10 @@ -## 1.0.0 - Babylon Launch +## 1.0.1 - Babylon Release Date: _unreleased_ -### Breaking changes -_None_ +- Fixed missing `RecordTopOfDbLedger` observer call in `LedgerTransactionsProcessor`. + +## 1.0.0 - Babylon Launch +Release Date: 28.09.2023 ### What’s new? - log warning if sql query takes longer than configured threshold (default to 250ms) for both entity framework and dapper queries. diff --git a/Directory.Build.props b/Directory.Build.props index aad66eb02..1c50faaa4 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -13,7 +13,7 @@ - 1.0.0 + 1.0.1 develop diff --git a/src/RadixDlt.NetworkGateway.DataAggregator/Services/LedgerTransactionsProcessor.cs b/src/RadixDlt.NetworkGateway.DataAggregator/Services/LedgerTransactionsProcessor.cs index 983115a3d..3438631fb 100644 --- a/src/RadixDlt.NetworkGateway.DataAggregator/Services/LedgerTransactionsProcessor.cs +++ b/src/RadixDlt.NetworkGateway.DataAggregator/Services/LedgerTransactionsProcessor.cs @@ -193,9 +193,10 @@ private void ReportOnLedgerExtensionSuccess(ConsistentLedgerExtension ledgerExte var currentTimestamp = _clock.UtcNow; var committedTransactionSummary = commitReport.FinalTransaction; + var roundTimestampDiff = currentTimestamp - ledgerExtension.LatestTransactionSummary.RoundTimestamp; - _observers.ForEach(x => - x.ReportOnLedgerExtensionSuccess(currentTimestamp, currentTimestamp - ledgerExtension.LatestTransactionSummary.RoundTimestamp, totalCommitMs, commitReport.TransactionsCommittedCount)); + _observers.ForEach(x => x.RecordTopOfDbLedger(ledgerExtension.LatestTransactionSummary.StateVersion, ledgerExtension.LatestTransactionSummary.RoundTimestamp)); + _observers.ForEach(x => x.ReportOnLedgerExtensionSuccess(currentTimestamp, roundTimestampDiff, totalCommitMs, commitReport.TransactionsCommittedCount)); _logger.LogInformation( "Committed {TransactionCount} transactions to the DB in {TotalCommitTransactionsMs}ms [EntitiesTouched={DbEntriesWritten}]", From 445beb72d367a6be3c554ba9d38aeb723778bb15 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Pawelec?= Date: Fri, 29 Sep 2023 13:45:20 +0200 Subject: [PATCH 02/14] return validation error if from state version is beyond known ledger tip --- .../Services/LedgerStateQuerier.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/RadixDlt.NetworkGateway.PostgresIntegration/Services/LedgerStateQuerier.cs b/src/RadixDlt.NetworkGateway.PostgresIntegration/Services/LedgerStateQuerier.cs index ef37aa9cd..9b816c126 100644 --- a/src/RadixDlt.NetworkGateway.PostgresIntegration/Services/LedgerStateQuerier.cs +++ b/src/RadixDlt.NetworkGateway.PostgresIntegration/Services/LedgerStateQuerier.cs @@ -340,7 +340,7 @@ private async Task GetLedgerStateAfterStateVersion(long state if (ledgerState == null) { - throw new InvalidStateException("State version is beyond the end of the known ledger"); + throw InvalidRequestException.FromOtherError("State version is beyond the end of the known ledger"); } return ledgerState; From 1cc9d631ed67c35f53938f949023081d9df4d501 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Krzysztof=20=C5=81abu=C5=9B?= Date: Wed, 4 Oct 2023 10:48:30 +0200 Subject: [PATCH 03/14] Fixed invalid response model for HTTP 400 Bad Request responses on input parameter validation failure. --- CHANGELOG.md | 1 + apps/GatewayApi/GatewayApiStartup.cs | 12 +++++++++--- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index efff06014..407ec47b3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,7 @@ Release Date: _unreleased_ - Fixed missing `RecordTopOfDbLedger` observer call in `LedgerTransactionsProcessor`. +- Fixed invalid response model for HTTP 400 Bad Request responses on input parameter validation failure. ## 1.0.0 - Babylon Launch Release Date: 28.09.2023 diff --git a/apps/GatewayApi/GatewayApiStartup.cs b/apps/GatewayApi/GatewayApiStartup.cs index 35d9a7171..34d53f2bb 100644 --- a/apps/GatewayApi/GatewayApiStartup.cs +++ b/apps/GatewayApi/GatewayApiStartup.cs @@ -64,12 +64,14 @@ using GatewayApi.SlowRequestLogging; using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Mvc; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; using Newtonsoft.Json; using Prometheus; using RadixDlt.NetworkGateway.GatewayApi; +using RadixDlt.NetworkGateway.GatewayApi.Services; using RadixDlt.NetworkGateway.PostgresIntegration; using RadixDlt.NetworkGateway.PrometheusIntegration; using System; @@ -106,10 +108,14 @@ public void ConfigureServices(IServiceCollection services) services .AddControllers() .AddControllersAsServices() - .AddNewtonsoftJson(o => + .AddNewtonsoftJson(options => { - o.SerializerSettings.NullValueHandling = NullValueHandling.Ignore; - o.SerializerSettings.DateTimeZoneHandling = DateTimeZoneHandling.Utc; + options.SerializerSettings.NullValueHandling = NullValueHandling.Ignore; + options.SerializerSettings.DateTimeZoneHandling = DateTimeZoneHandling.Utc; + }) + .ConfigureApiBehaviorOptions(options => + { + options.InvalidModelStateResponseFactory = ctx => ctx.HttpContext.RequestServices.GetRequiredService().GetClientError(ctx); }); services From 64ae061df86d64c0a59919e130c58866b0578e54 Mon Sep 17 00:00:00 2001 From: Kim Fehrs <122281269+kofrdx@users.noreply.github.com> Date: Wed, 4 Oct 2023 11:02:41 +0200 Subject: [PATCH 04/14] Update releases.yml (#470) --- .github/workflows/releases.yml | 4 ---- 1 file changed, 4 deletions(-) diff --git a/.github/workflows/releases.yml b/.github/workflows/releases.yml index b790241f4..38261ed09 100644 --- a/.github/workflows/releases.yml +++ b/.github/workflows/releases.yml @@ -7,7 +7,6 @@ env: jobs: build: runs-on: ubuntu-22.04 - environment: release steps: - uses: actions/checkout@c85c95e3d7251135ab7dc9ce3241c5835cc595a9 - name: Setup .NET SDK @@ -78,7 +77,6 @@ jobs: context: "." dockerfile: "./apps/DatabaseMigrations/Dockerfile" platforms: "linux/amd64" - environment: release cache_tag_suffix: "release" enable_dockerhub: "true" restore_artifact: "true" @@ -127,7 +125,6 @@ jobs: context: "." dockerfile: "./apps/DataAggregator/Dockerfile" platforms: "linux/amd64" - environment: release cache_tag_suffix: "release" enable_dockerhub: "true" restore_artifact: "true" @@ -176,7 +173,6 @@ jobs: context: "." dockerfile: "./apps/GatewayApi/Dockerfile" platforms: "linux/amd64" - environment: release cache_tag_suffix: "release" enable_dockerhub: "true" restore_artifact: "true" From 22818b9e7640737e8091831f52b3228855725f16 Mon Sep 17 00:00:00 2001 From: PawelPawelec-RDX <122867949+PawelPawelec-RDX@users.noreply.github.com> Date: Wed, 4 Oct 2023 11:02:57 +0200 Subject: [PATCH 05/14] add missing entry to changelog (Return 400 with validation error instead of 500 if `from_ledger_state` `state_version` is beyond known ledger tip) --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 407ec47b3..b47e97706 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,7 @@ Release Date: _unreleased_ - Fixed missing `RecordTopOfDbLedger` observer call in `LedgerTransactionsProcessor`. - Fixed invalid response model for HTTP 400 Bad Request responses on input parameter validation failure. +- Return 400 with validation error instead of 500 if `from_ledger_state` `state_version` is beyond known ledger tip. ## 1.0.0 - Babylon Launch Release Date: 28.09.2023 From 8ea3678f70e69864d7e3c43e6bb543b817bd90ab Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Krzysztof=20=C5=81abu=C5=9B?= Date: Thu, 5 Oct 2023 09:12:23 +0200 Subject: [PATCH 06/14] Use lazy loading of Core API provider in CoreApiHandler to prevent ctor exceptions --- .../CoreCommunications/CoreApiHandler.cs | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/src/RadixDlt.NetworkGateway.GatewayApi/CoreCommunications/CoreApiHandler.cs b/src/RadixDlt.NetworkGateway.GatewayApi/CoreCommunications/CoreApiHandler.cs index 480339218..db96cc849 100644 --- a/src/RadixDlt.NetworkGateway.GatewayApi/CoreCommunications/CoreApiHandler.cs +++ b/src/RadixDlt.NetworkGateway.GatewayApi/CoreCommunications/CoreApiHandler.cs @@ -66,6 +66,7 @@ using RadixDlt.NetworkGateway.Abstractions.CoreCommunications; using RadixDlt.NetworkGateway.GatewayApi.Configuration; using RadixDlt.NetworkGateway.GatewayApi.Services; +using System; using System.Net.Http; using System.Threading; using System.Threading.Tasks; @@ -93,7 +94,7 @@ public interface ICoreApiHandler internal class CoreApiHandler : ICoreApiHandler { private readonly INetworkConfigurationProvider _networkConfigurationProvider; - private readonly ICoreApiProvider _coreApiProvider; + private readonly Lazy _coreApiProvider; public CoreApiHandler( INetworkConfigurationProvider networkConfigurationProvider, @@ -101,7 +102,7 @@ public CoreApiHandler( HttpClient httpClient) { _networkConfigurationProvider = networkConfigurationProvider; - _coreApiProvider = ChooseCoreApiProvider(coreNodesSelectorService, httpClient); + _coreApiProvider = new Lazy(() => ChooseCoreApiProvider(coreNodesSelectorService, httpClient)); } public byte GetNetworkId() @@ -116,12 +117,12 @@ public string GetNetworkName() public CoreApiNode GetCoreNodeConnectedTo() { - return _coreApiProvider.CoreApiNode; + return _coreApiProvider.Value.CoreApiNode; } public TransactionApi GetTransactionApi() { - return _coreApiProvider.TransactionApi; + return _coreApiProvider.Value.TransactionApi; } public async Task> PreviewTransaction( @@ -129,7 +130,7 @@ public TransactionApi GetTransactionApi() CancellationToken token = default) { return await CoreApiErrorWrapper.ResultOrError(() => - _coreApiProvider.TransactionApi.TransactionPreviewPostAsync(request, token)); + _coreApiProvider.Value.TransactionApi.TransactionPreviewPostAsync(request, token)); } private static ICoreApiProvider ChooseCoreApiProvider(ICoreNodesSelectorService coreNodesSelectorService, HttpClient httpClient) From bb8f73ccac058379ba8e58af10f30e0ef116dbfb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Pawelec?= Date: Thu, 5 Oct 2023 08:07:49 +0200 Subject: [PATCH 07/14] add node name to transaction preview/submit metrics. --- .../TransactionSubmitter.cs | 1 + .../Services/IPreviewService.cs | 8 ++-- .../Services/IPreviewServiceObserver.cs | 6 +-- .../PendingTransactionResubmissionService.cs | 1 + .../Services/SubmissionTrackingService.cs | 1 + .../DataAggregatorMetricsObserver.cs | 8 ++-- .../GatewayApiMetricObserver.cs | 42 +++++++++++-------- 7 files changed, 40 insertions(+), 27 deletions(-) diff --git a/src/RadixDlt.NetworkGateway.Abstractions/CoreCommunications/TransactionSubmitter.cs b/src/RadixDlt.NetworkGateway.Abstractions/CoreCommunications/TransactionSubmitter.cs index 273728548..bb740e9c9 100644 --- a/src/RadixDlt.NetworkGateway.Abstractions/CoreCommunications/TransactionSubmitter.cs +++ b/src/RadixDlt.NetworkGateway.Abstractions/CoreCommunications/TransactionSubmitter.cs @@ -270,6 +270,7 @@ public interface ITransactionSubmitterObserver public record SubmitContext( CoreApi.TransactionApi TransactionApi, + string TargetNode, string NetworkName, TimeSpan SubmissionTimeout, bool IsResubmission, diff --git a/src/RadixDlt.NetworkGateway.GatewayApi/Services/IPreviewService.cs b/src/RadixDlt.NetworkGateway.GatewayApi/Services/IPreviewService.cs index 9530b2d72..a8804ff98 100644 --- a/src/RadixDlt.NetworkGateway.GatewayApi/Services/IPreviewService.cs +++ b/src/RadixDlt.NetworkGateway.GatewayApi/Services/IPreviewService.cs @@ -97,17 +97,19 @@ public PreviewService(ICoreApiHandler coreApiHandler, IEnumerable x.PreHandlePreviewRequest(request)); + var selectedNode = _coreApiHandler.GetCoreNodeConnectedTo(); + await _observers.ForEachAsync(x => x.PreHandlePreviewRequest(request, selectedNode.Name)); var response = await HandlePreviewAndCreateResponse(request, token); - await _observers.ForEachAsync(x => x.PostHandlePreviewRequest(request, response)); + await _observers.ForEachAsync(x => x.PostHandlePreviewRequest(request, selectedNode.Name, response)); return response; } catch (Exception ex) { - await _observers.ForEachAsync(x => x.HandlePreviewRequestFailed(request, ex)); + var selectedNode = _coreApiHandler.GetCoreNodeConnectedTo(); + await _observers.ForEachAsync(x => x.HandlePreviewRequestFailed(request, selectedNode.Name, ex)); throw; } diff --git a/src/RadixDlt.NetworkGateway.GatewayApi/Services/IPreviewServiceObserver.cs b/src/RadixDlt.NetworkGateway.GatewayApi/Services/IPreviewServiceObserver.cs index 6d1855b26..243df0e06 100644 --- a/src/RadixDlt.NetworkGateway.GatewayApi/Services/IPreviewServiceObserver.cs +++ b/src/RadixDlt.NetworkGateway.GatewayApi/Services/IPreviewServiceObserver.cs @@ -71,9 +71,9 @@ namespace RadixDlt.NetworkGateway.GatewayApi.Services; public interface IPreviewServiceObserver { - ValueTask PreHandlePreviewRequest(GatewayModel.TransactionPreviewRequest request); + ValueTask PreHandlePreviewRequest(GatewayModel.TransactionPreviewRequest request, string targetNode); - ValueTask PostHandlePreviewRequest(GatewayModel.TransactionPreviewRequest request, GatewayModel.TransactionPreviewResponse response); + ValueTask PostHandlePreviewRequest(GatewayModel.TransactionPreviewRequest request, string targetNode, GatewayModel.TransactionPreviewResponse response); - ValueTask HandlePreviewRequestFailed(GatewayModel.TransactionPreviewRequest request, Exception exception); + ValueTask HandlePreviewRequestFailed(GatewayModel.TransactionPreviewRequest request, string targetNode, Exception exception); } diff --git a/src/RadixDlt.NetworkGateway.PostgresIntegration/Services/PendingTransactionResubmissionService.cs b/src/RadixDlt.NetworkGateway.PostgresIntegration/Services/PendingTransactionResubmissionService.cs index 9533ee628..37563a49b 100644 --- a/src/RadixDlt.NetworkGateway.PostgresIntegration/Services/PendingTransactionResubmissionService.cs +++ b/src/RadixDlt.NetworkGateway.PostgresIntegration/Services/PendingTransactionResubmissionService.cs @@ -279,6 +279,7 @@ private async Task Resubmit(PendingTransactionWithCh var result = await TransactionSubmitter.Submit( new SubmitContext( TransactionApi: coreApiProvider.TransactionsApi, + TargetNode: chosenNode.Name, NetworkName: _networkConfigurationProvider.GetNetworkName(), SubmissionTimeout: _mempoolOptionsMonitor.CurrentValue.ResubmissionNodeRequestTimeout, IsResubmission: true, diff --git a/src/RadixDlt.NetworkGateway.PostgresIntegration/Services/SubmissionTrackingService.cs b/src/RadixDlt.NetworkGateway.PostgresIntegration/Services/SubmissionTrackingService.cs index ae4654de4..9f176e97f 100644 --- a/src/RadixDlt.NetworkGateway.PostgresIntegration/Services/SubmissionTrackingService.cs +++ b/src/RadixDlt.NetworkGateway.PostgresIntegration/Services/SubmissionTrackingService.cs @@ -121,6 +121,7 @@ public async Task ObserveSubmissionToGatewayAndSubmitToNetwork await SubmitToNetworkAndUpdatePendingTransaction( new SubmitContext( TransactionApi: transactionApi, + TargetNode: nodeName, NetworkName: networkName, SubmissionTimeout: submissionTimeout, IsResubmission: false, diff --git a/src/RadixDlt.NetworkGateway.PrometheusIntegration/DataAggregatorMetricsObserver.cs b/src/RadixDlt.NetworkGateway.PrometheusIntegration/DataAggregatorMetricsObserver.cs index c1259f0ea..11d7f6798 100644 --- a/src/RadixDlt.NetworkGateway.PrometheusIntegration/DataAggregatorMetricsObserver.cs +++ b/src/RadixDlt.NetworkGateway.PrometheusIntegration/DataAggregatorMetricsObserver.cs @@ -448,7 +448,7 @@ ValueTask ITransactionSubmitterObserver.ObserveSubmitAttempt(SubmitContext conte throw new Exception("Aggregator is only supposed to handle resubmissions"); } - _transactionResubmissionAttemptCount.Inc(); + _transactionResubmissionAttemptCount.WithLabels(context.TargetNode).Inc(); return ValueTask.CompletedTask; } @@ -465,15 +465,15 @@ ValueTask ITransactionSubmitterObserver.ObserveSubmitResult(SubmitContext contex if (nodeSubmissionResult.IsSubmissionSuccess()) { - _transactionResubmissionSuccessCount.Inc(); + _transactionResubmissionSuccessCount.WithLabels(context.TargetNode).Inc(); } if (nodeSubmissionResult.IsSubmissionError()) { - _transactionResubmissionErrorCount.Inc(); + _transactionResubmissionErrorCount.WithLabels(context.TargetNode).Inc(); } - _transactionResubmissionResolutionByResultCount.WithLabels(nodeSubmissionResult.MetricLabel()).Inc(); + _transactionResubmissionResolutionByResultCount.WithLabels(nodeSubmissionResult.MetricLabel(), context.TargetNode).Inc(); return ValueTask.CompletedTask; } diff --git a/src/RadixDlt.NetworkGateway.PrometheusIntegration/GatewayApiMetricObserver.cs b/src/RadixDlt.NetworkGateway.PrometheusIntegration/GatewayApiMetricObserver.cs index 294f713c3..4d90ad115 100644 --- a/src/RadixDlt.NetworkGateway.PrometheusIntegration/GatewayApiMetricObserver.cs +++ b/src/RadixDlt.NetworkGateway.PrometheusIntegration/GatewayApiMetricObserver.cs @@ -62,7 +62,9 @@ * permissions under this License. */ +using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.Routing; using Prometheus; using RadixDlt.NetworkGateway.Abstractions.CoreCommunications; using RadixDlt.NetworkGateway.Abstractions.Extensions; @@ -116,44 +118,50 @@ internal class GatewayApiMetricObserver : private static readonly Counter _transactionSubmitRequestCount = Metrics .CreateCounter( "ng_construction_transaction_submission_request_count", - "Number of transaction submission requests" + "Number of transaction submission requests", + new CounterConfiguration { LabelNames = new[] { "target_node" } } ); private static readonly Counter _transactionSubmitSuccessCount = Metrics .CreateCounter( "ng_construction_transaction_submission_success_count", - "Number of transaction submission successes" + "Number of transaction submission successes", + new CounterConfiguration { LabelNames = new[] { "target_node" } } ); private static readonly Counter _transactionSubmitErrorCount = Metrics .CreateCounter( "ng_construction_transaction_submission_error_count", - "Number of transaction submission errors" + "Number of transaction submission errors", + new CounterConfiguration { LabelNames = new[] { "target_node" } } ); private static readonly Counter _transactionPreviewRequestCount = Metrics .CreateCounter( "ng_construction_transaction_preview_request_count", - "Number of transaction preview requests" + "Number of transaction preview requests", + new CounterConfiguration { LabelNames = new[] { "target_node" } } ); private static readonly Counter _transactionPreviewSuccessCount = Metrics .CreateCounter( "ng_construction_transaction_preview_success_count", - "Number of transaction preview successes" + "Number of transaction preview successes", + new CounterConfiguration { LabelNames = new[] { "target_node" } } ); private static readonly Counter _transactionPreviewErrorCount = Metrics .CreateCounter( "ng_construction_transaction_preview_error_count", - "Number of transaction preview errors" + "Number of transaction preview errors", + new CounterConfiguration { LabelNames = new[] { "target_node" } } ); private static readonly Counter _transactionSubmitResolutionByResultCount = Metrics .CreateCounter( "ng_construction_transaction_submission_resolution_count", "Number of various resolutions at transaction submission time", - new CounterConfiguration { LabelNames = new[] { "result" } } + new CounterConfiguration { LabelNames = new[] { "result", "target_node" } } ); private static readonly Gauge _ledgerTipRoundTimestampVsGatewayApiClockLagAtLastRequestSeconds = Metrics @@ -230,23 +238,23 @@ ValueTask ILedgerStateQuerierObserver.LedgerRoundTimestampClockSkew(TimeSpan dif return ValueTask.CompletedTask; } - ValueTask IPreviewServiceObserver.PreHandlePreviewRequest(GatewayModel.TransactionPreviewRequest request) + ValueTask IPreviewServiceObserver.PreHandlePreviewRequest(GatewayModel.TransactionPreviewRequest request, string targetNode) { - _transactionPreviewRequestCount.Inc(); + _transactionPreviewRequestCount.WithLabels(targetNode).Inc(); return ValueTask.CompletedTask; } - ValueTask IPreviewServiceObserver.PostHandlePreviewRequest(GatewayModel.TransactionPreviewRequest request, GatewayModel.TransactionPreviewResponse response) + ValueTask IPreviewServiceObserver.PostHandlePreviewRequest(GatewayModel.TransactionPreviewRequest request, string targetNode, GatewayModel.TransactionPreviewResponse response) { - _transactionPreviewSuccessCount.Inc(); + _transactionPreviewSuccessCount.WithLabels(targetNode).Inc(); return ValueTask.CompletedTask; } - ValueTask IPreviewServiceObserver.HandlePreviewRequestFailed(GatewayModel.TransactionPreviewRequest request, Exception exception) + ValueTask IPreviewServiceObserver.HandlePreviewRequestFailed(GatewayModel.TransactionPreviewRequest request, string targetNode, Exception exception) { - _transactionPreviewErrorCount.Inc(); + _transactionPreviewErrorCount.WithLabels(targetNode).Inc(); return ValueTask.CompletedTask; } @@ -291,7 +299,7 @@ public ValueTask ObserveSubmitAttempt(SubmitContext context) throw new Exception("The Gateway API is only supposed to handle initial transaction submissions"); } - _transactionSubmitRequestCount.Inc(); + _transactionSubmitRequestCount.WithLabels(context.TargetNode).Inc(); return ValueTask.CompletedTask; } @@ -308,15 +316,15 @@ public ValueTask ObserveSubmitResult(SubmitContext context, NodeSubmissionResult if (nodeSubmissionResult.IsSubmissionSuccess()) { - _transactionSubmitSuccessCount.Inc(); + _transactionSubmitSuccessCount.WithLabels(context.TargetNode).Inc(); } if (nodeSubmissionResult.IsSubmissionError()) { - _transactionSubmitErrorCount.Inc(); + _transactionSubmitErrorCount.WithLabels(context.TargetNode).Inc(); } - _transactionSubmitResolutionByResultCount.WithLabels(nodeSubmissionResult.MetricLabel()).Inc(); + _transactionSubmitResolutionByResultCount.WithLabels(nodeSubmissionResult.MetricLabel(), context.TargetNode).Inc(); return ValueTask.CompletedTask; } From fcfea49742a5dd7af015db57083d52dd76becec9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Pawelec?= Date: Thu, 5 Oct 2023 12:14:23 +0200 Subject: [PATCH 08/14] exception handling reworked. --- .../Controllers/StateEntityController.cs | 2 - .../StateKeyValueStoreController.cs | 2 - .../Controllers/StateNonFungibleController.cs | 2 - .../Controllers/StatisticsController.cs | 2 - .../Controllers/StatusController.cs | 2 - .../Controllers/StreamController.cs | 7 +- .../Controllers/TransactionController.cs | 2 - .../Controllers/ValidatorStateController.cs | 2 - .../ExceptionHandlingMiddleware.cs | 118 +++++++++++++----- .../ExceptionHandlingMiddleware/Extensions.cs | 24 +--- apps/GatewayApi/GatewayApiStartup.cs | 4 + .../SlowRequestLogging/Extensions.cs | 1 + .../SlowRequestLoggingMiddleware.cs | 1 + .../AspNetCore/ExceptionFilter.cs | 99 --------------- .../AspNetCore/RequestAbortedFeature.cs | 18 +++ .../AspNetCore/RequestTimeoutFeature.cs | 24 ++++ .../AspNetCore/RequestTimeoutMiddleware.cs | 8 +- .../Exceptions/BadRequestException.cs | 9 ++ .../ClientClosedConnectionException.cs | 9 ++ .../Exceptions/InternalServerException.cs | 11 ++ .../Exceptions/InvalidRequestException.cs | 10 -- .../Exceptions/KnownGatewayErrorException.cs | 4 +- .../ServiceCollectionExtensions.cs | 5 - .../Services/IExceptionObserver.cs | 7 +- .../Services/ValidationErrorHandler.cs | 40 ++++-- .../GatewayApiMetricObserver.cs | 28 ++++- 26 files changed, 239 insertions(+), 202 deletions(-) rename src/RadixDlt.NetworkGateway.GatewayApi/Services/ExceptionHandler.cs => apps/GatewayApi/ExceptionHandlingMiddleware/ExceptionHandlingMiddleware.cs (62%) rename src/RadixDlt.NetworkGateway.GatewayApi/AspNetCore/InvalidModelStateFilter.cs => apps/GatewayApi/ExceptionHandlingMiddleware/Extensions.cs (84%) delete mode 100644 src/RadixDlt.NetworkGateway.GatewayApi/AspNetCore/ExceptionFilter.cs create mode 100644 src/RadixDlt.NetworkGateway.GatewayApi/AspNetCore/RequestAbortedFeature.cs create mode 100644 src/RadixDlt.NetworkGateway.GatewayApi/AspNetCore/RequestTimeoutFeature.cs create mode 100644 src/RadixDlt.NetworkGateway.GatewayApi/Exceptions/BadRequestException.cs create mode 100644 src/RadixDlt.NetworkGateway.GatewayApi/Exceptions/ClientClosedConnectionException.cs diff --git a/apps/GatewayApi/Controllers/StateEntityController.cs b/apps/GatewayApi/Controllers/StateEntityController.cs index 8b62ff39b..3cf3c8e3c 100644 --- a/apps/GatewayApi/Controllers/StateEntityController.cs +++ b/apps/GatewayApi/Controllers/StateEntityController.cs @@ -73,8 +73,6 @@ namespace GatewayApi.Controllers; [ApiController] [Route("state/entity")] -[ServiceFilter(typeof(ExceptionFilter))] -[ServiceFilter(typeof(InvalidModelStateFilter))] public class StateController : ControllerBase { private readonly IEntityHandler _entityHandler; diff --git a/apps/GatewayApi/Controllers/StateKeyValueStoreController.cs b/apps/GatewayApi/Controllers/StateKeyValueStoreController.cs index 825c525d4..afd689295 100644 --- a/apps/GatewayApi/Controllers/StateKeyValueStoreController.cs +++ b/apps/GatewayApi/Controllers/StateKeyValueStoreController.cs @@ -73,8 +73,6 @@ namespace GatewayApi.Controllers; [ApiController] [Route("state/key-value-store")] -[ServiceFilter(typeof(ExceptionFilter))] -[ServiceFilter(typeof(InvalidModelStateFilter))] public class StateKeyValueStoreController : ControllerBase { private readonly IKeyValueStoreHandler _keyValueStoreHandler; diff --git a/apps/GatewayApi/Controllers/StateNonFungibleController.cs b/apps/GatewayApi/Controllers/StateNonFungibleController.cs index 39723d723..973c77cd5 100644 --- a/apps/GatewayApi/Controllers/StateNonFungibleController.cs +++ b/apps/GatewayApi/Controllers/StateNonFungibleController.cs @@ -73,8 +73,6 @@ namespace GatewayApi.Controllers; [ApiController] [Route("state/non-fungible")] -[ServiceFilter(typeof(ExceptionFilter))] -[ServiceFilter(typeof(InvalidModelStateFilter))] public class StateNonFungibleController : ControllerBase { private readonly INonFungibleHandler _nonFungibleHandler; diff --git a/apps/GatewayApi/Controllers/StatisticsController.cs b/apps/GatewayApi/Controllers/StatisticsController.cs index f3a31a89a..ded0cc77d 100644 --- a/apps/GatewayApi/Controllers/StatisticsController.cs +++ b/apps/GatewayApi/Controllers/StatisticsController.cs @@ -73,8 +73,6 @@ namespace GatewayApi.Controllers; [ApiController] [Route("statistics")] -[ServiceFilter(typeof(ExceptionFilter))] -[ServiceFilter(typeof(InvalidModelStateFilter))] public class StatisticsController : ControllerBase { private readonly IValidatorHandler _validatorHandler; diff --git a/apps/GatewayApi/Controllers/StatusController.cs b/apps/GatewayApi/Controllers/StatusController.cs index 206045bfd..b440ee71b 100644 --- a/apps/GatewayApi/Controllers/StatusController.cs +++ b/apps/GatewayApi/Controllers/StatusController.cs @@ -74,8 +74,6 @@ namespace GatewayApi.Controllers; [ApiController] [Route("status")] -[ServiceFilter(typeof(ExceptionFilter))] -[ServiceFilter(typeof(InvalidModelStateFilter))] public sealed class StatusController : ControllerBase { private readonly IStatusHandler _statusHandler; diff --git a/apps/GatewayApi/Controllers/StreamController.cs b/apps/GatewayApi/Controllers/StreamController.cs index a9f7f293c..5e845580f 100644 --- a/apps/GatewayApi/Controllers/StreamController.cs +++ b/apps/GatewayApi/Controllers/StreamController.cs @@ -64,17 +64,20 @@ using Microsoft.AspNetCore.Mvc; using RadixDlt.NetworkGateway.GatewayApi.AspNetCore; +using RadixDlt.NetworkGateway.GatewayApi.Exceptions; using RadixDlt.NetworkGateway.GatewayApi.Handlers; +using System; +using System.Net.Http; using System.Threading; using System.Threading.Tasks; using GatewayModel = RadixDlt.NetworkGateway.GatewayApiSdk.Model; +// ReSharper disable HeuristicUnreachableCode +#pragma warning disable CS0162 // Unreachable code detected namespace GatewayApi.Controllers; [ApiController] [Route("stream")] -[ServiceFilter(typeof(ExceptionFilter))] -[ServiceFilter(typeof(InvalidModelStateFilter))] public sealed class StreamController : ControllerBase { private readonly ITransactionHandler _transactionHandler; diff --git a/apps/GatewayApi/Controllers/TransactionController.cs b/apps/GatewayApi/Controllers/TransactionController.cs index bd3081b1d..48bc0bac3 100644 --- a/apps/GatewayApi/Controllers/TransactionController.cs +++ b/apps/GatewayApi/Controllers/TransactionController.cs @@ -73,8 +73,6 @@ namespace GatewayApi.Controllers; [ApiController] [Route("transaction")] -[ServiceFilter(typeof(ExceptionFilter))] -[ServiceFilter(typeof(InvalidModelStateFilter))] public sealed class TransactionController : ControllerBase { private readonly ITransactionHandler _transactionHandler; diff --git a/apps/GatewayApi/Controllers/ValidatorStateController.cs b/apps/GatewayApi/Controllers/ValidatorStateController.cs index 8530e8417..6669ed71f 100644 --- a/apps/GatewayApi/Controllers/ValidatorStateController.cs +++ b/apps/GatewayApi/Controllers/ValidatorStateController.cs @@ -73,8 +73,6 @@ namespace GatewayApi.Controllers; [ApiController] [Route("state/validators")] -[ServiceFilter(typeof(ExceptionFilter))] -[ServiceFilter(typeof(InvalidModelStateFilter))] public class ValidatorController : ControllerBase { private readonly IValidatorStateHandler _validatorStateHandler; diff --git a/src/RadixDlt.NetworkGateway.GatewayApi/Services/ExceptionHandler.cs b/apps/GatewayApi/ExceptionHandlingMiddleware/ExceptionHandlingMiddleware.cs similarity index 62% rename from src/RadixDlt.NetworkGateway.GatewayApi/Services/ExceptionHandler.cs rename to apps/GatewayApi/ExceptionHandlingMiddleware/ExceptionHandlingMiddleware.cs index 72140b15c..e051e8be3 100644 --- a/src/RadixDlt.NetworkGateway.GatewayApi/Services/ExceptionHandler.cs +++ b/apps/GatewayApi/ExceptionHandlingMiddleware/ExceptionHandlingMiddleware.cs @@ -62,60 +62,106 @@ * permissions under this License. */ -using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.Diagnostics; +using Microsoft.AspNetCore.Http; using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Logging; +using Newtonsoft.Json; using RadixDlt.NetworkGateway.Abstractions.Extensions; +using RadixDlt.NetworkGateway.GatewayApi.AspNetCore; using RadixDlt.NetworkGateway.GatewayApi.Exceptions; +using RadixDlt.NetworkGateway.GatewayApi.Services; using System; using System.Collections.Generic; -using System.Net; +using System.Diagnostics; using System.Net.Http; +using System.Threading.Tasks; using CoreClient = RadixDlt.CoreApiSdk.Client; using CoreModel = RadixDlt.CoreApiSdk.Model; using GatewayModel = RadixDlt.NetworkGateway.GatewayApiSdk.Model; -namespace RadixDlt.NetworkGateway.GatewayApi.Services; +namespace GatewayApi.ExceptionHandlingMiddleware; -public interface IExceptionHandler +internal sealed class ExceptionHandlingMiddleware { - ActionResult CreateAndLogApiResultFromException(ActionContext actionContext, Exception exception, string traceId); -} - -internal class ExceptionHandler : IExceptionHandler -{ - private readonly ILogger _logger; - private readonly LogLevel _knownGatewayErrorLogLevel; + private readonly RequestDelegate _next; + private readonly ILogger _logger; private readonly IEnumerable _observers; + private readonly LogLevel _knownGatewayErrorLogLevel; - public ExceptionHandler(ILogger logger, IHostEnvironment env, IEnumerable observers) + public ExceptionHandlingMiddleware(RequestDelegate next, IHostEnvironment env, ILogger logger, + IEnumerable observers) { + _next = next; _logger = logger; _observers = observers; _knownGatewayErrorLogLevel = env.IsDevelopment() ? LogLevel.Information : LogLevel.Debug; } - public ActionResult CreateAndLogApiResultFromException(ActionContext actionContext, Exception exception, string traceId) + public async Task InvokeAsync(HttpContext context) { - var gatewayErrorException = LogAndConvertToKnownGatewayErrorException(exception, traceId); + try + { + await _next(context); + } + catch (Exception exception) + { + var traceId = Activity.Current?.Id ?? context.TraceIdentifier; - _observers.ForEach(x => x.OnException(actionContext, exception, gatewayErrorException)); + var gatewayErrorException = HandleException(context, exception, traceId); - return new JsonResult(new GatewayModel.ErrorResponse( - code: gatewayErrorException.StatusCode, - message: gatewayErrorException.UserFacingMessage, - details: gatewayErrorException.GatewayError, - traceId: traceId - )) - { - StatusCode = gatewayErrorException.StatusCode, - }; + var errorResponse = new GatewayModel.ErrorResponse( + code: gatewayErrorException.StatusCode, + message: gatewayErrorException.UserFacingMessage, + details: gatewayErrorException.GatewayError, + traceId: traceId + ); + + _observers.ForEach(x => x.OnException(context, exception, gatewayErrorException)); + + if (context.Response.HasStarted) + { + _logger.LogWarning("Response already started. Can't modify status code and response body"); + } + else + { + var jsonErrorResponse = JsonConvert.SerializeObject(errorResponse); + await WriteResponseAsync(context, jsonErrorResponse, gatewayErrorException.StatusCode); + } + } } - private KnownGatewayErrorException LogAndConvertToKnownGatewayErrorException(Exception exception, string traceId) + private KnownGatewayErrorException HandleException(HttpContext httpContext, Exception exception, string traceId) { switch (exception) { + case BadHttpRequestException badHttpRequestException : + _logger.LogInformation(exception, "Bad http request. [RequestTrace={TraceId}]", traceId); + return new BadRequestException("Bad http request", badHttpRequestException.Message); + + case OperationCanceledException operationCanceledException: + var requestTimeoutFeature = httpContext.Features.Get(); + var requestAbortedFeature = httpContext.Features.Get(); + + if (requestTimeoutFeature?.CancellationToken.IsCancellationRequested == true) + { + _logger.LogError(exception, "Request timed out after={Timeout} seconds, [RequestTrace={TraceId}]", requestTimeoutFeature.TimeoutAfter.TotalSeconds, traceId); + return InternalServerException.OfRequestTimeoutException(exception, traceId); + } + + if (requestAbortedFeature?.CancellationToken.IsCancellationRequested == true) + { + _logger.LogWarning(exception, "Request aborted by user. [RequestTrace={TraceId}]", traceId); + return new ClientClosedConnectionException("Client closed connection", "Client closed connection"); + } + + _logger.LogWarning( + exception, + "Unknown exception [RequestTrace={TraceId}]", + traceId + ); + + return InternalServerException.OfHiddenException(exception, traceId); case KnownGatewayErrorException knownGatewayErrorException: _logger.Log( _knownGatewayErrorLogLevel, @@ -127,8 +173,7 @@ private KnownGatewayErrorException LogAndConvertToKnownGatewayErrorException(Exc // HttpRequestException is returned from the Core API if we can't connect case HttpRequestException: - _logger.Log( - LogLevel.Information, + _logger.LogInformation( exception, "Error relaying request to upstream server [RequestTrace={TraceId}]", traceId @@ -137,20 +182,18 @@ private KnownGatewayErrorException LogAndConvertToKnownGatewayErrorException(Exc // CoreClient.ApiException is returned if we get a 500 from upstream but couldn't extract a WrappedCoreApiException case CoreClient.ApiException coreApiException: - _logger.Log( - LogLevel.Warning, + _logger.LogWarning( exception, "Unhandled error response from upstream core API, which didn't parse correctly into a known ErrorType we could wrap or unexpected ErrorType [RequestTrace={TraceId}]", traceId ); return InternalServerException.OfUnhandledCoreApiException( - coreApiException.ErrorContent.ToString() ?? string.Empty, + coreApiException.ErrorContent?.ToString() ?? string.Empty, traceId ); case InvalidCoreApiResponseException invalidCoreApiResponseException: - _logger.Log( - LogLevel.Warning, + _logger.LogWarning( exception, "Invalid Core API response [RequestTrace={TraceId}]", traceId @@ -161,8 +204,7 @@ private KnownGatewayErrorException LogAndConvertToKnownGatewayErrorException(Exc ); default: - _logger.Log( - LogLevel.Warning, + _logger.LogWarning( exception, "Unknown exception [RequestTrace={TraceId}]", traceId @@ -170,4 +212,12 @@ private KnownGatewayErrorException LogAndConvertToKnownGatewayErrorException(Exc return InternalServerException.OfHiddenException(exception, traceId); } } + + private async Task WriteResponseAsync(HttpContext context, string jsonResponse, int statusCode) + { + var response = context.Response; + response.ContentType = "application/json"; + response.StatusCode = statusCode; + await response.WriteAsync(jsonResponse); + } } diff --git a/src/RadixDlt.NetworkGateway.GatewayApi/AspNetCore/InvalidModelStateFilter.cs b/apps/GatewayApi/ExceptionHandlingMiddleware/Extensions.cs similarity index 84% rename from src/RadixDlt.NetworkGateway.GatewayApi/AspNetCore/InvalidModelStateFilter.cs rename to apps/GatewayApi/ExceptionHandlingMiddleware/Extensions.cs index 73fc0ffae..0a12ac6a4 100644 --- a/src/RadixDlt.NetworkGateway.GatewayApi/AspNetCore/InvalidModelStateFilter.cs +++ b/apps/GatewayApi/ExceptionHandlingMiddleware/Extensions.cs @@ -62,28 +62,14 @@ * permissions under this License. */ -using Microsoft.AspNetCore.Mvc.Filters; -using Microsoft.Extensions.DependencyInjection; -using RadixDlt.NetworkGateway.GatewayApi.Services; +using Microsoft.AspNetCore.Builder; -namespace RadixDlt.NetworkGateway.GatewayApi.AspNetCore; +namespace GatewayApi.ExceptionHandlingMiddleware; -public sealed class InvalidModelStateFilter : IActionFilter, IOrderedFilter +public static class Extensions { - public int Order => -3000; - - public void OnActionExecuting(ActionExecutingContext context) - { - if (context.Result == null && !context.ModelState.IsValid) - { - var validationErrorHandler = context.HttpContext.RequestServices.GetRequiredService(); - - context.Result = validationErrorHandler.GetClientError(context); - } - } - - public void OnActionExecuted(ActionExecutedContext context) + public static IApplicationBuilder UseCustomExceptionHandler(this IApplicationBuilder app) { - // no-op + return app.UseMiddleware(); } } diff --git a/apps/GatewayApi/GatewayApiStartup.cs b/apps/GatewayApi/GatewayApiStartup.cs index 34d53f2bb..7450ae60c 100644 --- a/apps/GatewayApi/GatewayApiStartup.cs +++ b/apps/GatewayApi/GatewayApiStartup.cs @@ -62,6 +62,7 @@ * permissions under this License. */ +using GatewayApi.ExceptionHandlingMiddleware; using GatewayApi.SlowRequestLogging; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Mvc; @@ -75,6 +76,8 @@ using RadixDlt.NetworkGateway.PostgresIntegration; using RadixDlt.NetworkGateway.PrometheusIntegration; using System; +using System.Threading; +using System.Threading.Tasks; namespace GatewayApi; @@ -126,6 +129,7 @@ public void ConfigureServices(IServiceCollection services) public void Configure(IApplicationBuilder application, IConfiguration configuration, ILogger logger) { application + .UseCustomExceptionHandler() .UseSlowRequestLogging() .UseRequestTimeout() .UseCors() diff --git a/apps/GatewayApi/SlowRequestLogging/Extensions.cs b/apps/GatewayApi/SlowRequestLogging/Extensions.cs index 0dcfba754..1ae702223 100644 --- a/apps/GatewayApi/SlowRequestLogging/Extensions.cs +++ b/apps/GatewayApi/SlowRequestLogging/Extensions.cs @@ -65,6 +65,7 @@ using Microsoft.AspNetCore.Builder; using Microsoft.Extensions.DependencyInjection; using System; +using System.Threading; namespace GatewayApi.SlowRequestLogging; diff --git a/apps/GatewayApi/SlowRequestLogging/SlowRequestLoggingMiddleware.cs b/apps/GatewayApi/SlowRequestLogging/SlowRequestLoggingMiddleware.cs index 31383bf27..4c0da79d9 100644 --- a/apps/GatewayApi/SlowRequestLogging/SlowRequestLoggingMiddleware.cs +++ b/apps/GatewayApi/SlowRequestLogging/SlowRequestLoggingMiddleware.cs @@ -66,6 +66,7 @@ using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; using System.Diagnostics; +using System.Threading; using System.Threading.Tasks; namespace GatewayApi.SlowRequestLogging; diff --git a/src/RadixDlt.NetworkGateway.GatewayApi/AspNetCore/ExceptionFilter.cs b/src/RadixDlt.NetworkGateway.GatewayApi/AspNetCore/ExceptionFilter.cs deleted file mode 100644 index 9caa2685e..000000000 --- a/src/RadixDlt.NetworkGateway.GatewayApi/AspNetCore/ExceptionFilter.cs +++ /dev/null @@ -1,99 +0,0 @@ -/* Copyright 2021 Radix Publishing Ltd incorporated in Jersey (Channel Islands). - * - * Licensed under the Radix License, Version 1.0 (the "License"); you may not use this - * file except in compliance with the License. You may obtain a copy of the License at: - * - * radixfoundation.org/licenses/LICENSE-v1 - * - * The Licensor hereby grants permission for the Canonical version of the Work to be - * published, distributed and used under or by reference to the Licensor’s trademark - * Radix ® and use of any unregistered trade names, logos or get-up. - * - * The Licensor provides the Work (and each Contributor provides its Contributions) on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, - * including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, - * MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. - * - * Whilst the Work is capable of being deployed, used and adopted (instantiated) to create - * a distributed ledger it is your responsibility to test and validate the code, together - * with all logic and performance of that code under all foreseeable scenarios. - * - * The Licensor does not make or purport to make and hereby excludes liability for all - * and any representation, warranty or undertaking in any form whatsoever, whether express - * or implied, to any entity or person, including any representation, warranty or - * undertaking, as to the functionality security use, value or other characteristics of - * any distributed ledger nor in respect the functioning or value of any tokens which may - * be created stored or transferred using the Work. The Licensor does not warrant that the - * Work or any use of the Work complies with any law or regulation in any territory where - * it may be implemented or used or that it will be appropriate for any specific purpose. - * - * Neither the licensor nor any current or former employees, officers, directors, partners, - * trustees, representatives, agents, advisors, contractors, or volunteers of the Licensor - * shall be liable for any direct or indirect, special, incidental, consequential or other - * losses of any kind, in tort, contract or otherwise (including but not limited to loss - * of revenue, income or profits, or loss of use or data, or loss of reputation, or loss - * of any economic or other opportunity of whatsoever nature or howsoever arising), arising - * out of or in connection with (without limitation of any use, misuse, of any ledger system - * or use made or its functionality or any performance or operation of any code or protocol - * caused by bugs or programming or logic errors or otherwise); - * - * A. any offer, purchase, holding, use, sale, exchange or transmission of any - * cryptographic keys, tokens or assets created, exchanged, stored or arising from any - * interaction with the Work; - * - * B. any failure in a transmission or loss of any token or assets keys or other digital - * artefacts due to errors in transmission; - * - * C. bugs, hacks, logic errors or faults in the Work or any communication; - * - * D. system software or apparatus including but not limited to losses caused by errors - * in holding or transmitting tokens by any third-party; - * - * E. breaches or failure of security including hacker attacks, loss or disclosure of - * password, loss of private key, unauthorised use or misuse of such passwords or keys; - * - * F. any losses including loss of anticipated savings or other benefits resulting from - * use of the Work or any changes to the Work (however implemented). - * - * You are solely responsible for; testing, validating and evaluation of all operation - * logic, functionality, security and appropriateness of using the Work for any commercial - * or non-commercial purpose and for any reproduction or redistribution by You of the - * Work. You assume all risks associated with Your use of the Work and the exercise of - * permissions under this License. - */ - -using Microsoft.AspNetCore.Mvc.Filters; -using RadixDlt.NetworkGateway.GatewayApi.Services; -using System.Diagnostics; - -namespace RadixDlt.NetworkGateway.GatewayApi.AspNetCore; - -public sealed class ExceptionFilter : IActionFilter, IOrderedFilter -{ - private readonly IExceptionHandler _exceptionHandler; - - public ExceptionFilter(IExceptionHandler exceptionHandler) - { - _exceptionHandler = exceptionHandler; - } - - public int Order => int.MaxValue - 10; - - public void OnActionExecuting(ActionExecutingContext context) - { - } - - public void OnActionExecuted(ActionExecutedContext context) - { - if (context.Exception == null) - { - return; - } - - // See https://github.com/dotnet/aspnetcore/blob/ae1a6cbe225b99c0bf38b7e31bf60cb653b73a52/src/Mvc/Mvc.Core/src/Infrastructure/DefaultProblemDetailsFactory.cs#L92 - var traceId = Activity.Current?.Id ?? context.HttpContext.TraceIdentifier; - - context.Result = _exceptionHandler.CreateAndLogApiResultFromException(context, context.Exception!, traceId); - context.ExceptionHandled = true; - } -} diff --git a/src/RadixDlt.NetworkGateway.GatewayApi/AspNetCore/RequestAbortedFeature.cs b/src/RadixDlt.NetworkGateway.GatewayApi/AspNetCore/RequestAbortedFeature.cs new file mode 100644 index 000000000..c530d7872 --- /dev/null +++ b/src/RadixDlt.NetworkGateway.GatewayApi/AspNetCore/RequestAbortedFeature.cs @@ -0,0 +1,18 @@ +using System.Threading; + +namespace RadixDlt.NetworkGateway.GatewayApi.AspNetCore; + +public interface IRequestAbortedFeature +{ + public CancellationToken CancellationToken { get; } +} + +public class RequestAbortedFeature : IRequestAbortedFeature +{ + public RequestAbortedFeature(CancellationToken cancellationToken) + { + CancellationToken = cancellationToken; + } + + public CancellationToken CancellationToken { get; } +} diff --git a/src/RadixDlt.NetworkGateway.GatewayApi/AspNetCore/RequestTimeoutFeature.cs b/src/RadixDlt.NetworkGateway.GatewayApi/AspNetCore/RequestTimeoutFeature.cs new file mode 100644 index 000000000..d5571eeb1 --- /dev/null +++ b/src/RadixDlt.NetworkGateway.GatewayApi/AspNetCore/RequestTimeoutFeature.cs @@ -0,0 +1,24 @@ +using System; +using System.Threading; + +namespace RadixDlt.NetworkGateway.GatewayApi.AspNetCore; + +public interface IRequestTimeoutFeature +{ + public CancellationToken CancellationToken { get; } + + public TimeSpan TimeoutAfter { get; } +} + +public class RequestTimeoutFeature : IRequestTimeoutFeature +{ + public RequestTimeoutFeature(CancellationToken cancellationToken, TimeSpan timeoutAfter) + { + CancellationToken = cancellationToken; + TimeoutAfter = timeoutAfter; + } + + public CancellationToken CancellationToken { get; } + + public TimeSpan TimeoutAfter { get; } +} diff --git a/src/RadixDlt.NetworkGateway.GatewayApi/AspNetCore/RequestTimeoutMiddleware.cs b/src/RadixDlt.NetworkGateway.GatewayApi/AspNetCore/RequestTimeoutMiddleware.cs index 6b76abbfe..8573286cc 100644 --- a/src/RadixDlt.NetworkGateway.GatewayApi/AspNetCore/RequestTimeoutMiddleware.cs +++ b/src/RadixDlt.NetworkGateway.GatewayApi/AspNetCore/RequestTimeoutMiddleware.cs @@ -82,11 +82,13 @@ public RequestTimeoutMiddleware(IOptions endpointOptions) public async Task InvokeAsync(HttpContext context, RequestDelegate next) { - using var timeoutSource = CancellationTokenSource.CreateLinkedTokenSource(context.RequestAborted); - + using var timeoutSource = new CancellationTokenSource(); timeoutSource.CancelAfter(_timeout); + context.Features.Set(new RequestAbortedFeature(context.RequestAborted)); + context.Features.Set(new RequestTimeoutFeature(timeoutSource.Token, _timeout)); - context.RequestAborted = timeoutSource.Token; + using var linkedSource = CancellationTokenSource.CreateLinkedTokenSource(context.RequestAborted, timeoutSource.Token); + context.RequestAborted = linkedSource.Token; await next(context); } diff --git a/src/RadixDlt.NetworkGateway.GatewayApi/Exceptions/BadRequestException.cs b/src/RadixDlt.NetworkGateway.GatewayApi/Exceptions/BadRequestException.cs new file mode 100644 index 000000000..3ff1e6196 --- /dev/null +++ b/src/RadixDlt.NetworkGateway.GatewayApi/Exceptions/BadRequestException.cs @@ -0,0 +1,9 @@ +namespace RadixDlt.NetworkGateway.GatewayApi.Exceptions; + +public sealed class BadRequestException : KnownGatewayErrorException +{ + public BadRequestException(string userFacingMessage, string internalMessage) + : base(400, null, userFacingMessage, internalMessage) + { + } +} diff --git a/src/RadixDlt.NetworkGateway.GatewayApi/Exceptions/ClientClosedConnectionException.cs b/src/RadixDlt.NetworkGateway.GatewayApi/Exceptions/ClientClosedConnectionException.cs new file mode 100644 index 000000000..ade381642 --- /dev/null +++ b/src/RadixDlt.NetworkGateway.GatewayApi/Exceptions/ClientClosedConnectionException.cs @@ -0,0 +1,9 @@ +namespace RadixDlt.NetworkGateway.GatewayApi.Exceptions; + +public sealed class ClientClosedConnectionException : KnownGatewayErrorException +{ + public ClientClosedConnectionException(string userFacingMessage, string internalMessage) + : base(499, null, userFacingMessage, internalMessage) + { + } +} diff --git a/src/RadixDlt.NetworkGateway.GatewayApi/Exceptions/InternalServerException.cs b/src/RadixDlt.NetworkGateway.GatewayApi/Exceptions/InternalServerException.cs index da9625bf1..cfa95474d 100644 --- a/src/RadixDlt.NetworkGateway.GatewayApi/Exceptions/InternalServerException.cs +++ b/src/RadixDlt.NetworkGateway.GatewayApi/Exceptions/InternalServerException.cs @@ -84,6 +84,17 @@ public static InternalServerException OfShareableException(Exception exception, ); } + public static InternalServerException OfRequestTimeoutException(Exception exception, string traceId) + { + var message = $"Request timed out. If reporting this issue, please include TraceId={traceId}"; + + return new InternalServerException( + new GatewayModel.InternalServerError(exception.GetType().Name, exception.Message), + message, + exception.Message + ); + } + public static InternalServerException OfHiddenException(Exception exception, string traceId) { var message = $"An unexpected error occurred handling the request. If reporting this issue, please include TraceId={traceId}"; diff --git a/src/RadixDlt.NetworkGateway.GatewayApi/Exceptions/InvalidRequestException.cs b/src/RadixDlt.NetworkGateway.GatewayApi/Exceptions/InvalidRequestException.cs index 45f846276..2b775a049 100644 --- a/src/RadixDlt.NetworkGateway.GatewayApi/Exceptions/InvalidRequestException.cs +++ b/src/RadixDlt.NetworkGateway.GatewayApi/Exceptions/InvalidRequestException.cs @@ -74,16 +74,6 @@ private InvalidRequestException(GatewayModel.InvalidRequestError gatewayError, s { } - public static InvalidRequestException FromValidationErrors(List validationErrors) - { - return new InvalidRequestException( - new GatewayModel.InvalidRequestError( - validationErrors - ), - "One or more validation errors occurred" - ); - } - public static InvalidRequestException FromOtherError(string error) { return new InvalidRequestException( diff --git a/src/RadixDlt.NetworkGateway.GatewayApi/Exceptions/KnownGatewayErrorException.cs b/src/RadixDlt.NetworkGateway.GatewayApi/Exceptions/KnownGatewayErrorException.cs index 7ded6e845..fb9ee1e9e 100644 --- a/src/RadixDlt.NetworkGateway.GatewayApi/Exceptions/KnownGatewayErrorException.cs +++ b/src/RadixDlt.NetworkGateway.GatewayApi/Exceptions/KnownGatewayErrorException.cs @@ -72,13 +72,13 @@ public abstract class KnownGatewayErrorException : Exception { public int StatusCode { get; } - public GatewayModel.GatewayError GatewayError { get; } + public GatewayModel.GatewayError? GatewayError { get; } public string? UserFacingMessage { get; } public string? InternalMessage { get; } - public KnownGatewayErrorException(int statusCode, GatewayModel.GatewayError gatewayError, string userFacingMessage, string internalMessage) + public KnownGatewayErrorException(int statusCode, GatewayModel.GatewayError? gatewayError, string userFacingMessage, string internalMessage) : base($"{userFacingMessage} ({internalMessage})") { StatusCode = statusCode; diff --git a/src/RadixDlt.NetworkGateway.GatewayApi/ServiceCollectionExtensions.cs b/src/RadixDlt.NetworkGateway.GatewayApi/ServiceCollectionExtensions.cs index 8aa2dcb2e..584082c3e 100644 --- a/src/RadixDlt.NetworkGateway.GatewayApi/ServiceCollectionExtensions.cs +++ b/src/RadixDlt.NetworkGateway.GatewayApi/ServiceCollectionExtensions.cs @@ -131,7 +131,6 @@ private static void AddSingletonServices(IServiceCollection services) // semi-regularly services.TryAddSingleton(); services.TryAddSingleton(x => x.GetRequiredService()); - services.TryAddSingleton(); services.TryAddSingleton(); services.TryAddSingleton(); services.TryAddSingleton(); @@ -139,10 +138,6 @@ private static void AddSingletonServices(IServiceCollection services) private static void AddRequestServices(IServiceCollection services) { - services - .AddScoped() - .AddScoped(); - services.TryAddScoped(); services.TryAddScoped(); services.TryAddScoped(); diff --git a/src/RadixDlt.NetworkGateway.GatewayApi/Services/IExceptionObserver.cs b/src/RadixDlt.NetworkGateway.GatewayApi/Services/IExceptionObserver.cs index b873e3ea9..991243f45 100644 --- a/src/RadixDlt.NetworkGateway.GatewayApi/Services/IExceptionObserver.cs +++ b/src/RadixDlt.NetworkGateway.GatewayApi/Services/IExceptionObserver.cs @@ -62,13 +62,16 @@ * permissions under this License. */ -using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.Http; using RadixDlt.NetworkGateway.GatewayApi.Exceptions; using System; +using GatewayModel = RadixDlt.NetworkGateway.GatewayApiSdk.Model; namespace RadixDlt.NetworkGateway.GatewayApi.Services; public interface IExceptionObserver { - void OnException(ActionContext actionContext, Exception exception, KnownGatewayErrorException gatewayErrorException); + void OnException(HttpContext httpContext, Exception exception, KnownGatewayErrorException gatewayErrorException); + + void OnValidationError(HttpContext httpContext, GatewayModel.GatewayError gatewayError, int statusCode); } diff --git a/src/RadixDlt.NetworkGateway.GatewayApi/Services/ValidationErrorHandler.cs b/src/RadixDlt.NetworkGateway.GatewayApi/Services/ValidationErrorHandler.cs index d743957e1..b19c7d585 100644 --- a/src/RadixDlt.NetworkGateway.GatewayApi/Services/ValidationErrorHandler.cs +++ b/src/RadixDlt.NetworkGateway.GatewayApi/Services/ValidationErrorHandler.cs @@ -62,11 +62,18 @@ * permissions under this License. */ +using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; +using Microsoft.Extensions.Hosting; +using Microsoft.Extensions.Logging; +using Newtonsoft.Json; +using RadixDlt.NetworkGateway.Abstractions.Extensions; using RadixDlt.NetworkGateway.GatewayApi.Exceptions; +using System; using System.Collections.Generic; using System.Diagnostics; using System.Linq; +using System.Net; using GatewayModel = RadixDlt.NetworkGateway.GatewayApiSdk.Model; namespace RadixDlt.NetworkGateway.GatewayApi.Services; @@ -78,16 +85,20 @@ public interface IValidationErrorHandler internal class ValidationErrorHandler : IValidationErrorHandler { - private readonly IExceptionHandler _exceptionHandler; + private readonly ILogger _logger; + private readonly IEnumerable _observers; + private readonly LogLevel _knownGatewayErrorLogLevel; - public ValidationErrorHandler(IExceptionHandler exceptionHandler) + public ValidationErrorHandler(IHostEnvironment env, ILogger logger, IEnumerable observers) { - _exceptionHandler = exceptionHandler; + _logger = logger; + _observers = observers; + _knownGatewayErrorLogLevel = env.IsDevelopment() ? LogLevel.Information : LogLevel.Debug; } public IActionResult GetClientError(ActionContext actionContext) { - var errors = new List(); + var validationErrors = new List(); foreach (var (path, modelValue) in actionContext.ModelState) { var errorMessagesToShow = modelValue @@ -98,15 +109,28 @@ public IActionResult GetClientError(ActionContext actionContext) if (errorMessagesToShow.Count > 0) { - errors.Add(new GatewayModel.ValidationErrorsAtPath(path, errorMessagesToShow)); + validationErrors.Add(new GatewayModel.ValidationErrorsAtPath(path, errorMessagesToShow)); } } - var invalidRequestError = InvalidRequestException.FromValidationErrors(errors); - // See https://github.com/dotnet/aspnetcore/blob/ae1a6cbe225b99c0bf38b7e31bf60cb653b73a52/src/Mvc/Mvc.Core/src/Infrastructure/DefaultProblemDetailsFactory.cs#L92 var traceId = Activity.Current?.Id ?? actionContext.HttpContext.TraceIdentifier; - return _exceptionHandler.CreateAndLogApiResultFromException(actionContext, invalidRequestError, traceId); + const string UserFacingMessage = "One or more validation errors occurred"; + const int StatusCode = (int)HttpStatusCode.BadRequest; + var gatewayError = new GatewayModel.InvalidRequestError(validationErrors); + + _logger.Log(_knownGatewayErrorLogLevel, "Validation error occured. Error: {error}, [RequestTrace={TraceId}]", JsonConvert.SerializeObject(validationErrors), traceId); + + var errorResponse = new GatewayModel.ErrorResponse( + code: StatusCode, + message: UserFacingMessage, + details: gatewayError, + traceId: traceId + ); + + _observers.ForEach(x => x.OnValidationError(actionContext.HttpContext, gatewayError, StatusCode)); + + return new JsonResult(errorResponse); } } diff --git a/src/RadixDlt.NetworkGateway.PrometheusIntegration/GatewayApiMetricObserver.cs b/src/RadixDlt.NetworkGateway.PrometheusIntegration/GatewayApiMetricObserver.cs index 4d90ad115..9c9314ff7 100644 --- a/src/RadixDlt.NetworkGateway.PrometheusIntegration/GatewayApiMetricObserver.cs +++ b/src/RadixDlt.NetworkGateway.PrometheusIntegration/GatewayApiMetricObserver.cs @@ -187,21 +187,41 @@ public void OnSqlQueryExecuted(string queryName, TimeSpan duration) _sqlQueryDuration.WithLabels(queryName).Observe(duration.TotalSeconds); } - void IExceptionObserver.OnException(ActionContext actionContext, Exception exception, KnownGatewayErrorException gatewayErrorException) + void IExceptionObserver.OnValidationError(HttpContext httpContext, GatewayModel.GatewayError gatewayError, int statusCode) { // actionContext.HttpContext.Request.Method - GET or POST - var routeValueDictionary = actionContext.RouteData.Values; + var routeValueDictionary = httpContext.GetRouteData().Values; // This is a lot of labels, but the rest depend on the action and exception, so the cardinality isn't massive / worrying // Method/Controller/Action align with the prometheus-net http metrics // https://github.com/prometheus-net/prometheus-net/blob/master/Prometheus.AspNetCore/HttpMetrics/HttpRequestMiddlewareBase.cs _apiResponseErrorCount .WithLabels( - actionContext.HttpContext.Request.Method, // method (GET or POST) + httpContext.Request.Method, // method (GET or POST) + routeValueDictionary.GetValueOrDefault("Controller") as string ?? string.Empty, // controller + routeValueDictionary.GetValueOrDefault("Action") as string ?? string.Empty, // action + string.Empty, // exception + gatewayError.GetType().Name, // gateway_error + statusCode.ToString(CultureInfo.InvariantCulture) // status_code + ) + .Inc(); + } + + void IExceptionObserver.OnException(HttpContext httpContext, Exception exception, KnownGatewayErrorException gatewayErrorException) + { + // actionContext.HttpContext.Request.Method - GET or POST + var routeValueDictionary = httpContext.GetRouteData().Values; + + // This is a lot of labels, but the rest depend on the action and exception, so the cardinality isn't massive / worrying + // Method/Controller/Action align with the prometheus-net http metrics + // https://github.com/prometheus-net/prometheus-net/blob/master/Prometheus.AspNetCore/HttpMetrics/HttpRequestMiddlewareBase.cs + _apiResponseErrorCount + .WithLabels( + httpContext.Request.Method, // method (GET or POST) routeValueDictionary.GetValueOrDefault("Controller") as string ?? string.Empty, // controller routeValueDictionary.GetValueOrDefault("Action") as string ?? string.Empty, // action exception.GetNameForMetricsOrLogging(), // exception - gatewayErrorException.GatewayError.GetType().Name, // gateway_error + gatewayErrorException.GatewayError?.GetType().Name ?? string.Empty, // gateway_error gatewayErrorException.StatusCode.ToString(CultureInfo.InvariantCulture) // status_code ) .Inc(); From b33020bc4d1a81ffe78320396482906ce54415c9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Pawelec?= Date: Thu, 5 Oct 2023 12:25:02 +0200 Subject: [PATCH 09/14] cleanup. --- apps/GatewayApi/Controllers/StateEntityController.cs | 1 - apps/GatewayApi/Controllers/StateKeyValueStoreController.cs | 1 - apps/GatewayApi/Controllers/StateNonFungibleController.cs | 1 - apps/GatewayApi/Controllers/StatisticsController.cs | 1 - apps/GatewayApi/Controllers/StatusController.cs | 1 - apps/GatewayApi/Controllers/StreamController.cs | 6 ------ apps/GatewayApi/Controllers/TransactionController.cs | 1 - apps/GatewayApi/Controllers/ValidatorStateController.cs | 1 - .../ExceptionHandlingMiddleware.cs | 3 +-- apps/GatewayApi/GatewayApiStartup.cs | 3 --- apps/GatewayApi/SlowRequestLogging/Extensions.cs | 1 - .../SlowRequestLogging/SlowRequestLoggingMiddleware.cs | 1 - .../Services/ValidationErrorHandler.cs | 3 --- .../GatewayApiMetricObserver.cs | 1 - 14 files changed, 1 insertion(+), 24 deletions(-) diff --git a/apps/GatewayApi/Controllers/StateEntityController.cs b/apps/GatewayApi/Controllers/StateEntityController.cs index 3cf3c8e3c..b4b1ff260 100644 --- a/apps/GatewayApi/Controllers/StateEntityController.cs +++ b/apps/GatewayApi/Controllers/StateEntityController.cs @@ -63,7 +63,6 @@ */ using Microsoft.AspNetCore.Mvc; -using RadixDlt.NetworkGateway.GatewayApi.AspNetCore; using RadixDlt.NetworkGateway.GatewayApi.Handlers; using System.Threading; using System.Threading.Tasks; diff --git a/apps/GatewayApi/Controllers/StateKeyValueStoreController.cs b/apps/GatewayApi/Controllers/StateKeyValueStoreController.cs index afd689295..1a0c8f26b 100644 --- a/apps/GatewayApi/Controllers/StateKeyValueStoreController.cs +++ b/apps/GatewayApi/Controllers/StateKeyValueStoreController.cs @@ -63,7 +63,6 @@ */ using Microsoft.AspNetCore.Mvc; -using RadixDlt.NetworkGateway.GatewayApi.AspNetCore; using RadixDlt.NetworkGateway.GatewayApi.Handlers; using System.Threading; using System.Threading.Tasks; diff --git a/apps/GatewayApi/Controllers/StateNonFungibleController.cs b/apps/GatewayApi/Controllers/StateNonFungibleController.cs index 973c77cd5..768f40b1f 100644 --- a/apps/GatewayApi/Controllers/StateNonFungibleController.cs +++ b/apps/GatewayApi/Controllers/StateNonFungibleController.cs @@ -63,7 +63,6 @@ */ using Microsoft.AspNetCore.Mvc; -using RadixDlt.NetworkGateway.GatewayApi.AspNetCore; using RadixDlt.NetworkGateway.GatewayApi.Handlers; using System.Threading; using System.Threading.Tasks; diff --git a/apps/GatewayApi/Controllers/StatisticsController.cs b/apps/GatewayApi/Controllers/StatisticsController.cs index ded0cc77d..69219b6ab 100644 --- a/apps/GatewayApi/Controllers/StatisticsController.cs +++ b/apps/GatewayApi/Controllers/StatisticsController.cs @@ -63,7 +63,6 @@ */ using Microsoft.AspNetCore.Mvc; -using RadixDlt.NetworkGateway.GatewayApi.AspNetCore; using RadixDlt.NetworkGateway.GatewayApi.Handlers; using System.Threading; using System.Threading.Tasks; diff --git a/apps/GatewayApi/Controllers/StatusController.cs b/apps/GatewayApi/Controllers/StatusController.cs index b440ee71b..4398c9c03 100644 --- a/apps/GatewayApi/Controllers/StatusController.cs +++ b/apps/GatewayApi/Controllers/StatusController.cs @@ -63,7 +63,6 @@ */ using Microsoft.AspNetCore.Mvc; -using RadixDlt.NetworkGateway.GatewayApi.AspNetCore; using RadixDlt.NetworkGateway.GatewayApi.Handlers; using RadixDlt.NetworkGateway.GatewayApi.Services; using System.Threading; diff --git a/apps/GatewayApi/Controllers/StreamController.cs b/apps/GatewayApi/Controllers/StreamController.cs index 5e845580f..2a7bb567d 100644 --- a/apps/GatewayApi/Controllers/StreamController.cs +++ b/apps/GatewayApi/Controllers/StreamController.cs @@ -63,16 +63,10 @@ */ using Microsoft.AspNetCore.Mvc; -using RadixDlt.NetworkGateway.GatewayApi.AspNetCore; -using RadixDlt.NetworkGateway.GatewayApi.Exceptions; using RadixDlt.NetworkGateway.GatewayApi.Handlers; -using System; -using System.Net.Http; using System.Threading; using System.Threading.Tasks; using GatewayModel = RadixDlt.NetworkGateway.GatewayApiSdk.Model; -// ReSharper disable HeuristicUnreachableCode -#pragma warning disable CS0162 // Unreachable code detected namespace GatewayApi.Controllers; diff --git a/apps/GatewayApi/Controllers/TransactionController.cs b/apps/GatewayApi/Controllers/TransactionController.cs index 48bc0bac3..c537e94d8 100644 --- a/apps/GatewayApi/Controllers/TransactionController.cs +++ b/apps/GatewayApi/Controllers/TransactionController.cs @@ -63,7 +63,6 @@ */ using Microsoft.AspNetCore.Mvc; -using RadixDlt.NetworkGateway.GatewayApi.AspNetCore; using RadixDlt.NetworkGateway.GatewayApi.Handlers; using System.Threading; using System.Threading.Tasks; diff --git a/apps/GatewayApi/Controllers/ValidatorStateController.cs b/apps/GatewayApi/Controllers/ValidatorStateController.cs index 6669ed71f..2e4a8e670 100644 --- a/apps/GatewayApi/Controllers/ValidatorStateController.cs +++ b/apps/GatewayApi/Controllers/ValidatorStateController.cs @@ -63,7 +63,6 @@ */ using Microsoft.AspNetCore.Mvc; -using RadixDlt.NetworkGateway.GatewayApi.AspNetCore; using RadixDlt.NetworkGateway.GatewayApi.Handlers; using System.Threading; using System.Threading.Tasks; diff --git a/apps/GatewayApi/ExceptionHandlingMiddleware/ExceptionHandlingMiddleware.cs b/apps/GatewayApi/ExceptionHandlingMiddleware/ExceptionHandlingMiddleware.cs index e051e8be3..283668f38 100644 --- a/apps/GatewayApi/ExceptionHandlingMiddleware/ExceptionHandlingMiddleware.cs +++ b/apps/GatewayApi/ExceptionHandlingMiddleware/ExceptionHandlingMiddleware.cs @@ -62,7 +62,6 @@ * permissions under this License. */ -using Microsoft.AspNetCore.Diagnostics; using Microsoft.AspNetCore.Http; using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Logging; @@ -139,7 +138,7 @@ private KnownGatewayErrorException HandleException(HttpContext httpContext, Exce _logger.LogInformation(exception, "Bad http request. [RequestTrace={TraceId}]", traceId); return new BadRequestException("Bad http request", badHttpRequestException.Message); - case OperationCanceledException operationCanceledException: + case OperationCanceledException: var requestTimeoutFeature = httpContext.Features.Get(); var requestAbortedFeature = httpContext.Features.Get(); diff --git a/apps/GatewayApi/GatewayApiStartup.cs b/apps/GatewayApi/GatewayApiStartup.cs index 7450ae60c..d9b1e4ef4 100644 --- a/apps/GatewayApi/GatewayApiStartup.cs +++ b/apps/GatewayApi/GatewayApiStartup.cs @@ -65,7 +65,6 @@ using GatewayApi.ExceptionHandlingMiddleware; using GatewayApi.SlowRequestLogging; using Microsoft.AspNetCore.Builder; -using Microsoft.AspNetCore.Mvc; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; @@ -76,8 +75,6 @@ using RadixDlt.NetworkGateway.PostgresIntegration; using RadixDlt.NetworkGateway.PrometheusIntegration; using System; -using System.Threading; -using System.Threading.Tasks; namespace GatewayApi; diff --git a/apps/GatewayApi/SlowRequestLogging/Extensions.cs b/apps/GatewayApi/SlowRequestLogging/Extensions.cs index 1ae702223..0dcfba754 100644 --- a/apps/GatewayApi/SlowRequestLogging/Extensions.cs +++ b/apps/GatewayApi/SlowRequestLogging/Extensions.cs @@ -65,7 +65,6 @@ using Microsoft.AspNetCore.Builder; using Microsoft.Extensions.DependencyInjection; using System; -using System.Threading; namespace GatewayApi.SlowRequestLogging; diff --git a/apps/GatewayApi/SlowRequestLogging/SlowRequestLoggingMiddleware.cs b/apps/GatewayApi/SlowRequestLogging/SlowRequestLoggingMiddleware.cs index 4c0da79d9..31383bf27 100644 --- a/apps/GatewayApi/SlowRequestLogging/SlowRequestLoggingMiddleware.cs +++ b/apps/GatewayApi/SlowRequestLogging/SlowRequestLoggingMiddleware.cs @@ -66,7 +66,6 @@ using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; using System.Diagnostics; -using System.Threading; using System.Threading.Tasks; namespace GatewayApi.SlowRequestLogging; diff --git a/src/RadixDlt.NetworkGateway.GatewayApi/Services/ValidationErrorHandler.cs b/src/RadixDlt.NetworkGateway.GatewayApi/Services/ValidationErrorHandler.cs index b19c7d585..c5be970e0 100644 --- a/src/RadixDlt.NetworkGateway.GatewayApi/Services/ValidationErrorHandler.cs +++ b/src/RadixDlt.NetworkGateway.GatewayApi/Services/ValidationErrorHandler.cs @@ -62,14 +62,11 @@ * permissions under this License. */ -using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Logging; using Newtonsoft.Json; using RadixDlt.NetworkGateway.Abstractions.Extensions; -using RadixDlt.NetworkGateway.GatewayApi.Exceptions; -using System; using System.Collections.Generic; using System.Diagnostics; using System.Linq; diff --git a/src/RadixDlt.NetworkGateway.PrometheusIntegration/GatewayApiMetricObserver.cs b/src/RadixDlt.NetworkGateway.PrometheusIntegration/GatewayApiMetricObserver.cs index 9c9314ff7..acd58551c 100644 --- a/src/RadixDlt.NetworkGateway.PrometheusIntegration/GatewayApiMetricObserver.cs +++ b/src/RadixDlt.NetworkGateway.PrometheusIntegration/GatewayApiMetricObserver.cs @@ -63,7 +63,6 @@ */ using Microsoft.AspNetCore.Http; -using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Routing; using Prometheus; using RadixDlt.NetworkGateway.Abstractions.CoreCommunications; From b8e510412b401bd7590106c83e895fbe356a771b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Pawelec?= Date: Thu, 5 Oct 2023 15:29:08 +0200 Subject: [PATCH 10/14] fix pull request comments. --- ...sions.cs => ApplicationBuilderExtension.cs} | 4 ++-- .../ExceptionHandlingMiddleware.cs | 18 +++++++----------- apps/GatewayApi/GatewayApiStartup.cs | 2 +- .../AspNetCore/RequestTimeoutFeature.cs | 6 +++--- .../Exceptions/BadRequestException.cs | 9 --------- .../ClientClosedConnectionException.cs | 9 --------- .../ClientConnectionClosedException.cs | 16 ++++++++++++++++ .../Exceptions/GenericBadRequestException.cs | 11 +++++++++++ .../Services/ValidationErrorHandler.cs | 6 ++---- 9 files changed, 42 insertions(+), 39 deletions(-) rename apps/GatewayApi/ExceptionHandlingMiddleware/{Extensions.cs => ApplicationBuilderExtension.cs} (96%) delete mode 100644 src/RadixDlt.NetworkGateway.GatewayApi/Exceptions/BadRequestException.cs delete mode 100644 src/RadixDlt.NetworkGateway.GatewayApi/Exceptions/ClientClosedConnectionException.cs create mode 100644 src/RadixDlt.NetworkGateway.GatewayApi/Exceptions/ClientConnectionClosedException.cs create mode 100644 src/RadixDlt.NetworkGateway.GatewayApi/Exceptions/GenericBadRequestException.cs diff --git a/apps/GatewayApi/ExceptionHandlingMiddleware/Extensions.cs b/apps/GatewayApi/ExceptionHandlingMiddleware/ApplicationBuilderExtension.cs similarity index 96% rename from apps/GatewayApi/ExceptionHandlingMiddleware/Extensions.cs rename to apps/GatewayApi/ExceptionHandlingMiddleware/ApplicationBuilderExtension.cs index 0a12ac6a4..8aa025cf9 100644 --- a/apps/GatewayApi/ExceptionHandlingMiddleware/Extensions.cs +++ b/apps/GatewayApi/ExceptionHandlingMiddleware/ApplicationBuilderExtension.cs @@ -66,9 +66,9 @@ namespace GatewayApi.ExceptionHandlingMiddleware; -public static class Extensions +public static class ApplicationBuilderExtension { - public static IApplicationBuilder UseCustomExceptionHandler(this IApplicationBuilder app) + public static IApplicationBuilder UseGatewayExceptionHandler(this IApplicationBuilder app) { return app.UseMiddleware(); } diff --git a/apps/GatewayApi/ExceptionHandlingMiddleware/ExceptionHandlingMiddleware.cs b/apps/GatewayApi/ExceptionHandlingMiddleware/ExceptionHandlingMiddleware.cs index 283668f38..7b56f4e84 100644 --- a/apps/GatewayApi/ExceptionHandlingMiddleware/ExceptionHandlingMiddleware.cs +++ b/apps/GatewayApi/ExceptionHandlingMiddleware/ExceptionHandlingMiddleware.cs @@ -86,15 +86,12 @@ internal sealed class ExceptionHandlingMiddleware private readonly RequestDelegate _next; private readonly ILogger _logger; private readonly IEnumerable _observers; - private readonly LogLevel _knownGatewayErrorLogLevel; - public ExceptionHandlingMiddleware(RequestDelegate next, IHostEnvironment env, ILogger logger, - IEnumerable observers) + public ExceptionHandlingMiddleware(RequestDelegate next, ILogger logger, IEnumerable observers) { _next = next; _logger = logger; _observers = observers; - _knownGatewayErrorLogLevel = env.IsDevelopment() ? LogLevel.Information : LogLevel.Debug; } public async Task InvokeAsync(HttpContext context) @@ -135,8 +132,8 @@ private KnownGatewayErrorException HandleException(HttpContext httpContext, Exce switch (exception) { case BadHttpRequestException badHttpRequestException : - _logger.LogInformation(exception, "Bad http request. [RequestTrace={TraceId}]", traceId); - return new BadRequestException("Bad http request", badHttpRequestException.Message); + _logger.LogWarning(exception, "Bad http request. [RequestTrace={TraceId}]", traceId); + return new GenericBadRequestException("Bad http request", badHttpRequestException.Message); case OperationCanceledException: var requestTimeoutFeature = httpContext.Features.Get(); @@ -144,14 +141,14 @@ private KnownGatewayErrorException HandleException(HttpContext httpContext, Exce if (requestTimeoutFeature?.CancellationToken.IsCancellationRequested == true) { - _logger.LogError(exception, "Request timed out after={Timeout} seconds, [RequestTrace={TraceId}]", requestTimeoutFeature.TimeoutAfter.TotalSeconds, traceId); + _logger.LogError(exception, "Request timed out after={Timeout} seconds, [RequestTrace={TraceId}]", requestTimeoutFeature.Timeout.TotalSeconds, traceId); return InternalServerException.OfRequestTimeoutException(exception, traceId); } if (requestAbortedFeature?.CancellationToken.IsCancellationRequested == true) { - _logger.LogWarning(exception, "Request aborted by user. [RequestTrace={TraceId}]", traceId); - return new ClientClosedConnectionException("Client closed connection", "Client closed connection"); + _logger.LogWarning(exception, "Request aborted by API client. [RequestTrace={TraceId}]", traceId); + return new ClientConnectionClosedException("Client closed connection", "Client closed connection"); } _logger.LogWarning( @@ -162,8 +159,7 @@ private KnownGatewayErrorException HandleException(HttpContext httpContext, Exce return InternalServerException.OfHiddenException(exception, traceId); case KnownGatewayErrorException knownGatewayErrorException: - _logger.Log( - _knownGatewayErrorLogLevel, + _logger.LogDebug( exception, "Known exception with http response code [RequestTrace={TraceId}]", traceId diff --git a/apps/GatewayApi/GatewayApiStartup.cs b/apps/GatewayApi/GatewayApiStartup.cs index d9b1e4ef4..a31e7f566 100644 --- a/apps/GatewayApi/GatewayApiStartup.cs +++ b/apps/GatewayApi/GatewayApiStartup.cs @@ -126,7 +126,7 @@ public void ConfigureServices(IServiceCollection services) public void Configure(IApplicationBuilder application, IConfiguration configuration, ILogger logger) { application - .UseCustomExceptionHandler() + .UseGatewayExceptionHandler() .UseSlowRequestLogging() .UseRequestTimeout() .UseCors() diff --git a/src/RadixDlt.NetworkGateway.GatewayApi/AspNetCore/RequestTimeoutFeature.cs b/src/RadixDlt.NetworkGateway.GatewayApi/AspNetCore/RequestTimeoutFeature.cs index d5571eeb1..0f00eaf58 100644 --- a/src/RadixDlt.NetworkGateway.GatewayApi/AspNetCore/RequestTimeoutFeature.cs +++ b/src/RadixDlt.NetworkGateway.GatewayApi/AspNetCore/RequestTimeoutFeature.cs @@ -7,7 +7,7 @@ public interface IRequestTimeoutFeature { public CancellationToken CancellationToken { get; } - public TimeSpan TimeoutAfter { get; } + public TimeSpan Timeout { get; } } public class RequestTimeoutFeature : IRequestTimeoutFeature @@ -15,10 +15,10 @@ public class RequestTimeoutFeature : IRequestTimeoutFeature public RequestTimeoutFeature(CancellationToken cancellationToken, TimeSpan timeoutAfter) { CancellationToken = cancellationToken; - TimeoutAfter = timeoutAfter; + Timeout = timeoutAfter; } public CancellationToken CancellationToken { get; } - public TimeSpan TimeoutAfter { get; } + public TimeSpan Timeout { get; } } diff --git a/src/RadixDlt.NetworkGateway.GatewayApi/Exceptions/BadRequestException.cs b/src/RadixDlt.NetworkGateway.GatewayApi/Exceptions/BadRequestException.cs deleted file mode 100644 index 3ff1e6196..000000000 --- a/src/RadixDlt.NetworkGateway.GatewayApi/Exceptions/BadRequestException.cs +++ /dev/null @@ -1,9 +0,0 @@ -namespace RadixDlt.NetworkGateway.GatewayApi.Exceptions; - -public sealed class BadRequestException : KnownGatewayErrorException -{ - public BadRequestException(string userFacingMessage, string internalMessage) - : base(400, null, userFacingMessage, internalMessage) - { - } -} diff --git a/src/RadixDlt.NetworkGateway.GatewayApi/Exceptions/ClientClosedConnectionException.cs b/src/RadixDlt.NetworkGateway.GatewayApi/Exceptions/ClientClosedConnectionException.cs deleted file mode 100644 index ade381642..000000000 --- a/src/RadixDlt.NetworkGateway.GatewayApi/Exceptions/ClientClosedConnectionException.cs +++ /dev/null @@ -1,9 +0,0 @@ -namespace RadixDlt.NetworkGateway.GatewayApi.Exceptions; - -public sealed class ClientClosedConnectionException : KnownGatewayErrorException -{ - public ClientClosedConnectionException(string userFacingMessage, string internalMessage) - : base(499, null, userFacingMessage, internalMessage) - { - } -} diff --git a/src/RadixDlt.NetworkGateway.GatewayApi/Exceptions/ClientConnectionClosedException.cs b/src/RadixDlt.NetworkGateway.GatewayApi/Exceptions/ClientConnectionClosedException.cs new file mode 100644 index 000000000..a5b8f1f70 --- /dev/null +++ b/src/RadixDlt.NetworkGateway.GatewayApi/Exceptions/ClientConnectionClosedException.cs @@ -0,0 +1,16 @@ +namespace RadixDlt.NetworkGateway.GatewayApi.Exceptions; + +public sealed class ClientConnectionClosedException : KnownGatewayErrorException +{ + /// + /// Non standard status code (there's no suitable one for such situation). + /// In NGINX means that the client closed the connection before the server answered the request. + /// Wanted to filter these cases out from standard internal server errors. + /// + private const int ClientConnectionClosedHttpStatusCode = 499; + + public ClientConnectionClosedException(string userFacingMessage, string internalMessage) + : base(ClientConnectionClosedHttpStatusCode, null, userFacingMessage, internalMessage) + { + } +} diff --git a/src/RadixDlt.NetworkGateway.GatewayApi/Exceptions/GenericBadRequestException.cs b/src/RadixDlt.NetworkGateway.GatewayApi/Exceptions/GenericBadRequestException.cs new file mode 100644 index 000000000..f1681f531 --- /dev/null +++ b/src/RadixDlt.NetworkGateway.GatewayApi/Exceptions/GenericBadRequestException.cs @@ -0,0 +1,11 @@ +using System.Net; + +namespace RadixDlt.NetworkGateway.GatewayApi.Exceptions; + +public class GenericBadRequestException : KnownGatewayErrorException +{ + public GenericBadRequestException(string userFacingMessage, string internalMessage) + : base((int)HttpStatusCode.BadRequest, null, userFacingMessage, internalMessage) + { + } +} diff --git a/src/RadixDlt.NetworkGateway.GatewayApi/Services/ValidationErrorHandler.cs b/src/RadixDlt.NetworkGateway.GatewayApi/Services/ValidationErrorHandler.cs index c5be970e0..d9a01a9eb 100644 --- a/src/RadixDlt.NetworkGateway.GatewayApi/Services/ValidationErrorHandler.cs +++ b/src/RadixDlt.NetworkGateway.GatewayApi/Services/ValidationErrorHandler.cs @@ -84,13 +84,11 @@ internal class ValidationErrorHandler : IValidationErrorHandler { private readonly ILogger _logger; private readonly IEnumerable _observers; - private readonly LogLevel _knownGatewayErrorLogLevel; - public ValidationErrorHandler(IHostEnvironment env, ILogger logger, IEnumerable observers) + public ValidationErrorHandler(ILogger logger, IEnumerable observers) { _logger = logger; _observers = observers; - _knownGatewayErrorLogLevel = env.IsDevelopment() ? LogLevel.Information : LogLevel.Debug; } public IActionResult GetClientError(ActionContext actionContext) @@ -117,7 +115,7 @@ public IActionResult GetClientError(ActionContext actionContext) const int StatusCode = (int)HttpStatusCode.BadRequest; var gatewayError = new GatewayModel.InvalidRequestError(validationErrors); - _logger.Log(_knownGatewayErrorLogLevel, "Validation error occured. Error: {error}, [RequestTrace={TraceId}]", JsonConvert.SerializeObject(validationErrors), traceId); + _logger.LogDebug("Validation error occured. Error: {error}, [RequestTrace={TraceId}]", JsonConvert.SerializeObject(validationErrors), traceId); var errorResponse = new GatewayModel.ErrorResponse( code: StatusCode, From 9bdd85a65dbfc4e70df153ea8f7267ccfb57091b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Pawelec?= Date: Fri, 6 Oct 2023 14:39:58 +0200 Subject: [PATCH 11/14] add warning when failed to parse transaction by toolkit. --- .../Services/SubmissionService.cs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/RadixDlt.NetworkGateway.GatewayApi/Services/SubmissionService.cs b/src/RadixDlt.NetworkGateway.GatewayApi/Services/SubmissionService.cs index 45a9eeb54..e82c0aa97 100644 --- a/src/RadixDlt.NetworkGateway.GatewayApi/Services/SubmissionService.cs +++ b/src/RadixDlt.NetworkGateway.GatewayApi/Services/SubmissionService.cs @@ -179,15 +179,16 @@ private async Task CheckPendingTransactionEpochValidity(GatewayModel.LedgerState await _observers.ForEachAsync(x => x.ObserveTransactionSubmissionToGatewayOutcome(TransactionSubmissionOutcome.ParseFailedStaticallyInvalid)); throw InvalidTransactionException.FromStaticallyInvalid(ex.error); } - catch (ToolkitModel.RadixEngineToolkitException) + catch (ToolkitModel.RadixEngineToolkitException ex) { await _observers.ForEachAsync(x => x.ObserveTransactionSubmissionToGatewayOutcome(TransactionSubmissionOutcome.ParseFailedIncorrectFormat)); + _logger.LogWarning(ex, "Corrupted notarized tx bytes"); throw InvalidTransactionException.FromUnsupportedPayloadType(); } catch (Exception ex) { await _observers.ForEachAsync(x => x.ObserveTransactionSubmissionToGatewayOutcome(TransactionSubmissionOutcome.ParseFailedOtherError)); - _logger.LogWarning(ex, "Unexpected exception when parsing / validating submitted transaction"); + _logger.LogWarning(ex, "Exception when parsing / validating submitted transaction"); throw; } } From 4c09998ab66ef8a7a189a89b293ed561cf40b1eb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Pawelec?= Date: Mon, 9 Oct 2023 12:17:57 +0200 Subject: [PATCH 12/14] fix metrics label for transaction submission. Put n/a for internal failures reported by gateway. --- .../Services/ISubmissionServiceObserver.cs | 2 +- .../Services/SubmissionService.cs | 9 +++++---- .../DataAggregatorMetricsObserver.cs | 11 +++++++---- .../GatewayApiMetricObserver.cs | 7 +++++-- 4 files changed, 18 insertions(+), 11 deletions(-) diff --git a/src/RadixDlt.NetworkGateway.GatewayApi/Services/ISubmissionServiceObserver.cs b/src/RadixDlt.NetworkGateway.GatewayApi/Services/ISubmissionServiceObserver.cs index 89f646ff0..d71297168 100644 --- a/src/RadixDlt.NetworkGateway.GatewayApi/Services/ISubmissionServiceObserver.cs +++ b/src/RadixDlt.NetworkGateway.GatewayApi/Services/ISubmissionServiceObserver.cs @@ -71,7 +71,7 @@ namespace RadixDlt.NetworkGateway.GatewayApi.Services; public interface ISubmissionServiceObserver { - ValueTask ObserveTransactionSubmissionToGatewayOutcome(TransactionSubmissionOutcome outcome); + ValueTask ObserveTransactionSubmissionToGatewayOutcome(TransactionSubmissionOutcome outcome, string? targetNode = null); } // This can be turned into an enum in future if we need this diff --git a/src/RadixDlt.NetworkGateway.GatewayApi/Services/SubmissionService.cs b/src/RadixDlt.NetworkGateway.GatewayApi/Services/SubmissionService.cs index e82c0aa97..7a92e6aeb 100644 --- a/src/RadixDlt.NetworkGateway.GatewayApi/Services/SubmissionService.cs +++ b/src/RadixDlt.NetworkGateway.GatewayApi/Services/SubmissionService.cs @@ -114,11 +114,12 @@ public SubmissionService( using var parsedTransaction = await HandlePreSubmissionParseTransaction(transactionBytes); await CheckPendingTransactionEpochValidity(ledgerState, parsedTransaction); + var targetNode = _coreApiHandler.GetCoreNodeConnectedTo(); var options = _coreApiIntegrationOptions.CurrentValue; var submissionResult = await _submissionTrackingService.ObserveSubmissionToGatewayAndSubmitToNetworkIfNew( _coreApiHandler.GetTransactionApi(), _coreApiHandler.GetNetworkName(), - _coreApiHandler.GetCoreNodeConnectedTo().Name, + targetNode.Name, new PendingTransactionHandlingConfig( options.MaxSubmissionAttempts, options.StopResubmittingAfter, @@ -134,17 +135,17 @@ public SubmissionService( // Note: I'm not sure this is the correct choice of what should be an API error here, but I don't want to change the API errors that are thrown this close to launch if (submissionResult.PermanentlyRejectedReason != null) { - await _observers.ForEachAsync(x => x.ObserveTransactionSubmissionToGatewayOutcome(TransactionSubmissionOutcome.PermanentlyRejected)); + await _observers.ForEachAsync(x => x.ObserveTransactionSubmissionToGatewayOutcome(TransactionSubmissionOutcome.PermanentlyRejected, targetNode.Name)); throw InvalidTransactionException.FromPermanentlyRejectedTransactionError(submissionResult.PermanentlyRejectedReason); } if (submissionResult.AlreadyKnown) { - await _observers.ForEachAsync(x => x.ObserveTransactionSubmissionToGatewayOutcome(TransactionSubmissionOutcome.DuplicateSubmission)); + await _observers.ForEachAsync(x => x.ObserveTransactionSubmissionToGatewayOutcome(TransactionSubmissionOutcome.DuplicateSubmission, targetNode.Name)); return new GatewayModel.TransactionSubmitResponse(duplicate: true); } - await _observers.ForEachAsync(x => x.ObserveTransactionSubmissionToGatewayOutcome(TransactionSubmissionOutcome.SubmittedToNetwork)); + await _observers.ForEachAsync(x => x.ObserveTransactionSubmissionToGatewayOutcome(TransactionSubmissionOutcome.SubmittedToNetwork, targetNode.Name)); return new GatewayModel.TransactionSubmitResponse(duplicate: false); } diff --git a/src/RadixDlt.NetworkGateway.PrometheusIntegration/DataAggregatorMetricsObserver.cs b/src/RadixDlt.NetworkGateway.PrometheusIntegration/DataAggregatorMetricsObserver.cs index 11d7f6798..720408aef 100644 --- a/src/RadixDlt.NetworkGateway.PrometheusIntegration/DataAggregatorMetricsObserver.cs +++ b/src/RadixDlt.NetworkGateway.PrometheusIntegration/DataAggregatorMetricsObserver.cs @@ -207,26 +207,29 @@ internal class DataAggregatorMetricsObserver : private static readonly Counter _transactionResubmissionAttemptCount = Metrics .CreateCounter( "ng_construction_transaction_resubmission_attempt_count", - "Number of transaction resubmission attempts" + "Number of transaction resubmission attempts", + new CounterConfiguration { LabelNames = new[] { "target_node" } } ); private static readonly Counter _transactionResubmissionSuccessCount = Metrics .CreateCounter( "ng_construction_transaction_resubmission_success_count", - "Number of transaction resubmission successes" + "Number of transaction resubmission successes", + new CounterConfiguration { LabelNames = new[] { "target_node" } } ); private static readonly Counter _transactionResubmissionErrorCount = Metrics .CreateCounter( "ng_construction_transaction_resubmission_error_count", - "Number of transaction resubmission errors" + "Number of transaction resubmission errors", + new CounterConfiguration { LabelNames = new[] { "target_node" } } ); private static readonly Counter _transactionResubmissionResolutionByResultCount = Metrics .CreateCounter( "ng_construction_transaction_resubmission_resolution_count", "Number of various resolutions of transaction resubmissions", - new CounterConfiguration { LabelNames = new[] { "result" } } + new CounterConfiguration { LabelNames = new[] { "result", "target_node" } } ); private static readonly Gauge _aggregatorIsUnhealthy = Metrics diff --git a/src/RadixDlt.NetworkGateway.PrometheusIntegration/GatewayApiMetricObserver.cs b/src/RadixDlt.NetworkGateway.PrometheusIntegration/GatewayApiMetricObserver.cs index acd58551c..6b98c3503 100644 --- a/src/RadixDlt.NetworkGateway.PrometheusIntegration/GatewayApiMetricObserver.cs +++ b/src/RadixDlt.NetworkGateway.PrometheusIntegration/GatewayApiMetricObserver.cs @@ -292,7 +292,7 @@ ValueTask ISubmissionTrackingServiceObserver.OnSubmissionTrackedInDatabase(bool return ValueTask.CompletedTask; } - public ValueTask ObserveTransactionSubmissionToGatewayOutcome(TransactionSubmissionOutcome outcome) + public ValueTask ObserveTransactionSubmissionToGatewayOutcome(TransactionSubmissionOutcome outcome, string? targetNode = null) { var label = outcome switch { @@ -306,7 +306,10 @@ public ValueTask ObserveTransactionSubmissionToGatewayOutcome(TransactionSubmiss TransactionSubmissionOutcome.StartEpochInFuture => "start_epoch_in_future", TransactionSubmissionOutcome.EndEpochInPast => "end_epoch_in_past", }; - _transactionSubmitResolutionByResultCount.WithLabels(label).Inc(); + + targetNode ??= "n/a"; + + _transactionSubmitResolutionByResultCount.WithLabels(label, targetNode).Inc(); return ValueTask.CompletedTask; } From e9634fa5aa5b9075916d30bc0874401a95f2b4ed Mon Sep 17 00:00:00 2001 From: Kim Fehrs <122281269+kofrdx@users.noreply.github.com> Date: Mon, 9 Oct 2023 16:04:33 +0200 Subject: [PATCH 13/14] CVE 2023 4911 for development branch (#527) * update glibc * add a comment on when to remove CVE fix * add a comment on when to remove CVE fix --- apps/DataAggregator/Dockerfile | 9 +++++++++ apps/DatabaseMigrations/Dockerfile | 9 +++++++++ apps/GatewayApi/Dockerfile | 9 +++++++++ 3 files changed, 27 insertions(+) diff --git a/apps/DataAggregator/Dockerfile b/apps/DataAggregator/Dockerfile index 1b59d931d..ab77a6ae4 100644 --- a/apps/DataAggregator/Dockerfile +++ b/apps/DataAggregator/Dockerfile @@ -5,6 +5,15 @@ FROM mcr.microsoft.com/dotnet/aspnet:7.0-bookworm-slim AS base WORKDIR /app +# Fixes CVE-2023-4911 can be removed when we update the base OS image to include this fix +# docker run -it mcr.microsoft.com/dotnet/aspnet:7.0-bookworm-slim ldd --version +# This fix can be removed as long as the version printed in the above command is 2.36-9+deb12u3 or above +RUN apt-get update -y \ + && apt-get -y --no-install-recommends install \ + libc6=2.36-9+deb12u3 \ + && apt-get clean \ + && rm -rf /var/lib/apt/lists/* + FROM mcr.microsoft.com/dotnet/sdk:7.0-bookworm-slim AS build WORKDIR /src COPY . . diff --git a/apps/DatabaseMigrations/Dockerfile b/apps/DatabaseMigrations/Dockerfile index 42b35b4fb..0d91b82e0 100644 --- a/apps/DatabaseMigrations/Dockerfile +++ b/apps/DatabaseMigrations/Dockerfile @@ -5,6 +5,15 @@ FROM mcr.microsoft.com/dotnet/aspnet:7.0-bookworm-slim AS base WORKDIR /app +# Fixes CVE-2023-4911 can be removed when we update the base OS image to include this fix +# docker run -it mcr.microsoft.com/dotnet/aspnet:7.0-bookworm-slim ldd --version +# This fix can be removed as long as the version printed in the above command is 2.36-9+deb12u3 or above +RUN apt-get update -y \ + && apt-get -y --no-install-recommends install \ + libc6=2.36-9+deb12u3 \ + && apt-get clean \ + && rm -rf /var/lib/apt/lists/* + FROM mcr.microsoft.com/dotnet/sdk:7.0-bookworm-slim AS build WORKDIR /src COPY . . diff --git a/apps/GatewayApi/Dockerfile b/apps/GatewayApi/Dockerfile index a17497fb3..7c80729da 100644 --- a/apps/GatewayApi/Dockerfile +++ b/apps/GatewayApi/Dockerfile @@ -5,6 +5,15 @@ FROM mcr.microsoft.com/dotnet/aspnet:7.0-bookworm-slim AS base WORKDIR /app +# Fixes CVE-2023-4911 can be removed when we update the base OS image to include this fix +# docker run -it mcr.microsoft.com/dotnet/aspnet:7.0-bookworm-slim ldd --version +# This fix can be removed as long as the version printed in the above command is 2.36-9+deb12u3 or above +RUN apt-get update -y \ + && apt-get -y --no-install-recommends install \ + libc6=2.36-9+deb12u3 \ + && apt-get clean \ + && rm -rf /var/lib/apt/lists/* + FROM mcr.microsoft.com/dotnet/sdk:7.0-bookworm-slim AS build WORKDIR /src COPY . . From 89d9179fcb3fbe50b23ab955e4a43687d030d59b Mon Sep 17 00:00:00 2001 From: Kim Fehrs <122281269+kofrdx@users.noreply.github.com> Date: Mon, 9 Oct 2023 16:04:37 +0200 Subject: [PATCH 14/14] CVE-2023-4911 (#526) * update glibc * update migrations * Update DataAggregator * fix syntax --- apps/DataAggregator/Dockerfile | 7 +++++++ apps/DatabaseMigrations/Dockerfile | 7 +++++++ apps/GatewayApi/Dockerfile | 7 +++++++ 3 files changed, 21 insertions(+) diff --git a/apps/DataAggregator/Dockerfile b/apps/DataAggregator/Dockerfile index 1b59d931d..ddb8c8096 100644 --- a/apps/DataAggregator/Dockerfile +++ b/apps/DataAggregator/Dockerfile @@ -5,6 +5,13 @@ FROM mcr.microsoft.com/dotnet/aspnet:7.0-bookworm-slim AS base WORKDIR /app +# Fixes CVE-2023-4911 can be removed when we update the base OS image to include this fix +RUN apt-get update -y \ + && apt-get -y --no-install-recommends install \ + libc6=2.36-9+deb12u3 \ + && apt-get clean \ + && rm -rf /var/lib/apt/lists/* + FROM mcr.microsoft.com/dotnet/sdk:7.0-bookworm-slim AS build WORKDIR /src COPY . . diff --git a/apps/DatabaseMigrations/Dockerfile b/apps/DatabaseMigrations/Dockerfile index 42b35b4fb..671afd7a0 100644 --- a/apps/DatabaseMigrations/Dockerfile +++ b/apps/DatabaseMigrations/Dockerfile @@ -5,6 +5,13 @@ FROM mcr.microsoft.com/dotnet/aspnet:7.0-bookworm-slim AS base WORKDIR /app +# Fixes CVE-2023-4911 can be removed when we update the base OS image to include this fix +RUN apt-get update -y \ + && apt-get -y --no-install-recommends install \ + libc6=2.36-9+deb12u3 \ + && apt-get clean \ + && rm -rf /var/lib/apt/lists/* + FROM mcr.microsoft.com/dotnet/sdk:7.0-bookworm-slim AS build WORKDIR /src COPY . . diff --git a/apps/GatewayApi/Dockerfile b/apps/GatewayApi/Dockerfile index a17497fb3..2f6d69576 100644 --- a/apps/GatewayApi/Dockerfile +++ b/apps/GatewayApi/Dockerfile @@ -5,6 +5,13 @@ FROM mcr.microsoft.com/dotnet/aspnet:7.0-bookworm-slim AS base WORKDIR /app +# Fixes CVE-2023-4911 can be removed when we update the base OS image to include this fix +RUN apt-get update -y \ + && apt-get -y --no-install-recommends install \ + libc6=2.36-9+deb12u3 \ + && apt-get clean \ + && rm -rf /var/lib/apt/lists/* + FROM mcr.microsoft.com/dotnet/sdk:7.0-bookworm-slim AS build WORKDIR /src COPY . .