Skip to content
Open
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
29 changes: 29 additions & 0 deletions SqliteCache.Tests/BulkInsertTests.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Text;
using System.Threading.Tasks;
using Microsoft.Extensions.Caching.Distributed;
Expand Down Expand Up @@ -124,5 +125,33 @@ public async Task MultipleBulkCalls()
CollectionAssert.AreEqual(item2, DefaultEncoding.GetBytes("test two"));
}
}

[TestMethod]
public void LargeScaleBulkTests()
{
using (var cache = CreateDefault(true))
{
const int count = 100_000;

var testObject = new List<KeyValuePair<string, byte[]>>();
for (var n = 0; n < count; n++)
{
testObject.Add(new KeyValuePair<string, byte[]>($"item{n+1}", DefaultEncoding.GetBytes($"value{n+1}")));
}

cache.SetBulk(testObject, new DistributedCacheEntryOptions
{
AbsoluteExpiration = DateTimeOffset.UtcNow.AddDays(1)
});

var value1 = cache.Get("item1");
Assert.IsNotNull(value1);
CollectionAssert.AreEqual(DefaultEncoding.GetBytes("value1"), value1);

var lastValue = cache.Get($"item{count}");
Assert.IsNotNull(lastValue);
CollectionAssert.AreEqual(DefaultEncoding.GetBytes($"value{count}"), lastValue);
}
}
}
}
60 changes: 55 additions & 5 deletions SqliteCache/SqliteCache.cs
Original file line number Diff line number Diff line change
Expand Up @@ -390,11 +390,16 @@ private void CreateForSet(DbCommand cmd, string key, byte[] value, DistributedCa
cmd.Parameters.AddWithValue("@key", key);
cmd.Parameters.AddWithValue("@value", value);

AddExpirationParameters(cmd, options);
var expirationValues = GetExpirationValues(options);

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

private void CreateBulkInsert(DbCommand cmd, IEnumerable<KeyValuePair<string, byte[]>> keyValues, DistributedCacheEntryOptions options)
{
var expirationValues = GetExpirationValues(options);

StringBuilder sb = new StringBuilder();
sb.AppendLine(DbCommands.Commands[(int)Operation.BulkInsert]);
int i = 0;
Expand All @@ -408,12 +413,13 @@ private void CreateBulkInsert(DbCommand cmd, IEnumerable<KeyValuePair<string, by
sb.Remove(sb.Length - 1, 1);
sb.Append(";");

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

cmd.CommandText = sb.ToString();
}

private void AddExpirationParameters(DbCommand cmd, DistributedCacheEntryOptions options)
private (DateTimeOffset? expiry, TimeSpan? renewal) GetExpirationValues(DistributedCacheEntryOptions options)
{
DateTimeOffset? expiry = null;
TimeSpan? renewal = null;
Expand All @@ -434,8 +440,7 @@ 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);
return (expiry, renewal);
}

public void Set(string key, byte[] value, DistributedCacheEntryOptions options)
Expand Down Expand Up @@ -470,6 +475,51 @@ public Task SetBulkAsync(IEnumerable<KeyValuePair<string, byte[]>> keyValues, Di
});
}

public void SetBulk(IEnumerable<KeyValuePair<string, byte[]?>> keyValues, DistributedCacheEntryOptions options,
CancellationToken cancel = default)
{
var expirationValues = GetExpirationValues(options);
var expiryParamValue = expirationValues.expiry?.Ticks ?? (object) DBNull.Value;
var renewalParamValue = expirationValues.renewal?.Ticks ?? (object)DBNull.Value;

var keyValuesArray = keyValues.ToArray(); // prevent possible multiple enumeration

Commands.Use(Operation.BulkInsert, cmd =>
{
using var transaction = cmd.Connection?.BeginTransaction();
cmd.Transaction = transaction;
cmd.CommandText += " ($key, $val, $expiry, $renewal);";

var keyParam = cmd.CreateParameter();
keyParam.ParameterName = "$key";
cmd.Parameters.Add(keyParam);

var valParam = cmd.CreateParameter();
valParam.ParameterName = "$val";
cmd.Parameters.Add(valParam);

var expiryParam = cmd.CreateParameter();
expiryParam.ParameterName = "$expiry";
cmd.Parameters.Add(expiryParam);

var renewalParam = cmd.CreateParameter();
renewalParam.ParameterName = "$renewal";
cmd.Parameters.Add(renewalParam);

foreach (var row in keyValuesArray)
{
keyParam.Value = row.Key;
valParam.Value = row.Value;
expiryParam.Value = expiryParamValue;
renewalParam.Value = renewalParamValue;

cmd.ExecuteNonQuery();
}

transaction?.Commit();
});
}

public void RemoveExpired()
{
var removed = (long) Commands.Use(Operation.RemoveExpired, cmd =>
Expand Down