mini-lsm通关笔记Week3Day5
在本章中,您将实现Transaction的所有接口。您的实现将为事务内的修改维护一个私有工作区,并批量提交它们,以便事务内的所有修改在提交之前仅对事务本身可见。我们只在提交时检查冲突(即可序列化冲突),这就是乐观并发控制(OCC)。
要运行测试用例,请执行以下操作:
cargo x copy-test --week 3 --day 5
cargo x scheck
Task 1-Local Workspace + Put and Delete
在此任务中,您需要修改:
src/txn.rs
现在可以通过向
local_storage
中插入相应的key/value
来实现put
和delete
,local_storage
是一个存储没有时间戳的键(key)的memtable。请注意,对于删除,您仍然需要将其实现为插入空值,而不是从跳表中删除值。
实现类似MemTable
,put
插入函数:
pub fn put(&self, key: &[u8], value: &[u8]) {
self.local_storage
.insert(Bytes::copy_from_slice(key), Bytes::copy_from_slice(value));
}
delete
删除函数:
pub fn delete(&self, key: &[u8]) {
self.local_storage
.insert(Bytes::copy_from_slice(key), Bytes::new());
}
Task 2-Get and Scan
在此任务中,您需要修改:
src/txn.rs
对于
get
,您应该首先在local_storage
中查找。如果找到值,则根据是否为删除标记,返回该值或None
。对于scan
,当您为没有时间戳的键(key)的memtable实现迭代器时,您需要为skiplist实现一个TxnLocalIterator
,如第1.1章所述。你需要在TxnIterator
中存储一个TwoMergeIterator<TxnLocalIterator, FusedIterator<LsmIterator>>
。最后,鉴于TwoMergeIterator
将保留子迭代器中的删除标记,您需要修改TxnIterator
实现以正确处理删除的记录。
TxnLocalIterator
这个迭代器,用于遍历local_storage
。该部分的实现完全可以参照MemTableIterator
:
impl TxnLocalIterator {
fn entry_to_item(entry: Option<Entry<'_, Bytes, Bytes>>) -> (Bytes, Bytes) {
entry
.map(|x| (x.key().clone(), x.value().clone()))
.unwrap_or_else(|| (Bytes::new(), Bytes::new()))
}
}
impl StorageIterator for TxnLocalIterator {
type KeyType<'a> = &'a [u8];
fn value(&self) -> &[u8] {
&self.borrow_item().1[..]
}
fn key(&self) -> &[u8] {
&self.borrow_item().0[..]
}
fn is_valid(&self) -> bool {
!self.borrow_item().0.is_empty()
}
fn next(&mut self) -> Result<()> {
let entry = self.with_iter_mut(|iter| TxnLocalIterator::entry_to_item(iter.next()));
self.with_mut(|x| *x.item = entry);
Ok(())
}
}
pub fn scan(self: &Arc<Self>, lower: Bound<&[u8]>, upper: Bound<&[u8]>) -> Result<TxnIterator> {
let mut local_iter = TxnLocalIteratorBuilder {
map: self.local_storage.clone(),
iter_builder: |map| map.range((map_bound(lower), map_bound(upper))),
item: (Bytes::new(), Bytes::new()),
}
.build();
let entry = local_iter.with_iter_mut(|iter| TxnLocalIterator::entry_to_item(iter.next()));
local_iter.with_mut(|x| *x.item = entry);
...
}
TxnIterator
内部有一个TwoMergeIterator<TxnLocalIterator, FusedIterator<LsmIterator>>
类型的迭代器,需要在此基础上,实现跳过local_storage
中被删除记录的操作。
impl TxnIterator {
pub fn create(
txn: Arc<Transaction>,
iter: TwoMergeIterator<TxnLocalIterator, FusedIterator<LsmIterator>>,
) -> Result<Self> {
let mut iter = Self { _txn: txn, iter };
iter.skip_deletes()?; // 【新增】跳过被删除记录
Ok(iter)
}
// 【新增】跳过被删除记录
fn skip_deletes(&mut self) -> Result<()> {
while self.iter.is_valid() && self.iter.value().is_empty() {
self.iter.next()?;
}
Ok(())
}
}
impl StorageIterator for TxnIterator {
...
fn next(&mut self) -> Result<()> {
self.iter.next()?;
self.skip_deletes()?; // 【新增】跳过被删除记录
Ok(())
}
...
}
Task 3-Commit
在此任务中,您需要修改:
src/txn.rs
我们假设事务只能在单个线程上使用。一旦你的事务进入提交阶段,你应该将
self.commited
设置为true,这样用户就不能对事务做任何其他操作了。如果事务已经提交,则put
、delete
、scan
和get
实现应该出错。您的提交实现应该简单地从本地存储收集所有键值对,并向存储引擎提交一个写入批处理。
先使用原子操作修改committed
变量,在生成批量操作,再执行。
pub fn commit(&self) -> Result<()> {
self.committed
.compare_exchange(false, true, Ordering::SeqCst, Ordering::SeqCst)
.expect("cannot operate on committed txn!");
let batch = self
.local_storage
.iter()
.map(|entry| {
if entry.value().is_empty() {
WriteBatchRecord::Del(entry.key().clone())
} else {
WriteBatchRecord::Put(entry.key().clone(), entry.value().clone())
}
})
.collect::<Vec<_>>();
self.inner.write_batch(&batch)
}
同时在put
、delete
、scan
和get
函数的开始添加:
if self.committed.load(Ordering::SeqCst) {
panic!("cannot operate on committed txn!");
}
Task 4-Atomic WAL
在此任务中,您需要修改:
src/wal.rs src/mem_table.rs
请注意,提交涉及到产生一个写批次,目前,写批次并不保证原子性。您需要更改WAL实现以生成写入批处理的页眉和页脚。
新的WAL编码如下:
| HEADER | BODY | FOOTER | | u32 | u16 | var | u64 | u16 | var | ... | u32 | | batch_size | key_len | key | ts | value_len | value | more key-value pairs ... | checksum |
batch_size
是BODY
部分的大小,checksum
是BODY部分的校验和。没有测试用例来验证您的实现。只要你通过了所有现有的测试用例,并实现了上面的WAL格式,一切都应该没问题。
您应该实现Wal::put_batch和MemTable::put_batch。原始的put函数应该将单个键值对视为一个批处理。也就是说,此时,你的put函数应该调用put_batch。
一个批处理应该在同一个mem表和同一个WAL中处理,即使它超过mem表大小限制。
跳过,查看skyzh
的实现也没有相关实现。
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 微软正式发布.NET 10 Preview 1:开启下一代开发框架新篇章
· 没有源码,如何修改代码逻辑?
· PowerShell开发游戏 · 打蜜蜂
· 在鹅厂做java开发是什么体验
· WPF到Web的无缝过渡:英雄联盟客户端的OpenSilver迁移实战