Expand description
How a string value sits in a record, and what comes back out of one.
The map underneath stores opaque bytes against a key. Everything a string needs beyond those bytes, which is its encoding and its expiry deadline, is carried in a one byte header in front of them:
+--------+-------------------------+-------------------+-----------+
| meta | expire at, u64 LE | access, 24 bits | payload |
| u8 | 8 bytes, only if tagged | 3 bytes, tagged | see below |
+--------+-------------------------+-------------------+-----------+One byte, and eight more only for a key that has a deadline, which most keys do not. The alternative is a second lookup into a side table for the TTL, and a second lookup is a second cache miss on a path whose whole budget is one.
§The access field
Three bytes saying when the key was last read, or how often, depending on
which eviction policy is in force. It is crate::access::Access and the
reasoning about what goes in it lives there.
It is behind a tag bit the same way the deadline is, but unlike the deadline every record written now has one. The bit is there so that a record written before the field existed still reads back correctly rather than to make the field optional, and it sits after the deadline for the same reason: the deadline stays at offset one and everything that reads one goes on working.
Three bytes on every key is a real cost and it is worth being straight about
why it is paid unconditionally rather than only under a policy that reads it.
A key written under noeviction and then read under allkeys-lru has to be
rankable, and it cannot become rankable later without the record growing,
which means moving it, on what is usually a read. Paying three bytes always
is the version where switching policy at runtime does the obvious thing.
The payload depends on the encoding. An int holds the eight bytes of the
integer and not its digits, which is what makes INCR a probe, an add and a
store with no arena traffic at all (08 section 2). An embstr and a raw
hold the bytes as given. The difference between those two is the name
OBJECT ENCODING reports and nothing else, which is also true in Redis:
embstr there means the value was allocated next to its object header, a
distinction yo does not have because every value is already next to its
key.
§The type tag
The meta byte also says which type the key holds, in three bits that were
spare. That is what TYPE reads and it is what a command will read before it
decides whether it is looking at its own type or at somebody else’s, and both
of those want the answer to come out of the byte the lookup already fetched
rather than out of a second structure. A string is zero, so nothing written
before the tag existed reads back as anything else.
§The tier tag
The last bit says whether the payload is the value or a place on the file
where the value is. That is 14 section 4.1’s tier tag, and the reason it is
a bit here rather than a lookup somewhere else is the number it exists to
make possible: at most 1.05 device reads for every point read. A point read
that has to consult a second structure to find out whether the value is
resident has already spent the miss it was trying to avoid.
A demoted record keeps its deadline, its access field, its type and its
encoding, and adds a length. So TTL, TYPE, OBJECT ENCODING, STRLEN,
EXISTS and the whole of eviction go on answering at memory speed on a key
whose value is on the device, and only the commands that actually want the
bytes pay for them. write_cold_record writes one and cold is the
branch that reads it.
Structs§
Enums§
- Encoding
- What
OBJECT ENCODINGcalls a string. - Kind
- What
TYPEcalls a key, and what the meta byte’s tag holds. - Str
- A stored value, read back out of a record.
Constants§
- EMBSTR_
MAX - The longest value Redis calls
embstrrather thanraw.
Functions§
- access
- What the access field in a record says, or
Noneif it has no room for one. - cold
- Where the value of a demoted record is, or
Noneif the record is resident. - cold_
record_ len - How many bytes a record pointing at a demoted value occupies.
- expire_
at - The deadline in a record, if it has one.
- has_
expiry - Whether a record carries a deadline at all, without reading it.
- is_
expired - Whether a record’s deadline has passed at
now_ms. - kind
- The type a record holds.
- raw_
in_ place - The bytes of a raw record, to be written over where they lie.
- read
- The value in a record.
- read_
int_ in_ place - The integer in an int encoded record, and where its bytes start.
- record_
len - How many bytes a record holding this value will occupy.
- set_
access - Stamp the access field, in place, over whatever was there.
- slot
- The slab number in a record that has one.
- slot_
record_ len - How many bytes a record pointing at a slab slot occupies.
- str_len
- How long the value in a record is, without reading it.
- write_
cold_ record - Write a record saying the value is at
aton the file and islenbytes. - write_
int_ in_ place - Store
nback over an int payload that starts atat. - write_
int_ record - Write a record whose value is an integer the caller already has.
- write_
record - Write a whole record into
out, which must be exactlyrecord_lenlong. - write_
slot_ record - Write a record that points at
slotin the slab forkind.