FazBrowse GitHub Viewer | Trending |
URL:
| Home
Tools: [Download Repo ZIP]   [Original HTTPS Page]

Performance improves for HTTP file cache · include76/NetCoreServer@c7053ee · GitHub

Repository navigation

Commit c7053ee

Browse files
committed
Performance improves for HTTP file cache
1 parent c31e3a5 commit c7053ee

3 files changed

Lines changed: 104 additions & 49 deletions

File tree

‎.appveyor.yml‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
# Specify version format
2-
version: "5.0.14.{build}"
2+
version: "5.0.15.{build}"
33

44
# Image to use
55
image: Visual Studio 2019

‎source/NetCoreServer/FileCache.cs‎

Lines changed: 102 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
using System.Collections.Generic;
33
using System.IO;
44
using System.Text;
5+
using System.Threading;
56
using System.Web;
67

78
namespace NetCoreServer
@@ -21,11 +22,12 @@ public class FileCache : IDisposable
2122
/// <summary>
2223
/// Is the file cache empty?
2324
/// </summary>
24-
public bool Empty { get { lock (_lock) return _entriesByKey.Count == 0; } }
25+
public bool Empty => _entriesByKey.Count == 0;
26+
2527
/// <summary>
2628
/// Get the file cache size
2729
/// </summary>
28-
public int Size { get { lock (_lock) return _entriesByKey.Count; } }
30+
public int Size => _entriesByKey.Count;
2931

3032
/// <summary>
3133
/// Add a new cache value with the given timeout into the file cache
@@ -36,7 +38,7 @@ public class FileCache : IDisposable
3638
/// <returns>'true' if the cache value was added, 'false' if the given key was not added</returns>
3739
public bool Add(string key, byte[] value, TimeSpan timeout = new TimeSpan())
3840
{
39-
lock (_lock)
41+
using (new WriteLock(_lockEx))
4042
{
4143
// Try to find and remove the previous key
4244
_entriesByKey.Remove(key);
@@ -48,14 +50,14 @@ public class FileCache : IDisposable
4850
}
4951
}
5052

51-
/// <summary>
53+
/// <summary>
5254
/// Try to find the cache value by the given key
5355
/// </summary>
5456
/// <param name="key">Key to find</param>
5557
/// <returns>'true' and cache value if the cache value was found, 'false' if the given key was not found</returns>
5658
public (bool, byte[]) Find(string key)
5759
{
58-
lock (_lock)
60+
using (new ReadLock(_lockEx))
5961
{
6062
// Try to find the given key
6163
if (!_entriesByKey.TryGetValue(key, out var cacheValue))
@@ -72,7 +74,7 @@ public class FileCache : IDisposable
7274
/// <returns>'true' if the cache value was removed, 'false' if the given key was not found</returns>
7375
public bool Remove(string key)
7476
{
75-
lock (_lock)
77+
using (new WriteLock(_lockEx))
7678
{
7779
return _entriesByKey.Remove(key);
7880
}
@@ -98,16 +100,18 @@ public bool Remove(string key)
98100
// Try to find and remove the previous path
99101
RemovePathInternal(path);
100102

101-
// Insert the cache path
102-
if (!InsertPathInternal(path, prefix, timeout, handler))
103-
return false;
104-
105-
lock (_lock)
103+
using (new WriteLock(_lockEx))
106104
{
107105
// Add the given path to the cache
108106
_pathsByKey.Add(path, new FileCacheEntry(this, prefix, path, filter, handler, timeout));
107+
// Create entries by path map
108+
_entriesByPath[path] = new HashSet<string>();
109109
}
110110

111+
// Insert the cache path
112+
if (!InsertPathInternal(path, path, prefix, timeout, handler))
113+
return false;
114+
111115
return true;
112116
}
113117

@@ -118,7 +122,7 @@ public bool Remove(string key)
118122
/// <returns>'true' if the cache path was found, 'false' if the given path was not found</returns>
119123
public bool FindPath(string path)
120124
{
121-
lock (_lock)
125+
using (new ReadLock(_lockEx))
122126
{
123127
// Try to find the given key
124128
return _pathsByKey.ContainsKey(path);
@@ -140,14 +144,15 @@ public bool RemovePath(string path)
140144
/// </summary>
141145
public void Clear()
142146
{
143-
lock (_lock)
147+
using (new WriteLock(_lockEx))
144148
{
145149
// Stop all file system watchers
146150
foreach (var fileCacheEntry in _pathsByKey)
147151
fileCacheEntry.Value.StopWatcher();
148152

149153
// Clear all cache entries
150154
_entriesByKey.Clear();
155+
_entriesByPath.Clear();
151156
_pathsByKey.Clear();
152157
}
153158
}
@@ -156,7 +161,10 @@ public void Clear()
156161

157162
#region Cache implementation
158163

159-
private readonly object _lock = new object();
164+
private readonly ReaderWriterLockSlim _lockEx = new ReaderWriterLockSlim();
165+
private Dictionary<string, MemCacheEntry> _entriesByKey = new Dictionary<string, MemCacheEntry>();
166+
private Dictionary<string, HashSet<string>> _entriesByPath = new Dictionary<string, HashSet<string>>();
167+
private Dictionary<string, FileCacheEntry> _pathsByKey = new Dictionary<string, FileCacheEntry>();
160168

161169
private class MemCacheEntry
162170
{
@@ -244,7 +252,7 @@ private static void OnCreated(object sender, FileSystemEventArgs e, FileCache ca
244252
if (IsDirectory(file))
245253
return;
246254

247-
cache.InsertFileInternal(file, key, entry._timespan, entry._handler);
255+
cache.InsertFileInternal(entry._path, file, key, entry._timespan, entry._handler);
248256
}
249257

250258
private static void OnChanged(object sender, FileSystemEventArgs e, FileCache cache, FileCacheEntry entry)
@@ -262,22 +270,15 @@ private static void OnChanged(object sender, FileSystemEventArgs e, FileCache ca
262270
if (IsDirectory(file))
263271
return;
264272

265-
cache.InsertFileInternal(file, key, entry._timespan, entry._handler);
273+
cache.InsertFileInternal(entry._path, file, key, entry._timespan, entry._handler);
266274
}
267275

268276
private static void OnDeleted(object sender, FileSystemEventArgs e, FileCache cache, FileCacheEntry entry)
269277
{
270278
var key = e.FullPath.Replace(entry._path, entry._prefix);
271279
var file = e.FullPath;
272280

273-
// Skip missing files
274-
if (!File.Exists(file))
275-
return;
276-
// Skip directory updates
277-
if (IsDirectory(file))
278-
return;
279-
280-
cache.RemoveFileInternal(key);
281+
cache.RemoveFileInternal(entry._path, key);
281282
}
282283

283284
private static void OnRenamed(object sender, RenamedEventArgs e, FileCache cache, FileCacheEntry entry)
@@ -291,18 +292,15 @@ private static void OnRenamed(object sender, RenamedEventArgs e, FileCache cache
291292
if (!File.Exists(newFile))
292293
return;
293294
// Skip directory updates
294-
if (IsDirectory(oldFile) || IsDirectory(newFile))
295+
if (IsDirectory(newFile))
295296
return;
296297

297-
cache.RemoveFileInternal(oldKey);
298-
cache.InsertFileInternal(newFile, newKey, entry._timespan, entry._handler);
298+
cache.RemoveFileInternal(entry._path, oldKey);
299+
cache.InsertFileInternal(entry._path, newFile, newKey, entry._timespan, entry._handler);
299300
}
300301
};
301302

302-
private Dictionary<string, MemCacheEntry> _entriesByKey = new Dictionary<string, MemCacheEntry>();
303-
private Dictionary<string, FileCacheEntry> _pathsByKey = new Dictionary<string, FileCacheEntry>();
304-
305-
private bool InsertFileInternal(string file, string key, TimeSpan timeout, InsertHandler handler)
303+
private bool InsertFileInternal(string path, string file, string key, TimeSpan timeout, InsertHandler handler)
306304
{
307305
try
308306
{
@@ -314,23 +312,35 @@ private bool InsertFileInternal(string file, string key, TimeSpan timeout, Inser
314312
if (!handler(this, key, content, timeout))
315313
return false;
316314

315+
using (new WriteLock(_lockEx))
316+
{
317+
// Update entries by path map
318+
_entriesByPath[path].Add(key);
319+
}
320+
317321
return true;
318322
}
319323
catch (Exception) { return false; }
320324
}
321325

322-
private bool RemoveFileInternal(string key)
326+
private bool RemoveFileInternal(string path, string key)
323327
{
324328
try
325329
{
326330
key = key.Replace('\\', '/');
327331

332+
using (new WriteLock(_lockEx))
333+
{
334+
// Update entries by path map
335+
_entriesByPath[path].Remove(key);
336+
}
337+
328338
return Remove(key);
329339
}
330340
catch (Exception) { return false; }
331341
}
332342

333-
private bool InsertPathInternal(string path, string prefix, TimeSpan timeout, InsertHandler handler)
343+
private bool InsertPathInternal(string root, string path, string prefix, TimeSpan timeout, InsertHandler handler)
334344
{
335345
try
336346
{
@@ -342,7 +352,7 @@ private bool InsertPathInternal(string path, string prefix, TimeSpan timeout, In
342352
string key = keyPrefix + HttpUtility.UrlDecode(Path.GetFileName(item));
343353

344354
// Recursively insert sub-directory
345-
if (!InsertPathInternal(item, key, timeout, handler))
355+
if (!InsertPathInternal(root, item, key, timeout, handler))
346356
return false;
347357
}
348358

@@ -351,7 +361,7 @@ private bool InsertPathInternal(string path, string prefix, TimeSpan timeout, In
351361
string key = keyPrefix + HttpUtility.UrlDecode(Path.GetFileName(item));
352362

353363
// Insert file into the cache
354-
if (!InsertFileInternal(item, key, timeout, handler))
364+
if (!InsertFileInternal(root, item, key, timeout, handler))
355365
return false;
356366
}
357367

@@ -362,7 +372,7 @@ private bool InsertPathInternal(string path, string prefix, TimeSpan timeout, In
362372

363373
private bool RemovePathInternal(string path)
364374
{
365-
lock (_lock)
375+
using (new WriteLock(_lockEx))
366376
{
367377
// Try to find the given path
368378
if (!_pathsByKey.TryGetValue(path, out var cacheValue))
@@ -371,7 +381,12 @@ private bool RemovePathInternal(string path)
371381
// Stop the file system watcher
372382
cacheValue.StopWatcher();
373383

374-
// Erase cache path
384+
// Remove path entries
385+
foreach (var entryKey in _entriesByPath[path])
386+
_entriesByKey.Remove(entryKey);
387+
_entriesByPath.Remove(path);
388+
389+
// Remove cache path
375390
_pathsByKey.Remove(path);
376391

377392
return true;
@@ -394,16 +409,16 @@ public void Dispose()
394409

395410
protected virtual void Dispose(bool disposingManagedResources)
396411
{
397-
// The idea here is that Dispose(Boolean) knows whether it is
398-
// being called to do explicit cleanup (the Boolean is true)
399-
// versus being called due to a garbage collection (the Boolean
400-
// is false). This distinction is useful because, when being
401-
// disposed explicitly, the Dispose(Boolean) method can safely
402-
// execute code using reference type fields that refer to other
403-
// objects knowing for sure that these other objects have not been
404-
// finalized or disposed of yet. When the Boolean is false,
405-
// the Dispose(Boolean) method should not execute code that
406-
// refer to reference type fields because those objects may
412+
// The idea here is that Dispose(Boolean) knows whether it is
413+
// being called to do explicit cleanup (the Boolean is true)
414+
// versus being called due to a garbage collection (the Boolean
415+
// is false). This distinction is useful because, when being
416+
// disposed explicitly, the Dispose(Boolean) method can safely
417+
// execute code using reference type fields that refer to other
418+
// objects knowing for sure that these other objects have not been
419+
// finalized or disposed of yet. When the Boolean is false,
420+
// the Dispose(Boolean) method should not execute code that
421+
// refer to reference type fields because those objects may
407422
// have already been finalized."
408423

409424
if (!_disposed)
@@ -432,4 +447,44 @@ protected virtual void Dispose(bool disposingManagedResources)
432447

433448
#endregion
434449
}
450+
451+
/// <summary>
452+
/// Disposable lock class performs exit action on dispose operation.
453+
/// </summary>
454+
public class DisposableLock : IDisposable
455+
{
456+
private readonly Action _exitLock;
457+
458+
public DisposableLock(Action exitLock)
459+
{
460+
_exitLock = exitLock;
461+
}
462+
463+
public void Dispose()
464+
{
465+
_exitLock();
466+
}
467+
}
468+
469+
/// <summary>
470+
/// Read lock class enters read lock on construction and performs exit read lock on dispose.
471+
/// </summary>
472+
public class ReadLock : DisposableLock
473+
{
474+
public ReadLock(ReaderWriterLockSlim locker) : base(locker.ExitReadLock)
475+
{
476+
locker.EnterReadLock();
477+
}
478+
}
479+
480+
/// <summary>
481+
/// Write lock class enters write lock on construction and performs exit write lock on dispose.
482+
/// </summary>
483+
public class WriteLock : DisposableLock
484+
{
485+
public WriteLock(ReaderWriterLockSlim locker) : base(locker.ExitWriteLock)
486+
{
487+
locker.EnterWriteLock();
488+
}
489+
}
435490
}

‎source/NetCoreServer/NetCoreServer.csproj‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
<PropertyGroup>
44
<TargetFramework>net5.0</TargetFramework>
5-
<Version>5.0.14.0</Version>
5+
<Version>5.0.15.0</Version>
66
<Authors>Ivan Shynkarenka</Authors>
77
<Copyright>Copyright (c) 2019-2021 Ivan Shynkarenka</Copyright>
88
<RepositoryUrl>https://github.com/chronoxor/NetCoreServer</RepositoryUrl>

0 commit comments

Comments
 (0)

Back | FazBrowse Home | New Git URL