PommaLabs.KVLite 12.4.0

KVLite

License: MIT Donate Docs NuGet version NuGet downloads

standard-readme compliant GitLab pipeline status Quality gate Code coverage Renovate enabled

KVLite is a partition-based key-value cache built for SQL RDBMSs.

Library is feature complete and no further development is planned on this project, except for routine maintenance and bug fixes.

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)
  • 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 PostgreSQL cache.
PostgreSqlCache.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() { NullableBoolean = true },
                [2] = new() { 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.
        var 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, PostgreSQL, ...
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

@pomma89.

Contributing

MRs accepted.

Small note: If editing the README, please conform to the standard-readme specification.

Editing

Visual Studio Code, with Remote Containers extension, is the recommended way to work on this project.

A development container has been configured with all required tools.

Visual Studio Community is also supported and an updated solution file, kvlite.sln, has been provided.

Restoring dependencies

When opening the development container, dependencies should be automatically restored.

Anyway, dependencies can be restored with following command:

dotnet restore

Running tests

Tests can be run with following command:

dotnet test

Tests can also be run with following command, which collects coverage information:

./build.sh --target run-tests 

License

MIT © 2014-2024 PommaLabs Team and Contributors

Showing the top 20 packages that depend on PommaLabs.KVLite.

Packages Downloads
Finsa.Caravan.Common
A long description of the package. This shows up in the right pane of the Add Package Dialog as well as in the Package Manager Console when listing packages using the Get-Package command. THIS PACKAGE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE PACKAGE OR THE USE OR OTHER DEALINGS IN THE PACKAGE.
140
Finsa.Caravan.Common.Runtime
A long description of the package. This shows up in the right pane of the Add Package Dialog as well as in the Package Manager Console when listing packages using the Get-Package command. THIS PACKAGE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE PACKAGE OR THE USE OR OTHER DEALINGS IN THE PACKAGE.
135
PommaLabs.KVLite.AspNetCore
KVLite is a partition-based key-value cache built for SQL RDBMSs. This package contains ASP.NET Core extensions, helpers and components. For example, this package contains efficient extensions for storing POCOs inside ASP.NET Core Session.
73
PommaLabs.KVLite.AspNetCore
KVLite is a partition-based key-value cache built for SQL RDBMSs. This package contains ASP.NET Core extensions, helpers and components. For example, this package contains efficient extensions for storing POCOs inside ASP.NET Core Session.
244
PommaLabs.KVLite.AspNetCore
KVLite is a partition-based key-value cache built for SQL RDBMSs. This package contains ASP.NET Core extensions, helpers and components. For example, this package contains efficient extensions for storing POCOs inside ASP.NET Core Session.
257
PommaLabs.KVLite.AspNetCore
KVLite is a partition-based key-value cache built for SQL RDBMSs. This package contains ASP.NET Core extensions, helpers and components. For example, this package contains efficient extensions for storing POCOs inside ASP.NET Core Session.
274
PommaLabs.KVLite.AspNetCore
KVLite is a partition-based key-value cache built for SQL RDBMSs. This package contains ASP.NET Core extensions, helpers and components. For example, this package contains efficient extensions for storing POCOs inside ASP.NET Core Session.
295
PommaLabs.KVLite.AspNetCore
KVLite is a partition-based key-value cache built for SQL RDBMSs. This package contains ASP.NET Core extensions, helpers and components. For example, this package contains efficient extensions for storing POCOs inside ASP.NET Core Session.
416
PommaLabs.KVLite.Core
KVLite is a partition-based key-value cache built for SQL RDBMSs. This package contains core implementations and base classes.
68
PommaLabs.KVLite.Core
KVLite is a partition-based key-value cache built for SQL RDBMSs. This package contains core implementations and base classes.
139
PommaLabs.KVLite.Database
KVLite is a partition-based key-value cache built for SQL RDBMSs. This package contains SQL code shared by all drivers.
82
PommaLabs.KVLite.Database
KVLite is a partition-based key-value cache built for SQL RDBMSs. This package contains SQL code shared by all drivers.
126
PommaLabs.KVLite.Database
KVLite is a partition-based key-value cache built for SQL RDBMSs. This package contains SQL code shared by all drivers.
142
PommaLabs.KVLite.Database
KVLite is a partition-based key-value cache built for SQL RDBMSs. This package contains SQL code shared by all drivers.
283
PommaLabs.KVLite.Memory
KVLite is a partition-based key-value cache built for SQL RDBMSs. This package contains default in-memory driver.
78
PommaLabs.KVLite.Memory
KVLite is a partition-based key-value cache built for SQL RDBMSs. This package contains default in-memory driver.
87
PommaLabs.KVLite.Memory
KVLite is a partition-based key-value cache built for SQL RDBMSs. This package contains default in-memory driver.
115
PommaLabs.KVLite.Memory
KVLite is a partition-based key-value cache built for SQL RDBMSs. This package contains default in-memory driver.
142
PommaLabs.KVLite.Memory
KVLite is a partition-based key-value cache built for SQL RDBMSs. This package contains default in-memory driver.
769
PommaLabs.KVLite.Oracle
KVLite is a partition-based key-value cache built for SQL. KVLite can be stored on any Entity Framework compatible database, and each key/value pair can have its own lifetime and refresh mode. This package contains the Oracle driver used by KVLite.
135

https://gitlab.com/pommalabs/kvlite/-/releases

Version Downloads Last updated
12.4.0 18 12/17/2024
12.3.0 18 02/11/2024
12.2.1 19 02/11/2024
12.2.0 18 02/11/2024
12.1.0 20 02/11/2024
12.0.0 173 02/18/2022
11.1.0 145 12/07/2021
11.0.7 69 10/01/2021
11.0.5 341 04/25/2021
11.0.4 476 03/17/2021
11.0.3 25 03/14/2021
11.0.2 359 01/16/2021
11.0.1 41 12/27/2020
11.0.0 372 11/02/2020
10.1.1 22 10/27/2020
10.1.0 243 08/25/2020
10.0.5 36 06/21/2020
10.0.4 18 02/11/2024
10.0.3 18 02/11/2024
10.0.2 16 02/11/2024
10.0.0 23 08/24/2020
9.3.3 23 03/02/2020
9.2.7 21 02/10/2020
9.2.6 19 02/11/2024
9.2.5 16 02/11/2024
9.2.4 67 02/11/2020
9.2.3 19 02/11/2020
9.1.2 147 02/11/2020
9.0.6 22 02/20/2020
9.0.5 23 02/10/2020
9.0.4 18 02/11/2024
9.0.3 19 02/11/2024
8.1.3 20 02/10/2020
8.1.2 18 02/11/2024
8.1.0 18 02/11/2024
8.0.2 17 02/11/2024
8.0.1 14 02/11/2024
7.2.3 21 07/04/2020
7.2.2 15 02/11/2024
7.2.1 13 02/11/2024
7.1.1 20 02/11/2024
7.1.0 17 02/11/2024
7.0.3 18 02/11/2024
7.0.2 16 02/11/2024
6.4.4 21 02/10/2020
6.4.3 16 02/11/2024
6.4.2 18 02/11/2024
6.4.0 17 02/11/2024
6.3.3 16 02/11/2024
6.2.5 20 02/10/2020
6.2.1 22 02/10/2020
6.1.0 18 02/10/2020
6.0.7 16 02/10/2020
6.0.6 20 02/10/2020
6.0.5 130 02/10/2020
6.0.4 14 02/10/2020
6.0.3 46 02/10/2020
6.0.2 14 02/10/2020
6.0.1 16 02/10/2020
5.2.8 22 02/10/2020
5.2.7 17 02/10/2020
5.2.6.2 16 02/10/2020
5.2.6.1 15 02/10/2020
5.2.6 21 02/10/2020
5.2.5 17 02/10/2020
5.2.4 20 02/10/2020
5.2.2 17 02/10/2020
5.2.1 17 02/10/2020
5.1.1 20 02/10/2020
5.1.0 19 02/10/2020
5.0.6 20 02/10/2020
5.0.5 15 02/10/2020
5.0.4 19 02/10/2020
5.0.3 16 02/10/2020
5.0.2 20 02/10/2020
5.0.1 16 02/10/2020
4.5.1 40 02/10/2020
4.5.0 15 02/10/2020
4.4.2 17 02/10/2020
4.2.1 19 02/11/2024
4.0.0 17 02/11/2024
3.1.0 16 02/11/2024
3.0.3 17 02/11/2024
2.1.0 17 02/11/2024
1.5.0 15 02/11/2024
1.4.0 17 02/10/2020
1.3.0 14 02/11/2024
1.2.0 17 02/11/2024
1.0.0 18 02/11/2024
0.9.3 15 02/11/2024
0.9.2 21 02/11/2024
0.9.1 17 02/11/2024
0.6.5 27 04/09/2021
0.6.4 16 02/11/2024
0.6.3 16 02/11/2024
0.5.3 18 02/04/2024
0.5.1 16 02/11/2024
0.5.0 16 02/11/2024
0.4.7 16 02/11/2024
0.4.6 17 02/11/2024
0.4.5 15 02/11/2024
0.4.4 15 02/11/2024
0.4.3 18 02/11/2024
0.4.2 15 02/11/2024
0.4.1 19 02/11/2024
0.4.0 0 09/06/2014
0.3.9 0 08/20/2014
0.3.8 0 08/19/2014
0.3.7 0 08/18/2014
0.3.4 0 08/17/2014
0.3.3 0 08/17/2014
0.3.2 0 08/16/2014
0.3.1 0 08/16/2014
0.2.1 0 08/08/2014
0.2.0-alpha 0 08/05/2014
0.1.10-alpha 0 08/03/2014
0.1.9-alpha 0 08/03/2014
0.1.8-alpha 0 08/03/2014
0.1.7-alpha 0 07/27/2014