Fəsil 01 · 125 dəq oxu

Software Architecture terminləri

Software Architecture terminləri

Sistemin strukturu, quality attribute-lar, architectural style-lar və qərarların sənədləşdirilməsi ilə bağlı terminlər.

Bu fəsildə: ADR · API gateway · API versioning · Architectural constraint · Architectural style · Artifact · At-least-once delivery · Back-of-the-envelope estimation · Backpressure · Batch processing · Blue-green deployment · Bounded context · Breaking change · Bulkhead · Burn rate · Cache invalidation · Caching · Canary release · CAP theorem · Capacity planning · Cargo cult · CDN · Change data capture · Chaos engineering · Circuit breaker · Cohesion · Compensating transaction · Consistency guarantee · Consistent hashing · Conway's law · Correlation ID · Coupling · CQRS · Data lake · Data residency · Database index · Database per service · Dead letter queue · Deployability · Deployment · Distributed monolith · Distributed tracing · Dual write · End-to-end consistency · Error budget · ETL · Event sourcing · Event-driven architecture · Eventual consistency · Exactly-once · Expand-contract · Exponential backoff · Failover · Failure domain · Feature flag · Fitness function · Functional requirement · Golden signals · Graceful degradation · GraphQL · gRPC · Health check · Hexagonal architecture · Hot partition · Hot spot · Idempotency · Idempotency key · Kappa architecture · Lambda architecture · Latency · Layered architecture · Load balancer · Load shedding · Message broker · Microservice · Modular monolith · Monolith · MTBF · MTTR · NoSQL · Object storage · Observability · OLTP və OLAP · Outage · Outbox pattern · PACELC · Pagination · Partial failure · Partition key · Pub/sub · QPS · Quality attribute · Quality attribute scenario · Rate limiting · Redundancy · Regulatory compliance · Release · Replication · Replication lag · REST · Retry · Reverse proxy · Rolling deployment · RPC · Saga · Scalability · Seam · Serverless · Sharding · SLA · SLI · SLO · Strangler pattern · Stream processing · Structured logging · Tail latency · Tenancy model · Throughput · Timeout · Trade-off · Two-phase commit · Virtual waiting room · Webhook · Windowing


ADR

Architecture Decision Record: bir architecture qərarını, onun context-ini, nəticələrini və rədd edilmiş alternativləri qeyd edən, kodla birlikdə saxlanılan qısa sənəd.

ADR hər əhəmiyyətli qərar üçün bir ədəd yazılan qısa sənəddir. Məqsəd sənəd istehsal etmək deyil, qərarın niyəsini onu verən insanlardan uzun yaşatmaqdır. Altı ay sonra komanda dəyişir, alternativlər unudulur və heç kim qəribə strukturun qəsdən olub-olmadığını bilmir.

Kanonik struktur beş hissədən ibarətdir:

  • Status — Proposed, Accepted, Deprecated və ya başqa ADR tərəfindən superseded.
  • Context — qərarı məcbur edən problem və .
  • Decision — konkret nə seçildi.
  • Consequences — həm müsbət, həm mənfi nəticələr.
  • Alternatives considered — nəyə baxıldı və niyə rədd edildi.

Praktikada ADR-lər repository-də, məsələn docs/adr/ qovluğunda saxlanılır, ardıcıl nömrələnir və pull request-lərdə kod kimi review olunur. Məsələn, "ADR-0007: event-lərin publish edilməsi üçün cədvəli" Postgres və Kafka arasında event itkisinin niyə baş verdiyini, hansı həllin seçildiyini və niyə rədd edildiyini qeyd edir.

Tipik səhvlər: yalnız faydaları yazmaq (bu, marketinqdir, ADR deyil), köhnə ADR-ləri redaktə edib tarixi silmək, yaxud hər xırda seçim üçün ADR yazıb vacib olanları gözdən qaçırmaq. Yaxşı test sadədir: qərarı geri qaytarmaq baha başa gəlirsə, ADR-ə layiqdir. isə ADR-i arzudan avtomatik yoxlanılan çevirir.

Əlaqəli terminlər: , , ,

API gateway

Client-lər ilə backend service-lər arasında tək giriş nöqtəsi; routing, , və termination kimi cross-cutting məsələləri bir yerdə həll edir.

API gateway bütün xarici request-lərin keçdiyi qapıdır. Onun dəyəri ondadır ki, hər service-in ayrıca təkrarlamalı olduğu işləri bir yerə toplayır:

  • Routing — request-i path və ya versiyaya görə düzgün service-ə ötürmək.
  • — yoxlamaq ki, service-lər bunu təkrar etməsin.
  • — client başına quota tətbiq etmək.
  • termination — şifrələməni kənarda bitirmək.
  • Request aggregation — bir client request-i üçün bir neçə service-i çağırıb cavabları birləşdirmək.

ilə fərq: request-i ötürür və bitirir, API gateway isə bunun üzərinə API anlayışlarını (consumer, , quota, versiya) əlavə edir.

Fərqli client-lərin ehtiyacları çox fərqlənəndə BFF (backend for frontend) pattern-i istifadə olunur: hər client növü üçün həmin frontend komandasına məxsus ayrıca, nazik gateway.

Əsas risk gateway-in yeni monolitə çevrilməsidir. Bir az transformation, bir az validation, sonra bütöv biznes qaydası. Nəticədə hər dəyişiklik gateway komandasından keçir və service boundary-ləri mənasını itirir. Gateway request-in necə gəldiyini bilməlidir, onun biznes baxımından nə demək olduğunu yox.

Ümumi səhv tək monolit və tək client üçün API gateway qurmaqdır. Bu halda o, heç bir problemi həll etmədən , əlavə failure nöqtəsi və ops yükü gətirir.

Əlaqəli terminlər: , , , ,

API versioning

API-nin idarə etmək üçün eyni anda bir neçə contract versiyasını dəstəkləmə praktikası.

API versioning köhnə consumer-ləri sındırmadan contract-ı dəyişməyə imkan verir: köhnə versiya işləməyə davam edir, yenisi paralel açılır, consumer-lər öz tempi ilə keçir.

Ən yaxşı versioning strategiyası versiyaya ehtiyac yaratmamaqdır. Additive dəyişikliklər (yeni optional field, yeni endpoint) client-lər tolerant reader-dirsə heç kimi sındırmır və yeni versiya tələb etmir. Versiya yalnız real üçün lazımdır.

Versiyanı daşımağın əsas yolları:

  • URL-də (/v2/orders) — görünən, sadə, cache-friendly; amma bütün API-ni birdən versiyalamağa təşviq edir.
  • Header-də (Accept: application/vnd.shop.v2+json) — URL-lər təmiz qalır, amma debugging və test çətinləşir.
  • Tarixə görə — client öz versiyasını tarixlə sabitləyir, server dəyişiklikləri ardıcıl transformasiyalarla tətbiq edir (Stripe yanaşması).

Seçimdən daha vacibi deprecation siyasətidir: köhnə versiya nə qədər yaşayır, consumer-lərə necə xəbər verilir (Deprecation və Sunset header-ləri, birbaşa əlaqə) və kimin hələ də onu çağırdığı necə izlənir.

Hyrum's law burada xüsusilə ağrılıdır: kifayət qədər consumer varsa, sənədləşdirilməmiş davranışlardan da kimsə asılı olacaq. Ona görə contract test-ləri sənədlərdən daha etibarlı qoruyucudur.

Ümumi səhv hər kiçik dəyişiklik üçün yeni versiya açmaqdır: hər paralel versiya ayrıca saxlanmalı, test edilməli və təhlükəsizlik yamaları almalıdır.

Əlaqəli terminlər: , , ,

Architectural constraint

Dizaynerin dəyişə bilmədiyi, artıq verilmiş qərar və ya şərt: mövcud stack, büdcə, deadline, hüquqi tələb kimi.

Constraint seçim sahəsini daraldan, müzakirə olunmayan şərtdir. fərqli olaraq onu optimallaşdırmaq və ya başqa şeylə dəyişmək olmur: ona sadəcə əməl edilir.

Constraint-lər adətən üç mənbədən gəlir:

  • Texniki — mövcud stack, inteqrasiya olunmalı legacy sistem, şirkətin vahid cloud provider-i, köhnə client versiyaları.
  • Biznes — büdcə, deadline, komandanın ölçüsü və bacarıqları.
  • Hüquqi — , GDPR kimi qaydalar, audit və data saxlama müddətləri.

Hüquqi constraint-lər ən bahalısıdır, çünki gec aşkarlananda architecture-u yenidən qurmağı tələb edir. Məsələn, tələbi tək qlobal database ideyasını dərhal istisna edir.

Constraint-i "üstünlük verilən seçim"dən ayırmaq üçün sadə test: "Bu pozulsa, nə olar və bunu kim deyir?" Cavabda konkret insan və konkret nəticə yoxdursa, bu constraint deyil, vərdiş və ya zövqdür və müzakirə oluna bilər. Saxta constraint-lər dizaynı lazımsız yerə məhdudlaşdırır, real olanlar isə qeyd olunmalıdır ki, gələcək komandalar onların səbəbini bilsin.

Ümumi səhv constraint-ləri yazmamaqdır: hamı onları "bilir", ta ki yeni architect gəlib onlardan birini pozan dizayn təklif edənə qədər.

Əlaqəli terminlər: , , ,

Architectural style

Component-lərin adlandırılmış, təkrarlanan düzülüşü və onların qarşılıqlı əlaqə qaydaları; məsələn , , , .

Architectural style sistemin "formasına" verilən addır: component-lər necə düzülür və bir-biri ilə hansı qaydalarla danışır. , , , , , pipes-and-filters və ən geniş yayılmış style-lardır.

Style-ların dəyəri ondadır ki, məlum paketləri ilə gəlirlər. "" dedikdə təcrübəli mühəndis dərhal həm producer-consumer , həm də görünməz control flow-u və düşünür. Bu, ortaq lüğət yaradır və müzakirəni sürətləndirir.

Təhlükəsi isə dəblə gəlməsidir. Komandalar tez-tez style-ı problemə görə deyil, konfrans çıxışlarına görə seçir. Nəticədə tək komandalı startap on iki işlədir və bütün vaxtı platform problemlərinə xərcləyir.

Praktikada style-lar compose olur. hər module-un içində struktur istifadə edə, reporting üçün isə pipeline-a event-lər emit edə bilər. Düzgün sual "hansı style?" deyil, "mənə hansı lazımdır və bu komanda üçün onları ən ucuz hansı style verir?" sualıdır.

Faydalı yoxlama: sisteminiz adlandırıldığı style-dadır, yoxsa əslində başqa bir şeydir? Çox vaxt " architecture" adı altında gizlənir.

Əlaqəli terminlər: , , , ,

Artifact

Build və ya training prosesinin nəticəsi olan, versiyalanan və edilən fayl: container image, binary, paket və ya model faylı.

Build artifact source koddan (və ya ML-də training-dən) yaranan, edilməyə hazır məhsuldur: Docker image, JAR faylı, npm paketi, statik bundle, yaxud serialize olunmuş model. Əsas prinsip: bir dəfə build et, hər yerə et. Eyni artifact staging-də test olunur və dəyişmədən production-a gedir.

Bu niyə vacibdir? Hər environment üçün ayrıca build etsəniz, test etdiyiniz şeylə production-da işləyən şey fərqli ola bilər: fərqli dependency versiyası, fərqli compiler flag-i. Immutable artifact bu fərqi aradan qaldırır və sadə edir — əvvəlki versiyanı yenidən etmək kifayətdir.

Praktikada artifact-lər registry-də saxlanılır (container registry, package repository, model registry) və commit , build nömrəsi və ya semantic version ilə işarələnir. Security baxımından artifact-lərin imzalanması və reproducible build-lər hücumlarına qarşı vacib qoruyucudur.

Məsələn, ML pipeline-ında hər train edilmiş model metric-ləri, , data versiyası və kod commit-i ilə birlikdə versioned artifact kimi saxlanılır ki, istənilən vaxt bir əmrlə geri qaytarılsın.

Qeyd: "Artifact" sözü başqa mənada işlənir — orada stimulusdan təsirlənən sistem hissəsi (məsələn, catalogue service) nəzərdə tutulur.

Əlaqəli terminlər: , , ,

At-least-once delivery

Mesajın heç vaxt itməyəcəyinə, amma bəzən bir dəfədən çox çatdırıla biləcəyinə zəmanət verən delivery semantics-i; consumer-lərin olmasını tələb edir.

Distributed sistemlərdə mesaj delivery-si üçün üç klassik zəmanət var: (itə bilər, təkrarlanmaz), at-least-once (itməz, təkrarlana bilər) və (nə itir, nə təkrarlanır). Praktikada ən çox at-least-once seçilir, çünki ya çox bahadır, ya da yalnız məhdud şəraitdə mümkündür.

At-least-once necə yaranır? Producer mesajı göndərir və acknowledgement gözləyir. Ack gəlməzsə — network kəsildiyinə, ya da consumer işləyib cavab verməmiş çökdüyünə görə — mesaj yenidən göndərilir. Nəticədə consumer eyni mesajı iki dəfə ala bilər.

Buna görə at-least-once delivery ilə consumer həmişə cüt gedir. Məsələn, relay process-i event-i Kafka-ya publish edir, amma sətri "göndərildi" kimi işarələməmiş çökərsə, restart-dan sonra eyni event-i yenidən göndərəcək. "Order ödənildi" event-ini iki dəfə emal edən billing consumer müştəridən iki dəfə pul çıxarmamalıdır.

Tipik həllər:

  • Hər event-ə unikal id vermək və emal olunmuş id-ləri saxlamaq.
  • Əməliyyatları təbii etmək ("balansı 100 et", "100 əlavə et" əvəzinə).
  • Database-də unique ilə duplicate-ləri rədd etmək.

Səhv: duplicate-lərin "nadir" olduğunu düşünüb onları nəzərə almamaq. Nadir hadisələr böyük həcmdə gündəlik hadisəyə çevrilir.

Əlaqəli terminlər: , , ,

Back-of-the-envelope estimation

Sadə fərziyyələr və yuvarlaq rəqəmlərlə load, storage və bandwidth-in tərtibini bir neçə dəqiqəyə təxmin etmək üsulu.

Back-of-the-envelope estimation-ın məqsədi dəqiq cavab yox, düzgün tərtibdir. 230 və ya 250 request/s fərq etmir, 200 və ya 200 000 isə tamamilə fərqli sistem deməkdir.

Tipik addımlar:

  1. DAU → — istifadəçi sayı × gündəlik əməliyyat / 86 400 (≈ 10^5).
  2. Average → peak — peak faktoru ilə vurmaq, adətən 2–3, hadisələrdə daha çox.
  3. Storage — gündəlik yeni data × saxlama müddəti × faktoru.
  4. Bandwidth — × cavab ölçüsü, ingress və egress ayrıca.

Məsələn, 10M DAU, hər istifadəçi gündə 2 foto (2 MB) yükləyir: 20M upload/gün ≈ 230 upload/s, gündə 40 TB, ildə təxminən 14.6 PB, üç ilə 44 PB. Bu rəqəm dərhal deyir ki, fotolar database-də saxlanmamalıdır.

Hesablamanı tez etmək üçün bir neçə rəqəmi bilmək kifayətdir: RAM-dan oxuma ~100 ns, SSD random read ~100 µs, datacenter daxilində round trip ~0.5 ms, region-lar arası ~50–150 ms; 2^10 ≈ 10^3, 1 il ≈ 3 × 10^7 saniyə.

Ümumi səhv yalnız nəticəni yazıb fərziyyələri itirməkdir. Fərziyyələr nəticədən daha dəyərlidir, çünki reallıq fərqli çıxanda nəyin səhv olduğunu məhz onlar göstərir.

Əlaqəli terminlər: , , ,

Backpressure

Yavaş consumer-in sürətli producer-ə "yavaşla" siqnalı verməsi mexanizmi; queue-ların və buffer-lərin sonsuz böyüməsinin qarşısını alır.

Producer saniyədə 1 000 message göndərir, consumer isə 800 emal edə bilir. Arada nə baş verir? Backpressure yoxdursa, fərq queue-da yığılır. Sonsuz queue problemi gizlədir: monitoring yaşıl görünür, xəta yoxdur, amma dəqiqələrdən saatlara qalxır, sonda isə memory və ya disk dolur və hər şey birdən yıxılır.

Backpressure aşağı axının tutumu barədə məlumatı yuxarı axına ötürür. Əsas variantlar:

  • Bloklamaq — bounded queue dolanda producer gözləyir. Sadədir, amma gözləmə yuxarı yayıla bilər.
  • Xəta qaytarmaq — producer dərhal rədd cavabı alır və özü qərar verir: , buffer, ya imtina.
  • Sürəti azaltmaq — consumer nə qədər qəbul edə biləcəyini bildirir (məsələn, reactive stream-lərdə və TCP flow control-da olduğu kimi).
  • Atmaq — vacib olmayan data (metrics, log-lar) üçün ən köhnə və ya ən yeni elementləri atmaq.

Backpressure və ilə yaxındır, amma fərqli yerdən gəlir: server-in qoyduğu sabit qaydadır, backpressure isə sistemin real vaxtdakı tutumunun siqnalıdır.

Asinxron arxitekturalarda queue-lar yükü hamarlamaq üçün əladır, amma yalnız müvəqqəti fərqləri. Davamlı fərqi heç bir queue udmur.

Ümumi səhv queue-ya yuxarı hədd qoymamaqdır: limitsiz queue overload-u həll etmir, sadəcə onu gecikdirir və görünməz edir.

Əlaqəli terminlər: , , , ,

Batch processing

Sərhədi məlum olan data toplusunu, məsələn bir günün bütün event-lərini, bir job daxilində emal edib nəticə çıxarmaq.

Batch processing data-nı gəldikcə yox, toplu halda emal edir: "dünənki bütün sifarişlər", "keçən ayın log-ları". Input-un sərhədi məlumdur, job başlayır, bitir və nəticə yazır.

İdeyanın klassik forması MapReduce-dur: data hissələrə bölünür, hər hissə paralel emal olunur, nəticələr key üzrə qruplaşdırılıb birləşdirilir. Spark kimi müasir alətlər eyni modeli yaddaşda və daha rahat API ilə icra edir. Warehouse-da işləyən SQL transform-ları da batch processing-dir.

Yaxşı batch job-un əsas xüsusiyyəti təkrarlana bilməsidir:

  • yazı. Job append etmir, partition-ı (məsələn, bir günü) tamamilə overwrite edir. İki dəfə işləsə də, nəticə eynidir.
  • Deterministik input. Tarix parametr kimi verilir, job "indiki vaxt"a baxmır.
  • Atomik dərc. Yarımçıq nəticəni heç kim oxumur.

Bu xüsusiyyətlər backfill-i, yəni keçmiş dövrlər üçün yenidən hesablamanı, sadə əməliyyata çevirir: bug düzəldilir, job son 90 gün üçün yenidən işlədilir.

Batch-in qiyməti freshness-dir: nəticə ən yaxşı halda job intervalı qədər köhnədir. Əvəzində əməliyyatı sadədir: yıxılan job sadəcə yenidən işlədilir.

Ümumi səhv batch job-u append ilə yazmaqdır: ilk duplicate yaranır və heç kim bunu dashboard-dakı rəqəm şişənə qədər görmür.

Əlaqəli terminlər: , , , , ,

Blue-green deployment

İki tam mühitlə işləyən strategiyası: yeni versiya boş mühitdə qaldırılır və traffic bir anda ona keçirilir; switch-i geri çevirməkdir.

Blue-green deployment-də eyni sistemin iki tam nüsxəsi var. Biri ("blue") hazırda production traffic-ini qəbul edir. Yeni versiya ikincisində ("green") qaldırılır, smoke test-lərdən keçir və hazır olanda , router və ya DNS bütün traffic-i bir anda green-ə yönəldir. Növbəti rollar dəyişir.

Nə verir:

  • Ani . Köhnə versiya hələ də işləyir, geri qayıtmaq switch-i çevirməkdir və saniyələr çəkir.
  • Yalnız bir versiya traffic alır. Rolling-dən fərqli olaraq istifadəçi iki versiya arasında "atılmır".
  • Real mühitdə yoxlama. Green traffic almadan əvvəl production konfiqurasiyası ilə test oluna bilər.

Nəyə başa gəlir:

  • İkiqat infrastructure, heç olmasa switch anında. Cloud-da bu müvəqqəti xərcdir, öz data center-də isə daimi.
  • Database paylaşılır. İki mühit adətən eyni database-ə qoşulur, ona görə schema dəyişikliyi hər iki versiya ilə uyğun olmalıdır. Əks halda ani vədi pozulur. Burada pattern-i lazımdır.
  • Böyük . Switch hamını bir anda yeni versiyaya keçirir. Green-də test zamanı görünməyən problem dərhal bütün istifadəçilərə çatır.

Traffic az olduğu üçün statistik nəticə çıxmayanda və ya ani, bütöv keçid lazım olanda uyğundur.

Ümumi səhv database-i unutmaqdır: switch geri çevrilə bilər, amma green-in artıq tətbiq etdiyi breaking schema dəyişikliyi geri çevrilmir.

Əlaqəli terminlər: , , , , ,

Bounded context

Domain-driven design anlayışı: daxilində model-in və onun lüğətinin ardıcıl və birmənalı olduğu ərazi; eyni söz fərqli context-lərdə fərqli məna daşıya bilər.

Bounded context domain-driven design-ın ən praktik ideyasıdır. Böyük təşkilatda "Customer", "Order" və ya "Product" kimi sözlər hər departamentdə bir az fərqli məna daşıyır. Bunların hamısını bir universal model-ə sığışdırmaq cəhdi heç kimi qane etməyən, hər yerdən field yığılmış nəhəng obyekt yaradır.

Bounded context deyir: model-in yalnız müəyyən sərhəd daxilində ardıcıl olmasına razı olun. Billing context-ində Customer-in credit limit-i və ödəniş metodları var. Marketing context-ində eyni Customer-in segment-i və preference-ləri var. Bunlar iki ayrı model-dir və bu normaldır.

Praktikada bounded context-lər decomposition üçün ən yaxşı namizədlərdir. Hər context ayrıca module, sonra lazım olarsa ayrıca service olur. Context-lər arasında əlaqə explicit tərcümə ilə qurulur: billing marketing-in Customer obyektini birbaşa istifadə etmir, öz ehtiyac duyduğunu API və ya event vasitəsilə alır.

Context sərhədini tapmaq üçün siqnallar:

  • Eyni söz fərqli komandalarda fərqli sual doğurur.
  • Change history-də fayllar qrup halında dəyişir.
  • Fərqli komandalar fərqli invariant-lara cavabdehdir.

Tipik səhv bounded context-i technical layer-lə qarışdırmaqdır. "Repositories" context deyil; "Ordering" context-dir.

Əlaqəli terminlər: , , , ,

Breaking change

Contract-a edilən və mövcud consumer-lərin işini dəyişiklik etmədən pozan dəyişiklik: field-in silinməsi, tipin dəyişməsi və ya mənasının dəyişməsi.

Breaking change API və ya hər hansı contract-da elə dəyişiklikdir ki, ondan sonra mövcud consumer-lər öz kodlarını dəyişmədən düzgün işləyə bilmir. Əksi additive dəyişiklikdir: yeni optional field və ya yeni endpoint əlavə etmək heç kimi pozmur.

Tipik breaking change-lər:

  • Field və ya endpoint silmək, adını dəyişmək.
  • Field-in tipini dəyişmək (string-dən number-ə).
  • Optional field-i required etmək.
  • Error code-larını və ya status mənalarını dəyişmək.
  • Semantics-i səssizcə dəyişmək — ən amansız növ. Field-in adı eyni qalır, amma mənası dəyişir və heç bir compiler, heç bir schema validation bunu tutmur.

Breaking change-lər tez-tez sızan lüğətin nəticəsidir: interface daxili data model-i olduğu kimi açırsa, daxili refactoring avtomatik olaraq xarici pozmaya çevrilir.

Praktik yanaşma: breaking change-ləri nadir, explicit və migration dövrü ilə edin. Yeni versiya (v2) əlavə edin, köhnəni deprecated elan edin, consumer-lərin keçidini izləyin və yalnız sonra köhnəni söndürün. Consumer tərəfdə tolerant reader pattern-i kömək edir: tanımadığınız field-ləri nəzərə almayın, çatışmayan optional field-lərə dözün.

Contract test-ləri (məsələn, consumer-driven contract-lar) breaking change-i əvvəl tutmağın etibarlı yoludur.

Əlaqəli terminlər: , ,

Bulkhead

Resursların dependency-lər və ya iş növləri arasında izolyasiya olunmuş bölmələrə ayrılması ki, bir bölmədəki problem digərlərinə yayılmasın.

Bulkhead termini gəmi dizaynından gəlir: gövdə su keçirməyən bölmələrə bölünür ki, bir deşik bütün gəmini batırmasın. Software-də bu, resurs izolyasiyası deməkdir.

Tipik problem: service bütün dependency-lər üçün bir ortaq thread pool və ya connection pool istifadə edir. Az vacib dependency, məsələn recommendation service, yavaşlayır. Onun çağırışları cavab gözləyərək bütün thread-ləri tutur və checkout kimi vacib əməliyyatlar da xidmət ala bilmir. Bir yavaş dependency hamını boğur.

Bulkhead ilə hər dependency-yə və ya iş növünə ayrıca pool, semaphore və ya concurrency limiti verilir. Recommendation-lar üçün ayrılmış 20 thread dolanda yalnız recommendation çağırışları xəta verir, qalanları normal işləyir.

Bulkhead müxtəlif səviyyələrdə tətbiq olunur:

  • process daxilində ayrı thread və connection pool-ları;
  • vacib və vacib olmayan traffic üçün ayrı instance qrupları;
  • böyük miqyasda istifadəçiləri müstəqil cell-lərə bölmək, bu da kiçildir.

Qiyməti səmərəlilikdir: bir bölməyə ayrılmış resurs boş qalanda başqası onu istifadə edə bilmir. Pool ölçülərini real yükə görə seçmək və monitor etmək lazımdır.

Ümumi səhv hamının ortaq bir connection pool istifadə etdiyini yalnız incident zamanı kəşf etməkdir.

Əlaqəli terminlər: , , , ,

Burn rate

pəncərəsinə nisbətən nə sürətlə xərcləndiyini göstərən əmsal; burn rate 1 budget-in düz pəncərənin sonunda tükənəcəyi deməkdir.

Burn rate cari error rate-in icazə verdiyi error rate-ə nisbətidir. 99.9% 0.1% deməkdir. Hazırda request-lərin 0.1%-i uğursuzdursa, burn rate 1-dir və budget düz pəncərənin sonunda bitəcək. Error rate 1.44%-dirsə, burn rate 14.4-dür.

Nümunə: 30 günlük pəncərə 720 saatdır. Son 1 saatda budget-in 2%-i yanıbsa, burn rate = 0.02 / (1/720) = 14.4 olur. Bu sürətlə bütün aylıq budget 50 saatda tükənəcək.

Burn rate alerting səbəbə görə yox, simptoma görə alert verir: CPU-nun yüksək olması deyil, istifadəçi təcrübəsinin təhdid edəcək sürətlə pisləşməsi alert yaradır. Praktikada multi-window alert-lər istifadə olunur:

  • Sürətli yanma — məsələn, 1 saatlıq və 5 dəqiqəlik pəncərələrin ikisində də burn rate 14.4-dən yüksəkdir: page göndərilir. Qısa pəncərə problemin hələ davam etdiyini təsdiqləyir.
  • Yavaş yanma — məsələn, 6 saatda burn rate 6: iş saatlarında baxılacaq ticket yaradılır.

Bu yanaşma alert fatigue-u azaldır, çünki istifadəçiyə təsir etməyən dalğalanmalar alert yaratmır, ciddi problemlər isə tez aşkarlanır.

Ümumi səhv sabit error rate həddinə görə alert qurmaqdır. Belə hədd ya qısa dalğalanmalarda gecə adamı oyadır, ya da yavaş, amma ay boyu davam edən budget itkisini görmür.

Əlaqəli terminlər: , , , ,

Cache invalidation

Mənbə data dəyişəndə köhnə nüsxəni silmək və ya yeniləmək; məşhur zarafata görə computer science-in iki çətin problemindən biri.

Phil Karlton-a aid edilən zarafata görə, computer science-də iki çətin şey var: cache invalidation və adlandırma. Çətinlik ondadır ki, data iki yerdə yaşayır və onları sinxron saxlamaq distributed sistem problemidir.

Əsas yanaşmalar:

  • TTL (time to live) — data müəyyən müddətdən sonra avtomatik köhnəlmiş sayılır. Ən sadədir, amma kompromisdir: qısa TTL hit ratio-nu salır, uzun TTL köhnə data göstərir.
  • Explicit invalidation — data yazılanda kod müvafiq key-i silir. Dəqiqdir, amma hər yazı yolunu xatırlamaq lazımdır və birini unutmaq asandır.
  • Event-based invalidation — data dəyişikliyi event yaradır, isə ona reaksiya verir. bunun etibarlı formasıdır.
  • Versioned key-lər — data dəyişəndə yeni key yaranır, köhnə key sadəcə istifadəsiz qalıb vaxtı keçir. versioned URL-lər eyni ideyadır.

Tipik race condition: bir request köhnə dəyəri database-dən oxuyur, bu arada başqası yazıb silir, sonra birinci request köhnə dəyəri yazır. Nəticədə uzun müddət səhv qalır.

Database-ə yazıb sonra invalidation mesajı göndərmək də həll etdiyi problemidir: ikinci addım fail olarsa, köhnə qalır.

Ümumi səhv yalnız invalidation-a güvənib TTL qoymamaqdır. TTL təhlükəsizlik toridir: invalidation bug-ı olsa belə, köhnə data əbədi qalmır.

Əlaqəli terminlər: , , , ,

Caching

Bahalı hesablamanın və ya oxumanın nəticəsini daha sürətli yerdə saxlayıb təkrar istifadə etmək; performance və üçün tactic, qiyməti isə consistency-dir.

Cache bahalı nəticəni ucuz yerdə saxlayır. Hər cache hit database-dən və ya downstream service-dən götürülmüş bir yükdür. Cache bir neçə səviyyədə yaşaya bilər: client-də, , service-in öz yaddaşında (in-memory) və ayrıca distributed cache-də (Redis, Memcached).

Əsas strategiyalar:

  • Cache-aside — application cache-ə baxır, miss olanda database-dən oxuyub cache-ə yazır. Ən sadə və ən geniş yayılmışıdır.
  • Read-through — eyni məntiq, amma miss-i cache layer-in özü idarə edir.
  • Write-through — yazı eyni anda həm cache-ə, həm database-ə gedir. Cache təzə qalır, yazılar yavaşlayır.
  • Write-behind — yazı əvvəlcə cache-ə, sonra asynchronous database-ə gedir. Sürətlidir, amma cache yıxılsa data itə bilər.

Data-nın köhnəlməsi TTL ilə və ya açıq ilə idarə olunur. Populyar key-in TTL-i bitəndə minlərlə request eyni anda database-ə gedə bilər; bu, cache stampede (thundering herd) adlanır. Qarşısını key başına lock, TTL-ə və ya vaxtından əvvəl yeniləmə ilə almaq olar.

Cache əlavə etdiyiniz andan sistem ilə yaşayır: istifadəçi bir müddət köhnə data görə bilər. Bu, bilərəkdən qəbul edilməli .

Ümumi səhv hit ratio-nu ölçməməkdir. Aşağı hit ratio-lu cache performance qazandırmır, sadəcə əlavə hop, əlavə failure nöqtəsi və invalidation bug-ları gətirir.

Əlaqəli terminlər: , , , , ,

Canary release

Yeni versiyanı əvvəlcə traffic-in kiçik faizinə verib, köhnə versiya ilə müqayisə edərək avtomatik genişləndirən və ya geri qaytaran strategiyası.

Canary release kiçiltmək üçün tactic-dir. Yeni versiya əvvəlcə traffic-in 1–5%-ini alır, qalan istifadəçilər köhnə versiyada qalır. Metrikalar normaldırsa, pay mərhələlərlə artırılır: 5%, 25%, 50%, 100%. Səhv build beləliklə istifadəçilərin yalnız kiçik hissəsinə çatır və kiçik hissəsini yeyir.

əl ilə prosesdən etibarlı tactic-ə çevirən canary analysis-dir. Yeni versiyanın (error rate, , saturation) eyni anda işləyən köhnə versiyanın, yəni baseline-ın ilə müqayisə olunur. Mütləq hədlər yox, baseline ilə fərq əsas götürülür, çünki normal göstəricilər günün saatından asılı dəyişir. Fərq icazə veriləndən böyükdürsə, rollout insan gözləmədən dayanır və geri qaytarılır.

Şərtlər:

  • Kifayət qədər traffic. Az traffic-li service-də 1% statistik olaraq heç nə demir.
  • Sticky routing. İstifadəçi bir versiyaya bağlanmalıdır, əks halda hər request-də fərqli davranış görür.
  • Compatibility. İki versiya eyni anda işlədiyi üçün API və schema hər ikisi ilə uyğun olmalıdır.
  • Yaxşı SLI-lar. Pisləşməni ölçə bilmirsinizsə, yalnız yavaş .

Machine Learning kontekstində model keyfiyyətini yoxlamaq üçün istifadə olunur. Burada vurğu texniki sağlamlığındadır.

Ümumi səhv sadəcə faiz kimi qurmaq, amma avtomatik analysis və qurmamaqdır: o zaman problem yenə də insan qrafikə baxana qədər yayılır.

Əlaqəli terminlər: , , , , , ,

CAP theorem

Distributed sistemdə network partition baş verəndə consistency ilə arasında seçim etməli olduğunuzu deyən teorem.

CAP teoremi üç xüsusiyyətdən danışır: Consistency (hər read ən son write-ı görür), (hər request cavab alır) və Partition tolerance (node-lar arasında əlaqə kəsiləndə sistem işləməyə davam edir). Məşhur "üçündən ikisini seçin" ifadəsi yanlış sitatdır.

Düzgün oxunuş belədir: distributed sistemdə network partition-lar qaçılmazdır, ona görə P seçim deyil. Partition baş verəndə isə seçim qalır: ya bəzi request-ləri rədd edirsiniz (consistency-ni qoruyursunuz), ya da köhnəlmiş və ya ziddiyyətli data riski ilə cavab verirsiniz ( qoruyursunuz).

Praktik dərs: bu seçimi bütün sistem üçün yox, hər data set üçün edin. Məsələn:

  • Bank hesabının balansı — partition zamanı əməliyyatı rədd etmək ikiqat xərcləməkdən yaxşıdır (CP).
  • Sosial şəbəkədə like sayı — bir neçə saniyə köhnə rəqəm göstərmək xəta mesajından yaxşıdır (AP).

Partition olmayan normal vaxtlarda da oxşar var: ilə consistency arasında (bu, adlanır). Güclü consistency adətən replica-lar arasında koordinasiya tələb edir və hər write-ı yavaşladır.

Tipik səhv CAP-ı database seçimi üçün marketinq etiketi kimi istifadə etməkdir. Əsl sual budur: işlər pis gedəndə bu data üçün hansından imtina etməyə hazırıq?

Əlaqəli terminlər: , , ,

Capacity planning

Gözlənilən load, data həcmi və artım üçün lazım olan resursları (compute, storage, bandwidth) əvvəlcədən hesablama və planlaşdırma prosesi.

Capacity planning "nə qədər lazımdır və nə vaxt?" sualına cavab verir. Giriş məlumatları istifadəçi sayı, istifadə nümunələri, data ölçüsü və artım tempidir. Çıxış isə , storage, bandwidth və onları daşımaq üçün lazım olan resurslardır.

Proses adətən iki mərhələdən ibarətdir:

  • Dizayn zamanı — hesablama ilə tərtibi tapmaq: yüz request/s, yoxsa yüz min? Bir neçə GB, yoxsa petabyte? Bu, architecture qərarlarını məcbur edir, məsələn, binary data-nı çıxarmağı və ya .
  • İstismar zamanı — fərziyyələri real metrikalarla müqayisə etmək, artım trendini izləmək və resursları limitə çatmazdan əvvəl artırmaq.

Yaxşı capacity plan rəqəmlərlə yanaşı fərziyyələri də saxlayır: peak faktoru, faktoru, saxlama müddəti, orta obyekt ölçüsü. Fərziyyə dəyişəndə nəticəni yenidən hesablamaq bir neçə dəqiqə çəkir.

Plan həm də nəyin hələ lazım olmadığını göstərir. Peak-də 50 olan sistem üçün və ayrıca layer-i yalnız mürəkkəblik əlavə edir.

Ümumi səhv capacity planning-i bir dəfəlik iş saymaqdır: launch-dan əvvəl hesablanıb unudulmuş rəqəmlər bir il sonra artıq heç nəyi əks etdirmir.

Əlaqəli terminlər: , , ,

Cargo cult

Bir texnikanı və ya strukturu nə üçün lazım olduğunu anlamadan, sadəcə uğurlu şirkətlər istifadə etdiyi üçün təkrarlamaq.

Ad İkinci Dünya Müharibəsindən sonrakı bir hekayədən gəlir. Sakit okean adalarında yaşayanlar hərbi təyyarələrin yük gətirdiyini görmüşdülər. Təyyarələr gedəndən sonra bəziləri ağacdan uçuş zolağı, qüllə və qulaqlıq düzəltdilər, ki təyyarələr yenidən gəlsin. Forma eyni idi, amma təyyarələri gətirən səbəb orada yox idi.

Software-də bu belə görünür: "Netflix işlədir, biz də işlədək." "Google monorepo-da işləyir, biz də keçək." "Böyük şirkətlər Kafka işlədir, bizə də lazımdır." Həmin şirkətlər bu seçimləri öz problemlərinə görə ediblər: minlərlə mühəndis, nəhəng traffic, xüsusi tələblər. Kiçik komandada eyni struktur yalnız xərcini gətirir, faydasını yox.

Cargo cult-u tanımağın sadə yolu bir sual verməkdir: bu struktur hansı xidmət edir və bizdə həmin problem varmı? Cavab "hamı belə edir" və ya "best practice-dir" olursa, bu, xəbərdarlıq işarəsidir.

Bu, başqalarının təcrübəsindən öyrənməmək demək deyil. Fərq ondadır: texnikanı onun həll etdiyi problemlə birlikdə götürürsünüz. Problem sizdə də varsa, texnika uyğundur. Yoxdursa, sadəcə mürəkkəblik əlavə edirsiniz.

yazmaq cargo cult-a qarşı yaxşı müdafiədir: "niyə" bölməsini doldurmaq məcburiyyəti səbəbi olmayan qərarları üzə çıxarır.

Əlaqəli terminlər: , , , ,

CDN

Content-i istifadəçiyə coğrafi cəhətdən yaxın edge server-lərdə paylanmış şəbəkə; azaldır və origin-in yükünü götürür.

CDN dünyanın müxtəlif yerlərində yerləşən edge server-lərdən ibarət şəbəkəsidir. İstifadəçi faylı origin server-dən deyil, ona ən yaxın edge-dən alır. Bu, iki attribute-ə xidmət edir: (fiziki məsafə qısalır) və origin-in (request-lərin böyük hissəsi origin-ə heç çatmır).

Statik content (şəkil, JS, CSS, video) CDN üçün ideal namizəddir. Dinamik content də mümkündür: qısa TTL ilə API cavabları, edge-də termination və edge-də işləyən kod. Əsas metric hit ratio-dur: request-lərin neçə faizi origin-ə getmədən cavablandırılır.

Invalidation burada da çətindir. Purge API mövcuddur, amma dəyişikliyin bütün edge-lərə yayılması vaxt aparır. Daha etibarlı yanaşma versioned URL-lərdir: fayl adına content əlavə olunur (məsələn, app.3f9a2c.js). Yeni versiya yeni URL deməkdir, köhnə fayl isə dəyişmir. Bu halda faylları çox uzun TTL ilə və purge-dan imtina etmək olur.

Risk tərəfi: istifadəçiyə xas cavabların səhv key ilə bir istifadəçinin data-sını başqasına göstərə bilər. Bu, performance problemi deyil, security incident-idir.

Ümumi səhv CDN-i yalnız statik fayllar üçün düşünüb Cache-Control header-lərini təsadüfə buraxmaqdır. CDN-in nəyi və nə qədər saxlayacağını origin-in göndərdiyi header-lər müəyyən edir.

Əlaqəli terminlər: , , ,

Change data capture

Database-in transaction log-unu oxuyaraq hər insert, update və delete-i event stream-ə çevirən texnika; məsələn Debezium.

Change data capture (CDC) database-də baş verən dəyişiklikləri application kodunu dəyişmədən tutub başqa sistemlərə ötürmək üsuludur. Database onsuz da hər dəyişikliyi öz log-una yazır (Postgres-də WAL, MySQL-də binlog). CDC aləti bu log-u oxuyur və hər sətir dəyişikliyini event kimi, məsələn Kafka topic-inə, publish edir.

Niyə istifadə olunur? Application-ın həm database-ə, həm də yazması "" problemi yaradır: biri uğurlu, digəri uğursuz olanda data ayrışır. CDC ilə application yalnız database-ə yazır, event-lər isə log-dan etibarlı şəkildə çıxarılır. Bu, alternativ və ya onun tamamlayıcısıdır — Debezium cədvəlini oxumaq üçün də istifadə edilə bilər.

Tipik istifadə halları:

  • Search index-ini və ya database ilə sinxron saxlamaq.
  • real-time .
  • extraction zamanı köhnə cədvəllərin dəyişikliklərini yeni service-ə axıtmaq.

Trade-off-lar: CDC əlavə operational component-dir (connector-lar, offset idarəsi, schema dəyişiklikləri). Ən böyük risk isə odur ki, raw cədvəl dəyişiklikləri publish edəndə daxili schema faktiki olaraq public contract-a çevrilir və hər cədvəl refactoring-i downstream consumer-ləri poza bilər. Buna görə CDC-ni domain event-ləri ilə, yaxud cədvəli ilə birləşdirmək daha təhlükəsizdir.

Əlaqəli terminlər: , , ,

Chaos engineering

Sistemə qəsdən, nəzarətli şəkildə failure yeridərək fault tolerance mexanizmlərinin real şəraitdə işlədiyini yoxlayan eksperimental intizam.

Chaos engineering , , və kağız üzərində yox, real şəraitdə işlədiyini yoxlamaq üsuludur. Heç vaxt işə düşməyən bərpa mexanizmi adətən ilk dəfə lazım olanda sınır. Netflix-in Chaos Monkey-i, production-da təsadüfi instance-ları söndürən alət, bu yanaşmanı məşhurlaşdırıb.

Məqsəd təsadüfi sındırmaq deyil, eksperimentdir:

  • Steady state. Sistemin normal halı ilə təyin olunur, məsələn checkout uğur faizi.
  • Hipotez. "Bir zone itəndə steady state pozulmayacaq."
  • Kiçik . Bir instance ilə, staging-də, sonra production traffic-inin kiçik hissəsində başlanır. Eksperimenti dərhal dayandıran abort şərti olur.
  • Nəticə. Hipotez təsdiqlənmirsə, zəif nöqtə real əvvəl tapılıb.

Game day başlamağın ən ucuz yoludur: komanda planlı şəkildə failure ssenarisini canlandırır, məsələn database primary-ni söndürür, və alert-lərin, runbook-ların, və on-call prosesinin necə işlədiyini birlikdə izləyir. Bu, texnikanı olduğu qədər insanları da yoxlayır.

İlkin şərt yaxşı : nə baş verdiyini görə bilmirsinizsə, eksperiment yox, sadəcə etmiş olursunuz. xərclənmiş service-də eksperimentləri dayandırmaq da ağlabatandır.

Ümumi səhv chaos engineering-i production-dan başlamaqdır: əvvəl hipotez, monitoring və abort mexanizmi, sonra kiçik eksperiment, yalnız sonra production.

Əlaqəli terminlər: , , , , , ,

Circuit breaker

Xarab dependency-yə çağırışları müvəqqəti dayandıran mexanizm; xəta faizi həddi keçəndə gözləmək əvəzinə dərhal xəta qaytarır.

Circuit breaker elektrik sigortasından gəlir: problem olanda dövrəni açır ki, zərər yayılmasın. Software-də o, dependency-yə edilən çağırışları izləyir və üç vəziyyətdə olur:

  • Closed — normal rejim. Çağırışlar keçir, xətalar sayılır.
  • Open — xəta faizi və ya sayı həddi keçib. Çağırışlar dependency-yə getmir, dərhal xəta qaytarılır.
  • Half-open — müəyyən vaxtdan sonra az sayda sınaq request-i buraxılır. Uğurlu olsalar breaker bağlanır, olmasalar yenidən açılır.

Əsas fayda fail fast-dır. Xarab və ya yavaş dependency-ni hər dəfə qədər gözləmək thread-ləri və connection-ları tutur və yavaşlığı yuxarı ötürür. Açıq breaker millisaniyələr içində cavab verir. Əlavə olaraq, dependency-yə yük gəlmir və o, bərpa olunmaq üçün vaxt qazanır.

Circuit breaker ilə birlikdə tam mənasını alır: breaker açıq olanda data, default cavab və ya gizlətmək kimi alternativ olmalıdır.

Trade-off-lar: hədlər çox həssasdırsa, breaker qısa dalğalanmada sağlam dependency-ni kəsir. Az traffic-də statistika etibarsızdır. Breaker hər instance-da ayrıca saxlanılırsa, instance-lar dependency-nin vəziyyəti haqqında fərqli qərarlar verə bilər.

Ümumi səhv circuit breaker qurmaqdır: o zaman o, sadəcə daha sürətli xəta mənbəyidir.

Əlaqəli terminlər: , , , ,

Cohesion

Boundary daxilindəki şeylərin birlikdə dəyişmə dərəcəsi: cohesive module-a tək bir biznes səbəbi adətən yalnız onun özünə toxunur.

Cohesion module, class və ya service daxilindəki elementlərin bir-birinə nə qədər aid olduğunu ölçür. Ən praktik tərif belədir: boundary daxilindəki şeylər birlikdə dəyişirmi? Əgər endirim qaydalarındakı dəyişiklik yalnız pricing module-una toxunursa, o module cohesive-dir.

"High cohesion, " prinsipi o qədər təkrarlanıb ki, mənasını itirib, amma o, decomposition-un əsas məqsədidir. Cohesion boundary-nin içinə, isə boundary-lər arasına baxır. Yüksək cohesion dəyişiklikləri lokal saxlayır; aşağı cohesion isə bir module-u "müxtəlif şeylər qutusuna" çevirir, hər dəyişiklik orada başqa bir şeyi sındırmaq riski daşıyır.

Ən geniş yayılmış aşağı cohesion nümunəsi technical layer-ə görə bölmədir: "controllers", "services", "repositories" package-ləri. Burada bir package-dəki fayllar bir-biri ilə heç bir biznes əlaqəsi olmadan yalnız texniki rola görə bir yerdədir, və hər bütün package-lərə toxunur. Domain-ə görə bölmə (ordering, inventory, billing) daha yüksək cohesion verir.

Ölçmək üçün diaqrama yox, change history-yə baxın. Son əlli pull request-də hansı fayllar birlikdə dəyişir? Onlar eyni module-da olmaq istəyir.

Səhv: cohesion-u "kiçik module" ilə qarışdırmaq. Kiçik, amma bir-birini daim çağıran module-lar aşağı cohesion və yüksək birləşməsidir.

Əlaqəli terminlər: , , ,

Compensating transaction

artıq commit olunmuş addımın biznes nəticəsini əks hərəkətlə tarazlayan transaction; deyil, semantic undo-dur.

Database dəyişikliyin heç vaxt olmadığını iddia edir. isə bu mümkün deyil: addım artıq commit olunub və digər service-lər və ya istifadəçi onun nəticəsini görmüş ola bilər. Compensating transaction həmin addımın biznes baxımından əksini edir: ödəniş bloklanıbsa azad edir, məhsul rezerv olunubsa rezervi buraxır, order yaradılıbsa onu ləğv edilmiş kimi işarələyir.

Bu, semantic undo-dur və iz qoyur. Refund müştərinin bank çıxarışında ikinci əməliyyat kimi görünür. Bəzi addımları isə ümumiyyətlə geri qaytarmaq olmur: göndərilmiş e-poçtu geri çağırmaq mümkün deyil, yalnız üzrxahlıq e-poçtu göndərmək olar.

Praktik qaydalar:

  • Hər addımını dizayn edəndə onun compensation-ını da yazın.
  • Geri qaytarılmayan addımları (bildiriş, fiziki göndəriş) sonuna, uğursuz ola biləcək hər şeydən sonraya qoyun.
  • Compensation-lar olmalıdır, çünki onlar da olunur.
  • Compensation özü də uğursuz ola bilər; bunun üçün , alert və son çarə kimi manual müdaxilə planı lazımdır.

Ümumi səhv compensation-ı texniki kimi düşünməkdir: o, biznes əməliyyatıdır və onun mənasını product komandası ilə birlikdə müəyyən etmək lazımdır.

Əlaqəli terminlər: , , ,

Consistency guarantee

Sistemin oxuma zamanı nə görəcəyiniz barədə verdiyi vəd: həmişə ən son yazılanı, yoxsa bir az köhnəlmiş, amma sonda düzələn data-nı.

Data bir neçə yerdə saxlananda (, , başqa service) sual yaranır: indi yazdığım dəyəri dərhal oxuya bilərəmmi? Consistency guarantee bu sualın cavabıdır. O, sistemin istifadəçiyə verdiyi müqavilədir.

Ən çox rast gəlinən səviyyələr, güclüdən zəifə:

  • Strong consistency. Write tamamlanandan sonra hər kəs yeni dəyəri görür. Sistem tək bir nüsxə kimi davranır.
  • Read-your-writes. Siz öz yazdığınızı dərhal görürsünüz, başqaları isə bir az gec görə bilər.
  • . Hamı yeni dəyəri sonda görəcək, amma qısa müddət köhnə dəyər görünə bilər.

Güclü vəd daha baha başa gəlir. Replica-lar bir-biri ilə razılaşmalıdır, bu da artırır və network problemi zamanı azaldır ( və məhz bu barədədir). Zəif vəd sürətli və dayanıqlıdır, amma application köhnə data ilə işləməyi bacarmalıdır.

Məsələn: bank balansı üçün strong consistency lazımdır, profil şəkli üçün kifayətdir, istifadəçinin öz şərhini yazandan sonra görməsi üçün isə read-your-writes yetir.

Ümumi səhv bütün sistem üçün bir səviyyə seçməkdir. Doğru yanaşma hər data növü üçün ayrıca soruşmaqdır: köhnə dəyər görünsə nə itiririk? Bu seçim database və qərarlarını müəyyən etdiyi üçün sonradan dəyişmək çətindir.

Əlaqəli terminlər: , , , , ,

Consistent hashing

Key-ləri və node-ları eyni halqası üzərində yerləşdirən üsul; node əlavə və ya silinəndə key-lərin yalnız kiçik hissəsi yerini dəyişir.

Sadə (key) mod N düsturundan istifadə edir. Problem N dəyişəndə üzə çıxır: 10 node-dan 11-ə keçəndə key-lərin təxminən 90%-i başqa node-a düşür. üçün bu, bütün birdən boşalması, database üçün isə nəhəng data köçürməsi deməkdir.

Consistent hashing həm key-ləri, həm də node-ları eyni dairəvi fəzasına (halqaya) yerləşdirir. Hər key halqada saat əqrəbi istiqamətində rastlaşdığı ilk node-a aiddir. Yeni node əlavə ediləndə o, yalnız qonşusunun key-lərinin bir hissəsini götürür; təxminən 1/N key köçür.

Praktikada hər fiziki node halqada çoxlu virtual node kimi yerləşdirilir. Bu, yükü daha bərabər paylayır və node düşəndə onun payını bir qonşuya yox, bir çox node-a yayır. Güclü node-a daha çox virtual node verib çəkini də tənzimləmək olar.

Cassandra, DynamoDB, distributed client-ləri və bəzi bu üsuldan istifadə edir.

Ümumi səhv consistent hashing-in həll etdiyini düşünməkdir. O, key-ləri bərabər paylayır, amma bir key-in özü çox populyardırsa, onun bütün yükü yenə də bir node-a düşür.

Əlaqəli terminlər: , , ,

Conway's law

Sistemlərin onları quran təşkilatların kommunikasiya strukturunu əks etdirdiyini deyən müşahidə; architecture və komanda strukturu bir-birini formalaşdırır.

Melvin Conway 1968-ci ildə qeyd etmişdi ki, sistemi design edən təşkilat istər-istəməz öz kommunikasiya strukturunun surətini yaradır. Dörd komanda compiler yazırsa, dörd mərhələli compiler alınır. Bu, qanun deyil, amma o qədər ardıcıl müşahidə olunur ki, onu nəzərə almamaq baha başa gəlir.

Niyə belədir? Boundary-ni keçmək söhbət tələb edir. Hər tərəfində ayrı komanda olan boundary-yə hörmət edilir, çünki onu pozmaq başqa komanda ilə razılaşma deməkdir. Bir komandanın ortasından keçən boundary isə aşınır: contract-a əməl etməkdənsə o biri tərəfə birbaşa uzanmaq daha sürətlidir.

Ən faydalı nəticə "inverse Conway maneuver"-dir: müəyyən architecture istəyirsinizsə, komandaları ona uyğun düzün. Ayrı-ayrı service-lər istəyirsinizsə, hər service-ə sahib olan ayrıca komanda olmalıdır.

Praktik misal: üç komanda tək üzərində işləyir. Rəsmi diaqramda üç service var, amma schema dəyişiklikləri hər üç komandanın görüşünü tələb edir. Faktiki architecture diaqram deyil, bu görüşlərdir.

Qayda: komanda boundary-lərini çəkə bilmirsinizsə, service boundary-lərini çəkməyə hazır deyilsiniz. keçid əslində təşkilati dəyişiklikdir.

Əlaqəli terminlər: , , ,

Correlation ID

Bir request-ə və ya iş axınına verilən və bütün service-lərdən, queue-lardan keçərək log, metric və trace-lərə yazılan unikal identifikator.

Correlation ID ayrı-ayrı service-lərdə yazılan telemetry-ni bir hekayəyə birləşdirir. Request sistemə daxil olanda (adətən və ya ilk service-də) unikal ID alır. Sonra hər service onu gələn request-dən oxuyur, öz log-larına yazır və etdiyi hər çağırışa header kimi ötürür.

Nəticədə "bu konkret request-ə nə oldu?" sualı bir axtarışa çevrilir: ID-ni bilirsinizsə, onun bütün service-lərdəki log-larını və trace-ini tapırsınız. Müştəri dəstəyi üçün də faydalıdır: xəta səhifəsində göstərilən request ID ilə mühəndis konkret hadisəni dərhal tapa bilir.

istifadə olunanda trace ID adətən correlation ID rolunu oynayır. Bu, ayrıca ID yaratmaqdan yaxşıdır, çünki log-lar və trace-lər avtomatik bağlanır.

Ən çətin yer asinxron sərhədlərdir. yazılan event-in metadata-sında correlation ID olmalıdır, əks halda consumer-in işi heç bir request-ə bağlanmır. , və keçən message-lərdə də ID qorunmalıdır.

Ümumi səhv correlation ID-ni sonradan əlavə etməyə çalışmaqdır. O, istisnasız hər hop-dan keçməlidir; bir service onu ötürmürsə, zəncir orada qırılır. Ona görə bu, platform səviyyəsində ortaq kitabxana ilə həll olunur.

Əlaqəli terminlər: , , , ,

Coupling

Boundary-nin müxtəlif tərəflərindəki şeylərin birlikdə dəyişməyə məcbur olma dərəcəsi: birindəki dəyişiklik digərində dəyişiklik tələb edirsə, onlar coupled-dir.

Coupling iki hissənin bir-birindən nə qədər asılı olduğunu ölçür. Praktik tərif: A-nı dəyişəndə B-ni də dəyişmək məcburiyyətindəsinizmi? Əgər hə, onlar coupled-dir. Architecture-in böyük hissəsi coupling-i lazım olan yerdə saxlamaq və lazım olmayan yerdə aradan qaldırmaqdır.

Coupling-in bir neçə növü var:

  • Data coupling — iki service eyni cədvələ yazır; schema dəyişikliyi hər ikisinə toxunur.
  • Semantic coupling — consumer field-in gizli mənasından asılıdır.
  • Temporal coupling — A işləmək üçün B-nin eyni anda işlək olmasını tələb edir (synchronous çağırış).
  • coupling — iki unit həmişə birlikdə olunmalıdır.

Coupling-i azaltmaq üçün alətlər: dar API-lər, data ownership (hər data parçasının bir writer-i), event-lər vasitəsilə temporal decoupling, tolerant reader-lər. Məsələn, producer consumer-lər haqqında bilmir, ona görə yeni consumer əlavə etmək producer-ə heç nəyə başa gəlmir.

Coupling-in əlamətləri: chatty interface-lər, shared mutable data, eynivaxtlı , "bu dəyişiklik üçün üç komanda ilə danışmalıyam" hissi. Ən etibarlı ölçü change history-dir.

Səhv: bütün coupling-i pis saymaq. Sıfır coupling sıfır əməkdaşlıq deməkdir. Məqsəd coupling-i explicit, dar və stabil contract-lar üzərində cəmləməkdir.

Əlaqəli terminlər: , , ,

CQRS

Yazma (command) və oxuma (query) üçün ayrı model saxlayan pattern; read model-lər yazma tərəfinin event-lərindən qurulur və eventual consistent olur.

CQRS (Command Query Responsibility Segregation) sistemin iki tərəfini ayırır. Yazma tərəfi biznes qaydalarını yoxlayan, adətən normallaşdırılmış modeldir. Oxuma tərəfi bir və ya bir neçə read model-dir: konkret ekran və ya sorğu üçün hazır formada saxlanan denormallaşdırılmış data. Yazma tərəfi event publish edir, read model-lər onları dinləyib yenilənir.

Nə vaxt faydalıdır:

  • Oxuma ilə yazma arasında böyük asimmetriya var və onları ayrıca scale etmək lazımdır.
  • Sorğular bir neçə service-in data-sını birləşdirir; dünyasında join olmadığı üçün read model bu boşluğu doldurur.
  • Oxuma tərəfinə fərqli storage lazımdır, məsələn, axtarış üçün Elasticsearch.

Nəyə başa gəlir: iki model, sinxronizasiya kodu, yeni failure rejimləri və ən əsası . İstifadəçi order yaradıb siyahıya qayıdanda read model hələ yenilənməmiş ola bilər. UI-da bunun idarəsi lazımdır: optimistic göstərmə, read model müəyyən versiyaya çatana qədər gözləmə və ya "emal olunur" vəziyyəti.

CQRS tez-tez ilə birlikdə istifadə olunur, amma ondan asılı deyil: adi database üzərində də qurula bilər.

Ümumi səhv CQRS-i adi CRUD ekranlarına tətbiq etməkdir: oxuma və yazma modeli eyni görünəndə o, mürəkkəblikdən başqa heç nə vermir.

Əlaqəli terminlər: , , , ,

Data lake

Hər növ data-nı xam formada ucuz saxlayan analitik anbar; data warehouse təmizlənmiş cədvəllər, lakehouse isə ikisinin birləşməsini təklif edir.

Analitik data üçün üç əsas saxlama modeli var və onlar fərqli trade-off-lar verir.

Data warehouse strukturlu, təmizlənmiş cədvəlləri columnar formatda saxlayır. Schema yazarkən tətbiq olunur (schema-on-write). SQL sorğuları sürətlidir, rəqəmlər etibarlıdır. Zəif tərəfi: bahadır, xam və strukturlaşdırılmamış data (log-lar, şəkillər) üçün yararsızdır və transform zamanı atılan detal geri qayıtmır.

Data lake hər şeyi xam formada, fayllar kimi saxlayır. Schema oxuyarkən tətbiq olunur (schema-on-read). Ucuz və çevikdir, ML üçün xam data burada olur. Riski "data swamp"-dır: sahibsiz, sənədsiz fayllar yığılır və heç kim nəyin etibarlı olduğunu bilmir.

Lakehouse lake fayllarının üzərinə açıq cədvəl formatı ilə transaction, schema təkamülü və versiyalaşdırma əlavə edir. Məqsəd hər iki dünyanın üstünlüyünü bir nüsxə data üzərində verməkdir. Qiyməti nisbətən gənc ekosistem və əməliyyat mürəkkəbliyidir.

Hansı model seçilsə də, əsas problem adətən storage yox, data quality-dir. Upstream schema və ya məna dəyişikliyi pipeline-ı sındırmadan rəqəmləri yanlış edə bilər; ML model-ləri üçün bu, mənbəyidir. Data contract-lar (schema, məna, freshness, sahib) bu riski azaldır.

Ümumi səhv data lake-i "hər şeyi ora atarıq, sonra baxarıq" deyə qurmaqdır: sahibsiz data dəyər yox, xərc yaradır.

Əlaqəli terminlər: , , , , ,

Data residency

Data-nın fiziki olaraq harada (hansı ölkədə və ya region-da) saxlandığı və emal olunduğu barədə tələb; adətən qanundan və ya müştəri müqaviləsindən gəlir.

Data residency sadə sualdır: bu data hansı ölkədəki server-lərdə yaşayır? Bir çox ölkə və sənaye bunu tənzimləyir. Məsələn, Avropa istifadəçilərinin şəxsi məlumatları Avropa daxilində saxlanmalıdır, bank məlumatları ölkədən çıxmamalıdır, yaxud böyük müştəri müqavilədə "data-mız yalnız bizim region-da olsun" tələb edir.

Architecture üçün bunun nəticəsi böyükdür. Tək qlobal database ilə bu tələbi yerinə yetirmək olmur. Tipik həllər:

  • Region üzrə ayrı . Hər region-un öz database-i və service-ləri var, istifadəçi öz region-una yönləndirilir.
  • Region üzrə partition. Ortaq sistem, amma data istifadəçinin region-una görə müvafiq yerdə saxlanır.
  • Həssas data-nı ayırmaq. Yalnız tənzimlənən hissə lokal saxlanır, qalanı qlobal ola bilər.

Diqqət edilməli yerlər təkcə əsas database deyil: backup-lar, log-lar, analitika pipeline-ları, və üçüncü tərəf service-lər də data-nın nüsxəsini saxlaya bilər. Bir log sətrində qalan e-poçt ünvanı belə tələbi poza bilər.

Ümumi səhv data residency-ni gec aşkarlamaqdır. Sistem tək region üçün qurulub, sonra ilk böyük xarici müştəri bu tələblə gələndə architecture-u yenidən qurmaq lazım gəlir. Ona görə bu sual requirement-lər toplanarkən erkən verilməlidir.

Əlaqəli terminlər: , , , ,

Database index

Müəyyən sütunla axtarışı tam cədvəli skan etmədən etməyə imkan verən əlavə data strukturu; oxumanı sürətləndirir, yazmanı yavaşladır.

Database index cədvəlin yanında saxlanan və müəyyən sütun(lar) üzrə axtarışı sürətləndirən data strukturudur. Index olmadan database hər sətri yoxlamalıdır; index ilə birbaşa lazımi sətrə gedir.

Storage engine-lər əsasən iki ailəyə bölünür:

  • B-tree — data sıralı ağacda saxlanır və yerində yenilənir. Oxuma sürətli və proqnozlaşdırılandır. Relational database-lərin standartıdır.
  • LSM-tree — yazmalar əvvəl yaddaşa, sonra diskə ardıcıl fayllar kimi yazılır və fon rejimində birləşdirilir (compaction). Yazma çox ucuzdur, oxuma isə bir neçə faylı yoxlamalı ola bilər. Cassandra və RocksDB bunun üzərində qurulub.

Index pulsuz deyil. Hər insert və update cədvəldəki bütün index-ləri yeniləyir, index-lər disk və yaddaş tutur. Buna görə hər index-in arxasında konkret sorğu olmalıdır.

Composite index-də sütunların sırası vacibdir: (tenant_id, created_at) index-i tenant üzrə tarix sorğusuna kömək edir, yalnız created_at üzrə axtarışa isə kömək etmir.

Ümumi səhv yavaş sorğu görəndə düşünmədən index əlavə etməkdir. Bir sorğu sürətlənir, amma yazma ağır cədvəldə bütün yazmalar yavaşlayır. Əvvəlcə query plan-a baxın və mövcud index-lərdən hansının heç istifadə olunmadığını yoxlayın.

Əlaqəli terminlər: , , ,

Database per service

Hər service-in öz data-sına tək sahib olduğu və digər service-lərin ona yalnız API və ya event vasitəsilə çatdığı yanaşma; shared database anti-pattern-inin əksi.

Database per service data ownership qaydasının praktik formasıdır: hər data parçasının tam olaraq bir writer-i var. Service öz cədvəllərinə sahibdir, digərləri onlara birbaşa SQL ilə toxunmur, API çağırır və ya event-lərə abunə olur. Ayrı database server şərt deyil; başqa service-lərin girmədiyi ayrı schema da kifayətdir.

Əksi olan shared database anti-pattern-dir: bir neçə service eyni cədvəllərə yazır və ya onları birbaşa oxuyur. Qısa müddətdə rahatdır, çünki join-lər və transaction-lar işləyir. Amma bir service-in schema dəyişikliyi hamının dəyişikliyinə çevrilir, deploy-lar yenidən birləşir və nəticədə alınır.

Nə verir:

  • Müstəqil və schema təkamülü.
  • Hər service üçün uyğun storage seçimi və müstəqil scale.
  • Bir service-in yükü digərinin database-ini boğmur.

Nəyə başa gəlir:

  • Service-lər arası join yoxdur; data ya API ilə yığılır, ya da kopyalanır.
  • Transaction-lar lokaldır; bir neçə service-i əhatə edən dəyişiklik tələb edir.
  • Bütün biznes üzrə reporting üçün data-nı ayrıca yerə (warehouse, ) yığmaq lazımdır.

Ümumi səhv database-i ayırıb boundary-ləri düzgün çəkməməkdir: hər əməliyyat üç service-ə müraciət edirsə, problem database-də yox, decomposition-dadır.

Əlaqəli terminlər: , , , , ,

Dead letter queue

Bir neçə cəhddən sonra emal oluna bilməyən message-lərin köçürüldüyü ayrıca queue; əsas axını bloklamadan problemli message-ləri saxlayır.

Asinxron sistemlərdə bəzi message-lər heç vaxt uğurla emal oluna bilmir: format səhvdir, mövcud olmayan entity-yə istinad edir və ya consumer-də bug var. Belə message poison message adlanır.

zamanı broker emal olunmayan message-i yenidən çatdırır. Consumer yenə yıxılır, broker yenə çatdırır. Ordered queue-da isə qalan bütün message-lər bu tək message-in arxasında ilişib qalır. Bir səhv message bütün axını dayandırır.

Dead letter queue bunun həllidir: message müəyyən sayda (məsələn, 5) uğursuz cəhddən sonra ayrıca queue-ya köçürülür. Əsas axın davam edir, problemli message isə itmir. Əksər broker-lər (SQS, RabbitMQ, Kafka əsaslı framework-lər) bunu hazır dəstəkləyir. Message ilə birlikdə xətanın səbəbini, cəhd sayını və vaxtını saxlamaq faydalıdır.

DLQ-nun iki öhdəliyi var:

  • Monitoring. DLQ-da message sayı sıfırdan fərqlidirsə, alert yaranmalı və kimsə buna baxmalıdır.
  • Replay. Bug düzəldildikdən sonra message-ləri əsas queue-ya qaytarmaq üçün alət. Replay zamanı consumer-lərin olması vacibdir.

Keçici xətaları poison message-dən ayırmaq lazımdır: dependency müvəqqəti əlçatmazdırsa, message-i dərhal DLQ-ya göndərmək yox, ilə etmək düzgündür.

Ümumi səhv DLQ-nu zibil qutusu kimi istifadə etməkdir: heç kimin baxmadığı DLQ səssiz data itkisidir.

Əlaqəli terminlər: , , , ,

Deployability

Dəyişikliyin "merge olundu"dan "production-dadır"a nə qədər tez və təhlükəsiz çatdığını göstərən ; independent deployability isə bunu başqa komandalarla koordinasiyasız edə bilməkdir.

Deployability sistemin yeni versiyasını production-a çıxarmağın nə qədər tez, tez-tez və az riskli olduğunu təsvir edən . Ölçülə bilən göstəricilər: lead time (commit-dən production-a qədər vaxt), tezliyi, uğursuz faizi və müddəti.

Independent deployability architecture-in təşkiledici prinsipidir: komanda öz service-ini digər komandalarla koordinasiya etmədən edə bilməlidir. Deployable unit isə birlikdə build və olunan ən kiçik hissədir — bütün application, hər service.

Independent deployability-ni nə pozur?

  • cədvəlləri — schema dəyişikliyi bir neçə service-i birlikdə etməyə məcbur edir.
  • və versioning intizamının olmaması.
  • End-to-end test mərhələsi, harada bütün service-lər eyni vaxtda test olunmalıdır.

Əgər iki deployable unit həmişə birlikdə ship olunursa, onların müstəqilliyi uydurmadır və əlinizdə var.

: deployability tez-tez security ilə toqquşur. pipeline-dakı hər approval gate həm security control-dur, həm də gecikmə. Həll gate-ləri avtomatlaşdırmaq və riskli dəyişiklikləri feature flag-lar arxasında gizlətməkdir.

Əlaqəli terminlər: , , , ,

Deployment

konkret environment-də (staging, production) işə salınması prosesi; isə dəyişikliyin istifadəçilərə açılmasıdır.

Deployment yeni kodun və ya konfiqurasiyanın hədəf environment-ə yerləşdirilib işə salınmasıdır: container image-ləri Kubernetes-ə göndərmək, function-u platformaya yükləmək, model-i serving infrastructure-una qoymaq.

Vacib fərq: deploy deyil. Deploy kodun production-da olması, isə onun istifadəçilərə açılmasıdır. Feature flag-lar sayəsində kodu bu gün deploy edib gələn həftə edə bilərsiniz. Bu ayrılıq riski xeyli azaldır.

Geniş yayılmış deployment strategiyaları:

  • Rolling — instance-lar bir-bir yeni versiyaya keçir.
  • — yeni versiya paralel environment-də qalxır, traffic bir anda keçirilir.
  • — traffic-in kiçik hissəsi yeni versiyaya yönəldilir, metric-lər yaxşıdırsa genişləndirilir.

Deployment topology-si — hansı component-in harada, neçə instance ilə, hansı region-da işlədiyi — architecture qərarıdır və layiqdir. Onu infrastructure kodundan generate etmək diaqramların köhnəlməsinin qarşısını alır.

Tipik səhvlər: deploy-u əl ilə, sənədləşdirilməmiş addımlarla etmək; hər environment üçün ayrıca build etmək; planı olmadan deploy etmək. Yaxşı deployment darıxdırıcıdır: gündə bir neçə dəfə baş verir və heç kim bundan narahat olmur.

Əlaqəli terminlər: , , , ,

Distributed monolith

Ayrı service-lərə bölünmüş, amma onlar shared data və ya sıx səbəbindən birlikdə dəyişib olunmalı olan sistem: network xərci var, autonomy yoxdur.

Distributed monolith ən geniş yayılmış uğursuz nəticəsidir. Sistem kağız üzərində on ayrı service-dir, hər birinin öz repository-si və pipeline-ı var. Amma praktikada heç biri digərləri olmadan dəyişdirilə və ya oluna bilmir.

Bu, bütün xərclərini (network , , distributed debugging, versioning) daşıyıb, onların əsas faydasını — — almamaq deməkdir. Ən pis iki dünya: və distributed sistemin mürəkkəbliyi.

Əlamətlər:

  • Service-lər eyni database cədvəllərini share edir.
  • Bir üçün eyni anda bir neçə service-i etmək lazımdır.
  • Service-lər bir request daxilində bir-birini onlarla dəfə synchronous çağırır.
  • Bir service-in çökməsi zəncirvari bütün sistemi dayandırır.
  • Shared library-dəki dəyişiklik bütün service-lərin yenidən build-ini tələb edir.

Necə yaranır? Adətən domain-ə görə deyil, technical layer-ə görə və ya təsadüfi xətlərlə bölməkdən, data ownership-i həll etmədən extraction etməkdən.

Çıxış yolu: ya service-ləri düzgün görə yenidən çəkmək və hər data parçasına bir writer təyin etmək, ya da dürüst olub onları birləşdirmək. İkincisi tez-tez daha ucuz və daha ağıllıdır.

Əlaqəli terminlər: , , , ,

Distributed tracing

Bir request-in bir neçə service-dən keçən yolunu span-lar ağacı kimi qeyd edən texnika; vaxtın hansı hop-da itdiyini göstərir.

Distributed sistemdə bir istifadəçi request-i onlarla service-dən, queue-dan və database-dən keçir. Hər service-in öz log-u və metric-ləri var, amma "checkout niyə 4 saniyə çəkdi?" sualına heç biri tək cavab vermir. Trace bu yolu bütöv göstərir.

Trace span-lardan ibarətdir. Hər span bir iş vahididir (HTTP çağırışı, database sorğusu, message emalı): başlanğıc və bitiş vaxtı, atributları və valideyn span-ı var. Span-lar birlikdə ağac əmələ gətirir.

Bunun işləməsi üçün context propagation lazımdır: hər service gələn request-dən oxuyur və etdiyi hər çağırışa ötürür (HTTP-də W3C Trace Context header-ləri ilə). Asinxron sərhədlərdə trace context message-in metadata-sına yazılmalıdır.

OpenTelemetry bu sahədə vendor-dan asılı olmayan standartdır: API, SDK və telemetry protokolu. Onu seçmək backend-i sonradan dəyişmək imkanını saxlayır.

Hər request-i saxlamaq bahadır, ona görə tətbiq olunur. Head qərarı request-in əvvəlində verir: sadədir, amma nadir xətaları qaçırır. Tail request bitəndən sonra qərar verir və xətalı, yavaş trace-ləri saxlayır, amma əlavə infrastruktur tələb edir.

Ümumi səhv tracing-i yalnız bəzi service-lərdə aktiv etməkdir. Context-i ötürməyən bir köhnə service zənciri qırır və trace məhz problemin olduğu yerdə bitir.

Əlaqəli terminlər: , , , ,

Dual write

Bir əməliyyatda iki müstəqil sistemə (məsələn, database və ) ortaq transaction olmadan yazmaq; aralarında failure olanda sistemlər bir-birindən ayrışır.

Dual write service-in həm öz database-inə yazması, həm də eyni dəyişiklik haqqında başqa sistemə, adətən , və ya search index-ə xəbər verməsidir. İki yazma arasında ortaq transaction yoxdursa, aralarındakı hər crash, və ya sistemləri ayrışdırır:

  • Database commit uğurlu olur, event publish-i fail olur: digər service-lər dəyişiklikdən heç vaxt xəbər tutmur.
  • Event əvvəl gedir, database yazması fail olur: heç vaxt baş verməmiş dəyişiklik haqqında event yayılır.

problemi həll etmir, çünki process iki yazma arasında tamamilə ölə bilər. isə blocking-dir, azaldır və broker-lərin çoxu onu dəstəkləmir.

Standart həllər yazmalardan birini digərinin nəticəsinə çevirir:

  • . Event biznes dəyişikliyi ilə eyni lokal transaction-da cədvəlinə yazılır, ayrıca relay onu broker-ə çatdırır.
  • . Database-in transaction log-u oxunur və dəyişikliklər event kimi yayılır.

Hər iki halda nəticə , ona görə consumer-lər olmalıdır.

Dual write data migration-larında da görünür: köhnə və yeni storage-ə paralel yazanda onları reconciliation ilə müntəzəm müqayisə etmək lazımdır.

Ümumi səhv dual write-ı "nadir hal" sayıb görməzdən gəlməkdir: kifayət qədər yük altında nadir hal hər gün baş verir.

Əlaqəli terminlər: , , , , ,

End-to-end consistency

Bir neçə component-ə toxunan biznes əməliyyatının ya bütövlükdə baş verməsi, ya da heç baş verməməsi: data heç yerdə yarımçıq qalmır.

Bir sifariş verilir: order yaradılır, ödəniş alınır, anbardan məhsul çıxılır. End-to-end consistency o deməkdir ki, bu addımların hamısı birlikdə uğurlu olur və ya hamısı ləğv olunur. "Pul alındı, amma sifariş yoxdur" kimi aralıq vəziyyət qalmır.

bu asandır: hər şey bir database-dədir və bir transaction kifayət edir. Commit olunursa hamısı yazılır, xəta olursa hamısı geri qaytarılır.

Data bir neçə service-ə və database-ə bölünəndə bu imkan itir. Artıq ortaq transaction yoxdur və hər addım ayrıca uğurlu və ya uğursuz ola bilər. Belə hallarda consistency başqa vasitələrlə qurulur:

  • — addımları ardıcıl icra edir, xəta olanda isə əvvəlki addımları compensating transaction-larla geri alır.
  • — database yazısı ilə event göndərilməsinin birlikdə baş verməsini təmin edir.
  • — təkrar gələn mesajın ikinci dəfə təsir etməməsini təmin edir.

Bunun nəticəsi adətən : sistem sonda düzgün vəziyyətə gəlir, amma qısa müddət aralıq vəziyyət görünə bilər.

Buna görə ", yoxsa end-to-end consistency" klassik . Service-ləri ayırmaq müstəqillik verir, amma bir transaction-ın verdiyi sadə zəmanəti əlinizdən alır. Ümumi səhv service-ləri əvvəlcə ayırıb, consistency problemini sonra fərq etməkdir.

Əlaqəli terminlər: , , , , , ,

Error budget

icazə verdiyi uğursuzluq miqdarı (100% − ); reliability ilə sürəti arasında qərar vermək üçün istifadə olunur.

Error budget tərsidir. 99.9% olan service 0.1% uğursuzluğa "icazə verir". Ayda 10 milyon request emal edən service üçün bu, ayda 10 000 uğursuz request və ya təxminən 43 dəqiqəlik tam downtime deməkdir.

Bu rəqəmin dəyəri onu xərclənəcək resurs kimi düşünməkdədir. Hər pis , hər incident və hər planlı maintenance budget-dən yeyir. Komanda isə bunu əvvəlcədən razılaşdırılmış error budget policy ilə idarə edir:

  • Budget qalıb — risk götürmək olar: daha tez-tez , eksperimentlər, böyük dəyişikliklər.
  • Budget tükənməkdədir — yavaşlayır, əlavə review və test tələb olunur.
  • Budget tükənib — yeni dayandırılır və komanda pəncərə bərpa olunana qədər reliability işinə keçir.

Bu yanaşma product ilə operations arasındakı köhnə mübahisəni ("daha tez" və "daha sabit") rəqəmə əsaslanan qərara çevirir. Mübahisə artıq fikirlər arasında deyil, ölçü ətrafında gedir.

Ümumi səhv policy-ni yazmamaqdır. Əvvəlcədən razılaşdırılmayan error budget, ilk böyük təzyiqi altında unudulur.

Əlaqəli terminlər: , , ,

ETL

Extract, Transform, Load: data-nı mənbədən çıxarıb formalaşdırmaq və analitik sistemə yükləmək; ELT-də transform yükləmədən sonra, hədəf sistemin içində edilir.

ETL data-nı operational sistemlərdən analitik sistemə daşımağın klassik üç addımıdır:

  • Extract — data mənbədən çıxarılır: database dump-ı, , API, fayllar.
  • Transform — təmizlənir, birləşdirilir, analitik modelə salınır.
  • Load — hədəf sistemə, adətən yazılır.

Klassik ETL-də transform ayrıca serverdə və ya alətdə, yükləmədən əvvəl edilir. Bu, warehouse yaddaşı və hesablaması baha olanda məntiqli idi: yalnız hazır, sıxılmış data yüklənirdi.

ELT ardıcıllığı dəyişir: xam data olduğu kimi yüklənir, transform isə warehouse-un içində SQL ilə aparılır. Müasir columnar warehouse-lar və ucuz bunu əlverişli etdi. Üstünlükləri: xam data saxlanılır, transform səhvi tapılanda mənbəyə qayıtmadan yenidən hesablamaq olur, transform-lar SQL kimi version control-da saxlanılıb test oluna bilir.

Hər iki halda yaxşı pipeline olmalıdır: eyni dövr üçün yenidən işləyəndə duplicate yox, eyni nəticə verməlidir. Mənbənin schema-sı dəyişəndə nə baş verəcəyi də əvvəlcədən razılaşdırılmalıdır, ən yaxşısı data contract ilə.

Ümumi səhv transform məntiqini mənbəyə yaxın, extract addımında gizlətməkdir: səhv aşkar olunanda xam data artıq yoxdur və tarixi düzəltmək mümkün olmur.

Əlaqəli terminlər: , , , ,

Event sourcing

Cari state əvəzinə ona gətirən dəyişməz event-lərin ardıcıllığını saxlayan yanaşma; state event-lərin replay-i ilə alınır.

Adi sistem cari vəziyyəti saxlayır: hesabın balansı 120 manatdır. Event sourcing isə bu vəziyyətə gətirən faktları saxlayır: hesab açıldı, 200 manat mədaxil, 80 manat məxaric. Event-lər yalnız əlavə olunan event store-da saxlanır və dəyişdirilmir. Cari state event-ləri ardıcıl tətbiq etməklə (replay) hesablanır; uzun tarixçə üçün vaxtaşırı snapshot saxlanır ki, replay sonuncu snapshot-dan başlasın.

Nə verir:

  • Tam audit. Hər dəyişikliyin nə vaxt və niyə baş verdiyi data modelinin özündədir.
  • Temporal query-lər. Obyektin keçmiş istənilən andakı vəziyyətini bərpa etmək olar.
  • Yeni . Bütün tarixçəni yenidən oxuyub əvvəlcədən düşünülməmiş hesabat qurmaq mümkündür.

Nəyə başa gəlir:

  • Event schema təkamülü. Event-lər əbədidir; köhnə formatları oxumaq üçün versioning və upcasting daimi iş olur.
  • Öyrənmə əyrisi. Səhv data sətri düzəltməklə yox, düzəldici yeni event ilə düzəlir.
  • Sorğu çətinliyi. Event store sorğu üçün əlverişsizdir, ona görə adətən də lazım olur.

ilə tez-tez birlikdə gəlir, amma onlar bir-birindən asılı deyil.

Ümumi səhv event sourcing-i bütün sistemə tətbiq etməkdir: o, mühasibat və ödəniş kimi tarixçənin özü dəyər olan domain-lər üçündür, istifadəçi parametrləri üçün yox.

Əlaqəli terminlər: , , , ,

Event-driven architecture

Component-lərin bir-birini birbaşa çağırmaq əvəzinə event-lər emit edib onlara subscribe olaraq, adətən broker vasitəsilə, əlaqə saxladığı .

Event-driven architecture-də component "nəsə baş verdi" deyə event publish edir — OrderPlaced, PaymentFailed — və maraqlanan digər component-lər bu event-lərə reaksiya verir. Producer consumer-lərin kim olduğunu bilmir.

İki əsas alt-növ var:

  • Event notification — event yalnız nəyin baş verdiyini deyir (order id), detallar üçün consumer geri müraciət edir.
  • Event-carried state transfer — event lazımi data-nı özü daşıyır, consumer-in geri çağırmasına ehtiyac qalmır, amma event-lər böyüyür və schema contract-a çevrilir.

Güclü tərəfləri: yeni consumer əlavə etmək producer-ə heç nəyə başa gəlmir; temporal sayəsində consumer müvəqqəti işləməsə də sonradan yetişə bilər; audit və integration üçün təbii uyğunluq.

Zəif tərəfləri: control flow görünməz olur. "Order yerləşdiriləndə nə baş verir?" sualına cavab vermək üçün codebase boyu subscription-ları izləmək lazımdır. qəbul edilməli və product komandasına izah edilməlidir. Event-lərin sırası, duplicate-lər və schema evolution ayrıca diqqət tələb edir.

Məsələn, e-ticarətdə OrderPlaced event-inə inventory, email, analytics və loyalty service-ləri müstəqil reaksiya verir; beşinci consumer əlavə etmək ordering komandasının işinə toxunmur.

Tipik səhv event-ləri gizli command kimi istifadə etməkdir ("SendEmailNow") — bu, sadəcə gizlədir.

Əlaqəli terminlər: , , , ,

Eventual consistency

Yeni write-lar dayandıqdan sonra bütün və ya copy-lərin zamanla eyni vəziyyətə gələcəyinə zəmanət verən, amma ara dövrdə köhnə data göstərə bilən consistency modeli.

Eventual consistency deyir: dəyişiklik bütün sistemə dərhal yox, bir müddət sonra yayılacaq. Bu ara dövrdə müxtəlif hissələr fərqli vəziyyət görə bilər. Məsələn, order yerləşdirilib, amma search index-də inventory sayı hələ köhnədir.

Niyə qəbul edilir? Güclü consistency koordinasiya tələb edir: hər write bütün iştirakçıların razılığını gözləməlidir. Bu, artırır və partition zamanı azaldır ( teoremi). , , və service-lər arasında data copy-lərində eventual consistency demək olar ki, qaçılmazdır.

Praktikada əsas qayda: staleness barədə explicit olun. "Search-də göstərilən inventory sayları otuz saniyəyə qədər köhnə ola bilər" — bu, product komandasının üzərində düşünə biləcəyi design qərarıdır. Səssiz staleness isə baş verməyi gözləyən bug report-dur.

Faydalı texnikalar:

  • — istifadəçi öz dəyişikliyini dərhal görsün, başqaları bir az sonra.
  • UI-da "emal olunur" vəziyyəti göstərmək.
  • Uzun müddət ayrışan data-nı aşkarlamaq üçün reconciliation job-ları.

Tipik səhv eventual consistency-ni pul, inventory rezervasiyası və ya icazələr kimi data-ya düşünmədən tətbiq etməkdir. Hər data set üçün ayrıca soruşun: köhnə dəyər görmək burada nəyə başa gəlir?

Əlaqəli terminlər: , , ,

Exactly-once

Hər mesajın təsirinin nəticədə tam bir dəfə görünməsi vədi; praktikada , checkpoint və sink ilə əldə olunan effectively-once deməkdir.

Mesaj ötürülməsində üç delivery semantikası var:

  • At-most-once — yoxdur. Mesaj ya bir dəfə emal olunur, ya itir. Ucuzdur, bəzi metrikalar üçün qəbul ediləndir.
  • — itki yoxdur, amma duplicate yaradır. default-u budur.
  • Exactly-once — hər mesajın təsiri tam bir dəfə görünür.

Şəbəkə üzərində mesajı fiziki olaraq tam bir dəfə çatdırmaq mümkün deyil: ack itəndə göndərən mesajın çatıb-çatmadığını bilmir və ya təkrar göndərir, ya da itki riskini qəbul edir. Ona görə "exactly-once" deyəndə əslində effectively-once nəzərdə tutulur: mesaj bir neçə dəfə emal oluna bilər, amma nəticəyə bir dəfə təsir edir.

Bu iki mexanizmin birləşməsidir:

  1. Checkpoint — processor state-ini və input offset-ini atomik saxlayır, yıxılanda oradan davam edir.
  2. və ya transactional sink — key üzrə upsert, partition overwrite, və ya output ilə offset-in eyni transaction-da yazılması.

Vacib nüans: bu, framework-un yox, bütün yolun xüsusiyyətidir. daxilində exactly-once olsa belə, sink email göndərirsə və ya xarici API çağırırsa, duplicate yenə yaranır.

Ümumi səhv framework sənədindəki "exactly-once" sözünü görüb sink-in olub-olmadığını yoxlamamaqdır.

Əlaqəli terminlər: , , , , ,

Expand-contract

Breaking schema və ya interface dəyişikliyini hər biri ayrıca olunan və geri qaytarıla bilən addımlara bölən pattern: əvvəl yenini əlavə et, sonra köhnəni sil.

Expand-contract (parallel change) breaking dəyişikliyi downtime-sız etmək üçün pattern-dir. Rolling, və zamanı köhnə və yeni versiya eyni database-ə eyni anda müraciət edir, ona görə schema hər iki versiya ilə uyğun qalmalıdır. Pattern bunu dəyişikliyi üç fazaya bölməklə təmin edir.

Nümunə, sütunun adını dəyişmək:

  • Expand. Yeni sütun nullable olaraq əlavə olunur. Kod hər iki sütuna yazır, köhnədən oxuyur. Köhnə sətirlər hissə-hissə backfill edilir.
  • Migrate. Kod yeni sütundan oxumağa keçir, amma hər ikisinə yazmağa davam edir. Problem çıxsa, köhnə sütun hələ aktualdır və geri qayıtmaq təhlükəsizdir.
  • Contract. Köhnə versiyanın heç bir instance-ı qalmayanda köhnə sütuna yazma dayandırılır, bir müddət sonra sütun silinir.

Hər addım ayrıca və yalnız sonuncu geri qaytarılmır. Bu, bir dəyişikliyi bir neçə yayır, amma alternativ maintenance pəncərəsi və geri qaytarıla bilməyən migration-dır.

Eyni pattern database-dən kənarda da işləyir: API field-inin adını dəyişmək, queue mesaj formatını yeniləmək, bir service-dən digərinə keçid. bunun architecture miqyasında versiyasıdır.

Ümumi səhv contract addımını tələsdirməkdir: köhnə sütunu silməzdən əvvəl onu oxuyan son consumer-in, o cümlədən report və getdiyinə əmin olmaq lazımdır.

Əlaqəli terminlər: , , , , ,

Exponential backoff

Retry-lar arasındakı gözləmə müddətinin hər cəhddə eksponensial artırılması; jitter isə bu müddətə təsadüfi komponent əlavə edir.

Uğursuz çağırışı dərhal təkrarlamaq xarab service-ə ən çətin anında daha çox yük gətirir. Exponential backoff bunun qarşısını alır: hər cəhddən əvvəl gözləmə müddəti artır, adətən ikiqat (100 ms, 200 ms, 400 ms, 800 ms...). Gözləmə üçün yuxarı hədd və maksimal cəhd sayı da təyin olunur.

Amma backoff tək kifayət deyil. Dependency yıxılanda min client eyni anda xəta alır. Hamı eyni backoff formulunu istifadə edirsə, hamı eyni anda da edəcək və service bərpa olunan kimi yenidən sinxron dalğa ilə vurulacaq. Bu, thundering herd problemidir.

Jitter gözləmə müddətinə təsadüfi komponent əlavə edir. Ən geniş yayılmış variant "full jitter"-dir: gözləmə 0 ilə hesablanmış backoff arasında təsadüfi seçilir. Nəticədə retry-lar zamana bərabər paylanır və yük hamarlanır.

Exponential backoff başqa kontekstlərdə də işlənir: message consumer-lərin yenidən cəhdləri, reconnect məntiqi, zamanı client-in davranışı. Server 429 cavabı ilə Retry-After header-i qaytarırsa, client öz backoff-u əvəzinə həmin dəyərə əməl etməlidir.

Ümumi səhv jitter-siz backoff istifadə etməkdir: retry-lar seyrəlir, amma sinxron qalır və hər dalğa əvvəlkindən güclü olur.

Əlaqəli terminlər: , , ,

Failover

Xarab olan komponentin işini avtomatik və ya əl ilə ehtiyat komponentə ötürmə prosesi.

Failover çevirən mexanizmdir. Ehtiyat komponentin olması kifayət deyil. Sistem əsas komponentin xarab olduğunu aşkarlamalı, traffic-i ehtiyata yönləndirməli və bunu data itirmədən etməlidir.

İki əsas model var:

  • Active-passive — ehtiyat gözləmə rejimindədir və yalnız failure zamanı işə düşür. Sadədir, amma keçid vaxt aparır və ehtiyat nadir hallarda real yük altında yoxlanılır. "Ehtiyat server-imiz var idi, amma işləmədi" hekayəsi buradan gəlir.
  • Active-active — bütün nüsxələr eyni anda traffic qəbul edir. Bir node düşəndə qalanlar onun payını götürür. Keçid demək olar ki, anidir, amma state-in sinxronlaşdırılması və conflict-lərin həlli xeyli çətinləşir.

Failover-in ən çətin hissəsi aşkarlamadır. Çox tez failover etsəniz, keçici şəbəkə problemi lazımsız keçidə səbəb olur. Gec etsəniz, downtime uzanır. Ən təhlükəli ssenari split brain-dir: şəbəkə bölünür və hər iki tərəf özünü əsas hesab edib eyni data-ya yazmağa başlayır.

Ümumi səhv failover-i heç vaxt test etməməkdir. Test edilməmiş failover mexanizmi plan deyil, ümiddir.

Əlaqəli terminlər: , , , ,

Failure domain

Eyni səbəbdən birlikdə xarab ola bilən komponentlərin sərhədi, məsələn, bir host, rack, zone, region və ya ortaq .

Failure domain "bu xarab olanda başqa nə xarab olur?" sualının cavabıdır. yalnız nüsxələr fərqli failure domain-lərdə olanda işləyir.

Fiziki failure domain-lər iyerarxiya təşkil edir: host → rack → zone → region. Hər səviyyə daha nadir, amma daha geniş təsirli failure-lara qarşı qorunma deməkdir və hər səviyyə daha baha başa gəlir.

Daha təhlükəlisi məntiqi failure domain-lərdir, çünki diaqramlarda görünmürlər:

  • — bütün instance-lara eyni anda çatan səhv build.
  • Config — bütün region-lara eyni anda yayılan səhv və ya certificate.
  • Ortaq dependency — hamının istifadə etdiyi DNS, identity provider və ya .
  • İnsan — sistemi yalnız bir nəfərin bildiyi vəziyyət.

Failure domain-ləri kiçiltmək kiçiltmək deməkdir. Praktik yollar: zonalar üzrə paylamaq, və config dəyişikliklərini mərhələlərlə yaymaq və böyük sistemləri müstəqil cell-lərə bölmək. Cell-based architecture-da hər cell istifadəçilərin bir hissəsinə xidmət edir və bir cell-in yıxılması digərlərinə təsir etmir.

Ümumi səhv: "üç var" demək, amma hər üçünü eyni zonada saxlamaq.

Əlaqəli terminlər: , , ,

Feature flag

Kodun davranışını etmədən, runtime-da dəyişməyə imkan verən şərt; ayırmağın əsas vasitəsidir.

Feature flag kodda konfiqurasiyadan oxunan şərtdir: flag açıqdırsa yeni yol, bağlıdırsa köhnə yol işləyir. Bu, kodu production-a edib ayrıca qərarla etməyə imkan verir.

Flag-lərin bir neçə növü var və onlar fərqli ömür sürür:

  • flag-ləri. Tədrici açılış üçün: əvvəl komanda, sonra beta istifadəçilər, sonra faizlə hamı. Qısa ömürlü olmalıdır.
  • Operational flag-lər. : problemli və ya ağır əməliyyatı söndürmək. çox vaxt belə qurulur. Uzun ömürlüdür.
  • Experiment flag-ləri. üçün istifadəçiləri qruplara bölmək.

Nəyə başa gəlir:

  • Texniki borc. Hər flag kodda iki yol yaradır. On aktiv flag nəzəri olaraq 1024 kombinasiya deməkdir və onların çoxu test olunmur.
  • Köhnəlmiş flag-lər. Unudulmuş flag ölü kodu canlı saxlayır. 2012-ci ildə Knight Capital köhnə flag-i yeni məqsədlə istifadə etdi, yeni kodun bir server-ə çatmadığını görmədi və 45 dəqiqədə 440 milyon dollar itirdi.
  • Config . Flag dəyişikliyi pipeline-dan keçməyən . Bütün region-lara bir anda yayılan səhv flag səhv build qədər təhlükəlidir, ona görə flag dəyişiklikləri audit olunmalı və mərhələlərlə yayılmalıdır.

Hər flag-inin sahibi və silinmə tarixi olmalıdır, flag-in silinməsi isə "bitdi" tərifinə daxil edilməlidir.

Ümumi səhv flag-i pulsuz hesab etməkdir: onu əlavə etmək bir sətirdir, silmək isə çox vaxt heç kimin vəzifəsi deyil.

Əlaqəli terminlər: , , , , ,

Fitness function

Architecture qaydasını və ya hədəfini avtomatik yoxlayan test: məsələn "billing module-u marketing-dən import edə bilməz" və ya "checkout 400 ms-dən aşağıdır".

Architectural fitness function sistemin vacib xüsusiyyətini qoruyan avtomatlaşdırılmış yoxlamadır. Termin evolutionary architecture yanaşmasından gəlir: genetik alqoritmlərdə fitness function həllin nə qədər "yaxşı" olduğunu ölçdüyü kimi, burada da sistemin architecture niyyətinə nə qədər uyğun qaldığını ölçür.

Niyə lazımdır? Yazılmış qərar, hətta olsa belə, səssizcə pozula bilər. Yeni developer qaydanı bilmir, tələsik hotfix qısa yol seçir. Fitness function qaydanı arzudan CI-da qırmızı işığa çevirir.

Növləri:

  • Struktur — dependency qaydaları: "domain package-i infrastructure-dan import etmir". ArchUnit, dependency-cruiser, custom lint qaydaları ilə ucuzdur.
  • Operational — "hər HTTP endpoint-in configure olunub", "hər service açır".
  • Performance — gecə load test-ində hədəfi.
  • Security — dependency-lərdə məlum vulnerability yoxdur, kodda yoxdur.

Praktik yanaşma: komandanın şifahi razılaşdığı, amma tez-tez pozulan bir qayda seçin və bu həftə onu test-ə çevirin. Hər yeni üçün soruşun: bunu avtomatik yoxlaya bilərikmi?

Səhvlər: çox kövrək fitness function-lar yazmaq (hər refactoring-də sınır və komanda onları söndürür) və ya yalnız asan ölçüləni yoxlayıb vacib olanı unutmaq.

Əlaqəli terminlər: , , ,

Functional requirement

Sistemin nə etməli olduğunu təsvir edən tələb: istifadəçinin edə biləcəyi əməliyyat və ya sistemin göstərməli olduğu davranış.

Functional requirement sistemin nə etdiyini deyir: "istifadəçi foto yükləyir", "sifariş təsdiqlənəndə email göndərilir", "admin hesabı bloklaya bilir". Onlar adətən user story, use case və ya acceptance criteria formasında yazılır və test etmək nisbətən asandır: davranış ya var, ya yoxdur.

Architecture baxımından functional requirement-lər üç növ architectural driver-dən biridir, amma ən az həlledici olanıdır. Eyni dəstini də, onlarla də, funksiyalar da yerinə yetirə bilər. Strukturu əsasən və seçir.

Buna baxmayaraq, functional requirement-lər architecture üçün vacib məlumat daşıyır:

  • Domain-in forması. Hansı anlayışlar bir yerdə dəyişir, hansılar ayrı. Bu, boundary-ləri və tapmağa kömək edir.
  • Load-un mənbəyi. Hər əməliyyat gələcək request-dir. Onların tezliyi capacity hesablamasının girişidir.
  • Gizli . "Ödəniş iki dəfə çəkilməməlidir" functional səslənir, amma əslində və consistency tələbidir.

Ümumi səhv requirement toplamağı functional requirement-lərlə bitirməkdir. siyahısı tam ola bilər, amma neçə istifadəçi, hansı və hansı qaydalar daxilində sualları cavabsız qalırsa, architecture üçün əsas yoxdur.

Əlaqəli terminlər: , , ,

Golden signals

Service-in sağlamlığını izləmək üçün dörd əsas ölçü: , traffic, errors və saturation. RED və USE onların request və resurs yönümlü variantlarıdır.

Golden signals Google SRE kitabından gəlir və "nəyi ölçək?" sualına minimal cavabdır:

  • — request-lər nə qədər vaxt aparır. Uğurlu və uğursuz request-lər ayrıca ölçülməlidir, çünki sürətli xəta cavabları ortanı aldadıcı şəkildə yaxşılaşdırır.
  • Traffic — sistemə gələn yük (, message sayı).
  • Errors — uğursuz request-lərin nisbəti.
  • Saturation — resursun nə qədər dolu olduğu (CPU, memory, connection pool, queue uzunluğu).

İki praktik variant var. RED method (Rate, Errors, Duration) request emal edən service-lər üçündür və istifadəçinin nə hiss etdiyini göstərir. USE method (Utilization, Saturation, Errors) isə resurslar üçündür: disk, CPU, şəbəkə, pool-lar. RED "istifadəçi nə görür?", USE isə "hansı resurs darboğazdır?" sualına cavab verir. İncident zamanı adətən RED-dən başlayıb USE-a enirlər.

heç vaxt orta ilə yox, percentile-lərlə (p50, , ) izlənməlidir və histogram-dan hesablanmalıdır. Instance-ların ayrı-ayrı ortası riyazi cəhətdən mənasızdır.

Ümumi səhv yüzlərlə metric toplayıb bu dördünü ayırd etməməkdir. İncident zamanı ilk baxılan dashboard bu siqnallardan başlamalıdır.

Əlaqəli terminlər: , , , , ,

Graceful degradation

Dependency xarab olanda sistemin funksionallığın bir hissəsini itirərək əsas xidməti göstərməyə davam etməsi.

Graceful degradation "hər şey və ya heç nə" yanaşmasının alternatividir. Məhsul səhifəsi recommendation service-dən, rəylərdən, qiymət service-indən və stok məlumatından asılıdır. Recommendation-lar xarab olanda bütün səhifənin 500 qaytarması lazım deyil: səhifə onsuz da faydalıdır.

Bunun üçün hər dependency üçün fallback müəyyən edilir:

  • köhnə data — bir az köhnə qiymət heç bir qiymətdən yaxşıdır (bəzən isə yox: bunu biznes qərar verməlidir).
  • Default cavab — personal tövsiyələr əvəzinə populyar məhsullar.
  • gizlətmək — rəylər bloku sadəcə görünmür.
  • Funksionallığı sadələşdirmək — axtarış yalnız əsas sahələr üzrə işləyir.

Graceful degradation hədəflərini istifadəçi axınlarına görə ayırmağa imkan verir: checkout dörd "9" tələb edə bilər, tövsiyələr bloku isə iki "9" ilə kifayətlənə bilər, çünki onun xarab olması səhifəni yıxmır. ilə birlikdə işləyir: breaker açılanda fallback işə düşür.

Risklər də var. Fallback özü kod yoludur: nadir işləyir, nadir test olunur və ən lazım olan anda səhv çıxa bilər. Fallback başqa zəif dependency-yə müraciət edirsə, o, ikinci failure nöqtəsidir. Degraded rejim istifadəçiyə və monitoring-ə görünməlidir, əks halda heç kim sistemin yarımçıq işlədiyini bilmir.

Ümumi səhv fallback-i yazıb heç vaxt işə salmamaqdır: onu chaos testləri və ya ilə müntəzəm yoxlamaq lazımdır.

Əlaqəli terminlər: , , , ,

GraphQL

Server-in bir schema açdığı, client-in isə tam olaraq hansı data-ya ehtiyacı olduğunu query ilə özü seçdiyi API dili.

GraphQL-də server tipli schema açır: Order, Customer, Product və onların əlaqələri. Client bir query-də ehtiyac duyduğu field-ləri, o cümlədən əlaqəli obyektləri seçir və cavab məhz bu formada gəlir.

Bu, iki klassik problemini həll edir. Over-fetching — 100 field gəlir, 3-ü lazımdır. Under-fetching — bir ekranı çəkmək üçün bir neçə ardıcıl request lazımdır. Fərqli ekranları olan mobil və web client-lər üçün bu böyük qazancdır: frontend komandası backend-dən yeni endpoint gözləmədən irəliləyir.

Amma çeviklik mürəkkəbliyi server tərəfə köçürür:

  • problemi. Naiv resolver-lər hər əlaqəli obyekt üçün ayrıca sorğu edir. DataLoader kimi batching mexanizmləri məcburidir.
  • HTTP . Query-lər adətən bir POST endpoint-inə gedir, ona görə və browser işləmir. Persisted query-lər bunu qismən həll edir.
  • Query mürəkkəbliyi. Client istənilən dərinlikdə query yaza bilər. Depth və complexity limitləri olmadan bir request database-i yükləyə bilər.
  • . Bütün trafik bir endpoint-dən keçdiyi üçün endpoint səviyyəsində metrikalar az şey deyir.

GraphQL ən çox müxtəlif client-lərə xidmət edən frontend qatında (BFF) və ya bir neçə backend service-i birləşdirən aggregation qatında özünü doğruldur. Service-lər arası daxili kommunikasiya üçün nadir hallarda ən yaxşı seçimdir.

Ümumi səhv GraphQL-i "API dizaynını client-lərə həvalə etmək" kimi görməkdir: schema yenə də dizayn olunmalı, versioning və deprecation yenə də idarə olunmalıdır.

Əlaqəli terminlər: , , ,

gRPC

Protobuf contract-ları, HTTP/2 və binary serialization üzərində qurulmuş, yüksək performanslı framework-ü.

gRPC Google-un açıq mənbəli framework-üdür. Contract Protocol Buffers (Protobuf) faylında yazılır: service-lər, method-lar və mesaj tipləri. Ondan onlarla dil üçün client və server kodu generasiya olunur.

Üstünlükləri:

  • Performance. Binary serialization JSON-dan kiçik və parse etmək üçün ucuzdur. HTTP/2 bir bağlantı üzərində çoxlu paralel çağırışa imkan verir.
  • Streaming. Client streaming, server streaming və bidirectional streaming birinci sinif imkanlardır.
  • Tip təhlükəsizliyi. Generated kod contract-dan kənara çıxmağa imkan vermir.
  • Təkamül. Protobuf field-ləri nömrə ilə identifikasiya olunur, ona görə yeni field əlavə etmək köhnə client-ləri sındırmır.

Qiyməti də var. Browser gRPC-ni birbaşa danışa bilmir, gRPC-Web və ya proxy lazımdır. Binary trafiki curl ilə oxumaq olmur, debugging üçün xüsusi alətlər gərəkir. HTTP praktiki olaraq işləmir. Ortaq .proto faylları və generated kod isə tərəflər arasında daha sıx yaradır.

Ona görə gRPC-nin təbii yeri daxili service-lər arası kommunikasiyadır: hər iki tərəf sizin nəzarətinizdədir, və vacibdir. Kənar dünyaya isə çox vaxt və ya açılır, gRPC daxildə qalır.

Ümumi səhv Protobuf-da field nömrələrini təkrar istifadə etmək və ya dəyişməkdir: bu, wire formatında səssiz və köhnə client-lər data-nı yanlış oxuyur.

Əlaqəli terminlər: , , , , ,

Health check

və ya orchestrator-un instance-ın traffic qəbul etməyə hazır olub-olmadığını müəyyən etmək üçün dövri olaraq çağırdığı yoxlama.

Health check failure detection-un ən sadə formasıdır. və ya orchestrator (məsələn, Kubernetes) müəyyən intervalla instance-ın xüsusi endpoint-ini çağırır. Bir neçə ardıcıl uğursuz cavabdan sonra instance rotasiyadan çıxarılır və ya yenidən başladılır.

Faydalı fərq iki növ yoxlama arasındadır:

  • Liveness — process sağdırmı? Uğursuz olanda instance yenidən başladılır.
  • Readiness — instance traffic qəbul etməyə hazırdırmı? Uğursuz olanda yalnız traffic dayandırılır, instance öldürülmür. Məsələn, isinərkən və ya start-up zamanı.

Yoxlamanın dərinliyi əsas dizayn qərarıdır. Çox səthi yoxlama (port açıqdır) process işlədiyi, amma database-ə qoşula bilmədiyi halı görmür. Çox dərin yoxlama (bütün dependency-lər) isə ortaq dependency-nin qısa problemində bütün instance-ları eyni anda sağlam olmayan elan edir. Nəticədə kiçik problem tam çevrilir.

Interval və threshold-lar birbaşa təsir edir: yalnız failure-u aşkarlaya bildiyiniz qədər sürətlidir. Çox həssas threshold isə qısa gecikmələrdə instance-ları lazımsız yerə çıxarır.

Ümumi səhv health check-in sağlam cavab verməsini sistemin istifadəçi üçün işləməsi ilə qarışdırmaqdır. Health check instance haqqında danışır, istifadəçi təcrübəsini isə ölçür.

Əlaqəli terminlər: , , , ,

Hexagonal architecture

Domain logic-i mərkəzdə saxlayıb xarici dünyadan port-lar (interface-lər) ilə ayıran, konkret texnologiyaları isə adapter-lər vasitəsilə qoşan .

Hexagonal architecture (ports and adapters) Alistair Cockburn-un ideyasıdır. Mərkəzdə domain logic oturur və o, database, HTTP, və ya UI haqqında heç nə bilmir. Domain ehtiyac duyduğu hər şey üçün port müəyyən edir — məsələn OrderRepository və ya PaymentGateway interface-i.

Adapter-lər bu port-ları konkret texnologiyalar üçün implement edir: Postgres adapter-i, controller, Kafka consumer, test üçün in-memory double. Dependency istiqaməti həmişə içəriyə doğrudur: adapter domain-dən asılıdır, domain adapter-dən yox.

Güclü tərəfləri:

  • Domain logic infrastructure-suz, sürətli unit test-lərlə yoxlanılır.
  • Database və ya broker dəyişmək adapter dəyişikliyidir, biznes kodu toxunulmaz qalır.
  • Eyni domain-ə bir neçə giriş nöqtəsi (API, CLI, event consumer) asanlıqla qoşulur.

Zəif tərəfləri: sadə CRUD application üçün artıq indirection. Hər cədvəl üçün port, adapter və mapping yazmaq heç bir dəyər yaratmadan kodu üç dəfə artırır. Komandalar bəzən mükəmməl adapter-lər qurub ortaya heç bir real logic qoymur.

Nə vaxt seçməli: domain logic dəyərli və mürəkkəbdirsə, infrastructure isə təsadüfi və dəyişkəndirsə. hər module-u daxilində hexagonal struktur yaxşı işləyir.

Əlaqəli terminlər: , , ,

Hot partition

Yükün qeyri-bərabər paylanması nəticəsində digərlərindən qat-qat çox traffic alan və ya key; "celebrity problem" kimi də tanınır.

yükü node-lar arasında paylamaq üçündür, amma key-lər bərabər deyil. Milyonlarla izləyicisi olan istifadəçi, viral olmuş post, ölçüsü digərlərindən yüz dəfə böyük tenant və ya flash sale-dəki bir məhsul bütün traffic-i bir yönəldir. Həmin yanır, digərləri isə boş dayanır. Bu, hot partition adlanır.

bu problemi həll etmir: o, key-ləri bərabər paylayır, amma bir key-in özü populyardırsa, onun bütün yükü yenə bir node-a düşür. Range isə ardıcıl key-lər (məsələn timestamp) bütün yeni yazmaları son yığaraq struktur hot partition yaradır.

Tipik həllər:

  • Key-i bölmək — populyar key-ə təsadüfi suffix əlavə edib yazmaları bir neçə yaymaq; oxuma zamanı nəticələri birləşdirmək.
  • — populyar oxumaları database-ə çatdırmamaq.
  • Ayrıca — böyük tenant-ı və ya key-i öz resursuna köçürmək. Bunun üçün directory əsaslı daha çevikdir.

Hot partition-ı aşkarlamaq üçün yükü yalnız ümumi yox, və key səviyyəsində ölçmək lazımdır.

Ümumi səhv seçərkən yalnız orta yükə baxmaqdır. Ən böyük key-in payını soruşun: o, bir tutumunu aşa bilərmi?

Əlaqəli terminlər: , , ,

Hot spot

Sistemin qalan hissəsindən qat-qat çox yük alan hissəsi: bir service, endpoint, cədvəl və ya key. Sistemin həddini adətən o müəyyən edir.

Yük heç vaxt bərabər paylanmır. Çox sistemdə kiçik bir hissə işin böyük payını görür: axtarış endpoint-i, məhsul kataloqu, login, bir populyar məhsulun səhifəsi. Bu hissəyə hot spot deyilir.

Hot spot vacibdir, çünki sistem bütövlükdə ən zəif nöqtəsi qədər güclüdür. Qalan hər şey rahat işləsə belə, hot spot dolanda istifadəçi yavaşlıq və ya xəta görür.

Architecture baxımından əsas sual budur: hot spot-u qalanından ayrıca scale edə bilirikmi?

  • bütün application birlikdə scale olunur. Kataloq üçün əlavə güc lazımdırsa, bütün nüsxələrini artırırsınız.
  • Ayrılmış service-lərdə yalnız yüklü hissəni böyütmək olar.
  • və ya hot spot-a gələn oxumaların çoxunu ona çatmadan cavablandırır.

Data səviyyəsində hot spot-un xüsusi forması : bir və ya key bütün traffic-i öz üzərinə çəkir.

Ümumi səhv hot spot-u təxminlə müəyyən etməkdir. Onu ölçmək lazımdır: hansı endpoint, hansı cədvəl, hansı key ən çox resurs işlədir? Çox vaxt cavab gözlənilməz olur, sistemi ayırmaq qərarı isə yalnız ölçülmüş hot spot üçün məna kəsb edir.

Əlaqəli terminlər: , , , , ,

Idempotency

Əməliyyatı bir dəfə və ya bir neçə dəfə icra etməyin eyni nəticə verməsi xüsusiyyəti; və təkrar mesajları təhlükəsiz edir.

Əməliyyat idempotent-dir, əgər onu təkrar icra etmək sistemin vəziyyətini ilk icradan sonrakından fərqli etmirsə. "Balansı 100 et" idempotent-dir; "balansa 100 əlavə et" isə yox. HTTP-də GET, PUT və DELETE idempotent nəzərdə tutulur, POST isə adətən yox.

Distributed sistemlərdə bu xüsusiyyət vacibdir, çünki network etibarsızdır. Client request göndərir, cavab gəlmir — request çatmayıb, yoxsa çatıb, emal olunub, amma cavab itib? Client bunu bilə bilmir və edir. Əməliyyat idempotent deyilsə, müştəri iki dəfə ödəniş edə bilər.

Eyni problem də var: eyni event-i iki dəfə çatdıra bilər, ona görə consumer-lər idempotent olmalıdır.

Praktik texnikalar:

  • — client hər əməliyyata unikal key verir (Stripe API-si belə işləyir); server key-i yadda saxlayır və təkrarda əvvəlki cavabı qaytarır.
  • Deduplication — emal olunmuş event id-lərini saxlamaq.
  • Natural idempotency — əməliyyatları delta yox, son vəziyyət kimi ifadə etmək.
  • Unique database səviyyəsində duplicate-ləri rədd edir.

də eyni prinsip keçərlidir: model arabir yanlış tool-u və ya eyni tool-u iki dəfə çağıracaq, ona görə idempotent tool-lar daha təhlükəsizdir.

Əlaqəli terminlər: , ,

Idempotency key

Client-in hər əməliyyata verdiyi unikal identifikator; server onun sayəsində təkrar request-i tanıyır və əməliyyatı ikinci dəfə icra etmir.

Idempotency key təbiətcə olmayan əməliyyatları (adətən POST) üçün təhlükəsiz etməyin standart yoludur.

Problem belədir: client POST /payments göndərir, server ödənişi keçirir, amma cavab düşür. Client ödənişin keçib-keçmədiyini bilə bilmir. etməsə, ödəniş itə bilər. etsə, müştəridən iki dəfə pul çıxıla bilər.

Həll: client hər məntiqi əməliyyat üçün unikal key yaradır (məsələn, UUID) və onu Idempotency-Key header-ində göndərir. Retry-larda eyni key istifadə olunur. Server:

  1. Key-i ilk dəfə görəndə əməliyyatı icra edir və nəticəni key ilə birlikdə saxlayır.
  2. Eyni key ilə təkrar request gələndə əməliyyatı yenidən icra etmir, saxlanmış cavabı qaytarır.
  3. Eyni key ilə fərqli body gələrsə, xəta qaytarır.

Implementation detalları vacibdir. Key-in saxlanması əməliyyatın özü ilə eyni transaction-da olmalıdır, əks halda crash arada boşluq qoyur. Eyni key ilə paralel gələn iki request-dən yalnız biri icra olunmalıdır. Key-lər məhdud müddət (məsələn, 24 saat) saxlanılır.

Stripe API-si bu pattern-in ən tanınmış nümunəsidir. Eyni ideya alıcılarında və message consumer-lərində event id-ləri ilə deduplication şəklində işləyir.

Ümumi səhv key-i server tərəfdə yaratmaqdır: key client-də, ilk cəhddən əvvəl yaranmalıdır, yoxsa onu tanıya bilməz.

Əlaqəli terminlər: , , ,

Kappa architecture

imtina edib bütün emalı tək kod bazası ilə aparan, yenidən hesablamanı log-u replay etməklə edən data architecture.

Kappa architecture cavab olaraq irəli sürülüb. Sual sadədir: dəqiq hesablaya bilirsə, ayrıca niyə lazımdır?

Kappa-da hər şey bir job-dur. Event-lər uzun retention-lı log-da (məsələn, Kafka topic-ində) saxlanılır və bu log həqiqət mənbəyidir. Məntiq dəyişəndə:

  1. Job-un yeni versiyası işə salınır və log-u başdan oxuyur.
  2. Yeni view köhnəsinin yanında qurulur.
  3. Yeni versiya yetişəndə sorğular ona keçirilir, köhnə job və view silinir.

Üstünlüyü bir kod bazasıdır: eyni qayda iki dəfə yazılmır və nəticələr ayrışmır. Real-time və tarixi hesablama eyni məntiqdən gəlir.

Kappa iki şərt ödənəndə kifayətdir. Birincisi, hesablama məntiqi stream-də ifadə oluna bilir. İkincisi, replay makul vaxtda bitir və tam tarixi log-da saxlamaq baha deyil (tiered storage bunu ucuzlaşdırır). İllərlə yığılmış event-i hər dəyişiklikdə yenidən emal etmək günlər çəkirsə və ya ad-hoc analitik sorğular lazımdırsa, yanına warehouse əlavə olunur.

Kappa stream-in əməliyyat qiymətini də miras alır: stateful processor-lar, tənzimləməsi, üçün sink-lər.

Ümumi səhv log retention-ı qısa saxlayıb Kappa qurmaqdır: replay lazım olanda tarixi data artıq silinib.

Əlaqəli terminlər: , , , ,

Lambda architecture

Eyni event-ləri paralel olaraq dəqiq, amma gec və tez, amma təxmini speed layer ilə emal edib nəticələri serving layer-də birləşdirən data architecture.

Lambda architecture stream processor-lara dəqiq nəticə üçün hələ etibar edilmədiyi dövrdə yaranıb. İdeya: eyni event axınını iki yolla emal etmək.

  • bütün tarixi data-nı dövri olaraq sıfırdan emal edir. Nəticə dəqiqdir, amma saatlarla gecikir.
  • Speed layer yalnız son event-ləri ilə emal edir. Nəticə saniyələrlə təzədir, amma təxmini ola bilər.
  • Serving layer sorğu zamanı hər ikisini birləşdirir: batch view-nun əhatə etmədiyi son dövr speed layer-dən götürülür. Növbəti batch işləyəndə speed layer-in nəticəsi atılır.

Bu, həm freshness, həm də dəqiqlik verir. Stream tərəfdə səhv olsa belə, onu bir neçə saat sonra özü düzəldir.

Qiyməti iki kod bazasıdır. Eyni biznes qaydası iki fərqli framework-də yazılır və sinxron saxlanılmalıdır. Zamanla implementasiyalar ayrışır: dashboard-dakı rəqəm gün ərzində bir, gecə batch-dən sonra başqa olur və hansının doğru olduğunu araşdırmaq ayrıca işə çevrilir. Bu, ML-dəki ilə eyni problemdir.

Bu gün Lambda architecture əsasən tarixi hesablama stream-də ifadə olunmayacaq qədər mürəkkəb olanda seçilir; əks halda daha sadədir.

Ümumi səhv iki layer-i fərqli komandalara verməkdir: kod ayrışması o zaman həftələrlə yox, günlərlə ölçülür.

Əlaqəli terminlər: , , , ,

Latency

Tək bir request-in göndərildiyi andan cavabın alındığı ana qədər keçən vaxt; adətən millisaniyə ilə və percentile-lər (p50, ) ilə ölçülür.

Latency bir əməliyyatın nə qədər vaxt apardığıdır: istifadəçi düyməni basır, cavab gəlir — aradakı vaxt latency-dir. isə sistemin vahid zamanda nə qədər iş gördüyüdür. İkisi əlaqəlidir, amma fərqlidir: sistem saniyədə on min request emal edə bilər, amma hər biri iki saniyə çəkə bilər.

Latency heç vaxt tək rəqəm deyil, paylanmadır. Orta dəyər (average) aldadıcıdır, çünki bir neçə çox yavaş request-i gizlədir. Buna görə percentile-lər istifadə olunur: p50 (median) tipik təcrübəni, isə ən pis 1%-i göstərir. Bax: .

Latency haradan gəlir?

  • Network round-trip-ləri — hər service çağırışı əlavə millisaniyələr deməkdir.
  • Database query-ləri, disk I/O.
  • Queue-larda gözləmə (load artdıqca kəskin böyüyür).
  • Serialization, cold start-lar, garbage collection pause-ları.

Architecture qərarları latency-yə birbaşa təsir edir. function çağırışı nanosaniyələrdir; eyni çağırış network üzərindən millisaniyələrdir. Bir request zəncirvari beş service çağırırsa, latency-lər toplanır.

Tactic-lər: , data-nı istifadəçiyə yaxın yerləşdirmək, paralel çağırışlar, asynchronous emal. Hər biri başqa yerdə xərc yaradır — adətən consistency və ya mürəkkəblik.

Əlaqəli terminlər: , , , ,

Layered architecture

Component-lərin üfüqi layer-lərdə (presentation, business logic, persistence) düzüldüyü və hər layer-in yalnız özündən aşağıdakını çağıra bildiyi .

Layered (n-tier) architecture ən tanış style-dır: yuxarıda presentation (UI, controller-lər), ortada business logic, aşağıda persistence (database access). Hər layer yalnız aşağıdakı layer-ə müraciət edir. Əksər web framework-lər və dərsliklər bu strukturu default olaraq təklif edir.

Güclü tərəfləri: anlamaq və öyrətmək asandır, yeni developer dərhal hər şeyin harada olduğunu bilir. UI məsələlərini data kodundan ayırır. Kiçik layihələr üçün tamamilə yetərlidir.

Zəif tərəfləri:

  • Pass-through layer-lər — çox vaxt business layer sadəcə çağırışı aşağı ötürür və heç bir dəyər qatmır, amma hər dəyişiklik hər üç layer-ə toxunur.
  • Domain strukturu görünmür — layering kodun texniki rolunu deyir, biznes anlayışlarını yox. Böyük layered application səliqəli etiketlənmiş mərtəbələri olan big ball of mud-a çevrilir.
  • Database-mərkəzli düşüncə — ən aşağı layer hər şeyi formalaşdırır.

Praktikada daha yaxşı yanaşma layer-ləri domain module-larının içinə qoymaqdır: hər module (ordering, billing) öz kiçik layer-lərinə malikdir. Beləcə həm texniki ayrılıq, həm də domain-ə görə əldə olunur.

Uyğun gəldiyi hal: tək komandası və hələ tam başa düşülməmiş domain-i olan kiçik və orta ölçülü application-lar.

Əlaqəli terminlər: , , ,

Load balancer

Gələn traffic-i bir neçə instance arasında paylayan və xarab instance-ları rotasiyadan çıxaran komponent; horizontal və üçün tactic.

Load balancer client ilə eyni service-in bir neçə instance-ı arasında dayanır və hər request-i onlardan birinə yönləndirir. Bu, iki şey verir: yeni instance əlavə etməklə horizontal böyümək () və xarab instance-ın traffic almağı dayandırması ().

İki səviyyə var:

  • L4 — TCP/UDP səviyyəsində işləyir, yalnız IP və port görür. Çox sürətlidir və protokoldan asılı deyil.
  • L7 — HTTP-ni anlayır, path, header və ya cookie-yə görə routing edə bilir. Daha çevikdir, amma request-i parse etdiyi üçün daha bahadır.

Tipik alqoritmlər: round robin (növbə ilə), least connections (ən az məşğul instance-a) və (eyni key həmişə eyni node-a). Hansı instance-ın sağlam olduğunu load balancer müəyyən edir.

Session affinity (sticky session) istifadəçini həmişə eyni instance-a bağlayır. Bu, yaddaşdakı session state-i qoruyur, amma yükü qeyri-bərabər paylayır və instance yıxılanda session-lar itir. Stateless service-lər bu ehtiyacı tamamilə aradan qaldırır.

Load balancer-in özü də request yolunda bir hop və potensial single point of failure-dur. Managed cloud load balancer-lər adətən daxildə həll edir; öz qurduğunuz isə ən azı iki nüsxə və tələb edir.

Ümumi səhv ya çox səthi (yalnız port açıqdır), ya da çox dərin (bütün dependency-lər) etməkdir. İkincisi ortaq database-in qısa problemində bütün instance-ları eyni anda rotasiyadan çıxarır.

Əlaqəli terminlər: , , , , ,

Load shedding

Overload zamanı service-in request-lərin bir hissəsini dərhal rədd etməsi ki, qalanlarına normal xidmət göstərə bilsin.

Hər service-in tutumu var. Yük bu tutumu keçəndə iki seçim qalır: hamıya yavaş cavab vermək və ya bəzilərinə dərhal "yox" deyib qalanlarına vaxtında cavab vermək. Birinci seçim cəlbedici görünür, amma praktikada daha pisdir: queue-lar böyüyür, keçir, client-lər edir və yük daha da artır. Sonda heç kimə uğurlu cavab getmir.

Load shedding ikinci seçimi edir. Service öz vəziyyətinə (concurrent request sayı, queue uzunluğu, CPU, ) baxır və hədd aşılanda yeni request-ləri ucuz şəkildə, əsas işi görməzdən əvvəl rədd edir. Rədd edilən request 503 və ya 429 alır, client isə ilə sonra yenidən cəhd edə bilər.

Vacib sual nəyi atmaqdır. Bütün request-lər bərabər deyil:

  • checkout və login request-ləri analytics event-lərindən önəmlidir;
  • istifadəçinin gözlədiyi request-lər background job-lardan önəmlidir;
  • deadline-ı artıq keçmiş request-i emal etməyə ümumiyyətlə dəyməz.

Bunun üçün request-lərə prioritet əvvəlcədən verilməlidir.

Load shedding fərqlidir: client başına sabit qaydadır, load shedding isə server-in real vaxtda ümumi vəziyyətinə reaksiyasıdır. İkisi birlikdə işləyir.

Ümumi səhv load shedding-i prioritetsiz qurmaqdır: overload zamanı vacib traffic də digərləri ilə eyni ehtimalla atılır.

Əlaqəli terminlər: , , , ,

Message broker

Producer-lərdən mesajları qəbul edib consumer-lərə çatdıran ara infrastructure; Kafka, RabbitMQ, SQS kimi sistemlər producer ilə consumer-i bir-birindən ayırır.

Message broker component-lərin bir-birini birbaşa çağırmadan əlaqə saxlamasına imkan verən vasitəçidir. Producer mesajı broker-ə yazır və işinə davam edir; consumer onu öz tempində oxuyur. Bu, və asynchronous emalın əsasıdır.

İki əsas model var:

  • Queue (RabbitMQ, SQS) — hər mesaj bir consumer tərəfindən emal olunur, sonra silinir. İş paylamaq üçün uyğundur.
  • Log / stream (Kafka) — mesajlar sıralı log-da saxlanılır, bir neçə consumer qrupu eyni stream-i müstəqil oxuyur və hətta geri qayıdıb təkrar oxuya bilir.

Nə qazandırır? Temporal : consumer çöksə, mesajlar gözləyir. Load leveling: qəfil traffic artımı queue-da yığılır, backend-i boğmur. Yeni consumer-ləri producer-ə toxunmadan əlavə etmək imkanı.

Nəyə başa gəlir? Broker özü kritik infrastructure-dur və operate edilməlidir. Delivery semantics-i (adətən ) duplicate-lər deməkdir. Mesajların sırası yalnız partition daxilində zəmanətlidir. Debug çətinləşir, çünki request-in yolu bir neçə asynchronous addıma bölünür.

Tipik problem: database-ə yazıb sonra broker-ə publish etmək — ikincisi fail olarsa, event itir. və ya bunu həll edir.

Əlaqəli terminlər: , , ,

Microservice

Öz data-sına sahib olan, network üzərindən əlaqə saxlayan və digərlərindən asılı olmadan oluna bilən kiçik service; microservice architecture belə service-lərdən qurulur.

Microservice architecture-də sistem çoxsaylı müstəqil service-lərə bölünür. Hər service bir biznes imkanına (adətən bir ) cavabdehdir, öz database-inə sahibdir və digərləri ilə yalnız API və ya event-lər vasitəsilə danışır. Təşkiledici prinsip ölçü deyil, -dir: komanda digər komandalarla koordinasiya etmədən edə bilməlidir.

Güclü tərəfləri: komandalar bir-birindən olur; hər service ayrıca scale oluna, fərqli texnologiya istifadə edə və (düzgün design olunubsa) bütün sistemi çökdürmədən fail ola bilər.

Zəif tərəfləri: network artıq application-ın içindədir. , , versioning, distributed transaction-lar, və local development — hamısı çətinləşir. Güclü platform dəstəyi (CI/CD, service discovery, , monitoring) olmadan microservice-lər geri qaytarılmayan vergiyə çevrilir.

Nə vaxt mənalıdır? cadence-ləri həqiqətən toqquşan bir neçə komanda və bu qədər service-i işlətmək üçün platform yetkinliyi olanda. Tək komanda üçün demək olar ki, heç vaxt.

Ən böyük səhv: data ownership-i həll etmədən bölmək. ilə microservice-lər . Daha təhlükəsiz yol başlamaq və module-ları yalnız konkret səbəbi olduqda extract etməkdir.

Əlaqəli terminlər: , , , ,

Modular monolith

Tək kimi işləyən, amma daxildə explicit interface-ləri, öz data-sı və enforce edilən dependency qaydaları olan module-lara bölünmüş sistem.

Modular monolith operational sadəliyini təşkilati üstünlüklərinin çoxu ilə birləşdirir. Sistem bir process, bir , bir database instance-ıdır, amma daxildə ordering, billing, inventory kimi module-lar aydın sərhədlərlə ayrılıb.

Əsas qaydalar:

  • Hər module öz cədvəllərinə sahibdir; başqa module-un cədvəlinə birbaşa query yazılmır.
  • Module-lararası müraciət yalnız published API (public interface) vasitəsilə gedir.
  • Dependency istiqamətləri tooling ilə enforce olunur (ArchUnit, dependency-cruiser, build sistemi səviyyəsində module-lar).

Güclü tərəfləri: function çağırışı network çağırışından sürətli və etibarlıdır; transaction-lar sadədir; module-lararası refactoring distributed migration deyil, compiler-in yoxladığı dəyişiklikdir. Ən vacibi — sonradan bölünmə üçün ən yaxşı başlanğıc nöqtəsidir: hər module namizəd service-dir.

Zəif tərəfləri: intizam avtomatik enforce edilməsə, boundary-lər aşınır və sistem adi big ball of mud-a qayıdır. Scaling kobuddur — hər şey birlikdə scale olunur. Bütün komandalar eyni qatarını paylaşır.

Praktik misal: mövcud tapıb onları explicit module-lara çevirmək, aralarındakı shared cədvəlləri aradan qaldırmaq — bu, extraction-dan əvvəlki zəruri addımdır və bir çox sistem elə burada dayanmalıdır.

Əlaqəli terminlər: , , , ,

Monolith

Bütün funksionallığı tək kimi build və olunan application; özlüyündə pis deyil, sadəcə daxili strukturu intizam tələb edir.

Monolith bütün kodun bir build olunduğu və bir unit kimi edildiyi sistemdir. Söz tez-tez təhqiramiz mənada işlənir, amma bu ədalətsizdir: dünyanın ən uğurlu məhsullarının çoxu monolith kimi başlayıb və bəziləri hələ də elə qalır.

Üstünlükləri:

  • Operational sadəlik — bir , bir log axını, bir monitoring nöqtəsi.
  • Sürətli və etibarlı daxili çağırışlar — network yoxdur, yoxdur.
  • Sadə transaction-lar — bir database, ACID zəmanətləri.
  • Asan refactoring — IDE və compiler bütün kodu görür.

Problemlər adətən monolith-in özündən deyil, onun daxili strukturunun olmamasından yaranır. Boundary-siz monolith "big ball of mud"a çevrilir: hər şey hər şeydən asılıdır, kiçik dəyişiklik gözlənilməz yerləri sındırır, çox komanda eyni kodda bir-birinə mane olur.

Monolith-in həqiqi məhdudiyyətləri: bütün sistem birlikdə scale olunur; bir komandanın bug-ı hamının bloklaya bilər; texnologiya seçimi hamı üçün eynidir.

Praktik tövsiyə: monolith ilə başlayın, amma onu kimi qurun. Monolith-dən extraction lazım olanda ilə incremental gedin, big-bang rewrite-dan qaçın.

Əlaqəli terminlər: , , ,

MTBF

Mean Time Between Failures: sistemin və ya komponentin iki ardıcıl failure arasında normal işlədiyi orta vaxt.

MTBF failure-ların nə qədər nadir baş verdiyini ölçür. ilə birlikdə müəyyən edir: = MTBF / (MTBF + ). Məsələn, ayda bir dəfə (təxminən 720 saatdan bir) xarab olan və 45 dəqiqəyə bərpa olunan service-in təxminən 99.9%-dir.

Termin hardware dünyasından gəlir, orada disk və ya server-in istehsalçı tərəfindən elan edilmiş xarakteristikasıdır. Software sistemlərində isə MTBF-ə ən çox komponentin özü yox, dəyişikliklər təsir edir: deploy-lar, config dəyişiklikləri və dependency-lərdəki problemlər.

MTBF-i artırmağın yolları:

  • Daha yaxşı test və review, xüsusən də config dəyişiklikləri üçün.
  • , ki tək komponentin xarab olması sistemin failure-una çevrilməsin.
  • Kiçik və tez-tez deploy-lar, ki hər biri daha az risk daşısın.

Ümumi səhv MTBF-i tək hədəf etməkdir. Dəyişiklikləri dayandıraraq MTBF-i artırmaq olar, amma bu, sistemi təkamül edə bilməyən sistemə çevirir. Praktikada MTBF ilə birlikdə idarə olunmalıdır.

Əlaqəli terminlər: , ,

MTTR

Mean Time To Recovery: failure aşkarlandıqdan sistemin normal işə qayıtmasına qədər keçən orta vaxt.

MTTR failure-dan nə qədər tez çıxdığınızı ölçür. O, formulunun yarısıdır: = / ( + MTTR). Failure-ların sayını azaltmaq çətin və bahalıdır, amma onlardan çıxmaq vaxtını azaltmaq çox vaxt daha ucuzdur.

MTTR bir neçə mərhələdən ibarətdir və hər biri ayrıca yaxşılaşdırıla bilər:

  • Aşkarlama — monitoring və alerting problemi nə qədər tez görür?
  • Diaqnoz — log, metric və trace-lər səbəbi nə qədər tez göstərir?
  • Bərpa — , və ya restart nə qədər tez həyata keçir?
  • Təsdiq — sistemin həqiqətən sağaldığını haradan bilirik?

Architecture qərarları MTTR-ə birbaşa təsir edir. Stateless service-lər bir neçə saniyəyə yenidən başladılır. Avtomatik problemi istifadəçilərin kiçik hissəsində tutur. yeni kodu olmadan söndürməyə imkan verir. Data migration-ları isə adətən MTTR-in ən pis düşmənidir, çünki onları geri qaytarmaq çətindir.

Ümumi səhv yalnız , yəni "heç vaxt xarab olmasın"a fokuslanmaqdır. Etibarlı sistemlər az xarab olan yox, tez sağalan sistemlərdir.

Əlaqəli terminlər: , , , ,

NoSQL

Relational modeldən fərqli data modelləri (key-value, document, wide-column, graph) istifadə edən database-lərin ümumi adı.

NoSQL vahid texnologiya deyil, relational olmayan database-lərin geniş ailəsidir. Əsas növlər:

  • Key-value (Redis, DynamoDB) — key ilə çox sürətli oxuma və yazma; key-dən başqa heç nə ilə axtarmaq olmur.
  • Document (MongoDB) — bütöv obyektləri nested strukturla saxlayır; sənədlər arası join zəifdir.
  • Wide-column (Cassandra, Bigtable) — nəhəng yazma həcmi; sorğular əvvəlcədən bilinməli, data onlara görə modelləşdirilməlidir.
  • Graph (Neo4j) — əlaqələr üzərində çox addımlı keçid üçün.

Seçim access pattern-dən başlamalıdır: data hansı key ilə oxunur, hansı sorğular tez-tez işləyir, hansı əməliyyatlar atomik olmalıdır.

"NoSQL-də schema yoxdur" mifdir. Schema həmişə var; relational database onu yazma anında yoxlayır (schema-on-write), document store isə onun yoxlanmasını oxuyan koda buraxır (schema-on-read). Köhnə formatda yazılmış sənədləri hər reader handle etməlidir.

NoSQL adətən horizontal scaling-i asanlaşdırır, amma bunun qarşılığında join-lər, çevik sorğular və çox obyektli transaction-lar zəifləyir. Müasir relational database-lər (məsələn JSONB ilə Postgres) bir çox NoSQL ehtiyacını da qarşılayır.

Ümumi səhv NoSQL-i "scale lazım olacaq" deyə erkən seçməkdir. Bu gün itirilən sorğu çevikliyi gələcəkdə gəlməyə bilən yükün qiymətidir.

Əlaqəli terminlər: , , , ,

Object storage

Faylları (şəkil, video, backup) key ilə saxlayan ucuz, praktiki olaraq limitsiz storage; məsələn Amazon S3.

Object storage böyük, strukturlaşdırılmamış data üçündür: şəkillər, video-lar, PDF-lər, log arxivləri, backup-lar. Hər obyekt unikal key ilə saxlanır və bütöv şəkildə yazılır və oxunur. Fayl sistemi kimi qovluq iyerarxiyası, database kimi sorğu yoxdur, amma qiymət aşağı, durability isə çox yüksəkdir.

Faylları database-də BLOB kimi saxlamaq adətən səhvdir: database şişir, backup və yavaşlayır, bahalı storage ucuz data ilə dolur.

Standart pattern blob ilə metadata-nın ayrılmasıdır. Faylın özü object storage-də saxlanır. Sahibi, ölçüsü, tarixi, icazələri və object key-i isə database-də saxlanır. Axtarış və icazə yoxlaması database-də, bayt-lar isə storage-də qalır.

Yükləmə və endirmə üçün presigned URL istifadə olunur: server client-ə konkret obyekt üçün qısa müddətli, imzalanmış link verir və client faylı birbaşa storage ilə mübadilə edir. Application server-i gigabaytları ötürməkdən azad olur. Oxuma tərəfində önünə adətən qoyulur.

Ümumi səhv bucket-ləri ictimai açıq qoymaqdır. Bir çox data sızması məhz yanlış konfiqurasiya olunmuş bucket-dən baş verib; default olaraq hər şey private olmalı, giriş isə presigned URL ilə verilməlidir.

Əlaqəli terminlər: , ,

Observability

Sistemin xaricə verdiyi siqnallara (log, metric, trace) baxaraq onun daxilində nə baş verdiyini, o cümlədən əvvəlcədən təxmin edilməyən suallara cavabı nə qədər tez tapa bildiyimizi göstərən .

Monitoring əvvəlcədən bildiyimiz problemləri izləyir: disk dolubmu, error rate həddi keçibmi? Observability isə daha geniş sualdır: sistem gözlənilmədən pis davrananda, heç kimin əvvəlcədən dashboard qurmadığı bir sualı (məsələn, "niyə yalnız bir region-da, yalnız bir client versiyasında var?") nə qədər tez cavablandıra bilirik?

Observability adətən üç siqnal üzərində qurulur:

  • Log-lar — ayrı-ayrı event-lərin zəngin konteksti; ən baha siqnal.
  • Metric-lər — aqreqasiya olunmuş rəqəmlər; ucuz, alert üçün ideal, amma detalı itirir.
  • Trace-lər — bir request-in service-lər boyunca yolu; distributed sistemdə vaxtın harada itdiyini göstərir.

Bu siqnalları ortaq identifikatorlar (, ) birləşdirir. Onlar olmadan üç ayrı məlumat adası alınır.

Observability diaqnoz mərhələsini qısaldır və ölçülməsi üçün bazadır. O, architectural qərardır: context propagation hər hop-dan və asinxron sərhəddən keçməlidir, ona görə platform səviyyəsində standartlaşdırılır və sonradan əlavə etmək bahalı olur. Telemetry-nin öz xərci də var və ona büdcə ayrılmalıdır.

Ümumi səhv observability-ni alət almaqla eyniləşdirməkdir. Kommersial platforma ötürməyən service-lərdən ibarət sistemi görünən etmir.

Əlaqəli terminlər: , , , , ,

OLTP və OLAP

İki fərqli iş yükü: OLTP çoxlu kiçik transaction-la application-a xidmət edir, OLAP isə böyük data həcmi üzərində analitik sorğular icra edir.

OLTP (online transaction processing) və OLAP (online analytical processing) database-lərin iki fərqli istifadə formasıdır və onları eyni sistemdə qarışdırmaq ən çox yayılmış performance problemlərindən biridir.

OLTP application-ın gündəlik işidir: sifariş yarat, balansı yenilə, istifadəçini id ilə tap. Sorğular kiçikdir, key və ya index ilə bir neçə sətrə toxunur, saniyədə minlərlə icra olunur. Əsas metric və concurrency-dir. Storage adətən row-oriented olur, çünki bütöv sətir birlikdə oxunur və yazılır.

OLAP analitikanın işidir: son ilin satışlarını region və məhsul üzrə qruplaşdır. Sorğu az olur, amma hər biri milyonlarla sətri scan edir və yalnız bir neçə sütuna baxır. Əsas metric . Storage columnar olur: sütunlar ayrıca və sıxılmış saxlanılır, lazım olmayanlar heç oxunmur.

Analitik sorğunu production database-də işlətmək onun və I/O-sunu tutur və istifadəçinin hiss etdiyi artırır. Ona görə data və ya dövri export ilə ayrıca analitik sistemə, məsələn , köçürülür.

Ümumi səhv "hələlik işlədərik" deyib OLAP yükünü row-oriented OLTP database-də saxlamaqdır: yük ayrılır, amma alət yenə yanlışdır.

Əlaqəli terminlər: , , , , ,

Outage

Sistemin və ya onun vacib bir hissəsinin istifadəçilər üçün işləmədiyi, yaxud istifadəyə yararsız dərəcədə pis işlədiyi dövr.

Outage istifadəçinin gözü ilə ölçülür. Server-lər işləyə bilər, amma checkout düyməsi xəta verirsə, istifadəçi üçün bu outage-dir. Səhifələrin açılması 30 saniyə çəkirsə, texniki cəhətdən sistem "işləyir", amma praktikada yenə outage-dir.

Outage-lər müxtəlif ölçüdə olur:

  • Tam outage — sistem heç cavab vermir.
  • Qismən outage — bəzi funksiyalar, bəzi region-lar və ya bəzi istifadəçilər təsirlənir.
  • Degradation — sistem işləyir, amma çox yavaş və ya xətalarla.

İki rəqəm outage-in nə qədər ağrılı olduğunu göstərir: nə qədər tez-tez baş verir () və nə qədər tez bərpa olunur (). məqsədləri, SLO-lar və məhz outage-lərə ayrılan "icazəli vaxtı" ölçür.

Architecture qərarları outage-in həm ehtimalına, həm də ölçüsünə təsir edir. və ehtimalı azaldır, bölmək və problemi kiçik hissədə saxlayır, isə tam outage-i qismən outage-ə çevirir.

Faydalı sual: hansı outage xəbərlərə düşərdi? Cavab hansı hissənin ən çox qorunmalı olduğunu göstərir. Ümumi səhv outage-dən sonra yalnız günahkarı axtarmaqdır. Blameless postmortem isə səbəbi və onu təkrarlanmağa qoymayan dəyişikliyi tapır.

Əlaqəli terminlər: , , , , , ,

Outbox pattern

Event-ləri biznes dəyişikliyi ilə eyni database transaction-ında outbox cədvəlinə yazıb, sonra ayrıca relay process-i ilə broker-ə publish edən pattern; event itkisinin qarşısını alır.

Outbox pattern "" problemini həll edir. Service həm database-ə yazmalı, həm də event-i Kafka-ya publish etməlidir. Bunları ardıcıl etsəniz: database commit uğurlu olur, Kafka publish-i fail olur — event itir. Əks ardıcıllıqda isə event gedir, amma database olur — mövcud olmayan dəyişiklik haqqında event yayılır.

Həll: event-i birbaşa broker-ə yox, eyni database-dəki outbox cədvəlinə yazın — biznes dəyişikliyi ilə eyni transaction-da. Ya ikisi birlikdə commit olur, ya heç biri. Sonra ayrıca relay process-i outbox-u oxuyur (polling və ya ilə), event-ləri broker-ə publish edir və sətirləri göndərilmiş kimi işarələyir.

Nəticələr:

  • + Event itkisi yoxdur; database transaction-ı single source of truth-dur.
  • + Publish biznes koduna toxunmadan oluna bilər.
  • − Əlavə (relay-in poll intervalı qədər).
  • − Delivery : relay publish edib işarələməmiş çökərsə, event təkrarlanır, ona görə consumer-lər olmalıdır.
  • − Outbox cədvəli müntəzəm prune edilməlidir.

Alternativlər: (kövrək və əksər broker-lər dəstəkləmir), birbaşa (əlavə operational component). Kitabdakı -0007 nümunəsi məhz bu seçimi sənədləşdirir.

Əlaqəli terminlər: , , , ,

PACELC

genişlənməsi: partition olanda ilə consistency, partition olmayanda isə ilə consistency arasında seçim etməlisiniz.

teoremi yalnız network partition anından danışır. Partition-lar isə nadirdir; sistem vaxtının böyük hissəsini normal rejimdə keçirir. PACELC bu boşluğu doldurur:

  • Partition olanda: Availability və ya Consistency.
  • Else, yəni normal vaxtda: Latency və ya Consistency.

İkinci hissə gündəlik təcrübəyə daha yaxındır. Güclü consistency hər yazmada replica-lar arasında koordinasiya tələb edir. Replica-lar fərqli region-lardadırsa, hər yazmaya onlarla, bəlkə yüzlərlə millisaniyə əlavə olunur. Async isə cavabı dərhal qaytarır, amma oxuma köhnə data görə bilər.

Database-ləri bu çərçivədə təsvir etmək daha dürüstdür. Leader-dən sync oxuyan klassik relational setup PC/EC-dir: hər iki halda consistency seçir. Default konfiqurasiyada Cassandra və DynamoDB PA/EL-dir: hər iki halda cavab və sürət seçir. Bir çox sistem bu seçimi hər sorğu üçün ayrıca tənzimləməyə icazə verir: eyni database-də balans oxuması leader-dən, like sayı isə ən yaxın gələ bilər.

Ümumi səhv PACELC-i database-in sabit etiketi kimi qəbul etməkdir. Əksər müasir database-lərdə bu, konfiqurasiya və hər əməliyyat üçün verilən qərardır.

Əlaqəli terminlər: , , , ,

Pagination

Böyük nəticə dəstini hissə-hissə qaytarma texnikası; əsas variantları offset və cursor pagination-dır.

Heç bir API "bütün sifarişləri" bir cavabda qaytarmamalıdır. Pagination nəticəni səhifələrə bölür və client növbəti səhifəni ayrıca istəyir.

Offset pagination (?offset=10000&limit=50) ən sadə formadır və istənilən səhifəyə birbaşa keçməyə imkan verir. İki problemi var:

  • Database hər dəfə offset qədər sətri keçib atmalıdır, ona görə dərin səhifələr getdikcə yavaşlayır.
  • Səhifələr arasında yeni yazı əlavə olunanda və ya silinəndə elementlər təkrarlanır və ya itir.

Cursor pagination (?after=<cursor>&limit=50) "son gördüyüm elementdən sonrakıları ver" deyir. Cursor adətən sıralama açarını (məsələn, created_at və id) kodlaşdıran opaque string-dir. Sorğu index üzərində birbaşa həmin nöqtədən başlayır, ona görə səhifənin dərinliyindən asılı olmayaraq sabit sürətlə işləyir və dəyişən data-da ardıcıl qalır.

Cursor-un qiyməti: "47-ci səhifəyə keç" və ya "cəmi neçə səhifə var" mümkün olmur və ya bahalıdır. Admin panelindəki kiçik cədvəl üçün offset tamamilə kifayətdir. Böyük, tez dəyişən data, feed-lər və sinxronizasiya API-ləri üçün isə cursor demək olar ki, həmişə daha yaxşıdır.

Cursor-u opaque saxlamaq vacibdir: client onu parse etməməli və özü qurmamalıdır. Əks halda daxili sıralama açarı contract-ın hissəsinə çevrilir.

Ümumi səhv pagination-ı sonradan əlavə etməyə saxlamaqdır: səhifələnməmiş list endpoint-ini səhifələnmiş etmək mövcud client-lər üçün .

Əlaqəli terminlər: , , ,

Partial failure

Distributed sistemdə bəzi hissələrin işləyib, bəzilərinin xarab olduğu vəziyyət; tez-tez hansı hissənin xarab olduğunu dəqiq bilmək belə mümkün olmur.

Tək process-də işlər sadədir: ya proqram işləyir, ya da çöküb. Distributed sistemdə isə üçüncü vəziyyət yaranır: bir hissə işləyir, digəri yox. Payment service cavab verir, amma inventory susur. Database işləyir, amma bir geridə qalıb.

Partial failure-ın ən çətin tərəfi qeyri-müəyyənlikdir. Başqa service-ə request göndərdiniz və cavab gəlmədi. Bu üç fərqli şey ola bilər:

  • Request heç çatmayıb.
  • Request çatıb və icra olunub, amma cavab yolda itib.
  • Service sadəcə çox yavaşdır və cavab hələ gələcək.

Kənardan bunları ayırd etmək mümkün deyil. Ona görə "ödəniş alındımı?" sualı birdən çətinləşir.

Partial failure tam çöküşdən daha təhlükəlidir, çünki gizli qalır. Hər komponentin öz dashboard-u yaşıl görünə bilər, amma istifadəçi xəta alır. Yavaş dependency isə thread-ləri və connection-ları tutub, problemi zəncirvari olaraq digər service-lərə yayır.

Buna qarşı istifadə olunan əsas vasitələr: (sonsuz gözləməmək), ilə (təkrar cəhd zərərsiz olsun), (xəstə dependency-yə yük verməmək), (problemi bir hissədə saxlamaq) və (problemi görmək).

Ümumi səhv network-ü etibarlı saymaq və remote call-u adi function call kimi yazmaqdır. Hər remote call uğursuz ola bilər və kod bunu nəzərə almalıdır.

Əlaqəli terminlər: , , , , , ,

Partition key

zamanı hər sətrin hansı düşəcəyini müəyyən edən dəyər; məsələn tenant_id və ya user_id.

Partition key data-nın shard-lar arasında necə bölünəcəyini müəyyən edir. Bu, data qatında geri qaytarılması ən baha qərarlardan biridir, çünki onu dəyişmək bütün data-nı köçürmək deməkdir.

Yaxşı partition key iki şərtə cavab verir:

  • Sorğular onu daxil edir. Ən tez-tez işləyən sorğular bir getməlidir. Key-i daxil etməyən sorğu bütün shard-lara gedir (scatter-gather) və ən yavaş qədər yavaşdır.
  • Yük bərabər paylanır. Bir neçə dəyər yükün böyük hissəsini yaradırsa, yaranır.

Bu iki şərt tez-tez ziddiyyət təşkil edir. SaaS-da tenant_id birinci şərtə əla uyğundur: tenant daxilindəki sorğu və transaction-lar bir qalır. Amma bir nəhəng tenant öz yandıra bilər. order_id yükü bərabər paylayır, amma tenant-ın bütün order-lərini siyahılamaq hər getməyi tələb edir.

Partition key seçimi layiqdir: context-də sorğu paylanmasını və ən böyük key-in payını, consequence-lərdə isə hansı sorğuların bahalaşdığını yazın.

Ümumi səhv key-i data modelinə görə seçməkdir. Onu access pattern-ə görə seçin: hansı sorğu ən çox işləyir və hansı əməliyyat atomik olmalıdır?

Əlaqəli terminlər: , , ,

Pub/sub

Publisher-in mesajı topic-ə göndərdiyi və həmin topic-ə abunə olan bütün subscriber-lərin onun nüsxəsini aldığı messaging modeli.

Pub/sub (publish-subscribe) modelində publisher mesajı konkret alıcıya deyil, topic-ə göndərir. Topic-ə abunə olan hər subscriber mesajın öz nüsxəsini alır. Publisher subscriber-ləri tanımır, onların sayını da bilmir.

Queue modeli ilə fərq:

  • Queue — hər mesajı yalnız bir consumer emal edir. İşi worker-lər arasında paylamaq üçündür.
  • Pub/sub — hər mesaj bütün subscriber-lərə çatır. Bir event bir neçə müstəqil reaksiya doğuranda istifadə olunur.

Məsələn, "sifariş yaradıldı" event-i anbar, analitika və bildiriş service-lərinə eyni anda lazımdır. Pub/sub ilə order service onların heç birini çağırmır. Yeni subscriber əlavə etmək üçün publisher-ə toxunmaq lazım deyil. Bu, əsas mexanizmidir və xeyli azaldır.

Praktikada bir çox broker hər iki modeli birləşdirir: topic-ə bir neçə subscriber group abunə olur, hər group daxilində isə mesajlar consumer-lər arasında queue kimi paylanır.

Qiyməti: delivery adətən , yəni subscriber-lər olmalıdır. Kimin hansı event-ə asılı olduğu kodda görünmür. Event-in formatı faktiki olaraq public interface-ə çevrilir və onu dəyişmək ola bilər.

Ümumi səhv pub/sub-ı gizli command kanalı kimi istifadə etməkdir: publisher konkret subscriber-in konkret işi görəcəyini gözləyirsə, bu, deyil, sadəcə görünməz synchronous asılılıqdır.

Əlaqəli terminlər: , , , , ,

QPS

Queries per second: sistemin və ya komponentin bir saniyədə qəbul etdiyi request sayı, load-un əsas ölçüsü.

QPS (və ya RPS) load-u ifadə etməyin ən geniş yayılmış yoludur. Capacity hesablamasında o, adətən gündəlik aktiv istifadəçilərdən çıxarılır: QPS ≈ DAU × gündəlik əməliyyat sayı / 86 400. Gündə təxminən 10^5 saniyə olduğundan, gündə 1 milyon əməliyyat təxminən 10–12 QPS-ə bərabərdir.

Bir neçə vacib nüans:

  • Average və peak fərqlidir. Traffic gün ərzində bərabər paylanmır. Adi məhsulda peak orta göstəricidən 2–3 dəfə, kampaniya zamanı 10 dəfə və daha çox yüksək ola bilər. Sistem peak üçün ölçülür.
  • Oxuma və yazma ayrı sayılmalıdır. 5 000 read QPS ilə 5 000 write QPS tamamilə fərqli architecture tələb edir. Oxumanı ilə ucuzlaşdırmaq olar, yazmanı isə çox vaxt yox.
  • Bir user request bir neçə daxili request-dir. Feed səhifəsi açılanda backend-də onlarla çağırış ola bilər. Hər komponentin öz QPS-i var.

QPS konkret formasıdır və ilə birlikdə oxunmalıdır: "10 000 QPS" yalnız " 200 ms-dən aşağı olmaqla" kimi şərtlə mənalıdır.

Ümumi səhv yalnız orta QPS-ə baxmaqdır: orta göstəriciyə görə ölçülmüş sistem məhz ən vacib anda, peak-də yıxılır.

Əlaqəli terminlər: , , , ,

Quality attribute

Sistemin nə etdiyini deyil, bunu nə qədər yaxşı etdiyini təsvir edən xüsusiyyət: performance, , , security, maintainability, və s.

sistemin nə etdiyini deyir: "istifadəçi order yerləşdirə bilər". Quality attribute-lər isə bunu hansı keyfiyyətlə etdiyini deyir: nə qədər tez, nə qədər etibarlı, nə qədər təhlükəsiz, dəyişdirmək nəyə başa gəlir. Bunlara tez-tez "non-functional requirement-lər" və ya "-ility"-lər deyilir.

Architecture-i ən çox məhz quality attribute-lər formalaşdırır. Eyni malik iki sistem tamamilə fərqli architecture-ə malik ola bilər, çünki biri , digəri cost-u prioritetləşdirir. Struktur bir və ya bir neçə attribute üçün tactic-dir: üçün, performance üçün, modular boundary-lər maintainability üçün.

İki praktik qayda:

  • Konkretləşdirin. " olmalıdır" test oluna bilməz. formasında yazın: source, stimulus, environment, , response, measure.
  • Sıralayın. Hər attribute arzuolunandır, ona görə siyahı kifayət deyil. " consistency-dən üstündür" qərardır; "hər ikisi vacibdir" məsuliyyətdən qaçmaqdır.

Prioritetləri architect uydurmur: onlar biznes modelindən, ən qorxulu failure-dan, artım əyrisindən və komandanın imkanlarından gəlir.

Faydalı vərdiş: kimsə yeni struktur təklif edəndə soruşun — bu hansı attribute-ə xidmət edir? Cavab yoxdursa, bu, çox güman ki, .

Əlaqəli terminlər: , , , ,

Quality attribute scenario

test oluna bilən formaya salan şablon: source, stimulus, environment, , response və measure.

"Sistem sürətli olmalıdır" və ya " olmalıdır" kimi cümlələr nə design üçün əsas ola bilər, nə test oluna bilər, nə də mübahisə mövzusu ola bilər. Quality attribute scenario bu qeyri-müəyyənliyi altı hissəli sabit forma ilə aradan qaldırır:

  • Source — şərti kim və ya nə yaradır (istifadəçilər, başqa sistem, attacker).
  • Stimulus — nə baş verir (traffic artımı, node-un çökməsi, yeni tələbi).
  • Environment — hansı şəraitdə (normal load, peak, degraded mode).
  • — sistemin hansı hissəsi təsirlənir.
  • Response — sistem nə etməlidir.
  • Measure — uğuru necə ölçəcəyik.

Nümunə: məhsulun launch-ının ilk saatında web client-lərdən gələn traffic catalogue service-ə qarşı saniyədə 500 request-dən 5 000-ə yüksəlir. Service xidmət göstərməyə davam edir, 300 ms-dən aşağı qalır və heç bir manual müdaxilə tələb olunmur.

Bu formada yazılmış scenario-nun dəyəri ondadır ki, ona qarşı design edə, load test apara və kimsə onu pozacaq dəyişiklik təklif edəndə göstərə bilərsiniz. Measure hissəsi isə birbaşa çevrilə bilər.

Scenario-lar yalnız runtime attribute-lər üçün deyil. Maintainability scenario-su: "yeni ödəniş provider-i əlavə etmək bir developer-in iki gününü alır və yalnız payments module-una toxunur."

Tipik səhv measure-u yazmamaqdır — onsuz scenario yenə arzuya çevrilir.

Əlaqəli terminlər: , , ,

Rate limiting

Client-in müəyyən müddət ərzində göndərə biləcəyi request sayının məhdudlaşdırılması; həm sistemin yükdən, həm də abuse-dan qorunması üçün istifadə olunur.

Rate limiting iki rol oynayır. Reliability baxımından o, bir client-in, səhv yazılmış script-in və ya cron job-un hamının resursunu yeməsinin qarşısını alır. Security baxımından isə brute force, scraping və DoS hücumlarına qarşı ilk müdafiə xəttidir. Mexanizm hər iki halda eynidir.

Ən çox yayılmış alqoritmlər:

  • Token bucket — bucket sabit sürətlə token-larla dolur, hər request bir xərcləyir. Bucket-in tutumu qədər qısa burst-a icazə verir, orta sürəti isə məhdudlaşdırır. API-lər üçün ən geniş yayılmış seçimdir.
  • Leaky bucket — request-lər queue-ya girir və sabit sürətlə emal olunur. Çıxışı hamarlayır, burst-lara icazə vermir.
  • Fixed və — müəyyən pəncərədə request-ləri sayır. Sadədir, amma fixed window pəncərə sərhədində ikiqat burst-a imkan verir.

Limit aşılanda HTTP-də standart cavab 429 Too Many Requests status-u və Retry-After header-idir. Bu, client-ə nə vaxt yenidən cəhd edə biləcəyini deyir.

Limit adətən , istifadəçi, IP və ya tenant üzrə tətbiq olunur və çox vaxt səviyyəsində yerləşir. Bir neçə instance olanda sayğac ortaq store-da (məsələn, Redis-də) saxlanmalıdır, əks halda hər instance limiti ayrıca hesablayır.

Ümumi səhv limitləri real istifadəyə baxmadan seçməkdir: çox aşağı limit legitim istifadəçiləri bloklayır, çox yüksək limit isə heç nədən qorumur.

Əlaqəli terminlər: , , , ,

Redundancy

Bir komponentin xarab olmasının sistemin dayanmasına səbəb olmaması üçün critical komponentlərin artıq nüsxələrinin saxlanması.

Redundancy üçün əsas tactic-dir: hər critical komponentin ehtiyatı olsun ki, tək bir failure bütün sistemi dayandırmasın. Riyaziyyat təsirlidir: hər biri 99% available olan iki müstəqil heç olmasa birinin işləmə ehtimalı 99.99%-dir.

Ölçü adətən N+k ilə ifadə olunur. N yükü daşımaq üçün lazım olan nüsxə sayıdır:

  • N+1 — bir nüsxənin itkisinə dözür.
  • N+2 — bir nüsxə planlı maintenance-də olarkən digərinin gözlənilmədən xarab olmasına da dözür.

Əsas şərt müstəqillikdir. Eyni rack-da, eyni zone-da yerləşən, eyni config-lə və eyni anda edilən replica-lar birlikdə xarab olur. Bu halda üç əslində bir və riyaziyyat işləmir. Real böyük hissəsi məhz belə correlated failure-lardan yaranır.

Redundancy-nin qiyməti var: əlavə infrastructure xərci, state-in sinxronlaşdırılması və daha mürəkkəb əməliyyatlar. Stateless komponentlər üçün redundancy ucuzdur, sadəcə daha çox instance işə salınır. Stateful komponentlər üçün isə və bütün çətinlikləri gəlir.

Ümumi səhv redundancy-ni yalnız server səviyyəsində düşünüb ortaq dependency-ləri unutmaqdır: hamının istifadə etdiyi tək DNS, auth service və ya database.

Əlaqəli terminlər: , , ,

Regulatory compliance

Sistemin qanun və sənaye standartlarının tələblərinə (GDPR, PCI DSS, HIPAA və s.) uyğun olması; architecture üçün dəyişməz .

Bəzi tələblər biznesdən yox, qanundan gəlir. Şəxsi məlumatların qorunması (GDPR), kart məlumatlarının emalı (PCI DSS), tibbi məlumatlar (HIPAA) və ya maliyyə qaydaları sistemin nə edə biləcəyini və necə qurulmalı olduğunu müəyyən edir.

Compliance digər bir məsələdə fərqlənir: onu qoymaq olmur. Performance-ı bir az azaltmaq mümkündür, amma qanunu "bir az" pozmaq mümkün deyil. Ona görə compliance adətən attribute kimi yox, kimi davranır.

Architecture-a tipik təsirləri:

  • Data harada saxlanır — bəzi data müəyyən ölkədən çıxmamalıdır ().
  • Kim nəyə baxa bilir — giriş məhdudlaşdırılır və hər giriş qeyd olunur.
  • Data nə qədər saxlanır — müəyyən müddətdən sonra silinməli və ya istifadəçinin tələbi ilə silinə bilməlidir.
  • Həssas hissələr necə ayrılır — məsələn, kart məlumatları ayrıca, kiçik bir hissədə saxlanır ki, ciddi yoxlama yalnız o hissəyə tətbiq olunsun.

Son nöqtə vacib architecture texnikasıdır: həssas data-nı kiçik bir yerə yığmaq compliance-ın əhatə dairəsini və xərcini kəskin azaldır.

Ümumi səhv compliance-ı layihənin sonunda yoxlanılacaq bir şey kimi görməkdir. "İstifadəçinin bütün data-sını silin" tələbi data onlarla service-ə və backup-a səpələnəndən sonra gəlirsə, onu yerinə yetirmək çox çətin olur.

Əlaqəli terminlər: , , ,

Release

Dəyişikliyin istifadəçilər üçün əlçatan edilməsi; fərqli olaraq kodun production-da olması deyil, onun işə qoşulmasıdır.

Release yeni funksionallığın və ya düzəlişin istifadəçilərə çatdırılmasıdır. Sözü iki mənada eşidəcəksiniz: versiyalanmış paket (v2.3.0 release-i) və istifadəçiyə açılma anı. Architecture söhbətlərində ikinci məna vacibdir, çünki o, ayrıla bilər.

≠ release. Kod production-a oluna, amma arxasında söndürülmüş qala bilər. Sonra flag əvvəlcə daxili istifadəçilər, sonra 5%, sonra hamı üçün açılır. Problem çıxarsa, tələb etmir — flag söndürülür. Bu ayrılıq release riskini xeyli azaldır.

Release cadence komanda və architecture strukturunu birbaşa təsir edir. Bir neçə komanda eyni işləyirsə, onlar ortaq release qatarını paylaşır və bir komandanın gecikməsi hamını gözlədir. əsas vədi məhz budur: hər komanda öz cadence-i ilə release etsin.

Release pipeline-ı təhlükəsizlik baxımından da vacibdir: imzalanmış , reproducible build-lər, approval gate-lər. Amma hər gate həm control, həm də gecikmədir — security ilə arasında klassik .

Tipik səhvlər: release-ləri seyrək və böyük etmək (hər biri risk yığır), release notes yazmamaq, deprecation dövrü olmadan release etmək.

Əlaqəli terminlər: , , , ,

Replication

Eyni data-nın bir neçə node-da nüsxəsinin saxlanması; , oxuma və üçün tactic.

Replication eyni data-nı bir neçə node-da saxlamaqdır. O, üç şeyi birdən verir: bir node düşəndə data əlçatan qalır (), oxumaları replica-lara paylamaq olur () və data istifadəçiyə yaxın region-da yerləşə bilər ().

Üç əsas model var:

  • Leader-follower — bütün yazmalar bir leader-ə gedir, follower-lər onun dəyişikliklərini təkrarlayır. Ən sadə və ən geniş yayılmış modeldir.
  • Multi-leader — bir neçə node yazma qəbul edir, məsələn hər region-da bir. Eyni data iki yerdə dəyişəndə conflict yaranır və onu həll etmək lazımdır.
  • Leaderless — client bir neçə node-a yazır və bir neçəsindən oxuyur. W + R > N quorum qaydası oxumanın ən azı bir təzə nüsxəyə rast gəlməsini təmin edir.

İkinci seçim synchronous və asynchronous arasındadır. Sync replication data itkisinin qarşısını alır, amma hər yazmanı ən yavaş replica qədər yavaşladır. Async sürətlidir, amma yaradır və leader düşəndə son yazmalar itə bilər.

Ümumi səhv replica-nı backup hesab etməkdir. Səhvən silinmiş cədvəl bir saniyə sonra bütün replica-lardan da silinir; replication hardware failure-dan qoruyur, insan səhvindən yox.

Əlaqəli terminlər: , , , , ,

Replication lag

Asynchronous follower-in leader-dən geri qalma müddəti; bu müddətdə oxuyan istifadəçi köhnə data görür.

Asynchronous leader yazmanı təsdiqləyir və dəyişikliyi follower-lərə sonra göndərir. Aradakı gecikmə replication lag adlanır. Normal vəziyyətdə o, millisaniyələrlə ölçülür, amma yük artanda, şəbəkə yavaşlayanda və ya follower ağır sorğu ilə məşğul olanda saniyələrə, bəzən dəqiqələrə çata bilər.

İstifadəçi lag-ı xəta kimi yox, qəribə davranış kimi görür:

  • Profilində adını dəyişir, səhifəni yeniləyir və köhnə adı görür ( pozulur).
  • Şərh bir yeniləmədə görünür, növbətidə yox olur, çünki oxuma daha geridə qalan düşür (monotonic reads pozulur).

Tipik həllər: istifadəçinin öz data-sını qısa müddət leader-dən oxumaq, client-də son yazmanın versiyasını saxlayıb ən azı o versiyanı tələb etmək, istifadəçini eyni bağlamaq.

Lag-ı metrik kimi izləmək və onun üçün alert qurmaq vacibdir. Böyük lag zamanı da təhlükəlidir: geri qalan follower leader olarsa, təsdiqlənmiş yazmalar itir.

Ümumi səhv lag-ı "həmişə kiçikdir" deyə nəzərə almamaqdır. O, məhz yük ən böyük olanda, yəni ən çox istifadəçinin baxdığı anda böyüyür.

Əlaqəli terminlər: , , ,

REST

Sistemi resource-lar toplusu kimi modelləşdirən və onlar üzərindəki əməliyyatları standart HTTP metodları ilə ifadə edən API stili.

REST (Representational State Transfer) API-ni əməliyyatlar yox, resource-lar ətrafında qurur: /orders, /orders/42, /orders/42/items. Nə etmək istədiyinizi URL yox, HTTP metodu deyir: GET oxuyur, POST yaradır, PUT tam əvəz edir, PATCH qismən dəyişir, DELETE silir.

REST-in əsas gücü ondadır ki, bu semantika bütün internet infrastrukturu tərəfindən başa düşülür. GET təhlükəsiz və olduğu üçün proxy-lər və onu edə bilər. PUT və DELETE olduğu üçün etmək təhlükəsizdir. POST isə deyil, ona görə kritik əməliyyatlarda tələb edir.

Status kodları da contract-ın hissəsidir:

  • 2xx — uğur.
  • 4xx — client-in səhvi; eyni request-i təkrarlamaq mənasızdır.
  • 5xx — server-in səhvi; sonra etmək olar.

Hər xəta üçün 200 qaytarıb body-də "success: false" yazmaq monitoring-i və kor edir.

REST public API-lər və partnyor inteqrasiyaları üçün ən təhlükəsiz seçimdir: hər dildə client var, curl ilə test etmək olar, browser-də tam işləyir. Zəif tərəfi over-fetching və under-fetching-dir, həm də hər domain əməliyyatının resource-a təbii sığmamasıdır. "Sifarişi ləğv et" kimi əməliyyatlar üçün POST /orders/42/cancel normal praktikadır.

Ümumi səhv REST-i database cədvəllərinin HTTP örtüyü kimi dizayn etməkdir: resource-lar consumer-in ehtiyaclarını deyil, daxili schema-nı əks etdirir və hər refactoring çevrilir.

Əlaqəli terminlər: , , , ,

Retry

Uğursuz çağırışın avtomatik təkrarlanması; keçici xətalara qarşı effektivdir, amma yalnız əməliyyatlar üçün təhlükəsizdir.

Retry keçici xətalara qarşı ən sadə müdafiədir: connection qırılıb, instance restart olub, leader seçimi gedir. Belə hallarda ikinci cəhd çox vaxt uğurlu olur.

Retry yalnız iki şərt ödənəndə təhlükəsizdir:

  • Xəta keçicidir. 503, connection reset, təkrarlana bilər. 400, 401 və validation xətaları isə yox: onları təkrarlamaq eyni nəticəni verəcək.
  • Əməliyyat . alan request server-də artıq icra olunmuş ola bilər. olmayan əməliyyatı (məsələn, ödənişi) olmadan təkrarlamaq dublikat yaradır.

Retry-lar dərhal edilməməlidir: və ilə zamana yayılmalıdır.

Ən böyük təhlükə retry amplification-dır. Request dörd qatdan keçirsə və hər qat 3 cəhd edirsə, ən dərindəki service bir istifadəçi hərəkəti üçün 3 × 3 × 3 = 27 request alır, özü də ən zəif anında. Bu, retry storm adlanır və uzadır. Həll yolları: retry-ı yalnız bir qatda etmək və retry budget istifadə etmək, yəni retry-ların sayını ümumi request-lərin müəyyən faizi ilə (məsələn, 10%) məhdudlaşdırmaq.

Ümumi səhv retry-ı həm kodda, həm kitabxanada, həm də service mesh-də eyni anda aktiv etməkdir: amplification heç kimin bilmədiyi yerdə yaranır.

Əlaqəli terminlər: , , , , ,

Reverse proxy

Client-lərin adından deyil, server-lərin adından request qəbul edib onları backend-ə ötürən ara server; termination, və routing üçün istifadə olunur.

Reverse proxy backend server-lərin qarşısında dayanır. Client onunla danışır və arxada neçə server olduğunu, onların harada yerləşdiyini bilmir. Forward proxy-dən fərqi budur: forward proxy client-ləri təmsil edir (məsələn, korporativ şəbəkədə internetə çıxış), reverse proxy isə server-ləri.

Tipik vəzifələr:

  • termination — şifrələməni proxy-də bitirmək, backend-i sertifikat idarəçiliyindən azad etmək.
  • Routing — path və ya host-a görə müxtəlif backend-lərə yönləndirmək.
  • və compression — cavabları saxlamaq və sıxmaq.
  • — bir neçə backend arasında paylamaq.
  • Qoruma — backend-in ünvanını gizlətmək, sadə filtrlər tətbiq etmək.

nginx, HAProxy və Envoy ən çox istifadə olunan nümunələrdir. , və əslində reverse proxy-nin ixtisaslaşmış formalarıdır. Fərq vurğudadır: paylamaya, API səviyyəli anlayışlara (consumer, quota, versiya), isə coğrafi fokuslanır.

Ümumi səhv proxy config-ini architecture-un bir hissəsi kimi görməməkdir. Routing qaydaları, timeout-lar və rewrite-lar kod qədər davranışa təsir edir, amma çox vaxt review-dan və version control-dan kənarda qalır.

Əlaqəli terminlər: , , ,

Rolling deployment

Instance-ların qrup-qrup yeni versiyaya keçirildiyi strategiyası; əlavə infrastructure tələb etmir, amma boyunca iki versiya eyni anda işləyir.

Rolling deployment-də yeni versiya bütün instance-lara bir anda yox, növbə ilə çatdırılır. bir və ya bir neçə instance-ı traffic-dən çıxarır, onlar yenilənir, keçəndən sonra geri qaytarılır və proses növbəti qrupla davam edir. Kubernetes default strategiyası budur.

Nə verir:

  • Sadəlik və ucuzluq. İkinci mühit lazım deyil, yalnız bir neçə əlavə instance üçün ehtiyat tutum kifayətdir.
  • Tədricilik. Yeni versiya keçmirsə, rollout dayanır və zərər ilk qrupla məhdudlaşır.

Nəyə başa gəlir:

  • İki versiya eyni anda işləyir. boyunca bir istifadəçinin request-ləri həm köhnə, həm yeni versiyaya düşə bilər. Ona görə yeni versiya köhnə ilə eyni API-ni, mesaj formatını və database schema-sını başa düşməlidir. Schema dəyişiklikləri üçün lazımdır.
  • Yavaş . Geri qayıtmaq tərsinə ikinci rolling deployment deməkdir və böyük sistemdə dəqiqələr çəkir.
  • keyfiyyəti həlledicidir. yalnız "process işləyir" yoxlayırsa, səhv versiya bütün instance-lara rahatlıqla yayılır.

Stateless service-lər və backward compatible dəyişikliklər üçün yaxşı default-dur. Daha kiçik lazımdırsa, , ani lazımdırsa daha uyğundur.

Ümumi səhv rolling deployment-i təhlükəsiz hesab etməkdir: səhv tədricən yayılır, amma metrikaya baxan yoxdursa, nəticədə yenə hamıya çatır.

Əlaqəli terminlər: , , , , ,

RPC

Uzaq sistemdəki function-u lokal function çağırışı kimi göstərən, əməliyyat yönümlü kommunikasiya modeli.

RPC (Remote Procedure Call) API-ni resource-lar yox, function-lar dilində təsvir edir: CreateOrder, CancelOrder, GetShippingQuote. Client kodda adi function çağırır, framework isə argumentləri serialize edib şəbəkə üzərindən göndərir, cavabı geri qaytarır.

Bu model domain əməliyyatlarını təbii ifadə edir. resource-a sığdırmaq çətin olan "hesablamanı başlat" və ya "sifarişi təsdiqlə" kimi əməliyyatlar RPC-də sadəcə bir method-dur. Contract adətən IDL (interface definition language) ilə yazılır və ondan client və server kodu generasiya olunur. Bu da tip təhlükəsizliyi verir.

RPC-nin tələsi də elə rahatlığındadır. Uzaq çağırış lokal kimi görünür, amma:

  • min dəfələrlə yavaşdır;
  • şəbəkə səbəbindən uğursuz ola, düşə bilər;
  • cavab gəlmədikdə əməliyyatın icra olunub-olunmadığı bilinmir.

Interface bu fərqləri gizlədirsə, developer-lər döngü içində uzaq çağırışlar edir və chatty interface-lər yaranır. Nəticədə toplanır, isə səviyyəsinə çatır.

RPC-nin tarixi uzundur: CORBA, Java RMI, SOAP, Thrift. Bu gün ən geniş yayılmış forması .

Ümumi səhv RPC interface-ini lokal object interface-i kimi dizayn etməkdir: kiçik, tez-tez çağırılan method-lar əvəzinə şəbəkə çağırışları az və iri olmalıdır, uğursuzluq isə contract-ın açıq hissəsi olmalıdır.

Əlaqəli terminlər: , , , ,

Saga

Bir neçə service-i əhatə edən biznes əməliyyatını lokal transaction-lar ardıcıllığına bölən pattern; addım uğursuz olanda əvvəlki addımlar compensating transaction-larla tarazlanır.

Saga distributed transaction-ı olmadan həll edir. Proses addımlara bölünür və hər addım öz service-ində lokal transaction kimi commit olunur. Məsələn: order yaradılır, ödəniş bloklanır, məhsul rezerv olunur, göndəriş planlaşdırılır. Anbarda məhsul yoxdursa, artıq bitmiş addımlar tərs sırada compensating transaction-larla ləğv edilir: ödəniş azad edilir, order ləğv olunur.

Koordinasiyanın iki üsulu var:

  • Choreography. Mərkəz yoxdur; hər service event-ləri dinləyir, öz addımını atır və yeni event publish edir. Loose verir, amma proses heç bir yerdə yazılmır, onu izləmək çətindir və addımlar artdıqca service-lər arasında cyclic dependency yaranır.
  • Orchestration. Ayrıca orchestrator hər service-ə command göndərir və prosesi state machine kimi saxlayır. Görünürlük və xəta idarəsi asanlaşır, amma orchestrator mərkəzləşir və biznes məntiqi oraya axmağa meyl edir.

Saga ACID deyil. Aralıq vəziyyətlər digər əməliyyatlara görünür (isolation yoxdur), ona görə "pending" state-lər və semantic lock-lar lazım olur. Event-lər etibarlı publish edilməlidir (), addımlar isə olmalıdır, çünki delivery .

Ümumi səhv saga-nı boundary problemini ört-basdır etmək üçün istifadə etməkdir: hər biznes əməliyyatı saga tələb edirsə, service-lər çox güman ki, yanlış yerdən bölünüb.

Əlaqəli terminlər: , , , , ,

Scalability

Load artdıqca sistemin cost və qəbuledilən hədlərdə saxlayaraq işləməyə davam etmək qabiliyyəti.

Scalability sualı belədir: load on dəfə artanda və cost ilə nə baş verir? Scalable sistem əlavə resurs verildikdə proporsional olaraq daha çox iş görür və istifadəçi təcrübəsi pisləşmir.

İki əsas yanaşma var:

  • Vertical scaling (scale up) — daha güclü maşın: daha çox CPU, RAM. Sadədir, amma tavanı var və bahalaşır.
  • Horizontal scaling (scale out) — daha çox maşın. Nəzəri olaraq limitsizdir, amma sistemin stateless olmasını, data-nın bölünməsini () və koordinasiyanı tələb edir.

Scalability-ni çətinləşdirən adətən compute deyil, state-dir. Stateless web server-ləri əlavə etmək asandır; tək database isə tez-tez bottleneck olur. Tactic-lər: read replica-lar, , , asynchronous emal və queue-lar ilə load leveling.

"Scalable olmalıdır" cümləsi faydasızdır. Konkret yazın: hansı load, hansı müddətdə, hansı hədəfi ilə, manual müdaxiləsiz və ya müdaxilə ilə. Gündə on istifadəçi gözləyən sistemlə on milyon gözləyənin "kifayət" anlayışı fərqlidir.

Tipik səhv: heç vaxt gəlməyəcək load üçün erkən optimize etmək. və bu gün xərc çıxarır; ehtiyac gələndə isə adətən başqa bir yerdə bottleneck çıxır.

Əlaqəli terminlər: , , , ,

Seam

Sistemdə elə təbii xətt ki, onun boyunca kodu az dağıntı ilə ayırmaq, əvəz etmək və ya müstəqil dəyişdirmək mümkündür.

Seam termini Michael Feathers-in legacy kodla işləmə haqqındakı işindən gəlir: davranışı həmin yeri redaktə etmədən dəyişə biləcəyiniz nöqtə. Architecture səviyyəsində isə seam sistemin içində artıq mövcud olan, amma hələ explicit edilməmiş təbii boundary-dir — tikiş yeri kimi, orada parçalamaq ən az zərər verir.

Seam-ləri necə tapmaq olar?

  • Change history — hansı fayllar həmişə birlikdə dəyişir, hansılar heç vaxt? Birlikdə dəyişməyən qruplar arasında seam var.
  • Domain lüğəti — "Customer" sözünün mənasının dəyişdiyi yer çox vaxt sərhədidir.
  • Data access — hansı kod hansı cədvəllərə yazır? Az kəsişən qruplar namizəddir.

Mövcud decomposition-ında ilk addım seam-ləri müəyyən etmək, ikinci addım isə onları içində explicit etməkdir: published interface-lər, öz cədvəlləri olan module-lar, aralarında shared data yoxdur. Yalnız bundan sonra, konkret səbəb olduqda, module ayrıca service kimi extract edilir.

Test kontekstində də seam vacibdir: interface arxasındakı dependency-ni test double ilə əvəz etmək imkanı. hər port bir seam-dir.

Səhv: seam-i real kodda deyil, diaqramda axtarmaq. Diaqram niyyəti, change history reallığı göstərir.

Əlaqəli terminlər: , , , ,

Serverless

Kodun event-lərlə trigger olunan function-lar şəklində olunduğu, server-lərin, scaling-in və lifecycle-ın isə platform tərəfindən idarə edildiyi model.

Serverless-də siz server idarə etmirsiniz: function yazırsınız (AWS Lambda, Google Cloud Functions, Azure Functions), platform onu HTTP request, queue mesajı, fayl yüklənməsi və ya cron kimi event-lər gələndə işə salır. Server-lər əlbəttə var — sadəcə onlar sizin probleminiz deyil.

Güclü tərəfləri:

  • Sıfır idle cost — heç bir request yoxdursa, heç nə ödəmirsiniz.
  • Avtomatik scaling — platform paralel instance-ları özü qaldırır.
  • Minimal operational səth — patching, platformanın işidir.

Zəif tərəfləri:

  • Cold start — uzun müddət çağırılmayan function-un ilk çağırışı yüzlərlə millisaniyə, bəzən saniyələr gecikir.
  • Execution limitləri — müddət, yaddaş, payload ölçüsü məhduddur.
  • Vendor lock-in — trigger-lər, IAM, event formatları platformaya məxsusdur.
  • Görünməz architecture — sistem console-da bir-birinə bağlanmış yüz xırda function-a çevrilir və heç kim bütöv mənzərəni review edə bilmir.
  • Sabit yüksək load-da cost ənənəvi server-lərdən baha ola bilər.

Uyğun gəldiyi hallar: spiky və ya aşağı həcmli workload-lar, glue code, event handler-lər, şəkil emalı kimi asynchronous tapşırıqlar. Məsələn, gecə reporting pipeline-ı və ya fayl yükləndikdə thumbnail yaradan function.

Praktik tövsiyə: function-ları və onların trigger-lərini infrastructure as code ilə saxlayın ki, architecture repository-də görünsün.

Əlaqəli terminlər: , , ,

Sharding

Data-nı görə hissələrə bölüb hər hissəni ayrı node-da saxlamaq; tək node-un limitini aşan yazma və həcm üçün horizontal scaling.

Sharding (partitioning) data-nı hissələrə bölür və hər hissəni ayrı node-da saxlayır. eyni data-nı çoxaldırsa, sharding fərqli data-nı paylayır. Tək node-un yaza bildiyindən çox yazmağın və tək diskə sığmayan data-nı saxlamağın yeganə yolu budur.

İki əsas üsul var:

  • Range partitioning — key aralıqlarına görə. Range sorğuları effektivdir, amma ardıcıl key-lər (timestamp) yeni yazmaları bir shard-a yığır.
  • partitioning — key-in görə. Yük bərabər paylanır, amma range sorğuları bütün shard-lara getməlidir.

Sharding-in qiyməti böyükdür. daxil etməyən sorğular bütün shard-lara gedir. Shard-lar arası transaction və ya tələb edir. Səhv seçilmiş dəyişmək bütün data-nı canlı sistemdə yenidən paylamaq deməkdir.

Buna görə sharding son çarə olmalıdır. Ondan əvvəl daha böyük server (vertical scaling), read replica-lar, və köhnə data-nın arxivləşdirilməsi sınanmalıdır. Bunlar çox vaxt illərlə kifayət edir.

Ümumi səhv sharding-i "gələcəkdə lazım olar" deyə erkən tətbiq etməkdir. Siz join-ləri və transaction-ları bu gün itirirsiniz, scale isə heç vaxt gəlməyə bilər.

Əlaqəli terminlər: , , , , ,

SLA

Service Level Agreement: provider ilə müştəri arasında service səviyyəsi haqqında müqavilə; pozulanda maliyyə və ya hüquqi nəticələri olur.

SLA xarici öhdəlikdir. komandanın daxili hədəfidirsə, SLA müştəriyə yazılı verilmiş vəddir və pozulanda nəticəsi var: adətən service kreditləri, pulun bir hissəsinin qaytarılması və ya müqaviləni ləğv etmək hüququ.

SLA-nı ayırmaq vacibdir, çünki onlar fərqli auditoriyaya xidmət edir. SLA hüquqşünaslar və satış üçündür, isə mühəndislər üçün. Qızıl qayda: daxili həmişə xarici SLA-dan sərt olmalıdır. Müştəriyə 99.5% vəd edirsinizsə, daxili hədəf 99.9% ola bilər. Aradakı fərq problemi müqavilə pozulmamışdan əvvəl görmək üçün buferdir.

Architecture baxımından SLA-lar iki istiqamətdə işləyir. Sizin müştərilərə verdiyiniz SLA-lar sisteminizin minimum availability-sini müəyyən edir. Dependency-lərinizin sizə verdiyi SLA-lar isə maksimumu müəyyən edir: critical path-də 99.9% SLA-lı xarici provider varsa, siz 99.99% vəd edə bilməzsiniz.

Ümumi səhv provider-in SLA-sını onun real etibarlılığı ilə qarışdırmaqdır. SLA yalnız pozulanda nə qədər kompensasiya alacağınızı deyir. Downtime-ı isə geri qaytarmır.

Əlaqəli terminlər: , ,

SLI

Service Level Indicator: service-in istifadəçiyə nə qədər yaxşı xidmət etdiyini göstərən ölçü, adətən yaxşı event-lərin bütün etibarlı event-lərə nisbəti kimi.

SLI "sistem yaxşı işləyirmi?" sualını ölçülə bilən rəqəmə çevirir. Ən faydalı forma nisbətdir: yaxşı event-lər / bütün etibarlı event-lər. Belə SLI həmişə 0 ilə 100% arasında olur və müxtəlif service-ləri eyni dildə müqayisə etməyə imkan verir.

Tipik SLI növləri:

  • — uğurla cavablanan request-lərin faizi (məsələn, 5xx olmayanlar).
  • — müəyyən həddən tez cavablanan request-lərin faizi (məsələn, 300 ms-dən tez).
  • Freshness — son N dəqiqədə yenilənmiş data-nın faizi.
  • Correctness — düzgün nəticə verən əməliyyatların faizi.

Əsas qayda: SLI istifadəçinin təcrübəsini ölçməlidir, server-in daxili vəziyyətini yox. CPU 20%-də ola bilər, istifadəçi isə yenə də alır. Buna görə ölçü istifadəçiyə ən yaxın nöqtədən, yəni , və ya client-in özündən götürülür.

Ümumi səhv onlarla SLI müəyyən etməkdir. Hər əsas istifadəçi axını üçün bir-iki SLI kifayətdir. Qalanları debugging üçün metric-lərdir.

Əlaqəli terminlər: , , ,

SLO

Service Level Objective: üçün müəyyən vaxt pəncərəsində qoyulmuş daxili hədəf, məsələn, "28 gündə request-lərin 99.9%-i uğurlu olmalıdır".

SLO komandanın özünə verdiyi reliability vədidir. O üç hissədən ibarətdir: hansı ölçülür, hədəf nədir və hansı vaxt pəncərəsində. Məsələn: "checkout request-lərinin 99.9%-i 28 günlük sürüşən pəncərədə 500 ms-dən tez və xətasız cavablanmalıdır."

SLO-nun dəyəri onun tərsindən gəlir: 99.9% hədəf 0.1% deməkdir. Bu budget reliability ilə yeni sürəti arasında ortaq valyutaya çevrilir.

Yaxşı SLO-nun xüsusiyyətləri:

  • İstifadəçinin hiss etdiyi şeyə bağlıdır, infrastructure-un detallarına yox.
  • sərtdir, ki müqavilə pozulmamışdan əvvəl reaksiya vermək üçün vaxt qalsın.
  • 100% deyil. Mükəmməllik nə mümkündür, nə də lazımdır. Əlavə hər "9" təxminən on dəfə baha başa gəlir.
  • Hər axın üçün ayrıdır. Checkout və tövsiyələr bloku eyni hədəfə layiq deyil.

Ümumi səhv SLO-nu bir dəfə yazıb unutmaqdır. Əgər SLO heç vaxt qərarlara təsir etmirsə, yəni pozulanda heç nə dəyişmirsə, o sadəcə dashboard-dakı bir rəqəmdir.

Əlaqəli terminlər: , , ,

Strangler pattern

Köhnə sistemi tədricən əvəz etmə yolu: traffic-in bir hissəsini yeni component-ə yönəldib yoxlamaq, genişləndirmək və köhnə path-i nəhayət söndürmək.

Strangler fig pattern-in adı Martin Fowler-in müşahidə etdiyi tropik əncir ağacından gəlir: o, ev sahibi ağacın ətrafında böyüyür və zamanla onu tamamilə əvəz edir. Proqram təminatında ideya eynidir — köhnə sistemi bir anda yenidən yazmaq əvəzinə, onu hissə-hissə yeni kodla əvəz etmək.

Necə işləyir:

  1. Köhnə sistemin qarşısına routing layer qoyun (, , və ya kod daxilində facade).
  2. Bir funksionallığı (məsələn, invoice yaradılması) yeni component-də implement edin.
  3. Traffic-in kiçik dilimini yeni component-ə yönəldin, nəticələri yoxlayın — bəzən köhnə və yeni nəticələri paralel müqayisə edərək.
  4. Dilimi genişləndirin, köhnə path-i təqaüdə göndərin.
  5. Növbəti funksionallıqla təkrarlayın.

Niyə vacibdir? Big-bang rewrite-lar iki il itirməyin ən etibarlı yoludur: köhnə sistem dayanmadan dəyişir, yeni sistem heç vaxt "hazır" olmur və keçid günü bütün risk bir anda reallaşır. Strangler pattern isə hər addımda dəyər çatdırır və hər addım geri qaytarıla bilər.

Çətinliklər: keçid dövründə data iki yerdə yaşaya bilər və sinxronizasiya tələb edir ( burada kömək edir). Routing layer-in özü əlavə mürəkkəblikdir. Ən böyük risk isə prosesi yarıda dayandırıb həmişəlik iki sistemi saxlamaqdır.

Əlaqəli terminlər: , , ,

Stream processing

Sonu olmayan event axınını gəldikcə, saniyələr içində emal etmək; event time, və state idarəsi tələb edir.

Stream processing data-nı toplu halda yox, event-lər gəldikcə emal edir. Input-un sonu yoxdur, ona görə "bütün data gəlib, indi hesabla" anı da yoxdur. Fraud aşkarlama, real-time , monitoring və alert-lər tipik istifadə hallarıdır.

Batch-dən fərqli olaraq burada üç çətin məsələ var:

  • Event time və processing time. Hadisənin baş verdiyi an ilə sistemə çatdığı an fərqlidir, mobil client-lərdə saatlarla fərqlənə bilər. Düzgün aggregate event time üzrə hesablanmalıdır.
  • və late data. Sonsuz axın pəncərələrə bölünür, isə pəncərəni nə vaxt bağlamağın mümkün olduğunu təxmin edir.
  • State. Sayğaclar, açıq session-lar, join üçün gözləyən event-lər yaddaşda saxlanılır və yıxılmaya qarşı mütəmadi checkpoint olunur.

Tipik arxitekturada event-lər və ya log-da (məsələn, Kafka) saxlanılır, stream processor (Flink, Kafka Streams) onları oxuyub nəticəni database-ə, və ya başqa topic-ə yazır.

Qiyməti əməliyyatdadır: 24/7 işləyən stateful cluster, lag monitorinqi, state bərpası və scale olunanda state-in yenidən paylanması. Freshness tələbi həqiqətən saniyələr deyilsə, batch çox vaxt daha ucuz və etibarlıdır.

Ümumi səhv processing time üzrə aggregate hesablamaqdır: şəbəkə gecikməsi və ya consumer lag-ı biznes rəqəmlərini səssizcə dəyişir.

Əlaqəli terminlər: , , , , , ,

Structured logging

Log event-lərini sərbəst mətn əvəzinə adlandırılmış sahələri olan obyekt kimi (adətən JSON) yazmaq; axtarışı, filter-ləməni və aqreqasiyanı mümkün edir.

Sərbəst mətnli log ("User 42 failed to pay order 9001") insan üçün oxunaqlıdır, amma maşın üçün deyil. Ondan "son bir saatda hansı ödəniş provider-i ilə neçə xəta oldu?" sualına cavab almaq üçün kövrək regex-lər yazmaq lazımdır.

Structured log eyni event-i sahələrlə yazır: user_id, order_id, provider, status, duration_ms, trace_id. Belə log-larda istənilən sahəyə görə filter etmək, qruplaşdırmaq və saymaq olur. Metric-lərdən fərqli olaraq, burada yüksək cardinality-li sahələr (user_id, ) problem deyil, əksinə, "problem yalnız bir qrup istifadəçidədirmi?" sualına məhz onlar cavab verir.

Praktik qaydalar:

  • Ortaq sahə adları platform səviyyəsində razılaşdırılır, əks halda bir service user_id, digəri userId yazır və sorğular parçalanır.
  • hər log sətrinə avtomatik əlavə olunur.
  • Həssas data (parol, kart nömrəsi, şəxsi məlumat) log-a yazılmır və ya maskalanır.

Log-lar ən baha telemetry siqnalıdır: həcm traffic ilə xətti artır. Ona görə uğurlu request-lərə tətbiq olunur, xətalar isə tam yazılır. Saxlanma müddəti də şüurlu seçilməlidir.

Ümumi səhv hər şeyi DEBUG səviyyəsində log-lamaq və bunu saymaqdır. Çox log az log qədər faydasız ola bilər, çünki lazım olan sətir səs-küydə itir və hesab ayın sonunda gəlir.

Əlaqəli terminlər: , , ,

Tail latency

paylanmasının ən yavaş hissəsi, adətən p99 və ya p99.9 kimi percentile-lərlə ölçülür: request-lərin 99%-i bu vaxtdan tez, 1%-i isə daha gec tamamlanır.

Percentile tək orta rəqəm əvəzinə paylanma kimi təsvir edir. p50 (median) — request-lərin yarısı bundan tezdir. p99 — 99%-i bundan tezdir, ən yavaş 1% isə daha uzun çəkir. Bu ən yavaş hissəyə "tail" deyilir.

Niyə orta dəyər yox, tail? Çünki orta dəyər yalan danışır. Orta 50 ms ola bilər, amma p99 2 saniyədirsə, hər yüz istifadəçidən biri sistemi "donmuş" kimi hiss edir. Və ən aktiv istifadəçilər — ən çox request göndərənlər — tail-ə ən çox rast gələnlərdir.

Distributed sistemlərdə tail daha da vacibdir. Bir səhifə 20 backend çağırışından ibarətdirsə və hər birinin p99-u 1 saniyədirsə, səhifələrin təxminən 18%-i ən azı bir yavaş çağırışı gözləyəcək. Tail-lər fan-out ilə çoxalır.

Tail latency-nin tipik səbəbləri: garbage collection pause-ları, cold start-lar, miss-lər, queue-larda gözləmə, "noisy neighbour" effekti, retry-lar.

Tactic-lər: timeout-lar, hedged request-lər (yavaş cavabı gözləmədən ikinci göndərmək), fan-out-u azaltmaq, load-u bərabər paylamaq.

Praktikada hədəflər p99 ilə yazılır: "checkout path-ində p99 400 ms-dən aşağı qalmalıdır". Bu, həm measure hissəsi, həm də ola bilər.

Əlaqəli terminlər: , , ,

Tenancy model

Bir sistemin bir neçə müştərini (tenant-ı) necə yerləşdirdiyi: hamısı ortaq infrastrukturdadır, yoxsa hər birinin öz resursları var.

Tenant sistemdən istifadə edən ayrıca müştəridir: SaaS məhsulunda bir şirkət, bir məktəb və ya bir mağaza. Tenancy model həmin müştərilərin data-sının və resurslarının bir-birindən nə qədər ayrıldığını müəyyən edir.

Üç əsas variant var:

  • Hər şey ortaq (pool). Bütün tenant-lar eyni database-də, eyni cədvəllərdə yaşayır, hər sətirdə tenant_id olur. Ucuzdur və idarəsi asandır, amma bir tenant-ın yükü hamıya təsir edir, bir query-dəki səhv isə başqasının data-sını göstərə bilər.
  • Qismən ayrı (bridge). Ortaq application, amma hər tenant üçün ayrıca schema və ya database.
  • Tam ayrı (silo). Hər tenant öz instance-ında işləyir. Ən güclü isolation, amma ən baha və ən çətin idarə olunan variant.

Seçim adətən görə edilir: cost və sadəlik pool-a, security, compliance və böyük müştərilərin "data-mız ayrıca olsun" tələbi silo-ya doğru çəkir. Çox vaxt qarışıq model işlənir: kiçik müştərilər ortaq pool-da, iri müştərilər öz silo-larında.

Tenancy model-i sonradan dəyişmək çox bahadır, çünki o, data model-ə, , və billing-ə toxunur. Ona görə bu, erkən verilməli və qeyd edilməli qərardır.

Ümumi səhv pool modelində tenant_id filtrini hər query-də əllə yazmağa güvənməkdir. Bir unudulmuş WHERE şərti bir müştərinin data-sını digərinə göstərir. Filtr framework, database (məsələn row-level security) və ya repository səviyyəsində məcburi olmalıdır.

Əlaqəli terminlər: , , , ,

Throughput

Sistemin vahid zamanda emal etdiyi iş miqdarı: saniyədə request-lər, mesajlar, transaction-lar və ya emal olunan data həcmi.

Throughput "saniyədə nə qədər iş görə bilərik?" sualına cavab verir: (), saniyədə mesaj, dəqiqədə emal olunan sətir. tək bir request-in sürətidir; throughput isə sistemin ümumi tutumu.

İkisi arasındakı əlaqə intuitiv deyil. Batching throughput-u artırır (bir dəfəyə yüz sətir yazmaq yüz ayrı yazıdan səmərəlidir), amma hər bir elementin latency-sini artırır, çünki batch dolana qədər gözləmək lazımdır. Eləcə də sistem tutumunun hüdudlarına yaxınlaşdıqca queue-lar uzanır: throughput sabit qalır, isə kəskin artır. Buna görə sistemləri 100% yükdə deyil, ehtiyat tutumla işlətmək lazımdır.

Throughput-un bottleneck-i adətən sistemin ən zəif halqasıdır: tək database, lock olunan cədvəl, xarici API-nin . Hər yerə daha çox instance əlavə etmək bottleneck-i həll etmir, sadəcə onun qarşısında daha uzun növbə yaradır.

Ölçərkən diqqət:

  • Throughput-u həmişə hədəfi ilə birlikdə göstərin: " 300 ms-dən aşağı qalarkən saniyədə 5 000 request".
  • Uğurlu cavabları sayın; xəta qaytaran sürətli request-lər throughput deyil.
  • Load test-ləri real traffic formasına yaxın edin.

ML sistemlərində də system health monitoring-in əsas göstəricilərindən biridir: , error rate və throughput.

Əlaqəli terminlər: , , ,

Timeout

Şəbəkə çağırışının cavabını gözləmək üçün maksimal vaxt; bu vaxt keçəndə çağırış uğursuz sayılır və resurslar azad olunur.

Timeout distributed sistemdə ən əsas reliability tactic-dir. Şəbəkə üzərindən edilən çağırışın üç nəticəsi ola bilər: uğur, xəta və ya heç bir cavab. Üçüncü halda çağıran tərəf niyə cavab gəlmədiyini bilmir və timeout olmasa, sonsuza qədər gözləyə bilər. Bu müddətdə thread, connection və memory tutulu qalır.

Buna görə qayda sadədir: hər şəbəkə çağırışının timeout-u olmalıdır. Bir çox HTTP client və database driver-in default-u ya çox böyükdür, ya da ümumiyyətlə yoxdur, ona görə timeout-u açıq şəkildə təyin etmək lazımdır.

Timeout-u necə seçmək:

  • görə. Orta yox, dependency-nin və ya dəyəri əsas götürülür. 300 ms-dirsə, 400–500 ms ağlabatan başlanğıcdır.
  • Zəncir boyunca uyğunlaşdırmaqla. İstifadəçi 2 saniyədən sonra imtina edirsə, arxada 10 saniyə işləmək mənasızdır. Deadline propagation ilə ümumi büdcə ilk nöqtədə təyin olunur və hər növbəti çağırışa qalan vaxt ötürülür.

: çox qısa timeout sağlam, amma bir az yavaş cavabları xətaya çevirir və lazımsız retry-lar yaradır. Çox uzun timeout isə yavaş dependency-nin bütün sistemi boğmasına imkan verir.

Ümumi səhv timeout-u kitabxananın default-una buraxmaqdır: "timeout yoxdur" çox vaxt "sonsuz timeout var" deməkdir.

Əlaqəli terminlər: , , , ,

Trade-off

Bir xüsusiyyəti yaxşılaşdırmaq üçün digərindən şüurlu şəkildə imtina etmək; hər architecture qərarı bir digərinə qarşı dəyişir.

Architecture-də pulsuz nahar yoxdur. Hər qərar nəyisə qazandırır və nəyəsə başa gəlir. Daha sürətli write daha mürəkkəb read deməkdir; imtina deməkdir; performance verir, amma staleness və invalidation problemi gətirir.

Buna görə mücərrəd mənada "yaxşı architecture" yoxdur — yalnız müəyyən prioritetlər toplusu üçün yaxşı architecture var. da tövsiyə kimi deyil, trade-off paketi kimi qiymətləndirmək lazımdır.

Tez-tez rastlaşan trade-off-lar:

  • Consistency vs. — network partition zamanı ikisi birlikdə olmur ().
  • Performance vs. maintainability — denormalized cədvəllər və əllə tune edilmiş query-lər sürətli, amma dəyişməsi çətindir.
  • Security vs. — pipeline-dakı hər gate həm control, həm gecikmədir.
  • Flexibility vs. simplicity — abstraction layer-lər gəlməyə bilən gələcəyə hazırlaşır və bu gün xərc çıxarır.
  • vs. — batching biri üçün yaxşı, digəri üçün pisdir.

Yaxşı trade-off qərarının əlamətləri: nədən imtina edildiyi açıq deyilir, prioritetlər sıralanıb və əsaslandırma qeyd olunub. Pis qərarın əlaməti isə "hər ikisi vacibdir" cavabıdır — bu, qərar deyil, qərarı gələcəyə və production incident-ə ötürməkdir.

Əlaqəli terminlər: , , ,

Two-phase commit

Bir neçə müstəqil sistemdə transaction-ı atomik etmək üçün protokol: coordinator əvvəlcə hamıdan "hazırsan?" soruşur, hamı razıdırsa commit, əks halda abort edir.

Two-phase commit (2PC) distributed transaction-lar üçün klassik protokoldur. Məqsəd: məsələn, həm database-ə, həm yazmanın ya hər ikisinin uğurlu olması, ya da heç birinin olmaması.

İki mərhələ:

  1. Prepare — coordinator bütün iştirakçılara soruşur: dəyişikliyi commit etməyə hazırsınızmı? Hər iştirakçı dəyişikliyi davamlı yaddaşa yazır, lock-ları saxlayır və "hə" və ya "yox" cavabı verir.
  2. Commit / abort — hamı "hə" dedisə, coordinator commit əmri göndərir; kimsə "yox" dedisə və ya cavab vermədisə, hamı edir.

Nəzəri olaraq gözəldir, praktikada isə problemlidir:

  • Blocking — coordinator prepare-dən sonra çöksə, iştirakçılar lock-ları saxlayaraq qeyri-müəyyən müddət gözləyir.
  • — hər transaction əlavə network round-trip-ləri tələb edir.
  • — ən zəif iştirakçının bütün transaction-ı məhdudlaşdırır.
  • Dəstək — Kafka kimi bir çox müasir sistem XA/2PC-ni dəstəkləmir.

Buna görə 2PC nadir hallarda seçilir. Əvəzinə , və saga-lar (hər addımın kompensasiya əməliyyatı olan lokal transaction-lar ardıcıllığı) istifadə olunur. Bunlar qəbul edir, amma sistemi daha dözümlü edir.

Əlaqəli terminlər: , , ,

Virtual waiting room

Kəskin peak zamanı istifadəçiləri növbəyə yığıb sistemə yalnız onun emal edə biləcəyi sürətlə buraxan komponent.

Virtual waiting room bilet satışı, limitli məhsul buraxılışı və ya imtahan qeydiyyatı kimi hallarda istifadə olunur: bir anda gələn istifadəçi sayı backend-in tutumundan yüz, hətta min dəfə çox olur. Hamını eyni anda içəri buraxmaq əvəzinə sistem onları növbəyə düzür və saniyədə müəyyən sayda istifadəçini əsas axına keçirir.

Tipik quruluş:

  • Növbə səhifəsi verilir, ona görə də milyonlarla istifadəçiyə dözür.
  • Status endpoint-i istifadəçinin mövqeyini yüngül in-memory store-dan qaytarır.
  • İmzalanmış, qısa ömürlü növbəsi çatan istifadəçiyə verilir; əsas axına yalnız bu token-la buraxır.
  • Buraxılma sürəti backend-in real tutumuna görə tənzimlənir, dependency xarab olanda isə dayandırıla bilər.

Bu, istifadəçi səviyyəsində tətbiqidir: request-ləri rədd edir, waiting room isə onları gözlədir və istifadəçiyə nə qədər gözləyəcəyini göstərir. Waiting room həm də ədalətlilik alətidir: açılışdan əvvəl gələnləri təsadüfi sıraya düzmək sürətlə səhifə yeniləyən bot-ların üstünlüyünü aradan qaldırır.

Qiyməti var: əlavə komponent critical path-ə daxil olur və onun özü backend-dən daha available olmalıdır. İstifadəçi gözləyir və növbənin uzunluğu ilə real inventory arasında əlaqə yoxdursa, saatlarla gözləyib heç nə almaya bilər.

Ümumi səhv waiting room-u yalnız satış günü işə salmaqdır: o, load test və real yük altında yoxlanmalıdır.

Əlaqəli terminlər: , , , , , ,

Webhook

Hadisə baş verəndə server-in consumer-in əvvəlcədən qeyd etdiyi URL-ə HTTP request göndərməsi; polling-in push alternativi.

Webhook-da rollar tərsinə çevrilir: client server-dən "iş bitdimi?" deyə soruşmur, server iş bitəndə client-in qeyd etdiyi URL-ə özü request göndərir. Ödəniş provider-ləri, Git hosting-lər və SaaS platformaları inteqrasiyalar üçün geniş istifadə edir.

Polling ilə müqayisədə üstünlüyü aydındır: lazımsız trafik yoxdur, hadisə demək olar ki, dərhal çatdırılır. Amma webhook göndərən tərəf indi client-dir və client-in bütün problemlərini daşıyır:

  • . Alıcı əlçatmaz ola və ya 5xx qaytara bilər. ilə və son nəticədə uğursuz çatdırılmaların görünən siyahısı lazımdır.
  • Təkrar çatdırılma. Retry-lar səbəbindən eyni webhook bir neçə dəfə çata bilər. Alıcı event id-yə görə olmalıdır.
  • Sıralama. Webhook-ların göndərildiyi ardıcıllıqla çatacağına zəmanət yoxdur.
  • Təhlükəsizlik. URL açıqdır, istənilən kəs saxta request göndərə bilər. Payload ortaq ilə imzalanmalı ( signature) və alıcı imzanı və timestamp-ı yoxlamalıdır.

Alıcı tərəfdə yaxşı praktika webhook-u tez qəbul edib 2xx qaytarmaq, emalı isə queue-ya ötürməkdir. Uzun emal göndərənin səbəb olur və lazımsız retry-lar yaradır.

Ümumi səhv webhook-u yeganə məlumat mənbəyi etməkdir: itən webhook-lar üçün həmişə status endpoint-i və ya reconciliation mexanizmi ehtiyat yol kimi olmalıdır.

Əlaqəli terminlər: , , ,

Windowing

Sonsuz event axınını aggregate hesablamaq üçün zaman pəncərələrinə bölmək; watermark pəncərəni nə vaxt bağlamağın mümkün olduğunu müəyyən edir.

Stream-də "cəmi neçə klik oldu?" sualının cavabı yoxdur, çünki axın bitmir. Sual "hər 5 dəqiqədə neçə klik oldu?" formasına salınmalıdır. Windowing məhz budur. Üç əsas növ var:

  • Tumbling window — sabit ölçülü, üst-üstə düşməyən intervallar. Hər event tam bir pəncərəyə düşür. Dövri hesabatlar üçün tipikdir.
  • Sliding window — üst-üstə düşən intervallar, məsələn hər dəqiqə yenilənən "son 10 dəqiqə". Bir event bir neçə pəncərəyə düşür. Alert-lər və fraud qaydaları üçün uyğundur.
  • Session window — istifadəçinin aktivlik dövrü, fasilə müəyyən müddəti keçəndə bağlanır. Uzunluğu əvvəlcədən məlum deyil.

Pəncərələr event time üzrə qurulanda late data problemi yaranır: 14:00–14:05 pəncərəsinin event-i 14:20-də gələ bilər. Watermark "bu event time-dan əvvəlki event-lərin demək olar ki, hamısı gəlib" iddiasıdır; pəncərə watermark onu keçəndə bağlanır və nəticə emit olunur.

Watermark freshness ilə correctness arasında . Aqressiv watermark nəticəni tez, amma natamam verir; ehtiyatlı watermark dəqiq, amma gec. Watermark-dan sonra gələn event-lər üçün siyasət açıq seçilməlidir: atmaq, ayrıca yerə yazmaq və ya nəticəni yeniləmək.

Ümumi səhv late data siyasətini default-da buraxmaqdır: çox framework gec event-ləri səssizcə atır və rəqəmlər sistematik olaraq azalır.

Əlaqəli terminlər: , , ,