PommaLabs.KVLite.Polly
11.0.7
Prefix Reserved
See the version list below for details.
dotnet add package PommaLabs.KVLite.Polly --version 11.0.7
NuGet\Install-Package PommaLabs.KVLite.Polly -Version 11.0.7
<PackageReference Include="PommaLabs.KVLite.Polly" Version="11.0.7" />
paket add PommaLabs.KVLite.Polly --version 11.0.7
#r "nuget: PommaLabs.KVLite.Polly, 11.0.7"
// Install PommaLabs.KVLite.Polly as a Cake Addin #addin nuget:?package=PommaLabs.KVLite.Polly&version=11.0.7 // Install PommaLabs.KVLite.Polly as a Cake Tool #tool nuget:?package=PommaLabs.KVLite.Polly&version=11.0.7
KVLite
KVLite is a partition-based key-value cache built for SQL RDBMSs.
KVLite entries can be stored either in persistent or volatile fashion, and each key/value pair can have its own lifetime and refresh mode.
Entries are grouped by partition, where each key must be unique only inside the partition it belongs to.
Following RDBMSs are currently supported by KVLite:
- MySQL and MariaDB (.NET and .NET Core, with integration tests for MySQL only)
- Oracle (.NET and .NET Core)
- PostgreSQL (.NET and .NET Core, with integration tests)
- SQL Server (.NET and .NET Core, with integration tests)
- SQLite (.NET and .NET Core, with integrations tests)
KVLite implements different caching adapters for various libraries and frameworks:
An in-memory driver is also provided for unit testing.
Table of Contents
Install
KVLite requires a driver for your RDBMS, packages are linked above. For example, if you use MySQL, the you should install:
dotnet add package PommaLabs.KVLite.MySql
After that, you could install specific adapters (like the one for ASP.NET Core) and you should set your DB up according to the documentation below.
Storage layout
KVLite stores cache entries in a dedicated table, whose schema is as much tuned as possible for each RDBMS. The logical schema for cache entries table is the following:
Column name | Data type | Content |
---|---|---|
kvle_id |
guid or long |
Automatically generated ID. This is the primary key. |
kvle_hash |
long |
Hash of partition and key. This is the unique key. |
kvle_expiry |
long |
When the entry will expire, expressed as seconds after UNIX epoch. |
kvle_interval |
long |
How many seconds should be used to extend expiry time when the entry is retrieved. |
kvle_value |
byte[] |
Serialized and optionally compressed content of this entry. |
kvle_compressed |
bool |
Whether the entry content was compressed or not. |
kvle_partition |
string |
A partition holds a group of related keys. |
kvle_key |
string |
A key uniquely identifies an entry inside a partition. |
kvle_creation |
long |
When the entry was created, expressed as seconds after UNIX epoch. |
kvle_parent_hash0 |
long |
Optional parent entry hash, used to link entries in a hierarchical way. |
kvle_parent_key0 |
string |
Optional parent entry key, used to link entries in a hierarchical way. |
kvle_parent_hash1 |
long |
Optional parent entry hash, used to link entries in a hierarchical way. |
kvle_parent_key1 |
string |
Optional parent entry key, used to link entries in a hierarchical way. |
kvle_parent_hash2 |
long |
Optional parent entry hash, used to link entries in a hierarchical way. |
kvle_parent_key2 |
string |
Optional parent entry key, used to link entries in a hierarchical way. |
If SQL user chosen for KVLite has enough privileges, then cache entries table will be automatically created. Anyway, specialized schemas for supported RDBMS systems are available inside this project repository, at following links:
Each script might have a few comments suggesting how to further optimize cache entries table storage depending on the actual version of the specific RDBMS system.
Customizing SQL layout
Default name for cache entries table is kvle_cache_entries
and default SQL schema is kvlite
.
However, those values can be easily changed at runtime, as we do in the following snippet:
// Change cache entries table name for Oracle cache.
OracleCache.DefaultInstance.Settings.CacheEntriesTableName = "my_custom_name";
// Change SQL schema name for MySQL cache.
MySqlCache.DefaultInstance.Settings.CacheSchemaName = "my_schema_name";
// Change both table ans schema name for SQL Server cache.
SqlServerCache.DefaultInstance.Settings.CacheEntriesTableName = "my_custom_name";
SqlServerCache.DefaultInstance.Settings.CacheSchemaName = "my_schema_name";
Please perform those customizations as early as your application starts; for example, these are good places where to put the lines:
Program.cs
for console and Windows applications.Global.asax.cs
for classic web applications.Startup.cs
for Owin-based web applications or ASP.NET Core.
Usage
Let's start with a simple example of what you can do with KVLite:
/// <summary>
/// Learn how to use KVLite by examples.
/// </summary>
internal static class Program
{
/// <summary>
/// Learn how to use KVLite by examples.
/// </summary>
public static void Main()
{
// Some variables used in the examples.
var examplePartition1 = "example partition 1";
var examplePartition2 = "example partition 2";
var exampleKey1 = "example key 1";
var exampleKey2 = "example key 2";
var simpleValue = Math.PI;
var complexValue = new ComplexValue
{
Integer = 21,
NullableBoolean = null,
String = "Learning KVLite",
Dictionary = new Dictionary<short, ComplexValue>
{
[1] = new ComplexValue { NullableBoolean = true },
[2] = new ComplexValue { String = "Nested..." }
}
};
/*
* KVLite stores its values inside a given partition and each value is linked to a key.
* KVLite can contain more than one partition and each partition can contain more than one key.
*
* Therefore, values are stored according to this logical layout:
*
* [partition1] --> key1/value1
* --> key2/value2
* [partition2] --> key1/value1
* --> key2/value2
* --> key3/value3
*
* A key is unique inside a partition, not inside all cache.
* A partition, instead, is unique inside all cache.
*/
// You can start using the default caches immediately. Let's try to store some values in
// a way similar to the figure above, using the default persistent cache.
ICache persistentCache = PersistentCache.DefaultInstance;
persistentCache.AddTimed(examplePartition1, exampleKey1, simpleValue, persistentCache.Clock.UtcNow + TimeSpan.FromMinutes(5));
persistentCache.AddTimed(examplePartition1, exampleKey2, simpleValue, persistentCache.Clock.UtcNow + TimeSpan.FromMinutes(10));
persistentCache.AddTimed(examplePartition2, exampleKey1, complexValue, persistentCache.Clock.UtcNow + TimeSpan.FromMinutes(10));
persistentCache.AddTimed(examplePartition2, exampleKey2, complexValue, persistentCache.Clock.UtcNow + TimeSpan.FromMinutes(5));
PrettyPrint(persistentCache);
// Otherwise, you can customize you own cache... Let's see how we can use a volatile
// cache. Let's define the settings that we will use in new volatile caches.
var volatileCacheSettings = new VolatileCacheSettings
{
CacheName = "My In-Memory Cache", // The backend.
};
// Then the settings that we will use in new persistent caches.
var persistentCacheSettings = new PersistentCacheSettings
{
CacheFile = "CustomCache.sqlite", // The SQLite DB used as the backend for the cache.
ChancesOfAutoCleanup = 0.5, // Chance of an automatic a cache cleanup being issued.
};
// We create both a volatile and a persistent cache.
var volatileCache = new VolatileCache(volatileCacheSettings);
persistentCache = new PersistentCache(persistentCacheSettings);
// Use the new volatile cache!
volatileCache.AddTimed(examplePartition1, exampleKey1, Tuple.Create("Volatile!", 123), TimeSpan.FromMinutes(60));
PrettyPrint(volatileCache);
// Use the new persistent cache!
persistentCache.AddTimed(examplePartition2, exampleKey2, Tuple.Create("Persistent!", 123), TimeSpan.FromMinutes(60));
PrettyPrint(persistentCache);
/*
* An entry can be added to the cache in three different ways.
*
* "Timed" values last until the specified date and time, or for a specified timespan.
* Reading them will not extend their lifetime.
*
* "Sliding" values last for the specified lifetime, but, if read,
* their lifetime will be extended by the timespan specified initially.
*/
// Let's clear the volatile cache and let's a value for each type.
volatileCache.Clear();
volatileCache.AddTimed(examplePartition1, exampleKey1, simpleValue, volatileCache.Clock.UtcNow + TimeSpan.FromMinutes(10));
volatileCache.AddTimed(examplePartition1, exampleKey2, complexValue, TimeSpan.FromMinutes(15));
volatileCache.AddSliding(examplePartition2, exampleKey2, complexValue, TimeSpan.FromMinutes(15));
PrettyPrint(volatileCache);
}
private static void PrettyPrint(ICache cache)
{
Console.WriteLine($"Printing the contents of a {cache.GetType().Name}");
// When we use "Peek*" methods, the expiration time of entries is left untouched.
var cacheEntries = cache.PeekEntries<object>();
foreach (var cacheEntry in cacheEntries.OrderBy(ci => ci.Partition).ThenBy(ci => ci.Key))
{
Console.WriteLine($"{cacheEntry.Partition} --> {cacheEntry.Key} --> {cacheEntry.Value}");
}
Console.WriteLine();
}
private sealed class ComplexValue
{
public int Integer { get; set; }
public bool? NullableBoolean { get; set; }
public string String { get; set; }
public IDictionary<short, ComplexValue> Dictionary { get; set; }
public override string ToString() => nameof(ComplexValue);
}
}
Polly cache provider
Moreover, KVLite can be used as Polly cache provider:
// Every KVLite cache can be interfaced with Polly: Memory, MySQL, Oracle, ...
var options = new KVLiteCacheProviderOptions();
var cacheProvider = new KVLiteSyncCacheProvider<string>(PersistentCache.DefaultInstance, options);
var cachePolicy = Policy.Cache(cacheProvider, TimeSpan.FromMinutes(10));
var myGuid1 = cachePolicy.Execute(() => Guid.NewGuid().ToString(), new Context("MyGuid"));
var myGuid2 = cachePolicy.Execute(() => Guid.NewGuid().ToString(), new Context("MyGuid"));
// Two GUIDs are equal because they share the same key.
Debug.Assert(myGuid1 == myGuid2);
myGuid1 = cachePolicy.Execute(() => Guid.NewGuid().ToString(), new KVLiteContext("My", "Complex", "Key", 1));
myGuid2 = cachePolicy.Execute(() => Guid.NewGuid().ToString(), new KVLiteContext("My", "Complex", "Key", 2));
// Two GUIDs are not equal because they do not share the same key.
Debug.Assert(myGuid1 != myGuid2);
Examples
Further examples can be found in the following projects:
- ASP.NET Core: It shows how to register KVLite services and how to use it as a proper distributed cache implementation. Moreover, it shows how to use KVLite session extensions.
Maintainers
Contributing
PRs accepted.
Small note: If editing the README, please conform to the standard-readme specification.
License
MIT © 2014-2021 Alessio Parma
Product | Versions Compatible and additional computed target framework versions. |
---|---|
.NET | net5.0 was computed. net5.0-windows was computed. net6.0 was computed. net6.0-android was computed. net6.0-ios was computed. net6.0-maccatalyst was computed. net6.0-macos was computed. net6.0-tvos was computed. net6.0-windows was computed. net7.0 was computed. net7.0-android was computed. net7.0-ios was computed. net7.0-maccatalyst was computed. net7.0-macos was computed. net7.0-tvos was computed. net7.0-windows was computed. net8.0 was computed. net8.0-android was computed. net8.0-browser was computed. net8.0-ios was computed. net8.0-maccatalyst was computed. net8.0-macos was computed. net8.0-tvos was computed. net8.0-windows was computed. |
.NET Core | netcoreapp2.0 was computed. netcoreapp2.1 was computed. netcoreapp2.2 was computed. netcoreapp3.0 was computed. netcoreapp3.1 was computed. |
.NET Standard | netstandard2.0 is compatible. netstandard2.1 was computed. |
.NET Framework | net461 is compatible. net462 was computed. net463 was computed. net47 was computed. net471 was computed. net472 is compatible. net48 was computed. net481 was computed. |
MonoAndroid | monoandroid was computed. |
MonoMac | monomac was computed. |
MonoTouch | monotouch was computed. |
Tizen | tizen40 was computed. tizen60 was computed. |
Xamarin.iOS | xamarinios was computed. |
Xamarin.Mac | xamarinmac was computed. |
Xamarin.TVOS | xamarintvos was computed. |
Xamarin.WatchOS | xamarinwatchos was computed. |
-
.NETFramework 4.6.1
- Microsoft.Extensions.DependencyInjection.Abstractions (>= 2.0.0)
- Polly (>= 7.2.2)
- PommaLabs.KVLite (>= 11.0.7)
-
.NETFramework 4.7.2
- Microsoft.Extensions.DependencyInjection.Abstractions (>= 2.0.0)
- Polly (>= 7.2.2)
- PommaLabs.KVLite (>= 11.0.7)
-
.NETStandard 2.0
- Microsoft.Extensions.DependencyInjection.Abstractions (>= 2.0.0)
- Polly (>= 7.2.2)
- PommaLabs.KVLite (>= 11.0.7)
NuGet packages
This package is not used by any NuGet packages.
GitHub repositories
This package is not used by any popular GitHub repositories.
Version | Downloads | Last updated |
---|---|---|
12.3.0 | 186 | 11/15/2023 |
12.2.1 | 156 | 12/18/2022 |
12.2.0 | 89 | 12/17/2022 |
12.1.0 | 105 | 12/16/2022 |
12.0.0 | 192 | 1/23/2022 |
11.1.0 | 1,697 | 11/28/2021 |
11.0.7 | 151 | 9/24/2021 |
11.0.5 | 454 | 4/25/2021 |
11.0.4 | 399 | 3/17/2021 |
11.0.3 | 440 | 3/14/2021 |
11.0.2 | 429 | 1/16/2021 |
11.0.1 | 432 | 12/27/2020 |
11.0.0 | 466 | 11/1/2020 |
10.1.1 | 566 | 10/26/2020 |
10.1.0 | 552 | 8/12/2020 |
10.0.5 | 507 | 6/19/2020 |
10.0.4 | 528 | 6/14/2020 |
10.0.3 | 553 | 6/3/2020 |
10.0.2 | 530 | 6/1/2020 |
10.0.0 | 583 | 3/9/2020 |
9.3.3 | 566 | 3/2/2020 |
9.2.7 | 570 | 2/7/2020 |
9.2.6 | 541 | 2/6/2020 |
9.2.5 | 578 | 2/5/2020 |
9.2.4 | 593 | 1/4/2020 |
9.2.3 | 653 | 1/2/2020 |
9.1.2 | 618 | 8/31/2019 |
9.0.6 | 611 | 5/5/2019 |
9.0.5 | 648 | 2/26/2019 |
9.0.4 | 659 | 2/25/2019 |
9.0.3 | 756 | 12/28/2018 |
8.1.3 | 913 | 8/17/2018 |
8.1.2 | 865 | 8/16/2018 |
8.1.0 | 877 | 8/14/2018 |
8.0.2 | 901 | 7/7/2018 |
8.0.1 | 971 | 6/12/2018 |
7.2.3 | 968 | 4/5/2018 |
7.2.2 | 980 | 4/1/2018 |
7.2.1 | 1,133 | 4/1/2018 |