-
Notifications
You must be signed in to change notification settings - Fork 34
/
Copy pathClassicDiskCache.cs
159 lines (131 loc) · 6.99 KB
/
ClassicDiskCache.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
using System;
using System.Collections.Generic;
using System.IO;
using System.Threading;
using System.Threading.Tasks;
using Imazen.Common.Extensibility.ClassicDiskCache;
using Imazen.Common.Issues;
using Imazen.DiskCache.Cleanup;
using Microsoft.Extensions.Logging;
namespace Imazen.DiskCache
{
public class ClassicDiskCache
{
private readonly ClassicDiskCacheOptions options;
public ClassicDiskCache(ClassicDiskCacheOptions options, ILogger log)
{
this.Logger = log;
this.options = options;
this.options.MakeImmutable();
}
private ILogger Logger { get; }
private AsyncCustomDiskCache cache;
private CleanupManager cleaner;
/// <summary>
/// Returns true if the configured settings are valid and .NET (not NTFS) permissions will work.
/// </summary>
/// <returns></returns>
private bool IsConfigurationValid()
{
return !string.IsNullOrEmpty(options.PhysicalCacheDir) && options.Enabled;
}
private readonly object startSync = new object();
private volatile bool started;
/// <summary>
/// Returns true if the DiskCache instance is operational.
/// </summary>
private bool Started => started;
/// <summary>
/// Attempts to start the DiskCache using the current settings.
/// </summary>
// ReSharper disable once UnusedParameter.Global
public Task StartAsync(CancellationToken cancellationToken) {
if (!IsConfigurationValid()) throw new InvalidOperationException("DiskCache configuration invalid");
lock (startSync) {
if (started) return Task.CompletedTask;
if (!IsConfigurationValid()) throw new InvalidOperationException("DiskCache configuration invalid");
cache = new AsyncCustomDiskCache(Logger, options.PhysicalCacheDir, options.Subfolders, options.AsyncBufferSize);
//Init the cleanup worker
if (options.AutoClean) cleaner = new CleanupManager(Logger, cache, options.CleanupStrategy);
//If we're running with subfolders, enqueue the cache root for cleanup (after the 5 minute delay)
//so we don't eternally 'skip' files in the root or in other unused subfolders (since only 'accessed' subfolders are ever cleaned ).
cleaner?.CleanAll();
Logger?.LogInformation("DiskCache started successfully.");
//Started successfully
started = true;
return Task.CompletedTask;
}
}
/// <summary>
/// Cannot be restarted once stopped.
/// </summary>
/// <returns></returns>
// ReSharper disable once UnusedParameter.Global
public Task StopAsync(CancellationToken cancellationToken) {
cleaner?.Dispose();
cleaner = null;
return Task.CompletedTask;
}
public async Task<ICacheResult> GetOrCreate(string key, string fileExtension, AsyncWriteResult writeCallback)
{
//Cache the data to disk and return a path.
var r = await cache.GetCachedFile(key, fileExtension, writeCallback, options.CacheAccessTimeout, options.AsyncWrites);
if (r.Result == CacheQueryResult.Hit)
cleaner?.UsedFile(r.RelativePath, r.PhysicalPath);
return r;
}
private bool HasNtfsPermission(){
try {
if (!Directory.Exists(options.PhysicalCacheDir)) Directory.CreateDirectory(options.PhysicalCacheDir);
var testFile = Path.Combine(options.PhysicalCacheDir, "TestFile.txt");
File.WriteAllText(testFile, "You may delete this file - it is written and deleted just to verify permissions are configured correctly");
File.Delete(testFile);
return true;
} catch (Exception){
return false;
}
}
private string GetExecutingUser() {
try {
return Thread.CurrentPrincipal.Identity.Name;
} catch {
return "[Unknown - please check App Pool configuration]";
}
}
private bool CacheDriveOnNetwork()
{
string physicalCache = options.PhysicalCacheDir;
if (!string.IsNullOrEmpty(physicalCache))
{
return physicalCache.StartsWith("\\\\") || GetCacheDrive()?.DriveType == DriveType.Network;
}
return false;
}
private DriveInfo GetCacheDrive()
{
try
{
var drive = string.IsNullOrEmpty(options.PhysicalCacheDir) ? null : new DriveInfo(Path.GetPathRoot(options.PhysicalCacheDir));
return (drive?.IsReady == true) ? drive : null;
}
catch { return null; }
}
public IEnumerable<IIssue> GetIssues() {
var issues = new List<IIssue>();
if (cleaner != null) issues.AddRange(cleaner.GetIssues());
if (!HasNtfsPermission())
issues.Add(new Issue("DiskCache", "Not working: Your NTFS Security permissions are preventing the application from writing to the disk cache",
"Please give user " + GetExecutingUser() + " read and write access to directory \"" + options.PhysicalCacheDir + "\" to correct the problem. You can access NTFS security settings by right-clicking the aforementioned folder and choosing Properties, then Security.", IssueSeverity.ConfigurationError));
if (!Started && !options.Enabled) issues.Add(new Issue("DiskCache", "DiskCache has been disabled by DiskCache settings.", null, IssueSeverity.ConfigurationError));
//Warn user about setting hashModifiedDate=false in a web garden.
if (options.AsyncBufferSize < 1024 * 1024 * 2)
issues.Add(new Issue("DiskCache", "The asyncBufferSize should not be set below 2 megabytes (2097152). Found in the <diskcache /> element in Web.config.",
"A buffer that is too small will cause requests to be processed synchronously. Remember to set the value to at least 4x the maximum size of an output image.", IssueSeverity.ConfigurationError));
if (CacheDriveOnNetwork())
issues.Add(new Issue("DiskCache", "It appears that the cache directory is located on a network drive.",
"Both IIS and ASP.NET have trouble hosting websites with large numbers of folders over a network drive, such as a SAN. The cache will create " +
options.Subfolders.ToString() + " subfolders. If the total number of network-hosted folders exceeds 100, you should contact [email protected] and consult the documentation for details on configuring IIS and ASP.NET for this situation.", IssueSeverity.Warning));
return issues;
}
}
}