Parallel Processing

5 posts

cloudflare3 min readCurated summary

Scaling Security Insights: how we achieved a 10x increase in global scanning capacity

Security Insights needed a 10x throughput increase to scan all customers more frequently and detect risks sooner. The existing system was overwhelmed by Kafka backlogs, slow processing, database inefficiencies, and API timeouts. Cloudflare improved capacity by introducing parallel and lane-based processing, optimizing bulk database writes, and addressing regional latency between its API and database. ## Scaling Kafka Processing - Scans are scheduled and published to Apache Kafka. - Go-based checker services consume these messages, inspect accounts, zones, and DNS records, and send findings to an internal API. - Kafka’s partition ordering limits each consumer group to one active consumer per partition. - Slow messages could block all subsequent messages in the same partition. - Adding partitions was avoided because it would increase resource usage for shared Kafka brokers. ## Introducing Parallel Processing - Checkers were changed to consume messages in batches. - Each message in a batch is processed concurrently in its own goroutine. - This increased throughput without requiring additional Kafka partitions. - The trade-offs were higher memory usage and potentially more work to repeat after a process crash. ## Separating Slow and Fast Work - Some scans took seconds or milliseconds, while unusually large accounts or zones could take minutes or hours. - These slow messages caused head-of-line blocking for faster work. - Consumer groups and checkers were split into: - A fast lane for predictable, short-running scans - A slow lane for messages expected to require substantially more time - Fast-lane consumers skipped slow messages, allowing normal scans to continue without delay. ## Optimizing Postgres Writes - The API originally executed one insert/upsert transaction per insight. - A request containing up to 500,000 insights could therefore generate hundreds of thousands of database round trips. - Bulk insertion with `COPY` into a temporary table was tested but caused bloat in Postgres system tables. - The final hybrid approach used: - `UNNEST` for smaller batches - `COPY` for batches above a configured threshold - This delivered millisecond-level performance for small writes and completion within seconds for very large writes. ## Diagnosing API Timeouts - Client-side timeouts increased as scan volume grew. - Checkers sometimes spent 20–90% of their processing time waiting on a single API call. - Throughput initially rose but then deteriorated under heavy load. - The root cause was network latency: - Postgres was hosted in Portland, Oregon. - The API ran active-active in Portland and Amsterdam. - Requests routed to Amsterdam incurred roughly 50 milliseconds of network round-trip latency. - Amsterdam database queries held client connection-pool connections much longer—nearly three seconds on average versus about 10 milliseconds in Portland. - The connection pool became exhausted, causing requests to wait for available connections and creating uneven Kafka lag across partitions. Cloudflare’s results came from improving the full processing pipeline rather than relying on a single infrastructure change. Parallelize message handling, isolate slow workloads, batch database writes, and place latency-sensitive services close to their databases to achieve large throughput gains and more frequent security scanning.

Read original(opens in new tab)
github3 min readCurated summary

Run multiple agents at once with /fleet in Copilot CLI

GitHub Copilot CLI’s `/fleet` command lets multiple subagents work on independent tasks simultaneously rather than completing everything sequentially. An orchestrator decomposes the objective, manages dependencies, dispatches agents, and verifies their results. To benefit from parallel execution, users should define clear deliverables, boundaries, dependencies, and validation requirements. ## How `/fleet` Works - Breaks a task into discrete work items and identifies dependencies. - Runs independent items in parallel as background subagents. - Waits for completed work before dispatching dependent tasks. - Verifies results and assembles the final output. - Gives each subagent its own context window while sharing the same filesystem. - Prevents direct communication between subagents; the orchestrator coordinates them. ## Getting Started - Run `/fleet <objective prompt>` interactively, such as: ```bash /fleet Refactor the auth module, update tests, and fix the related docs in docs/auth/ ``` - For terminal-based non-interactive use: ```bash copilot -p "/fleet <YOUR TASK>" --no-ask-user ``` - The `--no-ask-user` option is required when no one is available to answer prompts. ## Writing Parallelizable Prompts - Define concrete deliverables such as individual files, test suites, or documentation sections. - Avoid vague requests that make it difficult to identify independent work. - Explicitly state: - File or module ownership - Constraints, such as avoiding dependency changes - Required tests, linting, or type checks - List dependencies so the orchestrator can serialize only the necessary work while parallelizing the rest. ## Using Custom Agents - Specialized agents can be defined in `.github/agents/`. - Agent definitions may specify: - Model - Tools - Role-specific instructions - Prompts can assign different agents to different tracks, such as using a technical writer for documentation and the default agent for code. - If no model is specified, the agent uses the current default model. ## Monitoring Fleet Execution - Review the initial decomposition to ensure the task has multiple independent tracks. - Use `/tasks` to inspect active background work. - Look for progress updates from separate tracks. - If work is proceeding sequentially, ask Copilot to decompose the task first and report each track’s status and blockers. ## Avoiding File Conflicts - Subagents share a filesystem without file locking. - If two agents edit the same file, the last completed write silently overwrites the other. - Assign distinct files or directories to each track. - For shared files, use temporary outputs and merge them afterward, or impose an explicit execution order. Use `/fleet` for well-partitioned work with clear ownership and dependencies. Careful prompt structure is essential: parallelism is most effective when agents can operate independently without competing for the same files.

Read original(opens in new tab)
cloudflare3 min readCurated summary

Launching Cloudflare’s Gen 13 servers- trading cache for cores for 2x edge compute performance

Cloudflare’s Gen 13 servers use AMD EPYC 5th Gen Turin processors to provide up to twice as many cores as Gen 12. However, Turin’s much smaller per-core cache caused the legacy FL1 request-handling layer to suffer severe latency increases, despite higher throughput. Cloudflare found that tuning alone could not fully solve the problem, reinforcing the need for FL2, a Rust-based rewrite designed to scale with cores rather than depend heavily on cache. ## Turin’s Core-Heavy Architecture - Gen 13 Turin processors offer: - Up to 192 cores and 384 SMT threads, compared with Gen 12’s 96 cores. - Improved instructions per cycle through the Zen 5 architecture. - Up to 32% lower power consumption per core. - DDR5-6400 support for greater memory bandwidth. - The tradeoff is substantially less cache: - Gen 12 Genoa-X provides 12 MB of L3 cache per core through 3D V-Cache. - The 192-core Turin 9965 provides only 2 MB per core. - This architecture favors aggregate throughput but challenges workloads dependent on cache locality. ## FL1’s Cache and Latency Problems - FL1, based on NGINX and LuaJIT, was optimized for Gen 12’s large cache. - AMD uProf measurements showed: - Dramatically higher L3 cache miss rates on Turin. - More requests requiring slow DRAM access. - Increasing latency as CPU utilization and cache contention rose. - An L3 hit takes roughly 50 CPU cycles, while a DRAM fetch can take more than 350 cycles. - As a result, Gen 13’s additional cores delivered throughput gains but introduced unacceptable latency penalties. ## Throughput Gains at an Unacceptable Cost - With FL1, Gen 13 produced: - 10% more throughput on the 128-core Turin 9755. - 31% more on the 160-core Turin 9845. - 62% more on the 192-core Turin 9965. - The Turin 9965 offered the strongest total-cost-of-ownership benefits. - However, latency increased by more than 50% at high CPU utilization, which would negatively affect customer experience and violate performance requirements. ## Hardware and Resource Tuning - Cloudflare tested several mitigations with AMD: - Hardware prefetcher and Data Fabric Probe Filter adjustments produced only marginal improvements. - Adding FL1 workers increased throughput but took resources away from other services. - CPU pinning and isolation provided limited benefits. - AMD’s Platform Quality of Service (PQOS) was used to control cache and memory-bandwidth sharing across Turin’s Core Complex Dies. ## Cache Isolation with PQOS - Reserving part of a single CCD’s cache for FL1 produced less than 5% additional throughput. - Configurations assigning FL1 50–75% of each CCD’s cache also delivered less than 5% improvement and caused minor degradation elsewhere. - A socket-level approach was more successful: - Six of twelve CCDs, aligned with a NUMA domain, were dedicated to FL1. - This provided more than 15% incremental throughput while keeping latency acceptable. - These results showed that workload placement and cache locality could help, but they were not a complete substitute for software designed around Turin’s cache profile. Cloudflare’s broader solution was FL2, a Rust-based rewrite of its core request-handling layer. By reducing dependence on large per-core caches, FL2 enabled Gen 13’s higher core count to translate into scalable edge-compute performance without the latency penalties seen with FL1.

Read original(opens in new tab)
gitlab2 min readCurated summary

Agentic code reviews for $0.25 each

GitLab introduces Code Review Flow, an agentic AI review feature priced at a flat $0.25 per merge request. It automatically analyzes code, repository context, pipelines, security findings, and compliance requirements, producing structured inline feedback. The post argues that predictable pricing and parallel execution can reduce review costs and shorten merge queues. ## The Code Review Bottleneck - AI coding tools have increased development speed, but review capacity has not kept pace. - Code review times have reportedly risen 91% on teams using AI coding tools. - Engineers at large companies wait a median of 13 hours for pull requests to merge. - 44% of engineering teams identify slow reviews as their biggest delivery blocker. - Existing AI review tools often use unpredictable token-based pricing, with some costing $15–$25 per review. ## How Code Review Flow Works - It starts automatically when a merge request is opened. - The agent: - Scans the code changes. - Explores relevant repository context. - Checks pipeline status and results. - Reviews security findings and compliance requirements. - Produces structured inline comments. - Because it runs within GitLab, reviews can execute in parallel across projects and organizations rather than sequentially in individual developers’ environments. ## Flat-Rate Pricing and Savings - Each review costs 0.25 GitLab Credits, or $0.25 at list pricing. - The price is the same regardless of merge request size or complexity. - Four reviews cost one GitLab Credit, making usage easy to forecast. - Compared with an estimated $25 cost for 15 minutes of senior-engineer review time, GitLab claims a 99% reduction in per-review cost. - Parallel reviews can unblock merge requests within minutes instead of hours. ## Scaling Reviews Across Teams - The low fixed price makes it practical to run reviews on every merge request. - Teams can define project-specific review instructions and guardrails. - Different projects can use Code Review Flow, Claude Code, Codex, or custom agents. - Results remain visible in GitLab while reviews run concurrently. ## Availability - The $0.25 pricing is available on GitLab.com, Dedicated, and self-managed GitLab instances running version 18.8.4 or later. - Users can try GitLab Duo Agent Platform through a free trial or contact their GitLab account representative. The post recommends enabling automated reviews broadly rather than reserving them for high-priority changes, using AI to handle routine feedback while engineers focus on architecture, mentorship, and higher-value decisions.

Read original(opens in new tab)
figma3 min readCurated summary

How Mozilla’s Rust dramatically improved our server-side performance | Figma Blog

Figma rewrote the performance-critical part of its multiplayer synchronization server from TypeScript to Rust to eliminate latency spikes and improve scalability. Rust’s low memory usage, lack of garbage collection, and high performance enabled Figma to isolate every document in its own process and make serialization more than ten times faster. However, Rust’s immaturity led Figma to abandon a full-server rewrite and use it selectively where performance mattered most. ## Scaling the Multiplayer Service - Figma’s server ran a fixed number of workers, with each document assigned exclusively to one worker. - The TypeScript server was single-threaded, so one slow operation could block synchronization for every document handled by that worker. - Encoding large documents was a frequent source of unpredictable delays. - Adding hardware or creating a Node.js process per document was impractical because of JavaScript VM memory overhead. - Figma temporarily isolated problematic “heavy” documents onto a separate worker pool, but this required manual monitoring and reassignment. ## Rust-Based Architecture - Figma moved performance-sensitive multiplayer logic into a separate Rust child process. - The Rust process communicates with its host through standard input and output. - Rust’s low memory usage made it feasible to run one process per document, fully parallelizing document operations. - Serialization became more than ten times faster, including for very large documents. - This architecture removed the worst-case blocking behavior of the original worker model. ## Server-Side Performance Improvements - Progressive rollout graphs showed a dramatic reduction in server performance problems after the Rust implementation reached full deployment. - The improvements primarily affected server stability and responsiveness, rather than directly making the client UI faster. - Users were less likely to experience synchronization hiccups caused by unusually large or expensive documents. - Figma reported substantial improvements in peak performance metrics compared with the old server. ## Benefits and Drawbacks of Rust - Rust provided: - Very low memory usage due to fine-grained memory control, no garbage collector, and a minimal standard library. - High performance comparable to lower-level languages such as C++. - Strong compile-time safety that prevents many classes of bugs common in C++. - The language was less mature than conventional server-side languages and still had significant rough edges. - Because of these limitations, Figma abandoned plans to rewrite the entire server in Rust. - Instead, it adopted Rust selectively for the most performance-sensitive components. Figma’s experience suggests that Rust can deliver major production benefits when applied to isolated, resource-intensive workloads, while a gradual or hybrid adoption strategy may be more practical than a complete rewrite.

Read original(opens in new tab)