<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/">
    <channel>
        <title>Apache Hudi: User-Facing Analytics</title>
        <link>https://hudi.apache.org/blog</link>
        <description>Apache Hudi Blog</description>
        <lastBuildDate>Thu, 22 Jan 2026 00:00:00 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <language>en</language>
        <item>
            <title><![CDATA[Scaling Autonomous Vehicle Data Infrastructure with Apache Hudi at Applied Intuition]]></title>
            <link>https://hudi.apache.org/blog/2026/01/22/apache-hudi-at-applied-intuition</link>
            <guid>https://hudi.apache.org/blog/2026/01/22/apache-hudi-at-applied-intuition</guid>
            <pubDate>Thu, 22 Jan 2026 00:00:00 GMT</pubDate>
            <description><![CDATA[---]]></description>
            <content:encoded><![CDATA[<hr>
<p><em>This post summarizes Applied Intuition's talk from the Apache Hudi community sync. Watch the recording on <a href="https://www.youtube.com/watch?v=gWw6hOcM-Fg" target="_blank" rel="noopener noreferrer" class="">YouTube</a>.</em></p>
<hr>
<p><img decoding="async" loading="lazy" alt="Talk title slide" src="https://hudi.apache.org/assets/images/img1-5584b5aa92db392fa1312536a6de7eed.png" width="1999" height="1233" class="img_ev3q"></p>
<p>Applied Intuition is the foremost enabler of autonomous vehicle (AV) systems, providing a suite of tools that help AV companies improve their entire stack—from simulation to data exploration. To support their mission, Applied Intuition built a unique data infrastructure that is flexible, scalable, and secure. After migrating to an Apache Hudi-powered data lakehouse, they transformed their data capabilities: query times dropped from 10 minutes to under 25 seconds, and they can now query 3-4 orders of magnitude more data than before.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="building-a-unique-data-infrastructure">Building a Unique Data Infrastructure<a href="https://hudi.apache.org/blog/2026/01/22/apache-hudi-at-applied-intuition#building-a-unique-data-infrastructure" class="hash-link" aria-label="Direct link to Building a Unique Data Infrastructure" title="Direct link to Building a Unique Data Infrastructure" translate="no">​</a></h2>
<p>Applied Intuition's data infrastructure is designed to meet the specific needs of its diverse customer base, including 17 of the top 20 OEMs. Their infrastructure is built around four core principles.</p>
<p>First, schemas must be flexible. Each customer determines their own data schema, so the infrastructure must handle a wide variety of data points without requiring rigid upfront definitions.</p>
<p>Second, compute needs to be tunable. Some customers are more cost-sensitive while others have larger-scale needs, so the infrastructure can adjust compute resources on a per-customer basis.</p>
<p>Third, everything must be cloud agnostic. Because customers operate on different cloud providers, the infrastructure—built on Kubernetes—works seamlessly across all of them without relying on a single vendor.</p>
<p>Finally, security and privacy are paramount. All data and infrastructure live within the customer's own cloud accounts. This ensures that customers fully own and control their data, enabling strict security, privacy, and retention policies.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="the-challenges-before-apache-hudi">The Challenges Before Apache Hudi<a href="https://hudi.apache.org/blog/2026/01/22/apache-hudi-at-applied-intuition#the-challenges-before-apache-hudi" class="hash-link" aria-label="Direct link to The Challenges Before Apache Hudi" title="Direct link to The Challenges Before Apache Hudi" translate="no">​</a></h2>
<img src="https://hudi.apache.org/assets/images/blog/2026-01-22-apache-hudi-at-applied-intuition/img2.png" alt="Architecture before Apache Hudi" width="800">
<p>Before adopting Apache Hudi, Applied Intuition's data infrastructure directly queried a raw data lake on S3/ABFS using SQL engines. While this approach worked initially, significant issues emerged as scale increased.</p>
<p>The system struggled to provide ACID transaction guarantees critical for data integrity. Storage costs kept climbing because storing all data in raw format was expensive. As small files accumulated, query performance degraded dramatically due to the I/O overhead of opening and closing countless files.</p>
<p>To address these challenges, Applied Intuition adopted Apache Hudi, which introduced a transactional layer and metadata management to their data lake—transforming file system storage into a modern data lakehouse.</p>
<p>Applied Intuition primarily uses <a href="https://hudi.apache.org/docs/table_types#copy-on-write-table" target="_blank" rel="noopener noreferrer" class="">Copy-on-Write</a> (COW) tables. While Hudi also offers <a href="https://hudi.apache.org/docs/table_types#merge-on-read-table" target="_blank" rel="noopener noreferrer" class="">Merge-on-Read</a> (MOR) tables for faster ingestion, their main priority is query performance. With COW tables, they achieve fast query execution while accepting slightly higher write latency.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="leveraging-hudi-features-to-shape-data-architecture">Leveraging Hudi Features to Shape Data Architecture<a href="https://hudi.apache.org/blog/2026/01/22/apache-hudi-at-applied-intuition#leveraging-hudi-features-to-shape-data-architecture" class="hash-link" aria-label="Direct link to Leveraging Hudi Features to Shape Data Architecture" title="Direct link to Leveraging Hudi Features to Shape Data Architecture" translate="no">​</a></h2>
<p>Applied Intuition leverages three core Hudi services to optimize its data infrastructure: file sizing, clustering, and metadata indexing.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="file-sizing-solving-the-small-file-problem">File Sizing: Solving the Small File Problem<a href="https://hudi.apache.org/blog/2026/01/22/apache-hudi-at-applied-intuition#file-sizing-solving-the-small-file-problem" class="hash-link" aria-label="Direct link to File Sizing: Solving the Small File Problem" title="Direct link to File Sizing: Solving the Small File Problem" translate="no">​</a></h3>
<p>File sizing was the very first reason they started using Hudi. The company runs thousands of simulations daily, each generating numerous small files. This led to the classic "small file problem"—Spark queries would spend significant time just opening and closing files to read metadata. Spark SQL performs best with files around 512MB, but simulation files are often just kilobytes.</p>
<img src="https://hudi.apache.org/assets/images/blog/2026-01-22-apache-hudi-at-applied-intuition/img3.png" alt="File sizing optimization" width="800">
<p>Hudi's file sizing service efficiently packs small files into optimally-sized files by analyzing previous commits and estimating the number of records per file. By combining countless kilobyte-sized files, Hudi drastically reduced I/O overhead and improved query performance. Their data now takes up 20x less space than with raw Parquet files, resulting in substantial S3 cost savings.</p>
<p>Rohit recalls this as the first "aha moment" with Hudi: "We had less than a gigabyte of data, but query performance was really slow. When we first tried file sizing, performance improved dramatically—and we saw all our data fit within megabytes. It was really cool to see that level of compression and query performance just out of the box."</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="clustering-optimizing-for-query-patterns">Clustering: Optimizing for Query Patterns<a href="https://hudi.apache.org/blog/2026/01/22/apache-hudi-at-applied-intuition#clustering-optimizing-for-query-patterns" class="hash-link" aria-label="Direct link to Clustering: Optimizing for Query Patterns" title="Direct link to Clustering: Optimizing for Query Patterns" translate="no">​</a></h3>
<p>Many of Applied Intuition's queries focus on specific chunks of data, or batches. Hudi's <a href="https://hudi.apache.org/docs/clustering/" target="_blank" rel="noopener noreferrer" class="">clustering</a> feature improves data co-location by arranging related records together, minimizing the number of files touched per query.</p>
<img src="https://hudi.apache.org/assets/images/blog/2026-01-22-apache-hudi-at-applied-intuition/img4.png" alt="Clustering optimization" width="800">
<p>For example, by clustering all data from a single "simulation run ID" into just one or two files, Hudi allows queries to avoid scanning thousands of files. This has led to massive improvements in query performance. Applied Intuition runs clustering jobs asynchronously to maintain low write latency while keeping query performance high.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="metadata-indexing-from-minutes-to-seconds">Metadata Indexing: From Minutes to Seconds<a href="https://hudi.apache.org/blog/2026/01/22/apache-hudi-at-applied-intuition#metadata-indexing-from-minutes-to-seconds" class="hash-link" aria-label="Direct link to Metadata Indexing: From Minutes to Seconds" title="Direct link to Metadata Indexing: From Minutes to Seconds" translate="no">​</a></h3>
<p>Before Hudi, loading data from raw cloud storage could take minutes, especially when listing millions of files. Hudi's <a href="https://hudi.apache.org/docs/metadata_indexing/" target="_blank" rel="noopener noreferrer" class="">metadata indexing</a> creates a file index that allows the dataframe to load in under two seconds—a huge UX improvement for their customers.</p>
<img src="https://hudi.apache.org/assets/images/blog/2026-01-22-apache-hudi-at-applied-intuition/img5.png" alt="Metadata indexing" width="800">
<p>Additionally, they use column stats indices, which store min/max values for key columns. When a query runs, Hudi uses these stats to skip irrelevant files that don't match the query criteria, enabling much faster lookups.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="extending-hudi-for-schema-flexibility">Extending Hudi for Schema Flexibility<a href="https://hudi.apache.org/blog/2026/01/22/apache-hudi-at-applied-intuition#extending-hudi-for-schema-flexibility" class="hash-link" aria-label="Direct link to Extending Hudi for Schema Flexibility" title="Direct link to Extending Hudi for Schema Flexibility" translate="no">​</a></h3>
<p>Given their wide customer base and evolving schema needs, Applied Intuition extended Hudi with two customizations: one to evict the cached file schema provider so mid-day schema updates are picked up during writes, and another to allow Parquet batching even when schemas differ across commits—common in simulation data where batches may have different columns.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="impact-performance-cost-and-scale">Impact: Performance, Cost, and Scale<a href="https://hudi.apache.org/blog/2026/01/22/apache-hudi-at-applied-intuition#impact-performance-cost-and-scale" class="hash-link" aria-label="Direct link to Impact: Performance, Cost, and Scale" title="Direct link to Impact: Performance, Cost, and Scale" translate="no">​</a></h2>
<p>The improvements with Hudi have been transformative. Applied Intuition can now query 3-4 orders of magnitude more data than before. Storage costs dropped significantly thanks to file packing that achieves 20x compression compared to raw Parquet files. Query times that once took 10 minutes now complete in under 25 seconds, and dataframe initialization that used to take minutes now happens in seconds.</p>
<p>Despite running on tight compute resources—just 1-2 machines running DeltaStreamer—their ingestion latency sits around 15 minutes. They can easily scale this by adding more compute when needed.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="next-steps-moving-beyond-postgresql">Next Steps: Moving Beyond PostgreSQL<a href="https://hudi.apache.org/blog/2026/01/22/apache-hudi-at-applied-intuition#next-steps-moving-beyond-postgresql" class="hash-link" aria-label="Direct link to Next Steps: Moving Beyond PostgreSQL" title="Direct link to Next Steps: Moving Beyond PostgreSQL" translate="no">​</a></h2>
<p>After successfully implementing Hudi on a few key tables, Applied Intuition is scaling Hudi to support their entire data lake architecture.</p>
<img src="https://hudi.apache.org/assets/images/blog/2026-01-22-apache-hudi-at-applied-intuition/img6.png" alt="PostgreSQL CDC architecture" width="800">
<p>A proof of concept integrates PostgreSQL CDC via Debezium into Kafka, which feeds into Hudi DeltaStreamer, replicating transactional data into a Hudi-powered lakehouse. This setup enables non-critical queries to shift away from PostgreSQL, reducing database load and improving overall product performance. It also opens up deeper analytical insights directly from the data lake for both internal teams and customers.</p>
<p>The team worked through some initial setup challenges, resolving tombstone record handling through PostgreSQL and Debezium configuration updates.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="acknowledgments">Acknowledgments<a href="https://hudi.apache.org/blog/2026/01/22/apache-hudi-at-applied-intuition#acknowledgments" class="hash-link" aria-label="Direct link to Acknowledgments" title="Direct link to Acknowledgments" translate="no">​</a></h2>
<p>Applied Intuition is grateful for the incredible support from the Apache Hudi community, which has significantly improved their data infrastructure. The Onehouse team—Sivabalan, Ethan, and Nadine—has been particularly helpful, staying up on long night calls to help debug issues and ensure the team understood the product deeply. Nadine also provided ongoing support by answering questions on Slack.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="conclusion">Conclusion<a href="https://hudi.apache.org/blog/2026/01/22/apache-hudi-at-applied-intuition#conclusion" class="hash-link" aria-label="Direct link to Conclusion" title="Direct link to Conclusion" translate="no">​</a></h2>
<p>Applied Intuition's journey with Apache Hudi demonstrates how a modern data lakehouse platform can solve complex data infrastructure challenges while unlocking new levels of performance and insight.</p>]]></content:encoded>
            <category>data lakehouse</category>
            <category>applied intuition</category>
        </item>
        <item>
            <title><![CDATA[Apache Hudi™ at Uber: Engineering for Trillion-Record-Scale Data Lake Operations]]></title>
            <link>https://hudi.apache.org/blog/2026/01/16/apache-hudi-at-uber</link>
            <guid>https://hudi.apache.org/blog/2026/01/16/apache-hudi-at-uber</guid>
            <pubDate>Fri, 16 Jan 2026 00:00:00 GMT</pubDate>
            <description><![CDATA[Redirecting... please wait!!]]></description>
            <content:encoded><![CDATA[<span>Redirecting... please wait!! <!-- -->or click <a href="https://www.uber.com/blog/apache-hudi-at-uber/">here</a></span>]]></content:encoded>
            <category>uber</category>
        </item>
        <item>
            <title><![CDATA[From Legacy to Leading: Funding Circle's Journey with Apache Hudi]]></title>
            <link>https://hudi.apache.org/blog/2026/01/15/funding-circle</link>
            <guid>https://hudi.apache.org/blog/2026/01/15/funding-circle</guid>
            <pubDate>Thu, 15 Jan 2026 00:00:00 GMT</pubDate>
            <description><![CDATA[---]]></description>
            <content:encoded><![CDATA[<hr>
<p><em>This post summarizes Funding Circle's presentation at the Apache Hudi Community Sync. Watch the recording on <a href="https://www.youtube.com/watch?v=P29dfaxUdTU" target="_blank" rel="noopener noreferrer" class="">YouTube</a>.</em></p>
<hr>
<p><img decoding="async" loading="lazy" alt="og" src="https://hudi.apache.org/assets/images/01-hudi-sync-funding-circle-5e541fbef46383b1af3482fce37acccb.png" width="1999" height="969" class="img_ev3q"></p>
<p><a href="https://www.fundingcircle.com/" target="_blank" rel="noopener noreferrer" class="">Funding Circle</a> is a lending company focused on helping small and medium-sized businesses access the funding they need to grow. Their instant decisioning engine allows customers to complete loan applications in minutes and receive decisions in seconds. To date, the company has supported more than 135,000 businesses with over £15 billion in loans.</p>
<p>In this community sync, Daniel Ford, Data Platform Engineer at Funding Circle, shared how his team built a modern data ingestion framework using Apache Hudi and the significant improvements they achieved.</p>
<p>Funding Circle's lending capability relies on a broad and complex data platform. The goal of the platform is to make data easy to work with, enabling users to drive business decisions without requiring specialized data engineering knowledge. Developers should be able to build quickly, while data consumers should be able to find and use information without constantly seeking support.</p>
<p>However, their existing legacy Kafka ingestion solution, developed back in 2018-2019, was actively undermining these goals.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="why-the-legacy-ingestion-system-needed-to-change">Why the Legacy Ingestion System Needed to Change<a href="https://hudi.apache.org/blog/2026/01/15/funding-circle#why-the-legacy-ingestion-system-needed-to-change" class="hash-link" aria-label="Direct link to Why the Legacy Ingestion System Needed to Change" title="Direct link to Why the Legacy Ingestion System Needed to Change" translate="no">​</a></h2>
<p>The legacy ingestion tool suffered from multiple critical problems that made maintaining and scaling the platform difficult:</p>
<ul>
<li class="">
<p><strong>Schema Evolution Instability:</strong> Handling changes in data schemas was often unreliable or completely unviable, leading to frequent pipeline breaks and manual intervention.</p>
</li>
<li class="">
<p><strong>Code Complexity and Age:</strong> The codebase was massively complex and dated, predating almost every current engineer on the Data Platform team, making maintenance and updates a slow, painful process.</p>
</li>
<li class="">
<p><strong>Centralized Control vs. Data Mesh:</strong> The pipelines featured centralized deployment and management, which fundamentally opposed the company's strategic aim of achieving a data mesh architecture.</p>
</li>
<li class="">
<p><strong>Lack of Ownership and Observability:</strong> There was no effective way to establish true end-to-end domain ownership for a data pipeline, and a general lack of observability made it hard to quickly diagnose and fix issues.</p>
</li>
<li class="">
<p><strong>Prohibitive Backfilling Times:</strong> Syncing large Kafka topics took prohibitively long in the legacy solution, severely limiting the ability to correct or reload historical data.</p>
</li>
<li class="">
<p><strong>Inability to Support Real-Time Data:</strong> The system was not designed to support near-real-time ingestion, trapping users in slow batch-processing cycles.</p>
</li>
</ul>
<p>To move forward, the team defined clear goals: the new system needed to deliver data within ten minutes, support stable schema evolution, integrate with metadata systems like DataHub, and offer strong monitoring, scalability, and built-in PII masking. It also needed to shift ownership to individual teams by allowing decentralized pipeline definitions.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="introducing-project-kirby-the-new-ingestion-framework">Introducing Project Kirby: The New Ingestion Framework<a href="https://hudi.apache.org/blog/2026/01/15/funding-circle#introducing-project-kirby-the-new-ingestion-framework" class="hash-link" aria-label="Direct link to Introducing Project Kirby: The New Ingestion Framework" title="Direct link to Introducing Project Kirby: The New Ingestion Framework" translate="no">​</a></h2>
<p>After six to seven months of development, the team created Kirby—short for <strong>"Kafka Ingestion Real-Time or Batch through YAML"</strong>—which provides a simple, configuration-driven interface built around Apache Hudi Streamer running on AWS EMR.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="architecture-and-user-experience">Architecture and User Experience<a href="https://hudi.apache.org/blog/2026/01/15/funding-circle#architecture-and-user-experience" class="hash-link" aria-label="Direct link to Architecture and User Experience" title="Direct link to Architecture and User Experience" translate="no">​</a></h3>
<p><img decoding="async" loading="lazy" alt="Kirby pipelines running both batch and streaming workloads" src="https://hudi.apache.org/assets/images/02-hudi-sync-funding-circle-0eedf72d96f6d96ddd3e95c42a4b838e.png" width="2441" height="1246" class="img_ev3q"></p>
<p>Kirby organizes ingestion into three major areas: declaration, compute, and access.</p>
<h4 class="anchor anchorTargetStickyNavbar_Vzrq" id="pipeline-declaration-and-deployment">Pipeline Declaration and Deployment<a href="https://hudi.apache.org/blog/2026/01/15/funding-circle#pipeline-declaration-and-deployment" class="hash-link" aria-label="Direct link to Pipeline Declaration and Deployment" title="Direct link to Pipeline Declaration and Deployment" translate="no">​</a></h4>
<p>Users define their pipeline in a simple YAML file stored in their own repository. This file specifies metadata such as region, domain ownership, and pipeline type. Users deploy the pipeline through GitHub Actions or Drone, depending on their existing CI/CD setup. Kirby converts the YAML definition into an Airflow DAG, letting users track the history and progress of their ingestion tasks. To safely manage secrets, the team extended the EMR step operator to load credentials from AWS Secrets Manager at runtime. Metadata embedded in the DAG also gives the central team visibility into how many pipelines exist, who owns them, and which versions are running.</p>
<h4 class="anchor anchorTargetStickyNavbar_Vzrq" id="compute-layer">Compute Layer<a href="https://hudi.apache.org/blog/2026/01/15/funding-circle#compute-layer" class="hash-link" aria-label="Direct link to Compute Layer" title="Direct link to Compute Layer" translate="no">​</a></h4>
<p>The actual ingestion jobs run via Hudi Streamer 0.11.1 on EMR 6.8, utilizing both batch and continuous streaming configurations. Hudi Streamer directly ingests data from Kafka on Funding Circle's centrally maintained EMR cluster.</p>
<h4 class="anchor anchorTargetStickyNavbar_Vzrq" id="access-layer">Access Layer<a href="https://hudi.apache.org/blog/2026/01/15/funding-circle#access-layer" class="hash-link" aria-label="Direct link to Access Layer" title="Direct link to Access Layer" translate="no">​</a></h4>
<p>Data is stored in S3 in the Hudi format and automatically registered with the Glue Data Catalog, making it instantly queryable via Athena.</p>
<p>The team currently serves approximately 80 topics with a range of batch and streaming workloads running simultaneously—some completing in minutes, others running continuously for hours.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="challenges-and-lessons-learned">Challenges and Lessons Learned<a href="https://hudi.apache.org/blog/2026/01/15/funding-circle#challenges-and-lessons-learned" class="hash-link" aria-label="Direct link to Challenges and Lessons Learned" title="Direct link to Challenges and Lessons Learned" translate="no">​</a></h2>
<p>The team discovered several challenges in building the system:</p>
<p><strong>EMR Learning Curve:</strong> Managing EMR required a deep understanding of Hadoop, YARN, HDFS, and Spark—knowledge the team initially lacked. For a young team of engineers with no prior domain knowledge, this was a significant hurdle to overcome. Issues still crop up occasionally, but the team now has a firm grasp on maintaining the cluster in a sustainable, scalable manner.</p>
<p><strong>Debugging Complexity:</strong> Debugging proved difficult due to EMR cluster complexity. Without a deep understanding of Spark and Hadoop, investigations led to many red herrings and doubling back. Building proficiency with these underlying technologies was essential for efficient troubleshooting.</p>
<p><strong>Scope Management:</strong> Initially excited by Hudi's extensive capabilities—such as time-travel querying and advanced table management—the team promised features that, while technically impressive, weren't core business requirements. This diluted the project scope and distracted from the primary goal: building a fast, simple Kafka ingestion pipeline. Through thorough testing and leveraging the Hudi community's expertise—which Daniel described as "the best I've ever encountered"—the team learned to align their scope more precisely with actual business needs.</p>
<p>The key lesson: properly assess business cases before committing to features. The team is now far more mature and capable of building future iterations, having gained this wealth of knowledge.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="concrete-achievements-and-business-value">Concrete Achievements and Business Value<a href="https://hudi.apache.org/blog/2026/01/15/funding-circle#concrete-achievements-and-business-value" class="hash-link" aria-label="Direct link to Concrete Achievements and Business Value" title="Direct link to Concrete Achievements and Business Value" translate="no">​</a></h2>
<p><img decoding="async" loading="lazy" alt="Achievements and business impact from Kirby and Hudi" src="https://hudi.apache.org/assets/images/03-hudi-sync-funding-circle-40c0c617187d079314da96348a9e469b.png" width="2446" height="1298" class="img_ev3q"></p>
<p>Despite the challenges, the transition to Kirby powered by Hudi delivered multiple benefits:</p>
<ul>
<li class="">
<p><strong>Real-Time Data Enablement:</strong> Pipeline refresh times dropped dramatically—from 30 to 60 minutes down to just 3 to 4 minutes. This enabled minute-level refreshes for analytics teams, providing fresher data and establishing a new paradigm for self-service analytics at the company.</p>
</li>
<li class="">
<p><strong>Elimination of Backfilling Problems:</strong> Massive-scale backfills that previously took weeks now complete in hours. Instead of working around hacky solutions to backfill data into the lake, teams can now use the same tool for that task.</p>
</li>
<li class="">
<p><strong>Automatic Schema Management:</strong> Schema evolution no longer requires manual work. What used to be an expensive full-load activity is now just an afterthought. Data producers have significantly increased confidence in their ability to evolve schemas without fear of breaking downstream pipelines.</p>
</li>
<li class="">
<p><strong>Engineering Efficiency:</strong> The shift to decentralized ownership allows teams to build and maintain their own ingestion pipelines without relying on central platform engineers. Integration with DataHub enables automatic column-level lineage between Kafka topics and Athena tables. Engineering teams have drastically simplified their ingestion workflows, freeing up valuable time to focus on building quality data products.</p>
</li>
<li class="">
<p><strong>Platform Modernization:</strong> Hudi served as the catalyst for the team to learn about and apply table formats to their platform for the first time. This knowledge has led them to identify a wealth of future use cases, such as migrating legacy pipelines to modern Change Data Capture (CDC) patterns using Hudi's upsert capabilities.</p>
</li>
</ul>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="the-future-with-hudi">The Future with Hudi<a href="https://hudi.apache.org/blog/2026/01/15/funding-circle#the-future-with-hudi" class="hash-link" aria-label="Direct link to The Future with Hudi" title="Direct link to The Future with Hudi" translate="no">​</a></h2>
<p>Kirby currently serves approximately 80 topics across the UK and US, with a range of batch and streaming workloads. That volume is expected to grow to over 200 topics in the coming years. Future development focuses on stability and expansion:</p>
<ul>
<li class="">
<p><strong>Reduce EMR Reliance:</strong> The team hopes to move to lightweight compute services such as AWS Glue for less time-critical batch workloads.</p>
</li>
<li class="">
<p><strong>S3 as a Source:</strong> To achieve 100% coverage, the team plans to leverage Hudi Streamer's S3 source capabilities, extending their unified ingestion interface to handle additional topic types beyond direct Kafka ingestion.</p>
</li>
<li class="">
<p><strong>Extended Source Support:</strong> The team also wants to extend the familiar Kirby interface to support new source types, including SFTP, API-based sources, and other internal systems.</p>
</li>
</ul>
<p>Project Kirby and Apache Hudi are poised to become the foundation of Funding Circle's future ingestion platform, transforming a complex, legacy system into a scalable, high-performance, and decentralized engine for data-driven decisions.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="conclusion">Conclusion<a href="https://hudi.apache.org/blog/2026/01/15/funding-circle#conclusion" class="hash-link" aria-label="Direct link to Conclusion" title="Direct link to Conclusion" translate="no">​</a></h2>
<p>Funding Circle's presentation offered a clear and practical look at how adopting a modern data platform can solve significant real-world engineering challenges. By moving away from their legacy architecture and building Project Kirby around Apache Hudi, the team delivered a solution that is faster, more reliable, and far easier for teams across the organization to use. The core of their success was Hudi's ability to provide a unified data lake storage format, enabling crucial features like automatic schema evolution and near-real-time ingestion. Ultimately, Hudi catalyzed the maturation of the team's data platform and decentralized model, setting the stage for advanced use cases like Change Data Capture (CDC).</p>]]></content:encoded>
            <category>data lakehouse</category>
            <category>funding circle</category>
        </item>
        <item>
            <title><![CDATA[Using Amazon EMR DeltaStreamer to stream data to multiple Apache Hudi tables]]></title>
            <link>https://hudi.apache.org/blog/2026/01/15/using-amazon-emr-deltastreamer-to-stream-data-to-multiple-apache-hudi-tables</link>
            <guid>https://hudi.apache.org/blog/2026/01/15/using-amazon-emr-deltastreamer-to-stream-data-to-multiple-apache-hudi-tables</guid>
            <pubDate>Thu, 15 Jan 2026 00:00:00 GMT</pubDate>
            <description><![CDATA[Redirecting... please wait!!]]></description>
            <content:encoded><![CDATA[<span>Redirecting... please wait!! <!-- -->or click <a href="https://aws.amazon.com/blogs/big-data/using-amazon-emr-deltastreamer-to-stream-data-to-multiple-apache-hudi-tables/">here</a></span>]]></content:encoded>
            <category>aws</category>
            <category>hudi streamer</category>
        </item>
        <item>
            <title><![CDATA[ExternalSpillableMap: Handle Maps Too Big for Memory]]></title>
            <link>https://hudi.apache.org/blog/2026/01/13/apache-hudi-externalspillablemap</link>
            <guid>https://hudi.apache.org/blog/2026/01/13/apache-hudi-externalspillablemap</guid>
            <pubDate>Tue, 13 Jan 2026 00:00:00 GMT</pubDate>
            <description><![CDATA[Redirecting... please wait!!]]></description>
            <content:encoded><![CDATA[<span>Redirecting... please wait!! <!-- -->or click <a href="https://codepointer.substack.com/p/apache-hudi-externalspillablemap">here</a></span>]]></content:encoded>
            <category>performance</category>
            <category>apache spark</category>
        </item>
        <item>
            <title><![CDATA[Apache Hudi 1.1 Deep Dive: Async Instant Time Generation for Flink Writers]]></title>
            <link>https://hudi.apache.org/blog/2026/01/09/hudi-11-deep-dive-flink-async-instant-gen</link>
            <guid>https://hudi.apache.org/blog/2026/01/09/hudi-11-deep-dive-flink-async-instant-gen</guid>
            <pubDate>Fri, 09 Jan 2026 00:00:00 GMT</pubDate>
            <description><![CDATA[---]]></description>
            <content:encoded><![CDATA[<hr>
<p><em>This blog was translated from the <a href="https://mp.weixin.qq.com/s/3r06DFdkaiGkF1_7NiovZw" target="_blank" rel="noopener noreferrer" class="">original blog in Chinese</a>.</em></p>
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="background">Background<a href="https://hudi.apache.org/blog/2026/01/09/hudi-11-deep-dive-flink-async-instant-gen#background" class="hash-link" aria-label="Direct link to Background" title="Direct link to Background" translate="no">​</a></h2>
<p>Before the Hudi 1.1 release, in order to guarantee the exactly-once semantics of the Hudi Flink sink, a new instant could only be generated after the previous instant was successfully committed to Hudi. During this period, Flink writers had to block and wait. Starting from Hudi 1.1, we introduce a new asynchronous instant generation mechanism for Flink writers. This approach allows writers to request the next instant even before the previous one has been committed successfully. At the same time, it still ensures the ordering and consistency of multi-transaction commits. In the following sections, we will first briefly introduce some of Hudi's basic concepts, and then dive into the details of asynchronous instant time generation.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="instant-time">Instant Time<a href="https://hudi.apache.org/blog/2026/01/09/hudi-11-deep-dive-flink-async-instant-gen#instant-time" class="hash-link" aria-label="Direct link to Instant Time" title="Direct link to Instant Time" translate="no">​</a></h2>
<p>Timeline is a core component of Hudi's architecture. It serves as the single source of truth for the table's state, recording all operations performed on a table. Each operation is identified by a commit with a monotonically increasing instant time, which indicates the start time of each transaction.</p>
<p><img decoding="async" loading="lazy" alt="Timeline" src="https://hudi.apache.org/assets/images/timeline-a2604a4e8aa82c2a568ffa9e01585c57.png" width="3049" height="1105" class="img_ev3q"></p>
<p>Hudi provides the following capabilities based on instant time:</p>
<ul>
<li class=""><strong>More efficient write rollbacks</strong>: Each Hudi commit corresponds to an instant time. The instant timestamp can be used to quickly locate files affected by failed writes.</li>
<li class=""><strong>File name-based file slicing</strong>: Since instant time is encoded into file names, Hudi can efficiently perform file slicing across different versions of files within a table.</li>
<li class=""><strong>Incremental queries</strong>: Each row in a Hudi table carries a <code>_hoodie_commit_time</code> metadata field. This allows incremental queries at any point in the timeline, even when full compaction or cleaning services are running asynchronously.</li>
</ul>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="completion-time">Completion Time<a href="https://hudi.apache.org/blog/2026/01/09/hudi-11-deep-dive-flink-async-instant-gen#completion-time" class="hash-link" aria-label="Direct link to Completion Time" title="Direct link to Completion Time" translate="no">​</a></h2>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="file-slicing-based-on-instant-time">File Slicing Based on Instant Time<a href="https://hudi.apache.org/blog/2026/01/09/hudi-11-deep-dive-flink-async-instant-gen#file-slicing-based-on-instant-time" class="hash-link" aria-label="Direct link to File Slicing Based on Instant Time" title="Direct link to File Slicing Based on Instant Time" translate="no">​</a></h3>
<p>Before release 1.0, Hudi organized data files in units called <code>FileGroup</code>. Each file group contains multiple <code>FileSlice</code>s. Each file slice contains one base file and multiple log files. Every compaction on a file group generates a new file slice. The timestamp in the base file name corresponds to the instant time of the compaction operation that wrote the file. The timestamp in the log file name is the same as the base instant time of the current file slice. Data files with the same instant time belong to the same file slice.</p>
<p><img decoding="async" loading="lazy" alt="File Slicing based on Instant Time" src="https://hudi.apache.org/assets/images/file-slicing-instant-time-5359912c2aa068992e7ba303c533acf8.png" width="1130" height="572" class="img_ev3q"></p>
<p>In concurrent write scenarios, the instant time naming convention of log files introduces certain limitations: as asynchronous compaction progresses, the base instant time can change. To ensure that writers can correctly determine the base instant time, the ordering between write commits and compaction scheduling must be enforced—meaning that compaction can only be scheduled when there are no ongoing write operations on the table. Otherwise, a log file might be written with an incorrect base instant time, potentially leading to data loss. As a result, compaction scheduling may block all writers in concurrency mode.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="file-slicing-based-on-completion-time">File Slicing Based on Completion Time<a href="https://hudi.apache.org/blog/2026/01/09/hudi-11-deep-dive-flink-async-instant-gen#file-slicing-based-on-completion-time" class="hash-link" aria-label="Direct link to File Slicing Based on Completion Time" title="Direct link to File Slicing Based on Completion Time" translate="no">​</a></h3>
<p>To address these issues, starting from version 1.0, Hudi introduced a new file slicing model based on a time interval defined by requested time and completion time. In release 1.x, each commit has two important time concepts: requested time and completion time. All generated timestamps are globally monotonically increasing. The timestamp in the log file name is no longer the base instant time, but rather the requested instant time of the write operation. During the file slicing process, Hudi looks up the completion time for each log file using its instant time and applies a new file slicing rule:</p>
<blockquote>
<p><em>A log file belongs to the file slice with the maximum base requested time that is less than or equal to the log file's completion time.</em> [5]</p>
</blockquote>
<p><img decoding="async" loading="lazy" alt="File Slicing based on Completion Time" src="https://hudi.apache.org/assets/images/file-slicing-completion-time-73f5ca1df8df5c17fccf51cf9c47056f.png" width="1142" height="580" class="img_ev3q"></p>
<p>The new file slicing mechanism is more flexible, allowing compaction scheduling to be completely decoupled from the writer's ingestion process. Based on this mechanism, Hudi has also implemented powerful non-blocking concurrency control. For details, refer to RFC-66 [5].</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="lsm-timeline">LSM Timeline<a href="https://hudi.apache.org/blog/2026/01/09/hudi-11-deep-dive-flink-async-instant-gen#lsm-timeline" class="hash-link" aria-label="Direct link to LSM Timeline" title="Direct link to LSM Timeline" translate="no">​</a></h3>
<p>The new file slicing mechanism requires efficient querying of completion time based on instant time. Starting from version 1.x, Hudi re-implemented the archived timeline. The new archived timeline organizes its data files in an LSM-tree structure, enabling fast range-filtering queries based on instant time and supporting efficient data skipping. For more details, refer to [6].</p>
<p><img decoding="async" loading="lazy" alt="LSM Timeline" src="https://hudi.apache.org/assets/images/lsm-timeline-93c42d7fa376fa8cc8c7030f3949559b.png" width="1308" height="658" class="img_ev3q"></p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="truetime">TrueTime<a href="https://hudi.apache.org/blog/2026/01/09/hudi-11-deep-dive-flink-async-instant-gen#truetime" class="hash-link" aria-label="Direct link to TrueTime" title="Direct link to TrueTime" translate="no">​</a></h2>
<p>A critical premise of Hudi's timeline is that the timestamp generated for instants must be globally monotonically increasing without any conflicts. However, maintaining time monotonicity in distributed transactions has long been a thorny problem due to:</p>
<ul>
<li class=""><strong>Clock skew</strong>: Physical clocks drift apart across machines.</li>
<li class=""><strong>Network latency</strong>: Communication delays between data centers.</li>
<li class=""><strong>Concurrent transactions</strong>: Difficulty in determining event ordering across regions.</li>
</ul>
<p>To solve this problem, Google Spanner's TrueTime [1] uses a globally synchronized clock with bounded uncertainty, allowing it to assign timestamps monotonically without conflicts. Similarly, Hudi introduced the TrueTime API [2] starting from the 1.0 release. There are generally two approaches to realize TrueTime semantics:</p>
<ul>
<li class="">A single shared time generator process or service, like Google Spanner's time service.</li>
<li class="">Each process generates its own time and waits until time &gt;= maximum expected clock drift across all processes, coordinated within a distributed lock.</li>
</ul>
<p><img decoding="async" loading="lazy" alt="TrueTime" src="https://hudi.apache.org/assets/images/truetime-03ef10cc297714f597ec0f7b7eefa6dc.png" width="704" height="511" class="img_ev3q"></p>
<p>Hudi's TrueTime API adopts the second approach: using a distributed lock to ensure only one process generates time at any given moment. The waiting mechanism ensures sufficient time passes so that the instant time is monotonically increasing globally.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="blocking-instant-time-generation-for-flink-writers">Blocking Instant Time Generation for Flink Writers<a href="https://hudi.apache.org/blog/2026/01/09/hudi-11-deep-dive-flink-async-instant-gen#blocking-instant-time-generation-for-flink-writers" class="hash-link" aria-label="Direct link to Blocking Instant Time Generation for Flink Writers" title="Direct link to Blocking Instant Time Generation for Flink Writers" translate="no">​</a></h2>
<p>For Flink streaming ingestion, each incremental transaction write can be roughly divided into the following stages:</p>
<ol>
<li class="">Writers write records into the in-memory buffer.</li>
<li class="">When the buffer is full or a checkpoint is triggered, writers send a request to the coordinator for an instant time.</li>
<li class="">Writers create data files based on the instant time and flush the data to storage.</li>
<li class="">The coordinator commits the flushed files and metadata after receiving the ACK event for the successful checkpoint.</li>
</ol>
<p><img decoding="async" loading="lazy" alt="Blocking Instant Generation" src="https://hudi.apache.org/assets/images/blocking-instant-gen-a010e393cfb2af831dc9169150da90da.png" width="1280" height="425" class="img_ev3q"></p>
<p>From Hudi's file slicing mechanism, we can see that the instant time is required before writers flush records into storage. Before 1.1, although the committing operation was performed asynchronously in the coordinator, the writer's request for a new instant would be blocked, because only after the previous instant is successfully committed can the coordinator create a new instant. Let's say a batch of records finished flushing with checkpoint <code>ckp_1</code> in the writer at <code>T1</code>, and <code>ckp_1</code> completed at time <code>T2</code>—the writer will be blocked in the time interval <code>[T1, T2]</code> (a new instant time is generated only after the <code>ckp_1</code> completion event is handled and committed to the Hudi timeline). This blocking behavior ensures strict transaction ordering across multiple instants; however, it can lead to significant throughput fluctuations under large-scale workloads.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="async-instant-time-generation">Async Instant Time Generation<a href="https://hudi.apache.org/blog/2026/01/09/hudi-11-deep-dive-flink-async-instant-gen#async-instant-time-generation" class="hash-link" aria-label="Direct link to Async Instant Time Generation" title="Direct link to Async Instant Time Generation" translate="no">​</a></h2>
<p>To address throughput fluctuation caused by blocking instant generation, Hudi 1.1 introduces asynchronous instant generation for Flink writers. Using the aforementioned example, "async" means that before the previous instant is successfully committed to the timeline (during the time range <code>[T1, T2]</code>), the coordinator can create a new instant and return it to the writer for flushing a new batch of data. Thus, writers are no longer blocked during checkpoints, and the ingestion throughput will be more stable and smoother.</p>
<p><img decoding="async" loading="lazy" alt="Async Instant Generation" src="https://hudi.apache.org/assets/images/async-instant-gen-24927649541bf14d72074edee3f38ab9.png" width="1280" height="417" class="img_ev3q"></p>
<p><strong>Overall write workflow:</strong></p>
<ul>
<li class=""><strong>Writer</strong>: Requests instant time from the coordinator before flushing data:<!-- -->
<ul>
<li class="">Data flushing may be triggered either by a checkpoint or by the buffer being full, so the request carries the last completed checkpoint ID, rather than the current checkpoint ID.</li>
<li class="">For a request made at the initial startup of a task, if it's recovered from state, the checkpoint ID is fetched from the state; otherwise, it's set to -1.</li>
</ul>
</li>
<li class=""><strong>Coordinator</strong>: Responsible for instant generation. Each checkpoint ID corresponds to an instant. The coordinator maintains the mapping in the WriteMeta buffer: <code>CheckpointID → { Instant → { Writer TaskID → Writer MetaEvent }}</code>, where <code>{Writer TaskID → Writer MetaEvent}</code> is the mapping between each writer's parallel task ID and the file metadata written during the current checkpoint interval. Upon receiving an instant request from a writer:<!-- -->
<ul>
<li class="">If an instant corresponding to the checkpoint ID already exists in the WriteMeta buffer, the cached instant is returned directly.</li>
<li class="">Otherwise, a new instant is generated, added to the WriteMeta buffer, and then returned to the writer.</li>
</ul>
</li>
<li class=""><strong>Writer</strong>: Completes data writing, generates data files, and sends the file metadata to the coordinator.</li>
<li class=""><strong>Coordinator</strong>: Upon receiving the file metadata from a writer, updates the WriteMeta buffer:<!-- -->
<ul>
<li class="">If there is no existing metadata for the current writer task in the buffer, the metadata is written directly into the cache.</li>
<li class="">If metadata for the current writer task already exists (i.e., the writer has flushed multiple times), the metadata is merged and then the cache is updated.</li>
</ul>
</li>
<li class=""><strong>Coordinator</strong>: When the ACK event for a checkpoint (ID = n) is received, it starts serially and orderly committing the instants recorded in the WriteMeta buffer. The commit scope includes all instants corresponding to checkpoint IDs &lt; n. Once committed successfully, the instants are removed from the buffer.<!-- -->
<ul>
<li class="">Since Flink's checkpoint ACK mechanism does not guarantee that the coordinator will receive an ACK for every checkpoint, the commit logic follows Flink's Checkpoint Subsume Contract [4]: if the ACK for checkpoint <code>ckp_i</code> is not received, its written metadata is subsumed into the pending metadata for the next checkpoint <code>ckp_i+1</code>, and will be committed once the ACK for <code>ckp_i+1</code> is received.</li>
</ul>
</li>
</ul>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="writemeta-failover">WriteMeta Failover<a href="https://hudi.apache.org/blog/2026/01/09/hudi-11-deep-dive-flink-async-instant-gen#writemeta-failover" class="hash-link" aria-label="Direct link to WriteMeta Failover" title="Direct link to WriteMeta Failover" translate="no">​</a></h3>
<p>Considering that the coordinator cannot guarantee receiving every checkpoint ACK event, the in-memory WriteMeta buffer needs to be persisted to Flink state to prevent data loss after a task failover.</p>
<p>When designing the snapshot for the WriteMeta buffer, the following points need to be considered:</p>
<ul>
<li class="">WriteMeta is initially stored in the writer's buffer. Only when a checkpoint is triggered or an eager flush occurs does the writer send the current WriteMeta to the coordinator.</li>
<li class="">During checkpointing, the coordinator takes a snapshot first, followed by the writers' snapshot. At this point, the coordinator's cache does not yet include the WriteMeta for the current checkpoint interval.</li>
</ul>
<p><img decoding="async" loading="lazy" alt="WriteMeta Failover" src="https://hudi.apache.org/assets/images/writemeta-failover-bbf45f83cbbad492e7aee22a38a13d6e.png" width="1280" height="446" class="img_ev3q"></p>
<p>Based on the above considerations, the WriteMeta snapshot process for checkpoint i is as follows:</p>
<ol>
<li class="">The coordinator first persists the WriteMeta for all checkpoint IDs &lt; i to Flink state.</li>
<li class="">The writer sends the WriteMeta generated during checkpoint i to the coordinator, awaiting commit.</li>
<li class="">The writer cleans up the historical WriteMeta in state and persists the WriteMeta for checkpoint i to Flink state.</li>
</ol>
<p>This approach of persisting the WriteMeta buffer ensures that metadata is not lost, while also preventing state bloat in the WriteMeta state.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="conclusion">Conclusion<a href="https://hudi.apache.org/blog/2026/01/09/hudi-11-deep-dive-flink-async-instant-gen#conclusion" class="hash-link" aria-label="Direct link to Conclusion" title="Direct link to Conclusion" translate="no">​</a></h2>
<p>The async instant time generation mechanism introduced in Hudi 1.1 is an important feature for improving the stability of Flink streaming ingestion. By eliminating the blocking dependency on the completion of the previous instant's commit, this mechanism solves the throughput fluctuation and backpressure problems under large-scale workloads. At the same time, it still maintains strong transactional guarantees and seamless integration with Flink's checkpoint semantics. This enhancement is fully backward-compatible and transparent to end users, enabling existing Flink streaming ingestion jobs to immediately benefit from smoother and more scalable ingestion with minimal changes. As the demands on real-time data lakes continue to grow, such innovations are key to building robust, high-performance lakehouse architectures.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="references">References<a href="https://hudi.apache.org/blog/2026/01/09/hudi-11-deep-dive-flink-async-instant-gen#references" class="hash-link" aria-label="Direct link to References" title="Direct link to References" translate="no">​</a></h2>
<p>[1] <a href="https://research.google/pubs/spanner-truetime-and-the-cap-theorem/" target="_blank" rel="noopener noreferrer" class="">https://research.google/pubs/spanner-truetime-and-the-cap-theorem/</a></p>
<p>[2] <a href="https://hudi.apache.org/docs/next/timeline/#timeline-components" target="_blank" rel="noopener noreferrer" class="">https://hudi.apache.org/docs/next/timeline/#timeline-components</a></p>
<p>[3] <a href="https://github.com/apache/hudi/blob/master/rfc/rfc-66/rfc-66.md" target="_blank" rel="noopener noreferrer" class="">https://github.com/apache/hudi/blob/master/rfc/rfc-66/rfc-66.md</a></p>
<p>[4] <a href="https://github.com/apache/flink/blob/master/flink-core/src/main/java/org/apache/flink/api/common/state/CheckpointListener.java" target="_blank" rel="noopener noreferrer" class="">https://github.com/apache/flink/blob/master/flink-core/src/main/java/org/apache/flink/api/common/state/CheckpointListener.java</a></p>
<p>[5] <a href="https://github.com/apache/hudi/blob/master/rfc/rfc-66/rfc-66.md" target="_blank" rel="noopener noreferrer" class="">https://github.com/apache/hudi/blob/master/rfc/rfc-66/rfc-66.md</a></p>
<p>[6] <a href="https://hudi.apache.org/docs/next/timeline/#lsm-timeline-history" target="_blank" rel="noopener noreferrer" class="">https://hudi.apache.org/docs/next/timeline/#lsm-timeline-history</a></p>]]></content:encoded>
            <category>apache flink</category>
            <category>streaming</category>
        </item>
        <item>
            <title><![CDATA[Apache Hudi 2025: A Year In Review]]></title>
            <link>https://hudi.apache.org/blog/2025/12/29/apache-hudi-2025-a-year-in-review</link>
            <guid>https://hudi.apache.org/blog/2025/12/29/apache-hudi-2025-a-year-in-review</guid>
            <pubDate>Mon, 29 Dec 2025 00:00:00 GMT</pubDate>
            <description><![CDATA[As we close out 2025, it's clear that this has been a transformative year for Apache Hudi. The community continued to grow, delivered major advances in interoperability and performance, published "The Definitive Guide" book, and expanded our global reach through meetups, conferences, and adoption worldwide.]]></description>
            <content:encoded><![CDATA[<img src="https://hudi.apache.org/assets/images/blog/2025-12-29-apache-hudi-2025-a-year-in-review/00-og.png" alt="drawing" style="width:100%;display:block;margin-left:auto;margin-right:auto;margin-top:18pt;margin-bottom:18pt">
<p>As we close out 2025, it's clear that this has been a transformative year for Apache Hudi. The community continued to grow, delivered major advances in interoperability and performance, published "The Definitive Guide" book, and expanded our global reach through meetups, conferences, and adoption worldwide.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="community-and-growth">Community and Growth<a href="https://hudi.apache.org/blog/2025/12/29/apache-hudi-2025-a-year-in-review#community-and-growth" class="hash-link" aria-label="Direct link to Community and Growth" title="Direct link to Community and Growth" translate="no">​</a></h2>
<p>2025 brought steady momentum in contributions and community engagement.</p>
<img src="https://hudi.apache.org/assets/images/blog/2025-12-29-apache-hudi-2025-a-year-in-review/01-pr-history.png" alt="drawing" style="width:100%;display:block;margin-left:auto;margin-right:auto;margin-top:18pt;margin-bottom:18pt">
<p>GitHub pull request activities remained steady throughout 2025, reflecting consistent development momentum. GitHub contributors reached over 500. Community engagement also grew — followers across social media platforms (LinkedIn, X, YouTube, and WeChat) increased to about 35,000, while Slack community users grew to close to 5,000.</p>
<p>The project celebrated new milestones in contributor recognition. <a href="https://people.apache.org/committer-index.html#zhangyue19921010" target="_blank" rel="noopener noreferrer" class="">Yue Zhang</a> was elected to the Project Management Committee (PMC) for driving several major RFCs and features, evangelizing Hudi at meetups, and leading the effort to productionize hundreds of petabytes at JD.COM. <a href="https://people.apache.org/committer-index.html#timbrown" target="_blank" rel="noopener noreferrer" class="">Tim Brown</a> was nominated as a committer for modernizing read/write operations behind Hudi 1.1's performance gains. <a href="https://people.apache.org/committer-index.html#schang" target="_blank" rel="noopener noreferrer" class="">Shawn Chang</a> was nominated as a committer for strengthening Spark integration and expanding Hudi adoption on AWS EMR.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="development-highlights">Development Highlights<a href="https://hudi.apache.org/blog/2025/12/29/apache-hudi-2025-a-year-in-review#development-highlights" class="hash-link" aria-label="Direct link to Development Highlights" title="Direct link to Development Highlights" translate="no">​</a></h2>
<p><a href="https://hudi.apache.org/releases/release-1.1" target="_blank" rel="noopener noreferrer" class="">Hudi 1.1</a> landed with over 800 commits from 50+ contributors. The headline feature is the pluggable table format framework — Hudi's storage engine is now pluggable, allowing its battle-tested transaction management, indexing, and concurrency control to work while storing data in Hudi's native format or other table formats like <a href="https://iceberg.apache.org/" target="_blank" rel="noopener noreferrer" class="">Apache Iceberg</a> via <a href="https://xtable.apache.org/" target="_blank" rel="noopener noreferrer" class="">Apache XTable (Incubating)</a>.</p>
<img src="https://hudi.apache.org/assets/images/blog/2025-12-29-apache-hudi-2025-a-year-in-review/03-hudi11.jpg" alt="drawing" style="width:80%;display:block;margin-left:auto;margin-right:auto;margin-top:18pt;margin-bottom:18pt">
<p>Performance saw major gains across the board. Parquet binary copy for clustering delivered 10-15x faster execution with 95% compute reduction. Apache Flink writer achieved 2-3x improved throughput with Avro conversion eliminated in the write path. Apache Spark metadata-table streaming ran ~18% faster for update-heavy workloads. Indexing enhancements — partitioned record index, partition-level bucket index, HFile caching, and Bloom filters — delivered up to 4x speedup for lookups on massive tables.</p>
<p>Spark 4.0 and Flink 2.0 support was added. <a href="https://polaris.apache.org/" target="_blank" rel="noopener noreferrer" class="">Apache Polaris (Incubating)</a> catalog integration enabled multi-engine queries with unified governance. Operational simplicity improved with storage-based locking that eliminated external dependencies. New merge modes replaced legacy payload classes with declarative options, and SQL procedures enhanced table management directly in Spark SQL. See more details in the <a href="https://hudi.apache.org/blog/2025/11/25/apache-hudi-release-1-1-announcement/" target="_blank" rel="noopener noreferrer" class="">release blog</a>.</p>
<p><a href="https://github.com/apache/hudi-rs" target="_blank" rel="noopener noreferrer" class="">Hudi-rs</a> expanded its feature support — release 0.3.0 introduced Merge-on-Read and incremental queries, while 0.4.0 added C++ bindings and Avro log file support. The native Rust implementation now powers Ray Data and Daft integrations for ML and multi-cloud analytics.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="new-book-published">New Book Published<a href="https://hudi.apache.org/blog/2025/12/29/apache-hudi-2025-a-year-in-review#new-book-published" class="hash-link" aria-label="Direct link to New Book Published" title="Direct link to New Book Published" translate="no">​</a></h2>
<p><a href="https://www.onehouse.ai/whitepaper/apache-hudi-the-definitive-guide" target="_blank" rel="noopener noreferrer" class="">Apache Hudi: The Definitive Guide</a>, published by O'Reilly, distills Hudi's 8+ years of innovation from 500+ contributors into a comprehensive resource for data engineers and architects.</p>
<img src="https://hudi.apache.org/assets/images/blog/2025-12-29-apache-hudi-2025-a-year-in-review/02-book-cover.png" alt="drawing" style="width:80%;display:block;margin-left:auto;margin-right:auto;margin-top:18pt;margin-bottom:18pt">
<p>Across 10 chapters, the guide covers writing and reading patterns, indexing strategies, table maintenance, concurrency handling, streaming pipelines with Hudi Streamer, and production deployment. With practical examples spanning batch, interactive, and streaming analytics, it takes you from getting started to building end-to-end lakehouse solutions at enterprise scale.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="meetups-and-conferences">Meetups and Conferences<a href="https://hudi.apache.org/blog/2025/12/29/apache-hudi-2025-a-year-in-review#meetups-and-conferences" class="hash-link" aria-label="Direct link to Meetups and Conferences" title="Direct link to Meetups and Conferences" translate="no">​</a></h2>
<p>Community events brought together thousands of attendees across meetups and conferences worldwide.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="bangalore-hudi-community-meetup">Bangalore Hudi Community Meetup<a href="https://hudi.apache.org/blog/2025/12/29/apache-hudi-2025-a-year-in-review#bangalore-hudi-community-meetup" class="hash-link" aria-label="Direct link to Bangalore Hudi Community Meetup" title="Direct link to Bangalore Hudi Community Meetup" translate="no">​</a></h3>
<p>A full-house gathering took place at Onehouse's India office in January, featuring deep dives into Hudi 1.0 and lakehouse architecture. The event connected developers, contributors, and enthusiasts from India's growing data engineering community.</p>
<img src="https://hudi.apache.org/assets/images/blog/2025-12-29-apache-hudi-2025-a-year-in-review/04-bgl-meetup.png" alt="drawing" style="width:90%;display:block;margin-left:auto;margin-right:auto;margin-top:18pt;margin-bottom:18pt">
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="1st-hudi-asia-meetup-by-kuaishou">1st Hudi Asia Meetup by Kuaishou<a href="https://hudi.apache.org/blog/2025/12/29/apache-hudi-2025-a-year-in-review#1st-hudi-asia-meetup-by-kuaishou" class="hash-link" aria-label="Direct link to 1st Hudi Asia Meetup by Kuaishou" title="Direct link to 1st Hudi Asia Meetup by Kuaishou" translate="no">​</a></h3>
<p>Our first-ever Asia meetup in March, organized by Kuaishou, drew 231 in-person attendees and 16,673 total platform views. The event featured discussions on Hudi's roadmap, lakehouse architecture patterns, and adoption stories from leading Chinese tech companies.</p>
<img src="https://hudi.apache.org/assets/images/blog/2025-12-29-apache-hudi-2025-a-year-in-review/05-kuaishou-meetup.png" alt="drawing" style="width:90%;display:block;margin-left:auto;margin-right:auto;margin-top:18pt;margin-bottom:18pt">
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="2nd-hudi-asia-meetup-by-jdcom">2nd Hudi Asia Meetup by JD.com<a href="https://hudi.apache.org/blog/2025/12/29/apache-hudi-2025-a-year-in-review#2nd-hudi-asia-meetup-by-jdcom" class="hash-link" aria-label="Direct link to 2nd Hudi Asia Meetup by JD.com" title="Direct link to 2nd Hudi Asia Meetup by JD.com" translate="no">​</a></h3>
<p>JD.com hosted the <a href="https://hudi.apache.org/blog/2025/12/01/apache-hudi-JD-meetup-asia-2025-recap/" target="_blank" rel="noopener noreferrer" class="">second Asia meetup</a> in October, bringing together contributors and adopters for talks on real-world lakehouse implementations, streaming ingestion at scale, and roadmap discussions.</p>
<img src="https://hudi.apache.org/assets/images/blog/2025-12-29-apache-hudi-2025-a-year-in-review/06-jd-meetup.png" alt="drawing" style="width:100%;display:block;margin-left:auto;margin-right:auto;margin-top:18pt;margin-bottom:18pt">
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="cmu-database-seminar">CMU Database Seminar<a href="https://hudi.apache.org/blog/2025/12/29/apache-hudi-2025-a-year-in-review#cmu-database-seminar" class="hash-link" aria-label="Direct link to CMU Database Seminar" title="Direct link to CMU Database Seminar" translate="no">​</a></h3>
<p>Vinoth Chandar, Hudi PMC Chair, presented "<a href="https://youtu.be/AYaw06_Xazo" target="_blank" rel="noopener noreferrer" class="">Apache Hudi: A Database Layer Over Cloud Storage for Fast Mutations &amp; Queries</a>" at Carnegie Mellon University's <a href="https://db.cs.cmu.edu/events/futuredata-apache-hudi/" target="_blank" rel="noopener noreferrer" class="">Future Data Systems Seminar</a>. The talk covered how Hudi brings database-like abstractions to data lakes — enabling efficient mutations, transaction management, and fast incremental reads through storage layout, indexing, and concurrency control design decisions. A must-watch that covers both the breadth and depth of Hudi's design concepts and what problems it solves.</p>
<img src="https://hudi.apache.org/assets/images/blog/2025-12-29-apache-hudi-2025-a-year-in-review/07-cmu-talk.png" alt="drawing" style="width:100%;display:block;margin-left:auto;margin-right:auto;margin-top:18pt;margin-bottom:18pt">
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="openxdata">OpenXData<a href="https://hudi.apache.org/blog/2025/12/29/apache-hudi-2025-a-year-in-review#openxdata" class="hash-link" aria-label="Direct link to OpenXData" title="Direct link to OpenXData" translate="no">​</a></h3>
<p>Amazon engineers presented "<a href="https://www.youtube.com/watch?v=KmAeQ57AGmc" target="_blank" rel="noopener noreferrer" class="">Powering Amazon Unit Economics at Scale Using Hudi</a>" at <a href="https://www.openxdata.ai/" target="_blank" rel="noopener noreferrer" class="">OpenXData</a>, sharing how they built Nexus — a scalable, configuration-driven platform with Hudi as the cornerstone of its data lake architecture. The talk highlighted how Hudi enables Amazon to tackle the massive challenge of understanding and improving unit-level profitability across their ever-growing businesses.</p>
<img src="https://hudi.apache.org/assets/images/blog/2025-12-29-apache-hudi-2025-a-year-in-review/08-openxdata-amzn-talk.png" alt="drawing" style="width:100%;display:block;margin-left:auto;margin-right:auto;margin-top:18pt;margin-bottom:18pt">
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="veloxcon">VeloxCon<a href="https://hudi.apache.org/blog/2025/12/29/apache-hudi-2025-a-year-in-review#veloxcon" class="hash-link" aria-label="Direct link to VeloxCon" title="Direct link to VeloxCon" translate="no">​</a></h3>
<p>Shiyan Xu, Hudi PMC member, presented on <a href="https://www.youtube.com/watch?v=N5RIWNEIBNQ" target="_blank" rel="noopener noreferrer" class="">Hudi integration with Velox and Gluten</a> for accelerating query performance, and on how Hudi-rs, the native Rust implementation with multi-language bindings such as Python and C++, can enable high-performance analytics across the open lakehouse ecosystem.</p>
<img src="https://hudi.apache.org/assets/images/blog/2025-12-29-apache-hudi-2025-a-year-in-review/09-veloxcon.png" alt="drawing" style="width:100%;display:block;margin-left:auto;margin-right:auto;margin-top:18pt;margin-bottom:18pt">
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="data-streaming-summit">Data Streaming Summit<a href="https://hudi.apache.org/blog/2025/12/29/apache-hudi-2025-a-year-in-review#data-streaming-summit" class="hash-link" aria-label="Direct link to Data Streaming Summit" title="Direct link to Data Streaming Summit" translate="no">​</a></h3>
<p>Two talks showcased Hudi's streaming capabilities. Zhenqiu Huang shared <a href="https://www.youtube.com/watch?v=FDeP0JKe7RQ" target="_blank" rel="noopener noreferrer" class="">how Uber runs 5,000+ Flink-Hudi pipelines</a>, ingesting 600TB daily with P90 freshness under 15 minutes. Shiyan Xu presented on <a href="https://www.youtube.com/watch?v=GUMiY44iy74" target="_blank" rel="noopener noreferrer" class="">Hudi's high-throughput streaming capabilities</a> such as record-level indexing, async table services, and the non-blocking concurrency control mechanism introduced in Hudi 1.0.</p>
<img src="https://hudi.apache.org/assets/images/blog/2025-12-29-apache-hudi-2025-a-year-in-review/10-dsssf.png" alt="drawing" style="width:100%;display:block;margin-left:auto;margin-right:auto;margin-top:18pt;margin-bottom:18pt">
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="open-source-data-summit">Open Source Data Summit<a href="https://hudi.apache.org/blog/2025/12/29/apache-hudi-2025-a-year-in-review#open-source-data-summit" class="hash-link" aria-label="Direct link to Open Source Data Summit" title="Direct link to Open Source Data Summit" translate="no">​</a></h3>
<p>Shiyan Xu presented on <a href="https://opensourcedatasummit.com/streaming-lakehouse/" target="_blank" rel="noopener noreferrer" class="">streaming-first lakehouse designs</a>, covering Merge-on-Read table type, record-level indexing, auto-file sizing, async compaction strategies, and non-blocking concurrency control that enable streaming lakehouses with optimized mutable data handling.</p>
<img src="https://hudi.apache.org/assets/images/blog/2025-12-29-apache-hudi-2025-a-year-in-review/11-osds.png" alt="drawing" style="width:100%;display:block;margin-left:auto;margin-right:auto;margin-top:18pt;margin-bottom:18pt">
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="content-highlights">Content Highlights<a href="https://hudi.apache.org/blog/2025/12/29/apache-hudi-2025-a-year-in-review#content-highlights" class="hash-link" aria-label="Direct link to Content Highlights" title="Direct link to Content Highlights" translate="no">​</a></h2>
<p>Throughout 2025, organizations across industries showcased their production adoption and implementation journeys through community syncs and blogs. These stories highlight Hudi's versatility as a lakehouse platform.</p>
<img src="https://hudi.apache.org/assets/images/blog/2025-12-29-apache-hudi-2025-a-year-in-review/12-content-highlights.png" alt="drawing" style="width:100%;display:block;margin-left:auto;margin-right:auto;margin-top:18pt;margin-bottom:18pt">
<p>Featured adoption stories:</p>
<ul>
<li class=""><strong>Uber</strong>: <a href="https://hudi.apache.org/blog/2025/12/12/from-batch-to-streaming-accelerating-data-freshness-in-ubers-data-lake" target="_blank" rel="noopener noreferrer" class="">From Batch to Streaming: Accelerating Data Freshness in Uber's Data Lake</a></li>
<li class=""><strong>Amazon</strong>: <a href="https://hudi.apache.org/blog/2025/03/31/amazon-hudi/" target="_blank" rel="noopener noreferrer" class="">Powering Amazon Unit Economics at Scale Using Apache Hudi</a></li>
<li class=""><strong>Kuaishou</strong>: <a href="https://youtu.be/Iv-2Gj-sHOw?si=QGqbLKdE-NiZ8Jnj" target="_blank" rel="noopener noreferrer" class="">Hudi Lakehouse: The Evolution of Data Infrastructure for AI workloads</a></li>
<li class=""><strong>Southwest Airlines</strong>: <a href="https://youtu.be/X3FW4IYmYE4?si=3rqNpu83pig5FJKU" target="_blank" rel="noopener noreferrer" class="">Modernizing Data Infrastructure using Apache Hudi</a></li>
<li class=""><strong>Halodoc</strong>: <a href="https://hudi.apache.org/blog/2025/06/13/Optimizing-Apache-Hudi-Workflows-Automation-for-Clustering-Resizing-Concurrency" target="_blank" rel="noopener noreferrer" class="">Optimizing Apache Hudi Workflows: Automation for Clustering, Resizing &amp; Concurrency</a></li>
<li class=""><strong>Uptycs</strong>: <a href="https://hudi.apache.org/blog/2025/03/26/uptycs/" target="_blank" rel="noopener noreferrer" class="">From Transactional Bottlenecks to Lightning-Fast Analytics</a></li>
</ul>
<p>The community also published noteworthy content:</p>
<ul>
<li class=""><a href="https://hudi.apache.org/blog/2025/03/05/hudi-21-unique-differentiators/" target="_blank" rel="noopener noreferrer" class="">21 Unique Reasons Why Apache Hudi Should Be Your Next Data Lakehouse</a></li>
<li class=""><a href="https://hudi.apache.org/blog/2025/01/08/the-future-of-data-lakehouses-a-fireside" target="_blank" rel="noopener noreferrer" class="">The Future of Data Lakehouses: A Fireside Chat with Vinoth Chandar</a></li>
<li class="">Deep Dive into Hudi's Indexing Subsystem: <a href="https://hudi.apache.org/blog/2025/10/29/deep-dive-into-hudis-indexing-subsystem-part-1-of-2" target="_blank" rel="noopener noreferrer" class="">part 1</a> and <a href="https://hudi.apache.org/blog/2025/11/12/deep-dive-into-hudis-indexing-subsystem-part-2-of-2" target="_blank" rel="noopener noreferrer" class="">part 2</a></li>
<li class=""><a href="https://hudi.apache.org/blog/2025/12/10/apache-hudi-11-deep-dive-optimizing-streaming-ingestion-with-flink/" target="_blank" rel="noopener noreferrer" class="">Hudi 1.1 Deep Dive: Optimizing Streaming Ingestion with Flink</a></li>
<li class=""><a href="https://hudi.apache.org/blog/2025/12/16/maximizing-throughput-nbcc/" target="_blank" rel="noopener noreferrer" class="">Maximizing Throughput with Apache Hudi NBCC: Stop Retrying, Start Scaling</a></li>
<li class=""><a href="https://hudi.apache.org/blog/2025/05/29/lsm-timeline/" target="_blank" rel="noopener noreferrer" class="">Exploring Apache Hudi’s New Log-Structured Merge (LSM) Timeline</a></li>
<li class=""><a href="https://hudi.apache.org/blog/2025/04/02/secondary-index/" target="_blank" rel="noopener noreferrer" class="">Introducing Secondary Index in Apache Hudi</a></li>
<li class="">Lakehouse Chronicles YouTube episodes: <a href="https://youtu.be/IxMSgPVcugs?si=lWBQuBqtbimUo-3Y" target="_blank" rel="noopener noreferrer" class="">ep. 5</a>, <a href="https://youtu.be/t_LtUTt3DXE?si=TzWoSgXtcgVnvjkq" target="_blank" rel="noopener noreferrer" class="">ep. 6</a>, and <a href="https://youtu.be/CdnYdw-dyTI?si=DMoc0ci7Ix8jl0F4" target="_blank" rel="noopener noreferrer" class="">ep. 7</a></li>
<li class=""><a href="https://hudi.apache.org/docs/notebooks" target="_blank" rel="noopener noreferrer" class="">Interactive Jupyter Notebooks for hands-on learning</a></li>
</ul>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="looking-ahead">Looking Ahead<a href="https://hudi.apache.org/blog/2025/12/29/apache-hudi-2025-a-year-in-review#looking-ahead" class="hash-link" aria-label="Direct link to Looking Ahead" title="Direct link to Looking Ahead" translate="no">​</a></h2>
<p>The upcoming releases in 2026 will bring AI/ML-focused capabilities — including unstructured data types, column groups for embeddings, vector search, and Lance/Vortex format support. We'll also continue expanding multi-format interoperability through the pluggable table format framework and advancing streaming-first optimizations for real-time ingestion and sub-minute freshness. See the full <a href="https://hudi.apache.org/roadmap" target="_blank" rel="noopener noreferrer" class="">roadmap</a> for details.</p>
<p>Whether you're contributing code, sharing feedback, or spreading the word — we'd love to have you involved:</p>
<ul>
<li class="">Contribute on GitHub: <a href="https://github.com/apache/hudi/contribute" target="_blank" rel="noopener noreferrer" class="">Hudi</a> &amp; <a href="https://github.com/apache/hudi-rs/contribute" target="_blank" rel="noopener noreferrer" class="">Hudi-rs</a></li>
<li class="">Join our <a href="https://hudi.apache.org/slack" target="_blank" rel="noopener noreferrer" class="">Slack community</a></li>
<li class="">Follow us on <a href="https://www.linkedin.com/company/apache-hudi/" target="_blank" rel="noopener noreferrer" class="">LinkedIn</a> and <a href="https://x.com/apachehudi" target="_blank" rel="noopener noreferrer" class="">X (Twitter)</a></li>
<li class="">Subscribe to our <a href="https://www.youtube.com/@apachehudi" target="_blank" rel="noopener noreferrer" class="">YouTube channel</a></li>
<li class="">Follow WeChat account "ApacheHudi" for news and content in Chinese</li>
<li class="">Participate in the <a href="https://hudi.apache.org/contribute/developer-sync-call" target="_blank" rel="noopener noreferrer" class="">developer syncs</a>, <a href="https://hudi.apache.org/community/syncs" target="_blank" rel="noopener noreferrer" class="">community syncs</a>, and <a href="https://hudi.apache.org/community/office_hours" target="_blank" rel="noopener noreferrer" class="">office hours</a></li>
<li class="">Subscribe to the dev mailing list (by sending an empty email): <a href="mailto:dev-subscribe@hudi.apache.org" target="_blank" rel="noopener noreferrer" class="">dev-subscribe@hudi.apache.org</a></li>
</ul>
<p>A huge thank you to everyone who contributed to Hudi's growth this year. With such a strong foundation, 2026 promises to be our most exciting year yet.</p>]]></content:encoded>
            <category>release</category>
        </item>
        <item>
            <title><![CDATA[How Zupee Cut S3 Costs by 60% with Apache Hudi]]></title>
            <link>https://hudi.apache.org/blog/2025/12/22/how-zupee-cut-s3-costs-60-percent-with-hudi</link>
            <guid>https://hudi.apache.org/blog/2025/12/22/how-zupee-cut-s3-costs-60-percent-with-hudi</guid>
            <pubDate>Mon, 22 Dec 2025 00:00:00 GMT</pubDate>
            <description><![CDATA[---]]></description>
            <content:encoded><![CDATA[<hr>
<p><em>This post summarizes Zupee's talk from the Apache Hudi community sync. Watch the recording on <a href="https://www.youtube.com/watch?v=Mpj8hnaZbe0" target="_blank" rel="noopener noreferrer" class="">YouTube</a>.</em></p>
<hr>
<p><img decoding="async" loading="lazy" alt="Talk title slide" src="https://hudi.apache.org/assets/images/og-3d6b097ba470e207ce0f3b00e4848a7c.png" width="1999" height="1023" class="img_ev3q"></p>
<p><a href="https://www.zupee.com/" target="_blank" rel="noopener noreferrer" class="">Zupee</a> is India's largest skill-based Ludo platform (<a href="https://en.wikipedia.org/wiki/Ludo" target="_blank" rel="noopener noreferrer" class="">Ludo</a> is a classic Indian board game), founded in 2018 with a vision of bringing moments of joy to users through meaningful entertainment. The company was the first to introduce a skill element to culturally relevant games like Ludo, reviving the joy of traditional Indian gaming.</p>
<p>At Zupee, data plays a crucial role in everything they do—from understanding user behavior to optimizing services. It sits at the core of their decision-making process. In this community sync, Amarjeet Singh, Senior Data Engineer at Zupee, shared how his team built a scalable data platform using Apache Hudi and the significant performance gains they achieved.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="data-platform-architecture">Data Platform Architecture<a href="https://hudi.apache.org/blog/2025/12/22/how-zupee-cut-s3-costs-60-percent-with-hudi#data-platform-architecture" class="hash-link" aria-label="Direct link to Data Platform Architecture" title="Direct link to Data Platform Architecture" translate="no">​</a></h2>
<p><img decoding="async" loading="lazy" alt="Data platform architecture" src="https://hudi.apache.org/assets/images/image1-3336d5fa43e0b8ed63ea668d2148fce0.png" width="1999" height="1085" class="img_ev3q"></p>
<p>Zupee's data platform architecture is designed to handle complex data needs efficiently. It consists of several layers working together:</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="three-tiered-data-lake">Three-Tiered Data Lake<a href="https://hudi.apache.org/blog/2025/12/22/how-zupee-cut-s3-costs-60-percent-with-hudi#three-tiered-data-lake" class="hash-link" aria-label="Direct link to Three-Tiered Data Lake" title="Direct link to Three-Tiered Data Lake" translate="no">​</a></h3>
<p>The data lake is structured into three zones:</p>
<ul>
<li class=""><strong>Landing Zone</strong>: Where raw data first arrives—like a receiving dock where all incoming data is stored in its original format.</li>
<li class=""><strong>Exploration Zone</strong>: Data is cleaned and prepared for analysts and data scientists to explore and derive insights.</li>
<li class=""><strong>Analytical Zone</strong>: Processed data optimized for analytical queries. This layer stores OLAP tables, facts and dimensions, or denormalized wide tables.</li>
</ul>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="metastore-and-api-layer">Metastore and API Layer<a href="https://hudi.apache.org/blog/2025/12/22/how-zupee-cut-s3-costs-60-percent-with-hudi#metastore-and-api-layer" class="hash-link" aria-label="Direct link to Metastore and API Layer" title="Direct link to Metastore and API Layer" translate="no">​</a></h3>
<p>This layer acts as the brain of the data platform, managing metadata and providing APIs for data access and integration.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="orchestration-and-framework-layer">Orchestration and Framework Layer<a href="https://hudi.apache.org/blog/2025/12/22/how-zupee-cut-s3-costs-60-percent-with-hudi#orchestration-and-framework-layer" class="hash-link" aria-label="Direct link to Orchestration and Framework Layer" title="Direct link to Orchestration and Framework Layer" translate="no">​</a></h3>
<p>The orchestration layer includes tools like Apache Airflow for scheduling and managing workflows, along with in-house tools and frameworks for ML operations, data ingestion, and data computation—including <a href="https://hudi.apache.org/docs/hoodie_deltastreamer/" target="_blank" rel="noopener noreferrer" class="">Hudi Streamer</a> for data ingestion.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="compute-layer">Compute Layer<a href="https://hudi.apache.org/blog/2025/12/22/how-zupee-cut-s3-costs-60-percent-with-hudi#compute-layer" class="hash-link" aria-label="Direct link to Compute Layer" title="Direct link to Compute Layer" translate="no">​</a></h3>
<p>The compute layer includes Apache Spark for large-scale data processing, and Amazon Athena and Trino for querying.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="real-time-serving-layer">Real-Time Serving Layer<a href="https://hudi.apache.org/blog/2025/12/22/how-zupee-cut-s3-costs-60-percent-with-hudi#real-time-serving-layer" class="hash-link" aria-label="Direct link to Real-Time Serving Layer" title="Direct link to Real-Time Serving Layer" translate="no">​</a></h3>
<p>For real-time data needs, Zupee uses Apache Flink as a powerful streaming framework to power their feature store and enable real-time model predictions.</p>
<p>The serving layer includes real-time dashboards powered by Flink, analytical dashboards powered by Athena and Trino, and Jupyter notebooks for ad-hoc analysis by data scientists and machine learning teams.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="workflow-based-data-ingestion">Workflow-Based Data Ingestion<a href="https://hudi.apache.org/blog/2025/12/22/how-zupee-cut-s3-costs-60-percent-with-hudi#workflow-based-data-ingestion" class="hash-link" aria-label="Direct link to Workflow-Based Data Ingestion" title="Direct link to Workflow-Based Data Ingestion" translate="no">​</a></h2>
<p><img decoding="async" loading="lazy" alt="Ingestion workflow" src="https://hudi.apache.org/assets/images/image2-ec9c06a8024b506d7c10190c07c037fb.png" width="1999" height="864" class="img_ev3q"></p>
<p>Zupee designed a data ingestion pipeline that provides smooth integration and scalability. At the heart of their approach is a centralized configuration system that gives them granular control over every aspect of their jobs.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="centralized-configuration">Centralized Configuration<a href="https://hudi.apache.org/blog/2025/12/22/how-zupee-cut-s3-costs-60-percent-with-hudi#centralized-configuration" class="hash-link" aria-label="Direct link to Centralized Configuration" title="Direct link to Centralized Configuration" translate="no">​</a></h3>
<p>The team uses YAML files to centrally manage job details and tenant-level settings. This promotes consistency and makes updates easy. The centralized system is hosted on Amazon EMR, ensuring uniformity across all ingestion jobs.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="multi-tenant-pipeline">Multi-Tenant Pipeline<a href="https://hudi.apache.org/blog/2025/12/22/how-zupee-cut-s3-costs-60-percent-with-hudi#multi-tenant-pipeline" class="hash-link" aria-label="Direct link to Multi-Tenant Pipeline" title="Direct link to Multi-Tenant Pipeline" translate="no">​</a></h3>
<p>Zupee runs a multi-tenant setup of generic pipelines. They can switch between different versions of Spark, Hudi Streamer, and Scala—all controlled at the tenant level.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="automated-spark-command-generation">Automated Spark Command Generation<a href="https://hudi.apache.org/blog/2025/12/22/how-zupee-cut-s3-costs-60-percent-with-hudi#automated-spark-command-generation" class="hash-link" aria-label="Direct link to Automated Spark Command Generation" title="Direct link to Automated Spark Command Generation" translate="no">​</a></h3>
<p>The system automatically generates Spark commands based on YAML configurations. This significantly reduces manual intervention, minimizes errors, and accelerates the development process.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="the-ingestion-flow">The Ingestion Flow<a href="https://hudi.apache.org/blog/2025/12/22/how-zupee-cut-s3-costs-60-percent-with-hudi#the-ingestion-flow" class="hash-link" aria-label="Direct link to The Ingestion Flow" title="Direct link to The Ingestion Flow" translate="no">​</a></h3>
<p>Here's how the workflow operates:</p>
<ol>
<li class="">A generic job reads a YAML file containing specific job information and tenant-level configurations.</li>
<li class="">A single trigger starts each pipeline.</li>
<li class="">The generic job creates Spark configurations and generates a <code>spark-submit</code> command with Hudi Streamer settings.</li>
<li class="">The command is submitted using Livy or EMR steps.</li>
<li class="">Throughout the process, the team tracks data lineage for monitoring and debugging.</li>
</ol>
<p>This workflow-based approach streamlines data ingestion, making it both scalable and reliable for handling large volumes of data.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="real-time-ingestion-with-hudi-streamer">Real-Time Ingestion with Hudi Streamer<a href="https://hudi.apache.org/blog/2025/12/22/how-zupee-cut-s3-costs-60-percent-with-hudi#real-time-ingestion-with-hudi-streamer" class="hash-link" aria-label="Direct link to Real-Time Ingestion with Hudi Streamer" title="Direct link to Real-Time Ingestion with Hudi Streamer" translate="no">​</a></h2>
<p>Zupee uses <a href="https://hudi.apache.org/docs/hoodie_deltastreamer/" target="_blank" rel="noopener noreferrer" class="">Hudi Streamer</a>, a utility built on checkpoint-based ingestion. It supports various sources including distributed file systems (S3, GCS), Kafka, and JDBC sources like MySQL and MongoDB.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="key-benefits">Key Benefits<a href="https://hudi.apache.org/blog/2025/12/22/how-zupee-cut-s3-costs-60-percent-with-hudi#key-benefits" class="hash-link" aria-label="Direct link to Key Benefits" title="Direct link to Key Benefits" translate="no">​</a></h3>
<ul>
<li class=""><strong>Checkpoint-based consistency</strong>: Ensures the ability to resume from the last checkpoint in case of interruption.</li>
<li class=""><strong>Easy backfills</strong>: Reprocess historical data efficiently based on checkpoints, without re-ingesting the entire dataset—saving both time and resources.</li>
<li class=""><strong>Data catalog syncing</strong>: Automatically syncs metadata to catalogs such as AWS Glue and Hive Metastore.</li>
<li class=""><strong>Built-in transformations</strong>: Supports SQL transformations and flat transformations, allowing complex logic to be applied on the fly.</li>
<li class=""><strong>Custom transformations</strong>: Developers can build custom transformation classes for specific requirements.</li>
</ul>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="deep-dive-how-hudi-streamer-works">Deep Dive: How Hudi Streamer Works<a href="https://hudi.apache.org/blog/2025/12/22/how-zupee-cut-s3-costs-60-percent-with-hudi#deep-dive-how-hudi-streamer-works" class="hash-link" aria-label="Direct link to Deep Dive: How Hudi Streamer Works" title="Direct link to Deep Dive: How Hudi Streamer Works" translate="no">​</a></h3>
<p><img decoding="async" loading="lazy" alt="Hudi Streamer internals" src="https://hudi.apache.org/assets/images/image3-c3ba49bf9e7606a21ab2c93ad60cb758.png" width="1564" height="1810" class="img_ev3q"></p>
<p>Here's how Hudi Streamer processes data internally:</p>
<ol>
<li class="">
<p><strong>Job Submission</strong>: A Spark job is submitted with Hudi properties specifying primary keys, checkpointing details, and other configurations.</p>
</li>
<li class="">
<p><strong>StreamSync Wrapper</strong>: The job creates a StreamSync wrapper (formerly DeltaSync) based on the provided properties.</p>
</li>
<li class="">
<p><strong>Continuous or Single Run</strong>: StreamSync initiates a synchronized process that can run continuously or as a single batch, depending on the parameters.</p>
</li>
<li class="">
<p><strong>Checkpoint Retrieval</strong>: Hudi Streamer checks if a checkpoint exists and retrieves the last position from the Hudi commit metadata. For S3-based ingestion, it checks the last modified date; for Kafka sources, it retrieves the last committed offset.</p>
</li>
<li class="">
<p><strong>Transformation</strong>: If transformations are configured (single or chained), they are applied to the source data based on the format (JSON, Avro, etc.).</p>
</li>
<li class="">
<p><strong>Write and Compaction</strong>: Data is written to the Hudi table. For Merge-On-Read tables, compaction runs inline or asynchronously to merge log files with base files.</p>
</li>
<li class="">
<p><strong>Metadata Sync</strong>: Finally, if enabled, metadata is synced to ensure catalog consistency.</p>
</li>
</ol>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="custom-solutions">Custom Solutions<a href="https://hudi.apache.org/blog/2025/12/22/how-zupee-cut-s3-costs-60-percent-with-hudi#custom-solutions" class="hash-link" aria-label="Direct link to Custom Solutions" title="Direct link to Custom Solutions" translate="no">​</a></h3>
<p>The Zupee team developed several custom solutions to enhance their Hudi Streamer pipeline:</p>
<ul>
<li class=""><strong>Dynamic Schema Generator</strong>: A custom class that dynamically generates schemas for JSON sources, enabling automatic schema creation based on incoming data.</li>
<li class=""><strong>Post-Process Transformations</strong>: Custom transformations to handle schema evolution at the source level.</li>
<li class=""><strong>Centralized Configurations</strong>: Hudi configurations managed centrally via YAML files or <code>hoodie-config.xml</code>, simplifying maintenance and updates.</li>
<li class=""><strong>Raw Data Handling</strong>: For raw data ingestion, they discovered that Hudi Streamer can infer schemas automatically without requiring a schema provider.</li>
</ul>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="results-cost-savings-and-performance-gains">Results: Cost Savings and Performance Gains<a href="https://hudi.apache.org/blog/2025/12/22/how-zupee-cut-s3-costs-60-percent-with-hudi#results-cost-savings-and-performance-gains" class="hash-link" aria-label="Direct link to Results: Cost Savings and Performance Gains" title="Direct link to Results: Cost Savings and Performance Gains" translate="no">​</a></h2>
<p>The migration to a Hudi-powered platform delivered significant outcomes:</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="60-reduction-in-s3-network-costs">60% Reduction in S3 Network Costs<a href="https://hudi.apache.org/blog/2025/12/22/how-zupee-cut-s3-costs-60-percent-with-hudi#60-reduction-in-s3-network-costs" class="hash-link" aria-label="Direct link to 60% Reduction in S3 Network Costs" title="Direct link to 60% Reduction in S3 Network Costs" translate="no">​</a></h3>
<p>After migrating from Hudi 0.10.x to 0.12.3 and enabling the <a href="https://hudi.apache.org/docs/metadata/" target="_blank" rel="noopener noreferrer" class="">Metadata Table</a>, Zupee reduced S3 network costs by over 60%. The metadata table eliminates expensive S3 file listing operations by maintaining an internal index of all files. The key settings are <code>hoodie.metadata.enable=true</code> for Spark and <code>hudi.metadata.listing-enabled=true</code> for Athena.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="15-minute-ingestion-sla">15-Minute Ingestion SLA<a href="https://hudi.apache.org/blog/2025/12/22/how-zupee-cut-s3-costs-60-percent-with-hudi#15-minute-ingestion-sla" class="hash-link" aria-label="Direct link to 15-Minute Ingestion SLA" title="Direct link to 15-Minute Ingestion SLA" translate="no">​</a></h3>
<p>The team achieved a 15-minute SLA for ingesting 2-5 million records using <a href="https://hudi.apache.org/docs/table_types/" target="_blank" rel="noopener noreferrer" class="">Merge-On-Read (MOR)</a> tables with Hudi's indexing for efficient record lookups during upserts.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="30-storage-reduction">30% Storage Reduction<a href="https://hudi.apache.org/blog/2025/12/22/how-zupee-cut-s3-costs-60-percent-with-hudi#30-storage-reduction" class="hash-link" aria-label="Direct link to 30% Storage Reduction" title="Direct link to 30% Storage Reduction" translate="no">​</a></h3>
<p>Switching from Snappy to ZSTD compression resulted in a 30% decrease in data size. While write times increased slightly, query performance improved significantly, and both storage and Athena costs decreased.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="small-file-management">Small File Management<a href="https://hudi.apache.org/blog/2025/12/22/how-zupee-cut-s3-costs-60-percent-with-hudi#small-file-management" class="hash-link" aria-label="Direct link to Small File Management" title="Direct link to Small File Management" translate="no">​</a></h3>
<p><a href="https://hudi.apache.org/docs/compaction/" target="_blank" rel="noopener noreferrer" class="">Async compaction</a> handles the small file problem by consolidating smaller files into larger ones, configured via <code>hoodie.parquet.small.file.limit</code> and <code>hoodie.parquet.max.file.size</code>. The team also explored Parquet page-level indexing to reduce query costs by allowing engines to read only relevant pages from files.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="why-hudi-over-other-table-formats">Why Hudi Over Other Table Formats?<a href="https://hudi.apache.org/blog/2025/12/22/how-zupee-cut-s3-costs-60-percent-with-hudi#why-hudi-over-other-table-formats" class="hash-link" aria-label="Direct link to Why Hudi Over Other Table Formats?" title="Direct link to Why Hudi Over Other Table Formats?" translate="no">​</a></h2>
<p>During the Q&amp;A, Amarjeet explained why Zupee chose Hudi over other table formats. The team ran POCs with Delta Lake but found that for near-real-time ingestion, Hudi performs much better. Since Zupee primarily works with real-time data rather than batch workloads, this was the deciding factor.</p>
<p>The native <a href="https://hudi.apache.org/docs/hoodie_streaming_ingestion#hudi-streamer" target="_blank" rel="noopener noreferrer" class="">Hudi Streamer</a> utility also adds significant value with its built-in checkpoint management, compaction, and catalog syncing—features that would require additional work with other ingestion approaches.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="best-practices-for-emr-upgrades">Best Practices for EMR Upgrades<a href="https://hudi.apache.org/blog/2025/12/22/how-zupee-cut-s3-costs-60-percent-with-hudi#best-practices-for-emr-upgrades" class="hash-link" aria-label="Direct link to Best Practices for EMR Upgrades" title="Direct link to Best Practices for EMR Upgrades" translate="no">​</a></h2>
<p>When asked about upgrading Hudi on EMR, Amarjeet shared their approach: instead of using the EMR-provided JARs, they use open-source JARs. This way, they can upgrade JARs using their multi-tenant framework, which controls which JAR goes to which job. They can also use different Spark versions for different jobs. For example, if they are currently running Hudi 0.12.3 and want to test version 0.14.1, they simply specify the JAR in their YAML file for that particular job.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="conclusion">Conclusion<a href="https://hudi.apache.org/blog/2025/12/22/how-zupee-cut-s3-costs-60-percent-with-hudi#conclusion" class="hash-link" aria-label="Direct link to Conclusion" title="Direct link to Conclusion" translate="no">​</a></h2>
<p>Zupee's journey with Apache Hudi demonstrates how a modern data platform can solve real-world engineering challenges at scale. By moving from legacy architecture to a Hudi-powered lakehouse, they built a platform that is robust, scalable, and cost-effective.</p>
<p>The keys to their success were:</p>
<ul>
<li class="">Enabling Hudi's <a href="https://hudi.apache.org/docs/metadata/" target="_blank" rel="noopener noreferrer" class="">Metadata Table</a> to eliminate file listing overhead</li>
<li class="">Using <a href="https://hudi.apache.org/docs/table_types/" target="_blank" rel="noopener noreferrer" class="">Merge-On-Read</a> tables with indexing for efficient upserts</li>
<li class="">Building custom transformations for flexible schema evolution</li>
<li class="">Centralizing configuration management for operational simplicity</li>
</ul>
<p>Watch the <a href="https://www.youtube.com/watch?v=Mpj8hnaZbe0" target="_blank" rel="noopener noreferrer" class="">full presentation on YouTube</a>. Ready to optimize your own data platform? Get started with <a href="https://hudi.apache.org/docs/quick-start-guide" target="_blank" rel="noopener noreferrer" class="">Hudi</a> and explore <a href="https://hudi.apache.org/docs/hoodie_streaming_ingestion#hudi-streamer" target="_blank" rel="noopener noreferrer" class="">Hudi Streamer</a> for your ingestion needs.</p>]]></content:encoded>
            <category>data lakehouse</category>
            <category>zupee</category>
        </item>
        <item>
            <title><![CDATA[Maximizing Throughput with Apache Hudi NBCC: Stop Retrying, Start Scaling]]></title>
            <link>https://hudi.apache.org/blog/2025/12/16/maximizing-throughput-nbcc</link>
            <guid>https://hudi.apache.org/blog/2025/12/16/maximizing-throughput-nbcc</guid>
            <pubDate>Tue, 16 Dec 2025 00:00:00 GMT</pubDate>
            <description><![CDATA[Data lakehouses often run multiple concurrent writers—streaming ingestion, batch ETL, maintenance jobs. The default approach, Optimistic Concurrency Control (OCC), assumes conflicts are rare and handles them through retries. That assumption breaks down in increasingly common scenarios, such as running maintenance batch jobs on tables receiving streaming writes. When conflicts become the norm, retries pile up with OCC, and the write throughput tanks.]]></description>
            <content:encoded><![CDATA[<p>Data lakehouses often run multiple concurrent writers—streaming ingestion, batch ETL, maintenance jobs. The default approach, Optimistic Concurrency Control (OCC), assumes conflicts are rare and handles them through retries. That assumption breaks down in increasingly common scenarios, such as running maintenance batch jobs on tables receiving streaming writes. When conflicts become the norm, retries pile up with OCC, and the write throughput tanks.</p>
<p>Hudi introduced <a href="https://hudi.apache.org/docs/concurrency_control#non-blocking-concurrency-control" target="_blank" rel="noopener noreferrer" class="">Non-Blocking Concurrency Control (NBCC)</a> in release 1.0, solving this problem by allowing writers to append data files in parallel and using the write completion time to determine the serialization order for reads or merges. We'll explore why OCC struggles under real-world concurrency, how NBCC works under the hood, and how to configure NBCC in your pipelines.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="the-problem-with-retries">The Problem with Retries<a href="https://hudi.apache.org/blog/2025/12/16/maximizing-throughput-nbcc#the-problem-with-retries" class="hash-link" aria-label="Direct link to The Problem with Retries" title="Direct link to The Problem with Retries" translate="no">​</a></h2>
<p>Picture this scenario: your streaming pipeline ingests clickstream data every minute from multiple Kafka topics. A nightly GDPR deletion job kicks off at midnight, scanning across thousands of partitions to purge user records—also touching data files the ingestion pipeline is actively writing to. By 3 AM, you get paged—the deletion job has failed repeatedly, burning compute resources while the ingestion writer keeps winning the race to commit.</p>
<p><img decoding="async" loading="lazy" alt="p1-occ-retries" src="https://hudi.apache.org/assets/images/p1-occ-retries-1c5ef5258672d5208443520f4d342dd4.png" width="1315" height="533" class="img_ev3q"></p>
<p>OCC assumes conflicts are rare—an assumption that held in traditional batch-oriented data lakes where jobs were scheduled sequentially. Most transactions will not overlap, so let them proceed optimistically and check for conflicts at commit time. But high-frequency streaming breaks this assumption: when you have minute-level ingestion plus long-running maintenance jobs, overlapping writes are not the exception—they are the norm.</p>
<p>This is a classic concurrency anti-pattern: under OCC, conflict probability grows with transaction duration. Long-running jobs competing against frequent short writes lose nearly every commit race and retry indefinitely. When both concurrent writers are running ingestion, without careful coordination between the writers (e.g., segregating writers by partitions), the consequences become more severe: conflicts occur more often, overall throughput is reduced, and compute costs increase. The key insight is that retries are the throughput killer—we need a fundamentally different approach.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="hudi-nbcc-write-in-parallel-serialize-by-completion-time">Hudi NBCC: Write in Parallel, Serialize by Completion Time<a href="https://hudi.apache.org/blog/2025/12/16/maximizing-throughput-nbcc#hudi-nbcc-write-in-parallel-serialize-by-completion-time" class="hash-link" aria-label="Direct link to Hudi NBCC: Write in Parallel, Serialize by Completion Time" title="Direct link to Hudi NBCC: Write in Parallel, Serialize by Completion Time" translate="no">​</a></h2>
<p>NBCC avoids conflicts by design: let every writer append updates to Hudi’s log files in the Merge-on-Read (MOR) table, then let readers or mergers follow the serialization order based on write completion time. Let's say there are two writers, both updating a record concurrently. Under NBCC, each writer produces its own log file containing the update. Since there's no file contention, there's nothing to conflict on. At read time or during compaction, Hudi follows the write completion time and processes the associated log files in the proper order.</p>
<p><img decoding="async" loading="lazy" alt="p2-nbcc-overview" src="https://hudi.apache.org/assets/images/p2-nbcc-overview-ae0d025dc5bd300ca4c6fbb56633b1af.png" width="831" height="567" class="img_ev3q"></p>
<p>Both OCC and NBCC require locking—OCC during commit validation, NBCC during timestamp generation. The key difference is how long the lock is held, and what happens after. OCC holds the lock while validating: for concurrent commits, it compares the sets of written files to detect conflicts—so validation time grows with both transaction size. If validation detects a conflict, the losing writers discard their completed work and retry. NBCC's lock duration is a negligible constant (a configurable clock skew duration, 200ms by default) regardless of transaction size: acquire lock, generate timestamp, sleep for clock skew, release. No file-level validation, no conflict detection, no retries.</p>
<table><thead><tr><th style="text-align:left"></th><th style="text-align:left">OCC</th><th style="text-align:left">NBCC</th></tr></thead><tbody><tr><td style="text-align:left">On conflict</td><td style="text-align:left">Abort and retry</td><td style="text-align:left">No conflicts—each writer appends separately</td></tr><tr><td style="text-align:left">Lock duration</td><td style="text-align:left">Scales with the number of written files to validate</td><td style="text-align:left">Constant (brief clock skew duration)</td></tr><tr><td style="text-align:left">Resource waste</td><td style="text-align:left">High</td><td style="text-align:left">Nearly none</td></tr></tbody></table>
<p>Hudi supports both OCC and NBCC for multi-writer scenarios. Hudi also offers <a href="https://hudi.apache.org/docs/concurrency_control#early-conflict-detection" target="_blank" rel="noopener noreferrer" class="">early conflict detection</a> for OCC, which can reduce wasted work by failing faster. However, OCC's validation lock duration still exceeds NBCC's timestamp generation time, and retries still occur after conflicts are detected—both impacting overall write throughput.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="how-nbcc-works-under-the-hood">How NBCC Works Under the Hood<a href="https://hudi.apache.org/blog/2025/12/16/maximizing-throughput-nbcc#how-nbcc-works-under-the-hood" class="hash-link" aria-label="Direct link to How NBCC Works Under the Hood" title="Direct link to How NBCC Works Under the Hood" translate="no">​</a></h2>
<p>Hudi NBCC relies on several design foundations to enable conflict-free concurrent writes and maximize throughput.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="record-keys-and-file-groups">Record Keys and File Groups<a href="https://hudi.apache.org/blog/2025/12/16/maximizing-throughput-nbcc#record-keys-and-file-groups" class="hash-link" aria-label="Direct link to Record Keys and File Groups" title="Direct link to Record Keys and File Groups" translate="no">​</a></h3>
<p>Hudi organizes data into file groups, where records with the same record key always route to the same file group. Hudi uses <a href="https://hudi.apache.org/docs/indexes" target="_blank" rel="noopener noreferrer" class="">indexes</a> to efficiently route records to file groups. For MOR tables, updates don't rewrite base files—instead, writers append updates to log files within the file group.</p>
<p><img decoding="async" loading="lazy" alt="p3-recordkey-filegroup" src="https://hudi.apache.org/assets/images/p3-recordkey-filegroup-52e09b55fdf2cf567939606461f57566.png" width="699" height="512" class="img_ev3q"></p>
<p>This record colocation is a key foundation for making NBCC possible. Records and their updates will always be routed to the same file group based on record keys, either in base files or log files—all associated with the same file ID that identifies the file group. The record key to file group mapping and the file ID association support read and merge operations by efficiently locating files to process. Also, concurrent Hudi writers use timestamps and write tokens to generate non-conflicting file names within each file group, and thus data writing can be non-blocking.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="completion-time-serializing-concurrent-writes">Completion Time: Serializing Concurrent Writes<a href="https://hudi.apache.org/blog/2025/12/16/maximizing-throughput-nbcc#completion-time-serializing-concurrent-writes" class="hash-link" aria-label="Direct link to Completion Time: Serializing Concurrent Writes" title="Direct link to Completion Time: Serializing Concurrent Writes" translate="no">​</a></h3>
<p>With NBCC, concurrent writers produce log files whose write transactions overlap in time. To process these files correctly, we need a proper serialization order. Consider: Writer A starts deltacommit 1 at T1 and completes at T5; Writer B starts deltacommit 2 at T2 and completes at T4. If we order by start timestamp, deltacommit 1 would be processed first—but it actually finished later than deltacommit 2. Completion time reflects the correct order for processing the files.</p>
<p><img decoding="async" loading="lazy" alt="p4-completion-time" src="https://hudi.apache.org/assets/images/p4-completion-time-2618f68e36e07386f433703893c7265b.png" width="688" height="442" class="img_ev3q"></p>
<p>Tracking the completion time is critical for NBCC. Concurrent writers flush records to files in parallel without any guarantee of completion order based on the start time. Hudi timeline tracks when each write is actually completed, enabling the correct serialization order for the writes.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="truetime-like-timestamp-generation">TrueTime-like Timestamp Generation<a href="https://hudi.apache.org/blog/2025/12/16/maximizing-throughput-nbcc#truetime-like-timestamp-generation" class="hash-link" aria-label="Direct link to TrueTime-like Timestamp Generation" title="Direct link to TrueTime-like Timestamp Generation" translate="no">​</a></h3>
<p>Distributed writers running on different machines face clock skew—their local clocks may differ by tens or even hundreds of milliseconds. Without coordination, two writers could generate the same timestamp or produce incorrect ordering.</p>
<p>Hudi solves this with a TrueTime-like mechanism inspired by <a href="https://static.googleusercontent.com/media/research.google.com/en//archive/spanner-osdi2012.pdf" target="_blank" rel="noopener noreferrer" class="">Google Spanner</a>:</p>
<p><img decoding="async" loading="lazy" alt="p5-truetime" src="https://hudi.apache.org/assets/images/p5-truetime-bf5e84f15b488a6a63dcb4abd5d6fb58.gif" width="400" height="319" class="img_ev3q"></p>
<p>The process works as follows:</p>
<ol>
<li class="">Acquire a distributed lock</li>
<li class="">Generate timestamp using local clock</li>
<li class="">Sleep for X milliseconds</li>
<li class="">Release lock</li>
</ol>
<p>The sleep accounts for the worst-case clock skew between writers. By waiting longer than the maximum expected skew before releasing the lock, Hudi guarantees that any subsequent writer will generate a strictly greater timestamp.</p>
<p>These monotonically increasing timestamps ensure that concurrent writers never produce conflicting or out-of-order commits—achieving transaction serializability and completing the foundation for NBCC.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="supporting-designs">Supporting Designs<a href="https://hudi.apache.org/blog/2025/12/16/maximizing-throughput-nbcc#supporting-designs" class="hash-link" aria-label="Direct link to Supporting Designs" title="Direct link to Supporting Designs" translate="no">​</a></h3>
<p>In scenarios suited for NBCC, we may encounter long commit histories and need to properly merge records. Hudi 1.0 introduced two supporting designs that complement NBCC.</p>
<p><strong>LSM Timeline</strong>: High-frequency streaming can produce millions of commits over time. Listing and parsing individual timeline files would be prohibitively slow, and the storage overhead can become a headache. <a href="https://hudi.apache.org/docs/timeline" target="_blank" rel="noopener noreferrer" class="">Hudi timeline</a> uses an LSM Tree structure—archiving older commits into sorted and compacted Parquet files for efficient lookups and reduced storage footprint.</p>
<p><strong>Merge Modes</strong>: When log files from concurrent writers need to be merged during reads or compaction, we need proper record-merging logic. Hudi supports flexible <a href="https://hudi.apache.org/docs/record_merger#merge-modes" target="_blank" rel="noopener noreferrer" class="">merge modes</a> that control how records with the same key are resolved—whether to keep the latest by commit time, respect user-defined ordering fields, or apply custom merge functions.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="using-nbcc">Using NBCC<a href="https://hudi.apache.org/blog/2025/12/16/maximizing-throughput-nbcc#using-nbcc" class="hash-link" aria-label="Direct link to Using NBCC" title="Direct link to Using NBCC" translate="no">​</a></h2>
<p>With the design foundations covered, let's see how NBCC works in practice.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="nbcc-in-action">NBCC in Action<a href="https://hudi.apache.org/blog/2025/12/16/maximizing-throughput-nbcc#nbcc-in-action" class="hash-link" aria-label="Direct link to NBCC in Action" title="Direct link to NBCC in Action" translate="no">​</a></h3>
<p>NBCC allows concurrent writers to append data to log files in a non-conflicting way. Subsequent read or merge operations will need to follow the write completion time to process the files. Take compaction as an example, as shown in the diagram below.</p>
<p><img decoding="async" loading="lazy" alt="p6-nbcc-compaction" src="https://hudi.apache.org/assets/images/p6-nbcc-compaction-0bb93a57d1dbcc4c0ef5890bef7eaa71.png" width="707" height="442" class="img_ev3q"></p>
<p>Consider two writers: Writer A creates deltacommit 1 (starts at T1, completes at T5); Writer B creates deltacommit 2 (starts at T2, completes at T3). When compaction is scheduled at T4, the planner only includes files from deltacommit 2, since its completion time (T3) is earlier than the compaction schedule time (T4). Deltacommit 1, though started earlier, is excluded because it hadn't completed yet when compaction was planned—its files will be included in a later compaction.</p>
<p>Snapshot reads follow the same rules. A query at T4 includes data from deltacommit 2 but excludes deltacommit 1, which hasn't finished yet. A query after T5 includes both deltacommits, with log files read in completion time order and records merged according to the configured merge mode.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="configuration">Configuration<a href="https://hudi.apache.org/blog/2025/12/16/maximizing-throughput-nbcc#configuration" class="hash-link" aria-label="Direct link to Configuration" title="Direct link to Configuration" translate="no">​</a></h3>
<p>NBCC requires Hudi 1.0+ and a lock provider for TrueTime-like timestamp generation. Common options for lock providers include ZooKeeper-based and DynamoDB-based, which integrate with existing infrastructure many organizations already run. Hudi 1.1 introduced the <a href="https://hudi.apache.org/docs/concurrency_control#storage-based-lock-provider" target="_blank" rel="noopener noreferrer" class="">storage-based lock provider</a>, which uses cloud storage conditional writes (S3, GCS) and requires no external server—an option with less operational overhead for cloud-native deployments.</p>
<div class="language-py codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-py codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#F8F8F2"><span class="token plain">hudi_writer_options </span><span class="token operator">=</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token string" style="color:rgb(255, 121, 198)">'hoodie.write.concurrency.mode'</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">'NON_BLOCKING_CONCURRENCY_CONTROL'</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token string" style="color:rgb(255, 121, 198)">'hoodie.write.lock.provider'</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">'org.apache.hudi.client.transaction.lock.StorageBasedLockProvider'</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><br></span></code></pre></div></div>
<p>To enable NBCC for your concurrent writers, configure the concurrency mode and lock provider options for each writer, as shown in the example above.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="when-to-use-nbcc">When to Use NBCC<a href="https://hudi.apache.org/blog/2025/12/16/maximizing-throughput-nbcc#when-to-use-nbcc" class="hash-link" aria-label="Direct link to When to Use NBCC" title="Direct link to When to Use NBCC" translate="no">​</a></h3>
<p>If you are running multiple concurrent streaming writers, or running streaming ingestion with batch maintenance jobs like GDPR deletion, NBCC is more suitable than OCC. The table below summarizes some common examples:</p>
<table><thead><tr><th style="text-align:left">Use Case</th><th style="text-align:left">Recommendation</th><th style="text-align:left">Why</th></tr></thead><tbody><tr><td style="text-align:left">Batch ETL with single writer or multiple coordinated writers</td><td style="text-align:left">OCC is fine</td><td style="text-align:left">No concurrency conflicts</td></tr><tr><td style="text-align:left">Multiple concurrent streaming writers</td><td style="text-align:left">NBCC</td><td style="text-align:left">Avoid retry storms</td></tr><tr><td style="text-align:left">Mixed streaming + batch maintenance</td><td style="text-align:left">NBCC</td><td style="text-align:left">Long-running jobs will not starve</td></tr><tr><td style="text-align:left">Copy-on-Write (COW) tables with infrequent updates</td><td style="text-align:left">OCC is fine</td><td style="text-align:left">COW rewrites base files anyway</td></tr><tr><td style="text-align:left">MOR tables with frequent updates</td><td style="text-align:left">NBCC</td><td style="text-align:left">Maximum benefit from log file separation</td></tr></tbody></table>
<p>Hudi NBCC is designed specifically for MOR tables. COW tables rewrite entire base files on updates, so file-level conflicts are unavoidable regardless of concurrency control mode. As of now, NBCC is restricted to working with tables using the simple bucket index or partition-level bucket index. Learn more from the <a href="https://hudi.apache.org/docs/concurrency_control#non-blocking-concurrency-control" target="_blank" rel="noopener noreferrer" class="">concurrency control docs page</a>.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="summary">Summary<a href="https://hudi.apache.org/blog/2025/12/16/maximizing-throughput-nbcc#summary" class="hash-link" aria-label="Direct link to Summary" title="Direct link to Summary" translate="no">​</a></h2>
<p>OCC assumes conflicts are rare. When you mix high-frequency streaming with long-running maintenance jobs, OCC's retry-on-conflict model breaks down—causing wasted compute, reduced throughput, and job starvation. Retries are the throughput killer.</p>
<p>NBCC takes a different approach: let every writer succeed by appending to separate log files, then follow the write completion time for reads and compaction. Three design foundations make this possible—record keys and file groups that colocate records and their updates, completion time tracking that properly orders overlapping write transactions, and TrueTime-like timestamp generation that guarantees monotonically increasing timestamps across distributed writers.</p>
<p>The result: maximum throughput for concurrent writes in Hudi pipelines. Long-running jobs complete without being starved, multiple ingestion pipelines coexist without contention, and your data platform scales without coordination overhead. Stop retrying, start scaling—see <a href="https://hudi.apache.org/docs/overview" target="_blank" rel="noopener noreferrer" class="">the docs</a> to get started.</p>]]></content:encoded>
            <category>data lakehouse</category>
            <category>concurrency control</category>
            <category>streaming</category>
        </item>
        <item>
            <title><![CDATA[From Batch to Streaming: Accelerating Data Freshness in Uber's Data Lake]]></title>
            <link>https://hudi.apache.org/blog/2025/12/12/from-batch-to-streaming-accelerating-data-freshness-in-ubers-data-lake</link>
            <guid>https://hudi.apache.org/blog/2025/12/12/from-batch-to-streaming-accelerating-data-freshness-in-ubers-data-lake</guid>
            <pubDate>Fri, 12 Dec 2025 00:00:00 GMT</pubDate>
            <description><![CDATA[Redirecting... please wait!!]]></description>
            <content:encoded><![CDATA[<span>Redirecting... please wait!! <!-- -->or click <a href="https://www.uber.com/blog/from-batch-to-streaming-accelerating-data-freshness-in-ubers-data-lake/">here</a></span>]]></content:encoded>
            <category>streaming</category>
            <category>apache flink</category>
            <category>data lakehouse</category>
            <category>uber</category>
        </item>
        <item>
            <title><![CDATA[Apache Hudi 1.1 Deep Dive: Optimizing Streaming Ingestion with Apache Flink]]></title>
            <link>https://hudi.apache.org/blog/2025/12/10/apache-hudi-11-deep-dive-optimizing-streaming-ingestion-with-flink</link>
            <guid>https://hudi.apache.org/blog/2025/12/10/apache-hudi-11-deep-dive-optimizing-streaming-ingestion-with-flink</guid>
            <pubDate>Wed, 10 Dec 2025 00:00:00 GMT</pubDate>
            <description><![CDATA[---]]></description>
            <content:encoded><![CDATA[<hr>
<p><em>This blog was translated from the <a href="https://mp.weixin.qq.com/s/ek80Fzw30FOawk1qeWxtCw" target="_blank" rel="noopener noreferrer" class="">original blog in Chinese</a>.</em></p>
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="background">Background<a href="https://hudi.apache.org/blog/2025/12/10/apache-hudi-11-deep-dive-optimizing-streaming-ingestion-with-flink#background" class="hash-link" aria-label="Direct link to Background" title="Direct link to Background" translate="no">​</a></h2>
<p>With the rise of real-time data processing, streaming ingestion has become a critical use case for Apache Hudi. Apache Flink, a robust stream processing framework, has been seamlessly integrated with Hudi to support near-real-time data ingestion. While the Flink integration has already provided powerful and comprehensive capabilities—such as robust exactly-once guarantees backed by Flink's checkpointing mechanism, flexible write modes, and rich index management—as data volumes scale into petabytes, achieving optimal performance for streaming ingestion becomes a challenge, leading to backpressure and high resource costs for streaming jobs.</p>
<p>There are multiple factors that impact streaming ingestion performance, such as the network shuffle overhead between Flink operators, SerDe costs within Hudi writers, and GC issues caused by memory management of in-memory buffers. With Hudi 1.1, meticulous refactoring and optimization work has been conducted to solve these problems, significantly enhancing the performance and stability of streaming ingestion with Flink.</p>
<p>In the subsequent sections, several key performance optimizations are introduced, including:</p>
<ul>
<li class="">Optimized SerDe between Flink operators</li>
<li class="">New performant Flink-native writers</li>
<li class="">Eliminated bytes copy for MOR log file writing</li>
</ul>
<p>Following that, performance benchmarks for streaming ingestion in Hudi 1.1 are presented to demonstrate the concrete improvements achieved.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="optimized-serde-between-flink-operators">Optimized SerDe Between Flink Operators<a href="https://hudi.apache.org/blog/2025/12/10/apache-hudi-11-deep-dive-optimizing-streaming-ingestion-with-flink#optimized-serde-between-flink-operators" class="hash-link" aria-label="Direct link to Optimized SerDe Between Flink Operators" title="Direct link to Optimized SerDe Between Flink Operators" translate="no">​</a></h2>
<p>Before Hudi 1.1, Avro was the default internal record format in the Flink writing/reading path, which means the first step in the write pipeline was converting Flink <code>RowData</code> into Avro record payload, and then it was used to create Hudi internal <code>HoodieRecord</code> for the following processing.</p>
<p><img decoding="async" loading="lazy" alt="Flink write pipeline before Hudi 1.1" src="https://hudi.apache.org/assets/images/flink-write-pipeline-before-1.1-b032868a09523f533cd153e5b4916a37.png" width="5798" height="1176" class="img_ev3q"></p>
<p>Almost every Flink job has to exchange data between its operators, and when the operators are not chained together (located in the same JVM process), records need to be serialized to bytes first before being sent to the downstream operator through the network. The shuffle serialization alone can be quite costly if not executed efficiently, and thus, when you examine the profiler output of the job, you will likely see serialization among the top consumers of CPU cycles. Flink actually has an out-of-the-box serialization framework, and there are performant internal serializers for basic types, like primitive types and row type. However, for generic types, e.g., <code>HoodieRecord</code>, Flink will fall back to the default serializer based on Kryo, which exhibits poor serialization performance.</p>
<p><a href="https://github.com/apache/hudi/blob/master/rfc/rfc-84/rfc-84.md" target="_blank" rel="noopener noreferrer" class="">RFC-84</a> proposed an improvement on SerDe between operators in the Hudi write pipeline based on the extensible type system of Flink:</p>
<ul>
<li class=""><code>HoodieFlinkInternalRow</code>: an object to replace <code>HoodieRecord</code> during data shuffle, which contains <code>RowData</code> as the data field and some necessary metadata fields, e.g., record key, partition path, etc.</li>
<li class=""><code>HoodieFlinkInternalRowTypeInfo</code>: a customized Flink type information for <code>HoodieFlinkInternalRow</code>.</li>
<li class=""><code>HoodieFlinkInternalRowSerializer</code>: a customized and efficient Flink <code>TypeSerializer</code> for the SerDe of <code>HoodieFlinkInternalRow</code>.</li>
</ul>
<p><img decoding="async" loading="lazy" alt="Optimized SerDe with HoodieFlinkInternalRow" src="https://hudi.apache.org/assets/images/optimized-serde-hoodie-flink-internal-row-a174bf7843c9b6bcd94d8a8175bbc6cf.png" width="5777" height="1176" class="img_ev3q"></p>
<p>With the customized Flink-native data structure and the companion serializer, the average streaming ingestion throughput can be boosted by about 25%.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="new-performant-flink-native-writers">New Performant Flink-Native Writers<a href="https://hudi.apache.org/blog/2025/12/10/apache-hudi-11-deep-dive-optimizing-streaming-ingestion-with-flink#new-performant-flink-native-writers" class="hash-link" aria-label="Direct link to New Performant Flink-Native Writers" title="Direct link to New Performant Flink-Native Writers" translate="no">​</a></h2>
<p>Historically, Hudi Flink writer used <code>HoodieRecord&lt;AvroPayload&gt;</code> with data serialized using Avro to represent incoming records. While this unified format worked across engines, it came at a performance cost:</p>
<p><img decoding="async" loading="lazy" alt="Legacy Avro-based write path" src="https://hudi.apache.org/assets/images/legacy-avro-write-path-6c853a93e2dcfc869834b8208c60260f.png" width="6094" height="1778" class="img_ev3q"></p>
<ul>
<li class=""><strong>Redundant SerDe</strong>: Take MOR table ingestion as an example. Flink reads records as <code>RowData</code>, which are converted to Avro <code>GenericRecord</code> and then serialized to Avro bytes for internal <code>HoodieAvroRecord</code>. During the later log writing, the Avro bytes in <code>HoodieAvroRecord</code> are deserialized back into Avro <code>IndexedRecord</code> for further processing before being appended to log files. Apparently, with Avro as the intermediate record representation, significant SerDe overhead is introduced.</li>
<li class=""><strong>Excess memory usage</strong>: The buffer in the writers is a Java List with intermediate Avro objects which will be released after being flushed to disk. These objects can increase heap usage and GC pressure, particularly under high-throughput streaming workloads.</li>
</ul>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="rfc-87-a-flink-native-write-path">RFC-87: A Flink-Native Write Path<a href="https://hudi.apache.org/blog/2025/12/10/apache-hudi-11-deep-dive-optimizing-streaming-ingestion-with-flink#rfc-87-a-flink-native-write-path" class="hash-link" aria-label="Direct link to RFC-87: A Flink-Native Write Path" title="Direct link to RFC-87: A Flink-Native Write Path" translate="no">​</a></h3>
<p><a href="https://github.com/apache/hudi/blob/master/rfc/rfc-87/rfc-87.md" target="_blank" rel="noopener noreferrer" class="">RFC-87</a> proposes and implements a shift in data representation: instead of transforming <code>RowData</code> to <code>GenericRecord</code>, the Flink writer now directly wraps <code>RowData</code> inside a specialized <code>HoodieRecord</code>. The entire write path now operates around the <code>RowData</code> structure, eliminating all redundant conversion overhead. This change is Flink engine-specific and transparent to the overall Hudi writer lifecycle.</p>
<p><img decoding="async" loading="lazy" alt="New Flink-native write path" src="https://hudi.apache.org/assets/images/flink-native-write-path-2b1938700dea8b09f055fd41a2fae96f.png" width="6094" height="1812" class="img_ev3q"></p>
<p><strong>Key Changes:</strong></p>
<ul>
<li class=""><strong>Customized HoodieRecord</strong>: Introduced <code>HoodieFlinkRecord</code> to encapsulate Flink's <code>RowData</code> directly—no extra conversion to Avro record in the writing path anymore.</li>
<li class=""><strong>Self-managed binary buffer</strong>: Flink's internal <code>BinaryRowData</code> is in binary format. We've implemented a binary buffer to cache the bytes of the <code>RowData</code>. The memory is managed by the writer and can be reused after the previously cached <code>RowData</code> bytes are flushed to disk. With this self-managed binary buffer, GC pressure can be effectively reduced even under high-throughput workloads.</li>
<li class=""><strong>Flexible log formats</strong>: For MOR tables, records are written as data blocks in log files. We currently support two kinds of block types:<!-- -->
<ul>
<li class=""><strong>Avro data block</strong>: Optimized for row-level writes, making it ideal for streaming ingestion or workloads with frequent updates and inserts. It's the default block type for log files.</li>
<li class=""><strong>Parquet data block</strong>: Columnar and better suited for data with a high compression ratio, e.g., records with primitive type fields.</li>
</ul>
</li>
</ul>
<p>By leveraging the Flink-native data model, the redundant Avro conversions along with SerDe overhead are eliminated in the streaming ingestion pipeline, which brings significant improvements in both write latency and resource consumption.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="eliminate-bytes-copy-for-mor-log-file-writing">Eliminate Bytes Copy for MOR Log File Writing<a href="https://hudi.apache.org/blog/2025/12/10/apache-hudi-11-deep-dive-optimizing-streaming-ingestion-with-flink#eliminate-bytes-copy-for-mor-log-file-writing" class="hash-link" aria-label="Direct link to Eliminate Bytes Copy for MOR Log File Writing" title="Direct link to Eliminate Bytes Copy for MOR Log File Writing" translate="no">​</a></h2>
<p>The log file of a MOR table is composed of data blocks. When writing a log file, the basic unit is the data block. The main contents of each block are shown in the figure below, where the <code>Content</code> is binary bytes generated by serializing the records in the buffer into the specified block format.</p>
<p><img decoding="async" loading="lazy" alt="Log file data block structure" src="https://hudi.apache.org/assets/images/log-file-data-block-structure-d685ccce3f9a19b496e8036cf3632a70.png" width="1280" height="1106" class="img_ev3q"></p>
<p>As mentioned in the previous section, the default data block format is Avro. In Hudi 1.1, the serialization of this block type has been carefully optimized by eliminating record-level bytes copy. Specifically, before Hudi 1.1, each record was serialized by the Avro Writer into a <code>ByteArrayOutputStream</code>, then the <code>toByteArray</code> method was invoked to obtain the data bytes for writing into the outer output stream for the data block. However, under the hood, <code>toByteArray</code> involves the creation of a new byte array and bytes copy. Since the bytes copy happens at the record level, it generates a large number of temporary objects in high-throughput scenarios, which further increases GC pressure.</p>
<p><img decoding="async" loading="lazy" alt="Bytes copy optimization" src="https://hudi.apache.org/assets/images/bytes-copy-optimization-9a2b7caea80f1dfc9d0a5bd1f0521173.png" width="1280" height="496" class="img_ev3q"></p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="improvement-in-hudi-11">Improvement in Hudi 1.1<a href="https://hudi.apache.org/blog/2025/12/10/apache-hudi-11-deep-dive-optimizing-streaming-ingestion-with-flink#improvement-in-hudi-11" class="hash-link" aria-label="Direct link to Improvement in Hudi 1.1" title="Direct link to Improvement in Hudi 1.1" translate="no">​</a></h3>
<p>In Hudi 1.1, the <code>writeTo</code> method of <code>ByteArrayOutputStream</code> is utilized to directly write the underlying data bytes to the outer output stream for the block, thereby avoiding additional record-level bytes copy and effectively reducing GC pressure.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="benchmark">Benchmark<a href="https://hudi.apache.org/blog/2025/12/10/apache-hudi-11-deep-dive-optimizing-streaming-ingestion-with-flink#benchmark" class="hash-link" aria-label="Direct link to Benchmark" title="Direct link to Benchmark" translate="no">​</a></h2>
<p>To demonstrate performance improvements in streaming ingestion with Flink, we performed comprehensive benchmark testing of Apache Hudi 1.1 vs. Apache Hudi 1.0 vs. Apache Paimon 1.0.1. Currently there is no standard test dataset for streaming read/write. Since it's important to run transparent and reproducible benchmarking, we decided to use the existing <a href="https://github.com/apache/paimon/tree/master/paimon-benchmark/paimon-cluster-benchmark" target="_blank" rel="noopener noreferrer" class="">Cluster benchmark program</a> of Apache Paimon inspired by <a href="https://github.com/nexmark/nexmark" target="_blank" rel="noopener noreferrer" class="">Nexmark</a>, as Paimon has previously claimed multi-fold better ingestion performance than Hudi in this benchmark, which is one of their key selling points.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="cluster-environment">Cluster Environment<a href="https://hudi.apache.org/blog/2025/12/10/apache-hudi-11-deep-dive-optimizing-streaming-ingestion-with-flink#cluster-environment" class="hash-link" aria-label="Direct link to Cluster Environment" title="Direct link to Cluster Environment" translate="no">​</a></h3>
<p>The benchmarks were run on an Alibaba Cloud EMR cluster, with the following settings:</p>
<ul>
<li class=""><strong>EMR on ECS</strong>: version 5.18.1<!-- -->
<ul>
<li class="">Master (x1): 8 vCPU, 32 GiB, 5 Gbps</li>
<li class="">Worker (x4): 24 vCPU, 96 GiB, 12 Gbps</li>
</ul>
</li>
<li class=""><strong>Apache Hudi version</strong>: 1.1 and 1.0.1</li>
<li class=""><strong>Apache Paimon version</strong>: 1.0.1</li>
<li class=""><strong>Apache Flink version</strong>: 1.17.2</li>
<li class=""><strong>HDFS version</strong>: 3.2.1</li>
</ul>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="streaming-ingestion-scenario">Streaming Ingestion Scenario<a href="https://hudi.apache.org/blog/2025/12/10/apache-hudi-11-deep-dive-optimizing-streaming-ingestion-with-flink#streaming-ingestion-scenario" class="hash-link" aria-label="Direct link to Streaming Ingestion Scenario" title="Direct link to Streaming Ingestion Scenario" translate="no">​</a></h3>
<p>The testing scenario is the most common streaming ingestion case in production: MOR table with UPSERT operation and Bucket index. We used the Paimon Benchmark program to simulate the workloads, where the data source was generated by the Flink DataGen connector, which produces records with primary keys ranging from 0 to 100,000,000, and the total record number is 500 million. For more detailed settings, refer to <a href="https://github.com/cshuo/streaming-benchmark" target="_blank" rel="noopener noreferrer" class="">this repository</a>.</p>
<p>Note that we disabled compaction in the test ingestion jobs, since it can significantly impact the performance of the ingestion job for both Hudi and Paimon and interfere with a fair comparison of ingestion performance. In fact, this is also common practice in production.</p>
<p>Additionally, the schema of a table also has a significant impact on write performance. There is a noticeable difference in the processing overhead between numeric primitive type fields and string type fields. Therefore, besides the default table schema (Schema1) used in the Paimon Benchmark program, we also added 3 different schemas containing mostly STRING-type fields, which are much more common in production scenarios.</p>
<table><thead><tr><th>Schema1</th><th>Schema2</th><th>Schema3</th><th>Schema4</th></tr></thead><tbody><tr><td>1 String + 10 BIGINT fields</td><td>20 String fields</td><td>50 String fields</td><td>100 String fields</td></tr></tbody></table>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="benchmark-results">Benchmark Results<a href="https://hudi.apache.org/blog/2025/12/10/apache-hudi-11-deep-dive-optimizing-streaming-ingestion-with-flink#benchmark-results" class="hash-link" aria-label="Direct link to Benchmark Results" title="Direct link to Benchmark Results" translate="no">​</a></h3>
<ul>
<li class="">For Schema1 with almost all fields being numeric type, the streaming ingestion performance of Hudi 1.1 is about 3.5 times that of Hudi 1.0. The performance gain mainly comes from the optimizations introduced in RFC-84 and RFC-87, which reduce the shuffle SerDe overhead in the ingestion pipeline and internal Avro SerDe costs in the writer.</li>
<li class="">Streaming ingestion throughput of Paimon is slightly higher than that of Hudi 1.1. Through detailed profiling and analysis, we found that this performance gap mainly stems from the fact that each <code>HoodieRecord</code> contains 5 extra String-type metadata fields by default, and in simple schema scenarios like Schema1, these record-level additional fields have a significant performance impact.</li>
</ul>
<p><img decoding="async" loading="lazy" alt="Benchmark results for Schema1" src="https://hudi.apache.org/assets/images/benchmark-schema1-e57e652fe29a9dcf5fd546b31c19421b.png" width="991" height="853" class="img_ev3q"></p>
<ul>
<li class="">For schemas where most fields are of STRING type, Hudi 1.1 achieves the best streaming ingestion performance. Based on the profiling analysis, we found that when the data fields are all strings, Paimon's approach of directly writing to Parquet files incurs noticeable compression overhead, which negatively impacts throughput—even though the benchmark tests used SNAPPY, the fastest available compression codec. Hudi, on the other hand, writes incremental data in row-based Avro format to log files. While its compression ratio is lower, this approach is more favorable for ingestion throughput across a variety of workloads.</li>
</ul>
<p><img decoding="async" loading="lazy" alt="Benchmark results for string-heavy schemas" src="https://hudi.apache.org/assets/images/benchmark-string-schemas-52fce427657dee18c89ea3bd4c54e9c6.png" width="1622" height="1081" class="img_ev3q"></p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="summary">Summary<a href="https://hudi.apache.org/blog/2025/12/10/apache-hudi-11-deep-dive-optimizing-streaming-ingestion-with-flink#summary" class="hash-link" aria-label="Direct link to Summary" title="Direct link to Summary" translate="no">​</a></h2>
<p>The optimizations in Hudi 1.1 around writer performance for Flink have brought significant, multi-fold improvements to streaming ingestion throughput. These enhancements are transparent and backward-compatible, allowing users to seamlessly upgrade their jobs from earlier Hudi versions to the latest version and enjoy the substantial performance gains without any additional operational overhead.</p>]]></content:encoded>
            <category>apache flink</category>
            <category>apache paimon</category>
            <category>performance</category>
        </item>
        <item>
            <title><![CDATA[Mastering Schema Evolution with Apache Hudi]]></title>
            <link>https://hudi.apache.org/blog/2025/12/03/Mastering-Schema-Evolution-with-Apache-Hudi</link>
            <guid>https://hudi.apache.org/blog/2025/12/03/Mastering-Schema-Evolution-with-Apache-Hudi</guid>
            <pubDate>Wed, 03 Dec 2025 00:00:00 GMT</pubDate>
            <description><![CDATA[Redirecting... please wait!!]]></description>
            <content:encoded><![CDATA[<span>Redirecting... please wait!! <!-- -->or click <a href="https://medium.com/@shaiksameer0045/the-chameleon-architecture-mastering-schema-evolution-with-apache-hudi-446da1a2f0c6">here</a></span>]]></content:encoded>
            <category>schema</category>
            <category>data lakehouse</category>
        </item>
        <item>
            <title><![CDATA[Next Generation Lakehouse: New Engine for the Intelligent Future | Apache Hudi Meetup Asia Recap]]></title>
            <link>https://hudi.apache.org/blog/2025/12/01/apache-hudi-JD-meetup-asia-2025-recap</link>
            <guid>https://hudi.apache.org/blog/2025/12/01/apache-hudi-JD-meetup-asia-2025-recap</guid>
            <pubDate>Mon, 01 Dec 2025 00:00:00 GMT</pubDate>
            <description><![CDATA[---]]></description>
            <content:encoded><![CDATA[<hr>
<p><em>This blog was translated from the <a href="https://mp.weixin.qq.com/s/LNMZGl-kXJTblOCO6s0BxQ" target="_blank" rel="noopener noreferrer" class="">original blog in Chinese</a>.</em></p>
<hr>
<p>Recently, the Apache Hudi Meetup Asia, hosted by JD.com, was successfully held at JD.com Group headquarters. Four technical experts from Onehouse, JD.com, Kuaishou, and Huawei gathered together, not only bringing a preview of Apache Hudi release 1.1, but also sharing their unique approaches to building data lakehouses. From AI scenario support to real-time data processing and cost optimization, each topic directly addressed the pain points that data engineers care about most.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="hudi-community-leader-joined-remotely">Hudi Community Leader Joined Remotely<a href="https://hudi.apache.org/blog/2025/12/01/apache-hudi-JD-meetup-asia-2025-recap#hudi-community-leader-joined-remotely" class="hash-link" aria-label="Direct link to Hudi Community Leader Joined Remotely" title="Direct link to Hudi Community Leader Joined Remotely" translate="no">​</a></h2>
<p>First, Vinoth Chandar, CEO &amp; Founder of Onehouse and Apache Hudi PMC Chair, delivered the opening remarks via video. He stated that after eight years of development, Hudi has become an important cornerstone in the data lake domain, and its vision has transformed into widely recognized achievements in the industry. The 1.0 version released last year marked the project's entry into a mature stage, bringing many database-like capabilities to the lakehouse.</p>
<p>Currently, the community is steadily advancing the 1.x series of versions, focusing on improving Flink performance, launching a new Trino connector, and enhancing interoperability through a pluggable table format layer. Facing the rapid development in the data lake field, Vinoth emphasized that excellent technology and robust design are the keys to long-term success. Hudi has now achieved many capabilities that commercial engines have not been able to deliver, thanks to its intelligent and creative community. Looking ahead, the community will be committed to building Hudi into a storage engine that supports all scenarios from BI to AI, exploring trending areas including unstructured data management and vector search.</p>
<p>Vinoth specially thanked JD.com for its significant contributions to Apache Hudi. Among the top 100 contributors, 6 were from JD.com. Finally, he also invited more developers to join this vibrant community to jointly promote innovation and development in data infrastructure.</p>
<p><img decoding="async" loading="lazy" alt="image 1" src="https://hudi.apache.org/assets/images/jdpost-image1-98f8473e19ed6ff3c1f91a0a47c779f8.png" width="1075" height="576" class="img_ev3q"></p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="jd-retail-data-lake-technical-challenges-and-outlook">JD Retail: Data Lake Technical Challenges and Outlook<a href="https://hudi.apache.org/blog/2025/12/01/apache-hudi-JD-meetup-asia-2025-recap#jd-retail-data-lake-technical-challenges-and-outlook" class="hash-link" aria-label="Direct link to JD Retail: Data Lake Technical Challenges and Outlook" title="Direct link to JD Retail: Data Lake Technical Challenges and Outlook" translate="no">​</a></h2>
<p>As the co-host of the event, Zhang Ke, Head of AI Infra &amp; Big Data Computing at JD Retail, welcomed guests and attendees who participated in this Meetup. He also pointed out two core challenges facing the data domain:</p>
<p>At the BI level, the long-standing problem of "unified stream and batch processing" has not yet been perfectly solved, forcing data R&amp;D personnel to duplicate work across multiple systems. This requires fundamentally reconstructing the data architecture and finding a new paradigm for unified stream and batch processing.</p>
<p>At the AI level, with the arrival of the multimodal era, traditional solutions that only handle structured data can no longer meet the needs. Whether it is data supply efficiency for model training, real-time feature computation for recommendation systems, or knowledge base construction required for large models, there is an urgent need for an underlying support system that can unify storage of multimodal data while balancing cost and performance.</p>
<p>The industry is looking forward to building a storage foundation through open-source technologies like Apache Hudi that can uniformly carry batch processing, stream computing, data analysis, and AI workloads.</p>
<p><img decoding="async" loading="lazy" alt="image 2" src="https://hudi.apache.org/assets/images/jdpost-image2-205cca47f2d4b91f38bc923e5c937be5.jpg" width="4032" height="3024" class="img_ev3q"></p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="apache-hudi-11-preview-and-ai-native-lakehouse-evolution">Apache Hudi 1.1 Preview and AI-Native Lakehouse Evolution<a href="https://hudi.apache.org/blog/2025/12/01/apache-hudi-JD-meetup-asia-2025-recap#apache-hudi-11-preview-and-ai-native-lakehouse-evolution" class="hash-link" aria-label="Direct link to Apache Hudi 1.1 Preview and AI-Native Lakehouse Evolution" title="Direct link to Apache Hudi 1.1 Preview and AI-Native Lakehouse Evolution" translate="no">​</a></h2>
<p>In the session "Apache Hudi 1.1 Preview and AI-Native Lakehouse Evolution," Ethan Guo (Yihua Guo), Data Architecture Engineer at Onehouse and Apache Hudi PMC member, shared Hudi's technical evolution path and future outlook. As the top contributor to the Hudi codebase, he systematically elaborated on the project positioning, version planning, and AI-native architecture.</p>
<p>Ethan pointed out that Apache Hudi's positioning goes far beyond being an open table format—it is an embedded, headless, distributed database system built on top of cloud storage. Hudi is moving from "a transactional database on the lakehouse" toward "an AI-native Lakehouse platform."</p>
<p>In the then-upcoming 1.1 release (now released), Hudi has achieved several important breakthroughs. Among them, the pluggable table format architecture effectively solves the pain point of format fragmentation in the current data lake ecosystem, enabling users to "write once, read in multiple formats." At the same time, Hudi has deeply optimized Flink integration, solving the throughput bottleneck in streaming writes through an asynchronous generation mechanism, and building a brand-new native writer that achieves end-to-end processing from Avro format to Flink RowData, significantly reducing serialization overhead and GC pressure. Real-world tests showed that Hudi 1.1's throughput performance in streaming lake ingestion scenarios was 3.5 times that of version 1.0.</p>
<p>Facing new challenges brought by the AI era, Hudi is actively building a native AI data foundation. By supporting unstructured data storage, optimizing column group structures for multimodal data, providing built-in vector indexing capabilities, and building a unified storage layer that supports transactions and version control, Hudi is committed to providing highly real-time, traceable, and easily extensible data support for AI workflows. This series of evolutions will propel Apache Hudi from an excellent data lake framework to a core data infrastructure supporting the AI era.</p>
<p><img decoding="async" loading="lazy" alt="image 3" src="https://hudi.apache.org/assets/images/jdpost-image3-c843eba5797513a93582fb8e5682b52c.jpg" width="4032" height="3024" class="img_ev3q"></p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="latest-architecture-evolution-of-apache-hudi-at-jdcom">Latest Architecture Evolution of Apache Hudi at JD.com<a href="https://hudi.apache.org/blog/2025/12/01/apache-hudi-JD-meetup-asia-2025-recap#latest-architecture-evolution-of-apache-hudi-at-jdcom" class="hash-link" aria-label="Direct link to Latest Architecture Evolution of Apache Hudi at JD.com" title="Direct link to Latest Architecture Evolution of Apache Hudi at JD.com" translate="no">​</a></h2>
<p>In the session "Latest Architecture Evolution of Apache Hudi at JD.com," Han Fei, Head of JD Real-time Data Platform, systematically introduced the latest architectural evolution and implementation results of Hudi in JD's production environment.</p>
<p>Addressing the performance bottleneck of native MOR tables in high-throughput scenarios, JD's Data Lake team reconstructed the data organization protocol of Hudi MOR tables based on LSM-Tree architecture. By replacing the original "Avro + Append" update mode with "Parquet + Create" mode, lock-free concurrent write capability was achieved. Combined with a series of optimization methods such as Engine-Native data format, Remote Partitioner strategy, and streaming incremental Compaction scheduling mechanism, read and write performance were significantly improved. Benchmark test results showed that the MOR-LSM solution's read and write performance was 2-10 times that of the native MOR-Avro solution, demonstrating significant technical advantages.</p>
<p>Facing the growing near-real-time requirements of BI scenarios, streaming dimension widening had gradually become a common challenge for multi-subject domain data processing. Traditional Flink streaming Join had problems such as state bloat and high maintenance complexity. JD's Data Lake team, drawing on Hudi's partial-update multi-stream splicing approach, built an indexing mechanism that supported primary-foreign key mapping. This mechanism efficiently completed streaming dimension association and real-time updates through the coordinated operation of forward and reverse indexes. At the same time, pluggable HBase was introduced as index storage, ensuring high-performance access capability in point query scenarios.</p>
<p>In exploring AI scenarios, the team designed and implemented the Hudi NativeIO SDK. This SDK builds four core modules: data invocation layer, cross-language Transformation layer, Hudi view management layer, and high-performance query layer, creating an end-to-end process for sample training engines to complete training directly based on data lake tables.</p>
<p>JD had deeply integrated these capabilities with business scenarios, applying them to the near-real-time transformation of the traffic data warehouse ADM layer. After a series of optimizations, the write throughput of the traffic browsing link increased from 45 million per minute to 80 million, Compaction execution efficiency doubled, and real-time consistency maintenance of SKU dimension information was achieved, completing a comprehensive transformation from T+1 offline repair mode to real-time processing mode.</p>
<p>While promoting self-developed technology, JD also actively gave back to the open-source community, with a total of 109 contributed and merged PRs. In the future, the team will continue to deepen Hudi's application in the real-time data lake domain, providing stronger data support capabilities for business innovation.</p>
<p><img decoding="async" loading="lazy" alt="image 4" src="https://hudi.apache.org/assets/images/jdpost-image4-0bc644b6a57a3145ebccc064d497349e.jpg" width="4032" height="3024" class="img_ev3q"></p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="how-kuaishous-real-time-lake-ingestion-empowered-bi--ai-scenario-architecture-upgrade">How Kuaishou's Real-time Lake Ingestion Empowered BI &amp; AI Scenario Architecture Upgrade<a href="https://hudi.apache.org/blog/2025/12/01/apache-hudi-JD-meetup-asia-2025-recap#how-kuaishous-real-time-lake-ingestion-empowered-bi--ai-scenario-architecture-upgrade" class="hash-link" aria-label="Direct link to How Kuaishou's Real-time Lake Ingestion Empowered BI &amp; AI Scenario Architecture Upgrade" title="Direct link to How Kuaishou's Real-time Lake Ingestion Empowered BI &amp; AI Scenario Architecture Upgrade" translate="no">​</a></h2>
<p>In the session "How Kuaishou's Real-time Lake Ingestion Empowers BI &amp; AI Scenario Architecture Upgrade," Wang Zeyu, Data Architecture R&amp;D Engineer at Kuaishou, introduced Kuaishou's complete evolution path and practical experience in building a real-time data lake based on Apache Hudi.</p>
<p>For traditional BI data warehouse scenarios, Kuaishou achieved an architecture upgrade from Mysql2Hive to Mysql2Hudi2.0. By introducing Hudi hourly partition tables, supporting multiple query modes such as full, incremental, and snapshot, and innovatively designing Full Compact and Minor Compact mechanisms to optimize data layout, Kuaishou improved the overall architecture. The introduction of bucket heterogeneity allowed full partitions and incremental partitions to support different bucket numbers, significantly reducing lake ingestion resource consumption. Compared with the original architecture, the new solution naturally supported long lifecycles and richer query behaviors. While reducing storage costs, it achieved a leap in data readiness time from day-level to minute-level.</p>
<p>At the AI storage architecture level, Kuaishou built a unified stream-batch data lake architecture, solving the core pain point of inconsistent offline and real-time training data. Through unified storage media, support for unified stream-batch consumption, logical wide table column splicing, and other capabilities, unified management and efficient reuse of training data were achieved. The metadata management mechanism based on Event-time timeline not only ensured data orderliness but also guaranteed real-time write performance through lock-free design.</p>
<p>In the future, Kuaishou will continue to improve the data lake's service capabilities in training, retrieval, analysis, and other multi-scenarios, promoting the evolution of the data lake toward a more intelligent and unified direction. Kuaishou's practice fully proves that the real-time data lake architecture based on Hudi can effectively support the modernization and upgrade needs of large-scale BI and AI scenarios.</p>
<p><img decoding="async" loading="lazy" alt="image 5" src="https://hudi.apache.org/assets/images/jdpost-image5-07e8b4eb07ad352e0410e07c0aab1f61.jpg" width="4032" height="3024" class="img_ev3q"></p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="deep-optimization-and-ai-exploration-of-apache-hudi-on-huawei-cloud">Deep Optimization and AI Exploration of Apache Hudi on Huawei Cloud<a href="https://hudi.apache.org/blog/2025/12/01/apache-hudi-JD-meetup-asia-2025-recap#deep-optimization-and-ai-exploration-of-apache-hudi-on-huawei-cloud" class="hash-link" aria-label="Direct link to Deep Optimization and AI Exploration of Apache Hudi on Huawei Cloud" title="Direct link to Deep Optimization and AI Exploration of Apache Hudi on Huawei Cloud" translate="no">​</a></h2>
<p>In the session "Deep Optimization and AI Exploration of Apache Hudi on Huawei Cloud," Yang Xuan, Big Data Lakehouse Kernel R&amp;D Engineer at Huawei, shared Huawei Cloud's technical practices and innovative breakthroughs in building a new generation Lakehouse architecture based on Apache Hudi. Facing challenges in real-time performance, intelligence, and management efficiency for enterprise-level data platforms, Huawei conducted in-depth exploration in three dimensions: platform architecture, kernel optimization, and ecosystem integration.</p>
<p>At the platform architecture level, Huawei developed the LDMS unified lakehouse management service platform, achieving fully managed operation and maintenance of table services. Through core capabilities such as intelligent data layout optimization and CBO statistics collection, this platform significantly reduced the operational complexity of the lakehouse platform, allowing users to focus more on business logic rather than underlying maintenance.</p>
<p>In terms of kernel optimization, Huawei made multiple deep modifications to Apache Hudi. Through de-Avro serialization optimization implemented via RFC-84/87, Flink write performance improved up to 10 times while significantly reducing GC pressure; the innovative LogIndex mechanism effectively solved the streaming read performance bottleneck in object storage scenarios; dynamic Schema change support made CDC lake ingestion processes more flexible; and the introduction of the column clustering mechanism provided a feasible solution for real-time processing of thousand-column sparse wide tables.</p>
<p>Hudi Native built a high-performance IO acceleration layer by rewriting Parquet read/write logic using Rust and adopting Arrow memory format to replace Avro. By providing a unified high-performance Java read/write interface through JNI, it achieved seamless integration with compute engines such as Spark and Flink, laying a solid foundation for future performance breakthroughs.</p>
<p>In ecosystem integration and AI exploration, Huawei built a management architecture supporting multimodal data. By using lake table formats to manage metadata of unstructured data, with actual files stored in object storage, it ensured ACID properties while avoiding data redundancy. At the same time, it integrated LanceDB to provide efficient vector retrieval capabilities, providing comprehensive data infrastructure support for AI application scenarios such as document retrieval and intelligent Q&amp;A.</p>
<p><img decoding="async" loading="lazy" alt="image 6" src="https://hudi.apache.org/assets/images/jdpost-image6-87ec3eaac37a810679ae6090c8953f9f.jpg" width="4032" height="3024" class="img_ev3q"></p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="conclusion">Conclusion<a href="https://hudi.apache.org/blog/2025/12/01/apache-hudi-JD-meetup-asia-2025-recap#conclusion" class="hash-link" aria-label="Direct link to Conclusion" title="Direct link to Conclusion" translate="no">​</a></h2>
<p>This meetup made us believe that the vast ocean of data lakehouses could not be separated from the "collective effort" of the open-source community and enterprises. Those technologies tempered on the business battlefield ultimately gave back as nutrients nourishing the entire ecosystem. This may be the purest romance of technology: making complex things simple and making the impossible possible. The road ahead is full of imagination, and together, we are shaping a more elegant and powerful future for data processing.</p>
<p><img decoding="async" loading="lazy" alt="image 7" src="https://hudi.apache.org/assets/images/jdpost-image7-361b48a09e72bed4c19f8be9bf616ed5.jpg" width="4032" height="3024" class="img_ev3q"></p>]]></content:encoded>
            <category>meetup</category>
            <category>data lakehouse</category>
        </item>
        <item>
            <title><![CDATA[Apache Hudi Dynamic Bloom Filter"]]></title>
            <link>https://hudi.apache.org/blog/2025/11/28/Apache-Hudi-Dynamic-Bloom-Filter</link>
            <guid>https://hudi.apache.org/blog/2025/11/28/Apache-Hudi-Dynamic-Bloom-Filter</guid>
            <pubDate>Fri, 28 Nov 2025 00:00:00 GMT</pubDate>
            <description><![CDATA[Redirecting... please wait!!]]></description>
            <content:encoded><![CDATA[<span>Redirecting... please wait!! <!-- -->or click <a href="https://codepointer.substack.com/p/apache-hudi-dynamic-bloom-filter">here</a></span>]]></content:encoded>
            <category>data lakehouse</category>
            <category>indexing</category>
        </item>
        <item>
            <title><![CDATA[Apache Hudi 1.1 is Here—Building the Foundation for the Next Generation of Lakehouse]]></title>
            <link>https://hudi.apache.org/blog/2025/11/25/apache-hudi-release-1-1-announcement</link>
            <guid>https://hudi.apache.org/blog/2025/11/25/apache-hudi-release-1-1-announcement</guid>
            <pubDate>Tue, 25 Nov 2025 00:00:00 GMT</pubDate>
            <description><![CDATA[The Hudi community is excited to announce the release of Hudi 1.1, a major milestone that sets the stage for the next generation of data lakehouse capabilities. This release represents months of focused engineering on foundational improvements, engine-specific optimizations, and key architectural enhancements, laying the foundation for ambitious features coming in future releases.]]></description>
            <content:encoded><![CDATA[<p>The Hudi community is excited to announce the <a href="https://hudi.apache.org/releases/release-1.1#release-111" target="_blank" rel="noopener noreferrer" class="">release of Hudi 1.1</a>, a major milestone that sets the stage for the next generation of data lakehouse capabilities. This release represents months of focused engineering on foundational improvements, engine-specific optimizations, and key architectural enhancements, laying the foundation for ambitious features coming in future releases.</p>
<p>Hudi continues to evolve rapidly, with contributions from a vibrant community of developers and users. The 1.1 release brings over 800 commits addressing performance bottlenecks, expanding engine support, and introducing new capabilities that make Hudi tables more reliable, faster, and easier to operate. Let’s dive into the highlights.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="pluggable-table-formatthe-foundation-for-multi-format-support">Pluggable Table Format—The Foundation for Multi-Format Support<a href="https://hudi.apache.org/blog/2025/11/25/apache-hudi-release-1-1-announcement#pluggable-table-formatthe-foundation-for-multi-format-support" class="hash-link" aria-label="Direct link to Pluggable Table Format—The Foundation for Multi-Format Support" title="Direct link to Pluggable Table Format—The Foundation for Multi-Format Support" translate="no">​</a></h2>
<p>Hudi 1.1 introduces a <a href="https://hudi.apache.org/docs/hudi_stack#pluggable-table-format" target="_blank" rel="noopener noreferrer" class="">pluggable table format</a> framework that opens up the powerful storage engine capabilities beyond Hudi’s native storage format to other table formats like Apache Iceberg and Delta Lake. This framework represents a fundamental shift in how Hudi approaches table format support, enabling native integration of multiple formats and giving you a unified system with total read-write compatibility across formats.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="vision-and-design">Vision and Design<a href="https://hudi.apache.org/blog/2025/11/25/apache-hudi-release-1-1-announcement#vision-and-design" class="hash-link" aria-label="Direct link to Vision and Design" title="Direct link to Vision and Design" translate="no">​</a></h3>
<p>The table format landscape in the modern lakehouse ecosystem is diverse and evolving. Like a game of rock-paper-scissors, different formats—Hudi, Iceberg, Delta Lake—each have unique strengths for specific use cases. Rather than forcing a one-size-fits-all approach, Hudi 1.1 introduces a pluggable table format framework that embraces the open lakehouse ecosystem and prevents vendor lock-in.</p>
<p>The framework is built on a clean abstraction layer that decouples Hudi’s core capabilities—transaction management, indexing, concurrency control, and table services—from the specific storage format used for data files. At the heart of this design is the <code>HoodieTableFormat</code> interface, which different format implementations can extend.</p>
<p><img decoding="async" loading="lazy" alt="pluggable table format" src="https://hudi.apache.org/assets/images/1-pluggable-TF-36f7e26bf8dc4a479bcaff713a24debd.png" width="894" height="665" class="img_ev3q"></p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="key-architectural-components">Key Architectural Components<a href="https://hudi.apache.org/blog/2025/11/25/apache-hudi-release-1-1-announcement#key-architectural-components" class="hash-link" aria-label="Direct link to Key Architectural Components" title="Direct link to Key Architectural Components" translate="no">​</a></h3>
<ul>
<li class="">Storage engine: Hudi’s storage engine capabilities, such as timeline management, concurrency control mechanisms, indexes, and table services, can work across multiple table formats</li>
<li class="">Pluggable adapters: Format-specific implementations handle the generation of conforming metadata upon writes</li>
</ul>
<p>Hudi’s artifact provides support for the native Hudi format, while <a href="https://xtable.apache.org/" target="_blank" rel="noopener noreferrer" class="">Apache XTable (incubating)</a> supplies pluggable format adapters. For example, <a href="https://github.com/apache/incubator-xtable/pull/723" target="_blank" rel="noopener noreferrer" class="">this XTable PR</a> implements the Iceberg adapter to allow you to add dependencies to your running pipelines as needed. This architecture enables organizations to choose the right format for each use case while maintaining a unified operational experience and leveraging Hudi’s sophisticated storage engine across all of them.</p>
<p>In the 1.1 release, the framework comes with native Hudi format support (configured via <code>hoodie.table.format=native</code> by default). Existing users don't need to change anything—tables continue to work exactly as before. The real excitement lies ahead: the framework paves the way for supporting additional formats like Iceberg and Delta Lake. Imagine writing high-frequency updates to a Hudi table efficiently with Hudi's record-level indexing capability while maintaining Iceberg metadata through the Iceberg adapter, which supports a wide range of catalogs for reads. The pluggable table format framework in 1.1 makes such usage patterns possible—a game-changer for organizations that need flexibility and openness in their data architecture.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="indexing-improvementsfaster-and-smarter-lookups">Indexing Improvements—Faster and Smarter Lookups<a href="https://hudi.apache.org/blog/2025/11/25/apache-hudi-release-1-1-announcement#indexing-improvementsfaster-and-smarter-lookups" class="hash-link" aria-label="Direct link to Indexing Improvements—Faster and Smarter Lookups" title="Direct link to Indexing Improvements—Faster and Smarter Lookups" translate="no">​</a></h2>
<p>Hudi’s indexing subsystem is one of its most powerful features, enabling fast record lookups during writes and efficient data skipping during reads.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="partitioned-record-index">Partitioned Record Index<a href="https://hudi.apache.org/blog/2025/11/25/apache-hudi-release-1-1-announcement#partitioned-record-index" class="hash-link" aria-label="Direct link to Partitioned Record Index" title="Direct link to Partitioned Record Index" translate="no">​</a></h3>
<p>Since version 0.14.0, Hudi has supported a global record index in the indexing subsystem—a breakthrough that enables blazing-fast lookups on large datasets. While this is ideal for globally unique identifiers like order IDs or SSNs, many scenarios only require uniqueness within a partition—for example, user events partitioned by date. Hudi 1.1 introduces the <a href="https://hudi.apache.org/docs/indexes#record-index" target="_blank" rel="noopener noreferrer" class="">partitioned record index</a>, a non-global variant of the record index that works with the combination of partition path and record key, leveraging partition information to prune irrelevant partitions during lookups and dramatically reducing the search space, and thus achieving efficient lookups even on very large datasets.</p>
<div class="language-sql codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-sql codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#F8F8F2"><span class="token comment" style="color:rgb(98, 114, 164)">-- Spark SQL: Create table with partitioned record index</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">CREATE</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">TABLE</span><span class="token plain"> user_activity </span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">  user_id STRING</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">  activity_type STRING</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">timestamp</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">BIGINT</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">  event_date </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">DATE</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">USING</span><span class="token plain"> hudi</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">TBLPROPERTIES </span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token string" style="color:rgb(255, 121, 198)">'primaryKey'</span><span class="token plain"> </span><span class="token operator">=</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">'user_id'</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token string" style="color:rgb(255, 121, 198)">'preCombineField'</span><span class="token plain"> </span><span class="token operator">=</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">'timestamp'</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token comment" style="color:rgb(98, 114, 164)">-- Enable partitioned record index</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token string" style="color:rgb(255, 121, 198)">'hoodie.metadata.record.level.index.enable'</span><span class="token plain"> </span><span class="token operator">=</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">'true'</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token string" style="color:rgb(255, 121, 198)">'hoodie.index.type'</span><span class="token plain"> </span><span class="token operator">=</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">'RECORD_LEVEL_INDEX'</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">PARTITIONED </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">BY</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token plain">event_date</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><br></span></code></pre></div></div>
<p>The partitioned record index enables index lookups that scale proportionally with partition size—file group accesses correlate directly to the data partition size, optimizing performance across heterogeneous data distributions. The design also supports future clustering operations that can dynamically expand file groups within partitions as they grow.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="partition-level-bucket-index">Partition-level Bucket Index<a href="https://hudi.apache.org/blog/2025/11/25/apache-hudi-release-1-1-announcement#partition-level-bucket-index" class="hash-link" aria-label="Direct link to Partition-level Bucket Index" title="Direct link to Partition-level Bucket Index" translate="no">​</a></h3>
<p>The bucket index is a popular choice for high-throughput write workloads because it eliminates expensive record lookups by deterministically mapping keys to file groups. However, the existing bucket index has a key limitation: once you set the number of buckets, changing it requires rewriting the entire table.</p>
<p>The 1.1 release introduces partition-level bucket index, which enables different bucket counts for different partitions using regex-based rules. This design allows tables to adapt as data volumes change over time—for example, older, smaller partitions can use fewer buckets while newer, larger partitions can have more.</p>
<div class="language-sql codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-sql codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#F8F8F2"><span class="token comment" style="color:rgb(98, 114, 164)">-- Spark SQL: Create table with partition-level bucket index</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">CREATE</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">TABLE</span><span class="token plain"> sales_transactions </span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">  transaction_id </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">BIGINT</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">  user_id </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">BIGINT</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">  amount </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">DOUBLE</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">  transaction_date </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">DATE</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">USING</span><span class="token plain"> hudi</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">TBLPROPERTIES </span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token string" style="color:rgb(255, 121, 198)">'primaryKey'</span><span class="token plain"> </span><span class="token operator">=</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">'transaction_id'</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token comment" style="color:rgb(98, 114, 164)">-- Partition-level bucket index</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token string" style="color:rgb(255, 121, 198)">'hoodie.index.type'</span><span class="token plain"> </span><span class="token operator">=</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">'BUCKET'</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token string" style="color:rgb(255, 121, 198)">'hoodie.bucket.index.hash.field'</span><span class="token plain"> </span><span class="token operator">=</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">'transaction_id'</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token string" style="color:rgb(255, 121, 198)">'hoodie.bucket.index.partition.rule.type'</span><span class="token plain"> </span><span class="token operator">=</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">'regex'</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token string" style="color:rgb(255, 121, 198)">'hoodie.bucket.index.partition.expressions'</span><span class="token plain"> </span><span class="token operator">=</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">'2023-.*,16;2024-.*,32;2025-.*,64'</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token string" style="color:rgb(255, 121, 198)">'hoodie.bucket.index.num.buckets'</span><span class="token plain"> </span><span class="token operator">=</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">'8'</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">PARTITIONED </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">BY</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token plain">transaction_date</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><br></span></code></pre></div></div>
<p>The partition-level bucket index is ideal for time-series data where partition sizes vary significantly over time. The adaptive bucket sizing helps you maintain optimal write performance as your data volume changes. See the <a href="https://hudi.apache.org/docs/indexes#additional-writer-side-indexes" target="_blank" rel="noopener noreferrer" class="">docs</a> and <a href="https://github.com/apache/hudi/blob/master/rfc/rfc-89/rfc-89.md" target="_blank" rel="noopener noreferrer" class="">RFC 89</a> for more information.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="indexing-performance-optimizations">Indexing Performance Optimizations<a href="https://hudi.apache.org/blog/2025/11/25/apache-hudi-release-1-1-announcement#indexing-performance-optimizations" class="hash-link" aria-label="Direct link to Indexing Performance Optimizations" title="Direct link to Indexing Performance Optimizations" translate="no">​</a></h3>
<p>Beyond new indexes, Hudi 1.1 delivers substantial performance improvements for metadata table operations:</p>
<ul>
<li class="">HFile block cache and prefetching: The new block cache stores recently accessed data blocks in memory, avoiding repeated reads from storage. For smaller HFiles, Hudi prefetches the entire file upfront rather than making multiple read requests. Benchmarks show approximately 4x speedup for repeated lookups, enabled by default.</li>
</ul>
<p><img decoding="async" loading="lazy" alt="metadata table key lookup" src="https://hudi.apache.org/assets/images/2-metadata-table-lookup-451d218e2a4fc0aac40b6d3c37522572.png" width="960" height="540" class="img_ev3q"></p>
<ul>
<li class="">HFile Bloom filter: Adding Bloom filters to HFiles enables Hudi to quickly determine whether a key might exist in a file before fetching data blocks, avoiding unnecessary I/O and dramatically speeding up point lookups. You can enable it with <code>hoodie.metadata.bloom.filter.enable=true</code>.</li>
</ul>
<p>These optimizations compound to make the metadata table significantly faster, directly improving both write and read performance across your Hudi tables. Additionally, Hudi 1.1 adds its own native HFile writer implementation, eliminating the dependency on HBase libraries. This refactoring significantly reduces the Hudi bundle size and provides the foundation for future HFile performance optimizations.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="faster-clustering-with-parquet-file-binary-copy">Faster Clustering with Parquet File Binary Copy<a href="https://hudi.apache.org/blog/2025/11/25/apache-hudi-release-1-1-announcement#faster-clustering-with-parquet-file-binary-copy" class="hash-link" aria-label="Direct link to Faster Clustering with Parquet File Binary Copy" title="Direct link to Faster Clustering with Parquet File Binary Copy" translate="no">​</a></h2>
<p>Clustering reorganizes data to improve query performance, but traditional approaches are expensive—decompressing, decoding, transforming, re-encoding, and re-compressing data even when no transformation is needed.</p>
<p>Hudi 1.1 implements Parquet file binary copy for clustering operations. Instead of processing records, this optimization directly copies Parquet RowGroups from source to destination files when schema-compatible, eliminating redundant transformations entirely.</p>
<p><img decoding="async" loading="lazy" alt="parquet binary copy" src="https://hudi.apache.org/assets/images/3-binary-copy-d58175c8f9cbd5817d1ace637a617a18.png" width="739" height="407" class="img_ev3q"></p>
<p>On 100GB test data, using Parquet file binary copy achieved 15x faster execution (18 minutes → 1.2 minutes) and 95% reduction in compute (28.7 task-hours → 1.3 task-hours) compared to the normal rewriting of Parquet files. Real-world validation with 1.7TB datasets (300 columns) showed approximately 5x performance improvement (35 min → 7.7 min) with CPU usage dropping from 90% to 60%.</p>
<p><img decoding="async" loading="lazy" alt="parquet binary copy chart" src="https://hudi.apache.org/assets/images/4-binary-copy-chart-f7366c95b72eb12b3ce39cc1b83bfcff.png" width="960" height="540" class="img_ev3q"></p>
<p>The optimization is currently supported for Copy-on-Write tables and enabled automatically when safe, with Hudi intelligently falling back to traditional clustering when schema reconciliation is required. You may refer to <a href="https://github.com/apache/hudi/pull/13365" target="_blank" rel="noopener noreferrer" class="">this PR</a> for more detail.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="storage-based-lock-providereliminating-external-dependencies-for-concurrent-writers">Storage-Based Lock Provider—Eliminating External Dependencies for Concurrent Writers<a href="https://hudi.apache.org/blog/2025/11/25/apache-hudi-release-1-1-announcement#storage-based-lock-providereliminating-external-dependencies-for-concurrent-writers" class="hash-link" aria-label="Direct link to Storage-Based Lock Provider—Eliminating External Dependencies for Concurrent Writers" title="Direct link to Storage-Based Lock Provider—Eliminating External Dependencies for Concurrent Writers" translate="no">​</a></h2>
<p>Multi-writer concurrency is critical for production data lakehouses, where multiple jobs need to write to the same table simultaneously. Historically, enabling multi-writer support in Hudi required setting up external lock providers like AWS DynamoDB, Apache Zookeeper, or Hive Metastore. While these work well, they add operational complexity—you need to provision, maintain, and monitor additional infrastructure.</p>
<p>Hudi 1.1 introduces a storage-based lock provider that eliminates this dependency entirely by managing concurrency directly using the <code>.hoodie/</code> directory in your table's storage layer.</p>
<p><img decoding="async" loading="lazy" alt="storage based lock provider" src="https://hudi.apache.org/assets/images/5-storage-based-lp-8528a28ca24084f9c01e550a1c312a36.png" width="708" height="373" class="img_ev3q"></p>
<p>The implementation uses conditional writes on a single lock file under <code>.hoodie/.locks/</code> to ensure only one writer holds the lock at a time, with heartbeat-based renewal and automatic expiration for fault tolerance. To use the storage-based lock provider, you need to add the corresponding Hudi cloud bundle (<code>hudi-aws-bundle</code> for S3 and <code>hudi-gcp-bundle</code> for GCS) and set the following configuration:</p>
<div class="language-properties codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-properties codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#F8F8F2"><span class="token plain">hoodie.write.lock.provider=org.apache.hudi.client.transaction.lock.StorageBasedLockProvider</span><br></span></code></pre></div></div>
<p>This approach eliminates the need for DynamoDB, ZooKeeper, or Hive Metastore dependencies, reducing operational costs and infrastructure complexity. The cloud-native design works directly with S3 or GCS storage features, with support for additional storage systems planned, making Hudi easier to operate at scale in cloud-native environments. Check out the <a href="https://hudi.apache.org/docs/concurrency_control#storage-based-lock-provider" target="_blank" rel="noopener noreferrer" class="">docs</a> and <a href="https://github.com/apache/hudi/blob/master/rfc/rfc-91/rfc-91.md" target="_blank" rel="noopener noreferrer" class="">RFC 91</a> for more detail.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="use-merge-modes-and-custom-mergerssay-goodbye-to-payload-classes">Use Merge Modes and Custom Mergers—Say Goodbye to Payload Classes<a href="https://hudi.apache.org/blog/2025/11/25/apache-hudi-release-1-1-announcement#use-merge-modes-and-custom-mergerssay-goodbye-to-payload-classes" class="hash-link" aria-label="Direct link to Use Merge Modes and Custom Mergers—Say Goodbye to Payload Classes" title="Direct link to Use Merge Modes and Custom Mergers—Say Goodbye to Payload Classes" translate="no">​</a></h2>
<p>A core design principle of Hudi is enabling the storage layer to understand how to merge updates to the same record key, even when changes arrive out of order—a common scenario with mobile apps, IoT devices, and distributed systems. Prior to Hudi 1.1, record merging logic was primarily implemented through payload classes, which were fragmented and lacked standardized semantics.</p>
<p>Hudi 1.1 deprecates payload classes and encourages users to adopt the new APIs introduced since 1.0 for record merging: merge modes and the <code>HoodieRecordMerger</code> interface.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="merge-modesdeclarative-record-merging">Merge Modes—Declarative Record Merging<a href="https://hudi.apache.org/blog/2025/11/25/apache-hudi-release-1-1-announcement#merge-modesdeclarative-record-merging" class="hash-link" aria-label="Direct link to Merge Modes—Declarative Record Merging" title="Direct link to Merge Modes—Declarative Record Merging" translate="no">​</a></h3>
<p>For common use cases, the <code>COMMIT_TIME_ORDERING</code> and <code>EVENT_TIME_ORDERING</code> merge modes provide a declarative way to specify merge behavior:</p>
<table><thead><tr><th style="text-align:left">Merge mode</th><th style="text-align:left">What does it do?</th></tr></thead><tbody><tr><td style="text-align:left"><code>COMMIT_TIME_ORDERING</code></td><td style="text-align:left">Picks the record with the highest completion time/instant as the final merge result (standard relational semantics or arrival time processing)</td></tr><tr><td style="text-align:left"><code>EVENT_TIME_ORDERING</code></td><td style="text-align:left">Picks the record with the highest value on a user-specified ordering field as the final merge result. Enables event time processing semantics for handling late-arriving data without corrupting record state.</td></tr></tbody></table>
<p>The default behavior is adaptive: if no ordering field (<code>hoodie.table.ordering.fields</code>) is configured, Hudi defaults to <code>COMMIT_TIME_ORDERING</code>; if one or more ordering fields are set, it uses <code>EVENT_TIME_ORDERING</code>. This makes Hudi work out-of-the-box for simple use cases while still supporting event-time ordering when needed.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="custom-mergersthe-flexible-approach">Custom Mergers—The Flexible Approach<a href="https://hudi.apache.org/blog/2025/11/25/apache-hudi-release-1-1-announcement#custom-mergersthe-flexible-approach" class="hash-link" aria-label="Direct link to Custom Mergers—The Flexible Approach" title="Direct link to Custom Mergers—The Flexible Approach" translate="no">​</a></h3>
<p>For complex merging logic—such as field-level reconciliation, aggregating counters, or preserving audit fields—the <code>HoodieRecordMerger</code> interface provides a modern, engine-native alternative to payload classes. You need to set the merge mode to <code>CUSTOM</code> and provide your own implementation of <code>HoodieRecordMerger</code>. By using the new API, you can achieve consistent merging across all code paths: precombine, updating writes, compaction, and snapshot reads—you are strongly encouraged to migrate to the new APIs. See <a href="https://hudi.apache.org/docs/record_merger" target="_blank" rel="noopener noreferrer" class="">the docs</a> for more details. For migration guidance, see the <a href="https://hudi.apache.org/releases/release-1.1#release-111" target="_blank" rel="noopener noreferrer" class="">release notes</a> and <a href="https://github.com/apache/hudi/pull/13499" target="_blank" rel="noopener noreferrer" class="">RFC-97</a>.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="apache-spark-integration-improvements">Apache Spark Integration Improvements<a href="https://hudi.apache.org/blog/2025/11/25/apache-hudi-release-1-1-announcement#apache-spark-integration-improvements" class="hash-link" aria-label="Direct link to Apache Spark Integration Improvements" title="Direct link to Apache Spark Integration Improvements" translate="no">​</a></h2>
<p>Spark remains one of the most popular engines for working with Hudi tables, and the 1.1 release brings several important enhancements.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="spark-40-support">Spark 4.0 Support<a href="https://hudi.apache.org/blog/2025/11/25/apache-hudi-release-1-1-announcement#spark-40-support" class="hash-link" aria-label="Direct link to Spark 4.0 Support" title="Direct link to Spark 4.0 Support" translate="no">​</a></h3>
<p>Spark 4.0 brought significant performance gains for ML/AI workloads, smarter query optimization with automatic join strategy switching, dynamic partition skew mitigation, and enhanced streaming capabilities. Hudi 1.1 adds Spark 4.0 support to unlock these improvements for working with Hudi tables. To get started, use the new <code>hudi-spark4.0-bundle_2.13:1.1.1</code> artifact in your dependency list.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="metadata-table-streaming-writes">Metadata Table Streaming Writes<a href="https://hudi.apache.org/blog/2025/11/25/apache-hudi-release-1-1-announcement#metadata-table-streaming-writes" class="hash-link" aria-label="Direct link to Metadata Table Streaming Writes" title="Direct link to Metadata Table Streaming Writes" translate="no">​</a></h3>
<p>Hudi 1.1 introduces streaming writes to the metadata table, unifying data and metadata writes into a single RDD execution chain. The key design generates metadata records directly during data writes in parallel across executors, eliminating redundant file lookups that previously created bottlenecks and enhancing reliability when performing stage retries in Spark.</p>
<p><img decoding="async" loading="lazy" alt="spark upsert time chart" src="https://hudi.apache.org/assets/images/6-spark-upsert-write-time-chart-734ca6975e38a28de049809099a99caa.png" width="960" height="540" class="img_ev3q"></p>
<p>A benchmark with update-intensive workloads showed that this 1.1 feature delivered about 18% faster write times for tables with record index, compared to Hudi 1.0. The feature is enabled by default for Spark writers.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="new-and-enhanced-sql-procedures">New and Enhanced SQL Procedures<a href="https://hudi.apache.org/blog/2025/11/25/apache-hudi-release-1-1-announcement#new-and-enhanced-sql-procedures" class="hash-link" aria-label="Direct link to New and Enhanced SQL Procedures" title="Direct link to New and Enhanced SQL Procedures" translate="no">​</a></h3>
<p>Hudi 1.1 expands the <a href="https://hudi.apache.org/docs/procedures" target="_blank" rel="noopener noreferrer" class="">SQL procedure</a> library with useful additions and enhanced capabilities for table management and observability, bringing operational capabilities directly into Spark SQL.</p>
<p>The new procedures, <code>show_cleans</code>, <code>show_clean_plans</code>, and <code>show_cleans_metadata</code>, provide visibility into cleaning operations:</p>
<div class="language-sql codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-sql codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#F8F8F2"><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">CALL</span><span class="token plain"> show_cleans</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">table</span><span class="token plain"> </span><span class="token operator">=</span><span class="token operator">&gt;</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">'hudi_table'</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">limit</span><span class="token plain"> </span><span class="token operator">=</span><span class="token operator">&gt;</span><span class="token plain"> </span><span class="token number">10</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">CALL</span><span class="token plain"> show_clean_plans</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">table</span><span class="token plain"> </span><span class="token operator">=</span><span class="token operator">&gt;</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">'hudi_table'</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">limit</span><span class="token plain"> </span><span class="token operator">=</span><span class="token operator">&gt;</span><span class="token plain"> </span><span class="token number">10</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">CALL</span><span class="token plain"> show_cleans_metadata</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">table</span><span class="token plain"> </span><span class="token operator">=</span><span class="token operator">&gt;</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">'hudi_table'</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">limit</span><span class="token plain"> </span><span class="token operator">=</span><span class="token operator">&gt;</span><span class="token plain"> </span><span class="token number">10</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><br></span></code></pre></div></div>
<p>The enhanced <code>run_clustering</code> procedure supports partition filtering with regex patterns:</p>
<div class="language-sql codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-sql codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#F8F8F2"><span class="token comment" style="color:rgb(98, 114, 164)">-- Cluster all 2025 partitions matching a pattern</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">CALL</span><span class="token plain"> run_clustering</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">table</span><span class="token plain"> </span><span class="token operator">=</span><span class="token operator">&gt;</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">'hudi_table'</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">  partition_regex_pattern </span><span class="token operator">=</span><span class="token operator">&gt;</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">'2025-.*'</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><br></span></code></pre></div></div>
<p>All <code>show</code> procedures, where applicable, were enhanced with <code>path</code> and <code>filter</code> parameters. <code>path</code> helps when <code>table_name</code> is not able to identify a table properly. <code>filter</code> can support advanced predicate expressions. For example:</p>
<div class="language-sql codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-sql codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#F8F8F2"><span class="token comment" style="color:rgb(98, 114, 164)">-- Find large files in recent partitions</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">CALL</span><span class="token plain"> show_file_status</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">  path </span><span class="token operator">=</span><span class="token operator">&gt;</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">'/data/warehouse/transactions'</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">  filter </span><span class="token operator">=</span><span class="token operator">&gt;</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"partition LIKE '2025-11%' AND file_size &gt; 524288000"</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><br></span></code></pre></div></div>
<p>The new and enhanced SQL procedures bring table management directly into Spark SQL, streamlining operations for SQL-focused workflows.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="apache-flink-integration-improvements">Apache Flink Integration Improvements<a href="https://hudi.apache.org/blog/2025/11/25/apache-hudi-release-1-1-announcement#apache-flink-integration-improvements" class="hash-link" aria-label="Direct link to Apache Flink Integration Improvements" title="Direct link to Apache Flink Integration Improvements" translate="no">​</a></h2>
<p>Flink is a popular choice for real-time data pipelines, and Hudi 1.1 brings substantial improvements to the Flink integration.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="flink-20-support">Flink 2.0 Support<a href="https://hudi.apache.org/blog/2025/11/25/apache-hudi-release-1-1-announcement#flink-20-support" class="hash-link" aria-label="Direct link to Flink 2.0 Support" title="Direct link to Flink 2.0 Support" translate="no">​</a></h3>
<p>Hudi 1.1 brings support for Flink 2.0, the first major Flink release in nine years. Flink 2.0 introduced disaggregated state storage (ForSt) that decouples state from compute for unlimited scalability, asynchronous state execution for improved resource utilization, adaptive broadcast join for efficient query processing, and materialized tables for simplified stream-batch unification. Use the new <code>hudi-flink2.0-bundle:1.1.1</code> artifact to get started.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="engine-native-record-support">Engine-Native Record Support<a href="https://hudi.apache.org/blog/2025/11/25/apache-hudi-release-1-1-announcement#engine-native-record-support" class="hash-link" aria-label="Direct link to Engine-Native Record Support" title="Direct link to Engine-Native Record Support" translate="no">​</a></h3>
<p>Hudi 1.1 eliminates expensive Avro conversions by processing Flink's native <code>RowData</code> format directly, enabling zero-copy operations throughout the pipeline. This automatic change (no configuration required) delivers 2-3x improvement in write and read performance on average compared to Hudi 1.0.</p>
<p><img decoding="async" loading="lazy" alt="flink throughput chart" src="https://hudi.apache.org/assets/images/7-flink-write-throughput-chart-040f5478de7a2bf3fa8ddb03ffa499c0.png" width="960" height="540" class="img_ev3q"></p>
<p>The above shows a benchmark that inserted 500 million records with a schema of 1 STRING and 10 BIGINT fields: Hudi 1.1 achieved 235.3k records per second and Hudi 1.0 67k records per second—over 3 times higher throughput.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="buffer-sort">Buffer Sort<a href="https://hudi.apache.org/blog/2025/11/25/apache-hudi-release-1-1-announcement#buffer-sort" class="hash-link" aria-label="Direct link to Buffer Sort" title="Direct link to Buffer Sort" translate="no">​</a></h3>
<p>For append-only tables, Hudi 1.1 introduces in-memory buffer sorting that pre-sorts records before flushing to Parquet. This delivers 15-30% better compression and faster queries through better min/max filtering. You can enable this feature with <code>write.buffer.sort.enabled=true</code> and specify sort keys via <code>write.buffer.sort.keys</code> (e.g., "timestamp,event_type"). You may also adjust the buffer size for sorting via <code>write.buffer.size</code> (default 1000 records).</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="new-integration-apache-polaris-incubating">New Integration: Apache Polaris (Incubating)<a href="https://hudi.apache.org/blog/2025/11/25/apache-hudi-release-1-1-announcement#new-integration-apache-polaris-incubating" class="hash-link" aria-label="Direct link to New Integration: Apache Polaris (Incubating)" title="Direct link to New Integration: Apache Polaris (Incubating)" translate="no">​</a></h2>
<p><a href="https://polaris.apache.org/" target="_blank" rel="noopener noreferrer" class="">Polaris (incubating)</a> is an open-source catalog for lakehouse platforms that provides multi-engine interoperability and unified governance across diverse table formats and query engines. Its key feature is enabling data teams to use multiple engines—Spark, Trino, Dremio, Flink, Presto—on a single copy of data with consistent metadata, governed openly by a diverse committee including Snowflake, AWS, Google Cloud, Azure, and others to prevent vendor lock-in.</p>
<p>Hudi 1.1 introduces <a href="https://hudi.apache.org/docs/catalog_polaris" target="_blank" rel="noopener noreferrer" class="">native integration with Polaris</a> (pending a Polaris release that includes <a href="https://github.com/apache/polaris/pull/1862" target="_blank" rel="noopener noreferrer" class="">this PR</a>), allowing users to register Hudi tables in the Polaris catalog and query them from any Polaris-compatible engine, simplifying multi-engine workflows and providing centralized role-based access control that works uniformly across S3, Azure Blob Storage, and Google Cloud Storage.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="whats-nextjoin-us-in-building-the-future">What’s Next—Join Us in Building the Future<a href="https://hudi.apache.org/blog/2025/11/25/apache-hudi-release-1-1-announcement#whats-nextjoin-us-in-building-the-future" class="hash-link" aria-label="Direct link to What’s Next—Join Us in Building the Future" title="Direct link to What’s Next—Join Us in Building the Future" translate="no">​</a></h2>
<p>The future of Hudi is incredibly exciting, and we're building it together with a vibrant, global community of contributors. Building on the strong foundation of 1.1, we're actively developing transformative AI/ML-focused capabilities for Hudi 1.2 and beyond—unstructured data types and column groups for efficient storage of embeddings and documents, Lance, Vortex, blob-optimized Parquet support, and vector search capabilities for lakehouse tables. This is just the beginning—we're reimagining what's possible in the lakehouse, from multi-format interoperability to next-generation AI/ML workloads, and we need your ideas, code, and creativity to make it happen.</p>
<p>Join us in building the future. Check out the <a href="https://hudi.apache.org/releases/release-1.1#release-111" target="_blank" rel="noopener noreferrer" class="">1.1 release notes</a> to get started, join our <a href="https://hudi.apache.org/slack/" target="_blank" rel="noopener noreferrer" class="">Slack space</a>, follow us on <a href="https://www.linkedin.com/company/apache-hudi" target="_blank" rel="noopener noreferrer" class="">LinkedIn</a> and <a href="http://x.com/apachehudi" target="_blank" rel="noopener noreferrer" class="">X (twitter)</a>, and subscribe (send an empty email) to the <a href="mailto:dev@hudi.apache.org" target="_blank" rel="noopener noreferrer" class="">mailing list</a>—let's build the next generation of Hudi together.</p>]]></content:encoded>
            <category>release</category>
            <category>performance</category>
            <category>apache xtable</category>
        </item>
        <item>
            <title><![CDATA[Deep Dive Into Hudi's Indexing Subsystem (Part 2 of 2)]]></title>
            <link>https://hudi.apache.org/blog/2025/11/12/deep-dive-into-hudis-indexing-subsystem-part-2-of-2</link>
            <guid>https://hudi.apache.org/blog/2025/11/12/deep-dive-into-hudis-indexing-subsystem-part-2-of-2</guid>
            <pubDate>Wed, 12 Nov 2025 00:00:00 GMT</pubDate>
            <description><![CDATA[In part 1, we explored how Hudi's metadata table functions as a self-managed, multimodal indexing subsystem. We covered its internal architecture—a partitioned Hudi Merge-on-Read (MOR) table using HFile format for efficient key lookups—and how the files, column stats, and partition stats indexes work together to implement powerful data skipping. These indexes dramatically reduce I/O by pruning partitions and files that don't contain the data your query needs.]]></description>
            <content:encoded><![CDATA[<p>In <a href="https://hudi.apache.org/blog/2025/10/29/deep-dive-into-hudis-indexing-subsystem-part-1-of-2/" target="_blank" rel="noopener noreferrer" class="">part 1</a>, we explored how Hudi's metadata table functions as a self-managed, multimodal indexing subsystem. We covered its internal architecture—a partitioned Hudi Merge-on-Read (MOR) table using HFile format for efficient key lookups—and how the files, column stats, and partition stats indexes work together to implement powerful data skipping. These indexes dramatically reduce I/O by pruning partitions and files that don't contain the data your query needs.</p>
<p>Now in part 2, we'll dive into more specialized indexes that handle different query patterns. We'll look at the record and secondary indexes, which provide exact file locations for equality-matching predicates rather than just skipping irrelevant files. We'll explore expression indexes that optimize queries with inline transformations like <code>from_unixtime()</code> or <code>substring()</code>. Finally, we'll cover async indexing, which lets you build resource-intensive indexes in the background without blocking your active read and write operations.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="equality-matching-with-record-and-secondary-indexes">Equality Matching with Record and Secondary Indexes<a href="https://hudi.apache.org/blog/2025/11/12/deep-dive-into-hudis-indexing-subsystem-part-2-of-2#equality-matching-with-record-and-secondary-indexes" class="hash-link" aria-label="Direct link to Equality Matching with Record and Secondary Indexes" title="Direct link to Equality Matching with Record and Secondary Indexes" translate="no">​</a></h2>
<p>Queries may contain equality-matching predicates like <code>A = X</code> or <code>B IN (X, Y, Z)</code>. While data skipping indexes such as column stats and partition stats help here too, record-level indexing goes further by pinpointing the exact data files containing those values.</p>
<p>Hudi’s multimodal indexing subsystem implements the <a href="https://hudi.apache.org/blog/2023/11/01/record-level-index/" target="_blank" rel="noopener noreferrer" class=""><em>record index</em></a> and <a href="https://hudi.apache.org/blog/2025/04/02/secondary-index/" target="_blank" rel="noopener noreferrer" class=""><em>secondary index</em></a> to meet this need:</p>
<ul>
<li class=""><strong>Record index</strong>: Stores mappings between record keys and the file locations that contain them.</li>
<li class=""><strong>Secondary index</strong>: Stores mappings between non-record-key column values and their corresponding record keys to support mapping to file locations.</li>
</ul>
<p>Note that the record index is located at the <code>record_index/</code> partition of the metadata table. You can create multiple secondary indexes, each for a chosen column, stored under a dedicated partition (prefixed with <code>secondary_index_</code>) in the metadata table.</p>
<p>The record index is a high-performance, general-purpose index that works on both the writer and reader sides. As described in <a href="https://hudi.apache.org/blog/2023/11/01/record-level-index/" target="_blank" rel="noopener noreferrer" class="">this blog</a>, its direct record-location lookup allows Hudi writers to efficiently route updates and deletes to their corresponding file groups in a Hudi table. The secondary index leverages the record index to look up non-record-key columns efficiently. The remainder of this section focuses on the reader side to show how these two indexes optimize equality-matching predicates.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="the-lookup-process">The lookup process<a href="https://hudi.apache.org/blog/2025/11/12/deep-dive-into-hudis-indexing-subsystem-part-2-of-2#the-lookup-process" class="hash-link" aria-label="Direct link to The lookup process" title="Direct link to The lookup process" translate="no">​</a></h3>
<p>Similar to the data skipping process, the query engine parses equality-matching predicates and pushes them down to the Hudi integration component. This component then performs the index lookup and returns the file locations to scan.</p>
<p><img decoding="async" loading="lazy" alt="Record and secondary index lookup process" src="https://hudi.apache.org/assets/images/fig1-86b4d7f2e4b5c535b2ec0036d223065c.png" width="974" height="769" class="img_ev3q"></p>
<p>First, let's consider the record index. When a query with an equality filter like <code>id = '001'</code> runs against a Hudi table where <code>id</code> is the record key, the engine uses the record index to find the exact file locations for that key. The index returns these locations to the query engine, which then plans the read execution.</p>
<p>This direct lookup dramatically optimizes the query by ensuring only the relevant file locations are scanned. For example, on a 400 GB synthetic Hudi table with 20,000 file groups, a query filtering on a single record key saw its execution time drop from 977 seconds to just 12 seconds—a 98% reduction—when using the record index.</p>
<p>Now, let's consider the case when the equality filter is <code>name = 'foo'</code> where <code>name</code> is not a record key field. A secondary index built for the column <code>name</code> will be used for the lookup process. Entries in the secondary index contain mappings of all <code>name</code> values and their corresponding record keys. Because multiple distinct records can have the same <code>name</code> value, the lookup may return multiple record keys. The next step is to look up these returned record keys in the record index to find the enclosing file locations for scanning. As you can tell, the record index must be enabled for using the secondary index.</p>
<p><a href="https://hudi.apache.org/blog/2025/04/02/secondary-index/#benchmarking" target="_blank" rel="noopener noreferrer" class="">A recent TPCDS benchmarking</a> shows that, by using the secondary index, query performance improved by about 45% on average, and the amount of data scanned was reduced by 90%.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="sql-examples">SQL examples<a href="https://hudi.apache.org/blog/2025/11/12/deep-dive-into-hudis-indexing-subsystem-part-2-of-2#sql-examples" class="hash-link" aria-label="Direct link to SQL examples" title="Direct link to SQL examples" translate="no">​</a></h3>
<p>You can specify <code>hoodie.metadata.record.index.enable</code> during table creation to enable the record index for the table:</p>
<div class="language-sql codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-sql codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#F8F8F2"><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">CREATE</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">TABLE</span><span class="token plain"> trips </span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">    ts </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">BIGINT</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">    id STRING</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">    rider STRING</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">    driver STRING</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">    fare </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">DOUBLE</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">    city STRING</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">    state STRING</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">USING</span><span class="token plain"> hudi</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> OPTIONS</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">    primaryKey </span><span class="token operator">=</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">'id'</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">    hoodie</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token plain">metadata</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token plain">record</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">index</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">enable</span><span class="token plain"> </span><span class="token operator">=</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">'true'</span><span class="token plain"> </span><span class="token comment" style="color:rgb(98, 114, 164)">-- enable record index</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">PARTITIONED </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">BY</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token plain">city</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> state</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><br></span></code></pre></div></div>
<p>To create a secondary index on a specific column, you can use <code>CREATE INDEX</code> like this:</p>
<div class="language-sql codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-sql codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#F8F8F2"><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">CREATE</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">INDEX</span><span class="token plain"> driver_idx </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">ON</span><span class="token plain"> trips </span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token plain">driver</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"> </span><span class="token comment" style="color:rgb(98, 114, 164)">-- enable secondary index on column `driver`</span><br></span></code></pre></div></div>
<p>When you write data to the example table, index data gets written to the record index and secondary index partitions in the metadata table, which then accelerates query execution during reads. Check out the <a href="https://hudi.apache.org/docs/sql_ddl" target="_blank" rel="noopener noreferrer" class="">SQL DDL page</a> for more examples.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="expression-index">Expression Index<a href="https://hudi.apache.org/blog/2025/11/12/deep-dive-into-hudis-indexing-subsystem-part-2-of-2#expression-index" class="hash-link" aria-label="Direct link to Expression Index" title="Direct link to Expression Index" translate="no">​</a></h2>
<p>Query predicates often contain expressions that perform inline transformations on columns, such as <code>from_unixtime()</code> or <code>substring()</code>. These expressions prevent a direct match with standard column indexes like column stats or partition stats. To optimize such queries, Hudi provides the <em>expression index</em> that operates on transformed column values. A full list of supported expressions is available in the <a href="https://hudi.apache.org/docs/sql_ddl/#create-expression-index" target="_blank" rel="noopener noreferrer" class="">documentation</a>.</p>
<p>Hudi currently supports two types of expression indexes:</p>
<ul>
<li class=""><strong>Column stats type</strong>: Stores file-level statistics (min, max, null count, value count) for the transformed values after applying the expression.</li>
<li class=""><strong>Bloom filter type</strong>: Stores a file-level bloom filter built from the transformed values after applying the expression.</li>
</ul>
<p>Each expression index—defined by its type, the expression used, and the target column—occupies a dedicated partition within the metadata table, identified by an <code>expr_index_</code> prefix in its partition path.</p>
<p>The column stats expression index functions similarly to a standard column stats index and is effective for data skipping. As the diagram below illustrates, a predicate containing a <code>from_unixtime()</code> expression is processed for lookup, and the corresponding expression index prunes the file list for the query engine.</p>
<p><img decoding="async" loading="lazy" alt="Expression index lookup process" src="https://hudi.apache.org/assets/images/fig2-7177a573864028788685add3456491ec.png" width="860" height="620" class="img_ev3q"></p>
<p>The bloom filter expression index is designed for equality-matching predicates. Unlike the record and secondary indexes, which provide exact file locations, this index uses a bloom filter—a space-efficient data structure for quick presence checks—to prune files. The query planner can skip a file if the bloom filter indicates a target value is definitively not present.</p>
<p>The bloom filter expression index is most effective for high-cardinality columns, where the probability of a "not present" result is higher, allowing more files to be skipped. For low-cardinality columns, the proposed <a href="https://github.com/apache/hudi/blob/master/rfc/rfc-92/rfc-92.md" target="_blank" rel="noopener noreferrer" class="">bitmap index</a> would be more efficient and represents a valuable future extension to Hudi's indexing subsystem.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="sql-examples-1">SQL examples<a href="https://hudi.apache.org/blog/2025/11/12/deep-dive-into-hudis-indexing-subsystem-part-2-of-2#sql-examples-1" class="hash-link" aria-label="Direct link to SQL examples" title="Direct link to SQL examples" translate="no">​</a></h3>
<p>Similar to creating a secondary index, you can create an expression index (column stats type) like this:</p>
<div class="language-sql codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-sql codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#F8F8F2"><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">CREATE</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">INDEX</span><span class="token plain"> ts_date </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">ON</span><span class="token plain"> trips</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">USING</span><span class="token plain"> column_stats</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token plain">ts</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"> </span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">  OPTIONS</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token plain">expr</span><span class="token operator">=</span><span class="token string" style="color:rgb(255, 121, 198)">'from_unixtime'</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> format</span><span class="token operator">=</span><span class="token string" style="color:rgb(255, 121, 198)">'yyyy-MM-dd'</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><br></span></code></pre></div></div>
<p>This example creates a column stats expression index on the column <code>ts</code> with the expression <code>from_unixtime</code> that transforms an epoch timestamp into a date string, allowing effective data skipping based on dates.</p>
<p>You can create a bloom filter expression index similarly:</p>
<div class="language-sql codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-sql codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#F8F8F2"><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">CREATE</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">INDEX</span><span class="token plain"> bloom_idx_rider </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">ON</span><span class="token plain"> trips</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">USING</span><span class="token plain"> bloom_filters</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token plain">rider</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">  OPTIONS</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token plain">expr</span><span class="token operator">=</span><span class="token string" style="color:rgb(255, 121, 198)">'lower'</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><br></span></code></pre></div></div>
<p>This example builds a bloom filter expression index using the lowercase values of column <code>rider</code>, optimizing for predicates that match lowercase rider names. Check out the <a href="https://hudi.apache.org/docs/sql_ddl" target="_blank" rel="noopener noreferrer" class="">SQL DDL page</a> for more examples.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="building-indexes-efficiently-with-the-async-indexer">Building Indexes Efficiently with the Async Indexer<a href="https://hudi.apache.org/blog/2025/11/12/deep-dive-into-hudis-indexing-subsystem-part-2-of-2#building-indexes-efficiently-with-the-async-indexer" class="hash-link" aria-label="Direct link to Building Indexes Efficiently with the Async Indexer" title="Direct link to Building Indexes Efficiently with the Async Indexer" translate="no">​</a></h2>
<p>Hudi provides flexible mechanisms for managing indexes. You can use SQL DDL commands—such as <code>CREATE INDEX</code>, <code>DROP INDEX</code>, and <code>SHOW INDEXES</code>—or programmatically set writer configurations via the Spark DataSource and Flink DataStream APIs. For example, setting <code>hoodie.metadata.index.partition.stats.enable=false</code> during a write operation drops the partition stats index. This action deletes the corresponding partition from the metadata table and skips indexing computations for subsequent writes until the configuration is re-enabled.</p>
<p>Creating a new index can be a resource-intensive operation, particularly for large tables and for indexes with high space complexity. For instance, the space complexity of the column stats index is O(columns × files), while the record index requires O(records) space. When adding such an index to a large table via DDL or a writer configuration, the time-consuming index initialization process must not block ongoing read and write operations.</p>
<p>To address this challenge, Hudi's index management is designed with two key goals: index creation should not block concurrent reads and writes, and once built, an index must serve consistent data up to the latest table commit. Hudi meets these requirements with its <a href="https://hudi.apache.org/docs/metadata_indexing/#setup-async-indexing" target="_blank" rel="noopener noreferrer" class="">async indexing</a> (illustrated below), which builds indexes in the background without interrupting active writers and readers.</p>
<p><img decoding="async" loading="lazy" alt="Async indexing process" src="https://hudi.apache.org/assets/images/fig3-ce0dd7f4352540791995b4b6b9b5387e.png" width="1112" height="814" class="img_ev3q"></p>
<p>The async indexing process consists of two phases: scheduling and execution. First, the scheduler creates an indexing plan that covers data up to the latest data table commit. Next, the executor reads the required file groups from the data table and writes the corresponding index data to the metadata table. While this process runs, concurrent writers can continue ingesting data. The async indexing executor writes index data to base files in the target index partitions in the metadata table, while the ongoing writer append new index data to log files in those partitions. Hudi uses a conflict resolution mechanism to determine if an indexing operation needs to be retried due to concurrent write conflicts.</p>
<p>To manage this concurrency, a lock provider must be configured for both the indexer and the data writers. Upon successful completion, the operation is marked by a completed indexing commit in the Hudi table’s timeline. For future improvements, the metadata table will employ non-blocking concurrency control to gracefully absorb conflicting updates from both indexing and write operations, thus avoiding wasteful retries. You can find configuration examples in the <a href="https://hudi.apache.org/docs/metadata_indexing/#setup-async-indexing" target="_blank" rel="noopener noreferrer" class="">documentation</a>.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="summary">Summary<a href="https://hudi.apache.org/blog/2025/11/12/deep-dive-into-hudis-indexing-subsystem-part-2-of-2#summary" class="hash-link" aria-label="Direct link to Summary" title="Direct link to Summary" translate="no">​</a></h2>
<p>Throughout this two-part series, we've explored how Hudi's indexing subsystem brings database-grade performance to the data lakehouse. In <a href="https://hudi.apache.org/blog/2025/10/29/deep-dive-into-hudis-indexing-subsystem-part-1-of-2/" target="_blank" rel="noopener noreferrer" class="">part 1</a>, we examined the metadata table's architecture and how files, column stats, and partition stats indexes work together to skip irrelevant data. In part 2, we covered specialized indexes—record, secondary, and expression indexes—that provide exact file locations for equality matching and handle transformed predicates. We also looked at async indexing, which lets you add resource-intensive indexes without blocking ongoing operations.</p>
<p>Here's a quick guide for choosing the right indexes for your workload:</p>
<ul>
<li class=""><strong>Files</strong>: Always enabled in the metadata table—provides partition and file lists in the table to facilitate common indexing processes</li>
<li class=""><strong>Column stats and partition stats</strong>: Enable by default and configure <code>hoodie.metadata.index.column.stats.column.list</code> to include only the columns you frequently filter on. These indexes are essential for range predicates and data skipping</li>
<li class=""><strong>Record index</strong>: Enable when you have frequent point lookups on record keys or when you need secondary indexes. The record index also optimizes Hudi's write path by efficiently routing updates and deletes</li>
<li class=""><strong>Secondary index</strong>: Create secondary indexes for non-record-key columns that appear in equality predicates. Each secondary index adds maintenance overhead, so focus on high-value columns</li>
<li class=""><strong>Expression index</strong>: Use expression indexes when queries contain predicates with inline transformations. Choose column stats type for range queries on transformed values, or bloom filter type for equality matching on high-cardinality columns</li>
<li class=""><strong>Async indexing</strong>: Use async indexing when adding indexes to large tables. The async indexer builds indexes in the background, keeping your writers and readers unblocked</li>
</ul>
<p>All indexes are maintained transactionally alongside data writes, ensuring consistency without sacrificing performance. The metadata table uses HFile format for fast point lookups and periodic compaction to keep reads efficient. This design makes Hudi's indexing subsystem both powerful and practical—ready to handle lakehouse-scale data while remaining simple to configure and operate.</p>
<p>As Hudi continues to evolve, the indexing subsystem is designed for extensibility. Upcoming features like the bitmap index for low-cardinality columns and vector search index for AI workloads will further expand its capabilities. By understanding these indexing patterns and following the configuration guidelines in this series, you can build lakehouse tables that deliver the query performance your analytics and data pipelines demand.</p>]]></content:encoded>
            <category>indexing</category>
            <category>data lakehouse</category>
            <category>data skipping</category>
        </item>
        <item>
            <title><![CDATA[How FreeWheel Uses Apache Hudi to Power Its Data Lakehouse]]></title>
            <link>https://hudi.apache.org/blog/2025/11/07/how-freewheel-uses-apache-hudi-to-power-its-data-lakehouse</link>
            <guid>https://hudi.apache.org/blog/2025/11/07/how-freewheel-uses-apache-hudi-to-power-its-data-lakehouse</guid>
            <pubDate>Fri, 07 Nov 2025 00:00:00 GMT</pubDate>
            <description><![CDATA[Talk title slide]]></description>
            <content:encoded><![CDATA[<p><img decoding="async" loading="lazy" alt="Talk title slide" src="https://hudi.apache.org/assets/images/image1-352adf99bd976cab9ea093daab842339.png" width="1257" height="437" class="img_ev3q"></p>
<p><em>This post summarizes a FreeWheel talk from the Apache Hudi Community Sync. Watch the recording on <a href="https://www.youtube.com/watch?v=hQNSf82o3Rk" target="_blank" rel="noopener noreferrer" class="">YouTube</a>.</em></p>
<p><a href="https://www.freewheel.com/" target="_blank" rel="noopener noreferrer" class="">FreeWheel</a>, a division of Comcast, provides advanced video advertising solutions across TV and digital platforms. As the business scaled, FreeWheel faced growing challenges maintaining consistency, freshness, and operational efficiency in its data systems. To address these challenges, the team began transitioning from a legacy Lambda architecture to a modern, <a href="https://hudi.apache.org/" target="_blank" rel="noopener noreferrer" class="">Apache Hudi</a>-powered lakehouse approach.</p>
<p>Their original stack, shown below, used multiple systems like <strong>Presto</strong>, <strong>ClickHouse</strong>, and <strong>Druid</strong> to serve analytical and real-time use cases. However, the architecture had some limitations:</p>
<p><img decoding="async" loading="lazy" alt="Original multi-engine architecture" src="https://hudi.apache.org/assets/images/image2-b8e66c22453fcee2810fc23d8cb480d0.png" width="1999" height="848" class="img_ev3q"></p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="data-freshness-issues">Data freshness issues<a href="https://hudi.apache.org/blog/2025/11/07/how-freewheel-uses-apache-hudi-to-power-its-data-lakehouse#data-freshness-issues" class="hash-link" aria-label="Direct link to Data freshness issues" title="Direct link to Data freshness issues" translate="no">​</a></h2>
<ul>
<li class="">Presto tables had a 3–4 hour delay, which was too slow for operational use cases.</li>
<li class="">Only ClickHouse and Druid offered near‑real‑time access (~5 minutes) but added complexity.</li>
</ul>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="complex-ingestion">Complex ingestion<a href="https://hudi.apache.org/blog/2025/11/07/how-freewheel-uses-apache-hudi-to-power-its-data-lakehouse#complex-ingestion" class="hash-link" aria-label="Direct link to Complex ingestion" title="Direct link to Complex ingestion" translate="no">​</a></h2>
<ul>
<li class="">Data came from logs, CDC streams, files, and databases.</li>
<li class="">Each system had its own ingestion pipeline and refresh logic.</li>
</ul>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="query-performance-bottlenecks">Query performance bottlenecks<a href="https://hudi.apache.org/blog/2025/11/07/how-freewheel-uses-apache-hudi-to-power-its-data-lakehouse#query-performance-bottlenecks" class="hash-link" aria-label="Direct link to Query performance bottlenecks" title="Direct link to Query performance bottlenecks" translate="no">​</a></h2>
<ul>
<li class="">With ~15 PB of data and 20M+ queries/day, scaling across three engines was costly and hard to operate.</li>
</ul>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="use-case-1-lambda-architecture-and-its-drawbacks">Use Case 1: Lambda Architecture and Its Drawbacks<a href="https://hudi.apache.org/blog/2025/11/07/how-freewheel-uses-apache-hudi-to-power-its-data-lakehouse#use-case-1-lambda-architecture-and-its-drawbacks" class="hash-link" aria-label="Direct link to Use Case 1: Lambda Architecture and Its Drawbacks" title="Direct link to Use Case 1: Lambda Architecture and Its Drawbacks" translate="no">​</a></h2>
<p><img decoding="async" loading="lazy" alt="Lambda architecture overview" src="https://hudi.apache.org/assets/images/image3-ecdbeac000b14fa4bf639c7e0daf8654.png" width="1999" height="857" class="img_ev3q"></p>
<p>FreeWheel initially followed a traditional Lambda architecture, which separated the processing of batch and real‑time data. This approach created several problems: it required duplicate pipelines for batch and real‑time processing (leading to inefficient engineering workflows), and it struggled to scale ClickHouse for large aggregates.</p>
<p>By consolidating on Hudi as the table format for both streaming and historical data, FreeWheel unified the storage layer and eliminated duplicate pipelines. Hudi’s <a href="https://hudi.apache.org/docs/write_operations/#upsert" target="_blank" rel="noopener noreferrer" class="">upserts</a> by key and <a href="https://hudi.apache.org/docs/table_types/#incremental-queries" target="_blank" rel="noopener noreferrer" class="">incremental processing</a> make it possible to serve near–real‑time analytics. The result is simpler operations, consistent logic, and a platform that scales with data volume and query complexity.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="use-case-2-real-time-inventory-management">Use Case 2: Real-Time Inventory Management<a href="https://hudi.apache.org/blog/2025/11/07/how-freewheel-uses-apache-hudi-to-power-its-data-lakehouse#use-case-2-real-time-inventory-management" class="hash-link" aria-label="Direct link to Use Case 2: Real-Time Inventory Management" title="Direct link to Use Case 2: Real-Time Inventory Management" translate="no">​</a></h2>
<p><img decoding="async" loading="lazy" alt="Real-time inventory with Hudi" src="https://hudi.apache.org/assets/images/image4-d88e573a6dba93c4a1edd9e87a09fa5f.png" width="1999" height="1621" class="img_ev3q"></p>
<p>Historically, daily ad inventory updates were a significant challenge. This method led to low forecasting accuracy and frequent delivery-performance mismatches.</p>
<p>By modernizing the platform with Hudi, FreeWheel updates inventory within minutes. Order changes are applied as upserts to Hudi tables and become queryable shortly thereafter, dramatically improving forecast accuracy and reducing delivery‑vs‑forecast mismatches.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="use-case-3-scalable-audience-data-processing">Use Case 3: Scalable Audience Data Processing<a href="https://hudi.apache.org/blog/2025/11/07/how-freewheel-uses-apache-hudi-to-power-its-data-lakehouse#use-case-3-scalable-audience-data-processing" class="hash-link" aria-label="Direct link to Use Case 3: Scalable Audience Data Processing" title="Direct link to Use Case 3: Scalable Audience Data Processing" translate="no">​</a></h2>
<p><img decoding="async" loading="lazy" alt="Audience data architecture with Hudi snapshot" src="https://hudi.apache.org/assets/images/image5-61c7ba9d9a0b31e28ce22ae67eb12d08.png" width="941" height="746" class="img_ev3q"></p>
<p>FreeWheel uses Aerospike to ingest audience segments for its online services, which involves handling high‑frequency, real‑time data. However, this setup brought a few key challenges—chiefly, the need for analytical insights on top of real‑time data and the need to efficiently manage bulk loads alongside frequent updates.</p>
<p>To address these challenges, FreeWheel introduced Hudi into the data pipeline. Hudi maintains a snapshot table for all audience data, enabling more flexible and efficient data management. It supports <a href="https://hudi.apache.org/docs/write_operations/#bulk_insert" target="_blank" rel="noopener noreferrer" class="">bulk inserts</a>, <a href="https://hudi.apache.org/docs/write_operations/#upsert" target="_blank" rel="noopener noreferrer" class="">upserts</a>, and change data capture (CDC), enabling smoother handling of updates and large‑scale data loads. Using CDC, large batches of audience updates are applied incrementally to the snapshot. With Hudi in place, the back‑end analytics system became much stronger, while the responsiveness of the online systems was preserved. This setup also improved the stability of the online targeting system, as heavy analytical workloads were moved off the key‑value store, reducing pressure on Aerospike and enhancing overall performance.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="hudi-in-practice-1-billionscale-updates-for-audiencesegment-ingestion">Hudi in practice 1: Billion‑scale updates for audience‑segment ingestion<a href="https://hudi.apache.org/blog/2025/11/07/how-freewheel-uses-apache-hudi-to-power-its-data-lakehouse#hudi-in-practice-1-billionscale-updates-for-audiencesegment-ingestion" class="hash-link" aria-label="Direct link to Hudi in practice 1: Billion‑scale updates for audience‑segment ingestion" title="Direct link to Hudi in practice 1: Billion‑scale updates for audience‑segment ingestion" translate="no">​</a></h2>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="use-case-overview">Use case overview<a href="https://hudi.apache.org/blog/2025/11/07/how-freewheel-uses-apache-hudi-to-power-its-data-lakehouse#use-case-overview" class="hash-link" aria-label="Direct link to Use case overview" title="Direct link to Use case overview" translate="no">​</a></h3>
<p>This implementation showcases how a large‑scale platform ingests and updates audience‑segmentation data at the billion‑record scale using Hudi tables.
The architecture efficiently handles high‑frequency updates across more than 63,000 partitions and a table over 600 TB, with performance optimizations at both the data and infrastructure levels.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="key-architecture-and-design-principles">Key architecture and design principles<a href="https://hudi.apache.org/blog/2025/11/07/how-freewheel-uses-apache-hudi-to-power-its-data-lakehouse#key-architecture-and-design-principles" class="hash-link" aria-label="Direct link to Key architecture and design principles" title="Direct link to Key architecture and design principles" translate="no">​</a></h3>
<p><img decoding="async" loading="lazy" alt="Audience ingestion architecture and scheduler" src="https://hudi.apache.org/assets/images/image6-c6d05709a20d20f096539322b86f933d.png" width="1333" height="1360" class="img_ev3q"></p>
<h4 class="anchor anchorTargetStickyNavbar_Vzrq" id="partitioning-and-orchestration"><strong>Partitioning and orchestration</strong><a href="https://hudi.apache.org/blog/2025/11/07/how-freewheel-uses-apache-hudi-to-power-its-data-lakehouse#partitioning-and-orchestration" class="hash-link" aria-label="Direct link to partitioning-and-orchestration" title="Direct link to partitioning-and-orchestration" translate="no">​</a></h4>
<p>FreeWheel uses the audience‑segment ID as the <a href="https://hudi.apache.org/docs/key_generation" target="_blank" rel="noopener noreferrer" class="">partition key</a>. Each partition can be processed independently, allowing many Spark jobs to run in parallel. Each job upserts data to the Hudi lakehouse table.</p>
<p>A central scheduler allocates work based on input size, priority, and write concurrency limits. This enables dynamic scaling across more than 63,000 partitions, where per-partition input sizes range from 1 million to 100 billion records.</p>
<h4 class="anchor anchorTargetStickyNavbar_Vzrq" id="decoupled-ingestion-pipeline"><strong>Decoupled ingestion pipeline</strong><a href="https://hudi.apache.org/blog/2025/11/07/how-freewheel-uses-apache-hudi-to-power-its-data-lakehouse#decoupled-ingestion-pipeline" class="hash-link" aria-label="Direct link to decoupled-ingestion-pipeline" title="Direct link to decoupled-ingestion-pipeline" translate="no">​</a></h4>
<ul>
<li class="">Scheduler: allocates resources based on input size and supports job priority, <a href="https://hudi.apache.org/docs/concurrency_control/" target="_blank" rel="noopener noreferrer" class="">multi-writer concurrency control</a>, and concurrency planning.</li>
<li class="">Ingestion job: Spark jobs process data and write it to the Hudi segment table in the lakehouse.</li>
</ul>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="challenges-of-input-data-at-scale">Challenges of input data at scale<a href="https://hudi.apache.org/blog/2025/11/07/how-freewheel-uses-apache-hudi-to-power-its-data-lakehouse#challenges-of-input-data-at-scale" class="hash-link" aria-label="Direct link to Challenges of input data at scale" title="Direct link to Challenges of input data at scale" translate="no">​</a></h3>
<ul>
<li class="">Table size: over 600 TB.</li>
<li class="">Partition count: 63,000 audience‑segment partitions.</li>
<li class="">Data skew: massive variation in partition sizes, ranging from 1 million to 100 billion records.</li>
</ul>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="metrics-and-performance-insights">Metrics and performance insights<a href="https://hudi.apache.org/blog/2025/11/07/how-freewheel-uses-apache-hudi-to-power-its-data-lakehouse#metrics-and-performance-insights" class="hash-link" aria-label="Direct link to Metrics and performance insights" title="Direct link to Metrics and performance insights" translate="no">​</a></h3>
<p><img decoding="async" loading="lazy" alt="Ingestion metrics and throughput" src="https://hudi.apache.org/assets/images/image7-a67a3aa2b20416cf15c3229553c626ce.png" width="1999" height="979" class="img_ev3q"></p>
<ul>
<li class="">Cost optimization<!-- -->
<ul>
<li class="">Unit cost on AWS: ~$0.10 per million records updated.</li>
</ul>
</li>
<li class="">Throughput: the pipeline supports up to 12 million upserts per second.</li>
</ul>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="operational-optimizations"><strong>Operational optimizations</strong><a href="https://hudi.apache.org/blog/2025/11/07/how-freewheel-uses-apache-hudi-to-power-its-data-lakehouse#operational-optimizations" class="hash-link" aria-label="Direct link to operational-optimizations" title="Direct link to operational-optimizations" translate="no">​</a></h3>
<ul>
<li class="">Handle S3 throttling by increasing partition parallelism. Hash partition prefixes and coordinate with AWS to raise per‑bucket request caps and remove I/O bottlenecks.</li>
<li class="">Balance SLA and cost with adaptive resource provisioning through the scheduler; choose resources based on input size to keep jobs stable while controlling spend.</li>
<li class="">Deduplicate before commit: group by record key, order by event timestamp, and write only the latest value to reduce churn and speed up writes.</li>
</ul>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="hudi-in-practice-2-realtime-aggregated-ingestion-using-spark-streaming--clustering">Hudi in practice 2: Real‑time aggregated ingestion using Spark Streaming + clustering<a href="https://hudi.apache.org/blog/2025/11/07/how-freewheel-uses-apache-hudi-to-power-its-data-lakehouse#hudi-in-practice-2-realtime-aggregated-ingestion-using-spark-streaming--clustering" class="hash-link" aria-label="Direct link to Hudi in practice 2: Real‑time aggregated ingestion using Spark Streaming + clustering" title="Direct link to Hudi in practice 2: Real‑time aggregated ingestion using Spark Streaming + clustering" translate="no">​</a></h2>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="pipeline-overview">Pipeline overview<a href="https://hudi.apache.org/blog/2025/11/07/how-freewheel-uses-apache-hudi-to-power-its-data-lakehouse#pipeline-overview" class="hash-link" aria-label="Direct link to Pipeline overview" title="Direct link to Pipeline overview" translate="no">​</a></h3>
<p>This implementation showcases an efficient pipeline where Spark Streaming ingests aggregated data into a Hudi lakehouse using the <a href="https://hudi.apache.org/docs/write_operations/#bulk_insert" target="_blank" rel="noopener noreferrer" class="">bulk_insert</a> operation, followed by <a href="https://hudi.apache.org/blog/2021/08/23/async-clustering/" target="_blank" rel="noopener noreferrer" class="">asynchronous clustering</a>.</p>
<p><img decoding="async" loading="lazy" alt="Streaming ingestion and clustering flow" src="https://hudi.apache.org/assets/images/image8-20b16694604281c92df9a8a9079c22b5.png" width="1003" height="445" class="img_ev3q"></p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="data-ingestion-flow">Data ingestion flow<a href="https://hudi.apache.org/blog/2025/11/07/how-freewheel-uses-apache-hudi-to-power-its-data-lakehouse#data-ingestion-flow" class="hash-link" aria-label="Direct link to Data ingestion flow" title="Direct link to Data ingestion flow" translate="no">​</a></h3>
<ul>
<li class="">Kafka: raw events are streamed into Kafka.</li>
<li class="">Spark SQL on Streaming: consumes Kafka messages and performs near‑real‑time aggregations.</li>
<li class="">bulk_insert into Hudi lakehouse: aggregated data is appended using <a href="https://hudi.apache.org/docs/write_operations/#bulk_insert" target="_blank" rel="noopener noreferrer" class="">bulk_insert</a>.</li>
<li class="">Clustering plan generation: clustering plans are created asynchronously.</li>
<li class="">HoodieClusteringJob: a cron job runs hourly to execute <a href="https://hudi.apache.org/docs/clustering/" target="_blank" rel="noopener noreferrer" class="">clustering</a> and consolidate small files.</li>
</ul>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="results-at-a-glance">Results at a glance<a href="https://hudi.apache.org/blog/2025/11/07/how-freewheel-uses-apache-hudi-to-power-its-data-lakehouse#results-at-a-glance" class="hash-link" aria-label="Direct link to Results at a glance" title="Direct link to Results at a glance" translate="no">​</a></h3>
<ul>
<li class="">Massive file reduction: clustering reduced total file count by nearly 90%, minimizing small‑file pressure and improving metadata performance.</li>
<li class="">Write throughput boost: increased by about 114% due to optimized file layout.</li>
<li class="">Faster queries: Presto query performance improved significantly after clustering.</li>
</ul>
<p>However, Spark Streaming is a macro‑batch system, typically executing every one or two minutes. As a result, it does not trigger clustering jobs immediately but instead generates clustering plans for later execution. In production, clustering jobs are scheduled to run hourly and apply only to stable partitions, ensuring compaction and file optimization without impacting real‑time ingestion.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="conclusion">Conclusion<a href="https://hudi.apache.org/blog/2025/11/07/how-freewheel-uses-apache-hudi-to-power-its-data-lakehouse#conclusion" class="hash-link" aria-label="Direct link to Conclusion" title="Direct link to Conclusion" translate="no">​</a></h2>
<p>FreeWheel’s journey with Hudi transformed its data architecture—offering unified access, real‑time freshness, and scalable operations. The team credits Hudi’s community and feature set as key to its success.</p>
<blockquote>
<p>“We’re lucky to choose Hudi as our Lakehouse. Thanks to the powerful Hudi community!” – Bing Jiang</p>
</blockquote>]]></content:encoded>
            <category>data lakehouse</category>
            <category>freewheel</category>
        </item>
        <item>
            <title><![CDATA[Deep Dive Into Hudi’s Indexing Subsystem (Part 1 of 2)]]></title>
            <link>https://hudi.apache.org/blog/2025/10/29/deep-dive-into-hudis-indexing-subsystem-part-1-of-2</link>
            <guid>https://hudi.apache.org/blog/2025/10/29/deep-dive-into-hudis-indexing-subsystem-part-1-of-2</guid>
            <pubDate>Wed, 29 Oct 2025 00:00:00 GMT</pubDate>
            <description><![CDATA[For decades, databases have relied on indexes—specialized data structures—to dramatically improve read and write performance by quickly locating specific records. Apache Hudi extends this fundamental principle to the data lakehouse with a unique and powerful approach. Every Hudi table contains a self-managed metadata table that functions as an indexing subsystem, enabling efficient data skipping and fast record lookups across a wide range of read and write scenarios.]]></description>
            <content:encoded><![CDATA[<p>For decades, databases have relied on indexes—specialized data structures—to dramatically improve read and write performance by quickly locating specific records. Apache Hudi extends this fundamental principle to the data lakehouse with a unique and powerful approach. Every Hudi table contains a self-managed metadata table that functions as an indexing subsystem, enabling efficient data skipping and fast record lookups across a wide range of read and write scenarios.</p>
<p>This two-part series dives into Hudi’s indexing subsystem. Part 1 explains the internal layout and data-skipping capabilities. <a href="https://hudi.apache.org/blog/2025/11/12/deep-dive-into-hudis-indexing-subsystem-part-2-of-2/" target="_blank" rel="noopener noreferrer" class="">Part 2</a> covers advanced features—record, secondary, and expression indexes—and asynchronous index maintenance. By the end, you’ll know how to leverage Hudi’s multimodal index to build more efficient lakehouse tables.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="the-metadata-table">The Metadata Table<a href="https://hudi.apache.org/blog/2025/10/29/deep-dive-into-hudis-indexing-subsystem-part-1-of-2#the-metadata-table" class="hash-link" aria-label="Direct link to The Metadata Table" title="Direct link to The Metadata Table" translate="no">​</a></h2>
<p>Within a Hudi table (the data table), the metadata table itself is a Hudi Merge-on-Read (MOR) table. Unlike a typical data table, it features a specialized layout. The table is physically partitioned by index type, with each partition containing the relevant index entries. For its physical storage, the metadata table uses HFile as the base file format. This choice is deliberate: HFile is exceptionally efficient at handling key lookups—the predominant query pattern for indexing. Let’s explore the partitioned layout and HFile’s internal structure.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="multimodal-indexing">Multimodal indexing<a href="https://hudi.apache.org/blog/2025/10/29/deep-dive-into-hudis-indexing-subsystem-part-1-of-2#multimodal-indexing" class="hash-link" aria-label="Direct link to Multimodal indexing" title="Direct link to Multimodal indexing" translate="no">​</a></h3>
<p>The metadata table is often referred to as a multimodal index because it houses a diverse range of index types, providing versatile capabilities to accelerate various query patterns. The following diagram illustrates the layout of the metadata table and its relationship with the main data table.</p>
<p><img decoding="async" loading="lazy" alt="Metadata table and data table layout" src="https://hudi.apache.org/assets/images/fig1-7461f8a910c9f7c87745a4a5e15c3498.png" width="956" height="650" class="img_ev3q"></p>
<p>The metadata table is located in the <code>.hoodie/metadata/</code> directory under the data table’s base path. It contains partitions for different indexes, such as the files index (under the <code>files/</code> partition) for tracking the data table’s partitions and files, and the column stats index (under the <code>column_stats/</code> partition) for tracking file-level statistics (e.g., min/max values) for specific columns. Each index partition stores mapping entries tailored to its specific purpose.</p>
<p>This partitioned design provides great flexibility, allowing you to enable only the indexes that suit your workload. It also ensures extensibility, making it straightforward to support new index types in the future. For example, the <a href="https://github.com/apache/hudi/blob/master/rfc/rfc-92/rfc-92.md" target="_blank" rel="noopener noreferrer" class="">bitmap index</a> and the vector search index are on the <a href="https://hudi.apache.org/roadmap" target="_blank" rel="noopener noreferrer" class="">roadmap</a> and will be maintained in their own dedicated partitions.</p>
<p>When committing to a data table, the metadata table is updated within the same transactional write. This crucial step ensures that index entries are always synchronized with data table records, upholding data integrity across the table. Therefore, choosing Merge-on-Read (MOR) as the table type for the metadata table is an obvious choice. MOR offers the advantage of absorbing high-frequency write operations, preventing the metadata table’s update process from becoming a bottleneck for overall table writes. To ensure efficient reading, Hudi automatically performs compaction on the metadata table based on its compaction configuration. By default, an inline compaction will be executed every 10 writes to the metadata table, merging accumulated log files with base files to produce a new set of read-optimized base files in HFile format.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="hfile-format">HFile format<a href="https://hudi.apache.org/blog/2025/10/29/deep-dive-into-hudis-indexing-subsystem-part-1-of-2#hfile-format" class="hash-link" aria-label="Direct link to HFile format" title="Direct link to HFile format" translate="no">​</a></h3>
<p>The HFile format stores key-value pairs in a sorted, immutable, and block-indexed way, modeled after Google’s SSTable introduced by the <a href="https://static.googleusercontent.com/media/research.google.com/en//archive/bigtable-osdi06.pdf" target="_blank" rel="noopener noreferrer" class="">Bigtable paper</a>. Here is the description of SSTable quoted from the paper:</p>
<blockquote>
<p>An SSTable provides a persistent, ordered immutable map from keys to values, where both keys and values are arbitrary byte strings. Operations are provided to look up the value associated with a specified key, and to iterate over all key/value pairs in a specified key range. Internally, each SSTable contains a sequence of blocks (typically each block is 64KB in size, but this is configurable). A block index (stored at the end of the SSTable) is used to locate blocks; the index is loaded into memory when the SSTable is opened. A lookup can be performed with a single disk seek: we first find the appropriate block by performing a binary search in the in-memory index, and then reading the appropriate block from disk.</p>
</blockquote>
<p>As you can tell, by implementing the SSTable, HFile is especially efficient at performing random access, which is the primary query pattern for indexing—given a specific piece of information, like a record key or a partition value, return matching results, such as the file ID that contains the record key, or the list of files that belong to the partition.</p>
<p><img decoding="async" loading="lazy" alt="HFile structure" src="https://hudi.apache.org/assets/images/fig2-686d385820b946c158ab5110d3bcbaf8.png" width="647" height="594" class="img_ev3q"></p>
<p>Because the keys in an HFile are stored in lexicographic order, a batched lookup with a common key prefix is also highly efficient, requiring only a sequential read of nearby keys.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="default-behaviors">Default behaviors<a href="https://hudi.apache.org/blog/2025/10/29/deep-dive-into-hudis-indexing-subsystem-part-1-of-2#default-behaviors" class="hash-link" aria-label="Direct link to Default behaviors" title="Direct link to Default behaviors" translate="no">​</a></h3>
<p>When a Hudi table is created, the metadata table will be enabled with three partitions by default: <em>files</em>, <em>column stats</em>, and <em>partition stats</em>:</p>
<ul>
<li class=""><strong>Files</strong>: stores the list of all partitions and the lists of all base files and log files of each partition, located at the <code>files/</code> partition of the metadata table.</li>
<li class=""><strong>Column stats</strong>: stores file-level statistics like min, max, value count, and null count for specified columns, located at the <code>column_stats/</code> partition of the metadata table.</li>
<li class=""><strong>Partition stats</strong>: stores partition-level statistics like min, max, value count, and null count for specified columns, located at the <code>partition_stats/</code> partition of the metadata table.</li>
</ul>
<p>By default, when no column is specified for column_stats and partition_stats, Hudi will index the first 32 columns (controlled by <code>hoodie.metadata.index.column.stats.max.columns.to.index</code>) available in the table schema.</p>
<p>Whenever a new write is performed on the data table, the metadata table will be updated accordingly. For any available index, new index entries will be upserted to its corresponding partition. For example, if the new write creates a new partition in the data table with some new base files, the files partition will be updated and contain the latest partition and file lists. Similarly, the column stats and partition stats partitions will receive new entries indicating the updated statistics for the new files and partitions.</p>
<p>Note that by design, you cannot disable the files partition, as it is a fundamental index that serves both read and write processes. You can still, although not recommended, disable the entire metadata table by setting <code>hoodie.metadata.enable=false</code> during a write.</p>
<p>We will discuss more details about how the default indexes work to improve read and write performance. We will also introduce more indexes supported by the metadata table with usage examples in the following sections.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="data-skipping-with-files-column-stats-and-partition-stats">Data Skipping with Files, Column Stats, and Partition Stats<a href="https://hudi.apache.org/blog/2025/10/29/deep-dive-into-hudis-indexing-subsystem-part-1-of-2#data-skipping-with-files-column-stats-and-partition-stats" class="hash-link" aria-label="Direct link to Data Skipping with Files, Column Stats, and Partition Stats" title="Direct link to Data Skipping with Files, Column Stats, and Partition Stats" translate="no">​</a></h2>
<p>Data skipping is a core optimization technique that avoids unnecessary data scanning. Its most basic form is physical partitioning, where data is organized into directories based on columns like <code>order_date</code> in a customer order table. When a query filters on a partitioned column, the engine uses <em>partition pruning</em> to read only the relevant directories. More advanced techniques store lightweight statistics—such as min/max values—for data within each file. The query engine consults this metadata first; if the stats indicate a file cannot contain the required data, the engine skips reading it entirely. This reduction in I/O is a key strategy for accelerating queries and lowering compute costs.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="the-data-skipping-process">The data skipping process<a href="https://hudi.apache.org/blog/2025/10/29/deep-dive-into-hudis-indexing-subsystem-part-1-of-2#the-data-skipping-process" class="hash-link" aria-label="Direct link to The data skipping process" title="Direct link to The data skipping process" translate="no">​</a></h3>
<p>Hudi’s indexing subsystem implements a multi-level skipping strategy using a combination of indexes. Query engines like Spark or Trino can leverage Hudi’s files, partition stats, and column stats indexes to improve performance dramatically. The process, illustrated in the figure below, unfolds in several stages.</p>
<p><img decoding="async" loading="lazy" alt="Data skipping process flow" src="https://hudi.apache.org/assets/images/fig3-468ec18846bf7194631d838fd9824bcf.png" width="981" height="706" class="img_ev3q"></p>
<p>First, the query engine parses the input SQL and extracts relevant filter predicates, such as <code>price &gt;= 300</code>. These predicates are pushed down to Hudi’s integration component, which manages the index lookup process.</p>
<p>The component then consults the files index to get an initial list of partitions. It prunes this list using the partition stats index, which holds partition-level statistics like min/max values. For example, any partition with a maximum price below 300 is skipped entirely.</p>
<p>After this initial pruning, the component consults the files index again to retrieve the list of data files within the remaining partitions. This file list is pruned further using the column stats index, which provides the same min/max statistics at the file level.</p>
<p>This multi-step process ensures that the query engine reads only the minimum set of files required to satisfy the query, significantly reducing the total amount of data processed.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="sql-examples">SQL examples<a href="https://hudi.apache.org/blog/2025/10/29/deep-dive-into-hudis-indexing-subsystem-part-1-of-2#sql-examples" class="hash-link" aria-label="Direct link to SQL examples" title="Direct link to SQL examples" translate="no">​</a></h3>
<p>The following examples demonstrate data skipping in action. We will create a Hudi table and execute Spark SQL queries against it, starting with both partition and column stats disabled to establish a baseline.</p>
<div class="language-sql codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-sql codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#F8F8F2"><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">CREATE</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">TABLE</span><span class="token plain"> orders </span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">    order_id STRING</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">    price </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">DECIMAL</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token number">12</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token number">2</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">    order_status STRING</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">    update_ts </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">BIGINT</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">    shipping_date </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">DATE</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">    shipping_country STRING</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">USING</span><span class="token plain"> HUDI</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">PARTITIONED </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">BY</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token plain">shipping_country</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">OPTIONS </span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">    primaryKey </span><span class="token operator">=</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">'order_id'</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">    preCombineField </span><span class="token operator">=</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">'update_ts'</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">    hoodie</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token plain">metadata</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">index</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">column</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token plain">stats</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">enable</span><span class="token plain"> </span><span class="token operator">=</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">'false'</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">    hoodie</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token plain">metadata</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">index</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">partition</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token plain">stats</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">enable</span><span class="token plain"> </span><span class="token operator">=</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">'false'</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><br></span></code></pre></div></div>
<p>And insert some sample data:</p>
<div class="language-sql codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-sql codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#F8F8F2"><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">INSERT</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">INTO</span><span class="token plain"> orders </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">VALUES</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token string" style="color:rgb(255, 121, 198)">'ORD001'</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> </span><span class="token number">389.99</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">'PENDING'</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain">    </span><span class="token number">17495166353</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">DATE</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">'2023-01-01'</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">'A'</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token string" style="color:rgb(255, 121, 198)">'ORD002'</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> </span><span class="token number">199.99</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">'CONFIRMED'</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain">  </span><span class="token number">17495167353</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">DATE</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">'2023-01-01'</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">'A'</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token string" style="color:rgb(255, 121, 198)">'ORD003'</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> </span><span class="token number">59.50</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain">  </span><span class="token string" style="color:rgb(255, 121, 198)">'SHIPPED'</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain">    </span><span class="token number">17495168353</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">DATE</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">'2023-01-11'</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">'B'</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token string" style="color:rgb(255, 121, 198)">'ORD004'</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> </span><span class="token number">99.00</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain">  </span><span class="token string" style="color:rgb(255, 121, 198)">'PENDING'</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain">    </span><span class="token number">17495169353</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">DATE</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">'2023-02-09'</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">'B'</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token string" style="color:rgb(255, 121, 198)">'ORD005'</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> </span><span class="token number">19.99</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain">  </span><span class="token string" style="color:rgb(255, 121, 198)">'PENDING'</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain">    </span><span class="token number">17495170353</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">DATE</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">'2023-06-12'</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">'C'</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token string" style="color:rgb(255, 121, 198)">'ORD006'</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> </span><span class="token number">5.99</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain">   </span><span class="token string" style="color:rgb(255, 121, 198)">'SHIPPED'</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain">    </span><span class="token number">17495171353</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">DATE</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">'2023-07-31'</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">'C'</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><br></span></code></pre></div></div>
<h4 class="anchor anchorTargetStickyNavbar_Vzrq" id="only-the-files-index">Only the files index<a href="https://hudi.apache.org/blog/2025/10/29/deep-dive-into-hudis-indexing-subsystem-part-1-of-2#only-the-files-index" class="hash-link" aria-label="Direct link to Only the files index" title="Direct link to Only the files index" translate="no">​</a></h4>
<p>With both column stats and partition stats disabled, only the files index is built during the insert operation. We’ll use the SQL below for our test:</p>
<div class="language-sql codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-sql codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#F8F8F2"><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">SELECT</span><span class="token plain"> order_id</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> price</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> shipping_country</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">FROM</span><span class="token plain"> orders</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">WHERE</span><span class="token plain"> price </span><span class="token operator">&gt;</span><span class="token plain"> </span><span class="token number">300</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><br></span></code></pre></div></div>
<p>This query looks for orders with price greater than 300, which only exist in partition 'A' (shipping_country = 'A'). After running the SQL, here's what we see in the Spark UI:</p>
<p><img decoding="async" loading="lazy" alt="Spark UI: files index only" src="https://hudi.apache.org/assets/images/fig4-d28ef18dd51d094cfb34e8bae0e65420.png" width="747" height="504" class="img_ev3q"></p>
<p>Spark read all 3 partitions and 3 files to find potential matches, but only 1 record from partition A actually satisfied the query condition.</p>
<h4 class="anchor anchorTargetStickyNavbar_Vzrq" id="enabling-column-stats">Enabling column stats<a href="https://hudi.apache.org/blog/2025/10/29/deep-dive-into-hudis-indexing-subsystem-part-1-of-2#enabling-column-stats" class="hash-link" aria-label="Direct link to Enabling column stats" title="Direct link to Enabling column stats" translate="no">​</a></h4>
<p>Now let's enable column stats while keeping partition stats disabled. Note that we can't do it the other way around—partition stats requires column stats to be enabled first.</p>
<div class="language-sql codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-sql codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#F8F8F2"><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">CREATE</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">TABLE</span><span class="token plain"> orders </span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">    order_id STRING</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">    price </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">DECIMAL</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token number">12</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token number">2</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">    order_status STRING</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">    update_ts </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">BIGINT</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">    shipping_date </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">DATE</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">    shipping_country STRING</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">USING</span><span class="token plain"> HUDI</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">PARTITIONED </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">BY</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token plain">shipping_country</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">OPTIONS </span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">    primaryKey </span><span class="token operator">=</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">'order_id'</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">    preCombineField </span><span class="token operator">=</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">'update_ts'</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">    hoodie</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token plain">metadata</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">index</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">column</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token plain">stats</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">enable</span><span class="token plain"> </span><span class="token operator">=</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">'true'</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">    hoodie</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token plain">metadata</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">index</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">partition</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token plain">stats</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">enable</span><span class="token plain"> </span><span class="token operator">=</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">'false'</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><br></span></code></pre></div></div>
<p>Running the same SQL gives us this in the Spark UI:</p>
<p><img decoding="async" loading="lazy" alt="Spark UI: column stats enabled" src="https://hudi.apache.org/assets/images/fig5-8c6a7e86f7bb1789e91ac4c539fd1b78.png" width="692" height="461" class="img_ev3q"></p>
<p>Now it shows all 3 partitions but only 1 file was scanned. Without partition stats, the query engine couldn't prune partitions, but column stats successfully filtered out the non-matching files. The compute cost of examining those 2 irrelevant partitions and their files could have been avoided with partition stats enabled.</p>
<h4 class="anchor anchorTargetStickyNavbar_Vzrq" id="enabling-column-stats-and-partition-stats">Enabling column stats and partition stats<a href="https://hudi.apache.org/blog/2025/10/29/deep-dive-into-hudis-indexing-subsystem-part-1-of-2#enabling-column-stats-and-partition-stats" class="hash-link" aria-label="Direct link to Enabling column stats and partition stats" title="Direct link to Enabling column stats and partition stats" translate="no">​</a></h4>
<p>Now let's enable partition stats as well. Since both indexes are enabled by default in Hudi 1.x, we can simply omit those additional configs from the CREATE statement:</p>
<div class="language-sql codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-sql codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#F8F8F2"><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">CREATE</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">TABLE</span><span class="token plain"> orders </span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">    order_id STRING</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">    price </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">DECIMAL</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token number">12</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token number">2</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">    order_status STRING</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">    update_ts </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">BIGINT</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">    shipping_date </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">DATE</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">    shipping_country STRING</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">USING</span><span class="token plain"> HUDI</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">PARTITIONED </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">BY</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token plain">shipping_country</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">OPTIONS </span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">    primaryKey </span><span class="token operator">=</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">'order_id'</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">    preCombineField </span><span class="token operator">=</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">'update_ts'</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><br></span></code></pre></div></div>
<p>Running the same SQL gives us this in the Spark UI:</p>
<p><img decoding="async" loading="lazy" alt="Spark UI: column + partition stats enabled" src="https://hudi.apache.org/assets/images/fig6-29897046b22cae13ecde0bad971f9544.png" width="685" height="457" class="img_ev3q"></p>
<p>Now we see the full pruning effect happened—only 1 relevant partition and 1 relevant file were scanned, thanks to both indexes working together. <a href="https://hudi.apache.org/blog/2025/10/22/Partition_Stats_Enhancing_Column_Stats_in_Hudi_1.0/" target="_blank" rel="noopener noreferrer" class="">This blog</a> shows a 93% reduction in query time running on a 1 TB dataset.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="configure-relevant-columns-to-be-indexed">Configure relevant columns to be indexed<a href="https://hudi.apache.org/blog/2025/10/29/deep-dive-into-hudis-indexing-subsystem-part-1-of-2#configure-relevant-columns-to-be-indexed" class="hash-link" aria-label="Direct link to Configure relevant columns to be indexed" title="Direct link to Configure relevant columns to be indexed" translate="no">​</a></h3>
<p>By default, Hudi indexes the first 32 columns for both partition stats and column stats. This limit prevents excessive metadata overhead—each indexed column requires computing min, max, null-count, and value-count statistics for every partition and data file. In most cases, you only need to index a small subset of columns that are frequently used in query predicates. You can specify which columns to be indexed to reduce the maintenance costs:</p>
<div class="language-sql codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-sql codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#F8F8F2"><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">CREATE</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">TABLE</span><span class="token plain"> orders </span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">    order_id STRING</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">    price </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">DECIMAL</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token number">12</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token number">2</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">    order_status STRING</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">    update_ts </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">BIGINT</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">    shipping_date </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">DATE</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">    shipping_country STRING</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">USING</span><span class="token plain"> HUDI</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">PARTITIONED </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">BY</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token plain">shipping_country</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">OPTIONS </span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">    primaryKey </span><span class="token operator">=</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">'order_id'</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">    preCombineField </span><span class="token operator">=</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">'update_ts'</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token string" style="color:rgb(255, 121, 198)">'hoodie.metadata.index.column.stats.column.list'</span><span class="token plain"> </span><span class="token operator">=</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">'price,shipping_date'</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><br></span></code></pre></div></div>
<p>The config <code>hoodie.metadata.index.column.stats.column.list</code> applies to both partition stats and column stats. By indexing just the <code>price</code> and <code>shipping_date</code> columns, queries filtering on price comparisons or shipping date ranges will already see significant performance improvements.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="key-takeaways-and-whats-next">Key Takeaways and What's Next<a href="https://hudi.apache.org/blog/2025/10/29/deep-dive-into-hudis-indexing-subsystem-part-1-of-2#key-takeaways-and-whats-next" class="hash-link" aria-label="Direct link to Key Takeaways and What's Next" title="Direct link to Key Takeaways and What's Next" translate="no">​</a></h2>
<p>Hudi’s metadata table is itself a Hudi Merge‑on‑Read (MOR) table that acts as a multimodal indexing subsystem. It is physically partitioned by index type (for example, <code>files/</code>, <code>column_stats/</code>, <code>partition_stats/</code>) and stores base files in the HFile (SSTable‑like) format. This layout provides fast point lookups and efficient batched scans by key prefix—exactly the access patterns indexing needs at lakehouse scale.</p>
<p>Index maintenance happens transactionally alongside data writes, keeping index entries consistent with the data table. Periodic compaction merges log files into read‑optimized HFile base files to keep point lookups fast and predictable. On the read path, Hudi composes multiple indexes to minimize I/O: the files index enumerates candidates, partition stats prune irrelevant partitions, and column stats prune non‑matching files. In effect, the engine scans only the minimum set of files required to satisfy a query.</p>
<p>In practice, the defaults are a strong starting point. Keep the metadata table enabled and explicitly list only the columns you frequently filter on via <code>hoodie.metadata.index.column.stats.column.list</code> to control metadata overhead. In <a href="https://hudi.apache.org/blog/2025/11/12/deep-dive-into-hudis-indexing-subsystem-part-2-of-2/" target="_blank" rel="noopener noreferrer" class="">part 2</a>, we’ll go deeper into accelerating equality‑matching and expression‑based predicates using the record, secondary, and expression indexes, and discuss how asynchronous index maintenance keeps writers unblocked while indexes build in the background.</p>]]></content:encoded>
            <category>indexing</category>
            <category>data lakehouse</category>
            <category>data skipping</category>
        </item>
        <item>
            <title><![CDATA[Partition Stats: Enhancing Column Stats in Hudi 1.0]]></title>
            <link>https://hudi.apache.org/blog/2025/10/22/Partition_Stats_Enhancing_Column_Stats_in_Hudi_1.0</link>
            <guid>https://hudi.apache.org/blog/2025/10/22/Partition_Stats_Enhancing_Column_Stats_in_Hudi_1.0</guid>
            <pubDate>Wed, 22 Oct 2025 00:00:00 GMT</pubDate>
            <description><![CDATA[For those tracking Apache Hudi's performance enhancements, the introduction of the column stats index was a significant development, as detailed in this blog. It represented a major advancement for query optimization by implementing a straightforward yet highly effective concept: storing lightweight, file-level statistics (such as min/max values and null counts) for specific columns. This provided Hudi's query engine a substantial performance improvement.]]></description>
            <content:encoded><![CDATA[<p>For those tracking Apache Hudi's performance enhancements, the introduction of the column stats index was a significant development, as <a href="https://www.onehouse.ai/blog/hudis-column-stats-index-and-data-skipping-feature-help-speed-up-queries-by-an-orders-of-magnitude" target="_blank" rel="noopener noreferrer" class="">detailed in this blog</a>. It represented a major advancement for query optimization by implementing a straightforward yet highly effective concept: storing lightweight, file-level statistics (such as min/max values and null counts) for specific columns. This provided Hudi's query engine a substantial performance improvement.</p>
<p><img decoding="async" loading="lazy" alt="cover" src="https://hudi.apache.org/assets/images/fig1-103edc705ab1254fb8b23ba25db76fd6.jpg" width="1944" height="1654" class="img_ev3q"></p>
<p>Instead of blindly scanning every single file for a query, the engine could first peek at the index entries—which is far more efficient than reading all the Parquet footers—to determine which files <em>couldn't</em> possibly contain the relevant data. This data-skipping capability meant engines could bypass large amounts of irrelevant data, slashing query latency. But that skipping process is conducted at the file level—what if we could apply a similar skipping logic at the partition level? Since a single physical partition can contain thousands of data files, applying this logic at the partition level can further amplify the performance gains by only considering files in the relevant partitions. This is precisely the capability that Hudi 1.0’s partition stats index introduces.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="multimodal-indexing">Multimodal Indexing<a href="https://hudi.apache.org/blog/2025/10/22/Partition_Stats_Enhancing_Column_Stats_in_Hudi_1.0#multimodal-indexing" class="hash-link" aria-label="Direct link to Multimodal Indexing" title="Direct link to Multimodal Indexing" translate="no">​</a></h2>
<p>Hudi’s <a href="https://hudi.apache.org/docs/indexes#multi-modal-indexing" target="_blank" rel="noopener noreferrer" class="">multimodal indexing subsystem</a> enhances both read and write performance in data lakehouses by supporting versatile index types optimized for different workloads. This subsystem is built on a scalable, internal metadata table that ensures ACID-compliant updates and efficient lookups, which in turn reduces full data scans. It houses various indexes—such as the files, column stats, and partition stats—which work together to improve efficiency in reads, writes, and upserts, providing scalable, low-latency query performance for large datasets in the lakehouse.</p>
<p>The partition stats index is built on top of the column stats index by aggregating its file-level statistics up to the partition level. As we've covered, the column stats index tracks statistics (min, max, null counts) for <em>individual files</em>, enabling fine-grained file pruning. The partition stats index, in contrast, summarizes these same statistics across <em>all files</em> within a single partition.</p>
<p>This partition-level aggregation allows Hudi to efficiently prune entire physical partitions before even examining file-level indexes, leading to faster query planning and execution by skipping large chunks of irrelevant data early in the process. In other words, the partition stats index provides a coarse-grained, high-level pruning layer on top of the fine-grained, file-level pruning enabled by the column stats index.</p>
<p>Because partition-level pruning happens first, it narrows down the scope of files that the column stats index needs to inspect, improving overall query performance and reducing overhead on large datasets. The diagram below illustrates the file pruning process:</p>
<p><img decoding="async" loading="lazy" alt="file pruning process" src="https://hudi.apache.org/assets/images/fig2-468ec18846bf7194631d838fd9824bcf.png" width="981" height="706" class="img_ev3q"></p>
<p>During query planning, the Hudi integration for the query engine takes the predicates parsed from user queries and queries the indexes within the metadata table.</p>
<ul>
<li class="">The files index is queried first to return an initial list of all partitions in the table.</li>
<li class="">The partition stats index then filters this partition list by checking if each partition’s min/max values for the indexed columns fall within the predicate's range. For example, with a predicate of <code>A = 100</code>, the index skips any partition whose <code>min(A)</code> is greater than 100 or whose <code>max(A)</code> is less than 100.</li>
<li class="">The files index is queried again to retrieve a list of all files <em>within</em> these pruned partitions.</li>
<li class="">This file list is then passed to the column stats index, which performs the final, fine-grained pruning by applying the query predicates to the file-level statistics.</li>
<li class="">Finally, this pruned list of files is returned to the query engine to complete query planning.</li>
</ul>
<p>This dual-layer pruning strategy is especially impactful in production systems managing large amounts of data. By complementing the fine-grained column stats index with this coarse-grained partition skipping, Hudi’s metadata table significantly reduces I/O, computation, and cost. For end-users, this translates directly into a better experience, turning queries that once took minutes into operations that complete in seconds.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="example-us-shipping-addresses">Example: US Shipping Addresses<a href="https://hudi.apache.org/blog/2025/10/22/Partition_Stats_Enhancing_Column_Stats_in_Hudi_1.0#example-us-shipping-addresses" class="hash-link" aria-label="Direct link to Example: US Shipping Addresses" title="Direct link to Example: US Shipping Addresses" translate="no">​</a></h2>
<p>To understand the impact, let's use the example table below, which stores US shipping addresses for online orders and is partitioned by <code>state</code>. This table could contain billions of records, and we want to run a query filtering on the <code>zip_code</code> column.</p>
<p>By default, the files, column stats, and partition stats indexes are all enabled in Hudi 1.0. You can create the Hudi table using Spark SQL, for example, without needing additional configs to enable column stats and partition stats:</p>
<div class="language-sql codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-sql codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#F8F8F2"><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">CREATE</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">TABLE</span><span class="token plain"> shipping_address </span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">    order_id STRING</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">    state STRING</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">    zip_code STRING</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">USING</span><span class="token plain"> HUDI</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">TBLPROPERTIES </span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">    primaryKey </span><span class="token operator">=</span><span class="token string" style="color:rgb(255, 121, 198)">'order_id'</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">    hoodie</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token plain">metadata</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">index</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">column</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token plain">stats</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">column</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token plain">list </span><span class="token operator">=</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">'zip_code'</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">PARTITIONED </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">BY</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token plain">state</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><br></span></code></pre></div></div>
<p>Note that, in practice, you would most likely want to use <code>hoodie.metadata.index.column.stats.column.list</code> to indicate which column(s) to index according to your business use case, otherwise, the first 32 columns in the table schema will be indexed by default, which probably won’t be optimal. The specified columns apply to both the column stats and partition stats indexes.</p>
<p>Without the column and partition stats indexes, a query for a specific ZIP code (e.g., <code>zip_code = '90001'</code>) would force the query engine to perform a full table scan. This is highly inefficient, leading to high query latency and excessive resource consumption.</p>
<p>With the indexes enabled, the process is drastically different.</p>
<ol>
<li class="">During write operations, the Hudi writer tracks statistics for the <code>zip_code</code> column. The column stats index stores min/max values for each data file, and the partition stats index aggregates and stores the min/max <code>zip_code</code> for each <code>state</code>.</li>
<li class="">At query time, suppose the partition stats index shows that the "California" partition contains ZIP codes from "90000" to "96199", while the "New York" partition contains ZIP codes from "10000" to "14999". When the query for <code>zip_code = '90001'</code> is executed, the query planner first consults the partition stats index. It sees that "90001" falls within the "California" partition's range but outside the "New York" partition's range.</li>
<li class="">The engine can therefore skip the entire "New York" partition (and any other partition like "Texas" or "Florida" whose ZIP code range doesn't include "90001"). The query proceeds by only reading data from the "California" partition—the only one that could possibly contain the data.</li>
</ol>
<p>This ability to prune entire partitions before reading any files is what provides such a significant performance gain.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="results-the-data-skipping-effect">Results: the Data Skipping Effect<a href="https://hudi.apache.org/blog/2025/10/22/Partition_Stats_Enhancing_Column_Stats_in_Hudi_1.0#results-the-data-skipping-effect" class="hash-link" aria-label="Direct link to Results: the Data Skipping Effect" title="Direct link to Results: the Data Skipping Effect" translate="no">​</a></h2>
<p>We conducted a focused benchmarking exercise using a synthetic dataset generated by the open-source tool <a href="https://github.com/onehouseinc/lake-loader" target="_blank" rel="noopener noreferrer" class="">lake_loader</a>. Specifically, we created a 1 TB table for the US shipping addresses example and built both the column stats and partition stats indexes on this dataset.</p>
<p>The benchmarking objective was to evaluate the performance impact from the two indexes for data skipping. To do this, we executed the following query in two scenarios:</p>
<div class="language-sql codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-sql codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#F8F8F2"><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">select</span><span class="token plain"> </span><span class="token function" style="color:rgb(80, 250, 123)">count</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token number">1</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">from</span><span class="token plain"> shipping_address </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">where</span><span class="token plain"> zip_code </span><span class="token operator">=</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">'10001'</span><br></span></code></pre></div></div>
<p>One with the column and partition stats indexes enabled (default), and one with both indexes disabled for reads, which forced a full table scan.</p>
<p>The Spark job was configured with:</p>
<ul>
<li class="">Executor cores = 4</li>
<li class="">Executor memory = 10g</li>
<li class="">Number of executors = 60</li>
</ul>
<p>The Spark DAGs for the two scenarios show the file pruning effect:</p>
<p><img decoding="async" loading="lazy" alt="Spark DAGs comparison" src="https://hudi.apache.org/assets/images/fig3-2a993e3d03e054e6e2697772a56e673f.png" width="3456" height="1992" class="img_ev3q"></p>
<p>With both column stats and partition stats indexes enabled (the left-side DAG), the number of files read was 19,304. In contrast, the disabled setup (the right-side DAG) resulted in reading 393,360 files—about 20 times more.</p>
<p>The runtime comparison chart below shows the query time difference (shorter is better):</p>
<p><img decoding="async" loading="lazy" alt="perf run time chart" src="https://hudi.apache.org/assets/images/fig4-8df00f82c190ca1663131f7dbb6ddf8d.jpg" width="2428" height="1720" class="img_ev3q"></p>
<p>Enabling data skipping with both the column stats and partition stats indexes for the Hudi table delivers approximately a 93% reduction in query runtime compared to the full scan (no data skipping).</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="conclusion">Conclusion<a href="https://hudi.apache.org/blog/2025/10/22/Partition_Stats_Enhancing_Column_Stats_in_Hudi_1.0#conclusion" class="hash-link" aria-label="Direct link to Conclusion" title="Direct link to Conclusion" translate="no">​</a></h2>
<p>The new partition stats index is a powerful addition to Hudi's multimodal indexing subsystem, directly addressing the challenge of query performance on large-scale partitioned tables. By working in concert with the existing column stats index, it provides a crucial layer of coarse-grained pruning, allowing the query engine to eliminate entire partitions from consideration <em>before</em> inspecting individual files. As our benchmark showed, this two-level pruning strategy—first by partition, then by file—is not just a minor tweak. It results in a dramatic reduction in I/O, slashing query runtimes by over 93% and enabling near-interactive query speeds. This feature solidifies Hudi's data-skipping capabilities, making it even more efficient to run demanding analytical queries directly on the data lakehouse, saving both time and computation costs.</p>]]></content:encoded>
            <category>indexing</category>
            <category>data lakehouse</category>
            <category>data skipping</category>
        </item>
        <item>
            <title><![CDATA[Modernizing Upstox's Data Platform with Apache Hudi, dbt, and EMR Serverless]]></title>
            <link>https://hudi.apache.org/blog/2025/10/16/Modernizing-Upstox-Data-Platform-with-Apache-Hudi-DBT-and-EMR-Serverless</link>
            <guid>https://hudi.apache.org/blog/2025/10/16/Modernizing-Upstox-Data-Platform-with-Apache-Hudi-DBT-and-EMR-Serverless</guid>
            <pubDate>Thu, 16 Oct 2025 00:00:00 GMT</pubDate>
            <description><![CDATA[Introduction]]></description>
            <content:encoded><![CDATA[<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="introduction">Introduction<a href="https://hudi.apache.org/blog/2025/10/16/Modernizing-Upstox-Data-Platform-with-Apache-Hudi-DBT-and-EMR-Serverless#introduction" class="hash-link" aria-label="Direct link to Introduction" title="Direct link to Introduction" translate="no">​</a></h2>
<p>In <a href="https://www.youtube.com/watch?v=dAM2zOvnPmw" target="_blank" rel="noopener noreferrer" class="">this community sharing session</a>, Manish Gaurav from Upstox shared insights into the complexities of managing data ingestion at scale. Drawing from the company’s experience as a leading online trading platform in India, the discussion highlighted challenges around file-level upserts, ensuring atomic operations, and handling small files effectively. Upstox shared how they built a modern data platform using Apache Hudi and dbt to address these issues. In this blog post, we’ll break down their solution and why it matters.</p>
<p>Upstox is a leading online trading platform that enables millions of users to invest in equities, commodities, derivatives, and currencies. With over 12 million customers generating 300,000 data requests daily, the company's data team is responsible for delivering the real-time insights that power key products, including:</p>
<ul>
<li class="">Search functionality</li>
<li class="">A customer service chatbot (powered by OpenAI)</li>
<li class="">Personalized portfolio recommendations</li>
</ul>
<p><img decoding="async" loading="lazy" src="https://hudi.apache.org/assets/images/fig1-3baa485e75ef728786f15b45d2d97d6b.png" width="1999" height="1312" class="img_ev3q"></p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="data-sources">Data Sources<a href="https://hudi.apache.org/blog/2025/10/16/Modernizing-Upstox-Data-Platform-with-Apache-Hudi-DBT-and-EMR-Serverless#data-sources" class="hash-link" aria-label="Direct link to Data Sources" title="Direct link to Data Sources" translate="no">​</a></h3>
<p>Upstox ingests 250–300 GB of structured and semi-structured data per day from a variety of sources:</p>
<ul>
<li class="">Order and transaction data from exchanges</li>
<li class="">Microservice telemetry from Cloudflare</li>
<li class="">Customer support data from platforms like Freshdesk and SquadStack</li>
<li class="">Behavioral analytics from Mixpanel</li>
<li class="">Data from operational databases (MongoDB, MySQL, and MS SQL) via AWS DMS</li>
</ul>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="the-challenges-with-initial-data-platform">The Challenges with Initial Data Platform<a href="https://hudi.apache.org/blog/2025/10/16/Modernizing-Upstox-Data-Platform-with-Apache-Hudi-DBT-and-EMR-Serverless#the-challenges-with-initial-data-platform" class="hash-link" aria-label="Direct link to The Challenges with Initial Data Platform" title="Direct link to The Challenges with Initial Data Platform" translate="no">​</a></h2>
<p>As Upstox grew, so did the complexity of its data operations. Here are some of the early bottlenecks the company faced:</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="data-ingestion-issues">Data Ingestion Issues<a href="https://hudi.apache.org/blog/2025/10/16/Modernizing-Upstox-Data-Platform-with-Apache-Hudi-DBT-and-EMR-Serverless#data-ingestion-issues" class="hash-link" aria-label="Direct link to Data Ingestion Issues" title="Direct link to Data Ingestion Issues" translate="no">​</a></h3>
<p>Prior to 2023, Upstox relied on no-code ingestion platforms like Hevo. While easy to adopt, these platforms introduced several limitations, including high licensing costs and a lack of fine-grained control over ingestion logic. File-level upserts required complex joins between incoming CDC (change data capture) datasets and target tables. Additionally, a lack of atomicity often led to inconsistent data writes, and small-file issues were rampant. To combat these problems, the team had to implement time-consuming re-partitioning and coalescing, along with complex salting strategies to distribute data evenly.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="downstream-consumption-struggles">Downstream Consumption Struggles<a href="https://hudi.apache.org/blog/2025/10/16/Modernizing-Upstox-Data-Platform-with-Apache-Hudi-DBT-and-EMR-Serverless#downstream-consumption-struggles" class="hash-link" aria-label="Direct link to Downstream Consumption Struggles" title="Direct link to Downstream Consumption Struggles" translate="no">​</a></h3>
<p>Analytics queries were primarily served through Amazon Athena, which presented several key limitations. For instance, it frequently timed out when querying large datasets and often exceeded the maximum number of partitions it could handle. Additionally, Athena's lack of support for stored procedures made it challenging to manage and reuse complex query logic. Attempts to improve performance with bucketing often created more small files, and the lack of native support for incremental queries further complicated their analytics workflow.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="the-modern-lakehouse-architecture">The Modern Lakehouse Architecture<a href="https://hudi.apache.org/blog/2025/10/16/Modernizing-Upstox-Data-Platform-with-Apache-Hudi-DBT-and-EMR-Serverless#the-modern-lakehouse-architecture" class="hash-link" aria-label="Direct link to The Modern Lakehouse Architecture" title="Direct link to The Modern Lakehouse Architecture" translate="no">​</a></h2>
<p><img decoding="async" loading="lazy" src="https://hudi.apache.org/assets/images/fig2-a2a9161b7ad75628a36b03514ae4a9c4.png" width="1934" height="1016" class="img_ev3q"></p>
<p>To tackle these problems, Upstox implemented a medallion architecture, organizing data into bronze, silver, and gold layers:</p>
<ul>
<li class=""><strong>Bronze (Raw Data):</strong> Data is ingested and stored in its raw format as Parquet files.</li>
<li class=""><strong>Silver (Cleaned and Filtered):</strong> Data is cleaned, filtered, and stored in Apache Hudi tables, which are updated incrementally.</li>
<li class=""><strong>Gold (Business-Ready):</strong> Data is aggregated for specific business use cases, modeled with dbt, and stored in Hudi.</li>
</ul>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="the-solution-a-modern-stack-with-hudi-dbt-and-emr-serverless">The Solution: A Modern Stack with Hudi, dbt, and EMR Serverless<a href="https://hudi.apache.org/blog/2025/10/16/Modernizing-Upstox-Data-Platform-with-Apache-Hudi-DBT-and-EMR-Serverless#the-solution-a-modern-stack-with-hudi-dbt-and-emr-serverless" class="hash-link" aria-label="Direct link to The Solution: A Modern Stack with Hudi, dbt, and EMR Serverless" title="Direct link to The Solution: A Modern Stack with Hudi, dbt, and EMR Serverless" translate="no">​</a></h3>
<p>Upstox re-architected its platform using Apache Hudi as the core data lake technology, dbt for transformations, and EMR Serverless for scalable compute. Airflow was used to orchestrate the entire workflow. Here's how this new stack addressed their challenges:</p>
<p><strong>Simplified Data Updates:</strong> Hudi provides built-in support for record-level upserts with atomic guarantees and snapshot isolation. This helped Upstox overcome the challenge of ensuring consistent updates to their fact and dimension tables.</p>
<p><strong>Improved Upsert Performance:</strong> To optimize upsert performance, the team leveraged Bloom index, especially for transaction-heavy fact tables. Indexing strategies were chosen based on data characteristics to balance latency and efficiency.</p>
<p><strong>Resolved Small-File Issues:</strong> Small files, which are common in streaming workloads, were mitigated using clustering jobs supported by Hudi. This process was scheduled to run weekly and ensured efficient file sizes and reduced storage overhead without manual intervention.</p>
<p><strong>Enabled Incremental Processing:</strong> Incremental joins allowed Upstox to process only new data daily. This enabled timely updates to the aggregated tables in the gold layer that power user-facing dashboards—a task that was not feasible with traditional Athena queries.</p>
<p><strong>Managed Metadata Growth:</strong> The accumulation of commit and metadata files in the Hudi table’s `.hoodie/` directory increased S3 listing costs and slowed down operations. Hudi's archival feature helped manage this by archiving older commits after a certain threshold, keeping metadata lean and efficient.</p>
<p><strong>Streamlined Data Modeling:</strong> The team used dbt on EMR Serverless to create materialized views over the Hudi datasets. This enabled the creation of efficient transformation layers (silver and gold) using familiar SQL workflows and managed compute.</p>
<p><strong>Flexible Data Materialization:</strong> dbt supported a variety of model types, including tables, views, and ephemeral models (Common Table Expressions, or CTEs). This gave teams the flexibility to optimize for performance, reuse, or simplicity, depending on the use case.</p>
<p><strong>Out-of-the-Box Lineage and Documentation:</strong> dbt helps visualize how data flows from one table to another, making it easier to debug and understand dependencies. The glossary feature allows teams to document column meanings and transformations clearly.</p>
<p><strong>Enforced Data Quality:</strong> With dbt, specific data quality rules can be added to individual tables or pipelines. This adds an extra layer of validation beyond the basic checks performed during data ingestion.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="cicd-and-orchestration">CI/CD and Orchestration<a href="https://hudi.apache.org/blog/2025/10/16/Modernizing-Upstox-Data-Platform-with-Apache-Hudi-DBT-and-EMR-Serverless#cicd-and-orchestration" class="hash-link" aria-label="Direct link to CI/CD and Orchestration" title="Direct link to CI/CD and Orchestration" translate="no">​</a></h3>
<p><img decoding="async" loading="lazy" src="https://hudi.apache.org/assets/images/fig3-3a9b031ca307c28c17434af09a0ee7bc.png" width="1932" height="882" class="img_ev3q"></p>
<p>Upstox uses Apache Airflow for orchestration, with dbt pipelines deployed via a Git-based CI/CD process. Merging a pull request in GitLab triggers the CI/CD pipeline, which automatically builds a new dbt image and publishes the updated data catalog. Airflow then runs the corresponding dbt jobs daily or on-demand, automating the entire transformation workflow.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="the-impact">The Impact<a href="https://hudi.apache.org/blog/2025/10/16/Modernizing-Upstox-Data-Platform-with-Apache-Hudi-DBT-and-EMR-Serverless#the-impact" class="hash-link" aria-label="Direct link to The Impact" title="Direct link to The Impact" translate="no">​</a></h3>
<p>The adoption of this modern data stack had a significant impact on Upstox's data platform. The company achieved extremely high data availability and consistency for critical datasets, reducing SLA breaches for complex joins by 70%. Furthermore, pipeline costs dropped by 40%, and query performance improved drastically thanks to Hudi's clustering and optimized joins.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="conclusion">Conclusion<a href="https://hudi.apache.org/blog/2025/10/16/Modernizing-Upstox-Data-Platform-with-Apache-Hudi-DBT-and-EMR-Serverless#conclusion" class="hash-link" aria-label="Direct link to Conclusion" title="Direct link to Conclusion" translate="no">​</a></h2>
<p>By leveraging Apache Hudi, dbt, and EMR Serverless, Upstox built a robust and cost-efficient data platform to serve its 12M+ customers, overcoming the significant challenges of data ingestion and analytics at scale. This transformation resolved critical issues like inconsistent data writes, small-file problems, and query timeouts, leading to tangible improvements in both performance and efficiency. With a 70% reduction in SLA breaches and a 40% drop in pipeline costs, the new architecture has empowered their BI and ML teams to move faster. Ultimately, this success story demonstrates how a modern data stack can not only solve immediate technical bottlenecks but also lay the groundwork for a scalable, self-service future that enables continued innovation.</p>]]></content:encoded>
            <category>upstox</category>
            <category>dbt</category>
            <category>data lakehouse</category>
        </item>
    </channel>
</rss>