12个版本 (破坏性更新)
0.9.3 | 2023年12月12日 |
---|---|
0.9.2 | 2023年11月22日 |
0.9.1 | 2023年10月7日 |
0.7.0 | 2023年6月14日 |
0.1.0 | 2023年3月27日 |
#86 在 数据库接口 中
每月1,889 次下载
在 krill 中使用
120KB
2.5K SLoC
键值存储X
Rust中各种键值存储后端的抽象层。专为Krill的使用场景而定制。
在各个后端之间切换应该像更改配置值一样简单。
目前默认提供了内存、文件系统和Postgres实现。
用法
创建一个KVX存储实例,并使用URL指定存储后端。例如
let namespace = Namespace::parse("some-namespace")?;
// in memory backend
let store = KeyValueStore::new(&Url::parse("memory://")?, namespace)?;
// use a file backend
let store = KeyValueStore::new(&Url::parse("local://tmp")?, namespace)?;
// use a postgres backend
let store = KeyValueStore::new(&Url::parse("postgres://user:password@host/database-name")?, namespace)?;
存储可以使用命名空间进行范围限定。命名空间可以进一步划分为(可能嵌套的)范围。
请注意,键、范围和命名空间具有Segment
类型,这是将命名空间、范围和键编码到文件系统所必需的。
存储支持基本的键值操作
fn is_empty(&self) -> Result<bool>;
fn has(&self, key: &Key) -> Result<bool>;
fn has_scope(&self, scope: &Scope) -> Result<bool>;
fn get(&self, key: &Key) -> Result<Option<Value>>;
fn list_keys(&self, scope: &Scope) -> Result<Vec<Key>>;
fn list_scopes(&self) -> Result<Vec<Scope>>;
fn store(&self, key: &Key, value: Value) -> Result<()>;
fn move_value(&self, from: &Key, to: &Key) -> Result<()>;
fn move_scope(&self, from: &Scope, to: &Scope) -> Result<()>;
fn delete(&self, key: &Key) -> Result<()>;
fn delete_scope(&self, scope: &Scope) -> Result<()>;
fn clear(&self) -> Result<()>;
/// Migrate the namespace (and all key value pairs) for this store.
fn migrate_namespace(&mut self, to: NamespaceBuf) -> Result<()>;
可以使用事务原子地执行一系列操作
store.transaction(scope, &mut move |t: &dyn KeyValueStoreBackend| {
let key = "counter".parse()?;
let value = t.get(&key)?;
let new_value = value.as_i64().unwrap_or_default() + 1;
t.store(&key, Value::from(new_value))?;
})?;
如果需要在事务中返回值(或者结果),则可以使用执行函数。值可以是结果类型,以防需要返回非kvx错误。
示例代码,其中self具有KeyValueStore,并希望返回全局范围内的所有键,同时验证某些保留键没有被使用。
主要收获是传递给执行函数的闭包可以返回类似于Result<Result<T, E>, kvx::Error>
的东西。
pub fn list_verified_keys(&self) -> Result<Vec<Keys>, MyError> {
self.store.execute(&Scope::global(), |kv| {
let keys = kv.list_keys(&Scope::global())?;
let forbidden_key = Key::new_global(segment!("reserved"));
if keys.contains(&forbidden_key) {
Ok(Err(MyError::ForbiddenKey))
} else {
Ok(Ok(keys))
}
})
.map_err(MyError::from)
}
队列机制可以创建和处理任务。可以在特定时间安排作业。
示例
use kvx::queue;
fn queue(store: &KeyValueStore) -> Result<(), kvx::Error> {
let name = "job";
let segment = Segment::parse(name).unwrap();
let value = Value::from("value");
// schedule a task
queue.schedule_task(
segment.into(),
value,
None,
ScheduleMode::FinishOrReplaceExisting,
)?;
// claim a pending task
let task_opt = queue.claim_scheduled_pending_task()?;
if let Some(task) = task_opt {
// do stuff...
// then finish the task
queue.finish_running_task(&Key::from(&task))?;
}
Ok(())
}
变更日志
版本0.9.3
存在一个问题,即旧锁文件可能不会被清理,例如如果使用此库的应用程序被OOM-killer终止,或者服务器关闭。为了避免这个问题,我们现在依赖于“fd-lock”包,该包反过来又使用“flock”,它依赖于操作系统级别的支持来确保调用者对锁文件的文件句柄具有唯一的访问权限。
参见: https://github.com/NLnetLabs/kvx/pull/62
版本 0.9.2
- 始终使用临时文件处理磁盘上的新值 #60
版本 0.9.1
- 将锁文件保留在作用域目录之外 #58
版本 0.9.0
已合并
- 在完成现有任务之前安排任务 #56
这是一个破坏性更改,因为现在用于任务的戳记使用毫秒而不是秒。
版本 0.8.0
这个版本引入了多项破坏性更改。特别是,我们现在为 Namespace
使用一个专用类型,并且不再在键前添加命名空间 Segment
。并且 Queue
实现已被彻底重写。
- 添加 KeyValueStore::execute #38
- 使用格式化的JSON打印磁盘上的值 #39
- 使用命名空间类型并支持命名空间迁移 #45
- 在磁盘存储中使用时写入临时文件并重命名 #46, #51
- 使用事务队列 #48, #50, #54
版本 0.7.0
没有进行功能性更改,但对crates.io上发布的crate进行了以下更新
- 修复了报告的许可协议,它是BSD-3
- 更新GitHub仓库链接到当前位置
- 更新Readme文件以在crates.io上提高可读性
版本 0.6.0
破坏性更改
- 从磁盘上的键中删除了隐式的.json扩展名(见PR #32)
依赖项
~9–21MB
~322K SLoC