Skip to content

Commit

Permalink
Fixed a bug where location sources were loading twice
Browse files Browse the repository at this point in the history
Fixed a bug where location sources were loading twice.  Added a semaphore to stop any threading issues, and also stopped it loading twice in the service configuration.
  • Loading branch information
vaughanknight committed Jun 19, 2024
1 parent d93320f commit 91799f7
Show file tree
Hide file tree
Showing 2 changed files with 51 additions and 14 deletions.
43 changes: 33 additions & 10 deletions src/CarbonAware.LocationSources/src/LocationSource.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
using Microsoft.Extensions.Options;
using System.Reflection;
using System.Text.Json;
using System.Threading;

namespace CarbonAware.LocationSources;

Expand All @@ -22,7 +23,6 @@ internal class LocationSource : ILocationSource

private LocationDataSourcesConfiguration _configuration => _configurationMonitor.CurrentValue;


/// <summary>
/// Creates a new instance of the <see cref="LocationSource"/> class.
/// </summary>
Expand Down Expand Up @@ -61,14 +61,21 @@ private async Task LoadLocationJsonFileAsync()
var sourceFiles = !_configuration.LocationSourceFiles.Any() ? DiscoverFiles() : _configuration.LocationSourceFiles;
foreach (var source in sourceFiles)
{
using Stream stream = GetStreamFromFileLocation(source);
var namedGeoMap = await JsonSerializer.DeserializeAsync<Dictionary<string, NamedGeoposition>>(stream, options);
foreach (var locationKey in namedGeoMap!.Keys)
if (File.Exists(source.DataFileLocation!))
{
using Stream stream = GetStreamFromFileLocation(source);
var namedGeoMap = await JsonSerializer.DeserializeAsync<Dictionary<string, NamedGeoposition>>(stream, options);
foreach (var locationKey in namedGeoMap!.Keys)
{
var geoInstance = namedGeoMap[locationKey];
geoInstance.AssertValid();
var key = BuildKey(source, locationKey);
AddToLocationMap(key, geoInstance, source.DataFileLocation, keyCounter);
}
}
else
{
var geoInstance = namedGeoMap[locationKey];
geoInstance.AssertValid();
var key = BuildKey(source, locationKey);
AddToLocationMap(key, geoInstance, source.DataFileLocation, keyCounter);
_logger.LogError($"Configured data source not found at '{source.DataFileLocation}'");
}
}
}
Expand All @@ -78,11 +85,27 @@ private String BuildKey(LocationSourceFile locationData, string locationName)
return $"{locationData.Prefix}{locationData.Delimiter}{locationName}";
}

/// <summary>
/// Semaphore is to stop any concurrent loading of the file, which
/// could in turn update the list twice and result in duplicate entries,
/// in turn resulting in suffixed keys to avoid clashes when not
/// required.
/// </summary>
private readonly SemaphoreSlim _semaphore = new SemaphoreSlim(1, 1);
private async Task LoadLocationFromFileIfNotPresentAsync()
{
if (!_allLocations.Any())
await _semaphore.WaitAsync();

try
{
if (!_allLocations.Any())
{
await LoadLocationJsonFileAsync();
}
}
finally
{
await LoadLocationJsonFileAsync();
_semaphore.Release();
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection.Extensions;
using System.Linq;

namespace GSF.CarbonAware.Configuration;

Expand All @@ -26,21 +27,34 @@ private static IServiceCollection ConfigureLocationDataSourcesConfiguration(thi
/// </summary>
public static IServiceCollection AddEmissionsServices(this IServiceCollection services, IConfiguration configuration)
{
services.ConfigureLocationDataSourcesConfiguration(configuration);
services.TryAddSingleton<ILocationSource, LocationSource>();
AddLocationServices(services, configuration);
services.AddDataSourceService(configuration);
services.TryAddSingleton<IEmissionsHandler, EmissionsHandler>();
services.TryAddSingleton<ILocationHandler, LocationHandler>();
return services;
}

/// <summary>
/// This stops the location configuration being loaded twice if needed for
/// historical emissions and forecasted emissions services.
/// </summary>
/// <param name="services"></param>
/// <param name="configuration"></param>
private static void AddLocationServices(IServiceCollection services, IConfiguration configuration)
{
if (!services.Any(x => x.ServiceType == typeof(ILocationSource)))
{
services.ConfigureLocationDataSourcesConfiguration(configuration);
services.TryAddSingleton<ILocationSource, LocationSource>();
}
}

/// <summary>
/// Add services needed in order to use an Forecast service.
/// </summary>
public static IServiceCollection AddForecastServices(this IServiceCollection services, IConfiguration configuration)
{
services.ConfigureLocationDataSourcesConfiguration(configuration);
services.TryAddSingleton<ILocationSource, LocationSource>();
AddLocationServices(services, configuration);
services.AddDataSourceService(configuration);
services.TryAddSingleton<IForecastHandler, ForecastHandler>();
services.TryAddSingleton<ILocationHandler, LocationHandler>();
Expand Down

0 comments on commit 91799f7

Please sign in to comment.