Revert "Continue compacting until content fits window during compaction"

This reverts commit 67fa8bee0b.
This commit is contained in:
Julien Cretin
2021-09-30 22:48:31 +02:00
committed by Julien Cretin
parent daa16d948f
commit 9dc5286633
3 changed files with 34 additions and 53 deletions

View File

@@ -266,24 +266,24 @@ impl Format {
/// The total virtual capacity in words, denoted by V. /// The total virtual capacity in words, denoted by V.
/// ///
/// We have V = (N - 1) × Q - M - 1. /// We have V = (N - 1) × (Q - 1) - M.
/// ///
/// We can show V ≥ (N - 2) × Q with the following steps: /// We can show V ≥ (N - 2) × (Q - 1) with the following steps:
/// - M + 1 ≤ Q from M < Q from [M](Format::max_prefix_len)'s definition /// - M ≤ Q - 1 from M < Q from [M](Format::max_prefix_len)'s definition
/// - -M - 1 ≥ -Q from above /// - -M ≥ -(Q - 1) from above
/// - V ≥ (N - 1) × Q - Q from V's definition /// - V ≥ (N - 1) × (Q - 1) - (Q - 1) from V's definition
pub fn virt_size(&self) -> Nat { pub fn virt_size(&self) -> Nat {
(self.num_pages() - 1) * self.virt_page_size() - self.max_prefix_len() - 1 (self.num_pages() - 1) * (self.virt_page_size() - 1) - self.max_prefix_len()
} }
/// The total user capacity in words, denoted by C. /// The total user capacity in words, denoted by C.
/// ///
/// We have C = V - N = (N - 1) × (Q - 1) - M - 2. /// We have C = V - N = (N - 1) × (Q - 2) - M - 1.
/// ///
/// We can show C ≥ (N - 2) × (Q - 1) - 2 with the following steps: /// We can show C ≥ (N - 2) × (Q - 2) - 2 with the following steps:
/// - V ≥ (N - 2) × Q from [V](Format::virt_size)'s definition /// - V ≥ (N - 2) × (Q - 1) from [V](Format::virt_size)'s definition
/// - C ≥ (N - 2) × Q - N from C's definition /// - C ≥ (N - 2) × (Q - 1) - N from C's definition
/// - (N - 2) × Q - N = (N - 2) × (Q - 1) - 2 by calculus /// - (N - 2) × (Q - 1) - N = (N - 2) × (Q - 2) - 2 by calculus
pub fn total_capacity(&self) -> Nat { pub fn total_capacity(&self) -> Nat {
// From the virtual capacity, we reserve N - 1 words for `Erase` entries and 1 word for a // From the virtual capacity, we reserve N - 1 words for `Erase` entries and 1 word for a
// `Clear` entry. // `Clear` entry.

View File

@@ -354,7 +354,7 @@ impl<S: Storage> Store<S> {
.build_internal(InternalEntry::Clear { min_key })?; .build_internal(InternalEntry::Clear { min_key })?;
// We always have one word available. We can't use `reserve` because this is internal // We always have one word available. We can't use `reserve` because this is internal
// capacity, not user capacity. // capacity, not user capacity.
while self.immediate_capacity()?.unwrap_or(0) < 1 { while self.immediate_capacity()? < 1 {
self.compact()?; self.compact()?;
} }
let tail = self.tail()?; let tail = self.tail()?;
@@ -370,9 +370,8 @@ impl<S: Storage> Store<S> {
if self.capacity()?.remaining() < length { if self.capacity()?.remaining() < length {
return Err(StoreError::NoCapacity); return Err(StoreError::NoCapacity);
} }
if self.immediate_capacity()?.unwrap_or(0) < usize_to_nat(length) { if self.immediate_capacity()? < usize_to_nat(length) {
self.compact()?; self.compact()?;
self.finish_compaction()?;
} }
Ok(()) Ok(())
} }
@@ -522,11 +521,10 @@ impl<S: Storage> Store<S> {
self.head = Some(head); self.head = Some(head);
let head_page = head.page(&self.format); let head_page = head.page(&self.format);
match self.parse_compact(head_page)? { match self.parse_compact(head_page)? {
WordState::Erased => (), WordState::Erased => Ok(()),
WordState::Partial => self.compact()?, WordState::Partial => self.compact(),
WordState::Valid(_) => self.compact_copy()?, WordState::Valid(_) => self.compact_copy(),
} }
self.finish_compaction()
} }
/// Recover a possible interrupted operation which is not a compaction. /// Recover a possible interrupted operation which is not a compaction.
@@ -687,7 +685,7 @@ impl<S: Storage> Store<S> {
if self.capacity()?.remaining() < length as usize { if self.capacity()?.remaining() < length as usize {
return Err(StoreError::NoCapacity); return Err(StoreError::NoCapacity);
} }
while self.immediate_capacity()?.unwrap_or(0) < length { while self.immediate_capacity()? < length {
self.compact()?; self.compact()?;
} }
Ok(()) Ok(())
@@ -787,23 +785,6 @@ impl<S: Storage> Store<S> {
Ok(()) Ok(())
} }
/// Finish a compaction sequence.
///
/// Because compaction may temporarily move the tail outside the virtual window, we must keep
/// compacting until that invariant is restored. As such, this function must be called each time
/// the store is compacted without checking whether there is immediate capacity.
fn finish_compaction(&mut self) -> StoreResult<()> {
let mut count = self.format.num_pages() - 2;
while count > 0 && self.immediate_capacity()?.is_none() {
self.compact()?;
count -= 1;
}
if self.immediate_capacity()?.is_none() {
return Err(StoreError::InvalidStorage);
}
Ok(())
}
/// Continues a transaction after it has been written. /// Continues a transaction after it has been written.
fn transaction_apply(&mut self, sorted_keys: &[Nat], marker: Position) -> StoreResult<()> { fn transaction_apply(&mut self, sorted_keys: &[Nat], marker: Position) -> StoreResult<()> {
self.delete_keys(&sorted_keys, marker)?; self.delete_keys(&sorted_keys, marker)?;
@@ -945,10 +926,10 @@ impl<S: Storage> Store<S> {
} }
/// Returns the number of words that can be written without compaction. /// Returns the number of words that can be written without compaction.
fn immediate_capacity(&self) -> StoreResult<Option<Nat>> { fn immediate_capacity(&self) -> StoreResult<Nat> {
let tail = self.tail()?; let tail = self.tail()?;
let end = or_invalid(self.head)? + self.format.virt_size(); let end = or_invalid(self.head)? + self.format.virt_size();
Ok(end.get().checked_sub(tail.get())) Ok(end.get().saturating_sub(tail.get()))
} }
/// Returns the position of the first word in the store. /// Returns the position of the first word in the store.
@@ -1413,34 +1394,34 @@ mod tests {
let mut driver = MINIMAL.new_driver().power_on().unwrap(); let mut driver = MINIMAL.new_driver().power_on().unwrap();
// Don't compact if enough immediate capacity. // Don't compact if enough immediate capacity.
assert_eq!(driver.store().immediate_capacity().unwrap(), Some(42)); assert_eq!(driver.store().immediate_capacity().unwrap(), 39);
assert_eq!(driver.store().capacity().unwrap().remaining(), 37); assert_eq!(driver.store().capacity().unwrap().remaining(), 34);
assert_eq!(driver.store().head().unwrap().get(), 0); assert_eq!(driver.store().head().unwrap().get(), 0);
driver.store_mut().prepare(37).unwrap(); driver.store_mut().prepare(34).unwrap();
assert_eq!(driver.store().head().unwrap().get(), 0); assert_eq!(driver.store().head().unwrap().get(), 0);
// Fill the store. // Fill the store.
for key in 0..4 { for key in 0..4 {
driver.insert(key, &[0x38; 32]).unwrap(); driver.insert(key, &[0x38; 28]).unwrap();
} }
driver.check().unwrap(); driver.check().unwrap();
assert_eq!(driver.store().immediate_capacity().unwrap(), Some(6)); assert_eq!(driver.store().immediate_capacity().unwrap(), 7);
assert_eq!(driver.store().capacity().unwrap().remaining(), 1); assert_eq!(driver.store().capacity().unwrap().remaining(), 2);
// Removing entries increases available capacity but not immediate capacity. // Removing entries increases available capacity but not immediate capacity.
driver.remove(0).unwrap(); driver.remove(0).unwrap();
driver.remove(2).unwrap(); driver.remove(2).unwrap();
driver.check().unwrap(); driver.check().unwrap();
assert_eq!(driver.store().immediate_capacity().unwrap(), Some(6)); assert_eq!(driver.store().immediate_capacity().unwrap(), 7);
assert_eq!(driver.store().capacity().unwrap().remaining(), 19); assert_eq!(driver.store().capacity().unwrap().remaining(), 18);
// Prepare for next write (8 words data + 1 word overhead). // Prepare for next write (7 words data + 1 word overhead).
assert_eq!(driver.store().head().unwrap().get(), 0); assert_eq!(driver.store().head().unwrap().get(), 0);
driver.store_mut().prepare(9).unwrap(); driver.store_mut().prepare(8).unwrap();
driver.check().unwrap(); driver.check().unwrap();
assert_eq!(driver.store().head().unwrap().get(), 18); assert_eq!(driver.store().head().unwrap().get(), 16);
// The available capacity did not change, but the immediate capacity is above 8. // The available capacity did not change, but the immediate capacity is above 8.
assert_eq!(driver.store().immediate_capacity().unwrap(), Some(14)); assert_eq!(driver.store().immediate_capacity().unwrap(), 14);
assert_eq!(driver.store().capacity().unwrap().remaining(), 19); assert_eq!(driver.store().capacity().unwrap().remaining(), 18);
} }
#[test] #[test]

View File

@@ -67,13 +67,13 @@ const TITAN: Config = Config {
#[test] #[test]
fn nordic_capacity() { fn nordic_capacity() {
let driver = NORDIC.new_driver().power_on().unwrap(); let driver = NORDIC.new_driver().power_on().unwrap();
assert_eq!(driver.model().capacity().total, 19141); assert_eq!(driver.model().capacity().total, 19123);
} }
#[test] #[test]
fn titan_capacity() { fn titan_capacity() {
let driver = TITAN.new_driver().power_on().unwrap(); let driver = TITAN.new_driver().power_on().unwrap();
assert_eq!(driver.model().capacity().total, 4323); assert_eq!(driver.model().capacity().total, 4315);
} }
#[test] #[test]