Rust

37 posts

discord3 min readCurated summary

Discord Patch Notes: February 3, 2025

Discord’s February 3, 2025 patch focuses on faster startup, more reliable infrastructure, improved notifications, and extensive usability fixes across desktop and mobile. Major changes include a roughly 20% improvement in median Android launch times, a Rust rewrite of the Media Proxy service that reduced aggregate latency by about 22%, and substantially improved push-notification delivery. The release also adds desktop DM Apps, redesigns Privacy & Safety settings, and addresses numerous platform-specific bugs. ## Performance and Reliability - Android app initialization improvements reduced launch times by approximately 20% on the median Android device. - Discord rewrote its Media Proxy service from Go to Rust and modernized its infrastructure: - Aggregate latency improved by about 22%. - Latency at the 90th percentile improved by more than 40%. - Service reliability also improved. - Push-notification reliability increased across server sizes: - Servers with fewer than 2,500 users now achieve a 99.98% delivery rate within 30 seconds. - Discord can notify approximately 20 million users on large servers such as Midjourney within 15 minutes of an `@everyone` mention. - Android 13 and earlier received fixes for layout failures caused by device rotation and incorrect screen-size calculations. ## New Features and Interface Updates - Desktop users can now launch Apps directly in DMs. - A quick-launch bar displays recently used Apps at the top of the DM list. - Privacy & Safety settings were redesigned with: - A new tab structure. - Better grouping and clearer descriptions. - Improvements intended to support future Trust settings. - macOS desktop notifications now use native operating-system APIs: - Message sounds respect Focus and Do Not Disturb modes. - Multiple notifications from the same channel can be grouped. - Desktop search now autocompletes `from:me` and `mentions:me`. - Linux now supports Soundshare and Wayland screen sharing, including video and audio support on Ubuntu 22 and later. - Mobile users can edit server invite links while inviting someone. - Push notifications now include user nicknames. ## General Bug Fixes - Fixed UI and layout problems involving: - Forum channel tag editing. - Overlapping channel-topic modals. - Incorrect friend-request layouts at narrow desktop widths. - Misaligned images, padding, borders, and animations across Shop, Quests, profiles, and reaction pickers. - Resolved Android issues involving duplicated channel-name letters, hidden confirmation dialogs, incorrect toast positioning, keyboard overlap, and navigation-bar padding. - Fixed mobile and iOS issues affecting: - Search-result counts and back-swipe navigation. - Text-entry scrolling during channel searches. - Excessive zooming. - Animated Emojis stopping after interaction. - Nitro Home back-swipe navigation. - Corrected problems with Privacy & Safety tabs being cropped or nonfunctional. - Improved reliability for the Claimed Quests interface, especially for users with many completed Quests. - Fixed profile customization issues related to Nitro Credit. - Corrected custom status duration behavior. - Resolved issues with mutual-friend and mutual-server views getting stuck loading. - Fixed Emoji picker server filters and enabled editing of Emoji forum tags whose associated Emoji had been deleted. - Corrected several Shop, tablet banner, profile-effect, and mobile DM layout issues. Overall, the patch combines measurable backend performance gains with a broad collection of cross-platform fixes. Users should see faster Android launches, more dependable media uploads and notifications, improved desktop and mobile navigation, and fewer layout and interface failures.

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

How we migrated our static analyzer from Java to Rust

Datadog migrated its static analyzer from Java to Rust after finding that ANTLR-based parsing was too slow and language support was incomplete. Rust’s strong integration with Tree-sitter enabled broader language coverage, faster scans, and lower memory usage. The migration preserved behavioral parity while tripling performance and reducing memory consumption tenfold. ## Why Performance Became a Priority - Datadog runs analysis directly in customers’ CI environments, often on resource-constrained runners. - On a two-core, 7 GB GitHub Actions runner, medium repositories took about five minutes to scan instead of the target of under three minutes. - Codiga’s previous hosted environment used large, tuned servers, which masked some performance problems. - Java also required customers to use JVM 17+, potentially conflicting with JVM versions already installed in their CI environments. - Improving Java offered limited upside, so the team considered a rewrite despite its cost and risk. ## Static Analyzer Architecture - The analyzer consists primarily of: - A parsing layer that builds an abstract syntax tree (AST). - An execution layer that analyzes the AST, reports violations, and offers fixes. - Tree-sitter generates the AST. - The existing Java binding lacked important functionality, including Tree-sitter pattern matching. - Tree-sitter’s core libraries are implemented in Rust, where support was more complete. - Analysis rules are written in JavaScript and were originally executed through GraalVM’s polyglot capabilities. - Fast parsing, pattern matching, and rule execution were central to meeting the desired CI performance. ## Migrating from Java to Rust - Rust was selected because it is a first-class part of the Tree-sitter ecosystem and provided better access to its features. - The migration required: - Feature parity with the Java implementation. - Identical analysis results and reported violations. - No execution-time regressions. - Migrating the parser was relatively straightforward because Rust support came directly from Tree-sitter. - The Rust implementation: - Tripled analyzer performance. - Reduced memory usage by a factor of ten. - JavaScript execution moved from GraalVM to `deno-core`, a Rust-based V8 integration. - Only the core JavaScript functionality was included. - Disk and network capabilities were excluded because analysis rules do not need them, improving security. ## Migration Strategy and Rust Adoption - The team treated automated equivalence and performance tests as requirements for a successful rewrite. - Rust allowed the analyzer to integrate more directly with its key dependencies rather than maintaining a separate Java binding. - The broader migration also required replacing supporting Java components with corresponding Rust libraries; the article indicates that these mappings were documented as part of the transition. Overall, the move to Rust was justified by the analyzer’s deployment model: faster execution and lower resource consumption directly improved the experience of customers running scans in constrained CI environments.

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

.NET Continuous Profiler: CPU and wall time profiling

Datadog’s .NET profiler uses low-overhead thread sampling to collect CPU and wall-time profiles suitable for production. CPU profiling measures time spent running on a processor, while wall-time profiling captures delays caused by I/O, locks, or scheduling. The implementation tracks managed threads, walks their stacks, aggregates samples, and periodically uploads profiles, with platform-specific optimizations for Windows and Linux. ## CPU and Wall-Time Profiling - CPU profiling identifies code consuming processor cycles. - Wall-time profiling finds slow methods regardless of whether threads are running, blocked, or waiting for I/O. - ETW, Linux `perf`, and .NET enter/leave hooks provide profiling data but impose too much overhead or require elevated privileges. - Datadog instead samples threads at intervals, captures their call stacks, and assigns durations to the samples. ## Managed Thread Sampling - The profiler tracks managed threads created through: - The thread pool - `Task` and `async/await` - Explicit `Thread` instances - `ICorProfilerCallback` notifications such as `ThreadCreated` and `ThreadDestroyed` maintain the `ManagedThreadList`. - `StackSamplerLoop` captures stacks and sends raw samples to `CpuTimeProvider` and `WallTimeProvider`. - `SamplesCollector` aggregates provider data, while a shared Rust exporter uploads profiles to Datadog every minute. - An early C# implementation caused native worker threads entering managed code to appear as application threads. Removing that implementation eliminated accidental self-sampling. ## Native Runtime Threads - Server-mode garbage collection uses native threads that can compete with application threads for CPU. - Because the .NET profiling API does not expose these threads directly, the profiler identifies them by their .NET 5 names: - `.NET Server GC` - `.NET BGC` - Their CPU usage is displayed under a Garbage Collector frame in the flame graph. ## CPU Sampling and Performance Optimizations - Every 10 milliseconds, the profiler searches for runnable managed threads, skipping up to 64 non-runnable threads. - `IsRunning` determines whether a thread is executing on a CPU: - Windows uses `NtQueryInformationThread`. - Linux reads `/proc/self/task/<tid>/stat`. - CPU sample duration is calculated from the difference between the thread’s current and previously recorded CPU consumption. - The initial Linux implementation used `std::ifstream` and `std::getline`, allocating an 8 KB buffer for each sample. - Replacing them with lower-level C file-reading code eliminated allocations and reduced CPU usage. The original approach consumed nearly 500 MB of allocations and about 2% of total CPU in testing. ## Wall-Time Profiling and Code Hotspots - Wall-time profiles work with Datadog tracing to explain why requests are slow. - The tracer provides the profiler with a span ID when a thread handles a request. - To avoid repeated expensive P/Invoke calls, the profiler exposes a memory location that the tracer can update directly. - Because short requests may finish before their thread is sampled, the profiler samples an additional group of ten span-associated threads beyond the initial five. - The duration between consecutive samples of a thread is attributed to the later sample, so heavily threaded applications produce longer individual wall-time sample durations.

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

How Figma Draws Inspiration From the Gaming World | Figma Blog

Figma’s technology shares more with a game engine than a traditional web application. Like games, it combines graphics rendering, interaction, multiplayer, animation, and other systems to create a responsive digital world. This architecture, along with close collaboration across engineering, design, product, and research, enables Figma and FigJam to support complex creative work in real time. ## Engineers as Digital World-Builders - Game engines combine foundational systems such as: - Graphics and rendering - User controls - Multiplayer - Physics and collisions - Animation - Artificial intelligence - Combat or other specialized mechanics - Figma similarly builds a 2D graphics and rendering system for the web. - Engineers ensure that text, shapes, and lines appear correctly while users pan and zoom across a canvas. - Collaboration is central to Figma, so its real-time collaboration engine is called “multiplayer,” inspired by cooperative games. - Figma and FigJam are built from many interacting “systems,” including: - Multiplayer editing - Spring animations - Audio and cursor chat - Component Variants - Plugins and widgets - Because these systems must run efficiently in browsers and mobile apps, Figma uses a game-engine-like stack rather than a conventional web stack. - The canvas is written in C++ and compiled to WebAssembly, helping address memory and performance constraints. ## Creativity Requires Systems-Level Collaboration - Game developers work closely with artists and designers to refine both technical behavior and user experience. - Figma follows a similar model, with engineers collaborating across product management, design, data science, and research. - Interdependent systems create emergent behavior: changing one system can affect many others. - The article compares this to *The Legend of Zelda: Breath of the Wild*, where fire can provide warmth and food, cause damage, or help defeat enemies. - Figma’s complexity produces comparable interactions, where an apparent problem in one feature may actually result from behavior elsewhere in the system. - This systems-level collaboration helps teams investigate unexpected failures and improve the overall product rather than treating each feature in isolation. Figma’s game-inspired approach is both architectural and cultural: build modular, interacting systems, optimize for real-time performance, and involve diverse disciplines in solving problems. Teams building similarly complex collaborative tools can benefit from treating the product as a living digital world rather than as a collection of disconnected web features.

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

Introducing Glommio, a thread-per-core crate for Rust and Linux

Thread-per-core architecture can significantly improve performance and reduce cloud costs by avoiding lock contention and expensive context switches. However, adopting it directly can reduce developer productivity because it requires new programming patterns and careful data ownership. Datadog developed Glommio, a Rust framework intended to make thread-per-core applications easier to build and maintain. ## Why Traditional Threading Has Limits - Applications commonly use multiple threads to perform independent tasks in parallel. - Shared data requires locks, which introduce contention and waiting. - Thread context switches can cost around five microseconds—potentially more than modern storage I/O operations using technologies such as `io_uring`. - Asynchronous programming reduces blocking, but many runtimes still rely on thread pools or separate worker threads for operations such as file I/O. ## How Thread-per-Core Works - Each CPU core runs a single application thread, often pinned to that core. - Because the operating system does not move the thread between cores, ordinary thread context switches are eliminated. - Hardware interrupts and auxiliary tasks can still interrupt execution. - For maximum performance, operators may reserve certain CPUs for interrupts and system services rather than application work. ## Sharding Data Across Cores - Thread-per-core applications depend on sharding: each thread owns a distinct subset of the data or requests. - Examples include assigning Kafka partitions or database key ranges to individual threads. - Requests assigned to one thread execute there to completion unless the code explicitly yields. - This ownership model prevents multiple threads from handling the same request or data simultaneously. ## Eliminating Locks - Since one thread processes a shard at a time, operations on that shard are naturally serialized. - A conventional threaded cache requires locks because multiple threads may update the same data concurrently. - Sharding reduces contention by dividing a large cache into smaller sections, but locks may still be needed if the operating system switches between threads. - With thread-per-core, updates to keys in the same shard occur sequentially, so an update can complete without acquiring a lock. ## Glommio and Existing Precedents - Thread-per-core is not a new concept; the author previously worked with Seastar, a C++ framework used by ScyllaDB. - Datadog’s Glommio brings the model to Rust while aiming to make its programming challenges more manageable. - The framework is motivated by the need to preserve developer productivity while achieving the efficiency gains of thread-per-core systems. Thread-per-core is most suitable for highly parallel, high-throughput workloads with naturally shardable data. Its performance benefits depend on disciplined data ownership and cooperative execution, while frameworks such as Glommio can reduce the complexity of adopting the model.

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

How Figma’s multiplayer technology works | Figma Blog

Figma built a custom multiplayer system because traditional operational transformation (OT) was too complex for its document-editing needs. Its client/server architecture synchronizes document changes over WebSockets, supports offline editing, and separates document collaboration from other data such as comments and users. The system began as a prototype that enabled rapid experimentation before being integrated into production. ## Why Figma Built Its Own Multiplayer System - In 2015, no major design tool offered real-time collaborative editing. - Figma avoided OT, the algorithm used by tools such as Google Docs, because it considered OT unnecessarily complex for its problem space. - The custom approach was designed to be simpler and faster to implement. - Multiplayer eliminated the need to export, email, or manually synchronize design files. - It also allowed non-designers—such as copywriters and developers—to participate or view work without interrupting the designer. ## Figma’s Client/Server Architecture - Figma clients are web pages connected to a server cluster through WebSockets. - Each multiplayer document runs in a separate server process, with all editors connected to that process. - When a document opens, the client downloads an initial copy of the file. - Subsequent changes are synchronized in both directions over the WebSocket connection. - Server performance and scaling were important considerations, later addressed in part through the use of Rust. ## Offline Editing and Reconnection - Clients can continue editing while offline for an arbitrary period. - When reconnecting, the client: - Downloads a fresh version of the document. - Reapplies its locally stored offline edits to that latest state. - Resumes synchronization through a new WebSocket connection. - This keeps connection and reconnection logic relatively simple by concentrating multiplayer complexity on already-connected clients. ## Separate Systems for Different Data - Figma’s multiplayer system is used only for syncing document changes. - Comments, users, teams, projects, and similar information are stored in Postgres. - That data is synchronized through a separate system because it has different requirements involving: - Performance - Offline availability - Security ## Prototyping Before Production - Figma first created a standalone browser-based prototype rather than experimenting directly in the production codebase. - The prototype simulated three clients connected to a server and visualized the complete system state. - Engineers could test: - Offline clients - Bandwidth-limited connections - Different collaborative algorithms - Alternative data structures - Once the design was validated, the ideas were transferred into the main codebase. Figma’s experience demonstrates that collaborative systems do not always require the most established algorithm. A focused, custom protocol—validated through fast prototyping—can provide a simpler solution when its data model and product requirements differ from tools like document editors.

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)