1
0
mirror of https://git.teknik.io/Teknikode/Teknik.git synced 2023-08-02 14:16:22 +02:00

Fixed caching to be singleton w/ background cleaner

This commit is contained in:
Uncled1023 2022-05-28 20:32:31 -07:00
parent ad532b0106
commit 0007dc8690
12 changed files with 225 additions and 121 deletions

View File

@ -261,7 +261,7 @@ namespace Teknik.Areas.Admin.Controllers
[HttpPost]
[ValidateAntiForgeryToken]
public IActionResult DeleteData(string type, string id, [FromServices] IBackgroundTaskQueue queue)
public IActionResult DeleteData(string type, string id, [FromServices] IBackgroundTaskQueue queue, [FromServices] ObjectCache cache)
{
var context = new ControllerContext();
context.HttpContext = Request.HttpContext;
@ -271,11 +271,11 @@ namespace Teknik.Areas.Admin.Controllers
switch (type)
{
case "upload":
var uploadController = new Upload.Controllers.UploadController(_logger, _config, _dbContext, queue);
var uploadController = new Upload.Controllers.UploadController(_logger, _config, _dbContext, queue, cache);
uploadController.ControllerContext = context;
return uploadController.Delete(id);
case "paste":
var pasteController = new Paste.Controllers.PasteController(_logger, _config, _dbContext, queue);
var pasteController = new Paste.Controllers.PasteController(_logger, _config, _dbContext, queue, cache);
pasteController.ControllerContext = context;
return pasteController.Delete(id);
case "shortenedUrl":

View File

@ -26,12 +26,14 @@ namespace Teknik.Areas.Paste.Controllers
public class PasteController : DefaultController
{
private readonly IBackgroundTaskQueue _queue;
private readonly ObjectCache _cache;
public PasteController(ILogger<Logger> logger, Config config, TeknikEntities dbContext, IBackgroundTaskQueue queue) : base(logger, config, dbContext)
public PasteController(ILogger<Logger> logger, Config config, TeknikEntities dbContext, IBackgroundTaskQueue queue, ObjectCache cache) : base(logger, config, dbContext)
{
_queue = queue;
_cache = cache;
}
[AllowAnonymous]
[TrackPageView]
public IActionResult Index()
@ -46,7 +48,7 @@ namespace Teknik.Areas.Paste.Controllers
[TrackPageView]
public async Task<IActionResult> ViewPaste(string type, string url, string password)
{
Models.Paste paste = PasteHelper.GetPaste(_dbContext, url);
Models.Paste paste = PasteHelper.GetPaste(_dbContext, _cache, url);
if (paste != null)
{
ViewBag.Title = (string.IsNullOrEmpty(paste.Title)) ? "Untitled Paste" : paste.Title + " | Pastebin";
@ -62,12 +64,12 @@ namespace Teknik.Areas.Paste.Controllers
// Check Expiration
if (PasteHelper.CheckExpiration(paste))
{
PasteHelper.DeleteFile(_dbContext, _config, _logger, url);
PasteHelper.DeleteFile(_dbContext, _cache, _config, _logger, url);
return new StatusCodeResult(StatusCodes.Status404NotFound);
}
// Increment View Count
PasteHelper.IncrementViewCount(_queue, _config, url);
PasteHelper.IncrementViewCount(_queue, _cache, _config, url);
PasteViewModel model = new PasteViewModel();
model.Url = url;
@ -234,7 +236,7 @@ namespace Teknik.Areas.Paste.Controllers
[TrackPageView]
public async Task<IActionResult> Edit(string url, string password)
{
Models.Paste paste = PasteHelper.GetPaste(_dbContext, url);
Models.Paste paste = PasteHelper.GetPaste(_dbContext, _cache, url);
if (paste != null)
{
if (paste.User?.Username != User.Identity.Name)
@ -246,7 +248,7 @@ namespace Teknik.Areas.Paste.Controllers
// Check Expiration
if (PasteHelper.CheckExpiration(paste))
{
PasteHelper.DeleteFile(_dbContext, _config, _logger, url);
PasteHelper.DeleteFile(_dbContext, _cache, _config, _logger, url);
return new StatusCodeResult(StatusCodes.Status404NotFound);
}
@ -338,7 +340,7 @@ namespace Teknik.Areas.Paste.Controllers
{
try
{
Models.Paste paste = PasteHelper.GetPaste(_dbContext, model.Url);
Models.Paste paste = PasteHelper.GetPaste(_dbContext, _cache, model.Url);
if (paste != null)
{
if (paste.User?.Username != User.Identity.Name)
@ -396,7 +398,7 @@ namespace Teknik.Areas.Paste.Controllers
paste.Syntax = model.Syntax;
paste.DateEdited = DateTime.Now;
PasteHelper.ModifyPaste(_dbContext, paste);
PasteHelper.ModifyPaste(_dbContext, _cache, paste);
// Delete the old file
storageService.DeleteFile(oldFile);
@ -416,13 +418,13 @@ namespace Teknik.Areas.Paste.Controllers
[HttpOptions]
public IActionResult Delete(string id)
{
Models.Paste foundPaste = PasteHelper.GetPaste(_dbContext, id);
Models.Paste foundPaste = PasteHelper.GetPaste(_dbContext, _cache, id);
if (foundPaste != null)
{
if (foundPaste.User?.Username == User.Identity.Name ||
User.IsInRole("Admin"))
{
PasteHelper.DeleteFile(_dbContext, _config, _logger, id);
PasteHelper.DeleteFile(_dbContext, _cache, _config, _logger, id);
return Json(new { result = true, redirect = Url.SubRouteUrl("p", "Paste.Index") });
}

View File

@ -19,9 +19,6 @@ namespace Teknik.Areas.Paste
{
public static class PasteHelper
{
private static object _cacheLock = new object();
private readonly static ObjectCache _pasteCache = new ObjectCache(300);
public static async Task<Models.Paste> CreatePaste(Config config, TeknikEntities db, string content, string title = "", string syntax = "text", ExpirationUnit expireUnit = ExpirationUnit.Never, int expireLength = 1, string password = "")
{
Models.Paste paste = new Models.Paste();
@ -127,7 +124,7 @@ namespace Teknik.Areas.Paste
}
}
public static void IncrementViewCount(IBackgroundTaskQueue queue, Config config, string url)
public static void IncrementViewCount(IBackgroundTaskQueue queue, ObjectCache cache, Config config, string url)
{
// Fire and forget updating of the download count
queue.QueueBackgroundWorkItem(async token =>
@ -139,34 +136,31 @@ namespace Teknik.Areas.Paste
using (TeknikEntities db = new TeknikEntities(optionsBuilder.Options))
{
var paste = GetPaste(db, url);
var paste = GetPaste(db, cache, url);
if (paste != null)
{
paste.Views++;
ModifyPaste(db, paste);
ModifyPaste(db, cache, paste);
}
}
});
});
}
public static Models.Paste GetPaste(TeknikEntities db, string url)
public static Models.Paste GetPaste(TeknikEntities db, ObjectCache cache, string url)
{
lock (_cacheLock)
{
var paste = _pasteCache.GetObject(url, (key) => db.Pastes.FirstOrDefault(up => up.Url == key));
var paste = cache.AddOrGetObject(url, (key) => db.Pastes.FirstOrDefault(up => up.Url == key));
if (paste != null &&
!db.Exists(paste))
db.Attach(paste);
if (paste != null &&
!db.Exists(paste))
db.Attach(paste);
return paste;
}
return paste;
}
public static void DeleteFile(TeknikEntities db, Config config, ILogger<Logger> logger, string url)
public static void DeleteFile(TeknikEntities db, ObjectCache cache, Config config, ILogger<Logger> logger, string url)
{
var paste = GetPaste(db, url);
var paste = GetPaste(db, cache, url);
if (paste != null)
{
try
@ -185,19 +179,13 @@ namespace Teknik.Areas.Paste
}
// Remove from the cache
lock (_cacheLock)
{
_pasteCache.DeleteObject(url);
}
cache.DeleteObject<PasteConfig>(url);
}
public static void ModifyPaste(TeknikEntities db, Models.Paste paste)
public static void ModifyPaste(TeknikEntities db, ObjectCache cache, Models.Paste paste)
{
// Update the cache's copy
lock (_cacheLock)
{
_pasteCache.UpdateObject(paste.Url, paste);
}
cache.UpdateObject(paste.Url, paste);
if (paste != null)
{

View File

@ -29,12 +29,14 @@ namespace Teknik.Areas.Upload.Controllers
public class UploadController : DefaultController
{
private readonly IBackgroundTaskQueue _queue;
private readonly ObjectCache _cache;
public UploadController(ILogger<Logger> logger, Config config, TeknikEntities dbContext, IBackgroundTaskQueue queue) : base(logger, config, dbContext)
public UploadController(ILogger<Logger> logger, Config config, TeknikEntities dbContext, IBackgroundTaskQueue queue, ObjectCache cache) : base(logger, config, dbContext)
{
_queue = queue;
_cache = cache;
}
[HttpGet]
[AllowAnonymous]
[TrackPageView]
@ -226,18 +228,18 @@ namespace Teknik.Areas.Upload.Controllers
long contentLength = 0;
DateTime dateUploaded = new DateTime();
var upload = UploadHelper.GetUpload(_dbContext, file);
var upload = UploadHelper.GetUpload(_dbContext, _cache, file);
if (upload != null)
{
// Check Expiration
if (UploadHelper.CheckExpiration(upload))
{
UploadHelper.DeleteFile(_dbContext, _config, _logger, file);
UploadHelper.DeleteFile(_dbContext, _cache, _config, _logger, file);
return new StatusCodeResult(StatusCodes.Status404NotFound);
}
// Increment the download count for this upload
UploadHelper.IncrementDownloadCount(_queue, _config, file);
UploadHelper.IncrementDownloadCount(_queue, _cache, _config, file);
fileName = upload.FileName;
url = upload.Url;
@ -412,13 +414,13 @@ namespace Teknik.Areas.Upload.Controllers
{
if (_config.UploadConfig.DownloadEnabled)
{
Models.Upload upload = UploadHelper.GetUpload(_dbContext, file);
Models.Upload upload = UploadHelper.GetUpload(_dbContext, _cache, file);
if (upload != null)
{
// Check Expiration
if (UploadHelper.CheckExpiration(upload))
{
UploadHelper.DeleteFile(_dbContext, _config, _logger, file);
UploadHelper.DeleteFile(_dbContext, _cache, _config, _logger, file);
return Json(new { error = new { message = "File Does Not Exist" } });
}
@ -454,14 +456,14 @@ namespace Teknik.Areas.Upload.Controllers
public IActionResult DeleteByKey(string file, string key)
{
ViewBag.Title = "File Delete | " + file ;
Models.Upload upload = UploadHelper.GetUpload(_dbContext, file);
Models.Upload upload = UploadHelper.GetUpload(_dbContext, _cache, file);
if (upload != null)
{
DeleteViewModel model = new DeleteViewModel();
model.File = file;
if (!string.IsNullOrEmpty(upload.DeleteKey) && upload.DeleteKey == key)
{
UploadHelper.DeleteFile(_dbContext, _config, _logger, file);
UploadHelper.DeleteFile(_dbContext, _cache, _config, _logger, file);
model.Deleted = true;
}
else
@ -476,13 +478,13 @@ namespace Teknik.Areas.Upload.Controllers
[HttpPost]
public IActionResult GenerateDeleteKey(string file)
{
Models.Upload upload = UploadHelper.GetUpload(_dbContext, file);
Models.Upload upload = UploadHelper.GetUpload(_dbContext, _cache, file);
if (upload != null)
{
if (upload.User?.Username == User.Identity.Name ||
User.IsInRole("Admin"))
{
var delKey = UploadHelper.GenerateDeleteKey(_dbContext, _config, file);
var delKey = UploadHelper.GenerateDeleteKey(_dbContext, _cache, _config, file);
return Json(new { result = new { url = Url.SubRouteUrl("u", "Upload.DeleteByKey", new { file = file, key = delKey }) } });
}
return Json(new { error = new { message = "You do not have permission to delete this Upload" } });
@ -494,13 +496,13 @@ namespace Teknik.Areas.Upload.Controllers
[HttpOptions]
public IActionResult Delete(string id)
{
Models.Upload foundUpload = UploadHelper.GetUpload(_dbContext, id);
Models.Upload foundUpload = UploadHelper.GetUpload(_dbContext, _cache, id);
if (foundUpload != null)
{
if (foundUpload.User?.Username == User.Identity.Name ||
User.IsInRole("Admin"))
{
UploadHelper.DeleteFile(_dbContext, _config, _logger, id);
UploadHelper.DeleteFile(_dbContext, _cache, _config, _logger, id);
return Json(new { result = true });
}
return Json(new { error = new { message = "You do not have permission to delete this Upload" } });

View File

@ -19,9 +19,6 @@ namespace Teknik.Areas.Upload
{
public static class UploadHelper
{
private static object _cacheLock = new object();
private readonly static ObjectCache _uploadCache = new ObjectCache(300);
public static async Task<Models.Upload> SaveFile(TeknikEntities db, Config config, Stream file, string contentType, long contentLength, bool encrypt, ExpirationUnit expirationUnit, int expirationLength)
{
return await SaveFile(db, config, file, contentType, contentLength, encrypt, expirationUnit, expirationLength, string.Empty, null, null, 256, 128);
@ -128,14 +125,14 @@ namespace Teknik.Areas.Upload
return upload;
}
public static string GenerateDeleteKey(TeknikEntities db, Config config, string url)
public static string GenerateDeleteKey(TeknikEntities db, ObjectCache cache, Config config, string url)
{
var upload = db.Uploads.FirstOrDefault(up => up.Url == url);
if (upload != null)
{
string delKey = StringHelper.RandomString(config.UploadConfig.DeleteKeyLength);
upload.DeleteKey = delKey;
ModifyUpload(db, upload);
ModifyUpload(db, cache, upload);
return delKey;
}
return null;
@ -151,7 +148,7 @@ namespace Teknik.Areas.Upload
return false;
}
public static void IncrementDownloadCount(IBackgroundTaskQueue queue, Config config, string url)
public static void IncrementDownloadCount(IBackgroundTaskQueue queue, ObjectCache cache, Config config, string url)
{
// Fire and forget updating of the download count
queue.QueueBackgroundWorkItem(async token =>
@ -163,34 +160,31 @@ namespace Teknik.Areas.Upload
using (TeknikEntities db = new TeknikEntities(optionsBuilder.Options))
{
var upload = GetUpload(db, url);
var upload = GetUpload(db, cache, url);
if (upload != null)
{
upload.Downloads++;
ModifyUpload(db, upload);
ModifyUpload(db, cache, upload);
}
}
});
});
}
public static Models.Upload GetUpload(TeknikEntities db, string url)
public static Models.Upload GetUpload(TeknikEntities db, ObjectCache cache, string url)
{
lock (_cacheLock)
{
var upload = _uploadCache.GetObject(url, (key) => db.Uploads.FirstOrDefault(up => up.Url == key));
var upload = cache.AddOrGetObject(url, (key) => db.Uploads.FirstOrDefault(up => up.Url == key));
if (upload != null &&
!db.Exists(upload))
db.Attach(upload);
if (upload != null &&
!db.Exists(upload))
db.Attach(upload);
return upload;
}
return upload;
}
public static void DeleteFile(TeknikEntities db, Config config, ILogger<Logger> logger, string url)
public static void DeleteFile(TeknikEntities db, ObjectCache cache, Config config, ILogger<Logger> logger, string url)
{
var upload = GetUpload(db, url);
var upload = GetUpload(db, cache, url);
if (upload != null)
{
try
@ -209,19 +203,13 @@ namespace Teknik.Areas.Upload
}
// Remove from the cache
lock (_cacheLock)
{
_uploadCache.DeleteObject(url);
}
cache.DeleteObject<UploadConfig>(url);
}
public static void ModifyUpload(TeknikEntities db, Models.Upload upload)
public static void ModifyUpload(TeknikEntities db, ObjectCache cache, Models.Upload upload)
{
// Update the cache's copy
lock (_cacheLock)
{
_uploadCache.UpdateObject(upload.Url, upload);
}
cache.UpdateObject(upload.Url, upload);
if (upload != null)
{

View File

@ -1408,7 +1408,7 @@ namespace Teknik.Areas.Users.Controllers
[HttpPost]
[ValidateAntiForgeryToken]
public IActionResult DeleteData(string type, string id, [FromServices] IBackgroundTaskQueue queue)
public IActionResult DeleteData(string type, string id, [FromServices] IBackgroundTaskQueue queue, [FromServices] ObjectCache cache)
{
var context = new ControllerContext();
context.HttpContext = Request.HttpContext;
@ -1418,11 +1418,11 @@ namespace Teknik.Areas.Users.Controllers
switch (type)
{
case "upload":
var uploadController = new Upload.Controllers.UploadController(_logger, _config, _dbContext, queue);
var uploadController = new Upload.Controllers.UploadController(_logger, _config, _dbContext, queue, cache);
uploadController.ControllerContext = context;
return uploadController.Delete(id);
case "paste":
var pasteController = new Paste.Controllers.PasteController(_logger, _config, _dbContext, queue);
var pasteController = new Paste.Controllers.PasteController(_logger, _config, _dbContext, queue, cache);
pasteController.ControllerContext = context;
return pasteController.Delete(id);
case "shortenedUrl":

View File

@ -0,0 +1,43 @@
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using System;
using System.Threading;
using System.Threading.Tasks;
using Teknik.Logging;
using Teknik.Utilities;
namespace Teknik.Services
{
public class CacheCleanerService : BackgroundService
{
private readonly ILogger<Logger> _logger;
public readonly ObjectCache _cache;
public CacheCleanerService(ObjectCache cache, ILogger<Logger> logger)
{
_cache = cache;
_logger = logger;
}
protected async override Task ExecuteAsync(CancellationToken cancellationToken)
{
_logger.LogInformation("Cache Cleaning Service is starting.");
while (!cancellationToken.IsCancellationRequested)
{
try
{
_cache.CleanCache();
await Task.Delay(new TimeSpan(0, 5, 0), cancellationToken);
}
catch (Exception ex)
{
_logger.LogError(ex, $"Error occurred cleaning cache.");
}
}
_logger.LogInformation("Cache Cleaning Service is stopping.");
}
}
}

View File

@ -101,6 +101,8 @@ namespace Teknik
services.AddHostedService<TaskQueueService>();
services.AddSingleton<IBackgroundTaskQueue, BackgroundTaskQueue>();
services.AddSingleton<ObjectCache, ObjectCache>(c => new ObjectCache(300));
services.AddHostedService<CacheCleanerService>();
services.AddScoped<IErrorController, ErrorController>();
// Add Tracking Filter scopes

24
Utilities/CacheEntry.cs Normal file
View File

@ -0,0 +1,24 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Teknik.Utilities
{
public class CacheEntry
{
public bool RollingExpiration { get; set; }
public TimeSpan CacheTime { get; set; }
public DateTime LastUpdate { get; set; }
public object Data { get; set; }
public CacheEntry(bool rollingExpiration, TimeSpan cacheTime, DateTime lastUpdate, object data)
{
RollingExpiration = rollingExpiration;
CacheTime = cacheTime;
LastUpdate = lastUpdate;
Data = data;
}
}
}

View File

@ -15,9 +15,9 @@ namespace Teknik.Utilities.Cryptography
public AesCounterMode(PooledArray initialCounter)
{
if (initialCounter == null) throw new ArgumentNullException("counter");
if (initialCounter.Array.Length != _BlockSize)
if (initialCounter.Length != _BlockSize)
throw new ArgumentException(String.Format("Counter size must be same as block size (actual: {0}, expected: {1})",
initialCounter.Array.Length, _BlockSize));
initialCounter.Length, _BlockSize));
// Generate a new instance of the Aes Algorithm in ECB mode with no padding
_Algo = Aes.Create();
@ -82,7 +82,7 @@ namespace Teknik.Utilities.Cryptography
}
set
{
if (value >= 0 && value < _EncryptedCounter.Array.Length)
if (value >= 0 && value < _EncryptedCounter.Length)
{
_CounterPosition = value;
}
@ -97,14 +97,14 @@ namespace Teknik.Utilities.Cryptography
if (initialCounter == null) throw new ArgumentNullException("counter");
// Check lengths
if (initialCounter.Array.Length != symmetricAlgorithm.BlockSize / 8)
if (initialCounter.Length != symmetricAlgorithm.BlockSize / 8)
throw new ArgumentException(String.Format("Counter size must be same as block size (actual: {0}, expected: {1})",
initialCounter.Array.Length, symmetricAlgorithm.BlockSize / 8));
initialCounter.Length, symmetricAlgorithm.BlockSize / 8));
_BlockSize = symmetricAlgorithm.BlockSize;
// Initialize Counter
_Counter = new PooledArray(initialCounter.Array.Length);
_Counter = new PooledArray(initialCounter.Length);
initialCounter.Array.CopyTo(_Counter.Array, 0);
// Initialize the encrypted counter
@ -144,7 +144,7 @@ namespace Teknik.Utilities.Cryptography
for (var i = 0; i < inputCount; i++)
{
// Encrypt the counter if we have reached the end, or
if (_CounterPosition >= _EncryptedCounter.Array.Length)
if (_CounterPosition >= _EncryptedCounter.Length)
{
//Reset current counter position
_CounterPosition = 0;
@ -178,7 +178,7 @@ namespace Teknik.Utilities.Cryptography
for (var i = 0; i < inputCount; i++)
{
// Encrypt the counter if we have reached the end, or
if (_CounterPosition >= _EncryptedCounter.Array.Length)
if (_CounterPosition >= _EncryptedCounter.Length)
{
//Reset current counter position
_CounterPosition = 0;
@ -203,7 +203,7 @@ namespace Teknik.Utilities.Cryptography
public void EncryptCounter()
{
// Encrypt the current counter to the encrypted counter
_CounterEncryptor.TransformBlock(_Counter.Array, 0, _Counter.Array.Length, _EncryptedCounter.Array, 0);
_CounterEncryptor.TransformBlock(_Counter.Array, 0, _Counter.Length, _EncryptedCounter.Array, 0);
}
public void ResetCounter()
@ -214,7 +214,7 @@ namespace Teknik.Utilities.Cryptography
public void IncrementCounter()
{
int j = _Counter.Array.Length;
int j = _Counter.Length;
while (--j >= 0 && ++_Counter.Array[j] == 0)
{
}

View File

@ -3,61 +3,112 @@ using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
namespace Teknik.Utilities
{
public class ObjectCache
{
private readonly static ConcurrentDictionary<string, Tuple<DateTime, object>> objectCache = new ConcurrentDictionary<string, Tuple<DateTime, object>>();
private readonly int _cacheSeconds;
private readonly int _defaultCacheTime;
private static object _cacheLock = new object();
public ObjectCache(int cacheSeconds)
private readonly static ConcurrentDictionary<string, CacheEntry> _objectCache = new ConcurrentDictionary<string, CacheEntry>();
public ObjectCache(int defaultCacheTime)
{
_cacheSeconds = cacheSeconds;
_defaultCacheTime = defaultCacheTime;
}
public T GetObject<T>(string key, Func<string, T> getObjectFunc)
public T AddOrGetObject<T>(string key, Func<string, T> getObjectFunc)
{
return AddOrGetObject(key, new TimeSpan(0, 0, _defaultCacheTime), getObjectFunc);
}
public T AddOrGetObject<T>(string key, TimeSpan cacheTime, Func<string, T> getObjectFunc)
{
T foundObject;
var cacheDate = DateTime.UtcNow;
if (objectCache.TryGetValue(key, out var result) &&
result.Item1 > cacheDate.Subtract(new TimeSpan(0, 0, _cacheSeconds)))
lock (_cacheLock)
{
return (T)result.Item2;
}
else
{
foundObject = getObjectFunc(key);
// Update the cache for this key
if (foundObject != null)
UpdateObject(key, foundObject, cacheDate);
if (_objectCache.TryGetValue(GenerateKey<T>(key), out var result) &&
CacheValid(result))
{
if (result.RollingExpiration)
{
result.LastUpdate = DateTime.UtcNow;
}
return (T)result.Data;
}
else
{
foundObject = getObjectFunc(key);
// Update the cache for this key
if (foundObject != null)
UpdateObject(key, foundObject, cacheTime);
}
}
return foundObject;
}
public void UpdateObject<T>(string key, T update)
public void UpdateObject<T>(string key, T data)
{
UpdateObject(key, update, DateTime.UtcNow);
UpdateObject(key, data, null, DateTime.UtcNow);
}
public void UpdateObject<T>(string key, T update, DateTime cacheTime)
public void UpdateObject<T>(string key, T data, TimeSpan cacheTime)
{
objectCache[key] = new Tuple<DateTime, object>(cacheTime, update);
UpdateObject(key, data, cacheTime, DateTime.UtcNow);
}
public void DeleteObject(string key)
public void UpdateObject<T>(string key, T data, TimeSpan? cacheTime, DateTime lastUpdate)
{
objectCache.TryRemove(key, out _);
if (_objectCache.TryGetValue(GenerateKey<T>(key), out var result))
{
result.Data = data;
if (cacheTime.HasValue)
result.CacheTime = cacheTime.Value;
result.LastUpdate = lastUpdate;
}
else
{
if (!cacheTime.HasValue)
cacheTime = new TimeSpan(0, 0, _defaultCacheTime);
_objectCache[GenerateKey<T>(key)] = CreateCacheEntry(data, cacheTime.Value, lastUpdate);
}
}
public bool CacheValid(string key)
public void DeleteObject<T>(string key)
{
if (objectCache.TryGetValue(key, out var result) &&
result.Item1 > DateTime.UtcNow.Subtract(new TimeSpan(0, 0, _cacheSeconds)))
return true;
return false;
_objectCache.TryRemove(GenerateKey<T>(key), out _);
}
public bool CacheValid(CacheEntry entry)
{
return entry.LastUpdate > DateTime.UtcNow.Subtract(entry.CacheTime);
}
public void CleanCache()
{
lock (_cacheLock)
{
foreach (var obj in _objectCache)
{
if (!CacheValid(obj.Value))
_objectCache.TryRemove(obj.Key, out _);
}
}
}
public string GenerateKey<T>(string key)
{
var typeKey = typeof(T).ToString();
return $"{typeKey}.{key}";
}
public CacheEntry CreateCacheEntry<T>(T data, TimeSpan cacheTime, DateTime lastUpdate)
{
return new CacheEntry(false, cacheTime, lastUpdate, data);
}
}
}

View File

@ -13,15 +13,19 @@ namespace Teknik.Utilities
public byte[] Array { get; private set; }
public readonly int Length;
public PooledArray(int size)
{
Array = _arrayPool.Rent(size);
Length = size;
}
public PooledArray(byte[] array)
{
Array = _arrayPool.Rent(array.Length);
array.CopyTo(Array, 0);
Length = array.Length;
}
public void Dispose()