Skip to content

uzyn/sparkv

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

25 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

SparKV

Crates.io Documentation CI Dependencies License: MIT

SparKV is an expirable in-memory key-value store for Rust with no dependencies.

Features

  1. Zero dependencies — pure Rust std, nothing added to your dependency tree.
  2. Flexible expiration duration (a.k.a. time-to-live or TTL) per entry instead of a database-wide common TTL.
    1. This is similar to DNS, where each entry of the same domain can have its own unique TTL.
  3. Automatically clears expired entries by default.
  4. Generic over the value type, defaulting to String. Store any type you like.
  5. Fast data-entry enforcements, including entry size, database size and max TTL.
  6. SparKV is intentionally not an LRU cache.
  7. Configurable.

Usage

Add SparKV to your Cargo dependencies:

$ cargo add sparkv

Quick start:

use sparkv::SparKV;

// The value type defaults to String.
let mut sparkv = SparKV::new();
sparkv.set("your-key", "your-value".to_string()).unwrap(); // write
let value = sparkv.get("your-key").unwrap(); // read

// Write with a unique TTL
sparkv.set_with_ttl("diff-ttl", "your-value".to_string(), std::time::Duration::from_secs(60)).unwrap();

set takes the value by value, and get returns an owned clone (so the value type must implement Clone).

Generic value types

SparKV is generic over the value type. Built-in types (integers, Vec<u8>, etc.) work out of the box:

use sparkv::SparKV;

let mut counters: SparKV<i64> = SparKV::new();
counters.set("hits", 42).unwrap();
assert_eq!(counters.get("hits"), Some(42));

To store your own type, implement ValueSize so SparKV can enforce max_item_size (return 0 if you don't care about size enforcement):

use sparkv::{SparKV, ValueSize};

#[derive(Clone)]
struct Session {
    user_id: u64,
    csrf: String,
}

impl ValueSize for Session {
    fn value_size(&self) -> usize {
        std::mem::size_of::<u64>() + self.csrf.len()
    }
}

let mut sessions: SparKV<Session> = SparKV::new();
sessions
    .set_with_ttl(
        "sid",
        Session { user_id: 1, csrf: "abc".to_string() },
        std::time::Duration::from_secs(60),
    )
    .unwrap();

API overview

Method Description
set(key, value) Insert/overwrite using config.default_ttl.
set_with_ttl(key, value, ttl) Insert/overwrite with an explicit TTL.
get(key) -> Option<V> Owned clone of a live (non-expired) value.
get_item(key) -> Option<&KvEntry<V>> Borrow the live entry, including its expired_at.
delete(key) -> Option<V> Remove and return the value.
contains_key(key) -> bool Expiry-aware presence check (agrees with get).
len() / is_empty() O(1) physical count (may include expired-but-unswept entries).
clear_expired() -> usize Sweep expired entries, returning how many were removed.

set / set_with_ttl return Result<(), Error>, where Error is one of CapacityExceeded, ItemSizeExceeded, or TTLTooLong.

Configuration

Construct a store with defaults via SparKV::new(), or supply your own Config with SparKV::with_config(config):

use sparkv::{Config, SparKV};

let mut config = Config::new();
config.max_items = 50_000;
config.max_item_size = None; // disable per-entry size enforcement
config.default_ttl = std::time::Duration::from_secs(30);

let mut sparkv: SparKV = SparKV::with_config(config);
Field Type Default Purpose
max_items usize 10_000 Maximum number of entries before set returns CapacityExceeded.
max_item_size Option<usize> Some(500_000) Maximum per-entry size (via ValueSize); None disables the check.
max_ttl Duration 1 hour Upper bound on any entry's TTL; exceeding it returns TTLTooLong.
default_ttl Duration 5 minutes TTL applied by set when none is given.
auto_clear_expired bool true Sweep expired entries automatically on writes/deletes.

Migrating from 0.1 to 0.2

0.2.0 makes the value type generic (SparKV<V>, defaulting to String). The store behaves exactly as before; the breaking changes are limited to how values are passed in and how max_item_size is configured. The SparKV and KvEntry type names still resolve to their String forms, so type annotations and struct fields are unaffected.

1. set / set_with_ttl now take the value by value. Pass an owned value instead of a &str:

// 0.1
kv.set("key", "value");
// 0.2
kv.set("key", "value".to_string()); // or "value".into()

get and delete still return Option<String> for the default String store, so your read sites do not change.

2. Config.max_item_size is now Option<usize>. Wrap the limit in Some, or use None to disable size enforcement:

// 0.1
config.max_item_size = 500_000;
// 0.2
config.max_item_size = Some(500_000); // or None to disable

3. Custom value types need a ValueSize impl — only relevant if you switch away from String. Built-in types (integers, Vec<u8>, &[u8], etc.) already implement it; see the generic example above.

No other call sites change.

Changelog

See CHANGELOG.md for the release history.

License

MIT License
Copyright © 2024 U-Zyn Chua

About

Expirable in-memory key-value store for Rust with no dependencies

Topics

Resources

License

Stars

Watchers

Forks

Packages

 
 
 

Contributors

Languages