#key-value-store #kubernetes #store #key-value #sqlite #persistence #secret-store

simple_kv_store

Provides a generic, async-friendly key-value store abstraction supporting multiple backends (inmemory, sqlite, kubernetes)

3 releases

new 0.1.2 Feb 9, 2025
0.1.1 Feb 8, 2025
0.1.0 Feb 8, 2025

#1363 in Database interfaces

Download history 325/week @ 2025-02-08

325 downloads per month

MIT license

20KB
256 lines

Key-Value Store Library for Automation Controller

This Rust library provides a generic, async-friendly key-value store abstraction supporting multiple backends:

  • In-Memory (for fast ephemeral storage)
  • Kubernetes ConfigMap/Secret (for distributed configuration)
  • SQLite (for persistent local storage)

This is intended for lightweight simple storage. It is by no means high performance or able to provide a high throughput.  Especially when using a kubernetes backend, keep in mind that with every set command the configmap or secret will be updated which can cause a high load in the kubernetes api server.

Currently it is mainly designed to be used in a simple smarthome automation controller to provide some simple persistence functionality. It is however designed generically so it can be used wherever deemed useful.

Features

Asynchronous API using tokioSupport for multiple backends (in-memory, Kubernetes, SQLite) ✔ Automatic resource creation for Kubernetes stores ✔ Efficient caching to reduce unnecessary API calls ✔ Clonable storage instances using Arc<T>Base64 encoding/decoding for Kubernetes Secrets

Installation

Run the following command to add the library to your project:

cargo add simple_kv_store

Usage

1. Create a Store Instance

Manually initialize the store based on your preferred backend.

In-Memory Store

use my_library::{KeyValueStore, InMemoryStore};


let store = KeyValueStore::InMemory(InMemoryStore::new());
let value = true; // define a owned string value
store.set("some_key", &value).await.unwrap(); // store the value
println!("Value: {}", store.get::<bool>("some_key").await.unwrap()); // retrieve the value -- type annotations are
                                                                     // needed in this case as no type can be deferred
                                                                     // from the println! usage

Kubernetes ConfigMap Store

use my_library::{KeyValueStore, KubernetesStore, KubernetesResource};


let store = KeyValueStore::Kubernetes(KubernetesStore::new("default", "config", KubernetesResource::ConfigMap).await.unwrap());
let value = 123; // define a owned string value
store.set("some_key", &value).await.unwrap(); // store the value
println!("Value: {}", store.get::<i64>("some_key").await.unwrap()); // retrieve the value -- type annotations are
                                                                    // needed in this case as no type can be deferred
                                                                    // from the println! usage

--> Check chapter Key values

Kubernetes Secret Store (Handles base64 encoding/decoding)

use my_library::{KeyValueStore, KubernetesStore, KubernetesResource};


let store = KeyValueStore::Kubernetes(KubernetesStore::new("default", "my-secret", KubernetesResource::Secret).await.unwrap());
let value = 123.123; // define a owned string value
store.set("some_key", &value).await.unwrap(); // store the value
println!("Value: {}", store.get::<f64>("some_key").await.unwrap()); // retrieve the value -- type annotations are
                                                                    // needed in this case as no type can be deferred
                                                                    // from the println! usage

--> Check chapter Key values

SQLite Store

use my_library::{KeyValueStore, SQLiteStore};


let store = KeyValueStore::SQLite(SQLiteStore::new("store.db").await); // create a sqlite backed store
let value = "some_value".to_string(); // define a owned string value
store.set("some_key", &value).await.unwrap(); // store the value
println!("Value: {}", store.get::<String>("some_key").await.unwrap()); // retrieve the value -- type annotations are
                                                                       // needed in this case as no type can be deferred
                                                                       // from the println! usage

Key values

Be aware: Kubernetes requires keys to consist of alphanumeric characters (A-Z, a-z, 0-9), dashes (-), underscores (_), and dots (.). This library provides a normalization function you can use to ensure your keys are always valid: normalize_key

Examples

let key = "device/switch/state";
assert_eq!(normalize_key(key), "device_switch_state");

let key = "config:mode/type";
assert_eq!(normalize_key(key), "config_mode_type");

let key = "user@domain.com";
assert_eq!(normalize_key(key), "user_domain.com");

API Overview

KeyValueStore

Represents a key-value store with different backends.

pub enum KeyValueStore {
    InMemory(InMemoryStore),
    Kubernetes(KubernetesStore),
    SQLite(SQLiteStore),
}

CRUD Methods

KeyValueStore implements the get, set and delete functions and will accept and return any type as value that can be serialized and deserialized with serde.


impl KeyValueStore {
    pub async fn get<T: DeserializeOwned>(&self, key: &str) -> Option<T> {
        ...
    }

    pub async fn set<T: Serialize>( &self, key: &str, value: &T,) -> Result<(), Box<dyn std::error::Error>> {
        ...
    }

    pub async fn delete(&self, key: &str) -> Result<(), Box<dyn std::error::Error>> {
        ...
    }
}

License

This project is licensed under the MIT License.

Dependencies

~70–99MB
~1.5M SLoC