rest.li README deprecation banner
States the decision, the date the repository goes read-only, and the reason: gRPC for performance, more languages, streaming and a community. Also states that no drop-in replacement is offered.
Reconstructed from 135 public repositories, their dated release histories on four package registries, and the security advisories filed against them: which parts of a derived-data platform LinkedIn kept building between 2015 and 2026, which it handed to a foundation, and which it let die without ever saying so. A reader leaves able to date any organisation's platform decisions from artefacts rather than announcements, and to argue the precompute, query and embed choices on the read path with numbers attached.
A member-facing page is assembled from data that was computed somewhere else, minutes or hours earlier, and it has a few milliseconds to arrive. The architectural question is not how to compute it. It is which parts of the machinery that stores and serves it you should own, given that the engines underneath will be replaced twice in a decade.
LinkedIn is a useful subject for this question because almost everything it decided is dated in public. The company published its storage engine, its change-capture system, its stream processor, its OLAP store, its service framework and its metadata catalogue, and each of those repositories now carries a release history, an archive notice, a fork, a transfer or an unexplained silence. Read together, the artefacts say something the announcements do not: the software LinkedIn still writes is not engines. It is the layer directly above them. Coral translates SQL dialects. Transport makes user-defined functions portable. OpenHouse reconciles table state. Hoptimator plans pipelines across Kafka, Flink and Venice. The engines those things sit on are Apache projects, several of which LinkedIn invented and gave away.
The registry record inverts the production record. Voldemort, which LinkedIn stopped running in 2018, is fully downloadable. Venice, which LinkedIn runs now, is not on Maven Central at all: its own README tells you to add a separate repository because "Venice dependencies are not published on Maven Central". Rest.li, deprecated in 2026, last published an artefact to Central on 17 December 2019, while its repository kept shipping releases up to 29.85.15 on 29 June 2026. If you judge a dependency's health by the registry, you will get LinkedIn exactly backwards.
What this guide covers: the derived-data half of LinkedIn's platform, meaning ingestion, change capture, materialisation and online serving of data computed elsewhere, plus the catalogue and translation layers around it, between roughly 2015 and September 2026. What it deliberately does not cover: Espresso, the online document store that holds primary member data and has never been published; the feed, search and advertising serving stacks; anything about ranking or model training beyond where it touches storage; and the internal economics, because no cost figure for any of this exists in public.
One shape recurs across every LinkedIn repository that touches derived data: compute offline or nearline, materialise into a store whose write path is a log, and then argue about how many network hops remain between the application and the bytes.
Venice's own write-path document is the clearest statement of the shape, and it is worth noticing how little of it is Venice. A store has versions; each version is a Kafka topic, called the version topic, and nearline writes arrive on a second topic, the real-time topic. Apache Helix assigns partitions to servers and decides which replica leads. ZooKeeper holds the metadata. The Venice server's job is to consume its partitions, apply them to local RocksDB, and report ingestion state back through Helix views. In other words, the system LinkedIn kept is a coordination and serving layer over three components it did not have to write, one of which, Helix, it wrote and then gave to the Apache Software Foundation in 2013 and still consumes from Maven Central today.
The read path is where the real design argument lives, and Venice's documentation prices it honestly. Three clients read the same data with the same API. The Thin Client takes two network hops through a router and is documented at under 10 milliseconds. The Fast Client takes one hop by routing straight to the partition and is documented at under 2 milliseconds. The Da Vinci Client takes zero hops, because it "eagerly loads Venice data into local RocksDB storage" inside the application process, and is documented at under 1 millisecond. The documentation states the price of the last one in the same breath: "Higher memory/disk footprint (stores full dataset locally)" and "Higher startup time (must bootstrap full dataset on first launch)". That is the entire precompute argument in two bullet points. Latency is bought with copies, and copies are paid for in bootstrap time and disk.
Kafka, Helix, Pinot, Samza and Gobblin all began at LinkedIn and now live at Apache. LinkedIn consumes them like everyone else, with one difference: it runs a patched fork of Kafka and says so plainly. The branch is "Apache Kafka trunk (upstream) up to some branch point", plus cherry-picks, plus "Patches that are on their way upstream but we have deployed internally in the meantime", plus "Patches that are of no interest to upstream".
Evidence: linkedin/kafka README, Helix releases 2013 to 2026
Coral defines an intermediate representation for SQL "independently of any SQL dialect" and converts between HiveQL, Spark SQL and Trino SQL. It published 343 versions of its Trino module between April 2021 and September 2026, which is the highest release cadence of anything LinkedIn puts on Maven Central. Transport does the same for user-defined functions. Neither stores a byte.
Evidence: Coral README, coral-trino release listing
Two stores survived a decade of replacement: Venice, for data computed elsewhere and read by key, and Ambry, for "trillions of small immutable objects (50K -100K) as well as billions of large objects". Both have an ingestion shape that general-purpose databases handle badly: a full dataset swap from a grid job, and immutable blobs at media scale.
Evidence: Venice README, Ambry README
The control plane is the newest layer and the one that best explains the pattern.
OpenHouse is described as "an open source control plane designed for efficient management
of tables within open data lakehouse deployments", and its published contract is an HTTP
API that puts Iceberg snapshots against a base table version. It does not own the table
format; Apache Iceberg does. It owns the reconciliation loop: the README says OpenHouse
"reconciles the observed state of Tables with the desired state by orchestrating various
data services". Hoptimator sits one level further out and turns a CREATE
MATERIALIZED VIEW statement into "running, multi-hop data pipelines that span
Kafka, Flink, Venice, and anything else you plug in". Both are the same architectural bet:
the durable asset is the description of what should be true, not the machinery that makes
it true.
There is one honest complication, and it is expensive. Owning the layer above an engine
does not keep you out of the engine. LinkedIn also maintains a fork of Iceberg whose
repository description is simply "A home for LinkedIn's changes to Apache Iceberg", whose
default branch is named openhouse-1.2.0, and which has published 85 versions
to Maven Central since June 2022. On 28 August 2026 it published two of them, one on the
1.2.0 line and one on the 1.5.2 line, which means two supported forks of somebody else's
format are alive at the same time. Nobody wrote that decision down anywhere public. The
artefacts are the only record that it was made.
Four forks in the road, each one visible in an artefact with a date on it, and the condition that would flip the answer for a different organisation.
| Decision | Chosen | Rejected | Because | Evidence |
|---|---|---|---|---|
| Storage engine ownership | Serve derived data from an in-house store | A general-purpose key-value store for both derived and primary data | Full-dataset swaps and region-local CRDT replication are not ordinary database features | Voldemort notice, 2018 |
| Read-path shape | Three clients at two, one and zero hops | One canonical client | Latency requirements differ by product surface, and the JVM hop dominates the tail | VIP-3, under discussion |
| Serving runtime | Proposal to rewrite the server read path in Rust | Continued JVM tuning | Target is a p99.99 under 1ms end to end, which garbage collection makes unpredictable | VIP-3 problem statement |
| Table format | Adopt Apache Iceberg and fork it | An in-house format | Interoperability with Spark, Trino and Flink is the point of the format | 85 fork releases since 2022 |
| Table governance | A declarative control plane over the format | Extending the catalogue inside each engine | Reconciliation must span engines that do not agree on anything else | OpenHouse Tables API spec |
| SQL portability | One intermediate representation, many dialects | Standardising on a single engine | Views outlive engines, and rewriting them per migration is unbounded work | Coral README |
| Change capture | A multi-tenant streaming system with change capture as one connector | A dedicated change-capture product | Mirroring and capture share the same operational surface and fault isolation needs | Brooklin README |
| Publication | Ship the live system from a private repository | Publishing Venice to Maven Central | Not stated anywhere public; the consequence is that the healthiest system leaves the weakest public trail | Venice dependency instructions |
LinkedIn publishes no postmortems for these systems. What exists instead is the defect record: five advisories and one abandoned pull request, which between them describe three failure classes an architect can plan against.
An advisory is not an incident report. It records what a defect made possible, who fixed it and when, and nothing about blast radius, detection time or whether anyone was hit. Every "blast radius" line below is therefore the exposure the defect created, not a measured impact. If a reader knows of a published LinkedIn postmortem for any of these systems, it belongs in this guide and I could not find one.
The third class is the one this method was built to find, and it has no advisory attached because nothing malfunctioned. Databus, LinkedIn's change-capture system with 3,700 stars, carries no deprecation banner of any kind; its successor Brooklin has been the published system for years, and a reader arriving at Databus today would find an ordinary repository. Feathr, the feature store LinkedIn open sourced in 2022 and placed under the LF AI and Data Foundation, still says in its README that it "is widely used in production at LinkedIn for many years", while its last Maven artefact is dated 30 June 2023. Venice's own README recommends Feathr as the feature store it backs. Dagli stopped at a beta in October 2021. TonY stopped in May 2022. None of them is archived, because only four repositories in the whole organisation are.
The design rule is mechanical and worth adopting: absence of an archive notice is not evidence of maintenance. The reliable signal is the registry, because publishing a release is an act somebody has to perform. Before adopting any component, look up its last release date on Maven Central, PyPI, npm, the Go proxy or Docker Hub, and compare that date with the last commit. A repository with recent commits and no release in two years is a project whose maintainers have moved on from shipping it to others.
Everything quantitative in this guide, with the date it was true and the artefact it came from. Latency figures are documentation targets, not measurements taken by this author.
| Metric | Value | At | Context | As of | Source |
|---|---|---|---|---|---|
| Query rate | 100k+ per second | Pinot, millisecond latency, 50+ user-facing products | 2026-09 | Pinot README | |
| Kafka fleet | thousands of brokers | Delivering trillions of messages per day on a patched fork | 2026-09 | linkedin/kafka README | |
| Blob store scale | trillions of objects | Ambry, objects of 50KB to 100KB plus billions of large ones | 2026-09 | Ambry README | |
| Read latency, two hops | < 10 ms | Venice | Thin client through the router | 2026-09 | Venice read APIs |
| Read latency, one hop | < 2 ms | Venice | Fast client, partition-aware routing | 2026-09 | Venice read APIs |
| Read latency, zero hops | < 1 ms | Venice | Da Vinci client, dataset resident in local RocksDB | 2026-09 | Da Vinci client doc |
| Tail-latency target | p99.99 < 1 ms | Venice | Goal of the proposed Rust read path, end to end at the client | 2026-09 | VIP-3 |
| Public repositories | 135 | Of which 4 are archived | 2026-09-15 | Archived listing | |
| Maven Central presence | 36 artifact groups | com.linkedin | No venice directory among them | 2026-09-15 | Group listing |
| rest.li public release span | 2013-02-15 to 2019-12-17 | Maven Central | 34 versions, last is 27.7.18 | 2026-09-15 | restli-server listing |
| rest.li repository version | 29.85.15 | GitHub | Released 2026-06-29, 23 days before the archive date | 2026-09-15 | CHANGELOG |
| Coral release cadence | 343 versions | Maven Central | coral-trino, 2021-04-09 to 2026-09-04 | 2026-09-15 | coral-trino listing |
| Iceberg fork releases | 85 versions | Maven Central | 2022-06-02 to 2026-08-28, two lines alive at once | 2026-09-15 | iceberg-core listing |
| Samza last release | 1.8.0, 2023-01-13 | Apache | Stream processor donated by LinkedIn | 2026-09-15 | samza-core listing |
| Gobblin handover gap | 11 months | Maven Central | com.linkedin ends 2017-07-21, org.apache begins 2018-06-20 | 2026-09-15 | Apache listing |
| Feathr last release | 2023-06-30 | Maven Central | 78 versions from 2022-04-26; README still claims production use | 2026-09-15 | feathr listing |
| DataHub releases | 1,271 | PyPI | acryl-datahub, 2021-04-02 to 2026-09-15 | 2026-09-15 | PyPI metadata |
| Frozen image pulls | 123,682,069 | Docker Hub | linkedin/datahub-gms, last updated 2024-04-02 | 2026-09-15 | Docker Hub metadata |
| Venice image pulls | 4,320 | Docker Hub | venicedb/venice-router, registered 2022-09-24 | 2026-09-15 | Docker Hub metadata |
Measured by nobody here: every latency figure is a target published in project documentation, and no independent benchmark of Venice, Pinot or Ambry appears in this corpus. Stated by the operator: the Pinot, Kafka and Ambry scale figures are LinkedIn's own claims in README files, with no date attached beyond the day the file was checked, so treat them as order-of-magnitude and current. Derived here: the eleven-month Gobblin handover gap and the twenty-three-day interval between rest.li's last release and its archive date are arithmetic on dated listings. Unknown: cost, cluster sizes, node counts, replication factors, and the actual production latency distribution of any of these systems. Nothing public answers those.
Twenty of the 55 sources behind this page, graded. The full ledger, with the quote supporting each claim, ships beside this file as sources.md. This corpus contains no conference talks, no papers and no engineering-blog posts, because the network policy in force during the research reached repositories and registries only; that limit is stated here rather than hidden.
States the decision, the date the repository goes read-only, and the reason: gRPC for performance, more languages, streaming and a community. Also states that no drop-in replacement is offered.
The announcement thread. Confirms artefacts stay on Maven Central, that issues and pull requests stop being accepted, and that adopters should choose their own replacement.
The final year of a deprecated framework is xDS work: subscribing the discovery client to an observer cluster over xDS, and adding metrics that detect clients approaching gRPC's per-message size limit.
Thirty-four versions, the first in February 2013 and the last in December 2019. The repository went on to 29.85.15. The public artefact trail ends seven years before the project does.
Records the end of the Dynamo-style era in two sentences: production use stopped in 2018, read-only use cases moved to Venice, and only some of the read-write ones did.
Defines Venice as a derived data storage platform with batch and streaming ingestion, active-active CRDT replication and multi-tenancy, and tells consumers to add a separate artifact repository because it is not on Maven Central.
Three clients, two, one and zero network hops, documented at under 10ms, under 2ms and under 1ms, all sharing get, batchGet and compute so a use case can move between them.
The embedded option: eager load of the whole dataset into local RocksDB, zero network hops, and the stated costs of disk footprint and bootstrap on first launch.
Proposes replacing the Java serving path with Rust to remove garbage collection, with a stated goal of p99.99 under 1ms end to end and a minimal scope of batch gets over gRPC and RocksDB. Justified by growth in AI workloads.
Six proposals, two accepted and four under discussion, covering authentication, per-record offset metadata, the Rust read path, lifecycle hooks, facet counting and Kubernetes.
Describes three safety defects in per-partition version swap, including records discarded at the swap point, proposes a cross-region barrier, and is closed by a bot after 37 days without review.
Version topics and real-time topics on Kafka, ZooKeeper for metadata, Helix for partition assignment and leader election, one ingestion task per store per version.
States the query-side numbers: 50+ user-facing products, millions of events per second ingested, more than 100,000 queries per second at millisecond latency.
Critical, CVSS 9.8, affecting every version before 0.11.0. Script execution reachable from the query endpoint and the ingestion path because the feature shipped on.
High, CVSS 8.7. Pinot's controller exposed operating system details, heap size and ZooKeeper paths on an endpoint anyone could reach; the fix was role-based access control in 1.0.0.
Critical, CVSS 9.3, versions 0.8.0 to before 1.3.0. A request whose path avoided a slash and contained a dot skipped authentication entirely, up to adding accounts.
Critical, CVSS 10.0. An MCP server defaulted to an open bind with authentication off, handing every tool, including table mutation, to any network-adjacent caller using the server's credentials.
Critical, CVSS 9.9. The metadata service parsed JSON Web Tokens without verifying signatures, so any user could be impersonated while authentication appeared to be on.
Documents a production fork in four categories: upstream trunk to a branch point, cherry-picks, patches on their way upstream, and patches of no interest to upstream. External contributions are not accepted.
The control plane's published contract: an HTTP interface that applies Iceberg snapshots against a base table version, with the declarative catalogue reconciling observed state against desired state.
123,682,069 pulls, registered May 2020, last updated April 2024, against 5,308,970 pulls on the live acryldata image that was updated the day this guide was written.
The README describes a platform "widely used in production at LinkedIn for many years" under a foundation; the last published artefact is from June 2023 and the repository is not archived.
Six rungs. The first three are an evening each and teach the read-path trade-off by making you pay for it; the last three are where a toy becomes something you would put a product on.
Compute a keyed dataset in a batch job, write it to a topic named for its version, and have a server consume the topic into a local embedded store. Serve reads by key.
Done when: a second batch run produces version 2 and a reader can still read version 1 throughout. Teaches: why a derived store's unit of change is a dataset version, not a row.
Cut readers over from version 1 to version 2 partition by partition. Then delay one partition and write a record that arrives after the swap point on the fast partition.
Done when: you can demonstrate a key that is briefly missing or stale, and then fix it with a barrier that waits for every partition. Teaches: the failure described in Venice pull request 2795, at a scale you can debug.
Add a client that routes straight to the partition owner instead of through a router, then a client that keeps the whole dataset in-process. Record p50 and p99 for all three.
Done when: you have three latency distributions and a measured cold-start bootstrap time for the embedded client. Teaches: that the hop you remove is often a runtime rather than a network round trip.
Add a second topic for streaming updates applied on top of the batch version, with a rule for what happens when both touch a key. Give it a conflict resolution strategy you can state in one sentence.
Done when: a batch push does not erase nearline updates written during the push. Teaches: why active-active derived stores reach for conflict-free data types rather than timestamps.
Move the dataset onto an open table format, then write a small service that accepts a declared desired state, such as retention or schema, and runs jobs until the observed state matches.
Done when: changing the declaration causes the reconciler to act without anyone running a job by hand. Teaches: the OpenHouse position, that the durable asset is the description rather than the machinery.
For every third-party component you depend on, record the last release date from its registry, the last commit date, whether the namespace has changed hands, and the count of open advisories.
Done when: you can name the three dependencies whose registry has been quiet longest and say what you would do if each stopped tomorrow. Teaches: the method this guide is built on, applied to your own risk.
These are the searches and the fetches that produced this page. None of them needs a search engine, which is why they still work when a blog is offline or a network policy blocks it.
https://repo1.maven.org/maven2/com/<org>/curl -s https://pypi.org/pypi/<package>/json | jq '.releases | keys'curl -s https://registry.npmjs.org/<package> | jq '.time'curl -s https://hub.docker.com/v2/repositories/<org>/<image>/curl -s https://proxy.golang.org/github.com/<org>/<repo>/@v/listgithub.com/orgs/<org>/repositories?q=archived%3Atrue&sort=updatedrepo:<org>/<repo> is:pr is:closed is:unmerged sort:comments-descpath:docs/contributing/proposals OR path:beps OR path:docs/adrraw.githubusercontent.com/<org>/<repo>/master/CHANGELOG.mdraw.githubusercontent.com/<org>/<repo>/master/gradle.propertiesgithub.com/advisories?query=<project>github.com/<org>/<repo>/security/advisories"enabled by default" advisory <project>repo:<org>/<repo> is:issue label:bug sort:reactions-+1-desc"derived data" platform serving store"version topic" OR "version swap" ingestion"control plane" table lakehouse reconcile"multi-hop" materialized view pipeline plannerxDS D2 discovery migration gRPC