Skip to main content

Tier

Struct Tier 

Source
pub struct Tier<B: Blocks> { /* private fields */ }
Expand description

The tier, which owns the file side of the keyspace.

Implementations§

Source§

impl<B: Blocks> Tier<B>

Source

pub fn new(blocks: B) -> Tier<B>

A tier over blocks, with a doorkeeper of the default window.

Source

pub fn with_window(blocks: B, window: usize) -> Tier<B>

A tier whose doorkeeper remembers window keys.

Source

pub const fn stats(&self) -> Stats

What has happened so far.

Source

pub fn store_bytes(&self) -> u64

How many bytes the store holds, which is what maxstore is compared against.

Asked of the store rather than added up here. Stats::bytes_out counts payload that was written and never goes down, and a limit on the file has to be a limit on the file.

Source

pub const fn blocks(&self) -> &B

The store, for a caller that has to flush or close it.

Source

pub const fn blocks_mut(&mut self) -> &mut B

The store, mutably, for the same reason.

Source

pub fn memory_bytes(&self) -> usize

What the tier’s own buffers cost, which the memory report has to include because they are not free and are not counted anywhere else.

Source

pub fn demote(&mut self, map: &mut RawMap, key: &[u8]) -> Result<bool>

Move key’s value out to the file.

Ok(false) when there is no such key, when it is already on the file, or when moving it would cost more memory than it saves. None of those is an error: a caller under memory pressure asks about a lot of keys and most of the answers are no.

§Errors

Whatever the store says when it cannot take the bytes.

Source

pub fn stash(&mut self, bytes: &[u8]) -> Result<Chain>

Write bytes to the file and answer where they went.

The store half of demotion with the record half left out, which is what a collection needs. A string’s value is its record, so Tier::demote can do both ends and does. A collection’s body is in a slab and its record holds a number, so the caller is the only one that can free the slot and rewrite the record, and all it wants from here is the chain.

§Errors

Whatever the store says when it cannot take the bytes.

Source

pub fn fetch(&mut self, chain: Chain, out: &mut Vec<u8>) -> Result<()>

Read a chain back into out, which is cleared first.

The other half of Tier::stash, and the doorkeeper does not get a vote here for the same reason it does not in Tier::thaw: a collection command needs its body in a slab to answer at all, so there is no serving it from the file and leaving it there. The read that costs one device read is the read that promotes.

§Errors

Whatever the store says when the chain will not read back.

Source

pub fn fault( &mut self, map: &mut RawMap, key: &[u8], out: &mut Vec<u8>, ) -> Result<Faulted>

Read key’s value, from the file if that is where it is.

out is cleared and filled only when the answer is Faulted::Served or Faulted::Promoted. It belongs to the caller so that a server can keep one buffer per shard and a fault costs no allocation once it has grown, which is Y7.

§Errors

Whatever the store says when the chain will not read back.

Source

pub fn thaw( &mut self, map: &mut RawMap, key: &[u8], out: &mut Vec<u8>, ) -> Result<Faulted>

Read key’s value and put it back in memory whatever the doorkeeper thinks.

This is for a command that is about to write the key. APPEND on a demoted value reads it, adds to it and stores the result, and the result is a resident record no matter which way the doorkeeper would have gone, so asking it would be asking a question whose answer cannot be used. The same goes for INCR, SETRANGE, SETBIT, GETSET and the rest of the read modify write family.

A promotion here still costs one device read and no more, and the value it read is the one the caller was going to ask for anyway.

§Errors

Whatever the store says when the chain will not read back.

Source

pub fn relieve( &mut self, map: &mut RawMap, budget: usize, policy: Policy, now_ms: u64, lfu: Lfu, ) -> Result<Relief>

Move values out until the map fits in budget bytes.

Answers with a Relief, which is what moved and what that was worth. Stops early when BARREN rounds in a row find nothing worth demoting, which is the case where every value left is shorter than the pointer that would replace it, and the honest answer there is that memory cannot be given back rather than that the loop should keep spinning.

Two things had to be right before that stop rule meant what it says, and both of them are about a sweep that runs long enough to make most of the keyspace cold. One barren round is a collision rather than a conclusion, which is what BARREN is for, and a round has to spend its budget on victims found rather than entries walked, which is what WALK is for. Each constant has the failure it prevents written on it.

§Compaction is the part that gives the memory back

Demoting a key does not free anything on its own, and finding that out is worth a paragraph. Replacing a long record with a short one leaves the long one behind as dead bytes in a segment the arena still owns, so the number a memory limit is compared against does not move until a segment is evacuated and handed back. So each round of demotions is followed by RawMap::compact_hard, which is the entry point written for a store that has run out of room and will evacuate a segment holding a single dead record rather than wait for a worthwhile one.

A round drains its whole pool before checking the budget again, so this can overshoot by up to the pool size. That is bounded by evict::CANDIDATES keys and it is the right way round: demoting one key too many costs one device read later, and stopping one key short costs a memory limit that was not respected.

§Why the count of values moved is not the answer on its own

Because the two halves of this loop run at different rates. Demotion happens key by key and compaction happens two megabytes at a time, so a sweep that has been running for a while is full of rounds that move values and free nothing, and rounds that move nothing and free a whole segment that earlier rounds had emptied out. The second kind is not rare: sampling draws one index segment, and in a keyspace that is mostly cold it draws a segment with nothing resident in it often.

A caller asking for room and reading only the count refuses its client’s write on one of those rounds, on a server whose memory just went down by two megabytes. That is what Relief::made_room is for and it is why this counts both.

§Errors

Whatever the store says when it cannot take the bytes.

Auto Trait Implementations§

§

impl<B> Freeze for Tier<B>
where B: Freeze,

§

impl<B> RefUnwindSafe for Tier<B>
where B: RefUnwindSafe,

§

impl<B> Send for Tier<B>
where B: Send,

§

impl<B> Sync for Tier<B>
where B: Sync,

§

impl<B> Unpin for Tier<B>
where B: Unpin,

§

impl<B> UnsafeUnpin for Tier<B>
where B: UnsafeUnpin,

§

impl<B> UnwindSafe for Tier<B>
where B: UnwindSafe,

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = !

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, !>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.