Skip to content

Commit 45312df

Browse files
committed
add wasm compatibility
1 parent d065d40 commit 45312df

40 files changed

Lines changed: 926 additions & 936 deletions

Cargo.lock

Lines changed: 0 additions & 21 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,6 @@ guardian = "1.3.0"
3939
scopeguard = "1.2.0"
4040
memmap2 = "0.9.5"
4141
interval-heap = "0.0.5"
42-
thiserror = "1.0"
4342

4443
[target.'cfg(not(target_arch = "wasm32"))'.dependencies]
4544
fs2 = "0.4.3"

README.md

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,6 @@ let tree = TreeBuilder::new()
5555
.with_max_memtable_size(100 * 1024 * 1024) // 100MB memtable size
5656
.with_block_size(4096) // 4KB block size
5757
.with_level_count(1) // Number of levels in LSM tree
58-
.with_vlog_value_threshold(4096) // Values smaller than this stored inline
5958
.with_vlog_max_file_size(128 * 1024 * 1024) // 128MB VLog file size
6059
.with_enable_vlog(true) // Enable/disable VLog
6160
.build()?
@@ -70,7 +69,6 @@ let tree = TreeBuilder::new()
7069

7170
### VLog Options
7271

73-
- `vlog_value_threshold`: Values smaller than this are stored inline in SSTables
7472
- `vlog_max_file_size`: Maximum size of VLog files before rotation
7573
- `with_enable_vlog()`: Enable/disable Value Log for large value storage
7674
- `vlog_gc_discard_ratio`: Threshold for triggering VLog garbage collection

src/batch.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -628,7 +628,8 @@ mod tests {
628628
// Now they should encode identically (both write value_len=0)
629629
assert_eq!(encoded_none, encoded_empty, "None and Some(&[]) should now encode identically");
630630

631-
// Test reading them back - both should return None (since both encode as value_len=0)
631+
// Test reading them back - both should return None (since both encode as
632+
// value_len=0)
632633
let mut reader_none = BatchReader::new(&encoded_none).unwrap();
633634
let (kind1, key1, value1) = reader_none.read_record().unwrap().unwrap();
634635
assert_eq!(kind1, InternalKeyKind::Delete);

src/cache.rs

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
1-
use quick_cache::Weighter;
2-
use quick_cache::{sync::Cache as QCache, Equivalent};
1+
use quick_cache::sync::Cache as QCache;
2+
use quick_cache::{Equivalent, Weighter};
33
use std::sync::atomic::AtomicU64;
44
use std::sync::Arc;
55

6-
use crate::sstable::{block::Block, InternalKeyTrait};
6+
use crate::sstable::block::Block;
7+
use crate::sstable::InternalKeyTrait;
78
use crate::Value;
89

910
pub type CacheID = u64;

src/checkpoint.rs

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,8 @@ impl CheckpointMetadata {
6767
}
6868
}
6969

70-
/// Checks if this metadata version is compatible with current implementation
70+
/// Checks if this metadata version is compatible with current
71+
/// implementation
7172
pub fn is_compatible(&self) -> bool {
7273
// For now, we only support version 1
7374
self.version == CHECKPOINT_VERSION
@@ -259,8 +260,9 @@ impl<K: InternalKeyTrait> DatabaseCheckpoint<K> {
259260

260261
/// Flushes all memtables to ensure checkpoint consistency
261262
fn flush_all_memtables(&self) -> Result<()> {
262-
// Keep calling compact_memtable until all memtables (active + immutable) are flushed
263-
// compact_memtable already handles the logic of checking if there's anything to flush
263+
// Keep calling compact_memtable until all memtables (active + immutable) are
264+
// flushed compact_memtable already handles the logic of checking if there's
265+
// anything to flush
264266
loop {
265267
// Check if there are any memtables to flush
266268
let has_active = {
@@ -381,7 +383,8 @@ impl<K: InternalKeyTrait> DatabaseCheckpoint<K> {
381383
CheckpointMetadata::from_bytes(&data)
382384
}
383385

384-
/// Helper function to copy a directory recursively (synchronous version to avoid recursion issues)
386+
/// Helper function to copy a directory recursively (synchronous version to
387+
/// avoid recursion issues)
385388
fn copy_directory_sync(source: &Path, dest: &Path) -> Result<u64> {
386389
if !source.exists() {
387390
return Ok(0);
@@ -484,7 +487,8 @@ mod tests {
484487
let bytes = metadata.to_bytes().unwrap();
485488

486489
// Verify the binary format structure
487-
// 4 bytes version + 8 bytes timestamp + 8 bytes seq + 8 bytes count + 8 bytes size = 36 bytes
490+
// 4 bytes version + 8 bytes timestamp + 8 bytes seq + 8 bytes count + 8 bytes
491+
// size = 36 bytes
488492
assert_eq!(bytes.len(), 36);
489493

490494
// Check that version is at the beginning (big endian)

src/commit.rs

Lines changed: 39 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,12 @@
11
// This commit pipeline is inspired by Pebble's commit pipeline.
22

3-
use std::sync::{
4-
atomic::{AtomicBool, AtomicPtr, AtomicU64, Ordering},
5-
Arc, Mutex,
6-
};
3+
use std::sync::atomic::{AtomicBool, AtomicPtr, AtomicU64, Ordering};
4+
use std::sync::{Arc, Mutex};
75

86
use tokio::sync::{oneshot, Semaphore};
97

10-
use crate::{
11-
batch::Batch,
12-
error::{Error, Result},
13-
};
8+
use crate::batch::Batch;
9+
use crate::error::{Error, Result};
1410

1511
const MAX_CONCURRENT_COMMITS: usize = 16;
1612
const DEQUEUE_BITS: u32 = 32;
@@ -132,7 +128,8 @@ impl CommitQueue {
132128
self.head_tail.fetch_add(1 << DEQUEUE_BITS, Ordering::Release);
133129
}
134130

135-
// Multi-consumer dequeue - removes the earliest enqueued Batch, if it is applied
131+
// Multi-consumer dequeue - removes the earliest enqueued Batch, if it is
132+
// applied
136133
fn dequeue_applied(&self) -> Option<Arc<CommitBatch>> {
137134
loop {
138135
let ptrs = self.head_tail.load(Ordering::Acquire);
@@ -210,8 +207,8 @@ impl CommitPipeline {
210207
}
211208

212209
/// Synchronous commit that bypasses the async pipeline machinery.
213-
/// This is suitable for cases where no flow control or async coordination is needed,
214-
/// such as delete list updates during compaction.
210+
/// This is suitable for cases where no flow control or async coordination
211+
/// is needed, such as delete list updates during compaction.
215212
pub(crate) fn sync_commit(&self, batch: Batch, sync_wal: bool) -> Result<()> {
216213
if self.shutdown.load(Ordering::Acquire) {
217214
return Err(Error::PipelineStall);
@@ -372,7 +369,7 @@ mod tests {
372369
use std::time::Duration;
373370

374371
use crate::sstable::InternalKeyKind;
375-
use crate::spawn::spawn;
372+
use crate::util::spawn;
376373

377374
use super::*;
378375

@@ -435,11 +432,14 @@ mod tests {
435432
#[tokio::test(flavor = "multi_thread", worker_threads = 4)]
436433
async fn test_concurrent_commits() {
437434
let pipeline = CommitPipeline::new(Arc::new(MockEnv));
435+
let num_tasks = 10;
436+
let (result_sender, result_receiver) = async_channel::bounded(num_tasks);
438437

439-
let mut handles = vec![];
440-
for i in 0..10 {
438+
// Spawn concurrent commit tasks
439+
for i in 0..num_tasks {
441440
let pipeline = pipeline.clone();
442-
let handle = spawn(async move {
441+
let sender = result_sender.clone();
442+
spawn(async move {
443443
let mut batch = Batch::new();
444444
batch
445445
.add_record(
@@ -448,13 +448,20 @@ mod tests {
448448
Some(&[1, 2, 3]),
449449
)
450450
.unwrap();
451-
pipeline.commit(batch, false).await
451+
let result = pipeline.commit(batch, false).await;
452+
let _ = sender.send((i, result)).await;
452453
});
453-
handles.push(handle);
454454
}
455455

456-
for (i, handle) in handles.into_iter().enumerate() {
457-
let result = handle.await.unwrap();
456+
// Collect all results
457+
let mut results = Vec::new();
458+
for _ in 0..num_tasks {
459+
let (i, result) = result_receiver.recv().await.unwrap();
460+
results.push((i, result));
461+
}
462+
463+
// Verify all commits succeeded
464+
for (i, result) in results {
458465
assert!(result.is_ok(), "Commit {i} failed: {result:?}");
459466
}
460467

@@ -494,11 +501,14 @@ mod tests {
494501
#[tokio::test(flavor = "multi_thread")]
495502
async fn test_concurrent_commits_with_delays() {
496503
let pipeline = CommitPipeline::new(Arc::new(DelayedMockEnv));
504+
let num_tasks = 5;
505+
let (result_sender, result_receiver) = async_channel::bounded(5);
497506

498-
let mut handles = vec![];
499-
for i in 0..5 {
507+
// Spawn concurrent commit tasks
508+
for i in 0..num_tasks {
500509
let pipeline = pipeline.clone();
501-
let handle = spawn(async move {
510+
let sender = result_sender.clone();
511+
spawn(async move {
502512
let mut batch = Batch::new();
503513
batch
504514
.add_record(
@@ -507,17 +517,19 @@ mod tests {
507517
Some(&[1, 2, 3]),
508518
)
509519
.unwrap();
510-
pipeline.commit(batch, false).await
520+
let result = pipeline.commit(batch, false).await;
521+
let _ = sender.send(result).await;
511522
});
512-
handles.push(handle);
513523
}
514524

515-
for handle in handles {
516-
assert!(handle.await.unwrap().is_ok());
525+
// Collect all results
526+
for _ in 0..num_tasks {
527+
let res = result_receiver.recv().await.unwrap();
528+
assert!(res.is_ok());
517529
}
518530

519531
// Verify sequence numbers are published correctly
520-
assert_eq!(pipeline.get_visible_seq_num(), 5);
532+
assert_eq!(pipeline.get_visible_seq_num(), num_tasks);
521533

522534
// Shutdown the pipeline
523535
pipeline.shutdown();

src/compaction/compactor.rs

Lines changed: 18 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,19 @@
1-
use crate::{
2-
compaction::{CompactionChoice, CompactionInput, CompactionStrategy},
3-
error::Result,
4-
iter::{BoxedIterator, CompactionIterator},
5-
levels::{write_manifest_to_disk, LevelManifest, ManifestChangeSet},
6-
lsm::CoreInner,
7-
memtable::ImmutableMemtables,
8-
sstable::{
9-
table::{Table, TableWriter},
10-
InternalKeyTrait,
11-
},
12-
vfs::File,
13-
vlog::VLog,
14-
Options as LSMOptions,
15-
};
16-
17-
use std::{
18-
collections::HashMap,
19-
fs::File as SysFile,
20-
path::{Path, PathBuf},
21-
sync::{Arc, RwLock, RwLockWriteGuard},
22-
};
1+
use crate::compaction::{CompactionChoice, CompactionInput, CompactionStrategy};
2+
use crate::error::Result;
3+
use crate::iter::{BoxedIterator, CompactionIterator};
4+
use crate::levels::{write_manifest_to_disk, LevelManifest, ManifestChangeSet};
5+
use crate::lsm::CoreInner;
6+
use crate::memtable::ImmutableMemtables;
7+
use crate::sstable::table::{Table, TableWriter};
8+
use crate::sstable::InternalKeyTrait;
9+
use crate::vfs::File;
10+
use crate::vlog::VLog;
11+
use crate::Options as LSMOptions;
12+
13+
use std::collections::HashMap;
14+
use std::fs::File as SysFile;
15+
use std::path::{Path, PathBuf};
16+
use std::sync::{Arc, RwLock, RwLockWriteGuard};
2317

2418
/// Compaction options
2519
pub(crate) struct CompactionOptions<K: InternalKeyTrait> {
@@ -175,7 +169,8 @@ impl<K: InternalKeyTrait> Compactor<K> {
175169
// Create a changeset for the compaction
176170
let mut changeset = ManifestChangeSet::<K>::default();
177171

178-
// Add tables to delete (the ones being merged) - use the same efficient approach as original
172+
// Add tables to delete (the ones being merged) - use the same efficient
173+
// approach as original
179174
for (level_idx, level) in manifest.levels.get_levels().iter().enumerate() {
180175
for &table_id in &input.tables_to_merge {
181176
if level.tables.iter().any(|t| t.id == table_id) {

0 commit comments

Comments
 (0)