TRM Labs

How TRM Labs Built a Petabyte-Scale Lakehouse on Apache Iceberg and StarRocks

TRM Labs needed to serve customer-facing blockchain analytics from multiple sites, including on-premises, which BigQuery could not support. After benchmarking table formats and query engines, the team built a lakehouse on Apache Iceberg and StarRocks, improving P95 response times by 50%.

50%

better P95 response times

After tuning Iceberg partitioning and clustering, StarRocks cluster sizing and caching for customer-facing workloads

54%

fewer query timeout errors

Same tuning work as the P95 improvement

470ms

lookup/filter response time

TRM benchmark on a 2.57 TB blockchain dataset, StarRocks Large cluster with data cache; Trino measured 1,030–1,410 ms

CompanyTRM Labs
Use caseCustomer-facing blockchain analytics
Previous solutionBigQuery + distributed Postgres (Citus Data)
CloudGoogle Cloud, On-premises
Employees<500
ReplacedBigQuery

Originally published at: TRM Labs Tech Blog

About TRM Labs

TRM Labs builds blockchain intelligence tools that financial institutions, crypto businesses and government agencies use to detect and investigate fraud and financial crime involving crypto assets. Its analytics platform works over petabytes of data from more than 30 blockchains and serves over 500 customer queries per minute.

TRM picked its storage format and query engine by benchmarking them on its own blockchain data: Apache Iceberg for tables on object storage, StarRocks for serving.

Key takeaway · Benchmark on real workloads

The original post was written by Vijay Shekhawat and Andrew Fisher (Staff Software Engineer), both of the TRM Labs Data Platform team.

The challenge

TRM's first-generation platform split user-facing analytics across two systems. A distributed Postgres cluster (Citus Data) answered point lookups and served small pre-aggregates. Larger queries and ad hoc aggregations that went beyond the Postgres cluster's capacity were federated to BigQuery.

Layered diagram of TRM's first-generation platform: TRM application on top; BigQuery and distributed Postgres as compute; BigQuery storage and SSD / persistent disks as storage; Google Cloud Platform as the platform
First-generation data platform

The team had tuned BigQuery for years, and it handled customer analytics well. Two things changed that:

  • Multi-site deployment. TRM needed to run its platform on several on-premise sites and share blockchain analytics data between them. A managed service such as BigQuery could not do this. The replacement had to be open source and deployable on Kubernetes.
  • Scale and performance. The largest customer-facing workload served from BigQuery holds more than 115 TB and grows 2–3% a month. Its read queries join several levels of tables and filter on time ranges and arrays. Holding a three-second P95 at high concurrency on BigQuery was hard without buying expensive compute slots, and moving the workload into distributed Postgres would have cost too much in storage alone.

The solution

TRM decided to build a lakehouse: lake-style flexibility with the performance and reliability of a warehouse. From its BigQuery and Postgres experience, the team knew it needed to read as little data as possible per query (compression, clustering, partitioning), vectorized execution, horizontal scaling for concurrency, and storage separated from compute so engines could be swapped or combined without copying data. That led it past traditional OLAP stores such as ClickHouse and toward two decisions: a table format and a query engine.

Table format

Storage needs at TRM grow exponentially each year as high-throughput blockchains are added. Cost settled the medium first: TRM noted that even the priciest object store costs 4x less than the cheapest SSD. It then compared three open table formats on object storage: Delta Lake 2.4.0, Apache Hudi 0.14.0 and Apache Iceberg 1.4.0.

Feature table comparing Delta Lake 2.4.0, Hudi 0.14.0 and Iceberg 1.4.0 on ACID transactions, schema evolution, time travel, merge support, indexing and clustering, partition evolution, merge-on-read, streaming and multi-engine support
Open table formats TRM evaluated

Delta Lake was ruled out because it lacks partition evolution and covers much the same ground as Iceberg for batch processing and analytics at scale. In TRM's Hudi benchmark, the best Hudi table ran 3x slower than Iceberg. TRM chose Iceberg for its read performance, wide adoption, active community and support across catalogs and engines.

Query engine

To prepare test data, the team exported 2–3 TB of tables from BigQuery to Parquet on Google Cloud Storage, converted them to Iceberg tables with PySpark (bucketed, sorted and partitioned around how queries filter by address, entity or time period), and kept table metadata in Dataproc Metastore.

TRM then tested three engines whose open source versions read Iceberg: Trino 433 on Google Dataproc, StarRocks 3.1 on GKE through the StarRocks Kubernetes operator, and DuckDB 0.9.2 on GCP virtual machines. Each ran at several cluster sizes, with JMeter generating concurrent load. The tests ran in early 2024.

Lookup/filter. Point and range lookups returning small result sets from a 2.57 TB dataset. StarRocks was fastest in every configuration and reached 470 ms with data caching. Trino ranged from 1,030 ms to 1,410 ms depending on cluster size. DuckDB took 2–3 seconds on a large single node; TRM stopped testing it here because its Iceberg extension did not yet support predicate pushdown.

Bar chart of lookup/filter response times in ms by configuration for DuckDB, Trino and StarRocks; StarRocks Large with data cache is lowest at 470 ms
Lookup/filter benchmark, 2.57 TB dataset

Complex aggregation. Queries with SUM, COUNT and GROUP BY plus array and date-range filters over a 2.85 TB dataset. StarRocks ran in about 2 seconds without caching and about 500 ms with caching on the largest cluster. Trino got faster as clusters grew but leveled off near 2.5 seconds.

Bar chart of complex aggregation response times in ms for Trino and StarRocks across cluster configurations; StarRocks Large with data cache is lowest at 510 ms
Complex aggregation benchmark, 2.85 TB dataset

Stress test. Each JMeter run lasted one minute with no query limit, stepping up to 55 concurrent threads. Trino slowed as concurrency rose, while StarRocks stayed ahead on both workloads, more so with data caching on. Trino had no data lake caching at the time of the tests; it arrived in Trino 439, which TRM has not yet evaluated.

Stress test at 55 threads: complex aggregations took 10,062 ms on Trino, 6,571 ms on StarRocks and 3,173 ms on StarRocks with data cache; lookup/filter took 8,743 ms, 1,881 ms and 707 ms
Stress test at 55 concurrent threads

The new platform

TRM built its next-generation platform for user-facing analytics with StarRocks as the query engine over Iceberg tables in cloud storage. Distributed Postgres stays in place on SSD / persistent disks, and the platform targets Google Cloud Platform as well as other clouds and on-premise sites.

Layered diagram of TRM's next-generation platform: TRM application; StarRocks and distributed Postgres as compute; Iceberg, cloud storage and SSD / persistent disks as storage; Google Cloud Platform and other clouds / on-premise as the platform
Next-generation data platform

Because Iceberg is an open format, TRM can add or swap engines later. It plans to re-test Trino and DuckDB as well as newer options such as ClickHouse and Crunchy Data Warehouse.

StarRocksApache IcebergCloud object storageDistributed Postgres (Citus Data)KubernetesGoogle Cloud

The results

After tuning Iceberg partitioning and clustering, StarRocks cluster sizing and caching, TRM reports:

  • 50% better P95 response times for customer-facing queries.
  • 54% fewer query timeout errors, which let the team meet its query performance targets.
  • One platform for many sites: Iceberg and StarRocks met the multi-site deployment requirement that BigQuery could not.
  • Lower storage cost potential: with data and metadata on object storage, TRM sees room to move workloads off the distributed Postgres cluster and cut SSD spend.

The team also found that production traffic exposed usage patterns and tuning opportunities that the benchmarks had not, and treats testing at real scale as a required step.