11 个版本 (6 个破坏性更新)

0.6.0 2024 年 2 月 1 日
0.4.0 2024 年 1 月 21 日
0.2.0 2023 年 12 月 26 日
0.0.4 2023 年 10 月 1 日
0.0.0 2023 年 3 月 27 日

#134并发

Download history 4/week @ 2024-04-02 62/week @ 2024-07-02

每月下载量 62 次

MIT 许可证

150KB
1.5K SLoC

someday

CI crates.io docs.rs

someday 是一个 多版本并发控制 原语。

它是一个无锁的、但内存消耗更大的 Mutex<T>RwLock<T> 的替代品。

无锁

Reader 是无锁的,并且大多数情况下是 无锁的

单个 Writer 是无锁的,但可能会复制数据。

Writer 想要更新 Reader 的数据时,它必须

  1. 原子性地更新一个指针,此时所有将来的读者都将看到新的数据
  2. 重新应用补丁到旧回收的数据 OR 如果无法回收,则复制数据

如果没有任何 Reader 挂在旧的 Commit 上,旧数据可以被 Writer 以较低的成本回收和重用。

如果有 Reader 挂在旧数据上,Writer 将复制数据,以便它可以继续。

用例

someday 在以下情况下表现最佳

您的数据

  • 相对便宜地复制和/或去重

并且您有 许多 读者

  • 希望无锁地获取数据副本
  • 暂时或永久地保留数据

并且有一个 writer

  • 希望无锁地修改数据
  • 希望尽快无锁地将更改推送到新读者
  • 相对于读操作,不经常修改数据
  • 通常与使用常规锁(MutexRwLock)的读者竞争

权衡

  • 内存使用增加: Writer 至少保留两个备份数据结构副本,而 Reader 可以保持无限量(只要它们继续持有引用)

  • 确定性补丁: 应用到您的数据上的补丁/函数必须是确定的,因为 Writer 可能会两次应用它们

  • 慢速写入: 写入比直接针对备份数据结构慢

API

someday 的 API 与 git 类似,并在语义上执行类似操作。

Writer

  1. 通过调用 add() 将一个 Patch(函数)添加到它们的数据中
  2. 通过 commit() 执行那些更改
  3. 可以随时查看本地或远程(读取器)数据
  4. 可以原子性地将那些更改通过 push() 推送到 Reader
  5. 可以在不等待 Reader 的情况下继续写入

Reader(s)

  1. 可以持续调用 head() 以低成本获取最新的 "head" Commit
  2. 可以永久保留那些 Commit 对象(尽管这可能以内存使用为代价)
  3. Writer 调用 push() 时,最终会赶上

示例

此示例显示了 Writer 的典型用法

  1. 添加一些更改
  2. 读取它们本地更改
  3. 通过调用 commit() 锁定那些更改
  4. 最后通过调用 push() 将那些更改揭示给读者

Reader

  1. 持续读取它们最新的当前数据 "head" Commit
  2. Writer 通过调用 push() 发布时,最终会赶上

代码

use someday::{
	Patch,
	Writer,Reader,
	Commit,CommitRef,
	CommitInfo,PushInfo,
};

// Create Reader/Writer for the string "hello".
let (r, mut w) = someday::new("hello".to_string());

// The readers see the data.
let commit: CommitRef<String> = r.head();
assert_eq!(commit.data, "hello");
assert_eq!(commit.timestamp, 0);

// Writer writes some data, but does not commit.
w.add(Patch::Ptr(|w, _| w.push_str(" world")));
// Nothing committed, data still the same everywhere.
let data: &String = w.data();
assert_eq!(*data, "hello");
// Patches not yet committed:
assert_eq!(w.staged().len(), 1);

// Readers still see old data.
assert_eq!(r.head().data, "hello");

// Writer writes some more data.
w.add(Patch::Ptr(|w, _| w.push_str("!")));
// Readers still see old data.
assert_eq!(r.head().data, "hello");

// Writer commits their patches.
let commit_info: CommitInfo = w.commit();
// The 2 operation were committed locally
// (only the Writer sees them).
assert_eq!(commit_info.patches, 2);

// Readers still see old data.
assert_eq!(r.head().data, "hello");

// Writer finally reveals those
// changes by calling `push()`.
let push_info: PushInfo = w.push();
assert_eq!(push_info.commits, 1);

// Now readers see updates.
let commit: CommitRef<String> = r.head();
assert_eq!(commit.data, "hello world!");
// Each call to `.commit()` added 1 to the timestamp.
assert_eq!(commit.timestamp, 1);

功能标志

这些功能用于(反)序列化。

您可以直接从

  • Writer<将您的数据 T 序列化或反序列化>
  • Reader<将您的数据 T 序列化或反序列化>
  • Commit<将您的数据 T 序列化或反序列化>
功能标志 目的
serde 启用 serdeSerialize & Deserialize
bincode 启用 bincode 2.0.0-rc.3Encode & Decode
borsh 启用 borshBorshSerialize & BorshDeserialize

MSRV

支持的最低Rust版本是 1.70.0

依赖项

~180–660KB
~12K SLoC