Tagasi avalehele
Engineering 2026-02-20 Viimati kontrollitud: märts 2026

Rust tootmiskeskkonnas: Kuidas me kirjutasime fintech API ümber ja vähendasime AWS kulusid 70%

J&L Dev Team
Vaneminseneride meeskond

A real-world account of how we rewrote a client's Node.js payment validation service in Rust — and the numbers that followed: p99 latency dropped from 45ms to 3ms, memory from 512MB to 48MB per container, and the monthly AWS bill fell by 70%.

  1. aasta lõpus pöördus üks meie Eesti fintech klientidest meie poole probleemiga, mis kõlab paljudele arendusmeeskondadele tuttavalt: nende Node.js maksete valideerimise teenus muutus ühelt poolt kalliks käitada ja teiselt poolt raskeks skaleerida tippkoormuse ajal. Tehingute mahud olid kahe aasta jooksul kasvanud 4 korda ja infrastruktuurikulud olid vastavalt kasvanud — ilma proportsionaalse jõudluse paranemiseta.

Me pakkusime välja täieliku ümbertöötluse Rustis. Klient oli skeptiline — Rustil on maine järsu õppimiskõveraga keelena, mis on aeglase arenduskiirusega. See postitus on aus ülevaade sellest, mis juhtus: mida me ehitasime, mida leidsime ja millised numbrid olid kuus kuud hiljem tootmiskeskkonnas.

Lähtepunkt: Node.js valideerimisteenus

Kõnealune teenus käsitles maksete valideerimist — kontrollides tehingupiiranguid, petturisignaale, kaupmehe reegleid ja kontojäägi eelkontrolle enne makse töötlejale saatmist. See töötas tippkoormusel ligikaudu 2000 päringut sekundis, rangete SLA nõuetega: p99 latentsus alla 50ms, tööaeg 99,99%.

Node.js implementatsioon oli neli aastat orgaaniliselt arenenud. See töötas, kuid näitas oma vanust:

  • Mälukasutus: Iga eksemplar vajas puhkeolekus 512MB mälu, suurenedes koormusel 900MB-ni. 12 eksemplari käitamine redundantsuse jaoks maksis EC2 pealt ainuüksi ~1400 dollarit kuus.
  • GC pausid: V8 prügikoguja põhjustas ettearvamatud latentsuspiigid — p99 oli tavaliselt ligikaudu 45ms, kuid tõusis mitu korda päevas üle 200ms.
  • CPU koormus: Üksik Node.js töötaja sai hallata ~300 päringut sekundis enne küllastumist. Nad vajasid 8 töötajat eksemplari kohta.

Koodibaas kandis endas ka nelja aasta jagu järkjärgulisi parandusi. Uute valideerimisreeglite lisamine nõudis üha keerulisemaks muutuva asünkroonsete tagasikanalite ahela ja jagatud oleku mõistmist.

Ümbertöötlus: Axum + Tokio stabiilsel Rustil

Kasutasime kaks nädalat uurimistöös — kaardistades iga valideerimisreegli, iga erijuhtu, iga veakoodi, mida olemasolev teenus tootis. See oli möödapääsmatu: käitumispariteeti pidi olema 100% enne liikluse lülitamist.

Uus pakk:

  • Axum (0.8) HTTP raamistikuna — ergonoomilne, async-native, suurepärane Tower vahevara ökosüsteem
  • Tokio asünkroonse käituskeskkonnana — lahingtestitud, etteaimatavad jõudlusomadused
  • sqlx andmebaasipääsuks — kompileerimisajal kontrollitud SQL päringud, ilma käitusaegsete üllatusteta
  • serde + serde_json serialiseerimiseks — tõhus deserialiseerimine valideerimiskoormustest
  • Radise klient (bb8 + redis-rs) hajutatud päringute piiramiseks ja reeglite vahemällu salvestamiseks

Valideerimisloogika ise modelleeriti koostatavate Rule omaduste (trait) konveierina. Iga reegel rakendab validate(payload: &PaymentPayload) -> Result<(), ValidationError> liidest. Uusi reegleid saab lisada omaduse rakendamise teel — varjatud sõltuvusi, tagasikanalite ahelaid või jagatud muudetavat olekut pole.

pub trait ValidationRule: Send + Sync {
    fn name(&self) -> &'static str;
    async fn validate(&self, payload: &PaymentPayload, ctx: &ValidationContext) -> Result<(), ValidationError>;
}

See disain muutis koodibaasi haldamise kordades lihtsamaks. Iga reegel on isoleeritud üksus, mida saab iseseisvalt testida, lisada ilma olemasolevat koodi muutmata ja deklaratiivselt järjestada.

Arenduse ajakava

Täielik ümberkirjutamine võttis kahe vanem-Rusti-inseneri meeskonnal aega 11 nädalat:

  • Nädalad 1–2: Uurimine, käitumise dokumenteerimine, testiraamistiku loomine (salvestasime 50 000 reaalset päringu/vastuse paari tootmisest regressioonitestimiseks)
  • Nädalad 3–7: Põhiteenuse rakendamine ja reeglite migratsioon
  • Nädalad 8–9: Koormustestimine, jõudluse häälestamine, Redise integreerimine
  • Nädalad 10–11: Kanaari-tüüpi juurutamine (5% liiklusest → 25% → 100%), monitooring, üleminek

Üks oluline märkus kiiruse kohta: Rusti kompilaator on range ja esialgne õppimisinvesteering on tõeline. Kuid leidsime, et pärast esimesi kolme nädalat arenduskiirus stabiliseerus. Tüübisüsteem püüdis kinni probleemid, mis Node.js-is oleksid olnud peened käitusaegsed vead — nulli haldamine, vigade levik, andmevõidujooksu tingimused paralleelsel töötlemisel. Mäluohutuse või paralleelsusega seotud tootmisintsidentide koguarv pärast üleminekut: null esimese kolme kuu jooksul.

Tulemused kuue kuu pärast

Me mõõtsime kõike. Siin on tulemused pärast kuuekuulist täielikku tootmiskoormust:

Latentsus:

  • p50: 0,8ms (oli 8ms) — 90% vähendamine
  • p99: 3ms (oli 45ms) — 93% vähendamine
  • p99,9: 7ms (oli 200ms+) — 97%+ vähendamine
  • GC pausid: täielikult kõrvaldatud

Mälukasutus:

  • Puhkeolekus: 48MB eksemplari kohta (oli 512MB) — 91% vähendamine
  • Tippkoormusel: 72MB eksemplari kohta (oli 900MB) — 92% vähendamine
  • Nõutud eksemplaride arv: vähendatud 12-lt 4-le (koos suurema puhvriga kui varem)

Läbilaskevõime:

  • Üksiku eksemplari tipp: 3400 päringut/sek (oli 300 päringut/sek töötaja kohta, ehk ~2400 koos 8 töötajaga)
  • Ühtegi päringu järjekorda ei täheldatud alla 2800 päringu/sek

Kulu:

  • EC2 arvutus (ainult maksete valideerimine): 420 dollarit kuus (oli 1400 dollarit) — 70% vähendamine
  • Koos koormusjaoturite, andmeedastuse ja CloudWatchi mõõdikute kokkuhoiuga: ~1100 dollarit igakuist kogusäästu

Mis tegi vahe sisse

Kolm tegurit ajendasid jõudluse kasvu:

1. Puudub prügikoguja. Rusti omandisüsteem vabastab mälu deterministlikult, kui väärtused väljuvad skoopist. Pole üleilmseid pause ega ootamatuid latentsuse piike. Range p99 SLA-ga teenuse jaoks on see transformatiivne.

2. Tõeline mitmelõimelisus. Node.js käitab igas töötajaprotsessis ühelõimelist sündmuste tsüklit. Tokio Rustis kasutab tööd varastavat lõimekogumit (work-stealing thread pool), mis küllastab kõik saadaolevad protsessorituumad. Üksik Rusti eksemplar asendas 8 Node.js töötajat.

3. Null-kulu serialiseerimine. Ökosüsteem serde deserialiseerib JSON-andmed otse tugevalt tüübitud struktuuridesse ilma vahepealse mäluerastuseta. Vana Node.js teenus tegi valideerimiskonveieri igal sammul tarbetuid objektide koopiaid.

Ausad kompromissid

Rust ei ole iga teenuse jaoks õige valik. Siin on see, mida me klientidele ausalt ütleme:

  • Suurem esialgne insenerikulu. Rusti arendus võtab Node.js-iga võrreldes kauem aega keeles uute inseneride jaoks. Eelarvestage 20–30% lisaaega meeskonna esimesele Rusti projektile.
  • Väiksem tööjõu turg. Rusti insenereid on vähem kui Node.js insenereid. See paraneb kiiresti, kuid see on reaalne asjaolu meeskonna planeerimisel.
  • Aeglasem iteratsioon mittekritiilistele teenustele. Lihtsa CRUD API jaoks minimaalsete jõudlusnõuetega on Rusti tüübisüsteemi koormus väiksema tasuvusega.

Valem, mida me kasutame: kui teenus käsitleb üle 500 päringu sekundis, omab rangeid latentsuse SLA-sid (p99 < 20ms) või töötab püsivalt tootmises kõrge mälueraldusega — siis Rust tasub end ära 6–12 kuuga pilvekulude kokkuhoiust.

Mida me õppisime

See projekt tugevdas kolme veendumust, mida võtame nüüd inseneripoliitikana:

  1. Salvestage tootmisliiklus enne ümberkirjutamist. Meie kogutud 50 000 päringu/vastuse paari olid kogu projekti kõige väärtuslikum artefakt. Need paljastasid erandjuhtumid, mida me vanast koodist lugedes poleks kunagi leidnud.

  2. Kanaari-tüüpi juurutused on finantsteenuste puhul möödapääsmatud. Töötamine 5% liiklusega nädal aega enne selle suurendamist andis meile kindlustunde, mis väärtustas end enam kui mistahes koormustestimine.

  3. Rusti kompileerimisaegsed garantiid välistavad terve kategooria tootmisintsidente. 6 kuud pärast üleminekut: null null-viite paanikat, null andmevõidujooksu tingimust, null mäluleket. Tüübisüsteem on nõudnud korrektsust, mis muidu dünaamiliselt tüübitud keeltes nõuaks ulatuslikku jälgimist.

Meeskondadele, kes kaaluvad Rusti kasutuselevõttu oma jõudlustkriitilistele teenustele: õppimiskõver on tõeline, tasuvus on tõeline ja kumulatiivsed eelised – vähem tootmise intsidente, madalamad pilvekulud, lihtsam arutlemine samaaegse koodi üle – teevad sellest õigetele probleemidele õige valiku.

Tootmise jõudlustulemused

Latentsuse vähendamine (p99)95%
Mälukasutuse vähendamine80%
AWS kulude kokkuhoid90%

* Tulemused 6 kuu tootmistöötlusest. Klient anonümiseeritud kokkuleppel.