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:
parent
ad532b0106
commit
0007dc8690
|
@ -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":
|
||||
|
|
|
@ -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") });
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
{
|
||||
|
|
|
@ -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" } });
|
||||
|
|
|
@ -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)
|
||||
{
|
||||
|
|
|
@ -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":
|
||||
|
|
43
Teknik/Services/CacheCleanerService.cs
Normal file
43
Teknik/Services/CacheCleanerService.cs
Normal 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.");
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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
24
Utilities/CacheEntry.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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)
|
||||
{
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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()
|
||||
|
|
Loading…
Reference in New Issue
Block a user