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 在 并发
每月下载量 62 次
150KB
1.5K SLoC
someday
someday
是一个 多版本并发控制 原语。
它是一个无锁的、但内存消耗更大的 Mutex<T>
或 RwLock<T>
的替代品。
无锁
单个 Writer
是无锁的,但可能会复制数据。
当 Writer
想要更新 Reader
的数据时,它必须
- 原子性地更新一个指针,此时所有将来的读者都将看到新的数据
- 重新应用补丁到旧回收的数据 OR 如果无法回收,则复制数据
如果没有任何 Reader
挂在旧的 Commit
上,旧数据可以被 Writer
以较低的成本回收和重用。
如果有 Reader
挂在旧数据上,Writer
将复制数据,以便它可以继续。
用例
someday
在以下情况下表现最佳
您的数据
- 相对便宜地复制和/或去重
并且您有 许多 读者
- 希望无锁地获取数据副本
- 暂时或永久地保留数据
并且有一个 writer
- 希望无锁地修改数据
- 希望尽快无锁地将更改推送到新读者
- 相对于读操作,不经常修改数据
- 通常与使用常规锁(
Mutex
、RwLock
)的读者竞争
权衡
-
内存使用增加:
Writer
至少保留两个备份数据结构副本,而Reader
可以保持无限量(只要它们继续持有引用) -
确定性补丁: 应用到您的数据上的补丁/函数必须是确定的,因为
Writer
可能会两次应用它们 -
慢速写入: 写入比直接针对备份数据结构慢
API
someday
的 API 与 git
类似,并在语义上执行类似操作。
Writer
- 通过调用
add()
将一个Patch
(函数)添加到它们的数据中 - 通过
commit()
执行那些更改 - 可以随时查看本地或远程(读取器)数据
- 可以原子性地将那些更改通过
push()
推送到Reader
的 - 可以在不等待
Reader
的情况下继续写入
Reader(s)
- 可以持续调用
head()
以低成本获取最新的 "head"Commit
- 可以永久保留那些
Commit
对象(尽管这可能以内存使用为代价) - 当
Writer
调用push()
时,最终会赶上
示例
此示例显示了 Writer
的典型用法
- 添加一些更改
- 读取它们本地更改
- 通过调用
commit()
锁定那些更改 - 最后通过调用
push()
将那些更改揭示给读者
和 Reader
- 持续读取它们最新的当前数据 "head"
Commit
- 当
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 |
启用 serde 的 Serialize & Deserialize |
bincode |
启用 bincode 2.0.0-rc.3 的 Encode & Decode |
borsh |
启用 borsh 的 BorshSerialize & BorshDeserialize |
MSRV
支持的最低Rust版本是 1.70.0
。
依赖项
~180–660KB
~12K SLoC