Skip to content

Commit 6b24d6d

Browse files
committed
finish persistence
Signed-off-by: Alex Chi <iskyzh@gmail.com>
1 parent 13ae8fe commit 6b24d6d

9 files changed

Lines changed: 218 additions & 49 deletions

File tree

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,3 +2,4 @@
22
.vscode/
33
sync-tmp/
44
mini-lsm.db/
5+
lsm.db/

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ We are working on a new version of the mini-lsm tutorial that is split into 3 we
5050
| 2.4 | Compaction Strategy - Leveled || | |
5151
| 2.5 | Manifest | 🚧 | | |
5252
| 2.6 | Write-Ahead Log | 🚧 | | |
53-
| 2.7 | Batch Write (and preparations for MVCC) | | | |
53+
| 2.7 | Batch Write + Checksum | | | |
5454
| 3.1 | Timestamp Encoding + Prefix Bloom Filter | | | |
5555
| 3.2 | Snapshot Read | | | |
5656
| 3.3 | Watermark and Garbage Collection | | | |

mini-lsm/src/bin/mini_lsm_cli.rs

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@ use std::path::PathBuf;
33
use anyhow::Result;
44
use bytes::Bytes;
55
use clap::{Parser, ValueEnum};
6-
76
use mini_lsm::compact::{
87
CompactionOptions, LeveledCompactionOptions, SimpleLeveledCompactionOptions,
98
TieredCompactionOptions,
@@ -21,11 +20,11 @@ enum CompactionStrategy {
2120
#[derive(Parser, Debug)]
2221
#[command(author, version, about, long_about = None)]
2322
struct Args {
24-
#[arg(long, default_value = "mini-lsm.db")]
23+
#[arg(long, default_value = "lsm.db")]
2524
path: PathBuf,
2625
#[arg(long, default_value = "leveled")]
2726
compaction: CompactionStrategy,
28-
#[arg(long)]
27+
#[arg(long, default_value = "true")]
2928
enable_wal: bool,
3029
}
3130

mini-lsm/src/compact.rs

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ pub(crate) enum CompactionController {
4646
}
4747

4848
impl CompactionController {
49-
fn generate_compaction_task(&self, snapshot: &LsmStorageState) -> Option<CompactionTask> {
49+
pub fn generate_compaction_task(&self, snapshot: &LsmStorageState) -> Option<CompactionTask> {
5050
match self {
5151
CompactionController::Leveled(ctrl) => ctrl
5252
.generate_compaction_task(&snapshot)
@@ -61,7 +61,7 @@ impl CompactionController {
6161
}
6262
}
6363

64-
fn apply_compaction_result(
64+
pub fn apply_compaction_result(
6565
&self,
6666
snapshot: &LsmStorageState,
6767
task: &CompactionTask,
@@ -247,14 +247,16 @@ impl LsmStorageInner {
247247
assert!(result.is_some());
248248
ssts_to_remove.push(result.unwrap());
249249
}
250+
let mut new_sst_ids = Vec::new();
250251
for file_to_add in sstables {
252+
new_sst_ids.push(file_to_add.sst_id());
251253
let result = snapshot.sstables.insert(file_to_add.sst_id(), file_to_add);
252254
assert!(result.is_none());
253255
}
254256
let mut state = self.state.write();
255257
*state = Arc::new(snapshot);
256258
self.manifest
257-
.add_record(&state_lock, ManifestRecord::Compaction(task))?;
259+
.add_record(&state_lock, ManifestRecord::Compaction(task, new_sst_ids))?;
258260
ssts_to_remove
259261
};
260262
for sst in ssts_to_remove {

mini-lsm/src/lsm_storage.rs

Lines changed: 99 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
1-
use std::collections::HashMap;
1+
use std::collections::{BTreeSet, HashMap, HashSet};
22
use std::fs::File;
33
use std::ops::Bound;
44
use std::path::{Path, PathBuf};
55
use std::sync::atomic::AtomicUsize;
66
use std::sync::Arc;
77

8-
use anyhow::Result;
8+
use anyhow::{Context, Result};
99
use bytes::Bytes;
1010
use parking_lot::{Mutex, RwLock};
1111

@@ -20,7 +20,7 @@ use crate::iterators::StorageIterator;
2020
use crate::lsm_iterator::{FusedIterator, LsmIterator};
2121
use crate::manifest::{Manifest, ManifestRecord};
2222
use crate::mem_table::{map_bound, MemTable};
23-
use crate::table::{SsTable, SsTableBuilder, SsTableIterator};
23+
use crate::table::{self, FileObject, SsTable, SsTableBuilder, SsTableIterator};
2424

2525
pub type BlockCache = moka::sync::Cache<(usize, usize), Arc<Block>>;
2626

@@ -103,6 +103,7 @@ impl Drop for MiniLsm {
103103

104104
impl MiniLsm {
105105
pub fn close(&self) -> Result<()> {
106+
self.inner.sync_dir()?;
106107
self.compaction_notifier.send(()).ok();
107108
let mut compaction_thread = self.compaction_thread.lock();
108109
if let Some(compaction_thread) = compaction_thread.take() {
@@ -157,36 +158,101 @@ impl LsmStorageInner {
157158
}
158159

159160
pub(crate) fn open(path: impl AsRef<Path>, options: LsmStorageOptions) -> Result<Self> {
161+
let mut state = LsmStorageState::create(&options);
160162
let path = path.as_ref();
163+
let mut next_sst_id = 1;
164+
let block_cache = Arc::new(BlockCache::new(1 << 20)); // 4GB block cache,
165+
let manifest;
166+
167+
let compaction_controller = match &options.compaction_options {
168+
CompactionOptions::Leveled(options) => {
169+
CompactionController::Leveled(LeveledCompactionController::new(options.clone()))
170+
}
171+
CompactionOptions::Tiered(options) => {
172+
CompactionController::Tiered(TieredCompactionController::new(options.clone()))
173+
}
174+
CompactionOptions::Simple(options) => CompactionController::Simple(
175+
SimpleLeveledCompactionController::new(options.clone()),
176+
),
177+
CompactionOptions::NoCompaction => CompactionController::NoCompaction,
178+
};
179+
161180
if !path.exists() {
162-
std::fs::create_dir_all(path)?;
163-
}
164-
let mut state = LsmStorageState::create(&options);
165-
if options.enable_wal {
166-
state.memtable = Arc::new(MemTable::create_with_wal(
167-
state.memtable.id(),
168-
Self::path_of_wal_static(path, state.memtable.id()),
169-
)?);
170-
}
181+
std::fs::create_dir_all(path).context("failed to create DB dir")?;
182+
if options.enable_wal {
183+
state.memtable = Arc::new(MemTable::create_with_wal(
184+
state.memtable.id(),
185+
Self::path_of_wal_static(path, state.memtable.id()),
186+
)?);
187+
}
188+
manifest =
189+
Manifest::create(path.join("MANIFEST")).context("failed to create manifest")?;
190+
manifest.add_record_when_init(ManifestRecord::NewMemtable(state.memtable.id()))?;
191+
} else {
192+
let (m, records) = Manifest::recover(path.join("MANIFEST"))?;
193+
let mut memtables = BTreeSet::new();
194+
for record in records {
195+
match record {
196+
ManifestRecord::Flush(sst_id) => {
197+
let res = memtables.remove(&sst_id);
198+
assert!(res, "memtable not exist?");
199+
state.l0_sstables.insert(0, sst_id);
200+
}
201+
ManifestRecord::NewMemtable(x) => {
202+
next_sst_id = x + 1;
203+
memtables.insert(x);
204+
}
205+
ManifestRecord::Compaction(task, output) => {
206+
let (new_state, _) =
207+
compaction_controller.apply_compaction_result(&state, &task, &output);
208+
// TODO: apply remove again
209+
state = new_state;
210+
}
211+
}
212+
}
213+
// recover SSTs
214+
for table_id in state
215+
.l0_sstables
216+
.iter()
217+
.chain(state.levels.iter().map(|(_, files)| files).flatten())
218+
{
219+
let table_id = *table_id;
220+
let sst = SsTable::open(
221+
table_id,
222+
Some(block_cache.clone()),
223+
FileObject::open(&Self::path_of_sst_static(path, table_id))
224+
.context("failed to open SST")?,
225+
)?;
226+
state.sstables.insert(table_id, Arc::new(sst));
227+
}
228+
// recover memtables
229+
if options.enable_wal {
230+
for id in memtables.iter() {
231+
let memtable =
232+
MemTable::recover_from_wal(*id, Self::path_of_wal_static(path, *id))?;
233+
state.imm_memtables.insert(0, Arc::new(memtable));
234+
next_sst_id = *id + 1;
235+
}
236+
state.memtable = Arc::new(MemTable::create_with_wal(
237+
next_sst_id,
238+
Self::path_of_wal_static(path, next_sst_id),
239+
)?);
240+
} else {
241+
state.memtable = Arc::new(MemTable::create(next_sst_id));
242+
}
243+
m.add_record_when_init(ManifestRecord::NewMemtable(state.memtable.id()))?;
244+
next_sst_id += 1;
245+
manifest = m;
246+
};
247+
171248
let storage = Self {
172249
state: Arc::new(RwLock::new(Arc::new(state))),
173250
state_lock: Mutex::new(()),
174251
path: path.to_path_buf(),
175-
block_cache: Arc::new(BlockCache::new(1 << 20)), // 4GB block cache,
176-
next_sst_id: AtomicUsize::new(1),
177-
compaction_controller: match &options.compaction_options {
178-
CompactionOptions::Leveled(options) => {
179-
CompactionController::Leveled(LeveledCompactionController::new(options.clone()))
180-
}
181-
CompactionOptions::Tiered(options) => {
182-
CompactionController::Tiered(TieredCompactionController::new(options.clone()))
183-
}
184-
CompactionOptions::Simple(options) => CompactionController::Simple(
185-
SimpleLeveledCompactionController::new(options.clone()),
186-
),
187-
CompactionOptions::NoCompaction => CompactionController::NoCompaction,
188-
},
189-
manifest: Manifest::create(path.join("MANIFEST"))?,
252+
block_cache,
253+
next_sst_id: AtomicUsize::new(next_sst_id),
254+
compaction_controller,
255+
manifest,
190256
options: options.into(),
191257
};
192258
storage.sync_dir()?;
@@ -259,8 +325,12 @@ impl LsmStorageInner {
259325
Ok(())
260326
}
261327

328+
pub(crate) fn path_of_sst_static(path: impl AsRef<Path>, id: usize) -> PathBuf {
329+
path.as_ref().join(format!("{:05}.sst", id))
330+
}
331+
262332
pub(crate) fn path_of_sst(&self, id: usize) -> PathBuf {
263-
self.path.join(format!("{:05}.sst", id))
333+
Self::path_of_sst_static(&self.path, id)
264334
}
265335

266336
pub(crate) fn path_of_wal_static(path: impl AsRef<Path>, id: usize) -> PathBuf {
@@ -303,7 +373,7 @@ impl LsmStorageInner {
303373
old_memtable.sync_wal()?;
304374

305375
self.manifest
306-
.add_record(&state_lock, ManifestRecord::NewWal(memtable_id))?;
376+
.add_record(&state_lock, ManifestRecord::NewMemtable(memtable_id))?;
307377

308378
Ok(())
309379
}

mini-lsm/src/manifest.rs

Lines changed: 45 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,36 +1,74 @@
1-
use std::fs::File;
1+
use std::fs::{File, OpenOptions};
2+
use std::io::{Read, Write};
23
use std::path::Path;
34
use std::sync::Arc;
45

5-
use anyhow::Result;
6+
use anyhow::{Context, Result};
67
use parking_lot::{Mutex, MutexGuard};
78
use serde::{Deserialize, Serialize};
9+
use serde_json::Deserializer;
810

911
use crate::compact::CompactionTask;
1012

11-
pub struct Manifest {}
13+
pub struct Manifest {
14+
file: Arc<Mutex<File>>,
15+
}
1216

1317
#[derive(Serialize, Deserialize)]
1418
pub enum ManifestRecord {
1519
Flush(usize),
16-
NewWal(usize),
17-
Compaction(CompactionTask),
20+
NewMemtable(usize),
21+
Compaction(CompactionTask, Vec<usize>),
1822
}
1923

2024
impl Manifest {
2125
pub fn create(path: impl AsRef<Path>) -> Result<Self> {
22-
Ok(Self {})
26+
Ok(Self {
27+
file: Arc::new(Mutex::new(
28+
OpenOptions::new()
29+
.read(true)
30+
.create_new(true)
31+
.write(true)
32+
.open(path)
33+
.context("failed to create manifest")?,
34+
)),
35+
})
2336
}
2437

2538
pub fn recover(path: impl AsRef<Path>) -> Result<(Self, Vec<ManifestRecord>)> {
26-
Ok((Self {}, Vec::new()))
39+
let mut file = OpenOptions::new()
40+
.read(true)
41+
.append(true)
42+
.open(path)
43+
.context("failed to recover manifest")?;
44+
let mut buf = Vec::new();
45+
file.read_to_end(&mut buf)?;
46+
let mut stream = Deserializer::from_slice(&buf).into_iter::<ManifestRecord>();
47+
let mut records = Vec::new();
48+
while let Some(x) = stream.next() {
49+
records.push(x?);
50+
}
51+
Ok((
52+
Self {
53+
file: Arc::new(Mutex::new(file)),
54+
},
55+
records,
56+
))
2757
}
2858

2959
pub fn add_record(
3060
&self,
3161
_state_lock_observer: &MutexGuard<()>,
3262
record: ManifestRecord,
3363
) -> Result<()> {
64+
self.add_record_when_init(record)
65+
}
66+
67+
pub fn add_record_when_init(&self, record: ManifestRecord) -> Result<()> {
68+
let mut file = self.file.lock();
69+
let buf = serde_json::to_vec(&record)?;
70+
file.write(&buf)?;
71+
file.sync_all()?;
3472
Ok(())
3573
}
3674
}

mini-lsm/src/mem_table.rs

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,8 +47,14 @@ impl MemTable {
4747
})
4848
}
4949

50+
/// Create a memtable from WAL
5051
pub fn recover_from_wal(id: usize, path: impl AsRef<Path>) -> Result<Self> {
51-
unimplemented!()
52+
let map = Arc::new(SkipMap::new());
53+
Ok(Self {
54+
id,
55+
wal: Some(Wal::recover(path.as_ref(), &map)?),
56+
map,
57+
})
5258
}
5359

5460
/// Get a value by key.

mini-lsm/src/table.rs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -102,8 +102,10 @@ impl FileObject {
102102
))
103103
}
104104

105-
pub fn open(_path: &Path) -> Result<Self> {
106-
unimplemented!()
105+
pub fn open(path: &Path) -> Result<Self> {
106+
let file = File::options().read(true).write(false).open(path)?;
107+
let size = file.metadata()?.len();
108+
Ok(FileObject(Some(file), size))
107109
}
108110
}
109111

0 commit comments

Comments
 (0)