diff --git a/src/Server/Database.cs b/src/Server/Database.cs index e5616c1..5c73269 100644 --- a/src/Server/Database.cs +++ b/src/Server/Database.cs @@ -1,54 +1,87 @@ using System.Collections.Concurrent; -public class Database +public class Database : IDisposable { - private ConcurrentDictionary _data = new(); - private ConcurrentDictionary _ex = new(); + private readonly ConcurrentDictionary _data = new(); + private readonly ConcurrentDictionary _ex = new(); + private readonly object _lock = new(); + private readonly object _cleanLock = new(); + private CancellationTokenSource _cts = new(); - public Database(long cleanupInterval = 1000) + public Database() { - System.Timers.Timer timer = new(cleanupInterval); - timer.Elapsed += (sender, e) => Clean(); - timer.AutoReset = true; - timer.Start(); + _ = Task.Run(Clean); } public void Set(string key, string value) { - _data[key] = value; - _ex.Remove(key, out _); + lock (_lock) + { + _data[key] = value; + _ex.Remove(key, out _); + } } public void Set(string key, string value, long ex) { - _data[key] = value; - _ex[key] = DateTimeOffset.UtcNow.AddMilliseconds(ex); + lock (_lock) + { + _data[key] = value; + _ex[key] = DateTimeOffset.UtcNow.AddMilliseconds(ex); + } } public void Set(string key, long ex) { - if (_data.ContainsKey(key)) - _ex[key] = DateTimeOffset.UtcNow.AddMilliseconds(ex); + lock (_lock) + { + if (_data.ContainsKey(key)) + _ex[key] = DateTimeOffset.UtcNow.AddMilliseconds(ex); + } } public void Del(string key) { - _data.Remove(key, out _); - _ex.Remove(key, out _); + lock (_lock) + { + _data.Remove(key, out _); + _ex.Remove(key, out _); + } } public string? Get(string key) { - if (_ex.TryGetValue(key, out var expire) && expire < DateTimeOffset.UtcNow) - Del(key); + lock (_lock) + { + if (_ex.TryGetValue(key, out var expire) && expire < DateTimeOffset.UtcNow) + Del(key); - return _data.TryGetValue(key, out var value) ? value : null; + return _data.TryGetValue(key, out var value) ? value : null; + } } + public void Lock() => Monitor.Enter(_cleanLock); + + public void Unlock() => Monitor.Exit(_cleanLock); + private void Clean() { - foreach (var (key, expire) in _ex) - if (expire < DateTimeOffset.UtcNow) - Del(key); + while (!_cts.Token.IsCancellationRequested) + { + foreach (var (key, expire) in _ex) + { + if (expire < DateTimeOffset.UtcNow) + lock (_cleanLock) Del(key); + } + } + } + + public void Dispose() + { + _cts.Cancel(); + _cts.Dispose(); + _data.Clear(); + _ex.Clear(); + GC.SuppressFinalize(this); } } \ No newline at end of file diff --git a/src/Server/Program.cs b/src/Server/Program.cs index b329bee..328c3d6 100644 --- a/src/Server/Program.cs +++ b/src/Server/Program.cs @@ -2,6 +2,6 @@ if (args.Length > 0) port = int.Parse(args[0]); -var database = new Database(15000); +var database = new Database(); var server = new Server(port, database); server.Run(); \ No newline at end of file diff --git a/tests/Server.UnitTests/Database.cs b/tests/Server.UnitTests/Database.cs index 5e8e27f..88a315b 100644 --- a/tests/Server.UnitTests/Database.cs +++ b/tests/Server.UnitTests/Database.cs @@ -12,7 +12,7 @@ public class DatabaseTests [InlineData("", "")] public void SetGet(string key, string value) { - var db = new Database(); + using var db = new Database(); db.Set(key, value); var result = db.Get(key); Assert.Equal(value, result); @@ -23,7 +23,7 @@ public void SetGet(string key, string value) [InlineData("key2", "value", 10)] public void SetGetEx(string key, string value, long ex) { - var db = new Database(); + using var db = new Database(); db.Set(key, value, ex); var result = db.Get(key); Assert.Equal(value, result); @@ -37,7 +37,7 @@ public void SetGetEx(string key, string value, long ex) [Fact] public void AutoClean() { - var db = new Database(10); + using var db = new Database(); db.Set("key1", "value"); db.Set("key2", "value", 50); db.Set("key3", "value", 20);