LinkedIn open-sourced Venice in 2022 as the store behind derived datasets such as People You May Know, after running it in production since late 2016. A Venice store can be loaded by a batch push and written by a stream at the same time. When a push swaps in a new version of the dataset the stream's recent writes have to be dealt with somehow. What does the design do, and what does it cost?
Show the full answer Hide the answer
The situation they were in
A derived dataset such as a candidate list is produced two ways at once: a heavy offline job recomputes the whole thing on a schedule, and a nearline job nudges individual keys as events arrive. The classic arrangement serves both and merges them in the application, which means every reader carries the merge logic and every reader can get it wrong.
What Venice does
A Venice store can be configured as a hybrid store with a rewind time. A batch push writes a complete new version of the dataset alongside the serving one. Before the swap, the real-time stream is rewound by the configured interval and re-applied on top of the new version, so the new version already carries the recent nearline updates at the moment it starts serving. LinkedIn titled the 2017 post describing this "Venice Hybrid: Doing Lambda Better", and the point of the title is that the batch and streaming paths are reconciled at ingest time rather than at read time. The application sees one key-value store.
LinkedIn's 2022 announcement put Venice behind more than 1800 datasets used by over 300 applications, with three client shapes: a thin client, a fast client, and Da Vinci, which keeps a local copy in the caller's own process and is documented at under 1 ms for a read because there is no network hop at all.
What it costs
- The rewind interval has to exceed the batch job's own lag. If the offline job cuts its input at 02:00 and finishes pushing at 05:30, a two-hour rewind leaves a ninety-minute hole where nearline updates were applied to a version that has just been discarded. Too long a rewind and the push takes longer and replays more.
- Every stream write must be replayable. Re-applying four hours of updates has to land in the same state as applying them once, so full-value overwrites and partial updates are safe and a read-modify-write counter is not.
- Two copies of the dataset exist during a push, so capacity is sized for roughly 2× the largest store.
- There is no read-your-writes guarantee, because the write path is asynchronous. A caller that writes and immediately reads can see the old value.
Where copying it would be a mistake
This shape earns itself at the scale where hundreds of applications would otherwise each implement the merge. For one derived table, a materialised view refreshed on a schedule in the database that already holds the data does the same job with no new system. Choose a hybrid store only when the number of independent readers that would otherwise have to agree on how to combine two views is large enough that the merge logic has already been written twice; data volume is not the threshold.
Common weak answers
- "Just write the stream output to the same table and let last-write-win." Without the rewind, the swap discards every nearline update applied to the previous version, and the dataset silently regresses by the length of the push. The failure raises nothing: the store is healthy and the values are old.
- "Serve batch and streaming separately and merge in the client." That is the arrangement being replaced; it works until the second team implements the merge differently.