Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add method to clear cache #15

Merged
merged 5 commits into from
Jan 30, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
64 changes: 64 additions & 0 deletions SqliteCache.Tests/ClearCacheTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
using System;
using Microsoft.Extensions.Caching.Distributed;
using Microsoft.Extensions.Logging;
using Microsoft.VisualStudio.TestTools.UnitTesting;

namespace NeoSmart.Caching.Sqlite.Tests
{
[TestClass]
public class ClearCacheTests : IDisposable
{
private readonly SqliteCacheOptions Configuration = new SqliteCacheOptions()
{
MemoryOnly = false,
CachePath = $"ClearCache-{Guid.NewGuid()}.db",
};

public void Dispose()
{
var logger = new TestLogger<ClearCacheTests>();
logger.LogInformation("Delete db at path {DbPath}", Configuration.CachePath);
try
{
System.IO.File.Delete(Configuration.CachePath);
}
catch(Exception ex)
{
logger.LogWarning(ex, "Unable to delete db file at {DbPath}", Configuration.CachePath);
}
}

private SqliteCache CreateDefault(bool persistent = false)
{
var logger = new TestLogger<SqliteCache>();
logger.LogInformation("Creating a connection to db {DbPath}", Configuration.CachePath);
var cacheDb = new SqliteCache(Configuration with { MemoryOnly = !persistent }, logger);

return cacheDb;
}

[TestMethod]
public void ItemsRemovedAfterClear()
{
using (var cache = CreateDefault(true))
{
var expiry = new DistributedCacheEntryOptions().SetAbsoluteExpiration(DateTimeOffset.UtcNow.AddDays(1));
cache.SetString("one", "foo", expiry);
cache.SetString("two", "bar", expiry);

Assert.AreEqual(cache.GetString("one"), "foo");
Assert.AreEqual(cache.GetString("two"), "bar");

// Test and check
cache.Clear();

var item1 = cache.Get("one");
Assert.IsNull(item1);

var item2 = cache.Get("two");
Assert.IsNull(item2);
}
}
}
}

81 changes: 56 additions & 25 deletions SqliteCache/DbCommandPool.cs
Original file line number Diff line number Diff line change
Expand Up @@ -50,65 +50,96 @@ public void Use(Operation type, Action<SqliteCommand> handler)
});
}

public R Use<R>(Operation type, Func<SqliteCommand, R> handler)
public R Use<R>(Func<SqliteConnection, R> handler)
{
if (!_connections.TryTake(out var db))
{
_logger.LogTrace("Adding a new connection to the connection pool", type);
_logger.LogTrace("Adding a new connection to the connection pool");
db = new SqliteConnection(_connectionString);
_logger.LogTrace("Opening connection to {SqliteCacheDbPath}", _connectionString);
db.Open();
}

var pool = _commands[(int)type];
if (!pool.TryTake(out var command))
{
_logger.LogTrace("Adding a new {DbCommand} command to the command pool", type);
command = new SqliteCommand(DbCommands.Commands[(int)type], db);
}

try
{
command.Connection = db;
return handler(command);
return handler(db);
}
finally
{
command.Parameters.Clear();
pool.Add(command);
_connections.Add(db);
}
}

public async Task<R> UseAsync<R>(Operation type, Func<SqliteCommand, Task<R>> handler)
public R Use<R>(Operation type, Func<SqliteCommand, R> handler)
{
return Use((conn) =>
{
var pool = _commands[(int)type];
if (!pool.TryTake(out var command))
{
_logger.LogTrace("Adding a new {DbCommand} command to the command pool", type);
command = new SqliteCommand(DbCommands.Commands[(int)type], conn);
}

try
{
command.Connection = conn;
return handler(command);
}
finally
{
command.Connection = null;
command.Parameters.Clear();
pool.Add(command);
}
});
}

public async Task<R> UseAsync<R>(Func<SqliteConnection, Task<R>> handler)
{
if (!_connections.TryTake(out var db))
{
_logger.LogTrace("Adding a new connection to the connection pool", type);
_logger.LogTrace("Adding a new connection to the connection pool");
db = new SqliteConnection(_connectionString);
_logger.LogTrace("Opening connection to {SqliteCacheDbPath}", _connectionString);
await db.OpenAsync();
}

var pool = _commands[(int)type];
if (!pool.TryTake(out var command))
{
_logger.LogTrace("Adding a new {DbCommand} command to the command pool", type);
command = new SqliteCommand(DbCommands.Commands[(int)type], db);
}

try
{
return await handler(command);
return await handler(db);
}
finally
{
command.Parameters.Clear();
pool.Add(command);
_connections.Add(db);
}
}

public Task<R> UseAsync<R>(Operation type, Func<SqliteCommand, Task<R>> handler)
{
return UseAsync(async (conn) =>
{
var pool = _commands[(int)type];
if (!pool.TryTake(out var command))
{
_logger.LogTrace("Adding a new {DbCommand} command to the command pool", type);
command = new SqliteCommand(DbCommands.Commands[(int)type], conn);
}

try
{
command.Connection = conn;
return await handler(command);
}
finally
{
command.Connection = null;
command.Parameters.Clear();
pool.Add(command);
}
});
}

public void Dispose()
{
foreach (var pool in _commands)
Expand Down
2 changes: 1 addition & 1 deletion SqliteCache/DbCommands.cs
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ static DbCommands()
{
Commands = new string[Count];

Commands[(int) Operation.Insert] =
Commands[(int)Operation.Insert] =
"INSERT OR REPLACE INTO cache (key, value, expiry, renewal) " +
"VALUES (@key, @value, @expiry, @renewal)";

Expand Down
34 changes: 27 additions & 7 deletions SqliteCache/SqliteCache.cs
Original file line number Diff line number Diff line change
Expand Up @@ -300,11 +300,11 @@ private async Task InitializeAsync(CancellationToken cancel)
}
}
#endif
#endregion
#endregion

public byte[] Get(string key)
{
return (byte[]) Commands.Use(Operation.Get, cmd =>
return (byte[])Commands.Use(Operation.Get, cmd =>
{
cmd.Parameters.AddWithValue("@key", key);
cmd.Parameters.AddWithValue("@now", DateTimeOffset.UtcNow.Ticks);
Expand All @@ -314,7 +314,7 @@ public byte[] Get(string key)

public async Task<byte[]> GetAsync(string key, CancellationToken cancel = default)
{
return (byte[]) (await Commands.UseAsync(Operation.Get, cmd =>
return (byte[])(await Commands.UseAsync(Operation.Get, cmd =>
{
cmd.Parameters.AddWithValue("@key", key);
cmd.Parameters.AddWithValue("@now", DateTimeOffset.UtcNow.Ticks);
Expand Down Expand Up @@ -388,6 +388,26 @@ private void CreateBulkInsert(DbCommand cmd, IEnumerable<KeyValuePair<string, by
cmd.CommandText = sb.ToString();
}

public void Clear()
{
Commands.Use(conn =>
{
using var cmd = new DbCommand("DELETE FROM cache WHERE 1=1;", conn);
cmd.ExecuteNonQuery();
return true;
});
}

public Task ClearAsync(CancellationToken cancel = default)
{
return Commands.UseAsync(async conn =>
{
using var cmd = new DbCommand("DELETE FROM cache WHERE 1=1;", conn);
await cmd.ExecuteNonQueryAsync(cancel);
return true;
});
}

private void AddExpirationParameters(DbCommand cmd, DistributedCacheEntryOptions options)
{
DateTimeOffset? expiry = null;
Expand All @@ -409,8 +429,8 @@ private void AddExpirationParameters(DbCommand cmd, DistributedCacheEntryOptions
expiry = (expiry ?? DateTimeOffset.UtcNow) + renewal;
}

cmd.Parameters.AddWithValue("@expiry", expiry?.Ticks ?? (object) DBNull.Value);
cmd.Parameters.AddWithValue("@renewal", renewal?.Ticks ?? (object) DBNull.Value);
cmd.Parameters.AddWithValue("@expiry", expiry?.Ticks ?? (object)DBNull.Value);
cmd.Parameters.AddWithValue("@renewal", renewal?.Ticks ?? (object)DBNull.Value);
}

public void Set(string key, byte[] value, DistributedCacheEntryOptions options)
Expand Down Expand Up @@ -463,7 +483,7 @@ public Task SetBulkAsync(IEnumerable<KeyValuePair<string, byte[]>> keyValues, Di

public void RemoveExpired()
{
var removed = (long) Commands.Use(Operation.RemoveExpired, cmd =>
var removed = (long)Commands.Use(Operation.RemoveExpired, cmd =>
{
cmd.Parameters.AddWithValue("@now", DateTimeOffset.UtcNow.Ticks);
return cmd.ExecuteScalar();
Expand All @@ -477,7 +497,7 @@ public void RemoveExpired()

public async Task RemoveExpiredAsync(CancellationToken cancel = default)
{
var removed = (long) (await Commands.UseAsync(Operation.RemoveExpired, cmd =>
var removed = (long)(await Commands.UseAsync(Operation.RemoveExpired, cmd =>
{
cmd.Parameters.AddWithValue("@now", DateTimeOffset.UtcNow.Ticks);
return cmd.ExecuteScalarAsync(cancel);
Expand Down