Amikor 2023 elején végignézzük, hogy mely cégek jelentettek be Rust-alapú komponenseket vagy publikáltak részletes mérnöki blogposztokat a nyelvről, egyértelmű minta rajzolódik ki: ott bukkan fel a Rust, ahol a memóriabiztonság és a nyers teljesítmény egyszerre kritikus szempont. Nézzük végig a legérdekesebb eseteket.

AWS: Firecracker-től a Bottlerocket-ig

Az AWS az egyik legrégebb óta és legkomolyabban elkötelezett nagyvállalati Rust-felhasználó. A Firecracker microVM technológia – ami a Lambda és a Fargate mögött fut – 2018 óta Rust-ban íródik, és pontosan azért választották a nyelvet, mert a hipervizor kódnak egyszerre kell gyorsnak és biztonságosnak lennie. Egy memóriahiba egy hipervizorban katasztrofális lehet, a Rust ownership modellje viszont már fordítási időben kiszűri a use-after-free és data race típusú hibák nagy részét.

A Bottlerocket, az AWS konténer-optimalizált Linux disztribúciója szintén Rust-ban íródott a felhasználói térben futó komponensek terén – az ötlet az, hogy minél kevesebb támadási felületet hagyjanak a rendszerben, és a Rust segít abban, hogy a kód maga is kevesebb sebezhetőséget tartalmazzon.

2022 végén jelent meg fejlesztői előnézetben (developer preview) az AWS SDK for Rust is, ami azt jelzi, hogy az AWS immár nem csak belső infrastruktúrához, hanem az ügyfelek felé is elsőrangú nyelvként kezeli a Rust-ot.

Megjegyzés

Az AWS SDK for Rust még developer preview állapotban van 2023 tavaszán, tehát production környezetben egyelőre óvatosan érdemes bevezetni – de a irány world egyértelmű.

Discord: a Go-ból a Rust-ba vezető út

A Discord mérnökei két nagy, nyilvánosan is dokumentált átállást hajtottak végre az elmúlt években. Az első a hangfeldolgozási (encoding) réteg volt, amit eredetileg Elixirben írtak, majd a garbage collector okozta kiszámíthatatlan szüneteket (latency spike-okat) próbálták kiküszöbölni azzal, hogy a legkritikusabb, valós idejű részt Rust-ba vitték át.

A második, talán még ismertebb eset a Read States szolgáltatás, amit eredetileg Go-ban írtak, majd Rust-ra cseréltek. A probléma itt is a garbage collector viselkedésével függött össze: a szolgáltatás egy hatalmas, memóriában tartott LRU cache-t kezelt, és a Go GC time to time nagy, néhány száz milliszekundumos szüneteket okozott, amikor végigment a hatalmas heap-en. A Rust-ra váltás után ezek a latency csúcsok eltűntek, mert nincs stop-the-world GC – helyette determinisztikus, ownership-alapú memóriakezelés van.

Íme egy leegyszerűsített, szemléltető példa arra, hogyan nézhet ki egy ilyen LRU-szerű cache réteg Rust-ban, tokio és axum segítségével:

use axum::{extract::State, routing::get, Json, Router};
use std::collections::HashMap;
use std::sync::{Arc, Mutex};

#[derive(Clone)]
struct AppState {
    cache: Arc<Mutex<HashMap<u64, String>>>,
}

async fn get_read_state(
    State(state): State<AppState>,
) -> Json<Option<String>> {
    let cache = state.cache.lock().unwrap();
    Json(cache.get(&42).cloned())
}

#[tokio::main]
async fn main() {
    let state = AppState {
        cache: Arc::new(Mutex::new(HashMap::new())),
    };

    let app = Router::new()
        .route("/read-state", get(get_read_state))
        .with_state(state);

    axum::Server::bind(&"0.0.0.0:3000".parse().unwrap())
        .serve(app.into_make_service())
        .await
        .unwrap();
}

Természetesen a valós Discord-implementáció ennél jóval összetettebb – saját eviction stratégiákkal, shardinggal és perzisztenciával –, de az alapelv ugyanez: egy egyszerű, kiszámítható memóriamodell, ahol nincs meglepetés a GC felől.

Tipp

Ha saját projektedben hasonló, alacsony latency-jű cache réteget szeretnél építeni, érdemes megnézni a dashmap crate-et is, ami lock-free, shardolt HashMap implementációt ad – sokkal jobb konkurens hozzáférés mellett, mint egy sima Mutex<HashMap<...>>.

Cloudflare: Rust az edge-en

A Cloudflare talán a legaggresszívebb abban, hogy a hálózati rétegbe minél mélyebbre vigye be a Rust-ot. A BoringTun, a Cloudflare nyílt forráskódú WireGuard implementációja userspace-ben, teljes egészében Rust-ban készült – ez lehetővé teszi, hogy VPN-szerű funkciókat futtassanak anélkül, hogy a kernel modulra lennének utalva, és eközben megtartsák a natív sebességhez közeli teljesítményt.

A Cloudflare Workers platform – amely V8 isolate-eket használ JavaScript és WebAssembly futtatására a hálózat szélén – szintén erősen támaszkodik Rust-ra írt komponensekre a runtime körül, a request routing és a biztonsági izoláció terén. A cég emellett olyan könyvtárakat is nyílt forráskódúvá tett, mint a lol-html, egy streaming HTML rewriter/parser, amit a Workers és a saját CDN termékeik is használnak HTML transzformációhoz valós időben, nagy áteresztőképesség mellett.

Az edge computing világában különösen fontos a kiszámítható, alacsony memóriaigényű futás – egy edge node ugyanis sokszor korlátozott erőforrásokkal rendelkezik, és több ezer, egymástól izolált kontextust kell egyszerre kezelnie. Itt a Rust zero-cost abstraction filozófiája konkrét üzleti előnnyé válik: nincs runtime overhead egy GC-től, és a bináris méret is jóval kisebb tud maradni, mint egy hasonló funkciójú Go vagy Java szolgáltatásé.

Miért éppen a Rust a kritikus rendszerekhez?

Ha összenézzük a fenti három céget, egy közös minta rajzolódik ki: mindhárom olyan rendszerekről beszél, ahol

  • a latency kiszámíthatósága kritikus (nincs helye GC szünetnek),
  • a memóriabiztonság üzembiztonsági kérdés (hipervizor, hálózati proxy, VPN),
  • és a erőforrás-hatékonyság közvetlen költségmegtakarítást jelent (kevesebb szerver, kisebb bináris, kevesebb RAM).

A Rust ownership és borrow checker rendszere pontosan ezekre a problémákra ad választ: fordítási időben kizárja a use-after-free, double-free és data race hibák túlnyomó többségét, mindezt anélkül, hogy futásidejű garbage collectorra lenne szükség.

Érdemes megemlíteni, hogy a nyelv maga is folyamatosan érik: a stabil Rust 1.68-ban már elérhetők olyan fejlettebb típusrendszeri eszközök is, mint a GAT-ok (Generic Associated Types), amelyek lehetővé teszik, hogy egy trait asszociált típusa maga is generikus legyen élettartam vagy típusparaméter szerint. Ez különösen hasznos olyan absztrakciók megírásakor, mint amilyeneket egy streaming parser vagy egy async iterátor könyvtár igényel – pont az a fajta munka, amit a Cloudflare lol-html-hez hasonló projektjei végeznek.

trait StreamingSource {
    type Item<'a> where Self: 'a;

    fn next_item<'a>(&'a mut self) -> Option<Self::Item<'a>>;
}

struct ChunkReader {
    buffer: Vec<u8>,
    pos: usize,
}

impl StreamingSource for ChunkReader {
    type Item<'a> = &'a [u8];

    fn next_item<'a>(&'a mut self) -> Option<Self::Item<'a>> {
        if self.pos >= self.buffer.len() {
            return None;
        }
        let chunk = &self.buffer[self.pos..];
        self.pos = self.buffer.len();
        Some(chunk)
    }
}

A hétköznapi kódban emellett egyre gyakoribb a let-else szintaxis is, ami sokat javít a hibakezelő ágak olvashatóságán – ez épp az a fajta apró, de sokat számító ergonómiai fejlesztés, ami miatt a nagyvállalati csapatok szívesebben váltanak Rust-ra:

fn parse_port(input: &str) -> u16 {
    let Ok(port) = input.parse::<u16>() else {
        eprintln!("Érvénytelen port, alapértelmezett 8080 lesz használva");
        return 8080;
    };
    port
}
Jó tudni

A GAT-ok és a let-else is stabil funkciók a jelenlegi (1.68-as) Rust-ban, tehát nyugodtan használhatod éles projektekben is – nem kísérleti, nightly-only feature-ökről van szó.

Mit tanulhatunk mindebből?

A legfontosabb tanulság talán az, hogy a Rust bevezetése ezeknél a cégeknél szinte sosem „mindent Rust-ra cserélünk” típusú döntés volt. Inkább célzott, adatvezérelt lépésekről beszélünk: azonosítottak egy konkrét fájdalompontot (GC szünet, memóriahiba, túl magas erőforrás-fogyasztás), majd az adott komponenst cserélték le, mérték az eredményt, és csak utána terjesztették ki a megközelítést.

Ez jó hír a magyar Rust-közösség számára is: nem kell egyből egy teljes rendszert újraírni ahhoz, hogy kihasználd a nyelv előnyeit. Egy jól körülhatárolt, teljesítmény-kritikus szolgáltatás – legyen az egy cache réteg, egy proxy, vagy egy adatfeldolgozó pipeline – tökéletes belépési pont lehet, ahogy azt a Discord Read States története is mutatja.

Összefoglalás

Az AWS, a Discord és a Cloudflare története is azt igazolja, hogy a Rust mára túllépett a „lelkes hobbisták nyelve” szerepkörön, és valódi, mérhető üzleti értéket termel a legnagyobb technológiai cégek infrastruktúrájában is. A közös nevező mindenhol ugyanaz: kiszámítható teljesítmény, memóriabiztonság futásidejű overhead nélkül, és egy egyre érettebb ökoszisztéma – legyen szó az axum-ról, a tokio-ról, vagy magáról a nyelvi típusrendszerről. Ha eddig csak figyelted a Rust térnyerését, ezek a történetek jó indokot adhatnak arra, hogy a következő teljesítmény-kritikus projektedhez te is kipróbáld.