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>
impl<B: Blocks> Tier<B>
Sourcepub fn with_window(blocks: B, window: usize) -> Tier<B>
pub fn with_window(blocks: B, window: usize) -> Tier<B>
A tier whose doorkeeper remembers window keys.
Sourcepub fn store_bytes(&self) -> u64
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.
Sourcepub const fn blocks_mut(&mut self) -> &mut B
pub const fn blocks_mut(&mut self) -> &mut B
The store, mutably, for the same reason.
Sourcepub fn memory_bytes(&self) -> usize
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.
Sourcepub fn demote(&mut self, map: &mut RawMap, key: &[u8]) -> Result<bool>
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.
Sourcepub fn stash(&mut self, bytes: &[u8]) -> Result<Chain>
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.
Sourcepub fn fetch(&mut self, chain: Chain, out: &mut Vec<u8>) -> Result<()>
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.
Sourcepub fn fault(
&mut self,
map: &mut RawMap,
key: &[u8],
out: &mut Vec<u8>,
) -> Result<Faulted>
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.
Sourcepub fn thaw(
&mut self,
map: &mut RawMap,
key: &[u8],
out: &mut Vec<u8>,
) -> Result<Faulted>
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.
Sourcepub fn relieve(
&mut self,
map: &mut RawMap,
budget: usize,
policy: Policy,
now_ms: u64,
lfu: Lfu,
) -> Result<Relief>
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.