Concepts
Time-series compression (delta-of-delta and XOR)
Intuition
Imagine a notebook where you write down, minute by minute, the time and the temperature of a room. Almost always the reading arrives at the right rhythm and the temperature barely changes. Instead of rewriting the full time and the full number on every line, you note only what changed relative to the previous line, and most of the time that is 'nothing changed'. The notebook gets much smaller, and you can keep the recent notes at hand, in memory, for quick lookup.
How it works
In Gorilla, each series is compressed separately and as a stream, point by point, using only information from the previous points; an original point takes 16 bytes (a 64-bit timestamp and a 64-bit double value). For the timestamps, since data usually arrives at a fixed interval, the delta between consecutive timestamps is computed and then the delta-of-delta (deltas 60, 60, 59, 61 become 0, -1, 2). The encoding has variable size: a single bit '0' when the delta-of-delta is zero, and headers '10', '110', '1110' and '1111' followed by 7, 9, 12 and 32 bits for ever larger ranges of values. For the values, the current double is XORed with the previous one: if the values are close, the sign, exponent and first mantissa bits match, and the result has many leading and trailing zeros, with the relevant bits grouped in the middle. If the XOR is zero, a single bit '0' is stored; otherwise '1' is stored plus a control bit, which indicates whether the significant bits fit in the same block of zeros as the previous value (only those are stored) or whether the number of leading zeros (5 bits) and the length of the significant stretch (6 bits) must be stored again. The data is organized into two-hour blocks, and larger blocks compress better but force short queries to decode more. Together, the two techniques brought the average cost from 16 bytes to 1.37 bytes per point, a 12x reduction.
In practice
This compression is what allowed Gorilla to keep the last 26 hours of data in RAM, as a write-through cache over HBase, since at least 85% of queries asked for that window; the 26 hours fit in 1.3 TB distributed across 20 machines at launch. The write path was designed to prioritize availability: each write goes to two independent instances in different regions, without guaranteeing consistency between them, and the local log accepts losing a little data in a crash. You would choose this approach for monitoring and metrics, with write-dominated workloads, reads over recent data and aggregations, and metrics with stable values at a regular interval.
Use it in a design
Practice the cases that rely on this concept in the System Design school.