Skip to content

Commit e32f76b

Browse files
committed
Add lazy cache for memory mapped file and keep files in the cache for 30 minutes.
1 parent f475da0 commit e32f76b

File tree

6 files changed

+41
-15
lines changed

6 files changed

+41
-15
lines changed

ElevationWebApi.sln.DotSettings.user

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
<wpf:ResourceDictionary xml:space="preserve" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:s="clr-namespace:System;assembly=mscorlib" xmlns:ss="urn:shemas-jetbrains-com:settings-storage-xaml" xmlns:wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
22
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AArgumentSpecificationsFactory_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003FLibrary_003FApplication_0020Support_003FJetBrains_003FRider2024_002E3_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003F402b2077f38742cb9b381ab9e79e493229c00_003F4c_003Fc2df050d_003FArgumentSpecificationsFactory_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
3+
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003ACachingService_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003FLibrary_003FApplication_0020Support_003FJetBrains_003FRider2024_002E3_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003F33378a9c845d49f69aababbbb563f96d5e00_003F6f_003F45e913ce_003FCachingService_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
34
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AFixtureMethodRunner_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003FLibrary_003FApplication_0020Support_003FJetBrains_003FRider2024_002E3_003Fresharper_002Dhost_003FSourcesCache_003Fd4ca1e5a92fc75932c8ddef25b934f48ed8015686ce1b79256171845d4291_003FFixtureMethodRunner_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
45
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AFuture_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003FLibrary_003FApplication_0020Support_003FJetBrains_003FRider2024_002E3_003Fresharper_002Dhost_003FSourcesCache_003F271b8d2d9d94e13a469269f46758864f64ad438c3174f74d4a591af6410e96b_003FFuture_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
56
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AILogger_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003FLibrary_003FApplication_0020Support_003FJetBrains_003FRider2024_002E3_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003Fcc50fd1d894d4d86ab26ac07ac0af5ec10718_003F51_003F119e8ab7_003FILogger_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>

ElevationWebApiTests/MemoryMapElevationProviderTests.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
using NSubstitute;
55
using Microsoft.Extensions.Logging;
66
using System.Runtime.CompilerServices;
7+
using LazyCache;
78

89
namespace ElevationWebApiTests;
910

@@ -24,7 +25,7 @@ public void Initialize()
2425
var hosting = Substitute.For<IWebHostEnvironment>();
2526
hosting.ContentRootFileProvider = new PhysicalFileProvider( GetSourceFileDirectory()!);
2627
var logger = Substitute.For<ILogger<MemoryMapElevationProvider>>();
27-
provider = new MemoryMapElevationProvider(hosting, logger);
28+
provider = new MemoryMapElevationProvider(hosting, logger, new CachingService());
2829
}
2930

3031

README.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,3 +21,8 @@ You can choose between two types of elevation providers by specifying the `ELEVA
2121
`MMAP` - For memory mapped elevation provider
2222
Default - In memory elevation provider
2323

24+
The memory mapped elevation provider is using a cache to store the files.
25+
You can change the cache sliding window timeout using `CACHE_SLIDING_WINDOW` environment variable.
26+
The default is 30 and the units are minutes.
27+
This means that if a file is not used in the last 30 minutes it will be evacuated from the cache.
28+

src/ElevationWebApi.csproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
</PropertyGroup>
1515

1616
<ItemGroup>
17+
<PackageReference Include="LazyCache.AspNetCore" Version="2.4.0" />
1718
<PackageReference Include="NetTopologySuite" Version="2.5.0" />
1819
<PackageReference Include="SharpZipLib" Version="1.3.3" />
1920
<PackageReference Include="Swashbuckle.AspNetCore" Version="6.5.0" />

src/MemoryMapElevationProvider.cs

Lines changed: 31 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
11
using System;
2-
using System.Collections.Concurrent;
32
using System.IO;
43
using System.IO.MemoryMappedFiles;
54
using System.Linq;
65
using System.Threading.Tasks;
6+
using LazyCache;
77
using Microsoft.AspNetCore.Hosting;
8+
using Microsoft.Extensions.Caching.Memory;
89
using Microsoft.Extensions.FileProviders;
910
using Microsoft.Extensions.Logging;
1011
using NetTopologySuite.Geometries;
@@ -20,18 +21,22 @@ public class MemoryMapElevationProvider : IElevationProvider
2021
{
2122
private readonly ILogger<MemoryMapElevationProvider> _logger;
2223
private readonly IFileProvider _fileProvider;
23-
private readonly ConcurrentDictionary<Coordinate, Task<FileAndSamples>> _mappedFilesCache;
24+
private readonly IAppCache _appCache;
25+
private readonly int _cacheSlidingWindowTimeInMinutes;
2426

2527
/// <summary>
2628
/// Constructor
2729
/// </summary>
2830
/// <param name="webHostEnvironment"></param>
2931
/// <param name="logger"></param>
30-
public MemoryMapElevationProvider(IWebHostEnvironment webHostEnvironment, ILogger<MemoryMapElevationProvider> logger)
32+
/// <param name="appCache"></param>
33+
public MemoryMapElevationProvider(IWebHostEnvironment webHostEnvironment, ILogger<MemoryMapElevationProvider> logger, IAppCache appCache)
3134
{
3235
_logger = logger;
36+
_appCache = appCache;
3337
_fileProvider = webHostEnvironment.ContentRootFileProvider;
34-
_mappedFilesCache = new();
38+
var cacheSlidingWindow = Environment.GetEnvironmentVariable("CACHE_SLIDING_WINDOW");
39+
_cacheSlidingWindowTimeInMinutes = string.IsNullOrWhiteSpace(cacheSlidingWindow) ? 30 : int.Parse(cacheSlidingWindow);
3540
}
3641

3742
/// <summary>
@@ -54,19 +59,31 @@ public Task<double[]> GetElevation(double[][] latLngs)
5459
var tasks = latLngs.Select(async latLng =>
5560
{
5661
var key = new Coordinate(Math.Floor(latLng[0]), Math.Floor(latLng[1]));
57-
if (!_mappedFilesCache.ContainsKey(key))
62+
var info = await _appCache.GetOrAddAsync(key.ToString(), () =>
5863
{
59-
var filePath = Path.Join(ElevationHelper.ELEVATION_CACHE, ElevationHelper.KeyToFileName(key));
60-
var fileInfo = _fileProvider.GetFileInfo(filePath);
61-
if (!fileInfo.Exists)
64+
return Task.Run(() =>
6265
{
63-
return 0;
64-
}
65-
_logger.LogInformation($"Loading {fileInfo.PhysicalPath} into memory mapped cache");
66-
_mappedFilesCache[key] = Task.Run(() => new FileAndSamples(MemoryMappedFile.CreateFromFile(fileInfo.PhysicalPath!, FileMode.Open), ElevationHelper.SamplesFromLength(fileInfo.Length)));
67-
}
68-
var info = await _mappedFilesCache[key];
66+
var filePath = Path.Join(ElevationHelper.ELEVATION_CACHE, ElevationHelper.KeyToFileName(key));
67+
var fileInfo = _fileProvider.GetFileInfo(filePath);
68+
if (!fileInfo.Exists)
69+
{
70+
_logger.LogWarning($"Missing hgt file for {key}");
71+
return new FileAndSamples(null, 0);
72+
}
73+
74+
_logger.LogInformation($"Loading {fileInfo.PhysicalPath} into memory mapped cache");
75+
return new FileAndSamples(
76+
MemoryMappedFile.CreateFromFile(fileInfo.PhysicalPath!, FileMode.Open),
77+
ElevationHelper.SamplesFromLength(fileInfo.Length));
78+
});
79+
80+
}, TimeSpan.FromMinutes(_cacheSlidingWindowTimeInMinutes));
6981

82+
if (info.File == null)
83+
{
84+
return 0;
85+
}
86+
7087
var exactLocation = new Coordinate(Math.Abs(latLng[0] - key.X) * (info.Samples - 1),
7188
(1 - Math.Abs(latLng[1] - key.Y)) * (info.Samples - 1));
7289

src/Startup.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ public Startup(IConfiguration configuration)
3636
/// <param name="services"></param>
3737
public void ConfigureServices(IServiceCollection services)
3838
{
39+
services.AddLazyCache();
3940
if (Environment.GetEnvironmentVariable("ELEVATION_PROVIDER") == "MMAP")
4041
{
4142
services.AddSingleton<IElevationProvider, MemoryMapElevationProvider>();

0 commit comments

Comments
 (0)