Skip to content

Commit

Permalink
Merge pull request #66 from vostok/DisposeLeaks
Browse files Browse the repository at this point in the history
Dispose leaks
  • Loading branch information
HolyPrapor authored Oct 14, 2021
2 parents 7665296 + 1a0a10d commit 52149dd
Show file tree
Hide file tree
Showing 10 changed files with 87 additions and 42 deletions.
33 changes: 25 additions & 8 deletions Vostok.Hosting/Components/BuildContext.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
using System;
using System.Collections.Generic;
using System.Linq;
using Vostok.ClusterConfig.Client.Abstractions;
using Vostok.Commons.Collections;
using Vostok.Configuration;
Expand All @@ -17,7 +18,6 @@
using Vostok.Logging.Abstractions;
using Vostok.Logging.Configuration;
using Vostok.Logging.Console;
using Vostok.Logging.File;
using Vostok.ServiceDiscovery.Abstractions;
using Vostok.Tracing;
using Vostok.Tracing.Abstractions;
Expand All @@ -32,12 +32,14 @@ internal class BuildContext : IDisposable
private readonly SubstitutableLog substitutableLog;
private readonly SubstitutableTracer substitutableTracer;
private readonly SubstitutableDatacenters substitutableDatacenters;
private readonly List<object> disposables;

public BuildContext()
{
substitutableLog = new SubstitutableLog();
substitutableTracer = new SubstitutableTracer();
substitutableDatacenters = new SubstitutableDatacenters();
disposables = new List<object>();
ExternalComponents = new HashSet<object>(ByReferenceEqualityComparer<object>.Instance);
}

Expand All @@ -59,7 +61,6 @@ public BuildContext()
public IVostokHostingEnvironmentSetupContext EnvironmentSetupContext { get; set; }
public IVostokConfigurationSetupContext ConfigurationSetupContext { get; set; }
public IVostokHostExtensions HostExtensions { get; set; }
public List<object> DisposableHostExtensions { get; set; }
public HashSet<object> ExternalComponents { get; }

public Logs Logs { get; set; }
Expand All @@ -75,7 +76,14 @@ public ILog Log

public void SubstituteTracer((ITracer tracer, TracerSettings tracerSettings) tracer)
=> substitutableTracer.SubstituteWith(tracer.tracer, tracer.tracerSettings);


public T RegisterDisposable<T>(T disposable)
{
if (disposable != null)
disposables.Add(disposable);
return disposable;
}

public IDatacenters Datacenters
{
get => substitutableDatacenters;
Expand All @@ -94,8 +102,7 @@ public void Dispose()
{
LogDisposing("VostokHostingEnvironment");

foreach (var hostExtension in DisposableHostExtensions ?? new List<object>())
TryDispose(hostExtension, $"{hostExtension.GetType().Name} extension");
TryDisposeImplicitComponents();

TryDispose(DiagnosticsHub, "Diagnostics");

Expand Down Expand Up @@ -143,16 +150,26 @@ public void LogDisabled(string name, string reason) =>
public void LogDisposing(string componentName) =>
Log.ForContext<VostokHostingEnvironment>().Info("Disposing of {ComponentName}..", componentName);

private void TryDispose(object component, string componentName)
private void TryDispose(object component, string componentName, bool shouldLog = true)
{
if (ExternalComponents.Contains(component))
return;

if (!(component is IDisposable disposable))
if (component is not IDisposable disposable)
return;

LogDisposing(componentName);
if (shouldLog)
LogDisposing(componentName);

disposable.Dispose();
}

private void TryDisposeImplicitComponents()
{
var registeredExtensions = new HashSet<object>(HostExtensions.GetAll().Select(x => x.Item2), ByReferenceEqualityComparer<object>.Instance);

foreach (var disposable in disposables ?? new List<object>())
TryDispose(disposable, $"{disposable.GetType().Name} extension", registeredExtensions.Contains(disposable));
}
}
}
3 changes: 1 addition & 2 deletions Vostok.Hosting/Components/Diagnostics/DiagnosticsBuilder.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
using System;
using Vostok.Commons.Helpers;
using Vostok.Datacenters;
using Vostok.Hercules.Client;
using Vostok.Hosting.Abstractions.Diagnostics;
using Vostok.Hosting.Components.Diagnostics.HealthChecks;
Expand Down Expand Up @@ -70,7 +69,7 @@ private DiagnosticInfo BuildDiagnosticInfo(BuildContext context, IHealthTracker
info.RegisterProvider(CreateEntry(WellKnownDiagnosticInfoProvidersNames.EnvironmentInfo), new EnvironmentInfoProvider(context.Datacenters));

if (infoSettings.AddSystemMetricsInfo)
info.RegisterProvider(CreateEntry(WellKnownDiagnosticInfoProvidersNames.SystemMetrics), new SystemMetricsProvider());
info.RegisterProvider(CreateEntry(WellKnownDiagnosticInfoProvidersNames.SystemMetrics), context.RegisterDisposable(new SystemMetricsProvider()));

if (infoSettings.AddLoadedAssembliesInfo)
info.RegisterProvider(CreateEntry(WellKnownDiagnosticInfoProvidersNames.LoadedAssemblies), new LoadedAssembliesProvider());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,16 @@

namespace Vostok.Hosting.Components.Diagnostics.InfoProviders
{
internal class SystemMetricsProvider : IDiagnosticInfoProvider, IObserver<CurrentProcessMetrics>
internal class SystemMetricsProvider : IDiagnosticInfoProvider, IObserver<CurrentProcessMetrics>, IDisposable
{
private static readonly TimeSpan ObservationPeriod = 5.Seconds();

private readonly CurrentProcessMonitor monitor = new CurrentProcessMonitor();
private readonly IDisposable subscription;
private volatile CurrentProcessMetrics metrics = new CurrentProcessMetrics();

public SystemMetricsProvider()
=> monitor.ObserveMetrics(ObservationPeriod).Subscribe(this);
=> subscription = monitor.ObserveMetrics(ObservationPeriod).Subscribe(this);

public object Query()
{
Expand Down Expand Up @@ -56,5 +57,11 @@ public void OnCompleted()
public void OnError(Exception error)
{
}

public void Dispose()
{
subscription?.Dispose();
monitor?.Dispose();
}
}
}
6 changes: 3 additions & 3 deletions Vostok.Hosting/Components/Environment/EnvironmentBuilder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -202,7 +202,7 @@ private VostokHostingEnvironment BuildInner(BuildContext context)
AnnotationsHelper.ReportLaunching(context.ApplicationIdentity, context.Metrics.Instance);

if (settings.DiagnosticMetricsEnabled)
HerculesSinkMetrics.Measure(context.HerculesSink, context.Metrics, context.Log);
context.RegisterDisposable(HerculesSinkMetrics.Measure(context.HerculesSink, context.Metrics, context.Log));

if (settings.ConfigureStaticProviders)
FlowingContext.Configuration.ErrorCallback = (errorMessage, error) => context.Log.ForContext(typeof(FlowingContext)).Error(error, errorMessage);
Expand Down Expand Up @@ -275,7 +275,7 @@ private VostokHostingEnvironment BuildInner(BuildContext context)
}

if (settings.DiagnosticMetricsEnabled)
LogLevelMetrics.Measure(context.Logs.LogEventLevelCounterFactory.CreateCounter(), context.Metrics);
context.RegisterDisposable(LogLevelMetrics.Measure(context.Logs.LogEventLevelCounterFactory.CreateCounter(), context.Metrics));

if (settings.ConfigureStaticProviders)
StaticProvidersHelper.Configure(vostokHostingEnvironment);
Expand All @@ -284,7 +284,7 @@ private VostokHostingEnvironment BuildInner(BuildContext context)
context.DiagnosticsHub.HealthTracker.LaunchPeriodicalChecks(vostokHostingEnvironment.ShutdownToken);

if (settings.DiagnosticMetricsEnabled)
HealthCheckMetrics.Measure(context.DiagnosticsHub.HealthTracker, context.Metrics);
context.RegisterDisposable(HealthCheckMetrics.Measure(context.DiagnosticsHub.HealthTracker, context.Metrics));

return vostokHostingEnvironment;
}
Expand Down
10 changes: 4 additions & 6 deletions Vostok.Hosting/Components/Hercules/HerculesSinkMetrics.cs
Original file line number Diff line number Diff line change
Expand Up @@ -27,17 +27,15 @@ private HerculesSinkMetrics(HerculesSink herculesSink, IMetricContext context, I
this.log = log.ForContext<HerculesSink>();
this.herculesSink = herculesSink;
tags = context.Tags;

context.Register(this, ScrapePeriod);
}

public static void Measure(IHerculesSink herculesSink, IVostokApplicationMetrics context, ILog log)
public static IDisposable Measure(IHerculesSink herculesSink, IVostokApplicationMetrics context, ILog log)
{
if (!(herculesSink is HerculesSink sink))
return;
return null;

// ReSharper disable once ObjectCreationAsStatement
new HerculesSinkMetrics(sink, context.Application.WithTag(WellKnownTagKeys.Component, "HerculesSink"), log);
var builtContext = context.Application.WithTag(WellKnownTagKeys.Component, "HerculesSink");
return builtContext.Register(new HerculesSinkMetrics(sink, builtContext, log), ScrapePeriod);
}

public IEnumerable<MetricEvent> Scrape(DateTimeOffset timestamp)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ public void Build(BuildContext context, IVostokHostingEnvironment environment)

builderCustomization.Customize(this);

context.DisposableHostExtensions = disposable;
disposable.ForEach(x => context.RegisterDisposable(x));
context.HostExtensions = HostExtensions;
}

Expand Down
15 changes: 6 additions & 9 deletions Vostok.Hosting/Components/Log/LogLevelMetrics.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,21 +11,18 @@ internal class LogLevelMetrics
{
private readonly LogEventLevelCounter counter;

public LogLevelMetrics(LogEventLevelCounter counter, IMetricContext context)
private LogLevelMetrics(LogEventLevelCounter counter)
{
this.counter = counter;

context.CreateMultiFuncGauge(ProvideMetrics);
}

public static void Measure(LogEventLevelCounter counter, IVostokApplicationMetrics context)
public static IDisposable Measure(LogEventLevelCounter counter, IVostokApplicationMetrics context)
{
// ReSharper disable once ObjectCreationAsStatement
new LogLevelMetrics(
counter,
context.Instance
return context.Instance
.WithTag(WellKnownTagKeys.Component, "VostokLog")
.WithTag(WellKnownTagKeys.Name, "EventsByLevel"));
.WithTag(WellKnownTagKeys.Name, "EventsByLevel")
.CreateMultiFuncGauge(new LogLevelMetrics(counter).ProvideMetrics)
as IDisposable;
}

private IEnumerable<MetricDataPoint> ProvideMetrics()
Expand Down
6 changes: 3 additions & 3 deletions Vostok.Hosting/Components/Metrics/HealthCheckMetrics.cs
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,10 @@ private HealthCheckMetrics(IHealthTracker healthTracker, IMetricContext context)
healthTracker.ObserveReports().Subscribe(this);
}

public static void Measure(IHealthTracker healthTracker, IVostokApplicationMetrics context)
public static IDisposable Measure(IHealthTracker healthTracker, IVostokApplicationMetrics context)
{
// ReSharper disable once ObjectCreationAsStatement
new HealthCheckMetrics(healthTracker, context.Instance.WithTag(WellKnownTagKeys.Component, "VostokHealthChecks"));
var metrics = new HealthCheckMetrics(healthTracker, context.Instance.WithTag(WellKnownTagKeys.Component, "VostokHealthChecks"));
return healthTracker.ObserveReports().Subscribe(metrics);
}

public void OnCompleted()
Expand Down
41 changes: 33 additions & 8 deletions Vostok.Hosting/Components/SystemMetrics/SystemMetricsBuilder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
using Vostok.Hosting.Abstractions;
using Vostok.Hosting.Setup;
using Vostok.Metrics;
using Vostok.Metrics.System.Dns;
using Vostok.Metrics.System.Gc;
using Vostok.Metrics.System.Host;
using Vostok.Metrics.System.Process;
Expand Down Expand Up @@ -39,6 +40,9 @@ public void Build(BuildContext context, IVostokHostingEnvironment environment)

if (RuntimeDetector.IsDotNetCore30AndNewer)
RegisterGcMonitor(settings, context, processMetricsContext);

if (RuntimeDetector.IsDotNet50AndNewer)
RegisterDnsMonitor(settings, context, processMetricsContext);

RegisterProcessMonitor(settings, context, processMetricsContext);

Expand All @@ -50,13 +54,14 @@ private void RegisterGcMonitor(SystemMetricsSettings settings, BuildContext cont
var gcMonitor = new GarbageCollectionMonitor();

context.HostExtensions.AsMutable().Add(gcMonitor);
context.DisposableHostExtensions.Add(gcMonitor);

if (settings.EnableGcEventsLogging)
context.DisposableHostExtensions.Add(gcMonitor.LogCollections(context.Log, gc => gc.Duration >= settings.GcMinimumDurationForLogging));
context.RegisterDisposable(gcMonitor.LogCollections(context.Log, gc => gc.Duration >= settings.GcMinimumDurationForLogging));

if (settings.EnableGcEventsMetrics)
context.DisposableHostExtensions.Add(gcMonitor.ReportMetrics(metricContext));
context.RegisterDisposable(gcMonitor.ReportMetrics(metricContext));

context.RegisterDisposable(gcMonitor);
}

private void RegisterProcessMonitor(SystemMetricsSettings settings, BuildContext context, IMetricContext metricContext)
Expand All @@ -66,7 +71,7 @@ private void RegisterProcessMonitor(SystemMetricsSettings settings, BuildContext
context.HostExtensions.AsMutable().Add(processMonitor);

if (settings.EnableProcessMetricsLogging)
context.DisposableHostExtensions.Add(processMonitor.LogPeriodically(context.Log, settings.ProcessMetricsLoggingPeriod));
context.RegisterDisposable(processMonitor.LogPeriodically(context.Log, settings.ProcessMetricsLoggingPeriod));

if (settings.EnableProcessMetricsReporting)
{
Expand All @@ -77,9 +82,11 @@ private void RegisterProcessMonitor(SystemMetricsSettings settings, BuildContext
};

var collector = new CurrentProcessMetricsCollector(collectorSettings);

collector.ReportMetrics(metricContext, settings.ProcessMetricsReportingPeriod);
context.RegisterDisposable(collector.ReportMetrics(metricContext, settings.ProcessMetricsReportingPeriod));
context.RegisterDisposable(collector);
}

context.RegisterDisposable(processMonitor);
}

private void RegisterHostMonitor(SystemMetricsSettings settings, BuildContext context, IMetricContext metricContext)
Expand All @@ -90,10 +97,28 @@ private void RegisterHostMonitor(SystemMetricsSettings settings, BuildContext co
context.HostExtensions.AsMutable().Add(hostMonitor);

if (settings.EnableHostMetricsLogging)
context.DisposableHostExtensions.Add(hostMonitor.LogPeriodically(context.Log, settings.HostMetricsLoggingPeriod));
context.RegisterDisposable(hostMonitor.LogPeriodically(context.Log, settings.HostMetricsLoggingPeriod));

if (settings.EnableHostMetricsReporting)
new HostMetricsCollector(hostMetricsSettings).ReportMetrics(metricContext, settings.HostMetricsReportingPeriod);
{
var collector = new HostMetricsCollector(hostMetricsSettings);
context.RegisterDisposable(collector.ReportMetrics(metricContext, settings.HostMetricsReportingPeriod));
context.RegisterDisposable(collector);
}

context.RegisterDisposable(hostMonitor);
}

private void RegisterDnsMonitor(SystemMetricsSettings settings, BuildContext context, IMetricContext metricContext)
{
var dnsMonitor = new DnsMonitor();

context.HostExtensions.AsMutable().Add(dnsMonitor);

if (settings.EnableDnsEventsMetrics)
context.RegisterDisposable(dnsMonitor.ReportMetrics(metricContext));

context.RegisterDisposable(dnsMonitor);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ public class SystemMetricsSettings

public bool EnableGcEventsMetrics { get; set; } = true;

public bool EnableDnsEventsMetrics { get; set; } = true;

public bool EnableProcessMetricsLogging { get; set; } = true;

public bool EnableProcessMetricsReporting { get; set; } = true;
Expand Down

0 comments on commit 52149dd

Please sign in to comment.