Skip to content

Commit 4e5370f

Browse files
authored
Merge pull request #769 from spacejam/tyler_0.26
cut 0.26
2 parents 2811bd5 + ed5fb80 commit 4e5370f

File tree

6 files changed

+115
-55
lines changed

6 files changed

+115
-55
lines changed

CHANGELOG.md

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
# 0.26
2+
3+
## New Features
4+
5+
* Transactions! You may now call `Tree::transaction` and
6+
perform reads, writes, and deletes within a provided
7+
closure with a `TransactionalTree` argument. This
8+
closure may be called multiple times if the transaction
9+
encounters a concurrent update in the process of its
10+
execution. Transactions may also be used on tuples of
11+
`Tree` objects, where the closure will then be
12+
parameterized on `TransactionalTree` instances providing
13+
access to each of the provided `Tree` instances. This
14+
allows you to atomically read and modify multiple
15+
`Tree` instances in a single atomic operation.
16+
These transactions are serializable, fully ACID,
17+
and optimistic.
18+
* `Tree::apply_batch` allows you to apply a `Batch`
19+
* `TransactionalTree::apply_batch` allow you to
20+
apply a `Batch` from within a transaction.
21+
22+
## Breaking Changes
23+
24+
* `Tree::batch` has been removed. Now you can directly
25+
create a `Batch` with `Batch::default()` and then apply
26+
it to a `Tree` with `Tree::apply_batch` or during a
27+
transaction using `TransactionalTree::apply_batch`.
28+
This facilitates multi-`Tree` batches via transactions.
29+
* `Event::Merge` has been removed, and `Tree::merge` will
30+
now send a complete `Event::Set` item to be distributed
31+
to all listening subscribers.

crates/pagecache/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[package]
22
name = "pagecache"
3-
version = "0.18.0"
3+
version = "0.19.0"
44
authors = ["Tyler Neely <[email protected]>"]
55
description = "lock-free pagecache and log for high-performance databases"
66
license = "MIT/Apache-2.0"

crates/sled/Cargo.toml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[package]
22
name = "sled"
3-
version = "0.25.0"
3+
version = "0.26.0"
44
authors = ["Tyler Neely <[email protected]>"]
55
description = "a modern embedded database"
66
license = "MIT/Apache-2.0"
@@ -25,7 +25,7 @@ measure_allocs = ["pagecache/measure_allocs"]
2525
check_snapshot_integrity = ["pagecache/check_snapshot_integrity"]
2626

2727
[dependencies]
28-
pagecache = { path = "../pagecache", version = "0.18" }
28+
pagecache = { path = "../pagecache", version = "0.19" }
2929
serde_bytes = "0.11"
3030
parking_lot = "0.9.0"
3131

crates/sled/src/flusher.rs

Lines changed: 24 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -83,30 +83,6 @@ fn run(
8383
if !shutdown.is_running() {
8484
break;
8585
}
86-
// we had no dirty data to flush,
87-
// so we can spend a little effort
88-
// cleaning up the file. try not to
89-
// spend more than half of our sleep
90-
// time rewriting pages though.
91-
while before.elapsed() < flush_every / 2 {
92-
match pagecache.attempt_gc() {
93-
Err(e) => {
94-
error!(
95-
"failed to clean file from async flush thread: {}",
96-
e
97-
);
98-
99-
#[cfg(feature = "failpoints")]
100-
pagecache.set_failpoint(e);
101-
102-
*shutdown = ShutdownState::ShutDown;
103-
sc.notify_all();
104-
return;
105-
}
106-
Ok(false) => break,
107-
Ok(true) => {}
108-
}
109-
}
11086
}
11187
Ok(_) => {
11288
wrote_data = true;
@@ -127,6 +103,30 @@ fn run(
127103
}
128104
}
129105

106+
// so we can spend a little effort
107+
// cleaning up the segments. try not to
108+
// spend more than half of our sleep
109+
// time rewriting pages though.
110+
while shutdown.is_running() && before.elapsed() < flush_every / 2 {
111+
match pagecache.attempt_gc() {
112+
Err(e) => {
113+
error!(
114+
"failed to clean file from periodic flush thread: {}",
115+
e
116+
);
117+
118+
#[cfg(feature = "failpoints")]
119+
pagecache.set_failpoint(e);
120+
121+
*shutdown = ShutdownState::ShutDown;
122+
sc.notify_all();
123+
return;
124+
}
125+
Ok(false) => break,
126+
Ok(true) => {}
127+
}
128+
}
129+
130130
let sleep_duration = flush_every
131131
.checked_sub(before.elapsed())
132132
.unwrap_or(Duration::from_millis(1));

crates/sled/src/subscription.rs

Lines changed: 11 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -19,18 +19,16 @@ static ID_GEN: AtomicUsize = AtomicUsize::new(0);
1919
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord)]
2020
pub enum Event {
2121
/// A new complete (key, value) pair
22-
Set(Vec<u8>, IVec),
23-
/// A new partial (key, merged value) pair
24-
Merge(Vec<u8>, IVec),
22+
Set(Arc<[u8]>, IVec),
2523
/// A deleted key
26-
Del(Vec<u8>),
24+
Del(Arc<[u8]>),
2725
}
2826

2927
impl Event {
3028
/// Return a reference to the key that this `Event` refers to
3129
pub fn key(&self) -> &[u8] {
3230
match self {
33-
Event::Set(k, ..) | Event::Merge(k, ..) | Event::Del(k) => &*k,
31+
Event::Set(k, ..) | Event::Del(k) => &*k,
3432
}
3533
}
3634
}
@@ -41,7 +39,6 @@ impl Clone for Event {
4139

4240
match self {
4341
Set(k, v) => Set(k.clone(), v.clone()),
44-
Merge(k, v) => Merge(k.clone(), v.clone()),
4542
Del(k) => Del(k.clone()),
4643
}
4744
}
@@ -181,31 +178,31 @@ fn basic_subscription() {
181178

182179
let mut s1 = subs.register([].to_vec());
183180

184-
let k2 = vec![];
181+
let k2: Arc<[u8]> = vec![].into();
185182
let r2 = subs.reserve(&k2).unwrap();
186183
r2.complete(Event::Set(k2.clone(), IVec::from(k2.clone())));
187184

188-
let k3 = vec![0];
185+
let k3: Arc<[u8]> = vec![0].into();
189186
let r3 = subs.reserve(&k3).unwrap();
190187
r3.complete(Event::Set(k3.clone(), IVec::from(k3.clone())));
191188

192-
let k4 = vec![0, 1];
189+
let k4: Arc<[u8]> = vec![0, 1].into();
193190
let r4 = subs.reserve(&k4).unwrap();
194191
r4.complete(Event::Del(k4.clone()));
195192

196-
let k5 = vec![0, 1, 2];
193+
let k5: Arc<[u8]> = vec![0, 1, 2].into();
197194
let r5 = subs.reserve(&k5).unwrap();
198-
r5.complete(Event::Merge(k5.clone(), IVec::from(k5.clone())));
195+
r5.complete(Event::Set(k5.clone(), IVec::from(k5.clone())));
199196

200-
let k6 = vec![1, 1, 2];
197+
let k6: Arc<[u8]> = vec![1, 1, 2].into();
201198
let r6 = subs.reserve(&k6).unwrap();
202199
r6.complete(Event::Del(k6.clone()));
203200

204-
let k7 = vec![1, 1, 2];
201+
let k7: Arc<[u8]> = vec![1, 1, 2].into();
205202
let r7 = subs.reserve(&k7).unwrap();
206203
drop(r7);
207204

208-
let k8 = vec![1, 2, 2];
205+
let k8: Arc<[u8]> = vec![1, 2, 2].into();
209206
let r8 = subs.reserve(&k8).unwrap();
210207
r8.complete(Event::Set(k8.clone(), IVec::from(k8.clone())));
211208

crates/sled/src/tree.rs

Lines changed: 46 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -170,7 +170,7 @@ impl Tree {
170170
// success
171171
if let Some(res) = subscriber_reservation.take() {
172172
let event =
173-
subscription::Event::Set(key.as_ref().to_vec(), value);
173+
subscription::Event::Set(key.as_ref().into(), value);
174174

175175
res.complete(event);
176176
}
@@ -409,7 +409,7 @@ impl Tree {
409409
if link.is_ok() {
410410
// success
411411
if let Some(res) = subscriber_reservation.take() {
412-
let event = subscription::Event::Del(key.as_ref().to_vec());
412+
let event = subscription::Event::Del(key.as_ref().into());
413413

414414
res.complete(event);
415415
}
@@ -503,9 +503,9 @@ impl Tree {
503503
if link.is_ok() {
504504
if let Some(res) = subscriber_reservation.take() {
505505
let event = if let Some(new) = new {
506-
subscription::Event::Set(key.as_ref().to_vec(), new)
506+
subscription::Event::Set(key.as_ref().into(), new)
507507
} else {
508-
subscription::Event::Del(key.as_ref().to_vec())
508+
subscription::Event::Del(key.as_ref().into())
509509
};
510510

511511
res.complete(event);
@@ -676,8 +676,7 @@ impl Tree {
676676
/// // events is a blocking `Iterator` over `Event`s
677677
/// for event in events.take(1) {
678678
/// match event {
679-
/// Event::Set(key, value) => assert_eq!(key, vec![0]),
680-
/// Event::Merge(key, partial_value) => {}
679+
/// Event::Set(key, value) => assert_eq!(key.as_ref(), &[0]),
681680
/// Event::Del(key) => {}
682681
/// }
683682
/// }
@@ -881,15 +880,48 @@ impl Tree {
881880

882881
let merge_operator = merge_operator_opt.unwrap();
883882

884-
let key = key.as_ref();
885-
let mut current = self.get(key)?;
886-
887883
loop {
888-
let tmp = current.as_ref().map(AsRef::as_ref);
889-
let next = merge_operator(key, tmp, value.as_ref()).map(IVec::from);
890-
match self.cas::<_, _, IVec>(key, tmp, next.clone())? {
891-
Ok(()) => return Ok(next),
892-
Err(new_current) => current = new_current,
884+
let guard = pin();
885+
let View { ptr, pid, node, .. } =
886+
self.node_for_key(key.as_ref(), &guard)?;
887+
888+
let (encoded_key, current_value) =
889+
if let Some((k, v)) = node.leaf_pair_for_key(key.as_ref()) {
890+
(k.clone(), Some(v.clone()))
891+
} else {
892+
let k = prefix_encode(&node.lo, key.as_ref());
893+
let old_v = None;
894+
(k, old_v)
895+
};
896+
897+
let tmp = current_value.as_ref().map(AsRef::as_ref);
898+
let new = merge_operator(key.as_ref(), tmp, value.as_ref())
899+
.map(IVec::from);
900+
901+
let mut subscriber_reservation = self.subscriptions.reserve(&key);
902+
903+
let frag = if let Some(ref new) = new {
904+
Frag::Set(encoded_key, new.clone())
905+
} else {
906+
Frag::Del(encoded_key)
907+
};
908+
let link = self.context.pagecache.link(pid, ptr, frag, &guard)?;
909+
910+
if link.is_ok() {
911+
if let Some(res) = subscriber_reservation.take() {
912+
let event = if let Some(new) = &new {
913+
subscription::Event::Set(
914+
key.as_ref().into(),
915+
new.clone(),
916+
)
917+
} else {
918+
subscription::Event::Del(key.as_ref().into())
919+
};
920+
921+
res.complete(event);
922+
}
923+
924+
return Ok(new);
893925
}
894926
M.tree_looped();
895927
}

0 commit comments

Comments
 (0)