132 lines
		
	
	
		
			4.4 KiB
		
	
	
	
		
			C#
		
	
	
	
	
	
			
		
		
	
	
			132 lines
		
	
	
		
			4.4 KiB
		
	
	
	
		
			C#
		
	
	
	
	
	
| using MareSynchronos.API.Routes;
 | |
| using MareSynchronosShared.Services;
 | |
| using MareSynchronosShared.Utils;
 | |
| using System.Net.Http.Headers;
 | |
| 
 | |
| namespace MareSynchronosStaticFilesServer.Services;
 | |
| 
 | |
| // Notify distribution server of file hashes downloaded via shards, so they are not prematurely purged from its cold cache
 | |
| public class ShardTouchMessageService : ITouchHashService
 | |
| {
 | |
|     private readonly ILogger<ShardTouchMessageService> _logger;
 | |
|     private readonly ServerTokenGenerator _tokenGenerator;
 | |
|     private readonly IConfigurationService<StaticFilesServerConfiguration> _configuration;
 | |
|     private readonly HttpClient _httpClient;
 | |
|     private readonly Uri _remoteCacheSourceUri;
 | |
|     private readonly HashSet<string> _touchHashSet = new();
 | |
|     private readonly ColdTouchHashService _nestedService = null;
 | |
| 
 | |
|     private CancellationTokenSource _touchmsgCts;
 | |
| 
 | |
|     public ShardTouchMessageService(ILogger<ShardTouchMessageService> logger, ILogger<ColdTouchHashService> nestedLogger,
 | |
|         ServerTokenGenerator tokenGenerator, IConfigurationService<StaticFilesServerConfiguration> configuration)
 | |
|     {
 | |
|         _logger = logger;
 | |
|         _tokenGenerator = tokenGenerator;
 | |
|         _configuration = configuration;
 | |
|         _remoteCacheSourceUri = _configuration.GetValueOrDefault<Uri>(nameof(StaticFilesServerConfiguration.DistributionFileServerAddress), null);
 | |
|         _httpClient = new();
 | |
|         _httpClient.DefaultRequestHeaders.UserAgent.Add(new ProductInfoHeaderValue("MareSynchronosServer", "1.0.0.0"));
 | |
| 
 | |
|         if (configuration.GetValueOrDefault(nameof(StaticFilesServerConfiguration.UseColdStorage), false))
 | |
|         {
 | |
|             _nestedService = new ColdTouchHashService(nestedLogger, configuration);
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     public Task StartAsync(CancellationToken cancellationToken)
 | |
|     {
 | |
|         if (_remoteCacheSourceUri == null)
 | |
|             return Task.CompletedTask;
 | |
| 
 | |
|         _logger.LogInformation("Touch Message Service started");
 | |
| 
 | |
|         _touchmsgCts = new();
 | |
| 
 | |
|         _ = TouchMessageTask(_touchmsgCts.Token);
 | |
| 
 | |
|         return Task.CompletedTask;
 | |
|     }
 | |
| 
 | |
|     public Task StopAsync(CancellationToken cancellationToken)
 | |
|     {
 | |
|         if (_remoteCacheSourceUri == null)
 | |
|             return Task.CompletedTask;
 | |
| 
 | |
|         _touchmsgCts.Cancel();
 | |
| 
 | |
|         return Task.CompletedTask;
 | |
|     }
 | |
| 
 | |
|     private async Task SendTouches(IEnumerable<string> hashes)
 | |
|     {
 | |
|         var mainUrl = _remoteCacheSourceUri;
 | |
|         var path = new Uri(mainUrl, MareFiles.Distribution + "/touch");
 | |
|         using HttpRequestMessage msg = new()
 | |
|         {
 | |
|             RequestUri = path
 | |
|         };
 | |
|         msg.Headers.Authorization = new AuthenticationHeaderValue("Bearer", _tokenGenerator.Token);
 | |
|         msg.Method = HttpMethod.Post;
 | |
|         msg.Content = JsonContent.Create(hashes);
 | |
|         if (_configuration.GetValueOrDefault(nameof(StaticFilesServerConfiguration.DistributionFileServerForceHTTP2), false))
 | |
|         {
 | |
|             msg.Version = new Version(2, 0);
 | |
|             msg.VersionPolicy = HttpVersionPolicy.RequestVersionExact;
 | |
|         }
 | |
| 
 | |
|         _logger.LogDebug("Sending remote touch to {path}", path);
 | |
|         try
 | |
|         {
 | |
|             using var result = await _httpClient.SendAsync(msg).ConfigureAwait(false);
 | |
|         }
 | |
|         catch (Exception ex)
 | |
|         {
 | |
|             _logger.LogError(ex, "Failure to send touches for {hashChunk}", hashes);
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     private async Task TouchMessageTask(CancellationToken ct)
 | |
|     {
 | |
|         List<string> hashes;
 | |
| 
 | |
|         while (!ct.IsCancellationRequested)
 | |
|         {
 | |
|             try
 | |
|             {
 | |
|                 lock (_touchHashSet)
 | |
|                 {
 | |
|                     hashes = _touchHashSet.ToList();
 | |
|                     _touchHashSet.Clear();
 | |
|                 }
 | |
|                 if (hashes.Count > 0)
 | |
|                     await SendTouches(hashes);
 | |
|                 await Task.Delay(TimeSpan.FromSeconds(60), ct).ConfigureAwait(false);
 | |
|             }
 | |
|             catch (Exception e)
 | |
|             {
 | |
|                 _logger.LogError(e, "Error during touch message task");
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         lock (_touchHashSet)
 | |
|         {
 | |
|             hashes = _touchHashSet.ToList();
 | |
|             _touchHashSet.Clear();
 | |
|         }
 | |
|         if (hashes.Count > 0)
 | |
|             await SendTouches(hashes);
 | |
|     }
 | |
| 
 | |
|     public void TouchColdHash(string hash)
 | |
|     {
 | |
|         if (_nestedService != null)
 | |
|             _nestedService.TouchColdHash(hash);
 | |
| 
 | |
|         lock (_touchHashSet)
 | |
|         {
 | |
|             _touchHashSet.Add(hash);
 | |
|         }
 | |
|     }
 | |
| }
 | 
