1use std::fmt;
9use std::sync::{Arc, Mutex, MutexGuard};
10
11use crate::error::{Error, Result};
12use crate::kv::{search, KVTransaction, KeySearchCancellation, KeySearchPage, KeySearchRequest};
13use crate::txn::{OwnedLeaseOutcome, OwnedReadSessionStatus, OwnedTransactionSessionStatus};
14use crate::types::{Key, TxnId, TxnMode, Value};
15
16pub trait OwnedKVScan: Send {
22 fn next_entry(&mut self) -> Result<Option<(Key, Value)>>;
24
25 fn next_search_entry(&mut self) -> Result<Option<(Key, Option<Value>)>> {
27 self.next_entry()
28 .map(|entry| entry.map(|(key, value)| (key, Some(value))))
29 }
30
31 fn close(&mut self) -> Result<()> {
33 Ok(())
34 }
35}
36
37pub trait OwnedKVTransaction: Send {
42 fn id(&self) -> TxnId;
44
45 fn mode(&self) -> TxnMode;
47
48 fn get(&mut self, key: &Key) -> Result<Option<Value>>;
50
51 fn put(&mut self, key: Key, value: Value) -> Result<()>;
53
54 fn delete(&mut self, key: Key) -> Result<()>;
56
57 fn scan_prefix(&mut self, prefix: &[u8]) -> Result<Box<dyn OwnedKVScan>>;
59
60 fn scan_range(&mut self, start: &[u8], end: &[u8]) -> Result<Box<dyn OwnedKVScan>>;
62
63 fn scan_from(&mut self, start: &[u8]) -> Result<Box<dyn OwnedKVScan>> {
65 let _ = start;
66 Err(Error::InvalidParameter {
67 param: "scan_from".into(),
68 reason: "backend does not implement cursor-based scanning".into(),
69 })
70 }
71
72 fn search_keys(&mut self, request: &KeySearchRequest) -> Result<KeySearchPage> {
74 self.search_keys_with_cancellation(request, &KeySearchCancellation::default())
75 }
76
77 fn search_keys_with_cancellation(
79 &mut self,
80 request: &KeySearchRequest,
81 cancellation: &KeySearchCancellation,
82 ) -> Result<KeySearchPage> {
83 let prepared = search::PreparedKeySearch::new(request)?;
84 let mut cursor_start = request.cursor.clone().unwrap_or_default();
85 let mut scan = if request.cursor.is_some() {
86 cursor_start.push(0);
87 self.scan_from(&cursor_start)?
88 } else {
89 self.scan_prefix(prepared.prefix())?
90 };
91 let result = prepared.collect(|| scan.next_search_entry(), request, cancellation);
92 match (result, scan.close()) {
93 (Err(error), _) | (Ok(_), Err(error)) => Err(error),
94 (Ok(page), Ok(())) => Ok(page),
95 }
96 }
97
98 fn commit(self: Box<Self>) -> Result<()>;
100
101 fn rollback(self: Box<Self>) -> Result<()>;
103}
104
105pub struct OwnedKVTransactionAdapter<'a> {
115 transaction: &'a mut dyn OwnedKVTransaction,
116}
117
118impl<'a> OwnedKVTransactionAdapter<'a> {
119 pub fn new(transaction: &'a mut dyn OwnedKVTransaction) -> Self {
121 Self { transaction }
122 }
123
124 fn collect_cursor(cursor: Box<dyn OwnedKVScan>) -> Result<Vec<(Key, Value)>> {
125 let mut cursor = cursor;
126 let mut entries = Vec::new();
127 while let Some(entry) = cursor.next_entry()? {
128 entries.push(entry);
129 }
130 cursor.close()?;
131 Ok(entries)
132 }
133}
134
135impl<'a> KVTransaction<'a> for OwnedKVTransactionAdapter<'a> {
136 fn id(&self) -> TxnId {
137 self.transaction.id()
138 }
139
140 fn mode(&self) -> TxnMode {
141 self.transaction.mode()
142 }
143
144 fn get(&mut self, key: &Key) -> Result<Option<Value>> {
145 self.transaction.get(key)
146 }
147
148 fn put(&mut self, key: Key, value: Value) -> Result<()> {
149 self.transaction.put(key, value)
150 }
151
152 fn delete(&mut self, key: Key) -> Result<()> {
153 self.transaction.delete(key)
154 }
155
156 fn scan_prefix(
157 &mut self,
158 prefix: &[u8],
159 ) -> Result<Box<dyn Iterator<Item = (Key, Value)> + '_>> {
160 let entries = Self::collect_cursor(self.transaction.scan_prefix(prefix)?)?;
161 Ok(Box::new(entries.into_iter()))
162 }
163
164 fn scan_range(
165 &mut self,
166 start: &[u8],
167 end: &[u8],
168 ) -> Result<Box<dyn Iterator<Item = (Key, Value)> + '_>> {
169 let entries = Self::collect_cursor(self.transaction.scan_range(start, end)?)?;
170 Ok(Box::new(entries.into_iter()))
171 }
172
173 fn search_keys_with_cancellation(
174 &mut self,
175 request: &KeySearchRequest,
176 cancellation: &KeySearchCancellation,
177 ) -> Result<KeySearchPage> {
178 self.transaction
179 .search_keys_with_cancellation(request, cancellation)
180 }
181
182 fn commit_self(self) -> Result<()> {
183 Err(Error::InvalidParameter {
184 param: "owned_transaction_adapter".to_owned(),
185 reason: "terminal ownership belongs to OwnedTransactionSession".to_owned(),
186 })
187 }
188
189 fn rollback_self(self) -> Result<()> {
190 Err(Error::InvalidParameter {
191 param: "owned_transaction_adapter".to_owned(),
192 reason: "terminal ownership belongs to OwnedTransactionSession".to_owned(),
193 })
194 }
195}
196
197pub trait OwnedKVStore: Send + Sync + 'static {
202 fn begin_owned_kv_transaction(
204 self: Arc<Self>,
205 mode: TxnMode,
206 ) -> Result<Box<dyn OwnedKVTransaction>>;
207}
208
209#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
214#[non_exhaustive]
215pub struct OwnedReadOptions {}
216
217pub trait OwnedSessionFactory: OwnedKVStore {
219 fn begin_owned_read(self: Arc<Self>, _options: OwnedReadOptions) -> Result<OwnedReadSession> {
221 let transaction = self.begin_owned_kv_transaction(TxnMode::ReadOnly)?;
222 OwnedReadSession::new(transaction)
223 }
224
225 fn begin_owned_transaction(self: Arc<Self>, mode: TxnMode) -> Result<OwnedTransactionSession> {
227 let transaction = self.begin_owned_kv_transaction(mode)?;
228 Ok(OwnedTransactionSession::new(transaction))
229 }
230}
231
232impl<T> OwnedSessionFactory for T where T: OwnedKVStore {}
233
234pub trait OwnedReadSessionApi: Send + Sync {
236 fn status(&self) -> OwnedReadSessionStatus;
238
239 fn acquire_lease(&self) -> Result<OwnedReadLease>;
241
242 fn close(&self) -> Result<OwnedReadSessionStatus>;
244}
245
246pub trait OwnedTransactionSessionApi: Send + Sync {
248 fn status(&self) -> OwnedTransactionSessionStatus;
250
251 fn acquire_lease(&self) -> Result<OwnedTransactionLease>;
253
254 fn commit(&self) -> Result<OwnedTransactionSessionStatus>;
256
257 fn rollback(&self) -> Result<OwnedTransactionSessionStatus>;
259}
260
261#[derive(Clone)]
263pub struct OwnedReadSession {
264 inner: Arc<Mutex<OwnedReadInner>>,
265}
266
267struct OwnedReadInner {
268 transaction: Option<Box<dyn OwnedKVTransaction>>,
269 status: OwnedReadSessionStatus,
270}
271
272impl Drop for OwnedReadInner {
273 fn drop(&mut self) {
274 if let Some(transaction) = self.transaction.take() {
275 let _ = transaction.rollback();
276 }
277 }
278}
279
280impl OwnedReadSession {
281 pub fn new(transaction: Box<dyn OwnedKVTransaction>) -> Result<Self> {
283 if transaction.mode() != TxnMode::ReadOnly {
284 return Err(Error::InvalidParameter {
285 param: "owned_read_session".to_owned(),
286 reason: "requires a read-only transaction".to_owned(),
287 });
288 }
289 Ok(Self {
290 inner: Arc::new(Mutex::new(OwnedReadInner {
291 transaction: Some(transaction),
292 status: OwnedReadSessionStatus::Open,
293 })),
294 })
295 }
296
297 pub fn status(&self) -> OwnedReadSessionStatus {
299 self.lock().status
300 }
301
302 pub fn acquire_lease(&self) -> Result<OwnedReadLease> {
304 let mut inner = self.lock();
305 if inner.status != OwnedReadSessionStatus::Open || inner.transaction.is_none() {
306 return Err(Error::TxnClosed);
307 }
308 inner.status = OwnedReadSessionStatus::LeaseActive;
309 Ok(OwnedReadLease {
310 inner: self.inner.clone(),
311 released: false,
312 })
313 }
314
315 pub fn close(&self) -> Result<OwnedReadSessionStatus> {
317 finish_read_session(&self.inner, OwnedLeaseOutcome::Closed)
318 }
319
320 fn lock(&self) -> MutexGuard<'_, OwnedReadInner> {
321 self.inner
322 .lock()
323 .expect("owned read session mutex poisoned")
324 }
325}
326
327impl OwnedReadSessionApi for OwnedReadSession {
328 fn status(&self) -> OwnedReadSessionStatus {
329 Self::status(self)
330 }
331
332 fn acquire_lease(&self) -> Result<OwnedReadLease> {
333 Self::acquire_lease(self)
334 }
335
336 fn close(&self) -> Result<OwnedReadSessionStatus> {
337 Self::close(self)
338 }
339}
340
341impl fmt::Debug for OwnedReadSession {
342 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
343 formatter
344 .debug_struct("OwnedReadSession")
345 .field("status", &self.status())
346 .finish_non_exhaustive()
347 }
348}
349
350pub struct OwnedReadLease {
352 inner: Arc<Mutex<OwnedReadInner>>,
353 released: bool,
354}
355
356impl OwnedReadLease {
357 pub fn with_transaction<T>(
362 &self,
363 operation: impl FnOnce(&mut dyn OwnedKVTransaction) -> Result<T>,
364 ) -> Result<T> {
365 let mut inner = self
366 .inner
367 .lock()
368 .expect("owned read session mutex poisoned");
369 if inner.status != OwnedReadSessionStatus::LeaseActive {
370 return Err(Error::TxnClosed);
371 }
372 operation(inner.transaction.as_deref_mut().ok_or(Error::TxnClosed)?)
373 }
374
375 pub fn finish(mut self, outcome: OwnedLeaseOutcome) -> Result<OwnedReadSessionStatus> {
377 let result = finish_read_session(&self.inner, outcome);
378 self.released = true;
379 result
380 }
381}
382
383impl Drop for OwnedReadLease {
384 fn drop(&mut self) {
385 if !self.released {
386 let _ = finish_read_session(&self.inner, OwnedLeaseOutcome::Closed);
387 self.released = true;
388 }
389 }
390}
391
392fn finish_read_session(
393 inner: &Arc<Mutex<OwnedReadInner>>,
394 outcome: OwnedLeaseOutcome,
395) -> Result<OwnedReadSessionStatus> {
396 let (transaction, target) = {
397 let mut inner = inner.lock().expect("owned read session mutex poisoned");
398 if inner.status.is_terminal() {
399 return Ok(inner.status);
400 }
401 let target = OwnedReadSessionStatus::from(outcome);
402 let transaction = inner.transaction.take();
403 inner.status = target;
404 (transaction, target)
405 };
406
407 if let Some(transaction) = transaction {
408 if transaction.rollback().is_err() {
409 let mut inner = inner.lock().expect("owned read session mutex poisoned");
410 inner.status = OwnedReadSessionStatus::Failed;
411 return Err(Error::TxnClosed);
412 }
413 }
414 Ok(target)
415}
416
417#[derive(Clone)]
419pub struct OwnedTransactionSession {
420 inner: Arc<Mutex<OwnedTransactionInner>>,
421}
422
423struct OwnedTransactionInner {
424 transaction: Option<Box<dyn OwnedKVTransaction>>,
425 status: OwnedTransactionSessionStatus,
426}
427
428impl Drop for OwnedTransactionInner {
429 fn drop(&mut self) {
430 if let Some(transaction) = self.transaction.take() {
431 let _ = transaction.rollback();
432 }
433 }
434}
435
436impl OwnedTransactionSession {
437 pub fn new(transaction: Box<dyn OwnedKVTransaction>) -> Self {
439 Self {
440 inner: Arc::new(Mutex::new(OwnedTransactionInner {
441 transaction: Some(transaction),
442 status: OwnedTransactionSessionStatus::Open,
443 })),
444 }
445 }
446
447 pub fn status(&self) -> OwnedTransactionSessionStatus {
449 self.lock().status
450 }
451
452 pub fn acquire_lease(&self) -> Result<OwnedTransactionLease> {
454 let mut inner = self.lock();
455 if !inner.status.can_acquire_lease() || inner.transaction.is_none() {
456 return Err(Error::TxnClosed);
457 }
458 inner.status = OwnedTransactionSessionStatus::LeaseActive;
459 Ok(OwnedTransactionLease {
460 inner: self.inner.clone(),
461 released: false,
462 })
463 }
464
465 pub fn with_transaction<T>(
472 &self,
473 operation: impl FnOnce(&mut dyn OwnedKVTransaction) -> Result<T>,
474 ) -> Result<T> {
475 let lease = self.acquire_lease()?;
476 let result = lease.with_transaction(operation);
477 let release = lease.finish(OwnedLeaseOutcome::Exhausted);
478 match (result, release) {
479 (Ok(value), Ok(_)) => Ok(value),
480 (Err(error), Ok(_)) => Err(error),
481 (_, Err(error)) => Err(error),
482 }
483 }
484
485 pub fn commit(&self) -> Result<OwnedTransactionSessionStatus> {
487 self.finish_transaction(true)
488 }
489
490 pub fn rollback(&self) -> Result<OwnedTransactionSessionStatus> {
492 self.finish_transaction(false)
493 }
494
495 fn finish_transaction(&self, commit: bool) -> Result<OwnedTransactionSessionStatus> {
496 let transaction = {
497 let mut inner = self.lock();
498 let allowed = if commit {
499 inner.status.can_commit()
500 } else {
501 inner.status.can_rollback()
502 };
503 if !allowed {
504 return Err(Error::TxnClosed);
505 }
506 inner.status = OwnedTransactionSessionStatus::Closed;
507 inner.transaction.take().ok_or(Error::TxnClosed)?
508 };
509
510 let result = if commit {
511 transaction.commit()
512 } else {
513 transaction.rollback()
514 };
515 let mut inner = self.lock();
516 inner.status = if result.is_ok() {
517 if commit {
518 OwnedTransactionSessionStatus::Committed
519 } else {
520 OwnedTransactionSessionStatus::RolledBack
521 }
522 } else {
523 OwnedTransactionSessionStatus::Closed
524 };
525 match result {
526 Ok(()) => Ok(inner.status),
527 Err(error) => Err(error),
528 }
529 }
530
531 fn lock(&self) -> MutexGuard<'_, OwnedTransactionInner> {
532 self.inner
533 .lock()
534 .expect("owned transaction session mutex poisoned")
535 }
536}
537
538impl OwnedTransactionSessionApi for OwnedTransactionSession {
539 fn status(&self) -> OwnedTransactionSessionStatus {
540 Self::status(self)
541 }
542
543 fn acquire_lease(&self) -> Result<OwnedTransactionLease> {
544 Self::acquire_lease(self)
545 }
546
547 fn commit(&self) -> Result<OwnedTransactionSessionStatus> {
548 Self::commit(self)
549 }
550
551 fn rollback(&self) -> Result<OwnedTransactionSessionStatus> {
552 Self::rollback(self)
553 }
554}
555
556impl fmt::Debug for OwnedTransactionSession {
557 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
558 formatter
559 .debug_struct("OwnedTransactionSession")
560 .field("status", &self.status())
561 .finish_non_exhaustive()
562 }
563}
564
565pub struct OwnedTransactionLease {
567 inner: Arc<Mutex<OwnedTransactionInner>>,
568 released: bool,
569}
570
571impl OwnedTransactionLease {
572 pub fn with_transaction<T>(
574 &self,
575 operation: impl FnOnce(&mut dyn OwnedKVTransaction) -> Result<T>,
576 ) -> Result<T> {
577 let mut inner = self
578 .inner
579 .lock()
580 .expect("owned transaction session mutex poisoned");
581 if inner.status != OwnedTransactionSessionStatus::LeaseActive {
582 return Err(Error::TxnClosed);
583 }
584 operation(inner.transaction.as_deref_mut().ok_or(Error::TxnClosed)?)
585 }
586
587 pub fn finish(mut self, outcome: OwnedLeaseOutcome) -> Result<OwnedTransactionSessionStatus> {
589 let mut inner = self
590 .inner
591 .lock()
592 .expect("owned transaction session mutex poisoned");
593 if inner.status != OwnedTransactionSessionStatus::LeaseActive {
594 self.released = true;
595 return Err(Error::TxnClosed);
596 }
597 inner.status = OwnedTransactionSessionStatus::from(outcome);
598 self.released = true;
599 Ok(inner.status)
600 }
601}
602
603impl Drop for OwnedTransactionLease {
604 fn drop(&mut self) {
605 if !self.released {
606 let mut inner = self
607 .inner
608 .lock()
609 .expect("owned transaction session mutex poisoned");
610 if inner.status == OwnedTransactionSessionStatus::LeaseActive {
611 inner.status = OwnedTransactionSessionStatus::MustAbort;
612 }
613 self.released = true;
614 }
615 }
616}
617
618#[cfg(test)]
619mod tests {
620 use std::sync::atomic::{AtomicUsize, Ordering};
621 use std::sync::Arc;
622
623 use super::{
624 OwnedKVScan, OwnedKVTransaction, OwnedKVTransactionAdapter, OwnedReadSession,
625 OwnedSessionFactory, OwnedTransactionSession,
626 };
627 use crate::error::Result;
628 use crate::kv::{memory::MemoryKV, KVTransaction};
629 use crate::txn::{OwnedLeaseOutcome, OwnedReadSessionStatus, OwnedTransactionSessionStatus};
630 use crate::types::{Key, TxnId, TxnMode, Value};
631
632 #[derive(Default)]
633 struct Calls {
634 commits: AtomicUsize,
635 rollbacks: AtomicUsize,
636 }
637
638 struct TestCursor;
639
640 impl OwnedKVScan for TestCursor {
641 fn next_entry(&mut self) -> Result<Option<(Key, Value)>> {
642 Ok(None)
643 }
644 }
645
646 struct TestTransaction {
647 calls: Arc<Calls>,
648 mode: TxnMode,
649 }
650
651 impl TestTransaction {
652 fn new(calls: Arc<Calls>, mode: TxnMode) -> Self {
653 Self { calls, mode }
654 }
655 }
656
657 impl OwnedKVTransaction for TestTransaction {
658 fn id(&self) -> TxnId {
659 TxnId(7)
660 }
661
662 fn mode(&self) -> TxnMode {
663 self.mode
664 }
665
666 fn get(&mut self, _key: &Key) -> Result<Option<Value>> {
667 Ok(None)
668 }
669
670 fn put(&mut self, _key: Key, _value: Value) -> Result<()> {
671 Ok(())
672 }
673
674 fn delete(&mut self, _key: Key) -> Result<()> {
675 Ok(())
676 }
677
678 fn scan_prefix(&mut self, _prefix: &[u8]) -> Result<Box<dyn OwnedKVScan>> {
679 Ok(Box::new(TestCursor))
680 }
681
682 fn scan_range(&mut self, _start: &[u8], _end: &[u8]) -> Result<Box<dyn OwnedKVScan>> {
683 Ok(Box::new(TestCursor))
684 }
685
686 fn scan_from(&mut self, _start: &[u8]) -> Result<Box<dyn OwnedKVScan>> {
687 Ok(Box::new(TestCursor))
688 }
689
690 fn commit(self: Box<Self>) -> Result<()> {
691 self.calls.commits.fetch_add(1, Ordering::SeqCst);
692 Ok(())
693 }
694
695 fn rollback(self: Box<Self>) -> Result<()> {
696 self.calls.rollbacks.fetch_add(1, Ordering::SeqCst);
697 Ok(())
698 }
699 }
700
701 #[test]
702 fn read_session_releases_its_transaction_once_at_terminal_outcome() {
703 let calls = Arc::new(Calls::default());
704 let session = OwnedReadSession::new(Box::new(TestTransaction::new(
705 calls.clone(),
706 TxnMode::ReadOnly,
707 )))
708 .unwrap();
709
710 let lease = session.acquire_lease().unwrap();
711 assert_eq!(session.status(), OwnedReadSessionStatus::LeaseActive);
712 assert!(session.acquire_lease().is_err());
713 assert_eq!(
714 lease.finish(OwnedLeaseOutcome::Exhausted).unwrap(),
715 OwnedReadSessionStatus::Exhausted
716 );
717 assert_eq!(calls.rollbacks.load(Ordering::SeqCst), 1);
718 assert_eq!(session.close().unwrap(), OwnedReadSessionStatus::Exhausted);
719 assert_eq!(calls.rollbacks.load(Ordering::SeqCst), 1);
720 }
721
722 #[test]
723 fn transaction_session_blocks_commit_while_leased_then_commits_once() {
724 let calls = Arc::new(Calls::default());
725 let session = OwnedTransactionSession::new(Box::new(TestTransaction::new(
726 calls.clone(),
727 TxnMode::ReadWrite,
728 )));
729
730 let lease = session.acquire_lease().unwrap();
731 assert!(session.commit().is_err());
732 assert_eq!(
733 lease.finish(OwnedLeaseOutcome::Exhausted).unwrap(),
734 OwnedTransactionSessionStatus::Committable
735 );
736 assert_eq!(
737 session.commit().unwrap(),
738 OwnedTransactionSessionStatus::Committed
739 );
740 assert_eq!(calls.commits.load(Ordering::SeqCst), 1);
741 assert!(session.rollback().is_err());
742 assert_eq!(calls.rollbacks.load(Ordering::SeqCst), 0);
743 }
744
745 #[test]
746 fn cancelled_or_dropped_lease_requires_a_single_rollback() {
747 let calls = Arc::new(Calls::default());
748 let session = OwnedTransactionSession::new(Box::new(TestTransaction::new(
749 calls.clone(),
750 TxnMode::ReadWrite,
751 )));
752
753 let lease = session.acquire_lease().unwrap();
754 assert_eq!(
755 lease.finish(OwnedLeaseOutcome::Cancelled).unwrap(),
756 OwnedTransactionSessionStatus::MustAbort
757 );
758 assert!(session.commit().is_err());
759 assert_eq!(
760 session.rollback().unwrap(),
761 OwnedTransactionSessionStatus::RolledBack
762 );
763 assert_eq!(calls.rollbacks.load(Ordering::SeqCst), 1);
764
765 let drop_calls = Arc::new(Calls::default());
766 let dropped = OwnedTransactionSession::new(Box::new(TestTransaction::new(
767 drop_calls.clone(),
768 TxnMode::ReadWrite,
769 )));
770 drop(dropped.acquire_lease().unwrap());
771 assert_eq!(dropped.status(), OwnedTransactionSessionStatus::MustAbort);
772 drop(dropped);
773 assert_eq!(drop_calls.rollbacks.load(Ordering::SeqCst), 1);
774 }
775
776 #[test]
777 fn compatibility_adapter_never_consumes_the_owned_terminal_transition() {
778 let store = Arc::new(MemoryKV::new());
779 let session = store
780 .clone()
781 .begin_owned_transaction(TxnMode::ReadWrite)
782 .unwrap();
783
784 session
785 .with_transaction(|owned| {
786 let mut compatibility = OwnedKVTransactionAdapter::new(owned);
787 compatibility.put(b"adapter".to_vec(), b"value".to_vec())?;
788 let rows = compatibility.scan_prefix(b"adapter")?.collect::<Vec<_>>();
789 assert_eq!(rows, vec![(b"adapter".to_vec(), b"value".to_vec())]);
790 assert!(compatibility.commit_self().is_err());
791 Ok(())
792 })
793 .unwrap();
794
795 assert_eq!(session.status(), OwnedTransactionSessionStatus::Committable);
796 session.commit().unwrap();
797
798 let read = store.begin_owned_read(Default::default()).unwrap();
799 let lease = read.acquire_lease().unwrap();
800 let value = lease
801 .with_transaction(|transaction| transaction.get(&b"adapter".to_vec()))
802 .unwrap();
803 assert_eq!(value, Some(b"value".to_vec()));
804 lease.finish(OwnedLeaseOutcome::Exhausted).unwrap();
805 }
806}