
<rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:media="http://search.yahoo.com/mrss/">
    <channel>
        <title><![CDATA[ The Cloudflare Blog ]]></title>
        <description><![CDATA[ Get the latest news on how products at Cloudflare are built, technologies used, and join the teams helping to build a better Internet. ]]></description>
        <link>https://blog.cloudflare.com</link>
        <atom:link href="https://blog.cloudflare.com/" rel="self" type="application/rss+xml"/>
        <language>en-us</language>
        <image>
            <url>https://blog.cloudflare.com/favicon.png</url>
            <title>The Cloudflare Blog</title>
            <link>https://blog.cloudflare.com</link>
        </image>
        <lastBuildDate>Wed, 08 Apr 2026 17:27:04 GMT</lastBuildDate>
        <item>
            <title><![CDATA[Human Native is joining Cloudflare]]></title>
            <link>https://blog.cloudflare.com/human-native-joins-cloudflare/</link>
            <pubDate>Thu, 15 Jan 2026 14:00:00 GMT</pubDate>
            <description><![CDATA[ Cloudflare acquires Human Native, an AI data marketplace specialising in transforming content into searchable and useful data, to accelerate work building new economic models for the Internet. ]]></description>
            <content:encoded><![CDATA[ <p></p><p>Today, we’re excited to share that Cloudflare has acquired <a href="https://www.humannative.ai/"><u>Human Native</u></a>, a UK-based AI data marketplace specializing in transforming multimedia content into searchable and useful data.</p>
    <div>
      <h3>Human Native x Cloudflare</h3>
      <a href="#human-native-x-cloudflare">
        
      </a>
    </div>
    <p>The Human Native team has spent the past few years focused on helping AI developers create better AI through licensed data. Their technology helps publishers and developers turn messy, unstructured content into something that can be understood, licensed and ultimately valued. They have approached data not as something to be scraped, but as an asset class that deserves structure, transparency and respect.</p><p>Access to high-quality data can lead to better technical performance. One of Human Native’s customers, a prominent UK video AI company, threw away their existing training data after achieving superior results with data sourced through Human Native. Going forward they are only training on fully licensed, reputably sourced, high-quality content.</p><p>This gives a preview of what the economic model of the Internet can be in the age of generative AI: better AI built on better data, with fair control, compensation and credit for creators.</p>
    <div>
      <h3>The Internet needs new economic models</h3>
      <a href="#the-internet-needs-new-economic-models">
        
      </a>
    </div>
    <p>For the last 30 years, the open Internet has been based on a fundamental value exchange: creators create content, aggregators (such as search engines or social media) send traffic. Creators can monetize that traffic through advertisements, subscriptions or direct support. This is the economic loop that has powered the explosive growth of the Internet.</p><p>But it’s under real strain.</p><p><a href="https://blog.cloudflare.com/crawlers-click-ai-bots-training/"><u>Crawl-to-referral</u></a> ratios are skyrocketing, with 10s of thousands of AI and bot crawls per real human visitor, and<b> </b>it’s unclear how multipurpose crawlers are using the content they access.</p><p>The community of creators who publish on the Internet is a diverse group: news publishers, content creators, financial professionals, technology companies, aggregators and more. But they have one thing in common: They want to decide how their content is used by AI systems.</p><p>Cloudflare’s work in building <a href="https://www.cloudflare.com/en-gb/ai-crawl-control/"><u>AI Crawl Control</u></a> and <a href="https://developers.cloudflare.com/ai-crawl-control/features/pay-per-crawl/what-is-pay-per-crawl/"><u>Pay Per Crawl</u></a> is predicated on a simple philosophy: Content owners should get to decide how and when their content is accessed by others. Many of our customers want to optimize their brand and content to make sure it is in every training data set and shows up in every new search; others want to have more control and only allow access if there is direct compensation.</p><p>Our tools like <a href="https://developers.cloudflare.com/ai-search/"><u>AI Search</u></a>, AI Crawl Control and Pay Per Crawl can help, wherever you land in that equation. The important thing is that the content owner gets to decide.</p>
    <div>
      <h3>New tools for AI developers</h3>
      <a href="#new-tools-for-ai-developers">
        
      </a>
    </div>
    <p>With the Human Native team joining Cloudflare, we are accelerating our work in helping customers transform their content to be easily accessed and understood by AI bots and agents in addition to their traditional human audiences.</p><p>Crawling is complex, expensive in terms of engineering and compute to process the content, and has no guarantees of quality control. A crawled index can contain duplicates, spam, illegal material and many more headaches. Developers are left with messy, unstructured data.</p><p>We recently announced our work in building the <a href="https://blog.cloudflare.com/an-ai-index-for-all-our-customers/"><u>AI Index</u></a>, a powerful new way for both foundation model companies and agents to access content at scale.</p><p>Instead of sending crawlers blindly and repeatedly across the open Internet, AI developers will be able to connect via a pub/sub model: participating websites will expose structured updates whenever their content changes, and developers will be able to subscribe to receive those updates in real time. </p><p>This opens up new avenues for content creators to experiment with new business models. </p>
    <div>
      <h3>Building the foundation for these new business models</h3>
      <a href="#building-the-foundation-for-these-new-business-models">
        
      </a>
    </div>
    <p>Cloudflare is investing heavily in creating the foundations for these new business models, starting with x402.</p><p>We recently announced that we are creating the <a href="https://blog.cloudflare.com/x402/"><u>x402 Foundation</u></a>, in partnership with Coinbase, to enable machine-to-machine transactions for digital resources.</p><p>Payments on the web have historically been designed for humans. We browse a merchant’s website, show intent by adding items to a cart, and confirm our intent to purchase by putting in our credit card information and clicking “Pay.” But what if you want to enable direct transactions between automated systems? We need protocols to allow machine-to-machine transactions. </p><p>Together, Human Native and Cloudflare will accelerate our work in building the basis of these new economic models for the Internet. </p>
    <div>
      <h3>What’s next</h3>
      <a href="#whats-next">
        
      </a>
    </div>
    <p>The Internet works best when it is open, fair, and independently sustainable. We’re excited to welcome the Human Native team to Cloudflare, and even more excited about what we will build together to improve the foundations of the Internet in the age of AI.</p><p>Onwards.</p> ]]></content:encoded>
            <category><![CDATA[AI]]></category>
            <category><![CDATA[Generative AI]]></category>
            <category><![CDATA[Data]]></category>
            <category><![CDATA[Acquisitions]]></category>
            <category><![CDATA[Developer Platform]]></category>
            <guid isPermaLink="false">Szd19ssv1kbKxjxNZhUmR</guid>
            <dc:creator>Will Allen</dc:creator>
            <dc:creator>James Smith</dc:creator>
        </item>
        <item>
            <title><![CDATA[Announcing support for GROUP BY, SUM, and other aggregation queries in R2 SQL]]></title>
            <link>https://blog.cloudflare.com/r2-sql-aggregations/</link>
            <pubDate>Thu, 18 Dec 2025 14:00:00 GMT</pubDate>
            <description><![CDATA[ Cloudflare’s R2 SQL, a distributed query engine, now supports aggregations. Explore how we built distributed GROUP BY execution, using scatter-gather and shuffling strategies to run analytics directly over your R2 Data Catalog. ]]></description>
            <content:encoded><![CDATA[ <p></p><p>When you’re dealing with large amounts of data, it’s helpful to get a quick overview — which is exactly what aggregations provide in SQL. Aggregations, known as “GROUP BY queries”, provide a bird’s eye view, so you can quickly gain insights from vast volumes of data.</p><p>That’s why we are excited to announce support for aggregations in <a href="https://blog.cloudflare.com/r2-sql-deep-dive/"><u>R2 SQL</u></a>, Cloudflare's serverless, distributed, analytics query engine, which is capable of running SQL queries over data stored in <a href="https://developers.cloudflare.com/r2/data-catalog/"><u>R2 Data Catalog</u></a>. Aggregations will allow users of <a href="https://developers.cloudflare.com/r2-sql/"><u>R2 SQL</u></a> to spot important trends and changes in the data, generate reports and find anomalies in logs.</p><p>This release builds on the already supported filter queries, which are foundational for analytical workloads, and allow users to find needles in haystacks of <a href="https://parquet.apache.org/"><u>Apache Parquet</u></a> files.</p><p>In this post, we’ll unpack the utility and quirks of aggregations, and then dive into how we extended R2 SQL to support running such queries over vast amounts of data stored in R2 Data Catalog.</p>
    <div>
      <h2>The importance of aggregations in analytics</h2>
      <a href="#the-importance-of-aggregations-in-analytics">
        
      </a>
    </div>
    <p>Aggregations, or “GROUP BY queries”, generate a short summary of the underlying data.</p><p>A common use case for aggregations is generating reports. Consider a table called “sales”, which contains historical data of all sales across various countries and departments of some organisation. One could easily generate a report on the volume of sales by department using this aggregation query:</p>
            <pre><code>SELECT department, sum(value)
FROM sales
GROUP BY department</code></pre>
            <p>
The “GROUP BY” statement allows us to split table rows into buckets. Each bucket has a label corresponding to a particular department. Once the buckets are full, we can then calculate “sum(value)” for all rows in each bucket, giving us the total volume of sales performed by the corresponding department.</p><p>For some reports, we might only be interested in departments that had the largest volume. That’s where an “ORDER BY” statement comes in handy:</p>
            <pre><code>SELECT department, sum(value)
FROM sales
GROUP BY department
ORDER BY sum(value) DESC
LIMIT 10</code></pre>
            <p>Here we instruct the query engine to sort all department buckets by their total sales volume in the descending order and only return the top 10 largest.</p><p>Finally, we might be interested in filtering out anomalies. For example, we might want to only include departments that had more than five sales total in our report. We can easily do that with a “HAVING” statement:</p>
            <pre><code>SELECT department, sum(value), count(*)
FROM sales
GROUP BY department
HAVING count(*) &gt; 5
ORDER BY sum(value) DESC
LIMIT 10</code></pre>
            <p>Here we added a new aggregate function to our query — “count(*)” — which calculates how many rows ended up in each bucket. This directly corresponds to the number of sales in each department, so we have also added a predicate in the “HAVING” statement to make sure that we only leave buckets with more than five rows in them.</p>
    <div>
      <h2>Two approaches to aggregation: compute sooner or later</h2>
      <a href="#two-approaches-to-aggregation-compute-sooner-or-later">
        
      </a>
    </div>
    <p>Aggregation queries have a curious property: they can reference columns that are not stored anywhere. Consider “sum(value)”: this column is computed by the query engine on the fly, unlike the “department” column, which is fetched from Parquet files stored on R2. This subtle difference means that any query that references aggregates like “sum”, “count” and others needs to be split into two phases.</p><p>The first phase is computing new columns. If we are to sort the data by “count(*)” column using “ORDER BY” statement or filter rows based on it using “HAVING” statement, we need to know the values of this column. Once the values of columns like “count(*)” are known, we can proceed with the rest of the query execution.</p><p>Note that if the query does not reference aggregate functions in “HAVING” or “ORDER BY”, but still uses them in “SELECT”, we can make use of a trick. Since we do not need the values of aggregate functions until the very end, we can compute them partially and merge results just before we are about to return them to the user.</p><p>The key difference between the two approaches is when we compute aggregate functions: in advance, to perform some additional computations on them later; or on the fly, to iteratively build results the user needs.</p><p>First, we will dive into building results on the fly — a technique we call “scatter-gather aggregations.” We will then build on top of that to introduce “shuffling aggregations” capable of running extra computations like “HAVING” and “ORDER BY” on top of aggregate functions.</p>
    <div>
      <h2>Scatter-gather aggregations</h2>
      <a href="#scatter-gather-aggregations">
        
      </a>
    </div>
    <p>Aggregate queries without “HAVING” and “ORDER BY” can be executed in a fashion similar to filter queries. For filter queries, R2 SQL picks one node to be the coordinator in query execution. This node analyzes the query and consults R2 Data Catalog to figure out which Parquet row groups may contain data relevant to the query. Each Parquet row group represents a relatively small piece of work that a single compute node can handle. Coordinator node distributes the work across many worker nodes and collects results to return them to the user.</p><p>In order to execute aggregate queries, we follow all the same steps and distribute small pieces of work between worker nodes. However, this time instead of just filtering rows based on the predicate in the “WHERE” statement, worker nodes also compute <b>pre-aggregates</b>.</p><p>Pre-aggregates represent an intermediary state of an aggregation. This is an incomplete piece of data representing a partially computed aggregate function on a subset of data. Multiple pre-aggregates can be merged together to compute the final value of an aggregate function. Splitting aggregate functions into pre-aggregates allows us to horizontally scale computation of aggregation, making use of vast compute resources available in Cloudflare’s network.</p>
          <figure>
          <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/2Vh0x4qHkjOuQTrxSzkVKx/84c05ebf590cb4949b188f5856a4e951/image2.png" />
          </figure><p>For example, pre-aggregate for “count(*)” is simply a number representing the count of rows in a subset of data. Computing the final “count(*)” is as easy as adding these numbers together. Pre-aggregate for “avg(value)” consists of two numbers: “sum(value)” and “count(*)”. The value of “avg(value)” can then be computed by adding together all “sum(value)” values, adding together all “count(*)” values and finally dividing one number by the other.</p><p>Once worker nodes have finished computing the pre-aggregates, they stream results to the coordinator node. The coordinator node collects all results, computes final values of aggregate functions from pre-aggregates, and returns the result to the user.</p>
    <div>
      <h2>Shuffling, beyond the limits of scatter-gather</h2>
      <a href="#shuffling-beyond-the-limits-of-scatter-gather">
        
      </a>
    </div>
    <p>Scatter-gather is highly efficient when the coordinator can compute the final result by merging small, partial states from workers. If you run a query like <code>SELECT sum(sales) FROM orders</code>, the coordinator receives a single number from each worker and adds them up. The memory footprint on the coordinator is negligible regardless of how much data resides in R2.</p><p>However, this approach becomes inefficient when the query requires sorting or filtering based on the <i>result</i> of an aggregation. Consider this query, which finds the top two departments by sales volume:</p>
            <pre><code>SELECT department, sum(sales)
FROM sales
GROUP BY department
ORDER BY sum(sales) DESC
LIMIT 2</code></pre>
            <p>Correctly determining the global Top 2 requires knowing the total sales for every department across the entire dataset. Because the data is spread effectively at random across the underlying Parquet files, sales for a specific department are likely split across many different workers. A department might have low sales on every individual worker, excluding it from any local Top 2 list, yet have the highest sales volume globally when summed together.</p><p>The diagram below illustrates how a scatter-gather approach would not work for this query. "Dept A" is the global sales leader, but because its sales are evenly spread across workers, it doesn’t make to some local Top 2 lists, and ends up being discarded by the coordinator.</p>
          <figure>
          <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/3ZJ6AfXzepKtJhiL6DcjiJ/07f4f523d871b25dcf444ee2ada546bd/image4.png" />
          </figure><p>Consequently, when the query orders results by their global aggregation, the coordinator cannot rely on pre-filtered results from workers. It must request the total count for <i>every</i> department from <i>every</i> worker to calculate the global totals before it can sort them. If you are grouping by a high-cardinality column like IP addresses or User IDs, this forces the coordinator to ingest and merge millions of rows, creating a resource bottleneck on a single node.</p><p>To solve this, we need <b>shuffling</b>, a way to colocate data for specific groups before the final aggregation occurs.</p>
    <div>
      <h3>Shuffling of aggregation data</h3>
      <a href="#shuffling-of-aggregation-data">
        
      </a>
    </div>
    <p>To address the challenges of random data distribution, we introduce a <b>shuffling stage</b>. Instead of sending results to the coordinator, workers exchange data directly with each other to colocate rows based on their grouping key.</p><p>This routing relies on <b>deterministic hash partitioning</b>. When a worker processes a row, it hashes the <code>GROUP BY</code> column to identify the destination worker. Because this hash is deterministic, every worker in the cluster independently agrees on where to send specific data. If "Engineering" hashes to Worker 5, every worker knows to route "Engineering" rows to Worker 5. No central registry is required.</p><p>The diagram below illustrates this flow. Notice how "Dept A" starts on Workers 1, 2 and 3. Because the hash function maps "Dept A" to Worker 1, all workers route those rows to that same destination.</p>
          <figure>
          <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/3Mw7FvL7ZJgDZqnh3ygkZM/9cfb493b5889d7efe43e4719d9523c93/image1.png" />
          </figure><p>Shuffling aggregates produces the correct results. However, this all-to-all exchange creates a timing dependency. If Worker 1 begins calculating the final total for "Dept A" before Worker 3 has finished sending its share of the data, the result will be incomplete.</p><p>To address this, we enforce a strict <b>synchronization barrier</b>. The coordinator tracks the progress of the entire cluster while workers buffer their outgoing data and flush it via <a href="https://grpc.io/"><u>gRPC</u></a> streams to their peers. Only when every worker confirms that it has finished processing its input files and flushing its shuffle buffers does the coordinator issue the command to proceed. This barrier guarantees that when the next stage begins, the dataset on each worker is complete and accurate.</p>
    <div>
      <h3>Local finalization</h3>
      <a href="#local-finalization">
        
      </a>
    </div>
    <p>Once the synchronization barrier is lifted, every worker holds the complete dataset for its assigned groups. Worker 1 now has 100% of the sales records for "Dept A" and can calculate the final total with certainty.</p><p>This allows us to push computational logic like filtering and sorting down to the worker rather than burdening the coordinator. For example, if the query includes <code>HAVING count(*) &gt; 5</code>, the worker can filter out groups that do not meet this criteria immediately after aggregation.</p><p>At the end of this stage, each worker produces a sorted, finalized stream of results for the groups it owns.</p>
    <div>
      <h3>The streaming merge</h3>
      <a href="#the-streaming-merge">
        
      </a>
    </div>
    <p>The final piece of the puzzle is the coordinator. In the scatter-gather model, the coordinator was responsible for the expensive task of aggregating and sorting the entire dataset. In the shuffling model, its role changes.</p><p>Because the workers have already computed the final aggregates and sorted them locally, the coordinator only needs to perform a <b>k-way merge</b>. It opens a stream to every worker and reads the results row by row. It compares the current row from each worker, picks the "winner" based on the sort order, and adds it to the query results that will be sent to the user.</p><p>This approach is particularly powerful for <code>LIMIT</code> queries. If a user asks for the top 10 departments, the coordinator merges the streams until it has found the top 10 items and then immediately stops processing. It does not need to load or merge the millions of remaining rows, allowing for greater scale of operation without over-consumption of compute resources.</p>
    <div>
      <h2>A powerful engine for processing massive datasets</h2>
      <a href="#a-powerful-engine-for-processing-massive-datasets">
        
      </a>
    </div>
    <p>With the addition of aggregations, <a href="https://developers.cloudflare.com/r2-sql/?cf_target_id=84F4CFDF79EFE12291D34EF36907F300"><u>R2 SQL</u></a> transforms from a tool great for filtering data into a powerful engine capable of data processing on massive datasets. This is made possible by implementing distributed execution strategies like scatter-gather and shuffling, where we are able to push the compute to where the data lives, using the scale of Cloudflare’s global compute and network. </p><p>Whether you are generating reports, monitoring high-volume logs for anomalies, or simply trying to spot trends in your data, you can now easily do it all within Cloudflare’s Developer Platform without the overhead of managing complex OLAP infrastructure or moving data out of R2.</p>
    <div>
      <h2>Try it now</h2>
      <a href="#try-it-now">
        
      </a>
    </div>
    <p>Support for aggregations in R2 SQL is available today. We are excited to see how you use these new functions with data in R2 Data Catalog.</p><ul><li><p><b>Get Started:</b> Check out our <a href="https://developers.cloudflare.com/r2-sql/sql-reference/"><u>documentation</u></a> for examples and syntax guides on running aggregation queries.</p></li><li><p><b>Join the Conversation:</b> If you have questions, feedback, or want to share what you’re building, join us in the Cloudflare <a href="https://discord.com/invite/cloudflaredev"><u>Developer Discord</u></a>.</p></li></ul><p></p> ]]></content:encoded>
            <category><![CDATA[R2]]></category>
            <category><![CDATA[Data]]></category>
            <category><![CDATA[Edge Computing]]></category>
            <category><![CDATA[Rust]]></category>
            <category><![CDATA[Serverless]]></category>
            <category><![CDATA[SQL]]></category>
            <guid isPermaLink="false">1qWQCp4QfhsZAs27s7fEc0</guid>
            <dc:creator>Jérôme Schneider</dc:creator>
            <dc:creator>Nikita Lapkov</dc:creator>
            <dc:creator>Marc Selwan</dc:creator>
        </item>
        <item>
            <title><![CDATA[R2 SQL: a deep dive into our new distributed query engine]]></title>
            <link>https://blog.cloudflare.com/r2-sql-deep-dive/</link>
            <pubDate>Thu, 25 Sep 2025 14:00:00 GMT</pubDate>
            <description><![CDATA[ R2 SQL provides a built-in, serverless way to run ad-hoc analytic queries against your R2 Data Catalog. This post dives deep under the Iceberg into how we built this distributed engine. ]]></description>
            <content:encoded><![CDATA[ <p>How do you run SQL queries over petabytes of data… without a server?</p><p>We have an answer for that: <a href="https://developers.cloudflare.com/r2-sql/"><u>R2 SQL</u></a>, a serverless query engine that can sift through enormous datasets and return results in seconds.</p><p>This post details the architecture and techniques that make this possible. We'll walk through our Query Planner, which uses <a href="https://developers.cloudflare.com/r2/data-catalog/"><u>R2 Data Catalog</u></a> to prune terabytes of data before reading a single byte, and explain how we distribute the work across Cloudflare’s <a href="https://www.cloudflare.com/network"><u>global network</u></a>, <a href="https://developers.cloudflare.com/workers/"><u>Workers</u></a> and <a href="https://www.cloudflare.com/developer-platform/products/r2/"><u>R2</u></a> for massively parallel execution.</p>
    <div>
      <h3>From catalog to query</h3>
      <a href="#from-catalog-to-query">
        
      </a>
    </div>
    <p>During Developer Week 2025, we <a href="https://blog.cloudflare.com/r2-data-catalog-public-beta/"><u>launched</u></a> R2 Data Catalog, a managed <a href="https://iceberg.apache.org/"><u>Apache Iceberg</u></a> catalog built directly into your Cloudflare R2 bucket. Iceberg is an open table format that provides critical database features like transactions and schema evolution for petabyte-scale <a href="https://www.cloudflare.com/learning/cloud/what-is-object-storage/">object storage</a>. It gives you a reliable catalog of your data, but it doesn’t provide a way to query it.</p><p>Until now, reading your R2 Data Catalog required setting up a separate service like <a href="https://spark.apache.org/"><u>Apache Spark</u></a> or <a href="https://trino.io/"><u>Trino</u></a>. Operating these engines at scale is not easy: you need to provision clusters, manage resource usage, and be responsible for their availability, none of which contributes to the primary goal of getting value from your data.</p><p><a href="https://developers.cloudflare.com/r2-sql/"><u>R2 SQL</u></a> removes that step entirely. It’s a serverless query engine that executes retrieval SQL queries against your Iceberg tables, right where your data lives.</p>
    <div>
      <h3>Designing a query engine for petabytes</h3>
      <a href="#designing-a-query-engine-for-petabytes">
        
      </a>
    </div>
    <p>Object storage is fundamentally different from a traditional database’s storage. A database is structured by design; R2 is an ocean of objects, where a single logical table can be composed of potentially millions of individual files, large and small, with more arriving every second.</p><p>Apache Iceberg provides a powerful layer of logical organization on top of this reality. It works by managing the table's state as an immutable series of snapshots, creating a reliable, structured view of the table by manipulating lightweight metadata files instead of rewriting the data files themselves.</p><p>However, this logical structure doesn't change the underlying physical challenge: an efficient query engine must still find the specific data it needs within that vast collection of files, and this requires overcoming two major technical hurdles:</p><p><b>The I/O problem</b>: A core challenge for query efficiency is minimizing the amount of data read from storage. A brute-force approach of reading every object is simply not viable. The primary goal is to read only the data that is absolutely necessary.</p><p><b>The Compute problem</b>: The amount of data that does need to be read can still be enormous. We need a way to give the right amount of compute power to a query, which might be massive, for just a few seconds, and then scale it down to zero instantly to avoid waste.</p><p>Our architecture for R2 SQL is designed to solve these two problems with a two-phase approach: a <b>Query Planner</b> that uses metadata to intelligently prune the search space, and a <b>Query Execution</b> system that distributes the work across Cloudflare's global network to process the data in parallel.</p>
    <div>
      <h2>Query Planner</h2>
      <a href="#query-planner">
        
      </a>
    </div>
    <p>The most efficient way to process data is to avoid reading it in the first place. This is the core strategy of the R2 SQL Query Planner. Instead of exhaustively scanning every file, the planner makes use of the metadata structure provided by R2 Data Catalog to prune the search space, that is, to avoid reading huge swathes of data irrelevant to a query.</p><p>This is a top-down investigation where the planner navigates the hierarchy of Iceberg metadata layers, using <b>stats</b> at each level to build a fast plan, specifying exactly which byte ranges the query engine needs to read.</p>
    <div>
      <h3>What do we mean by “stats”?</h3>
      <a href="#what-do-we-mean-by-stats">
        
      </a>
    </div>
    <p>When we say the planner uses "stats" we are referring to summary metadata that Iceberg stores about the contents of the data files. These statistics create a coarse map of the data, allowing the planner to make decisions about which files to read, and which to ignore, without opening them.</p><p>There are two primary levels of statistics the planner uses for pruning:</p><p><b>Partition-level stats</b>: Stored in the Iceberg manifest list, these stats describe the range of partition values for all the data in a given Iceberg manifest file. For a partition on <code>day(event_timestamp)</code>, this would be the earliest and latest day present in the files tracked by that manifest.</p><p><b>Column-level stats</b>: Stored in the manifest files, these are more granular stats about each individual data file. Data files in R2 Data Catalog are formatted using the <a href="https://parquet.apache.org/"><u>Apache Parquet</u></a>. For every column of a Parquet file, the manifest stores key information like:</p><ul><li><p>The minimum and maximum values. If a query asks for <code>http_status = 500</code>, and a file’s stats show its <code>http_status</code> column has a min of 200 and a max of 404, that entire file can be skipped.</p></li><li><p>A count of null values. This allows the planner to skip files when a query specifically looks for non-null values (e.g.,<code> WHERE error_code IS NOT NULL</code>) and the file's metadata reports that all values for <code>error_code</code> are null.</p></li></ul><p>Now, let's see how the planner uses these stats as it walks through the metadata layers.</p>
    <div>
      <h3>Pruning the search space</h3>
      <a href="#pruning-the-search-space">
        
      </a>
    </div>
    <p>The pruning process is a top-down investigation that happens in three main steps:</p><ol><li><p><b>Table metadata and the current snapshot</b></p></li></ol><p>The planner begins by asking the catalog for the location of the current table metadata. This is a JSON file containing the table's current schema, partition specs, and a log of all historical snapshots. The planner then fetches the latest snapshot to work with.</p><p>2. <b>Manifest list and partition pruning</b></p><p>The current snapshot points to a single Iceberg manifest list. The planner reads this file and uses the partition-level stats for each entry to perform the first, most powerful pruning step, discarding any manifests whose partition value ranges don't satisfy the query. For a table partitioned by <code>day(event_timestamp</code>), the planner can use the min/max values in the manifest list to immediately discard any manifests that don't contain data for the days relevant to the query.</p><p>3.<b> Manifests and file-level pruning</b></p><p>For the remaining manifests, the planner reads each one to get a list of the actual Parquet data files. These manifest files contain more granular, column-level stats for each individual data file they track. This allows for a second pruning step, discarding entire data files that cannot possibly contain rows matching the query's filters.</p><p>4. <b>File row-group pruning</b></p><p>Finally, for the specific data files that are still candidates, the Query Planner uses statistics stored inside Parquet file's footers to skip over entire row groups.</p><p>The result of this multi-layer pruning is a precise list of Parquet files, and of row groups within those Parquet files. These become the query work units that are dispatched to the Query Execution system for processing.</p>
          <figure>
          <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/7GKvgbex2vhIBqQ1G5UFjQ/2a99db7ae786b8e22a326bac0c9037d9/1.png" />
          </figure>
    <div>
      <h3>The Planning pipeline</h3>
      <a href="#the-planning-pipeline">
        
      </a>
    </div>
    <p>In R2 SQL, the multi-layer pruning we've described so far isn't a monolithic process. For a table with millions of files, the metadata can be too large to process before starting any real work. Waiting for a complete plan would introduce significant latency.</p><p>Instead, R2 SQL treats planning and execution together as a concurrent pipeline. The planner's job is to produce a stream of work units for the executor to consume as soon as they are available.</p><p>The planner’s investigation begins with two fetches to get a map of the table's structure: one for the table’s snapshot and another for the manifest list.</p>
    <div>
      <h4>Starting execution as early as possible</h4>
      <a href="#starting-execution-as-early-as-possible">
        
      </a>
    </div>
    <p>From that point on, the query is processed in a streaming fashion. As the Query Planner reads through the manifest files and subsequently the data files they point to and prunes them, it immediately emits any matching data files/row groups as work units to the execution queue.</p><p>This pipeline structure ensures the compute nodes can begin the expensive work of data I/O almost instantly, long before the planner has finished its full investigation.</p><p>On top of this pipeline model, the planner adds a crucial optimization: <b>deliberate ordering</b>. The manifest files are not streamed in an arbitrary sequence. Instead, the planner processes them in an order matching by the query's <code>ORDER BY</code> clause, guided by the metadata stats. This ensures that the data most likely to contain the desired results is processed first.</p><p>These two concepts work together to address query latency from both ends of the query pipeline.</p><p>The streamed planning pipeline lets us start crunching data as soon as possible, minimizing the delay before the first byte is processed. At the other end of the pipeline, the deliberate ordering of that work lets us finish early by finding a definitive result without scanning the entire dataset.</p><p>The next section explains the mechanics behind this "finish early" strategy.</p>
    <div>
      <h4>Stopping early: how to finish without reading everything</h4>
      <a href="#stopping-early-how-to-finish-without-reading-everything">
        
      </a>
    </div>
    <p>Thanks to the Query Planner streaming work units in an order matching the <code>ORDER BY </code>clause, the Query Execution system first processes the data that is most likely to be in the final result set.</p><p>This prioritization happens at two levels of the metadata hierarchy:</p><p><b>Manifest ordering</b>: The planner first inspects the manifest list. Using the partition stats for each manifest (e.g., the latest timestamp in that group of files), it decides which entire manifest files to stream first.</p><p><b>Parquet file ordering</b>: As it reads each manifest, it then uses the more granular column-level stats to decide the processing order of the individual Parquet files within that manifest.</p><p>This ensures a constantly prioritized stream of work units is sent to the execution engine. This prioritized stream is what allows us to stop the query early.</p><p>For instance, with a query like ... <code>ORDER BY timestamp DESC LIMIT 5</code>, as the execution engine processes work units and sends back results, the planner does two things concurrently:</p><p>It maintains a bounded heap of the best 5 results seen so far, constantly comparing new results to the oldest timestamp in the heap.</p><p>It keeps a "high-water mark" on the stream itself. Thanks to the metadata, it always knows the absolute latest timestamp of any data file that has not yet been processed.</p><p>The planner is constantly comparing the state of the heap to the water mark of the remaining stream. The moment the oldest timestamp in our Top 5 heap is newer than the high-water mark of the remaining stream, the entire query can be stopped.</p><p>At that point, we can prove no remaining work unit could possibly contain a result that would make it into the top 5. The pipeline is halted, and a complete, correct result is returned to the user, often after reading only a fraction of the potentially matching data.</p><p>Currently, R2 SQL supports ordering on columns that are part of the table's partition key only. This is a limitation we are working on lifting in the future.</p>
          <figure>
          <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/5qN9TeEuRZJIidYXFictG/8a55cc6088be3abdc3b27878daa76e40/image4.png" />
          </figure>
    <div>
      <h3>Architecture</h3>
      <a href="#architecture">
        
      </a>
    </div>
    
          <figure>
          <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/3wkvnT24y5E0k5064cqu0T/939402d16583647986eec87617379900/image3.png" />
          </figure>
    <div>
      <h2>Query Execution</h2>
      <a href="#query-execution">
        
      </a>
    </div>
    <p>Query Planner streams the query work in bite-sized pieces called row groups. A single Parquet file usually contains multiple row groups, but most of the time only a few of them contain relevant data. Splitting query work into row groups allows R2 SQL to only read small parts of potentially multi-GB Parquet files.</p><p>The server that receives the user’s request and performs query planning assumes the role of query coordinator. It distributes the work across query workers and aggregates results before returning them to the user.</p><p>Cloudflare’s network is vast, and many servers can be in maintenance at the same time. The query coordinator contacts Cloudflare’s internal API to make sure only healthy, fully functioning servers are picked for query execution. Connections between coordinator and query worker go through <a href="https://www.cloudflare.com/en-gb/application-services/products/argo-smart-routing/"><u>Cloudflare Argo Smart Routing</u></a> to ensure fast, reliable connectivity.</p><p>Servers that receive query execution requests from the coordinator assume the role of query workers. Query workers serve as a point of horizontal scalability in R2 SQL. With a higher number of query workers, R2 SQL can process queries faster by distributing the work among many servers. That’s especially true for queries covering large amounts of files.</p><p>Both the coordinator and query workers run on Cloudflare’s distributed network, ensuring R2 SQL has plenty of compute power and I/O throughput to handle analytical workloads.</p><p>Each query worker receives a batch of row groups from the coordinator as well as an SQL query to run on it. Additionally, the coordinator sends serialized metadata about Parquet files containing the row groups. Thanks to that, query workers know exact byte offsets where each row group is located in the Parquet file without the need to read this information from R2.</p>
    <div>
      <h3>Apache DataFusion</h3>
      <a href="#apache-datafusion">
        
      </a>
    </div>
    <p>Internally, each query worker uses <a href="https://github.com/apache/datafusion"><u>Apache DataFusion</u></a> to run SQL queries against row groups. DataFusion is an open-source analytical query engine written in Rust. It is built around the concept of partitions. A query is split into multiple concurrent independent streams, each working on its own partition of data.</p><p>Partitions in DataFusion are similar to partitions in Iceberg, but serve a different purpose. In Iceberg, partitions are a way to physically organize data on object storage. In DataFusion, partitions organize in-memory data for query processing. While logically they are similar – rows grouped together based on some logic – in practice, a partition in Iceberg doesn’t always correspond to a partition in DataFusion.</p><p>DataFusion partitions map perfectly to the R2 SQL query worker’s data model because each row group can be considered its own independent partition. Thanks to that, each row group is processed in parallel.</p><p>At the same time, since row groups usually contain at least 1000 rows, R2 SQL benefits from vectorized execution. Each DataFusion partition stream can execute the SQL query on multiple rows in one go, amortizing the overhead of query interpretation.</p><p>There are two ends of the spectrum when it comes to query execution: processing all rows sequentially in one big batch and processing each individual row in parallel. Sequential processing creates a so-called “tight loop”, which is usually more CPU cache friendly. In addition to that, we can significantly reduce interpretation overhead, as processing a large number of rows at a time in batches means that we go through the query plan less often. Completely parallel processing doesn’t allow us to do these things, but makes use of multiple CPU cores to finish the query faster.</p><p>DataFusion’s architecture allows us to achieve a balance on this scale, reaping benefits from both ends. For each data partition, we gain better CPU cache locality and amortized interpretation overhead. At the same time, since many partitions are processed in parallel, we distribute the workload between multiple CPUs, cutting the execution time further.</p>
          <figure>
          <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/2Tis1F5C1x3x6sIyJLL8ju/aae094818b1b7f6f8d6f857305948fbd/image1.png" />
          </figure><p>In addition to the smart query execution model, DataFusion also provides first-class Parquet support.</p><p>As a file format, Parquet has multiple optimizations designed specifically for query engines. Parquet is a column-based format, meaning that each column is physically separated from others. This separation allows better compression ratios, but it also allows the query engine to read columns selectively. If the query only ever uses five columns, we can only read them and skip reading the remaining fifty. This massively reduces the amount of data we need to read from R2 and the CPU time spent on decompression.</p><p>DataFusion does exactly that. Using R2 ranged reads, it is able to read parts of the Parquet files containing the requested columns, skipping the rest.</p><p>DataFusion’s optimizer also allows us to push down any filters to the lowest levels of the query plan. In other words, we can apply filters right as we are reading values from Parquet files. This allows us to skip materialization of results we know for sure won’t be returned to the user, cutting the query execution time further.</p>
    <div>
      <h3>Returning query results</h3>
      <a href="#returning-query-results">
        
      </a>
    </div>
    <p>Once the query worker finishes computing results, it returns them to the coordinator through <a href="https://grpc.io/"><u>the gRPC protocol</u></a>.</p><p>R2 SQL uses <a href="https://arrow.apache.org/"><u>Apache Arrow</u></a> for internal representation of query results. Arrow is an in-memory format that efficiently represents arrays of structured data. It is also used by DataFusion during query execution to represent partitions of data.</p><p>In addition to being an in-memory format, Arrow also defines the <a href="https://arrow.apache.org/docs/format/Columnar.html#format-ipc"><u>Arrow IPC</u></a> serialization format. Arrow IPC isn’t designed for long-term storage of the data, but for inter-process communication, which is exactly what query workers and the coordinator do over the network. The query worker serializes all the results into the Arrow IPC format and embeds them into the gRPC response. The coordinator in turn deserializes results and can return to working on Arrow arrays.</p>
    <div>
      <h2>Future plans</h2>
      <a href="#future-plans">
        
      </a>
    </div>
    <p>While R2 SQL is currently quite good at executing filter queries, we also plan to rapidly add new capabilities over the coming months. This includes, but is not limited to, adding:</p><ul><li><p>Support for complex aggregations in a distributed and scalable fashion;</p></li><li><p>Tools to help provide visibility in query execution to help developers improve performance;</p></li><li><p>Support for many of the configuration options Apache Iceberg supports.</p></li></ul><p>In addition to that, we have plans to improve our developer experience by allowing users to query their R2 Data Catalogs using R2 SQL from the Cloudflare Dashboard.</p><p>Given Cloudflare’s distributed compute, network capabilities, and ecosystem of developer tools, we have the opportunity to build something truly unique here. We are exploring different kinds of indexes to make R2 SQL queries even faster and provide more functionality such as full text search, geospatial queries, and more. </p>
    <div>
      <h2>Try it now!</h2>
      <a href="#try-it-now">
        
      </a>
    </div>
    <p>It’s early days for R2 SQL, but we’re excited for users to get their hands on it. R2 SQL is available in open beta today! Head over to our<a href="https://developers.cloudflare.com/r2-sql/get-started/"> <u>getting started guide</u></a> to learn how to create an end-to-end data pipeline that processes and delivers events to an R2 Data Catalog table, which can then be queried with R2 SQL.</p><p>
We’re excited to see what you build! Come share your feedback with us on our<a href="http://discord.cloudflare.com/"> <u>Developer Discord</u></a>.</p><div>
  
</div><p></p> ]]></content:encoded>
            <category><![CDATA[R2]]></category>
            <category><![CDATA[Birthday Week]]></category>
            <category><![CDATA[Data]]></category>
            <category><![CDATA[Deep Dive]]></category>
            <category><![CDATA[Edge Computing]]></category>
            <category><![CDATA[Rust]]></category>
            <category><![CDATA[Serverless]]></category>
            <category><![CDATA[SQL]]></category>
            <guid isPermaLink="false">7znvjodLkg1AxYlR992it2</guid>
            <dc:creator>Yevgen Safronov</dc:creator>
            <dc:creator>Nikita Lapkov</dc:creator>
            <dc:creator>Jérôme Schneider</dc:creator>
        </item>
        <item>
            <title><![CDATA[Building Jetflow: a framework for flexible, performant data pipelines at Cloudflare]]></title>
            <link>https://blog.cloudflare.com/building-jetflow-a-framework-for-flexible-performant-data-pipelines-at-cloudflare/</link>
            <pubDate>Wed, 23 Jul 2025 14:00:00 GMT</pubDate>
            <description><![CDATA[ Faced with a data-ingestion challenge at a massive scale, Cloudflare's Business Intelligence team built a new framework called Jetflow. ]]></description>
            <content:encoded><![CDATA[ <p>The Cloudflare Business Intelligence team manages a <a href="https://simple.wikipedia.org/wiki/Petabyte"><u>petabyte</u></a>-scale <a href="https://www.cloudflare.com/learning/cloud/what-is-a-data-lake/"><u>data lake</u></a> and ingests thousands of tables every day from many different sources. These include internal databases such as Postgres and ClickHouse, as well as external SaaS applications such as Salesforce. These tasks are often complex and tables may have hundreds of millions or billions of rows of new data each day. They are also business-critical for product decisions, growth plannings, and internal monitoring. In total, about <b>141 billion rows</b> are ingested every day.</p><p>As Cloudflare has grown, the data has become ever larger and more complex. Our existing <a href="https://www.ibm.com/think/topics/elt"><u>Extract Load Transform (ELT)</u></a> solution could no longer meet our technical and business requirements. After evaluating other common ELT solutions, we concluded that their performance generally did not surpass our current system, either.</p><p>It became clear that we needed to build our own framework to cope with our unique requirements — and so <b>Jetflow</b> was born. </p>
    <div>
      <h2>What we achieved</h2>
      <a href="#what-we-achieved">
        
      </a>
    </div>
    <p><b>Over 100x efficiency improvement in GB-s</b>:</p><ul><li><p>Our longest running job with 19 billion rows was taking <b>48 hours</b> using <b>300 GB of memory</b>, and now completes in <b>5.5 hours</b> using <b>4 GB of memory</b></p></li><li><p>We estimate that ingestion of 50 TB from Postgres via <b>Jetflow</b> could cost under $100 based on rates published by commercial cloud providers</p></li></ul><p><b>&gt;10x performance improvement:</b></p><ul><li><p>Our largest dataset was ingesting <b>60-80,000</b> rows per second, this is now <b>2-5 million</b> rows per second per database connection.</p></li><li><p>In addition, these numbers scale well with multiple database connections for some databases.</p></li></ul><p><b>Extensibility: </b></p><ul><li><p>The modular design makes it easy to extend and test<b>. </b>Today<b> Jetflow</b> works with ClickHouse, Postgres, Kafka, many different SaaS APIs, Google BigQuery and many others. It has continued to work well and remain flexible with the addition of new use cases.</p></li></ul>
    <div>
      <h2>How did we do this?</h2>
      <a href="#how-did-we-do-this">
        
      </a>
    </div>
    
    <div>
      <h3>Requirements</h3>
      <a href="#requirements">
        
      </a>
    </div>
    <p>The first step to designing our new framework had to be a clear understanding of the problems we were aiming to solve, with clear requirements to stop us creating new ones.</p>
    <div>
      <h5>Performant &amp; efficient</h5>
      <a href="#performant-efficient">
        
      </a>
    </div>
    <p>We needed to be able to move more data in less time as some ingestion jobs were taking ~24 hours, and our data will only grow. The data should be ingested in a streaming fashion and use less memory and compute resources than our existing solution.</p>
    <div>
      <h5>Backwards compatible </h5>
      <a href="#backwards-compatible">
        
      </a>
    </div>
    <p>Given the daily ingestion of thousands of tables, the chosen solution needed to allow for the migration of individual tables as needed. Due to our usage of <a href="https://spark.apache.org/"><u>Spark</u></a> downstream and Spark's limitations in merging disparate <a href="https://parquet.apache.org/"><u>Parquet</u></a> schemas, the chosen solution had to offer the flexibility to generate the precise schemas needed for each case to match legacy.</p><p>We also required seamless integration with our custom metadata system, used for dependency checks and job status information.</p>
    <div>
      <h5>Ease of use</h5>
      <a href="#ease-of-use">
        
      </a>
    </div>
    <p>We want a configuration file that can be version-controlled, without introducing bottlenecks on repositories with many concurrent changes.</p><p>To increase accessibility for different roles within the team, another requirement was no-code (or configuration as code) in the vast majority of cases. Users should not have to worry about availability or translation of data types between source and target systems, or writing new code for each new ingestion. The configuration needed should also be minimal — for example, data schema should be inferred from the source system and not need to be supplied by the user.</p>
    <div>
      <h5>Customizable</h5>
      <a href="#customizable">
        
      </a>
    </div>
    <p>Striking a balance with the no-code requirement above, although we want a low bar of entry we also want to have the option to tune and override options if desired, with a flexible and optional configuration layer. For example, writing Parquet files is often more expensive than reading from the database, so we want to be able to allocate more resources and concurrency as needed. </p><p>Additionally, we wanted to allow for control over where the work is executed, with the ability to spin up concurrent workers in different threads, different containers, or on different machines. The execution of workers and communication of data was abstracted away with an interface, and different implementations can be written and injected, controlled via the job configuration. </p>
    <div>
      <h5>Testable</h5>
      <a href="#testable">
        
      </a>
    </div>
    <p>We wanted a solution capable of running locally in a containerized environment, which would allow us to write tests for every stage of the pipeline. With “black box” solutions, testing often means validating the output after making a change, which is a slow feedback loop, risks not testing all edge cases as there isn’t good visibility of all code paths internally, and makes debugging issues painful.</p>
    <div>
      <h3>Designing a flexible framework </h3>
      <a href="#designing-a-flexible-framework">
        
      </a>
    </div>
    <p>To build a truly flexible framework, we broke the pipeline down into distinct stages, and then create a config layer to define the composition of the pipeline from these stages, and any configuration overrides. Every pipeline configuration that makes sense logically should execute correctly, and users should not be able to create pipeline configs that do not work. </p>
    <div>
      <h5>Pipeline configuration</h5>
      <a href="#pipeline-configuration">
        
      </a>
    </div>
    <p>This led us to a design where we created stages which were classified according to the meaningfully different categories of:</p><ul><li><p>Consumers</p></li><li><p>Transformers</p></li><li><p>Loaders</p></li></ul>
          <figure>
          <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/1ALi9AXyo5v1Y7cIjK619V/3c0735e2d5b36d5660072f92fe551ed3/image3.png" />
          </figure><p>The pipeline was constructed via a <a href="https://yaml.org/"><u>YAML</u></a> file that required a consumer, zero or more transformers, and at least one loader. Consumers create a data stream (via reading from the source system), Transformers (e.g. data transformations, validations) take a data stream input and output a data stream conforming to the same API so that they can be chained, and Loaders have the same data streaming interface, but are the stages with persistent effects — i.e. stages where data is saved to an external system. </p><p>This modular design means that each stage is independently testable, with shared behaviour (such as error handling and concurrency) inherited from shared base stages, significantly decreasing development time for new use cases and increasing confidence in code correctness.</p>
    <div>
      <h5>Data divisions</h5>
      <a href="#data-divisions">
        
      </a>
    </div>
    <p>Next, we designed a breakdown for the data that would allow the pipeline to be idempotent both on whole pipeline re-run and also on internal retry of any data partition due to transient error. We decided on a design that let us parallelize processing, while maintaining meaningful data divisions that allowed the pipeline to perform cleanups of data where required for a retry.</p><ul><li><p><b>RunInstance</b>: the least granular division, corresponding to a business unit for a single run of the pipeline (e.g. one month/day/hour of data). </p></li><li><p><b>Partition</b>: a division of the RunInstance that allows each row to be allocated to a partition in a way that is deterministic and self-evident from the row data without external state, and is therefore idempotent on retry. (e.g. an accountId range, a 10-minute interval)</p></li><li><p><b>Batch</b>: a division of the partition data that is non-deterministic and used only to break the data down into smaller chunks for streaming/parallel processing for faster processing with fewer resources. (e.g. 10k rows, 50 MB)</p></li></ul><p>The options that the user configures in the consumer stage YAML both construct the query that is used to retrieve the data from the source system, and also encode the semantic meaning of this data division in a system agnostic way, so that later stages understand what this data represents — e.g. this partition contains the data for all accounts IDs 0-500. This means that we can do targeted data cleanup and avoid, for example, duplicate data entries if a single data partition is retried due to error.</p>
          <figure>
          <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/1NQsitJmwRwSpiLkj2Hoig/81db1750523268bb427d51e1d2746a46/image2.png" />
          </figure>
    <div>
      <h3>Framework implementation</h3>
      <a href="#framework-implementation">
        
      </a>
    </div>
    
    <div>
      <h5>Standard internal state for stage compatibility </h5>
      <a href="#standard-internal-state-for-stage-compatibility">
        
      </a>
    </div>
    <p>Our most common use case is something like read from a database, convert to Parquet format, and then save to <a href="https://www.cloudflare.com/learning/cloud/what-is-object-storage/">object storage,</a> with each of these steps being a separate stage. As more use cases were onboarded to <b>Jetflow,</b> we had to make sure that if someone wrote a new stage it would be compatible with the other stages. We don’t want to create a situation where new code needs to be written for every output format and target system, or you end up with a custom pipeline for every different use case.</p><p>The way we have solved this problem is by having our stage extractor class only allow output data in a single format. This means as long as any downstream stages support this format as in the input and output format they would be compatible with the rest of the pipeline. This seems obvious in retrospect, but internally was a painful learning experience, as we originally created a custom type system and struggled with stage interoperability. </p><p>For this internal format, we chose to use <a href="https://arrow.apache.org/"><u>Arrow</u></a>, an in-memory columnar data format. The key benefits of this format for us are:</p><ul><li><p><b>Arrow ecosystem</b>: Many data projects now support Arrow as an output format. This means when we write extractor stages for new data sources, it is often trivial to produce Arrow output.</p></li><li><p><b>No serialisation overhead</b>: This makes it easy to move Arrow data between machines and even programming languages with minimum overhead. <b>Jetflow</b> was designed from the start to have the flexibility to be able to run in a wide range of systems via a job controller interface, so this efficiency in data transmission means there’s minimal compromise on performance when creating distributed implementations.</p></li><li><p><b>Reserve memory in large fixed-size batches to avoid memory allocations</b>: As Go is a garbage collected (GC) language and GC cycle times are affected mostly by the number of objects rather than the sizes of those objects, fewer heap objects reduces CPU time spent garbage collecting significantly, even if the total size is the same. As the number of objects to scan, and possibly collect, during a GC cycle increases with the number of allocations, if we have 8192 rows with 10 columns each, Arrow would only require us to do 10 allocations versus the 8192 allocations of most drivers that allocate on a row by row basis, meaning fewer objects and lower GC cycle times with Arrow.</p></li></ul>
    <div>
      <h5>Converting rows to columns</h5>
      <a href="#converting-rows-to-columns">
        
      </a>
    </div>
    <p>Another important performance optimization was reducing the number of conversion steps that happen when reading and processing data. Most data ingestion frameworks internally represent data as rows. In our case, we are mostly writing data in Parquet format, which is column based. When reading data from column-based sources (e.g. ClickHouse, where most drivers receive RowBinary format), converting into row-based memory representations for the specific language implementation is inefficient. This is then converted again from rows to columns to write Parquet files. These conversions result in a significant performance impact.</p><p><b>Jetflow</b> instead reads data from column-based sources in columnar formats (e.g. for ClickHouse-native Block format) and then copies this data into Arrow column format. Parquet files are then written directly from Arrow columns. The simplification of this process improves performance.</p>
          <figure>
          <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/5HEuO0Cn6Wob7tuR9hjnSP/852cdc44244f107b4289fb3b3553d213/image1.png" />
          </figure>
    <div>
      <h3>Writing each pipelines stage</h3>
      <a href="#writing-each-pipelines-stage">
        
      </a>
    </div>
    
    <div>
      <h5>Case study: ClickHouse</h5>
      <a href="#case-study-clickhouse">
        
      </a>
    </div>
    <p>When testing an initial version of <b>Jetflow</b>, we discovered<b> </b>that due to the architecture of ClickHouse, using additional connections would not be of any benefit, since ClickHouse was reading faster than we were receiving data. It should then be possible, with a more optimized database driver, to take better advantage of that single connection to read a much larger number of rows per second, without needing additional connections.</p><p>Initially, a custom database driver was written for ClickHouse, but we ended up switching to the excellent <a href="https://github.com/ClickHouse/ch-go"><u>ch-go low level library</u></a>, which directly reads <a href="https://clickhouse.com/docs/development/architecture#block"><u>Blocks</u></a> from ClickHouse in a columnar format. This had a dramatic effect on performance in comparison to the standard Go driver. Combined with the framework optimisations above, we now <b>ingest millions of rows per second</b> with a single ClickHouse connection.</p><p>A valuable lesson learned is that as with any software, tradeoffs are often made for the sake of convenience or a common use case that may not match your own. Most database drivers tend not to be optimized for reading large batches of rows, and have high per-row overhead.</p>
    <div>
      <h5>Case study: Postgres</h5>
      <a href="#case-study-postgres">
        
      </a>
    </div>
    <p>For Postgres, we use the excellent <a href="https://github.com/jackc/pgx"><u>jackc/pgx</u></a> driver, but instead of using the database/sql Scan interface, we directly receive the raw bytes for each row and use the jackc/pgx internal scan functions for each Postgres OID (Object Identifier) type.</p><p>The database/sql Scan interface in Go uses reflection to understand the type passed to the function and then also uses reflection to set each field with the column value received from Postgres. In typical scenarios, this is fast enough and easy to use, but falls short for our use cases in terms of performance. The <a href="https://github.com/jackc/pgx"><u>jackc/pgx</u></a> driver reuses the row bytes produced each time the next Postgres row is requested, resulting in zero allocations per row. This allows us to write high-performance, low-allocation code within Jetflow. With this design, we are able to achieve nearly <b>600,000 rows per second</b> per Postgres connection for most tables, with very low memory usage.</p>
    <div>
      <h2>Conclusion</h2>
      <a href="#conclusion">
        
      </a>
    </div>
    <p>As of early July 2025, the team ingests <b>77 billion</b> records per day via <b>Jetflow</b>. The remaining jobs are in the process of being migrated to <b>Jetflow</b>, which will bring the total daily ingestion to 141 billion records. The framework has allowed us to ingest tables in cases that would not otherwise have been possible, and provided significant cost savings due to ingestions running for less time and with fewer resources. </p><p>In the future, we plan to open source the project, and if you are interested in joining our team to help develop tools like this, then open roles can be found at <a href="https://www.cloudflare.com/en-gb/careers/jobs/"><u>https://www.cloudflare.com/careers/jobs/</u></a>.</p> ]]></content:encoded>
            <category><![CDATA[Data]]></category>
            <category><![CDATA[Go]]></category>
            <category><![CDATA[Performance]]></category>
            <category><![CDATA[Design]]></category>
            <category><![CDATA[Engineering]]></category>
            <guid isPermaLink="false">4wAX6JGDuRNIJwVqwdgrP8</guid>
            <dc:creator>Harry Hough</dc:creator>
            <dc:creator>Rebecca Walton-Jones </dc:creator>
            <dc:creator>Andy Fan</dc:creator>
            <dc:creator>Ricardo Margalhau</dc:creator>
            <dc:creator>Uday Sharma</dc:creator>
        </item>
        <item>
            <title><![CDATA[Over 700 million events/second: How we make sense of too much data]]></title>
            <link>https://blog.cloudflare.com/how-we-make-sense-of-too-much-data/</link>
            <pubDate>Mon, 27 Jan 2025 14:00:00 GMT</pubDate>
            <description><![CDATA[ Here we explain how we made our data pipeline scale to 700 million events per second while becoming more resilient than ever before. We share some math behind our approach and some of the designs of  ]]></description>
            <content:encoded><![CDATA[ <p>Cloudflare's network provides an enormous array of services to our customers. We collect and deliver associated data to customers in the form of event logs and aggregated analytics. As of December 2024, our data pipeline is ingesting up to 706M events per second generated by Cloudflare's services, and that represents 100x growth since our <a href="https://blog.cloudflare.com/http-analytics-for-6m-requests-per-second-using-clickhouse/"><u>2018 data pipeline blog post</u></a>. </p><p>At peak, we are moving 107 <a href="https://simple.wikipedia.org/wiki/Gibibyte"><u>GiB</u></a>/s of compressed data, either pushing it directly to customers or subjecting it to additional queueing and batching.</p><p>All of these data streams power things like <a href="https://developers.cloudflare.com/logs/"><u>Logs</u></a>, <a href="https://developers.cloudflare.com/analytics/"><u>Analytics</u></a>, and billing, as well as other products, such as training machine learning models for bot detection. This blog post is focused on techniques we use to efficiently and accurately deal with the high volume of data we ingest for our Analytics products. A previous <a href="https://blog.cloudflare.com/cloudflare-incident-on-november-14-2024-resulting-in-lost-logs/"><u>blog post</u></a> provides a deeper dive into the data pipeline for Logs. </p><p>The pipeline can be roughly described by the following diagram.</p>
          <figure>
          <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/5ihv6JXx19nJiEyfCaCg8V/ad7081720514bafd070cc38a04bc7097/BLOG-2486_2.jpg" />
          </figure><p>The data pipeline has multiple stages, and each can and will naturally break or slow down because of hardware failures or misconfiguration. And when that happens, there is just too much data to be able to buffer it all for very long. Eventually some will get dropped, causing gaps in analytics and a degraded product experience unless proper mitigations are in place.</p>
    <div>
      <h3>Dropping data to retain information</h3>
      <a href="#dropping-data-to-retain-information">
        
      </a>
    </div>
    <p>How does one retain valuable information from more than half a billion events per second, when some must be dropped? Drop it in a controlled way, by downsampling.</p><p>Here is a visual analogy showing the difference between uncontrolled data loss and downsampling. In both cases the same number of pixels were delivered. One is a higher resolution view of just a small portion of a popular painting, while the other shows the full painting, albeit blurry and highly pixelated.</p>
          <figure>
          <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/4kUGB4RLQzFb7cphMpHqAg/e7ccf871c73e0e8ca9dcac32fe265f18/Screenshot_2025-01-24_at_10.57.17_AM.png" />
          </figure><p>As we noted above, any point in the pipeline can fail, so we want the ability to downsample at any point as needed. Some services proactively downsample data at the source before it even hits Logfwdr. This makes the information extracted from that data a little bit blurry, but much more useful than what otherwise would be delivered: random chunks of the original with gaps in between, or even nothing at all. The amount of "blur" is outside our control (we make our best effort to deliver full data), but there is a robust way to estimate it, as discussed in the <a href="/how-we-make-sense-of-too-much-data/#extracting-value-from-downsampled-data"><u>next section</u></a>.</p><p>Logfwdr can decide to downsample data sitting in the buffer when it overflows. Logfwdr handles many data streams at once, so we need to prioritize them by assigning each data stream a weight and then applying <a href="https://en.wikipedia.org/wiki/Max-min_fairness"><u>max-min fairness</u></a> to better utilize the buffer. It allows each data stream to store as much as it needs, as long as the whole buffer is not saturated. Once it is saturated, streams divide it fairly according to their weighted size.</p><p>In our implementation (Go), each data stream is driven by a goroutine, and they cooperate via channels. They consult a single tracker object every time they allocate and deallocate memory. The tracker uses a <a href="https://en.wikipedia.org/wiki/Heap_(data_structure)"><u>max-heap</u></a> to always know who the heaviest participant is and what the total usage is. Whenever the total usage goes over the limit, the tracker repeatedly sends the "please shed some load" signal to the heaviest participant, until the usage is again under the limit.</p><p>The effect of this is that healthy streams, which buffer a tiny amount, allocate whatever they need without losses. But any lagging streams split the remaining memory allowance fairly.</p><p>We downsample more or less uniformly, by always taking some of the least downsampled batches from the buffer (using min-heap to find those) and merging them together upon downsampling.</p>
          <figure>
          <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/15VP0VYkrvkQboX9hrOy0q/e3d087fe704bd1b0ee41eb5b7a24b899/BLOG-2486_4.png" />
          </figure><p><sup><i>Merging keeps the batches roughly the same size and their number under control.</i></sup></p><p>Downsampling is cheap, but since data in the buffer is compressed, it causes recompression, which is the single most expensive thing we do to the data. But using extra CPU time is the last thing you want to do when the system is under heavy load! We compensate for the recompression costs by starting to downsample the fresh data as well (before it gets compressed for the first time) whenever the stream is in the "shed the load" state.</p><p>We called this approach "bottomless buffers", because you can squeeze effectively infinite amounts of data in there, and it will just automatically be thinned out. Bottomless buffers resemble <a href="https://en.wikipedia.org/wiki/Reservoir_sampling"><u>reservoir sampling</u></a>, where the buffer is the reservoir and the population comes as the input stream. But there are some differences. First is that in our pipeline the input stream of data never ends, while reservoir sampling assumes it ends to finalize the sample. Secondly, the resulting sample also never ends.</p><p>Let's look at the next stage in the pipeline: Logreceiver. It sits in front of a distributed queue. The purpose of logreceiver is to partition each stream of data by a key that makes it easier for Logpush, Analytics inserters, or some other process to consume.</p><p>Logreceiver proactively performs adaptive sampling of analytics. This improves the accuracy of analytics for small customers (receiving on the order of 10 events per day), while more aggressively downsampling large customers (millions of events per second). Logreceiver then pushes the same data at multiple resolutions (100%, 10%, 1%, etc.) into different topics in the distributed queue. This allows it to keep pushing something rather than nothing when the queue is overloaded, by just skipping writing the high-resolution samples of data.</p><p>The same goes for Inserters: they can skip <i>reading or writing</i> high-resolution data. The Analytics APIs can skip <i>reading</i> high resolution data. The analytical database might be unable to read high resolution data because of overload or degraded cluster state or because there is just too much to read (very wide time range or very large customer). Adaptively dropping to lower resolutions allows the APIs to return <i>some</i> results in all of those cases.</p>
    <div>
      <h3>Extracting value from downsampled data</h3>
      <a href="#extracting-value-from-downsampled-data">
        
      </a>
    </div>
    <p>Okay, we have some downsampled data in the analytical database. It looks like the original data, but with some rows missing. How do we make sense of it? How do we know if the results can be trusted?</p><p>Let's look at the math.</p>Since the amount of sampling can vary over time and between nodes in the distributed system, we need to store this information along with the data. With each event $x_i$ we store its sample interval, which is the reciprocal to its inclusion probability $\pi_i = \frac{1}{\text{sample interval}}$. For example, if we sample 1 in every 1,000 events, each of the events included in the resulting sample will have its $\pi_i = 0.001$, so the sample interval will be 1,000. When we further downsample that batch of data, the inclusion probabilities (and the sample intervals) multiply together: a 1 in 1,000 sample from a 1 in 1,000 sample is a 1 in 1,000,000 sample of the original population. The sample interval of an event can also be interpreted roughly as the number of original events that this event represents, so in the literature it is known as weight $w_i = \frac{1}{\pi_i}$.
<p></p>
We rely on the <a href="https://en.wikipedia.org/wiki/Horvitz%E2%80%93Thompson_estimator">Horvitz-Thompson estimator</a> (HT, <a href="https://www.stat.cmu.edu/~brian/905-2008/papers/Horvitz-Thompson-1952-jasa.pdf">paper</a>) in order to derive analytics about $x_i$. It gives two estimates: the analytical estimate (e.g. the population total or size) and the estimate of the variance of that estimate. The latter enables us to figure out how accurate the results are by building <a href="https://en.wikipedia.org/wiki/Confidence_interval">confidence intervals</a>. They define ranges that cover the true value with a given probability <i>(confidence level)</i>. A typical confidence level is 0.95, at which a confidence interval (a, b) tells that you can be 95% sure the true SUM or COUNT is between a and b.
<p></p><p>So far, we know how to use the HT estimator for doing SUM, COUNT, and AVG.</p>Given a sample of size $n$, consisting of values $x_i$ and their inclusion probabilities $\pi_i$, the HT estimator for the population total (i.e. SUM) would be

$$\widehat{T}=\sum_{i=1}^n{\frac{x_i}{\pi_i}}=\sum_{i=1}^n{x_i w_i}.$$

The variance of $\widehat{T}$ is:

$$\widehat{V}(\widehat{T}) = \sum_{i=1}^n{x_i^2 \frac{1 - \pi_i}{\pi_i^2}} + \sum_{i \neq j}^n{x_i x_j \frac{\pi_{ij} - \pi_i \pi_j}{\pi_{ij} \pi_i \pi_j}},$$

where $\pi_{ij}$ is the probability of both $i$-th and $j$-th events being sampled together.
<p></p>
We use <a href="https://en.wikipedia.org/wiki/Poisson_sampling">Poisson sampling</a>, where each event is subjected to an independent <a href="https://en.wikipedia.org/wiki/Bernoulli_trial">Bernoulli trial</a> ("coin toss") which determines whether the event becomes part of the sample. Since each trial is independent, we can equate $\pi_{ij} = \pi_i \pi_j$, which when plugged in the variance estimator above turns the right-hand sum to zero:

$$\widehat{V}(\widehat{T}) = \sum_{i=1}^n{x_i^2 \frac{1 - \pi_i}{\pi_i^2}} + \sum_{i \neq j}^n{x_i x_j \frac{0}{\pi_{ij} \pi_i \pi_j}},$$

thus

$$\widehat{V}(\widehat{T}) = \sum_{i=1}^n{x_i^2 \frac{1 - \pi_i}{\pi_i^2}} = \sum_{i=1}^n{x_i^2 w_i (w_i-1)}.$$

For COUNT we use the same estimator, but plug in $x_i = 1$. This gives us:

$$\begin{align}
\widehat{C} &amp;= \sum_{i=1}^n{\frac{1}{\pi_i}} = \sum_{i=1}^n{w_i},\\
\widehat{V}(\widehat{C}) &amp;= \sum_{i=1}^n{\frac{1 - \pi_i}{\pi_i^2}} = \sum_{i=1}^n{w_i (w_i-1)}.
\end{align}$$

For AVG we would use

$$\begin{align}
\widehat{\mu} &amp;= \frac{\widehat{T}}{N},\\
\widehat{V}(\widehat{\mu}) &amp;= \frac{\widehat{V}(\widehat{T})}{N^2},
\end{align}$$

if we could, but the original population size $N$ is not known, it is not stored anywhere, and it is not even possible to store because of custom filtering at query time. Plugging $\widehat{C}$ instead of $N$ only partially works. It gives a valid estimator for the mean itself, but not for its variance, so the constructed confidence intervals are unusable.
<p></p>
In all cases the corresponding pair of estimates are used as the $\mu$ and $\sigma^2$ of the normal distribution (because of the <a href="https://en.wikipedia.org/wiki/Central_limit_theorem">central limit theorem</a>), and then the bounds for the confidence interval (of confidence level ) are:

$$\Big( \mu - \Phi^{-1}\big(\frac{1 + \alpha}{2}\big) \cdot \sigma, \quad \mu + \Phi^{-1}\big(\frac{1 + \alpha}{2}\big) \cdot \sigma\Big).$$<p>We do not know the N, but there is a workaround: simultaneous confidence intervals. Construct confidence intervals for SUM and COUNT independently, and then combine them into a confidence interval for AVG. This is known as the <a href="https://www.sciencedirect.com/topics/mathematics/bonferroni-method"><u>Bonferroni method</u></a>. It requires generating wider (half the "inconfidence") intervals for SUM and COUNT. Here is a simplified visual representation, but the actual estimator will have to take into account the possibility of the orange area going below zero.</p>
          <figure>
          <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/69Vvi2CHSW8Gew0TWHSndj/1489cfe1ff57df4e7e1ca3c31a8444a5/BLOG-2486_5.png" />
          </figure><p>In SQL, the estimators and confidence intervals look like this:</p>
            <pre><code>WITH sum(x * _sample_interval)                              AS t,
     sum(x * x * _sample_interval * (_sample_interval - 1)) AS vt,
     sum(_sample_interval)                                  AS c,
     sum(_sample_interval * (_sample_interval - 1))         AS vc,
     -- ClickHouse does not expose the erf⁻¹ function, so we precompute some magic numbers,
     -- (only for 95% confidence, will be different otherwise):
     --   1.959963984540054 = Φ⁻¹((1+0.950)/2) = √2 * erf⁻¹(0.950)
     --   2.241402727604945 = Φ⁻¹((1+0.975)/2) = √2 * erf⁻¹(0.975)
     1.959963984540054 * sqrt(vt) AS err950_t,
     1.959963984540054 * sqrt(vc) AS err950_c,
     2.241402727604945 * sqrt(vt) AS err975_t,
     2.241402727604945 * sqrt(vc) AS err975_c
SELECT t - err950_t AS lo_total,
       t            AS est_total,
       t + err950_t AS hi_total,
       c - err950_c AS lo_count,
       c            AS est_count,
       c + err950_c AS hi_count,
       (t - err975_t) / (c + err975_c) AS lo_average,
       t / c                           AS est_average,
       (t + err975_t) / (c - err975_c) AS hi_average
FROM ...</code></pre>
            <p>Construct a confidence interval for each timeslot on the timeseries, and you get a confidence band, clearly showing the accuracy of the analytics. The figure below shows an example of such a band in shading around the line.</p>
          <figure>
          <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/4JEnnC6P4BhM8qB8J5yKqt/3635835967085f9b24f64a5731457ddc/BLOG-2486_6.png" />
          </figure>
    <div>
      <h3>Sampling is easy to screw up</h3>
      <a href="#sampling-is-easy-to-screw-up">
        
      </a>
    </div>
    <p>We started using confidence bands on our internal dashboards, and after a while noticed something scary: a systematic error! For one particular website the "total bytes served" estimate was higher than the true control value obtained from rollups, and the confidence bands were way off. See the figure below, where the true value (blue line) is outside the yellow confidence band at all times.</p>
          <figure>
          <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/CHCyKyXqPMj8DnMpBUf3N/772fb61f02b79c59417f66d9dc0b5d19/BLOG-2486_7.png" />
          </figure><p>We checked the stored data for corruption, it was fine. We checked the math in the queries, it was fine. It was only after reading through the source code for all of the systems responsible for sampling that we found a candidate for the root cause.</p><p>We used simple random sampling everywhere, basically "tossing a coin" for each event, but in Logreceiver sampling was done differently. Instead of sampling <i>randomly</i> it would perform <i>systematic sampling</i> by picking events at equal intervals starting from the first one in the batch.</p>
          <figure>
          <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/4xUwjxdylG5ARlFlDtv1OC/76db68677b7ae072b0a065f59d82c6f2/BLOG-2486_8.png" />
          </figure><p>Why would that be a problem?</p>There are two reasons. The first is that we can no longer claim $\pi_{ij} = \pi_i \pi_j$, so the simplified variance estimator stops working and confidence intervals cannot be trusted. But even worse, the estimator for the total becomes biased. To understand why exactly, we wrote a short repro code in Python:
<br /><p></p>
            <pre><code>import itertools

def take_every(src, period):
    for i, x in enumerate(src):
    if i % period == 0:
        yield x

pattern = [10, 1, 1, 1, 1, 1]
sample_interval = 10 # bad if it has common factors with len(pattern)
true_mean = sum(pattern) / len(pattern)

orig = itertools.cycle(pattern)
sample_size = 10000
sample = itertools.islice(take_every(orig, sample_interval), sample_size)

sample_mean = sum(sample) / sample_size

print(f"{true_mean=} {sample_mean=}")</code></pre>
            <p>After playing with different values for <code><b>pattern</b></code> and <code><b>sample_interval</b></code> in the code above, we realized where the bias was coming from.</p><p>Imagine a person opening a huge generated HTML page with many small/cached resources, such as icons. The first response will be big, immediately followed by a burst of small responses. If the website is not visited that much, responses will tend to end up all together at the start of a batch in Logfwdr. Logreceiver does not cut batches, only concatenates them. The first response remains first, so it always gets picked and skews the estimate up.</p>
          <figure>
          <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/2WZUzqCwr2A6WgX1T5UE8z/7a2e08b611fb64e64a61e3d5c792fe23/BLOG-2486_9.png" />
          </figure><p>We checked the hypothesis against the raw unsampled data that we happened to have because that particular website was also using one of the <a href="https://developers.cloudflare.com/logs/"><u>Logs</u></a> products. We took all events in a given time range, and grouped them by cutting at gaps of at least one minute. In each group, we ranked all events by time and looked at the variable of interest (response size in bytes), and put it on a scatter plot against the rank inside the group.</p>
          <figure>
          <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/2IXtqGkRjV0xs3wvwx609A/81e67736cacbccdd839c2177769ee4fe/BLOG-2486_10.png" />
          </figure><p>A clear pattern! The first response is much more likely to be larger than average.</p><p>We fixed the issue by making Logreceiver shuffle the data before sampling. As we rolled out the fix, the estimation and the true value converged.</p>
          <figure>
          <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/4TL1pKDLw7MA6yGMSCahJN/227cb22054e0e8fe65c7766aa6e4b541/BLOG-2486_11.png" />
          </figure><p>Now, after battle testing it for a while, we are confident the HT estimator is implemented properly and we are using the correct sampling process.</p>
    <div>
      <h3>Using Cloudflare's analytics APIs to query sampled data</h3>
      <a href="#using-cloudflares-analytics-apis-to-query-sampled-data">
        
      </a>
    </div>
    <p>We already power most of our analytics datasets with sampled data. For example, the <a href="https://developers.cloudflare.com/analytics/analytics-engine/"><u>Workers Analytics Engine</u></a> exposes the <a href="https://developers.cloudflare.com/analytics/analytics-engine/sql-api/#sampling"><u>sample interval</u></a> in SQL, allowing our customers to build their own dashboards with confidence bands. In the GraphQL API, all of the data nodes that have "<a href="https://developers.cloudflare.com/analytics/graphql-api/sampling/#adaptive-sampling"><u>Adaptive</u></a>" in their name are based on sampled data, and the sample interval is exposed as a field there as well, though it is not possible to build confidence intervals from that alone. We are working on exposing confidence intervals in the GraphQL API, and as an experiment have added them to the count and edgeResponseBytes (sum) fields on the httpRequestsAdaptiveGroups nodes. This is available under <code><b>confidence(level: X)</b></code>.</p><p>Here is a sample GraphQL query:</p>
            <pre><code>query HTTPRequestsWithConfidence(
  $accountTag: string
  $zoneTag: string
  $datetimeStart: string
  $datetimeEnd: string
) {
  viewer {
    zones(filter: { zoneTag: $zoneTag }) {
      httpRequestsAdaptiveGroups(
        filter: {
          datetime_geq: $datetimeStart
          datetime_leq: $datetimeEnd
      }
      limit: 100
    ) {
      confidence(level: 0.95) {
        level
        count {
          estimate
          lower
          upper
          sampleSize
        }
        sum {
          edgeResponseBytes {
            estimate
            lower
            upper
            sampleSize
          }
        }
      }
    }
  }
}
</code></pre>
            <p>The query above asks for the estimates and the 95% confidence intervals for <code><b>SUM(edgeResponseBytes)</b></code> and <code><b>COUNT</b></code>. The results will also show the sample size, which is good to know, as we rely on the <a href="https://en.wikipedia.org/wiki/Central_limit_theorem"><u>central limit theorem</u></a> to build the confidence intervals, thus small samples don't work very well.</p><p>Here is the response from this query:</p>
            <pre><code>{
  "data": {
    "viewer": {
      "zones": [
        {
          "httpRequestsAdaptiveGroups": [
            {
              "confidence": {
                "level": 0.95,
                "count": {
                  "estimate": 96947,
                  "lower": "96874.24",
                  "upper": "97019.76",
                  "sampleSize": 96294
                },
                "sum": {
                  "edgeResponseBytes": {
                    "estimate": 495797559,
                    "lower": "495262898.54",
                    "upper": "496332219.46",
                    "sampleSize": 96294
                  }
                }
              }
            }
          ]
        }
      ]
    }
  },
  "errors": null
}
</code></pre>
            <p>The response shows the estimated count is 96947, and we are 95% confident that the true count lies in the range 96874.24 to 97019.76. Similarly, the estimate and range for the sum of response bytes are provided.</p><p>The estimates are based on a sample size of 96294 rows, which is plenty of samples to calculate good confidence intervals.</p>
    <div>
      <h3>Conclusion</h3>
      <a href="#conclusion">
        
      </a>
    </div>
    <p>We have discussed what kept our data pipeline scalable and resilient despite doubling in size every 1.5 years, how the math works, and how it is easy to mess up. We are constantly working on better ways to keep the data pipeline, and the products based on it, useful to our customers. If you are interested in doing things like that and want to help us build a better Internet, check out our <a href="http://www.cloudflare.com/careers"><u>careers page</u></a>.</p> ]]></content:encoded>
            <category><![CDATA[Bugs]]></category>
            <category><![CDATA[Analytics]]></category>
            <category><![CDATA[Data]]></category>
            <category><![CDATA[GraphQL]]></category>
            <category><![CDATA[SQL]]></category>
            <category><![CDATA[Go]]></category>
            <category><![CDATA[Deep Dive]]></category>
            <category><![CDATA[Sampling]]></category>
            <guid isPermaLink="false">64DSvKdN853gq5Bx3Cyfij</guid>
            <dc:creator>Constantin Pan</dc:creator>
            <dc:creator>Jim Hawkridge</dc:creator>
        </item>
        <item>
            <title><![CDATA[Cloudflare incident on November 14, 2024, resulting in lost logs]]></title>
            <link>https://blog.cloudflare.com/cloudflare-incident-on-november-14-2024-resulting-in-lost-logs/</link>
            <pubDate>Tue, 26 Nov 2024 16:00:00 GMT</pubDate>
            <description><![CDATA[ On November 14, 2024, Cloudflare experienced a Cloudflare Logs outage, impacting the majority of customers using these products. During the ~3.5 hours that these services were impacted, about 55% of the logs we normally send to customers were not sent and were lost. The details of what went wrong and why are interesting both for customers and practitioners. ]]></description>
            <content:encoded><![CDATA[ <p>On November 14, 2024, Cloudflare experienced an incident which impacted the majority of customers using <a href="https://developers.cloudflare.com/logs"><u>Cloudflare Logs</u></a>. During the roughly 3.5 hours that these services were impacted, about 55% of the logs we normally send to customers were not sent and were lost. We’re very sorry this happened, and we are working to ensure that a similar issue doesn't happen again.</p><p>This blog post explains what happened and what we’re doing to prevent recurrences. Also, the systems involved and the particular class of failure we experienced will hopefully be of interest to engineering teams beyond those specifically using these products.</p><p>Failures within systems at scale are inevitable, and it’s essential that subsystems protect themselves from failures in other parts of the larger system to prevent cascades. In this case, a misconfiguration in one part of the system caused a cascading overload in another part of the system, which was itself misconfigured. Had it been properly configured, it could have prevented the loss of logs.</p>
    <div>
      <h2>Background</h2>
      <a href="#background">
        
      </a>
    </div>
    <p>Cloudflare’s network is a globally distributed system enabling and supporting a wide variety of services. Every part of this system generates event logs which contain detailed metadata about what’s happening with our systems around the world. For example, an event log is generated for every request to Cloudflare’s CDN. <a href="https://developers.cloudflare.c/om/logs"><u>Cloudflare Logs</u></a> makes these event logs available to customers, who use them in a number of ways, including compliance, observability, and accounting.</p><p>On a typical day, Cloudflare sends about 4.5 trillion individual event logs to customers. Although this represents less than 10% of the over 50 trillion total customer event logs processed, it presents unique challenges of scale when building a reliable and fault-tolerant system.</p>
    <div>
      <h2>System architecture</h2>
      <a href="#system-architecture">
        
      </a>
    </div>
    <p>Cloudflare’s network is composed of tens of thousands of individual servers, network hardware components, and specialized software programs located in over 330 cities around the world. Although Cloudflare’s <a href="https://developers.cloudflare.com/logs/edge-log-delivery/"><u>Edge Log Delivery</u></a> product will send customers their event logs directly from each server, most customers opt not to do this because doing so will create significant complication and cost at the receiving end.</p><p>By analogy, imagine the postal service ringing your doorbell once for each letter instead of once for each packet of letters. With thousands or millions of letters each second, the number of separate transactions that would entail becomes prohibitive.</p><p>Fortunately, we also offer <a href="https://developers.cloudflare.com/logs/about/"><u>Logpush</u></a>, which collects and pushes logs to customers in more predictable file sizes and which scales automatically with usage. In order to provide this feature several services work together to collect and push the logs, as illustrated in the diagram below:</p>
          <figure>
          <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/6pRdzUUrMbwG3AWsPncB3w/75146d1e379ccb126d8cd0210a6c12b8/image2.png" />
          </figure>
    <div>
      <h3>Logfwdr</h3>
      <a href="#logfwdr">
        
      </a>
    </div>
    <p>Logfwdr is an internal service written in Golang that accepts event logs from internal services running across Cloudflare’s global network and forwards them in batches to a service called Logreceiver. Logfwdr handles many different types of event logs, and one of its responsibilities is to determine which event logs should be forwarded and where they should be sent based on the type of event log, which customers it represents, and associated rules about where it should be processed. Configuration is provided to Logfwdr to enable it to make these determinations.</p>
    <div>
      <h3>Logreceiver</h3>
      <a href="#logreceiver">
        
      </a>
    </div>
    <p>Logreceiver (also written in Golang) accepts the batches of logs from across Cloudflare’s global network and further sorts them depending on the type of event and its purpose. For Cloudflare Logs, Logreceiver demultiplexes the batches into per-customer batches and forwards them to be buffered by Buftee. Currently, Logreceiver is handling about 45 PB (uncompressed) of customer event logs each day.</p>
    <div>
      <h3>Buftee</h3>
      <a href="#buftee">
        
      </a>
    </div>
    <p>It’s common for data pipelines to include a buffer. Producers and consumers of the data might be operating at different cadences, and parts of the pipeline will experience variances in how quickly they can process information. Using a <a href="https://en.wikipedia.org/wiki/Data_buffer"><u>buffer</u></a> makes it easier to manage these situations, and helps to prevent data loss if downstream consumers are broken. It’s also convenient to have a buffer that supports multiple downstream consumers with different cadences (<a href="https://en.wikipedia.org/wiki/Tee_(command)"><u>like the pipe fitting function of a tee</u></a>.)</p><p>At Cloudflare, we use an internal system called Buftee (written in Golang) to support this combined function. Buftee is a highly distributed system which supports a large number of named “buffers”. It supports operating on named “prefixes” (collections of buffers) as well as multiple representations/resolutions of the same time-indexed dataset. Using Buftee makes it possible for Cloudflare to handle extremely high throughput very efficiently.</p><p>For Cloudflare Logs, Buftee provides buffers for each Logpush job, containing 100% of the logs generated by the zone or account referenced by each job. This means that failure to process one customer’s job will not affect progress on another customer’s job. Handling buffers in this way avoids “head of line” blocking and also enables us to encrypt and delete each customer’s data separately if needed.</p><p>Buftee typically handles over 1 million buffers globally. The following is a snapshot of the number of buffers managed by Buftee servers in the period just prior to the incident.</p>
          <figure>
          <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/5g4Ev1CX8pGqtpNUY3vZOo/50d8dfa7a1e9c492a537e4822d801625/image5.png" />
          </figure>
    <div>
      <h3>Logpush</h3>
      <a href="#logpush">
        
      </a>
    </div>
    <p>Logpush is a Golang service which reads logs from Buftee buffers and pushes the results in batches to various destinations configured by customers. A batch could end up, for example, as a file in R2. Each job has a unique configuration, and only jobs that are active and configured will be pushed. Currently, we push over 600 million such batches each day.</p>
    <div>
      <h2>What happened</h2>
      <a href="#what-happened">
        
      </a>
    </div>
    <p>On November 14, 2024, we made a change to support an additional <a href="https://developers.cloudflare.com/logs/reference/log-fields/#datasets"><u>dataset</u></a> for Logpush. This required adding a new configuration to be provided to Logfwdr in order for it to know which customers’ logs to forward for this new stream. Every few minutes, a separate system re-generates the configuration used by Logfwdr to decide which logs need to be forwarded. A bug in this system resulted in a blank configuration being provided to Logfwdr.</p><p>This bug essentially informed Logfwdr that <b>no customers</b> had logs configured to be pushed. The team quickly noticed the mistake and reverted the change in under five minutes.</p><p>Unfortunately, this first mistake triggered a second, latent bug in Logfwdr itself. A failsafe introduced in the early days of this feature, when traffic was much lower, was configured to “fail open”. This failsafe was designed to protect against a situation when this specific Logfwdr configuration was unavailable (as in this case) by transmitting events for <b>all customers</b> instead of just those who had configured a Logpush job. This was intended to prevent the loss of logs at the expense of sending more logs than strictly necessary when individual hosts were prevented from getting the configuration due to intermittent networking errors, for example.</p><p>When this failsafe was first introduced, the potential list of customers was smaller than it is today. This small window of less than five minutes resulted in a massive spike<b> </b>in the number of customers whose logs were sent by Logfwdr.</p><p>Even given this massive overload, our systems would have continued to send logs if not for one additional problem. Remember that Buftee creates a separate buffer for each customer with their logs to be pushed. When Logfwdr began to send event logs for all customers, Buftee began to create buffers for each one as those logs arrived, and each buffer requires resources as well as the bookkeeping to maintain them. This massive increase, resulting in roughly 40 times more buffers, is not something we’ve provisioned Buftee clusters to handle. In the lead-up to impact, Buftee was managing 40 million buffers globally, as shown in the figure below.</p>
          <figure>
          <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/7HPwhkvRxiAQtjVbdVxRUN/a1d4aa174961f6a163e884011c8c18ad/image3.png" />
          </figure><p>A short temporary misconfiguration lasting just five minutes created a massive overload that took us several hours to fix and recover from. Because our backstops were not properly configured, the underlying systems became so overloaded that we could not interact with them normally.  A full reset and restart was required.</p>
    <div>
      <h2>Root causes</h2>
      <a href="#root-causes">
        
      </a>
    </div>
    <p>The bug in the Logfwdr configuration system was easy to fix, but it’s the type of bug that was likely to happen at some point.  We had planned for it by designing the original “fail open” behavior.  However, we neglected to regularly test that the broader system was capable of handling a fail open event.</p><p>The bigger failure was that Buftee became unresponsive.  Buftee’s purpose is to be a safeguard against bugs like this one.  A huge increase in the number of buffers is a failure mode that we had predicted, and had put mechanisms in Buftee to prevent this failure from cascading.  Our failure in this case was that we had not configured these mechanisms.  Had they been configured correctly, Buftee would not have been overwhelmed.</p><p>It's like having a seatbelt in a car, yet not fastening it. The seatbelt is there to protect you in case of an accident but if you don't actually buckle it up, it's not going to do its job when you need it. Similarly, while we had the safeguard of Buftee in place, we hadn't 'buckled it up' by configuring the necessary settings. We’re very sorry this happened and are taking steps to prevent a recurrence as described below.</p>
    <div>
      <h2>Going forward</h2>
      <a href="#going-forward">
        
      </a>
    </div>
    <p>We’re creating alerts to ensure that these particular misconfigurations will be impossible to miss, and we are also addressing the specific bug and the associated tests that triggered this incident.</p><p>Just as importantly, we accept that mistakes and misconfigurations are inevitable. All our systems at Cloudflare need to respond to these predictably and gracefully. Currently, we conduct regular “cut tests” to ensure that these systems will cope with the loss of a datacenter or a network failure. In the future, we’ll also conduct regular “overload tests” to simulate the kind of cascade which happened in this incident to ensure that our production systems will handle them gracefully.</p><p>Logpush is a robust and flexible platform for customers who need to integrate their own logging and monitoring systems with Cloudflare. Different Logpush jobs can be deployed to support multiple destinations or, with filtering, multiple subsets of logs.</p> ]]></content:encoded>
            <category><![CDATA[Logs]]></category>
            <category><![CDATA[Data]]></category>
            <category><![CDATA[Log Push]]></category>
            <guid isPermaLink="false">3SNSdDbbVziSrdGxDq4AzH</guid>
            <dc:creator>Jamie Herre</dc:creator>
            <dc:creator>Tom Walwyn</dc:creator>
            <dc:creator>Christian Endres</dc:creator>
            <dc:creator>Gabriele Viglianisi</dc:creator>
            <dc:creator>Mik Kocikowski</dc:creator>
            <dc:creator>Rian van der Merwe</dc:creator>
        </item>
        <item>
            <title><![CDATA[A look inside the Cloudflare ML Ops platform]]></title>
            <link>https://blog.cloudflare.com/mlops/</link>
            <pubDate>Thu, 07 Dec 2023 14:00:42 GMT</pubDate>
            <description><![CDATA[ To help our team continue to innovate efficiently, our MLOps effort has collaborated with Cloudflare’s data scientists to implement the following best practices ]]></description>
            <content:encoded><![CDATA[ <p></p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/4GvjtY1dTv1z9QsDBcP5q9/9430817e4123b426b6e26b1ef02d361a/image1.png" />
            
            </figure><p>We've been relying on ML and AI for our core services like Web Application Firewall (WAF) since the early days of Cloudflare. Through this journey, we've learned many lessons about running AI deployments at scale, and all the tooling and processes necessary. We recently launched <a href="/workers-ai/">Workers AI</a> to help abstract a lot of that away for inference, giving developers an easy way to leverage powerful models with just a few lines of code. In this post, we’re going to explore some of the lessons we’ve learned on the other side of the ML equation: <i>training</i>.</p><p>Cloudflare has extensive experience training models and using them to improve our products. A constantly-evolving ML model drives the <a href="/data-generation-and-sampling-strategies/">WAF attack score</a> that helps protect our customers from malicious payloads. Another evolving model powers our <a href="/stop-the-bots-practical-lessons-in-machine-learning/">bot management</a> product to catch and <a href="/cloudflare-bot-management-machine-learning-and-more/">prevent bot attacks</a> on our <a href="/machine-learning-mobile-traffic-bots/">customers</a>. Our customer support is <a href="/using-data-science-and-machine-learning-for-improved-customer-support/">augmented by data science</a>. We build machine learning to <a href="/threat-detection-machine-learning-models/">identify threats</a> with our global network. To top it all off, Cloudflare is delivering <a href="/scalable-machine-learning-at-cloudflare/">machine learning at unprecedented scale</a> across our network.</p><p>Each of these products, along with many others, has elevated ML models — including experimentation, training, and deployment — to a crucial position within Cloudflare. To help our team continue to innovate efficiently, our MLOps effort has collaborated with Cloudflare’s data scientists to implement the following best practices.</p>
    <div>
      <h3>Notebooks</h3>
      <a href="#notebooks">
        
      </a>
    </div>
    <p>Given a use case and data, the first step for many Data Scientist/AI Scientists is to set up an environment for exploring data, feature engineering, and model experiments. <a href="https://docs.jupyter.org/en/latest/start/index.html">Jupyter Notebooks</a> are a common tool to satisfy these requirements. These environments provide an easy remote Python environment that can be run in the browser or connected to a local code editor. To make notebooks scalable and open to collaboration, we deploy <a href="https://jupyter.org/hub">JupyterHub</a> on Kubernetes. With JupyterHub, we can manage resources for teams of Data Scientists and ensure they get a suitable development environment. Each team can tailor their environment by pre-installing libraries and configuring user environments to meet the specific needs, or even individual projects.</p><p>This notebook space is always evolving as well. Open source projects include further features, such as:</p><ul><li><p><a href="https://nbdev.fast.ai/">nbdev</a> - a Python package to improve the notebook experience</p></li><li><p><a href="https://www.kubeflow.org/docs/components/notebooks/overview/">Kubeflow</a> - the kubernetes native CNCF project for machine learning</p></li><li><p><a href="https://www.deploykf.org/">deployKF</a> - ML Platforms on any Kubernetes cluster, with centralized configs, in-place upgrades, and support for leading ML &amp; Data tools like Kubeflow, Airflow, and MLflow</p></li></ul>
    <div>
      <h3>GitOps</h3>
      <a href="#gitops">
        
      </a>
    </div>
    <p>Our goal is to provide an easy-to-use platform for Data Scientists and AI Scientists to develop and test machine learning models quickly. Hence, we are adopting GitOps as our continuous delivery strategy and infrastructure management on MLOps Platform in Cloudflare. GitOps is a software development methodology that leverages Git, a distributed version control system, as the single source of truth for defining and managing infrastructure, application configurations, and deployment processes. It aims to automate and streamline the deployment and management of applications and infrastructure in a declarative and auditable manner. GitOps aligns well with the principles of automation and collaboration, which are crucial for <a href="https://www.cloudflare.com/learning/ai/what-is-machine-learning/">machine learning (ML)</a> workflows. GitOps leverages Git repositories as the source of truth for declarative infrastructure and application code.</p><p>A data scientist needs to define the desired state of infrastructure and applications. This usually takes a lot of custom work, but with <a href="https://argo-cd.readthedocs.io/en/stable/">ArgoCD</a> and model templates, all it takes is a simple pull request to add new applications. Helm charts and Kustomize are both supported to allow for configuration for different environments and jobs. With ArgoCD, declarative GitOps will then start the Continuous Delivery process. ArgoCD will continuously check the desired state of the infrastructure and applications to ensure that they are synched with the Git repository.</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/46I2rJ6bnhsXmVC8f7sK3i/7b47bc18341611aad94ebbb81cb30be3/image2.png" />
            
            </figure><p>In the future, we plan to migrate our platform (including Jupyterhub) to <a href="https://www.kubeflow.org/">Kubeflow</a>, a machine learning workflow platform on Kubernetes that simplifies the development, deployment, and management of notebooks and end-to-end pipelines. This is best deployed using a new project, <a href="https://www.deploykf.org/">deployKF</a>, which allows for distributed configuration management across multiple components available with Kubeflow, and others that extend beyond what is offered within Kubeflow.</p>
    <div>
      <h3>Templates</h3>
      <a href="#templates">
        
      </a>
    </div>
    <p>Starting a project with the right tools and structure can be the difference between success and stagnation. Within Cloudflare, we've curated an array of model templates, which are production ready data science repositories with an example model. These model templates are deployed through production to continually ensure they are a stable foundation for future projects. To start a new project, all it takes is one Makefile command to build a new CICD project in the git project of the users’ choosing. These template utility packages are identical to those used in our Jupyter Notebooks and connections to R2 / S3 / GCS buckets, D1 / Postgres / Bigquery / Clickhouse databases. Data scientists can use these templates to instantly kickstart new projects with confidence. These templates are not yet publicly available, but our team plans to open source them in the future.</p><p><b>1. Training Template</b>Our model training template provides a solid foundation to build any model. This is configured to help extract, transform, and load data (ETL) from any data source. The template includes helper functions for feature engineering, tracking experiments with model metadata, and choose orchestration through a Directed Acyclic Graph (DAG) to productionalize the model pipeline. Each orchestration can be configured to use <a href="https://github.com/airflow-helm/charts">Airflow</a> or <a href="https://argoproj.github.io/argo-workflows/">Argo Workflows</a>.</p><p><b>2. Batch Inference Template</b>Batch and micro-batch inference can make a significant impact on processing efficiency. Our batch inference model template to schedule models for consistent results, and can be configured to use <a href="https://github.com/airflow-helm/charts">Airflow</a> or <a href="https://argoproj.github.io/argo-workflows/">Argo Workflows</a>.</p><p><b>3. Stream Inference Template</b>This template makes it easy for our team to deploy real-time inference. Tailored for Kubernetes as a microservice using <a href="https://fastapi.tiangolo.com/">FastAPI</a>, this template allows our team to run inference using familiar Python in a container. This microservice already has built-in REST interactive documentation with <a href="https://swagger.io/">Swagger</a> and integration with Cloudflare Access authentication tokens in <a href="https://developers.cloudflare.com/cloudflare-one/api-terraform/access-with-terraform/">terraform</a>.</p><p><b>4. Explainability Template</b>Our model template for explainability spins up dashboards to illuminate the model type and experiments. It is important to be able to understand key values such as a time window F1 score, the drift of features and data over time. Tools like <a href="https://streamlit.io/">Streamlit</a> and <a href="https://bokeh.org/">Bokeh</a> help to make this possible.</p>
    <div>
      <h3>Orchestration</h3>
      <a href="#orchestration">
        
      </a>
    </div>
    <p>Organizing data science into a consistent pipeline involves a lot of data and several model versions. Enter Directed Acyclic Graphs (DAGs), a robust flow chart orchestration paradigm that weaves together the steps from data to model, and model to inference. There are many unique approaches to running DAG pipelines, but we have found that data science teams' preference comes first. Each team has different approaches based on their use cases and experience.</p><p><a href="https://github.com/airflow-helm/charts"><b>Apache Airflow</b></a> <b>- The Standard DAG Composer</b>Apache Airflow is the standard as a DAG (Directed Acyclic Graphs)-based orchestration approach. With a vast community and extensive plugin support, <a href="/automating-data-center-expansions-with-airflow/">Airflow excels in handling diverse workflows</a>. The flexibility to integrate with a multitude of systems and a web-based UI for task monitoring make it a popular choice for orchestrating complex sequences of tasks. Airflow can be used to run any data or machine learning workflow.</p><p><a href="https://argoproj.github.io/argo-workflows/"><b>Argo Workflows</b></a> <b>- Kubernetes-native Brilliance</b>Built for Kubernetes, Argo Workflows embraces the container ecosystem for orchestrating workflows. It boasts an intuitive YAML-based workflow definition and excels in running microservices-based workflows. The integration with Kubernetes enables scalability, reliability, and native container support, making it an excellent fit for organizations deeply rooted in the Kubernetes ecosystem. Argo Workflows can also be used to run any data or machine learning workflow.</p><p><a href="https://www.kubeflow.org/docs/components/pipelines/v2/introduction/"><b>Kubeflow Pipelines</b></a> <b>- A Platform for Workflows</b>Kubeflow Pipelines is a more specific approach tailored for orchestrating machine learning workflows. “KFP” aims to address the unique demands of data preparation, model training, and deployment in the ML landscape. As an integrated component of the Kubeflow ecosystem, it streamlines ML workflows with a focus on collaboration, reusability, and versioning. Its compatibility with Kubernetes ensures seamless integration and efficient orchestration.</p><p><a href="https://temporal.io/"><b>Temporal</b></a> <b>- The Stateful Workflow Enabler</b>Temporal takes a stance by emphasizing the orchestration of long-running, stateful workflows. This relative newcomer shines in managing resilient, event-driven applications, preserving workflow state and enabling efficient recovery from failures. The unique selling point lies in its ability to manage complex, stateful workflows, providing a durable and fault-tolerant orchestration solution.</p><p>In the orchestration landscape, the choice ultimately boils down to the team and use case. These are all open source projects, so the only limitation is support for different styles of work, which we find is worth the investment.</p>
    <div>
      <h3>Hardware</h3>
      <a href="#hardware">
        
      </a>
    </div>
    <p>Achieving optimal performance necessitates an understanding of workloads and the underlying use cases in order to provide teams with <a href="/cloudflares-gen-x-servers-for-an-accelerated-future/">effective hardware</a>. The goal is to enable data scientists and strike a balance between enablement and utilization. Each workload is different, and it is important to fine tune each use case for the capabilities of <a href="/bringing-ai-to-the-edge/">GPUs</a> and <a href="/debugging-hardware-performance-on-gen-x-servers/">CPUs</a> to find the perfect tool for the job.  For core datacenter workloads and edge inference, GPUs have leveled up the speed and efficiency that is core to our business. With <a href="https://www.cloudflare.com/learning/performance/what-is-observability/">observability</a> and metrics consumed by <a href="https://prometheus.io/">Prometheus</a>, metrics enable us to track orchestration to be optimized for <a href="/getting-to-the-core/">performance</a>, maximize hardware utilization, and operate within a Kubernetes-native experience.</p>
    <div>
      <h3>Adoption</h3>
      <a href="#adoption">
        
      </a>
    </div>
    <p>Adoption is often one of the most challenging steps in the MLops journey. Before jumping into building, it is important to understand the different teams and their approach to data science. At Cloudflare, this process began years ago, when each of the teams started their own machine learning solutions separately. As these solutions evolved, we ran into the common challenge of working across the company to prevent work from becoming isolated from other teams. In addition, there were other teams that had potential for machine learning but did not have data science expertise within their team. This presented an opportunity for MLops to step in — both to help streamline and standardize the ML processes being employed by each team, and to introduce potential new projects to data science teams to start the ideation and discovery process.</p><p>When able, we have found the most success when we can help get projects started and shape the pipelines for success. Providing components for shared use such as notebooks, orchestration, data versioning (DVC), feature engineering (Feast), and model versioning (MLflow) allow for teams to collaborate directly.</p>
    <div>
      <h3>Looking forward</h3>
      <a href="#looking-forward">
        
      </a>
    </div>
    <p>There is no doubt that data science is <a href="/best-place-region-earth-inference/">evolving our business</a> and the <a href="/ai-companies-building-cloudflare/">businesses of our customers</a>. We improve our own products with models, and have built <a href="/announcing-ai-gateway/">AI infrastructure</a> that can help us <a href="https://www.cloudflare.com/application-services/solutions/">secure applications</a> and <a href="/secure-generative-ai-applications/">applications built with AI</a>. We can leverage the <a href="/workers-ai/">power of our network to deliver AI</a> for us and our customers. We have partnered with <a href="/partnering-with-hugging-face-deploying-ai-easier-affordable/">machine</a> <a href="https://www.cloudflare.com/press-releases/2023/cloudflare-partners-with-databricks">learning</a> <a href="https://www.cloudflare.com/press-releases/2023/cloudflare-and-meta-collaborate-to-make-llama-2-available-globally">giants</a> to make it easier for the data science community to deliver real value from data.</p><p>The call to action is this: join the <a href="https://discord.com/invite/cloudflaredev">Cloudflare community</a> in bringing modern software practices and tools to data science. Be on the lookout for more data science from Cloudflare. Help us securely leverage data to help build a better Internet.</p> ]]></content:encoded>
            <category><![CDATA[AI]]></category>
            <category><![CDATA[Data]]></category>
            <category><![CDATA[Developers]]></category>
            <category><![CDATA[Machine Learning]]></category>
            <category><![CDATA[MLops]]></category>
            <category><![CDATA[Hardware]]></category>
            <guid isPermaLink="false">1NOp4Ep4CYMxL6OYRa2FaU</guid>
            <dc:creator>Keith Adler</dc:creator>
            <dc:creator>Rio Harapan Pangihutan</dc:creator>
        </item>
        <item>
            <title><![CDATA[How Cloudflare instruments services using Workers Analytics Engine]]></title>
            <link>https://blog.cloudflare.com/using-analytics-engine-to-improve-analytics-engine/</link>
            <pubDate>Fri, 18 Nov 2022 14:00:00 GMT</pubDate>
            <description><![CDATA[ Learn how Cloudflare uses our own Workers Analytics Engine product to capture analytics about our own products! ]]></description>
            <content:encoded><![CDATA[ 
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/16jjPVsiGz8fzNxVYIgsbR/8c4c93497cd82108d0d74efaf52a96ad/image1-62.png" />
            
            </figure><p>Workers Analytics Engine is a new tool, <a href="/workers-analytics-engine/">announced earlier this year</a>, that enables developers and product teams to build time series analytics about anything, with high dimensionality, high cardinality, and effortless scaling. We built Analytics Engine for teams to gain insights into their code running in Workers, provide analytics to end customers, or even build usage based billing.</p><p>In this blog post we’re going to tell you about how we use Analytics Engine to build Analytics Engine. We’ve instrumented our own Analytics Engine SQL API using Analytics Engine itself and use this data to find bugs and prioritize new product features. We hope this serves as inspiration for other teams who are looking for ways to instrument their own products and gather feedback.</p>
    <div>
      <h3>Why do we need Analytics Engine?</h3>
      <a href="#why-do-we-need-analytics-engine">
        
      </a>
    </div>
    <p>Analytics Engine enables you to generate events (or “data points”) from Workers with <a href="https://developers.cloudflare.com/analytics/analytics-engine/get-started/">just a few lines of code</a>. Using the GraphQL or <a href="https://developers.cloudflare.com/analytics/analytics-engine/sql-api/">SQL API</a>, you can query these events and create useful insights about the business or technology stack. For more about how to get started using Analytics Engine, check out our <a href="https://developers.cloudflare.com/analytics/analytics-engine/">developer docs</a>.</p><p>Since we released the <a href="/analytics-engine-open-beta/">Analytics Engine open beta</a> in September, we’ve been adding new features at a rapid clip based on feedback from developers. However, we’ve had two big gaps in our visibility into the product.</p><p>First, our engineering team needs to answer <a href="https://www.cloudflare.com/learning/performance/what-is-observability/">classic observability questions</a>, such as: how many requests are we getting, how many of those requests result in errors, what are the nature of these errors, etc. They need to be able to view both aggregated data (like average error rate, or p99 response time) and drill into individual events.</p><p>Second, because this is a newly launched product, we are looking for product insights. By instrumenting the SQL API, we can understand the queries our customers write, and the errors they see, which helps us prioritize missing features.</p><p>We realized that Analytics Engine would be an amazing tool for both answering our technical observability questions, and also gathering product insight. That’s because we can log an event for every query to our SQL API, and then query for both aggregated performance issues as well as individual errors and queries that our customers run.</p><p>In the next section, we’re going to walk you through how we use Analytics Engine to monitor that API.</p>
    <div>
      <h2>Adding instrumentation to our SQL API</h2>
      <a href="#adding-instrumentation-to-our-sql-api">
        
      </a>
    </div>
    <p>The Analytics Engine SQL API lets you query events data in the same way you would an ordinary database. For decades, SQL has been the most common language for querying data. We wanted to provide an interface that allows you to immediately start asking questions about your data without having to learn a new query language.</p><p>Our SQL API parses user SQL queries, transforms and validates them, and then executes them against backend database servers. We then write information about the query back into Analytics Engine so that we can run our own analytics.Writing data into Analytics Engine from a Cloudflare Worker is very simple and <a href="https://developers.cloudflare.com/analytics/analytics-engine/get-started/">explained in our documentation</a>. We instrument our SQL API in the same way our users do, and this code excerpt shows the data we write into Analytics Engine:</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/L30suydy27OFKzv6ua9ML/c49d03afbb62a1e3df7229e6c30e087c/carbon--3--1.png" />
            
            </figure><p>With that data now being stored in Analytics Engine, we can then pull out insights about every field we’re reporting.</p>
    <div>
      <h2>Querying for insights</h2>
      <a href="#querying-for-insights">
        
      </a>
    </div>
    <p>Having our analytics in an SQL database gives you the freedom to write any query you might want. Compared to using something like metrics which are often predefined and purpose specific, you can define any custom dataset desired, and interrogate your data to ask new questions with ease.</p><p>We need to support datasets comprising trillions of data points. In order to accomplish this, we have implemented a sampling method called <a href="/explaining-cloudflares-abr-analytics/">Adaptive Bit Rate</a> (ABR). With ABR, if you have large amounts of data, your queries may be returned sampled events in order to respond in reasonable time. If you have more typical amounts of data, Analytics Engine will query all your data. This allows you to run any query you like and still get responses in a short length of time. Right now, you have to <a href="https://developers.cloudflare.com/analytics/analytics-engine/sql-api/#sampling">account for sampling in how you make your queries</a>, but we are exploring making it automatic.</p><p>Any data visualization tool can be used to visualize your analytics. At Cloudflare, we heavily use Grafana (<a href="https://developers.cloudflare.com/analytics/analytics-engine/grafana/">and you can too!</a>). This is particularly useful for observability use cases.</p>
    <div>
      <h3>Observing query response times</h3>
      <a href="#observing-query-response-times">
        
      </a>
    </div>
    <p>One query we pay attention to gives us information about the performance of our backend database clusters:</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/q8KADDDRyASR7nWPQHoKc/c633325aca2b64e464fc820abfd5e653/image2-45.png" />
            
            </figure><p>As you can see, the 99% percentile (corresponding to the 1% most complex queries to execute) sometimes spikes up to about 300ms. But on average our backend responds to queries within 100ms.</p><p>This visualization is itself generated from an SQL query:</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/6UchtUYBtnDuXwKO7afWUc/1a584c18a7ce7fb74bcc2599756ed6f7/carbon--2-.png" />
            
            </figure>
    <div>
      <h3>Customer insights from high-cardinality data</h3>
      <a href="#customer-insights-from-high-cardinality-data">
        
      </a>
    </div>
    <p>Another use of Analytics Engine is to draw insights out of customer behavior. Our SQL API is particularly well-suited for this, as you can take full advantage of the power of SQL. Thanks to our ABR technology, even expensive queries can be carried out against huge datasets.</p><p>We use this ability to help prioritize improvements to Analytics Engine. Our SQL API supports a fairly standard dialect of SQL but isn’t feature-complete yet. If a user tries to do something unsupported in an SQL query, they get back a structured error message. Those error messages are reported into Analytics Engine. We’re able to aggregate the kinds of errors that our customers encounter, which helps inform which features to prioritize next.</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/AmDjwutzQH089GhoJHvzw/b734eaa557a88f2d513968f20f10f28a/image3-36.png" />
            
            </figure><p>The SQL API returns errors in the format of <code>type of error: more details</code>, and so we can take the first portion before the colon to give us the type of error. We group by that, and get a count of how many times that error happened and how many users it affected:</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/5Z1KYNLPlcb3rYTPJ9Fi8f/78ac0462fa7b5b1ae2db27d1dfd67d2b/Screenshot-2022-11-18-at-08.33.57.png" />
            
            </figure><p>To perform the above query using an ordinary metrics system, we would need to represent each error type with a different metric. Reporting that many metrics from each microservice creates scalability challenges. That problem doesn’t happen with Analytics Engine, because it’s designed to handle high-cardinality data.</p><p>Another big advantage of a high-cardinality store like Analytics Engine is that you can dig into specifics. If there’s a large spike in SQL errors, we may want to find which customers are having a problem in order to help them or identify what function they want to use. That’s easy to do with another SQL query:</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/5ZghZO2Jyk153qPnvS13Mk/05891f4f5db7f1b3247e615c7d2373e1/carbon-3.png" />
            
            </figure><p>Inside Cloudflare, we have historically relied on querying our backend database servers for this type of information. Analytics Engine’s SQL API now enables us to open up our technology to our customers, so they can easily gather insights about their services at any scale!</p>
    <div>
      <h2>Conclusion and what’s next</h2>
      <a href="#conclusion-and-whats-next">
        
      </a>
    </div>
    <p>The insights we gathered about usage of the SQL API are a super helpful input to our product prioritization decisions. We already added <a href="https://developers.cloudflare.com/analytics/analytics-engine/sql-reference/">support for <code>substring</code> and <code>position</code> functions</a> which were used in the visualizations above.</p><p>Looking at the top SQL errors, we see numerous errors related to selecting columns. These errors are mostly coming from some usability issues related to the Grafana plugin. Adding support for the DESCRIBE function should alleviate this because without this, the Grafana plugin doesn’t understand the table structure. This, as well as other improvements to our Grafana plugin, is on our roadmap.</p><p>We also can see that users are trying to query time ranges for older data that no longer exists. This suggests that our customers would appreciate having extended data retention. We’ve recently extended our retention from 31 to 92 days, and we will keep an eye on this to see if we should offer further extension.</p><p>We saw lots of errors related to common mistakes or misunderstandings of proper SQL syntax. This indicates that we could provide better examples or error explanations in our documentation to assist users with troubleshooting their queries.</p><p>Stay tuned into our <a href="https://developers.cloudflare.com/analytics/analytics-engine/">developer docs</a> to be informed as we continue to iterate and add more features!</p><p>You can start using Workers Analytics Engine Now! Analytics Engine is now in open beta with free 90-day retention. <a href="https://dash.cloudflare.com/?to=/:account/workers/analytics-engine">Start using it  today</a> or <a href="https://discord.gg/cloudflaredev">join our Discord community</a> to talk with the team.</p> ]]></content:encoded>
            <category><![CDATA[Developer Week]]></category>
            <category><![CDATA[Analytics]]></category>
            <category><![CDATA[Logs]]></category>
            <category><![CDATA[Data]]></category>
            <category><![CDATA[Cloudflare Workers]]></category>
            <category><![CDATA[Developers]]></category>
            <category><![CDATA[Developer Platform]]></category>
            <guid isPermaLink="false">5Vbtic7QOMABAMIJbPSm7v</guid>
            <dc:creator>Jen Sells</dc:creator>
            <dc:creator>Miki Mokrysz</dc:creator>
        </item>
        <item>
            <title><![CDATA[The home page for Internet insights: Cloudflare Radar 2.0]]></title>
            <link>https://blog.cloudflare.com/radar2/</link>
            <pubDate>Fri, 30 Sep 2022 13:00:00 GMT</pubDate>
            <description><![CDATA[ Cloudflare Radar makes Internet trends, patterns and insights available to everyone, and we’re now making them even easier to find, understand and share. ]]></description>
            <content:encoded><![CDATA[ <p></p><p>Cloudflare Radar was <a href="/introducing-cloudflare-radar/">launched</a> two years ago to give everyone access to the Internet trends, patterns and insights Cloudflare uses to help improve our service and protect our customers.</p><p>Until then, these types of insights were only available internally at Cloudflare. However, true to our mission of helping build a better Internet, we felt everyone should be able to look behind the curtain and see the inner workings of the Internet. It’s hard to improve or understand something when you don’t have clear visibility over how it’s working.</p><p>On Cloudflare Radar you can find timely graphs and visualizations on Internet traffic, security and attacks, protocol adoption and usage, and outages that might be affecting the Internet. All of these can be narrowed down by timeframe, country, and Autonomous System (AS). You can also find interactive deep dive reports on important subjects such as <a href="https://radar.cloudflare.com/notebooks/ddos-2022-q2">DDoS</a> and the <a href="https://radar.cloudflare.com/notebooks/meris-botnet">Meris Botnet</a>. It’s also possible to search for any domain name to see details such as SSL usage and which countries their visitors are coming from.</p><p>Since launch, Cloudflare Radar has been used by NGOs to confirm the Internet disruptions their observers see in the field, by journalists looking for Internet trends related to an event in a country of interest or at volume of cyberattacks as retaliation to political sanctions, by analysts looking at the prevalence of new protocols and technologies, and even by brand PR departments using Cloudflare Radar data to analyze the online impact of a major sports event.</p><p>Cloudflare Radar has clearly become an important tool for many and, most importantly, we find it has helped shed light on parts of the Internet that deserve more attention and investment.</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/2UsXBgobiHaEt0VFbYejO7/2db75ff3526426c5e3505bcb4295936e/image2-68.png" />
            
            </figure>
    <div>
      <h3>Introducing Cloudflare Radar 2.0</h3>
      <a href="#introducing-cloudflare-radar-2-0">
        
      </a>
    </div>
    <p>What has made Cloudflare Radar so valuable is that the data and insights it contains are unique and trustworthy. Cloudflare Radar shows aggregate data from across the massive spectrum of Internet traffic we see every day, presenting you with datasets you won’t find elsewhere.</p><p>However, there were still gaps. Today, on the second anniversary of Cloudflare Radar, we are launching Cloudflare Radar 2.0 in beta. It will address three common pieces of feedback from users:</p><ul><li><p><b>Ease of finding insights and data.</b> The way information was structured on Cloudflare Radar made finding information daunting for some people. We are redesigning Cloudflare Radar so that it becomes a breeze.</p></li><li><p><b>Number of insights.</b> We know many users have wanted to see insights about other important parts of the Internet, such as email. We have also completely redesigned the Cloudflare Radar backend so that we can quickly add new insights over the coming months (including insights into email).</p></li><li><p><b><b><b>Sharing insights.</b></b></b> The options for sharing Cloudflare Radar insights were limited. We will now provide you the options you want, including downloadable and embeddable graphs, sharing to social media platforms, and an API.</p></li></ul>
    <div>
      <h3>Finding insights and data</h3>
      <a href="#finding-insights-and-data">
        
      </a>
    </div>
    <p>On a first visit to the <b>redesigned Cloudflare Radar homepage</b> one will notice:</p><ul><li><p>Prominent and intuitive filtering capabilities on the top bar. A global search bar is also coming soon.</p></li><li><p>Content navigation on the sidebar.</p></li><li><p>Content cards showing glanceable and timely information.</p></li></ul>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/3XD2Yn5kOz589Q5FmkLpcg/9b225b71cacfe40e05adf9f1a41e7e75/image4-30.png" />
            
            </figure><p>The content you find on the homepage are what we call “quick bytes”. Those link you to more in-depth content for that specific topic, which can also be found through the sidebar navigation.</p><p>At the top of the page you can search for a country, autonomous system number (ASN), domain, or report to navigate to a home page for that specific content. For example, the domain page for google.com:</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/10cuWEc5xyO1vCVtas5qD0/4f487564d31f5c59ec3391564148fcc0/image3-52.png" />
            
            </figure><p>The <b>navigation sidebar</b> allows you to find more detailed insights and data related to Traffic, Security &amp; Attacks, Adoption &amp; Usage, and Domains. (We will be adding additional topic areas in the future.) It also gives you quick access to the <b>Cloudflare Radar Outage Center</b>, a tool for tracking Internet disruptions around the world and to which we are dedicating a <a href="/announcing-cloudflare-radar-outage-center/">separate blog post</a>, and to <b>Radar Reports</b>, which are interactive deep dive reports on important subjects such as <a href="https://radar.cloudflare.com/notebooks/ddos-2022-q2">DDoS</a> and the <a href="https://radar.cloudflare.com/notebooks/meris-botnet">Meris Botnet</a>.</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/1PR3jScOjDRSlVbmHWoBLZ/b1529e2a2c7b32d10d5134f1f9976a88/image1-86.png" />
            
            </figure><p>Within these topic pages (such as the one for Adoption &amp; Usage shown above), you will find the <i>quick bytes</i> for the corresponding topic at the top, and <i>quick bytes</i> for related topics on the right. The <i>quick bytes</i> on the right allow you to quickly glance at and navigate to related sections.</p><p>In the middle of the page are the more detailed charts for the topic you’re exploring.</p>
    <div>
      <h3>Sharing insights</h3>
      <a href="#sharing-insights">
        
      </a>
    </div>
    <p>Cloudflare Radar’s reason to exist is to make Internet insights available to everyone, but historically we haven’t been as flexible as our users would want. People could download a snapshot of the graph, but not much more.</p><p>With Cloudflare Radar 2.0 we will be introducing three major new ways of using Radar insights and data:</p><ul><li><p><b>Social share</b>. Cloudflare Radar 2.0 charts have a more modern and clean look and feel, and soon you’ll be able to share them directly on the social media platform of your choice. No more dealing with low quality screenshots.</p></li><li><p><b>Embeddable charts</b>. The beautiful charts will also be able to be embedded directly into your webpage or blog - it will work just like a widget, always showing up-to-date information.</p></li><li><p><b><b><b>API.</b></b></b> If you like the data on Cloudflare Radar but want to manipulate it further for analysis, visualization, or for posting your own charts, you’ll have the Cloudflare Radar API available to you starting today.</p></li></ul><p>For example, the last seven days of HTTP traffic data for Portugal can be obtained from <a href="https://api.cloudflare.com/client/v4/radar/http/timeseries/device_type?dateRange=7d&amp;location=PT">https://api.cloudflare.com/client/v4/radar/http/timeseries/device_type?dateRange=7d&amp;location=PT</a></p><p><i>Note: The API is available today. To use the Cloudflare API you need an API token or key (more details</i> <a href="https://developers.cloudflare.com/api/get-started/"><i>here</i></a><i>). Embedding charts and sharing directly to social are new features to be released later this year.</i></p>
    <div>
      <h3>Technology changes</h3>
      <a href="#technology-changes">
        
      </a>
    </div>
    <p>Cloudflare Radar 2.0 was built on a new technology stack; we will write a blog post about why and how we did it soon. A lot changed: we now have proper <a href="https://graphql.org/">GraphQL</a> data endpoints and a public API, the website runs on top of <a href="https://pages.cloudflare.com/">Cloudflare Pages</a> and <a href="https://workers.cloudflare.com/">Workers</a>, and we're finally doing server-side rendering using <a href="https://remix.run/">Remix</a>. We adopted SVG whenever possible, built our reusable data visualization components system, and are using <a href="https://reactcosmos.org/">Cosmos</a> for visual TDD. These foundational changes will provide a better UX/UI to our users and give us speed when iterating and improving Cloudflare Radar in the future.</p><p>We hope you find this update valuable, and recommend you keep an eye on <a href="https://radar.cloudflare.com/">radar.cloudflare.com</a> to see the new insights and topics we’ll be adding regularly. If you have any feedback, please send it to us through the <a href="https://community.cloudflare.com/new-topic?category=General/Analytics&amp;tags=CloudflareRadar">Cloudflare Community</a>.</p> ]]></content:encoded>
            <category><![CDATA[Birthday Week]]></category>
            <category><![CDATA[Radar]]></category>
            <category><![CDATA[Insights]]></category>
            <category><![CDATA[Data]]></category>
            <category><![CDATA[API]]></category>
            <category><![CDATA[Product News]]></category>
            <guid isPermaLink="false">5UOo2pRBGamwFL6jbL6PlN</guid>
            <dc:creator>João Sousa Botto</dc:creator>
            <dc:creator>David Belson</dc:creator>
            <dc:creator>Ricardo Baeta</dc:creator>
        </item>
        <item>
            <title><![CDATA[Integrating Network Analytics Logs with your SIEM dashboard]]></title>
            <link>https://blog.cloudflare.com/network-analytics-logs/</link>
            <pubDate>Tue, 17 May 2022 15:46:30 GMT</pubDate>
            <description><![CDATA[ We’re excited to announce the availability of Network Analytics Logs for maximum visibility into L3/4 traffic and DDoS attacks ]]></description>
            <content:encoded><![CDATA[ <p></p><p>We’re excited to announce the availability of Network Analytics Logs. <a href="https://www.cloudflare.com/magic-transit/">Magic Transit</a>, <a href="https://www.cloudflare.com/magic-firewall/">Magic Firewall</a>, <a href="https://www.cloudflare.com/magic-wan/">Magic WAN</a>, and <a href="https://www.cloudflare.com/products/cloudflare-spectrum/">Spectrum</a> customers on the Enterprise plan can feed packet samples directly into storage services, <a href="https://www.cloudflare.com/network-services/solutions/network-monitoring-tools/">network monitoring tools</a> such as Kentik, or their <a href="https://www.cloudflare.com/learning/security/what-is-siem/">Security Information Event Management (SIEM)</a> systems such as Splunk to gain near real-time visibility into network traffic and <a href="https://www.cloudflare.com/learning/ddos/what-is-a-ddos-attack/">DDoS attacks</a>.</p>
    <div>
      <h2>What’s included in the logs</h2>
      <a href="#whats-included-in-the-logs">
        
      </a>
    </div>
    <p>By creating a Network Analytics Logs job, Cloudflare will continuously push logs of packet samples directly to the HTTP endpoint of your choice, including Websockets. The logs arrive in JSON format which makes them easy to parse, transform, and aggregate. The logs include packet samples of traffic dropped and passed by the following systems:</p><ol><li><p>Network-layer DDoS Protection Ruleset</p></li><li><p>Advanced TCP Protection</p></li><li><p>Magic Firewall</p></li></ol><p>Note that not all mitigation systems are applicable to all Cloudflare services. Below is a table describing which mitigation service is applicable to which Cloudflare service:</p>
<table>
<thead>
  <tr>
    <th><br /><span>Mitigation System</span></th>
    <th><span>Cloudflare Service</span></th>
  </tr>
  <tr>
    <th><span>Magic Transit</span></th>
    <th><span>Magic WAN</span></th>
    <th><span>Spectrum</span></th>
  </tr>
</thead>
<tbody>
  <tr>
    <td><span>Network-layer DDoS Protection Ruleset</span></td>
    <td><span>✅</span></td>
    <td><span>❌</span></td>
    <td><span>✅</span></td>
  </tr>
  <tr>
    <td><span>Advanced TCP Protection</span></td>
    <td><span>✅</span></td>
    <td><span>❌</span></td>
    <td><span>❌</span></td>
  </tr>
  <tr>
    <td><span>Magic Firewall </span></td>
    <td><span>✅</span></td>
    <td><span>✅</span></td>
    <td><span>❌</span></td>
  </tr>
</tbody>
</table><p>Packets are processed by the mitigation systems in the order outlined above. Therefore, a packet that passed all three systems may produce three packet samples, one from each system. This can be very insightful when troubleshooting and wanting to understand where in the stack a packet was dropped. To avoid overcounting the total passed traffic, Magic Transit users should only take into consideration the passed packets from the last mitigation system, Magic Firewall.</p><p>An example of a packet sample log:</p>
            <pre><code>{"AttackCampaignID":"","AttackID":"","ColoName":"bkk06","Datetime":1652295571783000000,"DestinationASN":13335,"Direction":"ingress","IPDestinationAddress":"(redacted)","IPDestinationSubnet":"/24","IPProtocol":17,"IPSourceAddress":"(redacted)","IPSourceSubnet":"/24","MitigationReason":"","MitigationScope":"","MitigationSystem":"magic-firewall","Outcome":"pass","ProtocolState":"","RuleID":"(redacted)","RulesetID":"(redacted)","RulesetOverrideID":"","SampleInterval":100,"SourceASN":38794,"Verdict":"drop"}</code></pre>
            <p>All the available log fields are documented here: <a href="https://developers.cloudflare.com/logs/reference/log-fields/account/network_analytics_logs/">https://developers.cloudflare.com/logs/reference/log-fields/account/network_analytics_logs/</a></p>
    <div>
      <h2>Setting up the logs</h2>
      <a href="#setting-up-the-logs">
        
      </a>
    </div>
    <p>In this walkthrough, we will demonstrate how to feed the Network Analytics Logs into Splunk via <a href="https://www.postman.com/">Postman</a>. At this time, it is only possible to set up Network Analytics Logs via API. Setting up the logs requires three main steps:</p><ol><li><p>Create a Cloudflare API token.</p></li><li><p>Create a Splunk Cloud HTTP Event Collector (HEC) token.</p></li><li><p>Create and enable a Cloudflare Logpush job.</p></li></ol><p>Let’s get started!</p>
    <div>
      <h3>1) Create a Cloudflare API token</h3>
      <a href="#1-create-a-cloudflare-api-token">
        
      </a>
    </div>
    <ol><li><p>Log in to your Cloudflare account and navigate to <b>My Profile.</b></p></li><li><p>On the left-hand side, in the collapsing navigation menu, click <b>API Tokens.</b></p></li><li><p>Click <b>Create Token</b> and then, under <b>Custom token</b>, click <b>Get started.</b></p></li><li><p>Give your custom token a name, and select an Account scoped permission to edit Logs. You can also scope it to a specific/subset/all of your accounts.</p></li><li><p>At the bottom, click <b>Continue to summary</b>, and then <b>Create Token</b>.</p></li><li><p><b>Copy</b> and save your token. You can also test your token with the provided snippet in Terminal.</p></li></ol><p>When you're using an API token, you don't need to provide your email address as part of the API credentials.</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/4HD1OtGie9s6CbcVPA7KUx/2f103d64615a7d44e9d3adebc1e12a5b/image5-17.png" />
            
            </figure><p>Read more about creating an API token on the Cloudflare Developers website: <a href="https://developers.cloudflare.com/api/tokens/create/">https://developers.cloudflare.com/api/tokens/create/</a></p>
    <div>
      <h3>2) Create a Splunk token for an HTTP Event Collector</h3>
      <a href="#2-create-a-splunk-token-for-an-http-event-collector">
        
      </a>
    </div>
    <p>In this walkthrough, we’re using a Splunk Cloud free trial, but <a href="https://developers.cloudflare.com/logs/get-started/enable-destinations/">you can use almost any service that can accept logs over HTTPS</a>. In some cases, if you’re using an on-premise SIEM solution, you may need to allowlist <a href="https://www.cloudflare.com/ips/">Cloudflare IP address</a> in your firewall to be able to receive the logs.</p><ol><li><p>Create a Splunk Cloud account. I created a trial account for the purpose of this blog.</p></li><li><p>In the Splunk Cloud dashboard, go to <b>Settings</b> &gt; <b>Data Input.</b></p></li><li><p>Next to <b>HTTP Event Collector</b>, click <b>Add new.</b></p></li><li><p>Follow the steps to create a token.</p></li><li><p>Copy your token and your allocated Splunk hostname and save both for later.</p></li></ol>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/3n4C580G6FxELEaOqPO7Wz/58c6f8fa136f71413b008ace676c3ca9/image2-41.png" />
            
            </figure><p>Read more about using Splunk with Cloudflare Logpush on the Cloudflare Developers website: <a href="https://developers.cloudflare.com/logs/get-started/enable-destinations/splunk/">https://developers.cloudflare.com/logs/get-started/enable-destinations/splunk/</a></p><p>Read more about creating an HTTP Event Collector token on Splunk’s website: <a href="https://docs.splunk.com/Documentation/Splunk/8.2.6/Data/UsetheHTTPEventCollector">https://docs.splunk.com/Documentation/Splunk/8.2.6/Data/UsetheHTTPEventCollector</a></p>
    <div>
      <h3>3) Create a Cloudflare Logpush job</h3>
      <a href="#3-create-a-cloudflare-logpush-job">
        
      </a>
    </div>
    <p>Creating and enabling a job is very straightforward. It requires only one API call to Cloudflare to create and enable a job.</p><p>To send the API calls I used <a href="https://www.postman.com/">Postman</a>, which is a user-friendly API client that was recommended to me by a colleague. It allows you to save and customize API calls. You can also use Terminal/CMD or any other API client/script of your choice.</p><p>One thing to notice is Network Analytics Logs are <b>account</b>-scoped. The API endpoint is therefore a tad different from what you would normally use for zone-scoped datasets such as HTTP request logs and DNS logs.</p><p>This is the endpoint for creating an account-scoped Logpush job:</p><p><code>https://api.cloudflare.com/client/v4/accounts/**{account-id}**/logpush/jobs</code></p><p>Your account identifier number is a unique identifier of your account. It is a string of 32 numbers and characters. If you’re not sure what your account identifier is, log in to Cloudflare, select the appropriate account, and copy the string at the end of the URL.</p><p><code>https://dash.cloudflare.com/**{account-id}**</code></p><p>Then, set up a new request in Postman (or any other API client/CLI tool).</p><p>To successfully create a Logpush job, you’ll need the HTTP method, URL, Authorization token, and request body (data). The request body must include a destination configuration (<code>destination_conf</code>), the specified dataset (<code>network_analytics_logs</code>, in our case), and the token (your Splunk token).</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/1SlAhohD7nH7Ge6ODVPiWT/132cb3aa127ba2cc4596b2fbc2f2c6e8/image1-48.png" />
            
            </figure><p><b>Method</b>:</p><p><code>POST</code></p><p><b>URL</b>:</p><p><code>https://api.cloudflare.com/client/v4/accounts/**{account-id}**/logpush/jobs</code></p><p><b>Authorization</b>: Define a Bearer authorization in the <b>Authorization</b> tab, or add it to the header, and add your Cloudflare API token.</p><p><b>Body</b>: Select a <b>Raw</b> &gt; <b>JSON</b></p>
            <pre><code>{
"destination_conf": "{your-unique-splunk-configuration}",
"dataset": "network_analytics_logs",
"token": "{your-splunk-hec-tag}",
"enabled": "true"
}</code></pre>
            <p>If you’re using Splunk Cloud, then your unique configuration has the following format:</p><p><code>**{your-unique-splunk-configuration}=**splunk://**{your-splunk-hostname}**.splunkcloud.com:8088/services/collector/raw?channel=**{channel-id}**&amp;header_Authorization=Splunk%20**{your-splunk–hec-token}**&amp;insecure-skip-verify=false</code></p><p>Definition of the variables:</p><p><code><b>{your-splunk-hostname}</b></code>= Your allocated Splunk Cloud hostname.</p><p><code><b>{channel-id}</b></code> = A unique ID that you choose to assign for.<b>`{your-splunk–hec-token}`</b> = The token that you generated for your Splunk HEC.</p><p>An important note is that customers should have a valid <a href="https://www.cloudflare.com/application-services/products/ssl/">SSL/TLS certificate</a> on their Splunk instance to support an encrypted connection.</p><p>After you’ve done that, you can create a GET request to the same URL (no request body needed) to verify that the job was created and is enabled.</p><p>The response should be similar to the following:</p>
            <pre><code>{
    "errors": [],
    "messages": [],
    "result": {
        "id": {job-id},
        "dataset": "network_analytics_logs",
        "frequency": "high",
        "kind": "",
        "enabled": true,
        "name": null,
        "logpull_options": null,
        "destination_conf": "{your-unique-splunk-configuration}",
        "last_complete": null,
        "last_error": null,
        "error_message": null
    },
    "success": true
}</code></pre>
            <p>Shortly after, you should start receiving logs to your Splunk HEC.</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/45iJNwBrNbf0ptNsRdc4N/7a2464708c75292b4f704a89a20220f2/image4-27.png" />
            
            </figure><p>Read more about enabling Logpush on the Cloudflare Developers website: <a href="https://developers.cloudflare.com/logs/reference/logpush-api-configuration/examples/example-logpush-curl/">https://developers.cloudflare.com/logs/reference/logpush-api-configuration/examples/example-logpush-curl/</a></p>
    <div>
      <h2>Reduce costs with R2 storage</h2>
      <a href="#reduce-costs-with-r2-storage">
        
      </a>
    </div>
    <p>Depending on the amount of logs that you read and write, the cost of third party cloud storage can skyrocket — forcing you to decide between managing a tight budget and being able to properly investigate networking and security issues. However, we believe that you shouldn’t have to make those trade-offs. With <a href="/logs-r2/">R2’s low costs</a>, we’re making this decision easier for our customers. Instead of feeding logs to a third party, you can reap the cost benefits of <a href="/logs-r2/">storing them in R2</a>.</p><p>To learn more about the <a href="https://www.cloudflare.com/developer-platform/r2/">R2 features and pricing</a>, check out the <a href="/r2-open-beta/">full blog post</a>. To enable R2, contact your account team.</p>
    <div>
      <h2>Cloudflare logs for maximum visibility</h2>
      <a href="#cloudflare-logs-for-maximum-visibility">
        
      </a>
    </div>
    <p><a href="https://www.cloudflare.com/plans/enterprise/">Cloudflare Enterprise</a> customers have access to detailed logs of the metadata generated by our products. These logs are helpful for troubleshooting, identifying network and configuration adjustments, and generating reports, especially when combined with logs from other sources, such as your servers, firewalls, routers, and other appliances.</p><p>Network Analytics Logs joins Cloudflare’s family of products on Logpush: <a href="https://developers.cloudflare.com/logs/reference/log-fields/zone/dns_logs/">DNS logs</a>, <a href="https://developers.cloudflare.com/logs/reference/log-fields/zone/firewall_events/">Firewall events</a>, <a href="https://developers.cloudflare.com/logs/reference/log-fields/zone/http_requests/">HTTP requests</a>, <a href="https://developers.cloudflare.com/logs/reference/log-fields/zone/nel_reports/">NEL reports</a>, <a href="https://developers.cloudflare.com/logs/reference/log-fields/zone/spectrum_events/">Spectrum events</a>, <a href="https://developers.cloudflare.com/logs/reference/log-fields/account/audit_logs/">Audit logs</a>, <a href="https://developers.cloudflare.com/logs/reference/log-fields/account/gateway_dns/">Gateway DNS</a>, <a href="https://developers.cloudflare.com/logs/reference/log-fields/account/gateway_http/">Gateway HTTP</a>, and <a href="https://developers.cloudflare.com/logs/reference/log-fields/account/gateway_network/">Gateway Network</a>.</p><p>Not using Cloudflare yet? <a href="https://dash.cloudflare.com/sign-up">Start now</a> with our Free and <a href="https://www.cloudflare.com/plans/pro/">Pro plans</a> to protect your websites against DDoS attacks, or <a href="https://www.cloudflare.com/magic-transit/">contact us</a> for comprehensive <a href="https://www.cloudflare.com/ddos/">DDoS protection</a> and <a href="https://www.cloudflare.com/learning/cloud/what-is-a-cloud-firewall/">firewall-as-a-service</a> for your entire network.</p> ]]></content:encoded>
            <category><![CDATA[DDoS]]></category>
            <category><![CDATA[Data]]></category>
            <category><![CDATA[Magic Transit]]></category>
            <category><![CDATA[Spectrum]]></category>
            <category><![CDATA[Logs]]></category>
            <category><![CDATA[Network]]></category>
            <category><![CDATA[SIEM]]></category>
            <guid isPermaLink="false">7J0cgdiD9dX3Xb9q1OaN3f</guid>
            <dc:creator>Omer Yoachimik</dc:creator>
            <dc:creator>Kyle Bowman</dc:creator>
        </item>
        <item>
            <title><![CDATA[Introducing the Customer Metadata Boundary]]></title>
            <link>https://blog.cloudflare.com/introducing-the-customer-metadata-boundary/</link>
            <pubDate>Tue, 07 Dec 2021 13:59:15 GMT</pubDate>
            <description><![CDATA[ Cloudflare’s Data Localisation Suite now helps customers localise metadata about their HTTP traffic. ]]></description>
            <content:encoded><![CDATA[ <p></p><p>Data localisation has gotten a lot of attention in recent years because a number of countries see it as a way of controlling or protecting their citizens’ data. Countries such as Australia, China, India, Brazil, and South Korea have or are currently considering regulations that assert <a href="https://www.cloudflare.com/learning/privacy/what-is-data-sovereignty/">legal sovereignty over their citizens’ personal data</a> in some fashion — health care data must be stored locally; public institutions may only contract with local service providers, etc.</p><p>In the EU, the recent “Schrems II” decision resulted in additional requirements for companies that transfer personal data outside the EU. And a number of highly regulated industries require that specific types of personal data stay within the EU’s borders.</p><p>Cloudflare is committed to helping our customers keep personal data in the EU. Last year, we introduced the <a href="/introducing-the-cloudflare-data-localization-suite/">Data Localisation Suite</a>, which gives customers control over where their data is inspected and stored.</p><p>Today, we’re excited to introduce the Customer Metadata Boundary, which expands the Data Localisation Suite to ensure that a customer’s end user traffic metadata stays in the EU.</p>
    <div>
      <h3>Metadata: a primer</h3>
      <a href="#metadata-a-primer">
        
      </a>
    </div>
    <p>“Metadata” can be a scary term, but it’s a simple concept — it just means “data about data.” In other words, it’s a description of activity that happened on our network. Every service on the Internet collects metadata in some form, and it’s vital to user safety and network availability.</p><p>At Cloudflare, we collect metadata about the usage of our products for several purposes:</p><ul><li><p>Serving analytics via our dashboards and APIs</p></li><li><p>Sharing logs with customers</p></li><li><p>Stopping security threats such as bot or DDoS attacks</p></li><li><p>Improving the performance of our network</p></li><li><p>Maintaining the reliability and resiliency of our network</p></li></ul><p>What does that collection look like in practice at Cloudflare? Our network consists of dozens of services: our Firewall, Cache, DNS Resolver, DDoS protection systems, Workers runtime, and more. Each service emits structured log messages, which contain fields like timestamps, URLs, usage of Cloudflare features, and the identifier of the customer’s account and zone.</p><p>These messages do not contain the <i>contents</i> of customer traffic, and so they do <b>not</b> contain things like usernames, passwords, personal information, and other private details of customers’ end users. However, these logs may contain end-user IP addresses, which is considered personal data in the EU.</p>
    <div>
      <h3>Data Localisation in the EU</h3>
      <a href="#data-localisation-in-the-eu">
        
      </a>
    </div>
    <p>The EU’s General Data Protection Regulation, or GDPR, is one of the world’s most comprehensive (and well known) data privacy laws. The GDPR does <i>not</i>, however, insist that personal data must stay in Europe. Instead, it provides a number of legal mechanisms to ensure that GDPR-level protections are available for EU personal data if it is transferred outside the EU to a third country like the United States. Data transfers from the EU to the US were, until recently, permitted under an agreement called the <a href="https://www.privacyshield.gov/welcome">EU-U.S. Privacy Shield Framework</a>.</p><p>Shortly after the GDPR went into effect, a privacy activist named <a href="https://en.wikipedia.org/wiki/Max_Schrems">Max Schrems</a> filed suit against Facebook for their data collection practices. In July 2020, the Court of Justice of the EU issued the “Schrems II” ruling — which, among other things, invalidated the Privacy Shield framework. However, the court upheld other valid transfer mechanisms that ensure EU personal data won’t be accessed by U.S. government authorities in a way that violates the GDPR.</p><p>Since the Schrems II decision, many customers have asked us how we’re protecting EU citizens’ data. Fortunately, Cloudflare has had <a href="https://www.cloudflare.com/gdpr/introduction/">data protection safeguards</a> in place since well before the Schrems II case, such as our <a href="https://assets.ctfassets.net/slt3lc6tev37/2RM2ZAb5XJiudjz4QHvth4/b3df347d8a7a629ccd5cadd4f7cfd2f3/BDES-1406_Privacy_Day_Whitepaper_2021.pdf">industry-leading commitments</a> on government data requests. In response to Schrems II in particular, we updated our customer <a href="https://www.cloudflare.com/cloudflare-customer-dpa/">Data Processing Addendum</a> (DPA). We incorporated the latest <a href="https://www.cloudflare.com/cloudflare-customer-scc/">Standard Contractual Clauses</a>, which are legal agreements approved by the EU Commission that enable data transfer. We also added additional safeguards as outlined in the <a href="https://edpb.europa.eu/news/news/2021/edpb-adopts-final-version-recommendations-supplementary-measures-letter-eu_en">EDPB’s June 2021 Recommendations on Supplementary Measures</a>. Finally, Cloudflare’s services are certified under the ISO 27701 standard, which maps to the GDPR’s requirements.</p><p>In light of these measures, we believe that our EU customers can use Cloudflare’s services in a manner consistent with GDPR and the Schrems II decision. Still, we recognize that many of our customers want their EU personal data to stay in the EU. For example, some of our customers in industries like healthcare, law, and finance may have additional requirements.  For that reason, we have developed an optional suite of services to address those requirements. We call this our Data Localisation Suite.</p>
    <div>
      <h3>How the Data Localisation Suite helps today</h3>
      <a href="#how-the-data-localisation-suite-helps-today">
        
      </a>
    </div>
    <p>Data Localisation is challenging for customers because of the volume and variety of data they handle. When it comes to their Cloudflare traffic, we’ve found that customers are primarily concerned about three areas:</p><ol><li><p>How do I ensure my encryption keys stay in the EU?</p></li><li><p>How can I ensure that services like caching and <a href="https://www.cloudflare.com/learning/ddos/glossary/web-application-firewall-waf/">WAF</a> only run in the EU?</p></li><li><p>How can ensure that metadata is never transferred outside the EU?</p></li></ol><p>To address the first concern, Cloudflare has long offered <a href="https://www.cloudflare.com/ssl/keyless-ssl/">Keyless SSL</a> and <a href="/introducing-cloudflare-geo-key-manager/">Geo Key Manager</a>, which ensure that private SSL/TLS key material never leaves the EU. Keyless SSL ensures that Cloudflare never has possession of the private key material at all; Geo Key Manager uses Keyless SSL under the hood to ensure the keys never leave the specified region.</p><p>Last year we addressed the second concern with <a href="/introducing-regional-services/">Regional Services</a>, which ensures that Cloudflare will only be able to decrypt and inspect the content of HTTP traffic inside the EU. In other words, SSL connections will only be terminated in the EU, and all of our layer 7 security and performance services will only run in our EU data centers.</p><p>Today, we’re enabling customers to address the third and final concern, and keep metadata local as well.</p>
    <div>
      <h3>How the Metadata Boundary Works</h3>
      <a href="#how-the-metadata-boundary-works">
        
      </a>
    </div>
    <p>The Customer Metadata Boundary ensures, simply, that end user traffic metadata that can identify a customer stays in the EU. This includes all the logs and analytics that a customer sees.</p><p>How are we able to do this? All the metadata that can identify a customer flows through a single service at our edge, before being forwarded to one of our core data centers.</p><p>When the Metadata Boundary is enabled for a customer, our edge ensures that any log message that identifies that customer (that is, contains that customer's Account ID) is not sent outside the EU. It will only be sent to our core data center in the EU, and not our core data center in the US.</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/12lcx2Efei67w6jfteXoMN/d3c2c22e54af6bf25d549067e559dae8/image2-14.png" />
            
            </figure>
    <div>
      <h3>What’s next</h3>
      <a href="#whats-next">
        
      </a>
    </div>
    <p>Today our Data Localisation Suite is focused on helping our customers in the EU localise data for their inbound HTTP traffic. This includes our Cache, Firewall, <a href="https://www.cloudflare.com/ddos/">DDoS protection</a>, and Bot Management products.</p><p>We’ve heard from customers that they want data localisation for more products and more regions. This means making all of our Data Localisation Products, including Geo Key Manager and Regional Services, work globally. We’re also working on expanding the Metadata Boundary to include our Zero Trust products like Cloudflare for Teams. Stay tuned!</p> ]]></content:encoded>
            <category><![CDATA[CIO Week]]></category>
            <category><![CDATA[Privacy]]></category>
            <category><![CDATA[Compliance]]></category>
            <category><![CDATA[Data]]></category>
            <category><![CDATA[Europe]]></category>
            <category><![CDATA[Policy & Legal]]></category>
            <guid isPermaLink="false">74am2210Vl5UqONcLXY00k</guid>
            <dc:creator>Jon Levine</dc:creator>
            <dc:creator>Emily Hancock</dc:creator>
        </item>
        <item>
            <title><![CDATA[The UEFA EURO 2020 final as seen online by Cloudflare Radar]]></title>
            <link>https://blog.cloudflare.com/the-uefa-euro-2020-final-as-seen-online-by-cloudflare-radar/</link>
            <pubDate>Mon, 12 Jul 2021 16:41:13 GMT</pubDate>
            <description><![CDATA[ Cloudflare has been publishing statistics about all the teams involved in EURO 2020 and traffic to betting websites, sports newspapers, streaming services and sponsors. Here’s a quick look at some specific highlights from England’s and Italy’s EURO 2020. ]]></description>
            <content:encoded><![CDATA[ <p></p><p>Last night’s Italy-England match was a nail-biter. 1-1 at full time, 1-1 at the end of extra time, and then an amazing penalty shootout with incredible goalkeeping by Pickford and Donnarumma.</p><p>Cloudflare has been publishing statistics about all the teams involved in EURO 2020 and traffic to betting websites, sports newspapers, streaming services and sponsors. Here’s a quick look at some specific highlights from England’s and Italy’s EURO 2020.</p><p>Two interesting peaks show up in UK visits to sports newspapers: the day after England-Germany and today after England’s defeat. Looks like fans are hungry for analysis and news beyond the goals. You can see all the data on the dedicated <a href="https://radar.cloudflare.com/euro2020/gb-en">England EURO 2020 page on Cloudflare Radar</a>.</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/1nD5hrSgZn7Oi8thGQVtnz/85c5e8c74e00f2f3a2cf77cbb44fb25f/cloudflare-radar-euro2020-newspapers-gb-eng-2021-07-12T15_41_57.198Z.png" />
            
            </figure><p>But it was a quiet morning for the websites of the England team’s sponsors.</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/6flXyJQHvIG74KKwE2Jj2Y/e0180ac258d8f234f1aa6e61fb6ccd29/cloudflare-radar-euro2020-team-sponsors-gb-eng-2021-07-12T15_42_47.404Z.png" />
            
            </figure><p>Turning to the winners, we can see that Italian readers are even more interested in knowing more about their team’s success.</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/3VeILWfrl1eKj2VKHIBoQz/f31a8b23a2c8f752269f9fe3d2a1b57a/cloudflare-radar-euro2020-newspapers-it-2021-07-12T15_45_21.870Z.png" />
            
            </figure><p>And this enthusiasm spills over into visits to the Italian team’s sponsors.</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/1ZPkR6GnTWXxcPdneoe1F4/be45def9ae95ee5624d9063d4ab6053c/cloudflare-radar-euro2020-team-sponsors-it-2021-07-12T15_46_40.648Z.png" />
            
            </figure><p>You can follow along on the dedicated Cloudflare Radar page for <a href="https://radar.cloudflare.com/euro2020/it">Italy in EURO 2020</a>.</p><p>Visit <a href="https://radar.cloudflare.com/">Cloudflare Radar</a> for information on global Internet trends, trending domains, attacks and usage statistics.</p> ]]></content:encoded>
            <category><![CDATA[Europe]]></category>
            <category><![CDATA[Radar]]></category>
            <category><![CDATA[Data]]></category>
            <guid isPermaLink="false">HhZbjLKlMXaZDlyb463Kf</guid>
            <dc:creator>John Graham-Cumming</dc:creator>
        </item>
        <item>
            <title><![CDATA[Using data science and machine learning for improved customer support]]></title>
            <link>https://blog.cloudflare.com/using-data-science-and-machine-learning-for-improved-customer-support/</link>
            <pubDate>Mon, 15 Jun 2020 11:00:00 GMT</pubDate>
            <description><![CDATA[ In this blog post we’ll explore three tricks that can be used for data science that helped us solve real problems for our customer support group and our customers. Two for natural language processing in a customer support context and one for identifying attack Internet attack traffic. ]]></description>
            <content:encoded><![CDATA[ <p>In this blog post we’ll explore three tricks that can be used for data science that helped us solve real problems for our customer support group and our customers. Two for natural language processing in a customer support context and one for identifying attack Internet attack traffic.</p><p>Through these examples, we hope to demonstrate how invaluable data processing tricks, visualisations and tools can be before putting data into a <a href="https://www.cloudflare.com/learning/ai/what-is-machine-learning/">machine learning algorithm</a>. By refining data prior to processing, we are able to achieve dramatically improved results without needing to change the underlying machine learning strategies which are used.</p>
    <div>
      <h3>Know the Limits (Language Classification)</h3>
      <a href="#know-the-limits-language-classification">
        
      </a>
    </div>
    <p>When browsing a social media site, you may find the site prompts you to translate a post even though it is in your language.</p><p>We recently came across a similar problem at Cloudflare when we were looking into language classification for chat support messages. Using an off-the-shelf classification algorithm, users with short messages often had their chats classified incorrectly and our analysis found there’s a correlation between the length of a message and the accuracy of the classification (based on the browser <i>Accept-Language</i> header and the languages of the country where the request was submitted):</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/1H7x1KifG0I4tDOfvdAdgK/1dc413df90de1351c60ec043b5807d5c/image2-5.png" />
            
            </figure><p>On a subset of tickets, comparing the classified language against the web browser <i>Accept-Language</i> header, we found there was broad agreement between these two properties. When we considered the languages associated with the user’s country, we found another signal.</p><p>In 67% of our sample, we found agreement between these three signals. In 15% of instances the classified language agreed with only the <i>Accept-Language</i> header and in 5% of cases there was only agreement with the languages associated with the user’s country.</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/4YKHwax3NQcfFkdDXYqmT9/9c3fdefcf530069d05fc8c74cbedd6bd/pie-chart.png" />
            
            </figure><p>We decided the ideal approach was to train a machine learning model that would take all three signals (plus the confidence rate from the language classification algorithm) and use that to make a prediction. By knowing the limits of a given classification algorithm, we were able to develop an approach that helped compliment it.</p><p>A naive approach to do the same may not even need a trained model to do so, simply requiring agreement between two of three properties (classified language, <i>Accept-Language</i> header and country header) helps make a decision about the right language to use.</p>
    <div>
      <h3>Hold Your Fire (Fuzzy String Matching)</h3>
      <a href="#hold-your-fire-fuzzy-string-matching">
        
      </a>
    </div>
    <p>Fuzzy String Matching is often used in natural language processing when trying to extract information from human text. For example, this can be used for extracting error messages from customer support tickets to do automatic classification. At Cloudflare, we use this as one signal in our natural language processing pipeline for support tickets.</p><p>Engineers often use the <a href="https://en.wikipedia.org/wiki/Levenshtein_distance">Levenshtein distance</a> algorithm for string matching; for example, this algorithm is implemented in the Python <a href="https://github.com/seatgeek/fuzzywuzzy">fuzzywuzzy</a> library. This approach has a high computational overhead (for two strings of length <i>k</i> and <i>l</i>, the algorithm runs in <i>O(k * l)</i> time).</p><p>To understand the performance of different string matching algorithms in a customer support context, we compared multiple algorithms (<a href="https://en.wikipedia.org/wiki/Cosine_similarity">Cosine</a>, Dice, <a href="https://en.wikipedia.org/wiki/Damerau%E2%80%93Levenshtein_distance">Damerau</a>, <a href="https://en.wikipedia.org/wiki/Longest_common_subsequence_problem">LCS</a> and Levenshtein) and measured the true positive rate (TP), false positive rate (FP) and the ratio of false positives to true positives (FP/TP).</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/2jtTXpLIyC8gFEXY0IoVUR/b81cc32f76a51eaeb9980e78b54a5d12/image4-1.png" />
            
            </figure><p>We opted for the Cosine algorithm, not just because it outperformed the Levenshtein algorithm, but also the computational difficulty was reduced to <i>O(k + l)</i> time. The Cosine similarity algorithm is a very simple algorithm; it works by representing words or phrases as a vector representation in a multidimensional vector space, where each unique letter of an alphabet is a separate dimension. The smaller the angle between the two vectors, the closer the word is to another.</p><p>The mathematical definitions of each string similarity algorithm and a scientific comparison can be found in our paper: <i>M. Pikies and J. Ali, "String similarity algorithms for a ticket classification system," 2019 6th International Conference on Control, Decision and Information Technologies (CoDIT), Paris, France, 2019, pp. 36-41.</i> <a href="https://doi.org/10.1109/CoDIT.2019.8820497"><i>https://doi.org/10.1109/CoDIT.2019.8820497</i></a></p><p>There were other optimisations we introduce to the fuzzy string matching approaches; the similarity threshold is determined by evaluating the True Positive and False Positive rates on various sample data. We further devised a new tokenization approach for handling phrases and numeric strings whilst using the <a href="https://fasttext.cc/">FastText</a> natural language processing library to determine candidate values for fuzzy string matching and to improve overall accuracy, we will share more about these optimisations in a further blog post.</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/5Ooy4FyAn96ohSAClqKA1G/e5483e8b44779a151ea64662a900de4c/image1-6.png" />
            
            </figure>
    <div>
      <h3>“Beyond it is Another Dimension” (Threat Identification)</h3>
      <a href="#beyond-it-is-another-dimension-threat-identification">
        
      </a>
    </div>
    <p>Attack alerting is particularly important at Cloudflare - this is useful for both monitoring the overall status of our network and providing proactive support to particular at-risk customers.</p><p>DDoS attacks can be represented in granularity by a few different features; including differences in request or error rates over a temporal baseline, the relationship between errors and request volumes and other metrics that indicate attack behaviour. One example of a metric we use to differentiate between whether a customer is under a low volume attack or they are experiencing another issue is the relationship between 499 error codes vs 5xx HTTP status codes. Cloudflare’s network edge returns a <a href="https://support.cloudflare.com/hc/en-us/articles/115003014512-4xx-Client-Error">499 status code</a> when the client disconnects before the origin web server has an opportunity to respond, whilst <a href="https://support.cloudflare.com/hc/en-us/articles/115003011431/">5xx status codes</a> indicate an error handling the request. In the chart below; the x-axis measures the differential increase in 5xx errors over a baseline, whilst the y-axis represents the rate of 499 responses (each scatter represents a 15 minute interval). During a DDoS attack we notice a linear correlation between these criteria, whilst origin issues typically have an increase in one metric instead of another:</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/66cEIbNUGVASqffqgo3r4q/2486d947b796704b2235bde389d0ee38/image7-1.png" />
            
            </figure><p>The next question is how this data can be used in more complicated situations - take the following example of identifying a credential stuffing attack in aggregate. We looked at a small number of anonymised data fields for the most prolific <a href="https://www.cloudflare.com/learning/security/how-to-improve-wordpress-security/">attackers</a> of WordPress login portals. The data is based purely on HTTP headers, in total we saw 820 unique IPs towards 16,248 distinct zones (the IPs were hashed and requests were placed into “buckets” as they were collected). As WordPress returns a HTTP 200 when a login fails and a HTTP 302 on a successful login (redirecting to the login panel), we’re able to analyse this just from the status code returned.</p><p>On the left hand chart, the x-axis represents a normalised number of unique zones that are <a href="https://www.cloudflare.com/ddos/under-attack/">under attack</a> (0 means the attacker is hitting the same site whilst 1 means the attacker is hitting all different sites) and the y-axis represents the success rate (using HTTP status codes, identifying the chance of a successful login). The right hand side chart switches the x-axis out for something called the “variety ratio” - this measures the rate of abnormal 4xx/5xx HTTP status codes (i.e. firewall blocks, rate limiting HTTP headers or 5xx status codes). We see clear clusters on both charts:</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/3lDpsL4cBqJFPCIthPHJTu/788fc21ce1c833e7847e95d8105a1ccd/image6-3.png" />
            
            </figure><p>However, by plotting this chart in three dimensions with all three fields represented - clusters appear. These clusters are then grouped using an unsupervised clustering algorithm (agglomerative hierarchical clustering):</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/18RKc8AxZxggjhuM59bUR8/9331e094e73b00d8fb240a817ef27253/image8.png" />
            
            </figure><p>Cluster 1 has 99.45% of requests from the same country and 99.45% from the same User-Agent. This tactic, however, has advantages when looking at other clusters - for example, Cluster 0 had 89% of requests coming from three User-Agents (75%, 12.3% and 1.7%, respectively). By using this approach we are able to correlate such attacks together even when they would be hard to identify on a request-to-request basis (as they are being made from different IPs and with different request headers). Such strategies allow us to fingerprint attacks regardless of whether attackers are continuously changing how they make these requests to us.</p><p>By aggregating data together then representing the data in multiple dimensions, we are able to gain visibility into the data that would ordinarily not be possible on a request-to-request basis. In product level functionality, it is often important to make decisions on a signal-to-signal basis (“should this request be challenged whilst this one is allowed?”) but by looking at the data in aggregate we are able to focus  on the interesting clusters and provide alerting systems which identify anomalies. Performing this in multiple dimensions provides the tools to reduce false positives dramatically.</p>
    <div>
      <h3>Conclusion</h3>
      <a href="#conclusion">
        
      </a>
    </div>
    <p>From natural language processing to intelligent threat fingerprinting, using data science techniques has improved our ability to build new functionality. Recently, new machine learning approaches and strategies have been designed to process this data more efficiently and effectively; however, preprocessing of data remains a vital tool for doing this. When seeking to optimise data processing pipelines, it often helps to look not just at the tools being used, but also the input and structure of the data you seek to process.</p><p>If you're interested in using data science techniques to identify threats on a large scale network, we're hiring for <a href="https://www.cloudflare.com/careers/jobs/">Support Engineers</a> (including Security Operations, Technical Support and Support Operations Engineering) in San Francisco, Austin, Champaign, London, Lisbon, Munich and Singapore.</p> ]]></content:encoded>
            <category><![CDATA[Data]]></category>
            <category><![CDATA[Support]]></category>
            <category><![CDATA[AI]]></category>
            <guid isPermaLink="false">2Fa1UWMwjIIBrrGpflk5Cj</guid>
            <dc:creator>Junade Ali</dc:creator>
            <dc:creator>Malgorzata Pikies</dc:creator>
            <dc:creator>Andronicus Riyono</dc:creator>
        </item>
        <item>
            <title><![CDATA[JavaScript Libraries Are Almost Never Updated Once Installed]]></title>
            <link>https://blog.cloudflare.com/javascript-libraries-are-almost-never-updated/</link>
            <pubDate>Mon, 27 Jan 2020 16:48:58 GMT</pubDate>
            <description><![CDATA[ An analysis, based on CDNJS data, of when and if JavaScript libraries are updated after being installed onto websites. ]]></description>
            <content:encoded><![CDATA[ <p>Cloudflare helps run <a href="https://cdnjs.com/">CDNJS</a>, a very popular way of including JavaScript and other frontend resources on web pages. With the CDNJS team’s permission we collect anonymized and aggregated data from CDNJS requests which we use to understand how people build on the Internet. Our analysis today is focused on one question: once installed on a site, do JavaScript libraries ever get updated?</p><p>Let’s consider jQuery, the most popular JavaScript library on Earth. This chart shows the number of requests made for a selected list of jQuery versions over the past 12 months:</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/78wDloZEiwcRwOAIt0a9i9/72f4ca0a07fcfe78bf499b353540cccd/1-1.png" />
            
            </figure><p>Spikes in the CDNJS data as you see with version 3.3.1 are not uncommon as very large sites add and remove CDNJS script tags.</p><p>We see a steady rise of version 3.4.1 following its release on May 2nd, 2019. What we don’t see is a substantial decline of old versions. Version 3.2.1 shows an average popularity of 36M requests at the beginning of our sample, and 29M at the end, a decline of approximately 20%. This aligns with a <a href="http://informationr.net/ir/9-2/paper174.html#2002">corpus of research</a> which shows the average website lasts somewhere between two and four years. What we don’t see is a decline in our old versions which come close to the volume of growth of new versions when they’re released. In fact the release of 3.4.1, as popular as it quickly becomes, doesn’t change the trend of old version deprecation at all.</p><p>If you’re curious, the oldest version of jQuery CDNJS includes is 1.10.0, released on May 25, 2013. The project still gets an average of 100k requests per day, and the sites which use it are growing in popularity:</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/5DVnwBygO53F6wCoy1lccv/3298e8dc13a983e3a3cf5de85991cd0e/2-1.png" />
            
            </figure><p>To confirm our theory, let’s consider another project, <a href="https://greensock.com/tweenmax/">TweenMax</a>:</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/17MaW2IADBKgVYgVcbQfJA/40c25be214ee62b3157540372ffb959f/3-1.png" />
            
            </figure><p>As this package isn’t as popular as jQuery, the data has been smoothed with a one week trailing average to make it easier to identify trends.</p><p>Version 1.20.4 begins the year with 18M requests, and ends it with 14M, a decline of about 23%, again in alignment with the loss of websites on the Internet. The growth of 2.1.3 shows clear evidence that the release of a new version has almost no bearing on the popularity of old versions, the trend line for those older versions doesn’t change even as 2.1.3 grows to 29M requests per day.</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/6LAcaWQJZ5P02U6pKMX4cP/ffc8d229444938c23ef03f09a3af6db0/4-1.png" />
            
            </figure><p>One conclusion is whatever libraries you publish will exist on websites forever. The underlying web platform consequently must support aged conventions indefinitely if it is to continue supporting the full breadth of the web.</p><p>Cloudflare is very interested in how we can contribute to a web which is kept up-to-date. Please make suggestions in the comments below.</p> ]]></content:encoded>
            <category><![CDATA[JavaScript]]></category>
            <category><![CDATA[CDNJS]]></category>
            <category><![CDATA[Data]]></category>
            <guid isPermaLink="false">db038KkCMtqjJlwaumhOn</guid>
            <dc:creator>Zack Bloom</dc:creator>
        </item>
        <item>
            <title><![CDATA[Logpush: the Easy Way to Get Your Logs to Your Cloud Storage]]></title>
            <link>https://blog.cloudflare.com/cloudflare-logpush-the-easy-way-to-get-your-logs-to-your-cloud-storage/</link>
            <pubDate>Mon, 25 Feb 2019 16:00:00 GMT</pubDate>
            <description><![CDATA[ Today, we’re excited to announce a new way to get your logs: Logpush, a tool for uploading your logs to your cloud storage provider, such as Amazon S3 or Google Cloud Storage. It’s now available in Early Access for Enterprise domains. ]]></description>
            <content:encoded><![CDATA[ <p></p>
    <div>
      <h4>Introducing Logpush</h4>
      <a href="#introducing-logpush">
        
      </a>
    </div>
    <p>Today, we’re excited to announce a new way to get your logs: Logpush, a tool for uploading your logs to your cloud storage provider, such as Amazon S3 or Google Cloud Storage. It’s now available in Early Access for Enterprise domains.</p><p>We first explained Cloudflare’s <a href="/what-cloudflare-logs/">logging functionality</a> almost six years ago. Since then, the number of domains on our network has grown by ten times. We’ve continued giving our Enterprise customers the ability to download logs using a <a href="https://developers.cloudflare.com/logs/logpull-api/">REST API</a>, which has gotten a large number of functional and technical updates. We’ve also been paying attention to how our customers’ needs have evolved, especially as we protect and accelerate increasingly larger domains. This led to the development of Logpush.</p>
    <div>
      <h4>The Value of Logs</h4>
      <a href="#the-value-of-logs">
        
      </a>
    </div>
    <p>Cloudflare works by being an intermediary between our customers’ websites, applications, and devices, and their end-users or potential attackers. As part of providing our service, we create a record of each request that goes through our network. These records (or request logs) have detailed information regarding the connecting client, our actions—including whether the request was served by the cache or blocked by our firewall—and the response from the origin web server. For Enterprise customers who request these logs, we save them for up to a week and make them available for download.</p><p>Although some of our customers download their logs only when they need to investigate a problem or question, others download them regularly by writing scripts using our “Logpull” API. They may then combine their logs with data from other parts of their infrastructure, such as their application servers or marketing tracking tools. This process allows them to create analytics to see what is happening across all of their platforms; debug issues with their Cloudflare configuration or their own systems; and monitor traffic and make adjustments to improve security or performance. In fact, many download the logs and then upload them to a few common cloud services that have become popular for storage and performing analysis.</p><p>We were glad to learn that our logs were proving so useful, but having each customer write their own script simply to download and then upload them to the same few places seemed really inefficient. Couldn’t we just do that on their behalf?</p>
    <div>
      <h4>Pushing Instead of Pulling</h4>
      <a href="#pushing-instead-of-pulling">
        
      </a>
    </div>
    <p>So that’s the basic idea behind Logpush: rather than writing a script to repeatedly download logs using our Logpull API, simply tell us once where to send them, and we’ll push them there for you. You’ll get the exact same logs either way. If you’re already using Logpull, we’ve made transitioning to Logpush easy by keeping all the functionality the same: you can select the fields you want to receive; change the time format for timestamp fields; and even get a randomly-sampled percentage of logs. When setting up a push job using the Logpush API, you can directly copy all of the previous options you had set in Logpull using the `logpull_options` parameter. We also provide a Logpush UI in our Analytics tab that walks you through the setup for a domain. Full documentation on all of our logging products is available in our new <a href="https://developers.cloudflare.com/logs/">developer documentation</a> section.</p><p>Logpush currently works with Amazon S3 and Google Cloud Storage, two of the most popular cloud storage providers. As you may already know, we’re big proponents of <a href="/bandwidth-alliance/">working with many cloud services</a>, so more popular destinations are coming soon. Want to help us decide where to push next? Take <a href="https://goo.gl/forms/CqkUu68ZwaTrtt8g2">this survey</a>. Interested in helping us build? We’re hiring Systems Engineers with an interest in data in <a href="https://boards.greenhouse.io/cloudflare/jobs/584886?gh_jid=584886">San Francisco</a>, <a href="https://boards.greenhouse.io/cloudflare/jobs/1277846?gh_jid=1277846">London</a>, and <a href="https://boards.greenhouse.io/cloudflare/jobs/1418746?gh_jid=1418746">Austin</a>.</p> ]]></content:encoded>
            <category><![CDATA[Analytics]]></category>
            <category><![CDATA[Data]]></category>
            <category><![CDATA[Privacy]]></category>
            <guid isPermaLink="false">5xRwdF89wuNoZ36P1qyxjO</guid>
            <dc:creator>Filipp Nisenzoun</dc:creator>
        </item>
        <item>
            <title><![CDATA[HTTP Analytics for 6M requests per second using ClickHouse]]></title>
            <link>https://blog.cloudflare.com/http-analytics-for-6m-requests-per-second-using-clickhouse/</link>
            <pubDate>Tue, 06 Mar 2018 13:00:00 GMT</pubDate>
            <description><![CDATA[ One of our large scale data infrastructure challenges here at Cloudflare is around providing HTTP traffic analytics to our customers. HTTP Analytics is available to all our customers via two options: ]]></description>
            <content:encoded><![CDATA[ <p>One of our large scale data infrastructure challenges here at Cloudflare is around providing HTTP traffic analytics to our customers. HTTP Analytics is available to all our customers via two options:</p><ul><li><p>Analytics tab in Cloudflare dashboard</p></li><li><p>Zone Analytics API with 2 endpoints</p><ul><li><p><a href="https://api.cloudflare.com/#zone-analytics-dashboard">Dashboard endpoint</a></p></li><li><p><a href="https://api.cloudflare.com/#zone-analytics-analytics-by-co-locations">Co-locations endpoint</a> (Enterprise plan only)</p></li></ul></li></ul><p>In this blog post I'm going to talk about the exciting evolution of the Cloudflare analytics pipeline over the last year. I'll start with a description of the old pipeline and the challenges that we experienced with it. Then, I'll describe how we leveraged ClickHouse to form the basis of a new and improved pipeline. In the process, I'll share details about how we went about schema design and performance tuning for ClickHouse. Finally, I'll look forward to what the Data team is thinking of providing in the future.</p><p>Let's start with the old data pipeline.</p>
    <div>
      <h3>Old data pipeline</h3>
      <a href="#old-data-pipeline">
        
      </a>
    </div>
    <p>The previous pipeline was built in 2014. It has been mentioned previously in <a href="https://blog.cloudflare.com/scaling-out-postgresql-for-cloudflare-analytics-using-citusdb/">Scaling out PostgreSQL for CloudFlare Analytics using CitusDB</a> and <a href="https://blog.cloudflare.com/more-data-more-data/">More data, more data</a> blog posts from the Data team.</p>
          <figure>
          <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/5MY5UJgdkM35pwDy4mMOxt/2c08b73e37001788547db620f00a5a92/Old-system-architecture.jpg" />
          </figure><p>It had following components:</p><ul><li><p><b>Log forwarder </b>- collected Cap'n Proto formatted logs from the edge, notably DNS and Nginx logs, and shipped them to Kafka in Cloudflare central datacenter.</p></li><li><p><b>Kafka cluster </b>- consisted of 106 brokers with x3 replication factor, 106 partitions, ingested Cap'n Proto formatted logs at average rate 6M logs per second.</p></li><li><p><b>Kafka consumers</b> - each of 106 partitions had dedicated Go consumer (a.k.a. Zoneagg consumer), which read logs and produced aggregates per partition per zone per minute and then wrote them into Postgres.
<b>Postgres database</b> - single instance PostgreSQL database (a.k.a. RollupDB), accepted aggregates from Zoneagg consumers and wrote them into temporary tables per partition per minute. It then rolled-up the aggregates into further aggregates with aggregation cron. More specifically:</p><ul><li><p>Aggregates per partition, minute, zone → aggregates data per minute, zone</p></li><li><p>Aggregates per minute, zone → aggregates data per hour, zone</p></li><li><p>Aggregates per hour, zone → aggregates data per day, zone</p></li><li><p>Aggregates per day, zone → aggregates data per month, zone</p></li></ul></li><li><p><b>Citus Cluster</b> - consisted of Citus main and 11 Citus workers with x2 replication factor (a.k.a. Zoneagg Citus cluster), the storage behind Zone Analytics API and our BI internal tools. It had replication cron, which did remote copy of tables from Postgres instance into Citus worker shards.</p></li><li><p><b>Zone Analytics API</b> - served queries from internal PHP API. It consisted of 5 API instances written in Go and queried Citus cluster, and was not visible to external users.</p></li><li><p><b>PHP API </b>- 3 instances of proxying API, which forwarded public API queries to internal Zone Analytics API, and had some business logic on zone plans, error messages, etc.</p></li><li><p><b>Load Balancer </b>- nginx proxy, forwarded queries to PHP API/Zone Analytics API.</p></li></ul><p>Cloudflare has grown tremendously since this pipeline was originally designed in 2014. It started off processing under 1M requests per second and grew to current levels of 6M requests per second. The pipeline had served us and our customers well over the years, but began to split at the seams. Any system should be re-engineered after some time, when requirements change.</p><p>Some specific disadvantages of the original pipeline were:</p><ul><li><p><b>Postgres SPOF</b> - single PostgreSQL instance was a SPOF (Single Point of Failure), as it didn't have replicas or backups and if we were to lose this node, whole analytics pipeline could be paralyzed and produce no new aggregates for Zone Analytics API.</p></li><li><p><b>Citus main SPOF</b> - Citus main was the entrypoint to all Zone Analytics API queries and if it went down, all our customers' Analytics API queries would return errors.</p></li><li><p><b>Complex codebase</b> - thousands of lines of bash and SQL for aggregations, and thousands of lines of Go for API and Kafka consumers made the pipeline difficult to maintain and debug.</p></li><li><p><b>Many dependencies</b> - the pipeline consisted of many components, and failure in any individual component could result in halting the entire pipeline.</p></li><li><p><b>High maintenance cost</b> - due to its complex architecture and codebase, there were frequent incidents, which sometimes took engineers from the Data team and other teams many hours to mitigate.</p></li></ul><p>Over time, as our request volume grew, the challenges of operating this pipeline became more apparent, and we realized that this system was being pushed to its limits. This realization inspired us to think about which components would be ideal candidates for replacement, and led us to build new data pipeline.</p><p>Our first design of an improved analytics pipeline centred around the use of the <a href="https://flink.apache.org/">Apache Flink</a> stream processing system. We had previously used Flink for other data pipelines, so it was a natural choice for us. However, these pipelines had been at a much lower rate than the 6M requests per second we needed to process for HTTP Analytics, and we struggled to get Flink to scale to this volume - it just couldn't keep up with ingestion rate per partition on all 6M HTTP requests per second.</p><p>Our colleagues on our DNS team had already built and productionized DNS analytics pipeline atop ClickHouse. They wrote about it in <a href="https://blog.cloudflare.com/how-cloudflare-analyzes-1m-dns-queries-per-second/">"How Cloudflare analyzes 1M DNS queries per second"</a> blog post. So, we decided to take a deeper look at ClickHouse.</p>
    <div>
      <h3>ClickHouse</h3>
      <a href="#clickhouse">
        
      </a>
    </div>
    <blockquote><p>"ClickHouse не тормозит."
Translation from Russian: ClickHouse doesn't have brakes (or isn't slow)
© ClickHouse core developers</p></blockquote><p>When exploring additional candidates for replacing some of the key infrastructure of our old pipeline, we realized that using a column oriented database might be well suited to our analytics workloads. We wanted to identify a column oriented database that was horizontally scalable and fault tolerant to help us deliver good uptime guarantees, and extremely performant and space efficient such that it could handle our scale. We quickly realized that ClickHouse could satisfy these criteria, and then some.</p><p><a href="https://clickhouse.yandex/">ClickHouse</a> is an open source column-oriented database management system capable of real time generation of analytical data reports using SQL queries. It is blazing fast, linearly scalable, hardware efficient, fault tolerant, feature rich, highly reliable, simple and handy. ClickHouse core developers provide great help on solving issues, merging and maintaining our PRs into ClickHouse. For example, engineers from Cloudflare have contributed a whole bunch of code back upstream:</p><ul><li><p>Aggregate function <a href="https://clickhouse.com/docs/en/sql-reference/aggregate-functions/reference/topk">topK</a> by <a href="https://github.com/vavrusa">Marek Vavruša</a></p></li><li><p>IP prefix dictionary by Marek Vavruša</p></li><li><p>SummingMergeTree engine optimizations by Marek Vavruša</p></li><li><p><a href="https://clickhouse.com/docs/en/engines/table-engines/integrations/kafka">Kafka table Engine</a> by Marek Vavruša. We're thinking to replace Kafka Go consumers with this engine when it will be stable enough and ingest from Kafka into ClickHouse directly.</p></li><li><p>Aggregate function <a href="https://clickhouse.yandex/docs/en/single/index.html#summapkey-value">sumMap</a> by <a href="https://github.com/bocharov">Alex Bocharov</a>. Without this function it would be impossible to build our new Zone Analytics API.</p></li><li><p><a href="https://github.com/yandex/ClickHouse/pull/1636">Mark cache fix</a> by Alex Bocharov</p></li><li><p><a href="https://github.com/yandex/ClickHouse/pull/1844">uniqHLL12 function fix</a> for big cardinalities by Alex Bocharov. The description of the issue and its fix should be an interesting reading.</p></li></ul>
          <figure>
          <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/6mN4cq6YrpiHjBfbP6vpCh/7181a8e68b4a63cd48e42e0eaf807191/ClickHouse-uniq-functions.png" />
          </figure><p>Along with filing many bug reports, we also report about every issue we face in our cluster, which we hope will help to improve ClickHouse in future.</p><p>Even though DNS analytics on ClickHouse had been a great success, we were still skeptical that we would be able to scale ClickHouse to the needs of the HTTP pipeline:</p><ul><li><p>Kafka DNS topic has on average 1.5M messages per second vs 6M messages per second for HTTP requests topic.</p></li><li><p>Kafka DNS topic average uncompressed message size is 130B vs 1630B for HTTP requests topic.</p></li><li><p>DNS query ClickHouse record consists of 40 columns vs 104 columns for HTTP request ClickHouse record.</p></li></ul><p>After unsuccessful attempts with Flink, we were skeptical of ClickHouse being able to keep up with the high ingestion rate. Luckily, early prototype showed promising performance and we decided to proceed with old pipeline replacement. The first step in replacing the old pipeline was to design a schema for the new ClickHouse tables.</p>
    <div>
      <h3>ClickHouse schema design</h3>
      <a href="#clickhouse-schema-design">
        
      </a>
    </div>
    <p>Once we identified ClickHouse as a potential candidate, we began exploring how we could port our existing Postgres/Citus schemas to make them compatible with ClickHouse.</p><p>For our <a href="https://api.cloudflare.com/#zone-analytics-dashboard">Zone Analytics API</a> we need to produce many different aggregations for each zone (domain) and time period (minutely / hourly / daily / monthly). For deeper dive about specifics of aggregates please follow Zone Analytics API documentation or this handy <a href="https://docs.google.com/spreadsheets/d/1zQ3yI3HB2p8hiM-Jwvq1-MaeEyIouix2I-iUAPZtJYw/edit#gid=1788221216">spreadsheet</a>.</p><p>These aggregations should be available for any time range for the last 365 days. While ClickHouse is a really great tool to work with non-aggregated data, with our volume of 6M requests per second we just cannot afford yet to store non-aggregated data for that long.</p><p>To give you an idea of how much data is that, here is some "napkin-math" capacity planning. I'm going to use an average insertion rate of 6M requests per second and $100 as a cost estimate of 1 TiB to calculate storage cost for 1 year in different message formats:</p><table><tr><th><p><b>Metric</b></p></th><th><p><b>Cap'n Proto</b></p></th><th><p><b>Cap'n Proto (zstd)</b></p></th><th><p><b>ClickHouse</b></p></th></tr><tr><td><p>Avg message/record size</p></td><td><p>1630 B</p></td><td><p>360 B</p></td><td><p>36.74 B</p></td></tr><tr><td><p>Storage requirements for 1 year</p></td><td><p>273.93 PiB</p></td><td><p>60.5 PiB</p></td><td><p>18.52 PiB (RF x3)</p></td></tr><tr><td><p>Storage cost for 1 year</p></td><td><p>$28M</p></td><td><p>$6.2M</p></td><td><p>$1.9M</p></td></tr></table><p>And that is if we assume that requests per second will stay the same, but in fact it's growing fast all the time.</p><p>Even though storage requirements are quite scary, we're still considering to store raw (non-aggregated) requests logs in ClickHouse for 1 month+. See "Future of Data APIs" section below.</p>
    <div>
      <h4>Non-aggregated requests table</h4>
      <a href="#non-aggregated-requests-table">
        
      </a>
    </div>
    <p>We store over <a href="https://docs.google.com/spreadsheets/d/1zQ3yI3HB2p8hiM-Jwvq1-MaeEyIouix2I-iUAPZtJYw/edit?usp=sharing">100+ columns</a>, collecting lots of different kinds of metrics about each request passed through Cloudflare. Some of these columns are also available in our <a href="https://support.cloudflare.com/hc/en-us/articles/216672448-Enterprise-Log-Share-Logpull-REST-API">Enterprise Log Share</a> product, however ClickHouse non-aggregated requests table has more fields.</p><p>With so many columns to store and huge storage requirements we've decided to proceed with the aggregated-data approach, which worked well for us before in old pipeline and which will provide us with backward compatibility.</p>
    <div>
      <h4>Aggregations schema design #1</h4>
      <a href="#aggregations-schema-design-1">
        
      </a>
    </div>
    <p>According to the <a href="https://api.cloudflare.com/#zone-analytics-dashboard">API documentation</a>, we need to provide lots of different requests breakdowns and to satisfy these requirements we decided to test the following approach:</p><ol><li><p>Create Cickhouse <a href="https://clickhouse.com/docs/en/sql-reference/statements/create/view">materialized views</a> with <a href="https://clickhouse.com/docs/en/engines/table-engines/mergetree-family/aggregatingmergetree">ReplicatedAggregatingMergeTree</a> engine pointing to non-aggregated requests table and containing minutely aggregates data for each of the breakdowns:</p><ul><li><p><b>Requests totals</b> - containing numbers like total requests, bytes, threats, uniques, etc.</p></li><li><p><b>Requests by colo</b> - containing requests, bytes, etc. breakdown by edgeColoId - each of 120+ Cloudflare datacenters</p></li><li><p><b>Requests by http status</b> - containing breakdown by HTTP status code, e.g. 200, 404, 500, etc.</p></li><li><p><b>Requests by content type</b> - containing breakdown by response content type, e.g. HTML, JS, CSS, etc.</p></li><li><p><b>Requests by country</b> - containing breakdown by client country (based on IP)</p></li><li><p><b>Requests by threat type</b> - containing breakdown by threat type</p></li><li><p><b>Requests by browser</b> - containing breakdown by browser family, extracted from user agent</p></li><li><p><b>Requests by ip class</b> - containing breakdown by client IP class</p></li></ul></li><li><p>Write the code gathering data from all 8 materialized views, using two approaches:</p><ul><li><p>Querying all 8 materialized views at once using JOIN</p></li><li><p>Querying each one of 8 materialized views separately in parallel</p></li></ul></li><li><p>Run performance testing benchmark against common Zone Analytics API queries</p></li></ol>
          <figure>
          <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/3iQvOQSik5xLDR7lkG0X9H/ee505f826947f6844c3e001bb83c7e7b/Schema-design--1-1.jpg" />
          </figure><p>Schema design #1 didn't work out well. ClickHouse JOIN syntax forces to write monstrous query over 300 lines of SQL, repeating the selected columns many times because you can do only <a href="https://github.com/yandex/ClickHouse/issues/873">pairwise joins</a> in ClickHouse.</p><p>As for querying each of materialized views separately in parallel, benchmark showed prominent, but moderate results - query throughput would be a little bit better than using our Citus based old pipeline.</p>
    <div>
      <h4>Aggregations schema design #2</h4>
      <a href="#aggregations-schema-design-2">
        
      </a>
    </div>
    <p>In our second iteration of the schema design, we strove to keep a similar structure to our existing Citus tables. To do this, we experimented with the SummingMergeTree engine, which is described in detail by the excellent ClickHouse <a href="https://clickhouse.com/docs/en/engines/table-engines/mergetree-family/summingmergetree">documentation</a>:</p><blockquote><p>In addition, a table can have nested data structures that are processed in a special way. If the name of a nested table ends in 'Map' and it contains at least two columns that meet the following criteria... then this nested table is interpreted as a mapping of key =&gt; (values...), and when merging its rows, the elements of two data sets are merged by 'key' with a summation of the corresponding (values...).</p></blockquote><p>We were pleased to find this feature, because the SummingMergeTree engine allowed us to significantly reduce the number of tables required as compared to our initial approach. At the same time, it allowed us to match the structure of our existing Citus tables. The reason was that the ClickHouse Nested structure ending in 'Map' was similar to the <a href="https://www.postgresql.org/docs/9.6/static/hstore.html">Postgres hstore</a> data type, which we used extensively in the old pipeline.</p><p>However, there were two existing issues with ClickHouse maps:</p><ul><li><p>SummingMergeTree does aggregation for all records with same primary key, but final aggregation across all shards should be done using some aggregate function, which didn't exist in ClickHouse.</p></li><li><p>For storing uniques (uniques visitors based on IP), we need to use AggregateFunction data type, and although SummingMergeTree allows you to create column with such data type, it will not perform aggregation on it for records with same primary keys.</p></li></ul><p>To resolve problem #1, we had to create a new aggregation function <a href="https://clickhouse.yandex/docs/en/single/index.html#summapkey-value">sumMap</a>. Luckily, ClickHouse source code is of excellent quality and its core developers are very helpful with reviewing and merging requested changes.</p><p>As for problem #2, we had to put uniques into separate materialized view, which uses the ReplicatedAggregatingMergeTree Engine and supports merge of AggregateFunction states for records with the same primary keys. We're considering adding the same functionality into SummingMergeTree, so it will simplify our schema even more.</p><p>We also created a separate materialized view for the Colo endpoint because it has much lower usage (5% for Colo endpoint queries, 95% for Zone dashboard queries), so its more dispersed primary key will not affect performance of Zone dashboard queries.</p>
          <figure>
          <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/2ZmeMrcqIgAI99UJgfx1HK/80923c6f467d95a1bd8d74e838f91d99/Schema-design--2.jpg" />
          </figure><p>Once schema design was acceptable, we proceeded to performance testing.</p>
    <div>
      <h3>ClickHouse performance tuning</h3>
      <a href="#clickhouse-performance-tuning">
        
      </a>
    </div>
    <p>We explored a number of avenues for performance improvement in ClickHouse. These included tuning index granularity, and improving the merge performance of the SummingMergeTree engine.</p><p>By default ClickHouse recommends to use 8192 index granularity. There is <a href="https://medium.com/@f1yegor/clickhouse-primary-keys-2cf2a45d7324">nice article</a> explaining ClickHouse primary keys and index granularity in depth.</p><p>While default index granularity might be excellent choice for most of use cases, in our case we decided to choose the following index granularities:</p><ul><li><p>For the main non-aggregated requests table we chose an index granularity of 16384. For this table, the number of rows read in a query is typically on the order of millions to billions. In this case, a large index granularity does not make a huge difference on query performance.</p></li><li><p>For the aggregated requests_* stables, we chose an index granularity of 32. A low index granularity makes sense when we only need to scan and return a few rows. It made a huge difference in API performance - query latency decreased by 50% and throughput increased by ~3 times when we changed index granularity 8192 → 32.</p></li></ul><p>Not relevant to performance, but we also disabled the min_execution_speed setting, so queries scanning just a few rows won't return exception because of "slow speed" of scanning rows per second.</p><p>On the aggregation/merge side, we've made some ClickHouse optimizations as well, like <a href="https://github.com/yandex/ClickHouse/pull/1330">increasing SummingMergeTree maps merge speed</a> by x7 times, which we contributed back into ClickHouse for everyone's benefit.</p><p>Once we had completed the performance tuning for ClickHouse, we could bring it all together into a new data pipeline. Next, we describe the architecture for our new, ClickHouse-based data pipeline.</p>
    <div>
      <h3>New data pipeline</h3>
      <a href="#new-data-pipeline">
        
      </a>
    </div>
    <p>The new pipeline architecture re-uses some of the components from old pipeline, however it replaces its most weak components.</p>
          <figure>
          <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/2UaoX6fPvFuhN1ecJpbe6v/8aee3bd1ab395cb4a4ac2b8db9575a23/New-system-architecture.jpg" />
          </figure><p>New components include:</p><ul><li><p><b>Kafka consumers </b>- 106 Go consumers per each partition consume Cap'n Proto raw logs and extract/prepare needed 100+ ClickHouse fields. Consumers no longer do any aggregation logic.</p></li><li><p><b>ClickHouse cluster</b> - 36 nodes with x3 replication factor. It handles non-aggregate requests logs ingestion and then produces aggregates using materialized views.</p></li><li><p><b>Zone Analytics API</b> - rewritten and optimized version of API in Go, with many meaningful metrics, healthchecks, failover scenarios.</p></li></ul><p>As you can see the architecture of new pipeline is much simpler and fault-tolerant. It provides Analytics for all our 7M+ customers' domains totalling more than 2.5 billion monthly unique visitors and over 1.5 trillion monthly page views.</p><p>On average we process 6M HTTP requests per second, with peaks of upto 8M requests per second.</p>
          <figure>
          <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/zjubA3CgetykRE8Angxln/7509dd5dea82ad7408339a5f78561b41/HTTP-Logfwdr-throughput.png" />
          </figure><p>Average log message size in <a href="https://capnproto.org/">Cap’n Proto</a> format used to be ~1630B, but thanks to amazing job on Kafka compression by our Platform Operations Team, it decreased significantly. Please see <a href="https://blog.cloudflare.com/squeezing-the-firehose/">"Squeezing the firehose: getting the most from Kafka compression"</a> blog post with deeper dive into those optimisations.</p>
    <div>
      <h4>Benefits of new pipeline</h4>
      <a href="#benefits-of-new-pipeline">
        
      </a>
    </div>
    <ul><li><p><b>No SPOF</b> - removed all SPOFs and bottlenecks, everything has at least x3 replication factor.</p></li><li><p><b>Fault-tolerant</b> - it's more fault-tolerant, even if Kafka consumer or ClickHouse node or Zone Analytics API instance fails, it doesn't impact the service.</p></li><li><p><b>Scalable</b> - we can add more Kafka brokers or ClickHouse nodes and scale ingestion as we grow. We are not so confident about query performance when cluster will grow to hundreds of nodes. However, Yandex team managed to scale their cluster to 500+ nodes, distributed geographically between several data centers, using two-level sharding.</p></li><li><p><b>Reduced complexity</b> - due to removing messy crons and consumers which were doing aggregations and <a href="https://www.cloudflare.com/learning/cloud/how-to-refactor-applications/">refactoring</a> API code we were able to:</p><ul><li><p>Shutdown Postgres RollupDB instance and free it up for reuse.</p></li><li><p>Shutdown Citus cluster 12 nodes and free it up for reuse. As we won't use Citus for serious workload anymore we can reduce our operational and support costs.</p></li><li><p>Delete tens of thousands of lines of old Go, SQL, Bash, and PHP code.</p></li><li><p>Remove WWW PHP API dependency and extra latency.</p></li></ul></li><li><p><b>Improved API throughput and latency </b>- with previous pipeline Zone Analytics API was struggling to serve more than 15 queries per second, so we had to introduce temporary hard rate limits for largest users. With new pipeline we were able to remove hard rate limits and now we are serving ~40 queries per second. We went further and did intensive load testing for new API and with current setup and hardware we are able serve up to ~150 queries per second and this is scalable with additional nodes.
</p>
          <figure>
          <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/1TCHFdGAndAank2N3agHQj/5db6811ef0918e8fc66b8543531f9733/Zone-Analytics-API-requests-latency-quantiles.png" />
          </figure><p></p></li><li><p><b>Easier to operate</b> - with shutdown of many unreliable components, we are finally at the point where it's relatively easy to operate this pipeline. ClickHouse quality helps us a lot in this matter.</p></li><li><p><b>Decreased amount of incidents</b> - with new more reliable pipeline, we now have fewer incidents than before, which ultimately has reduced on-call burden. Finally, we can sleep peacefully at night :-).</p></li></ul><p>Recently, we've improved the throughput and latency of the new pipeline even further with better hardware. I'll provide details about this cluster below.</p>
    <div>
      <h4>Our ClickHouse cluster</h4>
      <a href="#our-clickhouse-cluster">
        
      </a>
    </div>
    <p>In total we have 36 ClickHouse nodes. The new hardware is a big upgrade for us:</p><ul><li><p><b>Chassis</b> - Quanta D51PH-1ULH chassis instead of Quanta D51B-2U chassis (2x less physical space)</p></li><li><p><b>CPU</b> - 40 logical cores E5-2630 v3 @ 2.40 GHz instead of 32 cores E5-2630 v4 @ 2.20 GHz</p></li><li><p><b>RAM</b> - 256 GB RAM instead of 128 GB RAM</p></li><li><p><b>Disks</b> - 12 x 10 TB Seagate ST10000NM0016-1TT101 disks instead of 12 x 6 TB Toshiba TOSHIBA MG04ACA600E</p></li><li><p><b>Network</b> - 2 x 25G Mellanox ConnectX-4 in MC-LAG instead of 2 x 10G Intel 82599ES</p></li></ul><p>Our Platform Operations team noticed that ClickHouse is not great at running heterogeneous clusters yet, so we need to gradually replace all nodes in the existing cluster with new hardware, all 36 of them. The process is fairly straightforward, it's no different than replacing a failed node. The problem is that <a href="https://github.com/yandex/ClickHouse/issues/1821">ClickHouse doesn't throttle recovery</a>.</p><p>Here is more information about our cluster:</p><ul><li><p><b>Avg insertion rate</b> - all our pipelines bring together 11M rows per second.</p></li><li><p><b>Avg insertion bandwidth</b> - 47 Gbps.</p></li><li><p><b>Avg queries per second</b> - on average cluster serves ~40 queries per second with frequent peaks up to ~80 queries per second.</p></li><li><p><b>CPU time</b> - after recent hardware upgrade and all optimizations, our cluster CPU time is quite low.
</p>
          <figure>
          <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/61sIThxxM4s9mgA8nQSibn/4e09df1a744f0c1e2cd92b8bf5bfdd5f/ClickHouse-CPU-usage.png" />
          </figure><p></p></li><li><p><b>Max disk IO</b> (device time) - it's low as well.
</p>
          <figure>
          <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/1rNrJvGvXd6VNvPRH2qe0l/f7813d61e834b420515047062e25e791/Max-disk-IO.png" />
          </figure><p></p><p></p></li></ul><p>In order to make the switch to the new pipeline as seamless as possible, we performed a transfer of historical data from the old pipeline. Next, I discuss the process of this data transfer.</p>
    <div>
      <h4>Historical data transfer</h4>
      <a href="#historical-data-transfer">
        
      </a>
    </div>
    <p>As we have 1 year storage requirements, we had to do one-time ETL (Extract Transfer Load) from the old Citus cluster into ClickHouse.</p><p>At Cloudflare we love Go and its goroutines, so it was quite straightforward to write a simple ETL job, which:</p><ul><li><p>For each minute/hour/day/month extracts data from Citus cluster</p></li><li><p>Transforms Citus data into ClickHouse format and applies needed business logic</p></li><li><p>Loads data into ClickHouse</p></li></ul><p>The whole process took couple of days and over 60+ billions rows of data were transferred successfully with consistency checks. The completion of this process finally led to the shutdown of old pipeline. However, our work does not end there, and we are constantly looking to the future. In the next section, I'll share some details about what we are planning.</p>
    <div>
      <h3>Future of Data APIs</h3>
      <a href="#future-of-data-apis">
        
      </a>
    </div>
    
    <div>
      <h4>Log Push</h4>
      <a href="#log-push">
        
      </a>
    </div>
    <p>We're currently working on something called "Log Push". Log push allows you to specify a desired data endpoint and have your HTTP request logs sent there automatically at regular intervals. At the moment, it's in private beta and going to support sending logs to:</p><ul><li><p>Amazon S3 bucket</p></li><li><p>Google Cloud Service bucket</p></li><li><p>Other storage services and platforms</p></li></ul><p>It's expected to be generally available soon, but if you are interested in this new product and you want to try it out please contact our Customer Support team.</p>
    <div>
      <h4>Logs SQL API</h4>
      <a href="#logs-sql-api">
        
      </a>
    </div>
    <p>We're also evaluating possibility of building new product called Logs SQL API. The idea is to provide customers access to their logs via flexible API which supports standard SQL syntax and JSON/CSV/TSV/XML format response.</p><p>Queries can extract:</p><ul><li><p><b>Raw requests logs fields</b> (e.g. SELECT field1, field2, ... FROM requests WHERE ...)</p></li><li><p><b>Aggregated data from requests logs</b> (e.g. SELECT clientIPv4, count() FROM requests GROUP BY clientIPv4 ORDER BY count() DESC LIMIT 10)</p></li></ul><p>Google BigQuery provides similar <a href="https://cloud.google.com/bigquery/docs/reference/rest/v2/jobs/query">SQL API</a> and Amazon has product callled <a href="https://docs.aws.amazon.com/kinesisanalytics/latest/sqlref/analytics-sql-reference.html">Kinesis Data analytics</a> with SQL API support as well.</p><p>Another option we're exploring is to provide syntax similar to <a href="https://api.cloudflare.com/#dns-analytics-properties">DNS Analytics API</a> with filters and dimensions.</p><p>We're excited to hear your feedback and know more about your analytics use case. It can help us a lot to build new products!</p>
    <div>
      <h3>Conclusion</h3>
      <a href="#conclusion">
        
      </a>
    </div>
    <p>All this could not be possible without hard work across multiple teams! First of all thanks to other Data team engineers for their tremendous efforts to make this all happen. Platform Operations Team made significant contributions to this project, especially Ivan Babrou and Daniel Dao. Contributions from Marek Vavruša in DNS Team were also very helpful.</p><p>Finally, Data team at Cloudflare is a small team, so if you're interested in building and operating distributed services, you stand to have some great problems to work on. Check out the <a href="https://boards.greenhouse.io/cloudflare/jobs/613800">Distributed Systems Engineer - Data</a> and <a href="https://boards.greenhouse.io/cloudflare/jobs/688056">Data Infrastructure Engineer</a> roles in London, UK and San Francisco, US, and let us know what you think.</p> ]]></content:encoded>
            <category><![CDATA[Analytics]]></category>
            <category><![CDATA[Data]]></category>
            <category><![CDATA[Speed & Reliability]]></category>
            <category><![CDATA[Kafka]]></category>
            <category><![CDATA[Cap'n Proto]]></category>
            <category><![CDATA[API]]></category>
            <category><![CDATA[php]]></category>
            <category><![CDATA[Load Balancing]]></category>
            <category><![CDATA[NGINX]]></category>
            <guid isPermaLink="false">6VEE3i8wXN2CDKWJJ16uXS</guid>
            <dc:creator>Alex Bocharov</dc:creator>
        </item>
        <item>
            <title><![CDATA[Will Data Destroy Democracy?]]></title>
            <link>https://blog.cloudflare.com/will-data-destroy-democracy/</link>
            <pubDate>Fri, 15 Sep 2017 00:03:00 GMT</pubDate>
            <description><![CDATA[ Lawrence Lessig, Roy L. Furman Professor of Law and Leadership, Harvard Law School and Darren Bolding, CTO, Cambridge Analytica

Moderator: Matthew Prince, Co-Founder & CEO, Cloudflare

 ]]></description>
            <content:encoded><![CDATA[ <p><a href="https://twitter.com/lessig">Lawrence Lessig</a>, Roy L. Furman Professor of Law and Leadership, Harvard Law School and <a href="https://twitter.com/darrenbolding">Darren Bolding</a>, CTO, Cambridge Analytica</p><p>Moderator: <a href="https://twitter.com/eastdakota">Matthew Prince</a>, Co-Founder &amp; CEO, Cloudflare</p><p>MP: If there’s one person responsible for the Trump presidency, it seems there is a compelling argument that that might be you.</p><p>DB: I very much disagree with that.</p><p>MP: How does Cambridge Analytica work, and how did the Trump campaign use it to win the presidency?</p><p>DB: we take that data and match it up with lists of voters, and combine that data science to come up with ideas about you who might want to sell a product to, or in the case of politics, this is this person's’ propensity to vote, this is the candidate they are likely most interested in. WE also do all the digital advertising. By combining data with digital advertising, we have lots of power.</p><p>MP: so you don’t want to take credit for having won the election; but the campaign's use of data and targeting was an important factor in the election.</p><p>DB: Yes, and what Cambridge did was basically a great turnaround story.</p><p>MP: Lawrence you ran a presidential campaign focused on one issue; finance reform. Yet the candidate that spent half as much as Hillary Clinton won. Is finance still the issue or do we need to start thinking about data as the divider.</p><p>LL: My slogan was not “fix campaign finance” but “fix democracy first”. This means to fix all the different ways the system denies us a democracy in the sense that we are equal citizens. If you have a congress spending 30-70% of their time raising money, or gerrymandering, that is not a congress concerned with representing its citizens. This is not a system that produces citizenship driven to electing a president.</p><p>Our electoral college means that the vote of republicans here in California is worth nothing. These are all the ways in which we have a failed democracy.</p><p>I wanted to at least have a voice in the debate to rally around these issues.</p><p>What happened is the democratic party changed the rules just as i qualified to be on that stage. But i would suggest that the man who won took the same set of slogans - Drain the Swamp - and ran as full-force as he could and targeted as his opponent a woman who was “sold out” to these interests precisely.</p><p>I think it is the fundamental issue.</p><p>MP: One of the core tenets of democracy seems like a shared understanding.</p><p>If you have 15 different targeted messages, does that corrode the shared understanding?</p><p>LL: The truth is, in the half of DB’s world focused on commerce, it’s the best of all possible times. The half of the architecture of communication focused on giving people access to netflix, it’s the best of all possible times. We have to recognize that the internet is the best and worst of all possible times at the same time.</p><p>So when you shift to democracy, the same technologies undermine our ability to do democracy the way we did before.It used to be that the process of winning election was same as building coalition.It was in front, in plain sight, and when you won, you knew why.</p><p>When you have technology like Cambridge Analytica has perfected, the process of winning election is totally separate from governing.</p><p>MP: So Darren are you destroying democracy?</p><p>DB: The act of democracy is allowing people to choose who their representatives are. That doesn’t imply that everyone has to have the same shared context. I think it’s possibly beneficial that people with disparate points of view / interests they should have those interests addressed.</p><p>MP: But you work for a company that says they have a unique tech to do this better. What is it about the tech that makes it so much more better that doesn’t corrode shared understanding, on the other side?</p><p>DB: The shared understanding is out there is almost more cultural than anything. I think that having a conversation with you about the regulations that Germany might impose doesn’t permit you from knowing about other aspects of foreign policy with Germany; it’s just a specific thing you care about. Now if the messages are contradictory, that’s when it becomes a problem. But as long as people are maintaining consistent points of view, it’s not wrong to communicate about issues that are important to a specific set of persons.</p><p>LL: I wouldn't say that CA produced diffuse culture where there is no shared understanding. But what we don’t recognize enough is how extraordinary 1960s and 70s were for democracy, when everybody was focused on three television shows every night. America basically understood the same stuff.</p><p>MP: Former chair of FCC says that maybe this is actually the natural state today; in the 60s and 70s, 3 companies controlled profitable technology and spent more time being neutral and elevating conversation. Is this time period what we should be striving for or is that a reaction to fear of regulation?</p><p>LL: I agree this was extraordinary period. It defined how we understand democracy, and that period is gone.</p><p>That period is gone. I don’t want to return to it. Those three shows were too narrow in a number of ways. My point is that we don't yet have a good model for how to work a democracy where we all live in our own niche worlds of the basic facts.</p><p>The architecture of media today is just like the architecture of media in the 19th century.</p><p>Most journalism was partisan, all about rallying troops to own version of truth. The difference is that we have no way of knowing what the public thought.</p><p>The difference is that we have no way of knowing what the public thought then. We could only know what the politicians thought. We didn’t even have polling.</p><p>MP: But back then, you also had a particular understanding of what you were reading; today, FB has an algorithm, there is an editorial voice, and you don’t know what that is. There is some neutrality.</p><p>LL: Back then, media drove people to vote in a certain way or not. But today, the views of people about whether we should go to war in Iraq or whether immigrants deserve to be blocked, the views of the people matter directly.</p><p>Supposed to have a representative democracy, but we increasingly have a direct democracy composed for a public that doesn’t know anything about issues because we live in niche market bubble worlds that don’t inform us the way our broader world has in the past.</p><p>DB: data science is part of the solution. I can use a tool on FB to tell me what percentage of my wall is democrat or republican.</p><p>MP: So that’s the argument that we are only just getting used to tech. We will get better at being able to interpret these things and see through them.</p><p>DB: These tools also make it easier for smaller groups to get their povs out there into the general market. It costs less to get their message out there. You couldn't do that before because all the power was in a small number of hands. SoData science available to anybody through FB is actually quite powerful.</p><p>I for one thing that if you are accurately representing what the populace is interested in, that is not a bad thing for democracy; that’s a good thing.</p><p>If the public is fractured, that’s what the public deserves.</p><p>LL: As a kid, as a republican, I was celebrating the internet, I was saying exactly what DB was, but we didn't think enough about the ways it would change the context in which we could have the conversation.</p><p>We have never had the ability of someone to speak to 30 million people without an editor standing between. This is new. But now a guy can tweet, and it is seen by 30 million people, and we don’t yet know how to run a democracy with that dynamic.</p><p>I hitchhiked across the soviet union when i was young. And was told that in the soviet union they have a better system of free speech than you do in America. We wake up and realize that every newspaper is lying to us; so we have to read 7-8 different newspapers before we understand the truth. This develops a better culture of critical understanding than you have in US.</p><p>We have become the soviet union; our parents don’t yet know how to deal with a world in which everyone is lying to you.</p><p>But our kids know, and can figure it out based on 7or 8 different feeds.</p><p>MP: So is the solution time? Over time, If Cambridge Analytica won the election, what is the next trick? Who will win the next one?</p><p>DB: I think personalization of information that will allow individuals to better communicate with people they know. Rather than have one person broadcasting, you’ll have personal relationship.The dispersion of the central control over the message out to individuals is very powerful. Now instead of Donald Trump talking at you, you have someone else...</p><p>MP: It’s a way to trick the kids, then, isn’t it? If your friends are telling you something, that’s how you get the cynics.</p><p>LL: it’s certainly a wonderful development; but the problem is that if they’re doing that on the basis of a totally different understanding of the world. Some people think climate changes real; others false . If there’s no common gorond of understanding, that may be good for winning elections, but not for actually governing.</p><p>DB: you’re building a virtual community in each “town,” and each community is discussing what is important to them.</p><p>MP: I was just talking to an engineer in China, who said that democracy is great but it always drops below its lowest common denominator. How do we fix that if that’s the case?</p><p>DB: Our original founders wrestled with that idea; we have to keep trying.</p><p><b>Q&amp;A:</b></p><p>Q: Does Cambridge Analytical make problems like Willy Horton worse or better?</p><p>DB: I don’t think it plays that much of a role one way or another. Your context is the ads that played during the Bush campaign?</p><p>I think it just makes the message more amplified.</p><p>LL: Here we have a real disagreement. You have an assumption that people can’t be inconsistent in how they represent their world view. If we have a technology that perfects ability to elect people, but not through public conversation, that encourages this dramatic</p><p>DB: as long as the campaign is consistent and does not change its point of view…</p><p>LL: when have we ever seen that?</p><p>Q: Where do you draw the line on ethical microtargeting? Are you creating models to target people on the basis of racial messaging?</p><p>DB: I don’t think Cambridge pushed any racially charged messages--</p><p>MP: do you identify people… do you have a category that is racists?</p><p>DB: we had 15 models. It never even came up.</p><p>MP: how do we set a framework or a social contract so that Cambridge Analytica doesn’t have a racist profile?</p><p>LL: Today story that broke about ProPublica and FB basically had an anti-semitic ad category to market to people who hate Jews, and had used algorithms inside of FB to target anti-semites.</p><p>Mark Zuckerberg is interested in finding what people want and catering to it; and that’s fine. In 99% of what we care about, that’s what we want. But in democracy, that’s a terrifying possibility.</p><p>Q: People make decisions based on knowledge &amp; information they consume. We are now talking about driving mass behavior, which is different from just giving people what they want.</p><p>How can data science be used responsibly? What regulations do we need when social networks are driving mass behavior? If it’s not regulation, what other structures do we need?</p><p>DB: If you look at the EU, they have the GDPR, and there’s a control over how much information is available. People being aware of how much information they have to give up is going to be somewhat helpful. If you know what information you are giving up, you know what you are able to be targeted on. There will also need to be some sort of code of ethics about what is right and what is wrong to do with data. I am inherently not a fan of regulation. When you have that, entrenched players will create regulatory capture which will stifle innovation.</p><p>There should be some sort of element there. “Algorithms will find the worst in us if you let them go nuts.” And this is not all happening on one side of the spectrum.</p><p>LL: It’s fun and hopeful to talk about codes of ethics stifling the worst, but if the worst is profitable, the code of ethics will be eaten by the profit.</p><p>In one of Steven Bannon’s last interviews, he said, “What we want is the democrats to talk about identity politics every single day until the next election, and we’re going to talk about economic policy and we will beat them.” And you begin to realize that racism is just them playing the democrats. In our world of 2-second attention spans, what do you do to resist that?</p><p>All our sessions will be streamed live! If you can't make it to Summit, here's the link: <a href="http://www.cloudflare.com/summit17">cloudflare.com/summit17</a></p> ]]></content:encoded>
            <category><![CDATA[Internet Summit]]></category>
            <category><![CDATA[Policy & Legal]]></category>
            <category><![CDATA[Data]]></category>
            <guid isPermaLink="false">6uLlpwtoco9u5HL3pRsfI8</guid>
            <dc:creator>Internet Summit Team</dc:creator>
        </item>
        <item>
            <title><![CDATA[Data Transfers Post-Brexit: Smoothing the Transition]]></title>
            <link>https://blog.cloudflare.com/let-it-flow-let-it-flow-2/</link>
            <pubDate>Thu, 13 Apr 2017 13:06:47 GMT</pubDate>
            <description><![CDATA[ The average internet user consumes vast amounts of data on a daily basis but rarely – unless an avid follower of Max Schrems - thinks about how the data flows or the mechanisms and legal arrangements in place to make it all happen. ]]></description>
            <content:encoded><![CDATA[ <p>The average internet user consumes vast amounts of data on a daily basis but rarely – unless an avid follower of <a href="https://en.wikipedia.org/wiki/Max_Schrems">Max Schrems</a> - thinks about <i>how</i> the data flows or the mechanisms and legal arrangements in place to make it all happen. If companies like Cloudflare are doing their job well behind the scenes, you really shouldn’t have to – it just <i>works</i>, and so you can busy yourself emailing, communicating, transacting and sharing information.</p><p>Users benefit enormously from the free movement of data, and it is a highly regarded feature of living and doing business within the European Union. With the appropriate legal protections in place, scientific and societal benefits also flow along with the data, and the quality of our lives is improved immensely.</p><p>And the internet is an increasingly busy place:</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/0d4xWfDrYTaIukcOb80I4/48f01740d6fe9db086ce56231891f146/IMG_5925-1.JPG.jpeg" />
            
            </figure><p><i>Image courtesy of </i><a href="https://twitter.com/lorilewis"><i>@LoriLewis</i></a><i> and </i><a href="https://twitter.com/OfficiallyChadd"><i>@OfficiallyChadd</i></a></p>
    <div>
      <h3>Let it flow, let it flow...</h3>
      <a href="#let-it-flow-let-it-flow">
        
      </a>
    </div>
    <p>The European Commission reported in a <a href="https://ec.europa.eu/digital-single-market/en/news/communication-building-european-data-economy">communication</a> earlier this year that the European Data Economy – i.e the marketplace where digital data is exchanged as products or services derived from raw data – was estimated at EUR 272 billion in 2015, and that the value is expected to increase to EUR 643 billion by 2020, in large part thanks to ever-increasing amounts of data being generated by emerging technologies, such as the Internet of Things and Artificial Intelligence. Data is certainly big business.</p><p>Assuming no data flow restrictions (such as data localization laws), companies can more readily access performant and secure technologies, enter into new markets, develop new products and services and avail of efficiencies and cost reductions, all of which can be passed on to their customers. This is particularly important for early-stage companies such as Cloudflare, seeking to grow, invest and provide its offering to as many users as possible, and at the lowest price possible.</p><p>Having just <a href="/munich/">announced</a> our 110th data center and with more locations coming soon, our enthusiasm and love for data flows should be obvious. With 6 million+ customers, and 10% of internet (HTTPS) requests flowing through our network each month, we are definitely shifting a lot of information in order to provision our services. And almost one third of our data centers are located in Europe, an exciting and growing marketplace for Cloudflare.</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/3Xiiojjq0xN47e1YQbCk0w/48399dc445c25e7e470558c547369256/image00.png" />
            
            </figure><p>Cloudflare, like most companies, is working hard to ensure full and early implementation of the EU’s new General Data Protection Regulation (GDPR), which will apply to all companies offering goods and services to EU citizens as of May 2018. This is a progressive piece of legislation, which will help bolster user trust, and is perfectly in line with Cloudflare’s long-standing commitment to user privacy, transparency and business accountability. We’ll share further updates on our GDPR plans in due course.</p>
    <div>
      <h3>Importance of maintaining adequacy</h3>
      <a href="#importance-of-maintaining-adequacy">
        
      </a>
    </div>
    <p>Cloudflare’s main European office is located in London and Brexit introduces uncertainty for businesses based in the UK and beyond, which will be worked through as specific challenges arise. However, a particular issue related to data flows and transfers demands the immediate attention of policy makers and legislators.</p><p>According to a recent Frontier Economics <a href="https://www.frontier-economics.com/news/new-frontier-report-techuk-assesses-impact-brexit-uks-digital-sector/">report</a> for TechUK, 75% of the UK’s data transfer activity is with European Union countries. Those transfers, which are considered “domestic” today, quickly become foreign transfers as Brexit is implemented. It is clear that efforts must be made to maintain the stability of data transfers between EU Member States and the UK following the UK’s official departure from the European Union. Data will, in effect, need a new passport in order to travel and be processed on the other side.</p><p>The UK has committed to implement the GDPR in full notwithstanding its withdrawal from the EU, and so will continue to have a robust data protection regime in place. A finding of ‘adequacy’ for the UK by the European Commission – i.e. a legal assessment that the UK’s privacy protection regime is aligned with that of the EU - offers the least burdensome manner of retaining data flows with the EU, and the least friction for business. It is critical that this mechanism is taken seriously and is in place on Brexit Day One, so that businesses can continue to benefit from the seamless flow of data, without jumping through legal hoops and hurdles, and so that users can continue to not even notice the magic at play.</p><p>Cloudflare urges the UK Government to maintain its stated commitment to ensuring unhindered data flows after Brexit, and to work towards a strategy for achieving adequacy during the Brexit negotiations.</p> ]]></content:encoded>
            <category><![CDATA[Data]]></category>
            <category><![CDATA[Policy & Legal]]></category>
            <category><![CDATA[Europe]]></category>
            <category><![CDATA[Privacy]]></category>
            <category><![CDATA[Politics]]></category>
            <guid isPermaLink="false">3oOhwZGbVFmrePh4E2Bz6s</guid>
            <dc:creator>Caroline Greer</dc:creator>
        </item>
        <item>
            <title><![CDATA[More data, more data]]></title>
            <link>https://blog.cloudflare.com/more-data-more-data/</link>
            <pubDate>Tue, 12 Jul 2016 16:29:28 GMT</pubDate>
            <description><![CDATA[ The life of a request to CloudFlare begins and ends at the edge. But the afterlife! Like Catullus to Bithynia, the log generated by an HTTP request or a DNS query has much, much further to go. ]]></description>
            <content:encoded><![CDATA[ <p><i>"multas per gentes et multa per aequora"</i> <a href="#fn1">[1]</a></p><p>The life of a request to CloudFlare begins and ends at the edge. But the afterlife! Like Catullus to Bithynia, the log generated by an HTTP request or a <a href="https://www.cloudflare.com/learning/dns/what-is-dns/">DNS query</a> has much, much further to go.</p><p>This post comes from CloudFlare's Data Team. It reports the state of processing these sort of edge logs, including what's worked well for us and what remains a challenge in the time since our last <a href="/scaling-out-postgresql-for-cloudflare-analytics-using-citusdb/">post from April 2015</a>.</p>
    <div>
      <h3>Numbers, sense</h3>
      <a href="#numbers-sense">
        
      </a>
    </div>
    <p>In an edge network, where HTTP and DNS clients connect to thousands of servers distributed across the world, the key is to distribute those servers across many carefully picked points of presence—and with over 85 PoPs, no network has better representation than CloudFlare. The reverse of this distribution, however, has to happen for our network's logs. After anycast has scattered requests (and queries) to thousands of nodes at the edge, it's the Data Team's job to gather the resulting logs to a small number of central points and consolidate them for easy use by our customers.</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/4RE8n2RXdycpeks8rkfCed/4b1da94fdca0a502db8d5522cb65c12d/logfwdr-2016-07-11.png" />
            
            </figure><p>The charts above depict (with some artifacts due to counter resets) the total structured logs sent from the edge to one of these central points yesterday, July 11th. Yesterday we saw:</p><ul><li><p>An average of <i>3.6M HTTP logs per second</i>, with peaks over 4.5M logs/s</p></li><li><p>An average of <i>750K DNS logs per second</i>, with peaks over 1M logs/s</p></li></ul><p>This is a typical, ordinary day, with the edge serving hundreds of Gbps in any given minute and transiting for &gt;128M distinct IP addresses in any given hour.</p><p>Such a day results in nearly 360TB of raw, Cap’n Proto event logs. Brokering this data requires two Kafka clusters, comprising:</p><ul><li><p>1196 cores,</p></li><li><p>170 10G NICs,</p></li><li><p>10.6TB RAM, and</p></li><li><p>4.3PB disk.</p></li></ul><p>Downstream from Kafka sits roughly the same amount of hardware, some shared between Mesos + Docker and HDFS, some wholly dedicated to services like CitusDB.</p><p>We expect to see a significant increase in these numbers by the end of 2016.</p>
    <div>
      <h3>Things that work (and that don't break)</h3>
      <a href="#things-that-work-and-that-dont-break">
        
      </a>
    </div>
    <p>What's worked well in this system, and why?</p><p>There are more, but space only permits naming five things.</p><ol><li><p><b>The log forwarder</b>. The graphs above use metrics from all instances of our log forwarding service running on the edge. This internal software, written in go, handles structured logs both at the edge (from nginx and from our DNS service) and on the data center side of the pipeline. Its plugin architecture has made it remarkably easy to add new log types and Kafka endpoints, and we've had great visibility into its operation thanks to the metrics we collect from it on ingress, drops, and buffer sizes.</p></li><li><p><b>Kafka</b>. CloudFlare runs several Kafka clusters, including one with just under 80 brokers. Although some failure modes remain hard to automate, there's nothing else on the market that can do what we need it to do, and it does that every day remarkably well.</p></li><li><p><b>Persistence, and to a greatly improved extent, HTTP retrieval, for log sharing</b>. CloudFlare offers log storage and retrieval to enterprise customers (known as Log Share or ELS), using a dedicated Kafka consumer, HDFS, and several go services. Thanks to more monitoring, improved resiliency to many kinds of network failures, better runbooks, and a lot of hard work, Log Share today offers significantly better availability than it did at the end of 2015. (The story of this work, by everyone in the team and a number of others, too, is worth its own post to detail.)</p></li><li><p><b>CitusDB</b>. Few things in a data center may seem less glamorous than sharded PostgreSQL, but CitusDB continues to work great as a performant and easily managed service for persisting aggregated data. Support has been quite good, and the fact that it's "just PostgreSQL" under the hood greatly simplified a zero-downtime CitusDB major version upgrade and migration to a completely new cluster.</p></li><li><p><b>The platform and SRE</b>! None of these parts could work well without the hard work done by Data's sister team, the Platform Team. We're exceptionally fortunate to have some of the best tools at our disposal, including OpenTSDB + Grafana for metrics, Prometheus for alerting, Sentry for exception reports, ElasticSearch + Kibana for logs, Marathon + Mesos + Docker for orchestration, and nginx + zoidberg for load balancing. In the same way, we also remain most grateful to CloudFlare's fantastic SREs, who continue to work with us in making the data center, different from the edge though it is, a reliable and efficient place to work in.</p></li></ol>
    <div>
      <h3>What's yet to be done</h3>
      <a href="#whats-yet-to-be-done">
        
      </a>
    </div>
    <p>What do we want to improve or expect to add in the near future?</p><p>Time, not space, restricts these. In rough order:</p><ol><li><p><b>Make each service more reliable for the customer</b>. This work includes dozens of small things, from better runbooks and automation to capacity planning and wholesale updates to existing architecture. And it runs a continuum from external services like Log Share, which are squarely about providing data to customers, to internal ones, which are squarely about improving automation and visibility at the edge.</p></li><li><p><b>New analytics</b>. Customers rely on CloudFlare to know what happens at the edge. We're working today to build analytics systems for all of CloudFlare's offerings and to extend our existing analytics so they tell customers more of what they need. Much of the technology we use in these new systems remains up for grabs, although one piece we have up (and handling a million DNS event logs per second) is Spark Streaming.</p></li><li><p><b>New data pipelines</b>. Both customers and CloudFlare itself need systems that get request and query logs from one point to another. Such pipelines include external ones that push raw logs to the customer, as well as internal ones that let us push logs between data centers for disaster recovery and the like.</p></li><li><p><b>Better support for complex analysis</b>. Finally, there will always be customers and internal users who need stronger tooling for analyzing high-dimension data at scale. Making it easy for people write such analyses, whether from a UI or as jobs run against a cluster, is a huge challenge and one we look forward to proving out.</p></li></ol><p>For all these services, which must be built so they can work for everyone, a key challenge is making sure that the design (1) works well for the customer, and (2) can be implemented in an economic number of nodes. As much as we can, we work to build solutions that perform well on dozens to hundreds of nodes, not thousands.</p>
    <div>
      <h3>Til we meet again</h3>
      <a href="#til-we-meet-again">
        
      </a>
    </div>
    <p>In this post from the Data Team, we talked about what's working and what we hope to work on next. We'd also love to talk with you! If you have thoughts on this post, or on what you'd like the Data Team to write about next, please do tell.</p><p>In addition: data at CloudFlare is a small team, so if these problems interest you, you stand to have some great problems to work on. Check out the <a href="https://careers.jobscore.com/careers/cloudflare/jobs/systems-software-engineer-for-data-dDgoEIFa4r5kjbiGaltGfR">Systems Software Engineer for Data</a> and <a href="https://careers.jobscore.com/careers/cloudflare/jobs/data-analyst-bGWRWIlC8r5OFSdG1ZS6tF">Data Analyst</a> roles, and let us know what you think.</p><p>Finally, if you're headed to GopherCon, CloudFlare has three Gophers attending this week, including one from the Data Team, Alan Braithwaite <a href="https://twitter.com/Caust1c">@Caust1c</a>. (Many, many services in data, including all production services today, are built with go.) Look us up!</p><hr /><ol><li><p>"through many peoples and across many seas," the beginning of Catullus 101. <a href="#fnref1">↩︎</a></p></li></ol> ]]></content:encoded>
            <category><![CDATA[Bandwidth Costs]]></category>
            <category><![CDATA[Life at Cloudflare]]></category>
            <category><![CDATA[Data]]></category>
            <guid isPermaLink="false">6PEF3gafRP6KIb9xZ4DFua</guid>
            <dc:creator>Hunter Blanks</dc:creator>
        </item>
        <item>
            <title><![CDATA[iOS 9 — How did the launch really go?]]></title>
            <link>https://blog.cloudflare.com/ios-9-how-did-the-launch-really-go/</link>
            <pubDate>Tue, 22 Sep 2015 22:53:00 GMT</pubDate>
            <description><![CDATA[ On September 16 2015 at 10:00AM PST, Apple released their latest update to the iPhone: iOS 9. For several days after the announcement, ISPs and customers reported problems downloading iOS 9 due to overloaded servers. ]]></description>
            <content:encoded><![CDATA[ <p>On September 16 2015 at 10:00AM PST, Apple released their latest update to the iPhone: iOS 9. For several days after the announcement, ISPs and customers reported problems downloading iOS 9 due to overloaded servers. Obviously, the demand for iOS 9 was higher than even Apple anticipated, but how much higher? Few organizations outside of Apple have any idea what the actual iOS 9 adoption rates look like.</p><p>By analyzing the user agent strings of requests passing through the CloudFlare network, we were able to piece together a pretty good picture of iOS 9 uptake. Here’s an hour-by-hour look at requests from iOS 8 devices (blue) and iOS 9 devices (orange) for the first 24 hours after the announcement.</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/2evKuvfY86P7afmY5e1I1/305d979b400f4d66f3fee1aed100b63f/image_0-1.jpg" />
            
            </figure><p>We started seeing small amounts of iOS 9 usage <i>before</i> it was officially released, followed by a spike immediately after the launch (times are shown in UTC, so the 10:00AM announcement shows up as hour 18). You can also see a second spike at 10:00 UTC when Europe started waking up.</p>
    <div>
      <h3>What about iOS 9.1?</h3>
      <a href="#what-about-ios-9-1">
        
      </a>
    </div>
    <p>Even though the official release was for iOS 9.0, we also found beta iOS 9.1 in the wild. Curious about the comparative traffic between the two?</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/6k72S5uVWVMg9eFUOPbRE/34a49bebad7df0c18e97953f1361b724/image_1.jpg" />
            
            </figure><p>iOS 9.0 is shown in blue, while 9.1 is shown in orange. As you can see, 9.0 adoption exploded after Apple’s official launch, while the 9.1 beta requests remained steady.</p>
    <div>
      <h3>Current Adoption Rates</h3>
      <a href="#current-adoption-rates">
        
      </a>
    </div>
    <p>Fast-forward five days, and it’s clear that iOS 9 adoption didn’t slow down after the launch. As of September 21st, the relative traffic of iOS 9 (orange) to iOS 8 (blue) on the CloudFlare network is around 30-35%.</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/7mPQ7f7gXseuluOTyxNdIa/01eb29509017d42b9fb9c5557e4efc28/image_2-2.png" />
            
            </figure>
    <div>
      <h3>IPv6 and iOS 9</h3>
      <a href="#ipv6-and-ios-9">
        
      </a>
    </div>
    <p>A key part of the iOS 9 announcement was that all iOS apps running on 9.0+ will require IPv6 support. The IPv4 address space is nearly exhausted, and switching to IPv6 is the only way to keep adding more devices to the global Internet.</p><p>iOS 9’s IPv6 requirement compliments CloudFlare’s mission to make a better Internet. We’ve been doing our part to encourage IPv6 adoption by providing <a href="https://www.cloudflare.com/ipv6">free IPv6 support</a> to all web properties on our network.</p><p>Although it’s still pretty early, we can already see an uptick in IPv6 requests due to iOS 9:</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/CUgWWvcpZYinzb6e4h4ds/ff46afadf69fac1dea24d2421c7828ea/image_3.png" />
            
            </figure><p>The first chart shows the number of IPv4 vs. IPv6 requests for iOS 8, while the second shows iOS 9. Even after only a few days, we’re seeing a 1.07% increase in IPv6 requests thanks to iOS 9.</p>
    <div>
      <h3>Conclusion</h3>
      <a href="#conclusion">
        
      </a>
    </div>
    <p>Seeing over 5% of global Internet requests gives us an interesting perspective on current events. We get to watch the worldwide impact of everything from product launches like iOS 9 to hiccups in the Internet infrastructure like the <a href="/the-internet-is-a-cooperative-system-dns-outage-of-6-july-2015/">Dyn outage</a> of last July and even <a href="/how-syria-turned-off-the-internet/">governments turning off the Internet</a>. We try our best to use our unique vantage point to better the Internet as a whole and raise awareness for critical technologies like IPv6.</p> ]]></content:encoded>
            <category><![CDATA[iOS]]></category>
            <category><![CDATA[Data]]></category>
            <category><![CDATA[Analytics]]></category>
            <guid isPermaLink="false">vQNdhSnKWFxlfJahe1PK8</guid>
            <dc:creator>Timothy Fong</dc:creator>
        </item>
    </channel>
</rss>