Logo

Personal Ops Runbook

Personal runbook covering infrastructure operations for Cloud, Kubernetes, OpenStack, and Ceph environments. Includes deployment and teardown procedures, node management, cluster monitoring setup, and incident response workflows compiled from day-to-day operational work. Intended strictly for personal reference — configurations and scripts are environment-specific and not guaranteed to work as-is elsewhere.

Hành trình 1 TiB film.mp4 qua Ceph RADOS — Full Deep Dive

Tổng quan hạ tầng

Infrastructure Summary:
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
  8 OSD Nodes
  Mỗi node:  24 × HDD 14TB  (data OSD)  = 336 TB raw/node
             2 × SSD 2TB    (index OSD)  = 4 TB raw/node
  Tổng HDD raw:  8 × 24 × 14TB  = 2,688 PB
  Tổng SSD raw:  8 × 2 × 2TB    = 32 TB

  EC Pool (data):    k=6, m=2  trên HDD, failure domain = node
  Index Pool:        replication 3×  trên SSD

Sơ đồ ASCII tổng thể: Data Flow

┌─────────────────────────────────────────────────────────────────────┐
                        CLIENT (aws s3 cp)                          
  film.mp4 (1 TiB)  multipart upload  128 parts × 8 MiB mặc định
  (thực tế: xem phân tích bên dưới)                                
└───────────────────────────┬─────────────────────────────────────────┘
                             HTTPS PUT (per part)
                            
┌─────────────────────────────────────────────────────────────────────┐
                    CEPH RGW (RADOS Gateway)                        
  Beast frontend  thread pool  rgw::sal::Object::write()         
                                                                     
  Mỗi part 8 MiB  stripe thành 2 RADOS objects × 4 MiB           
  (rgw_obj_stripe_size = 4 MiB default)                             
                                                                     
  ┌──────────────┐          ┌──────────────┐                        
   DATA PATH               INDEX PATH                           
   (song song)             (song song)                          
  └──────┬───────┘          └──────┬───────┘                        
└─────────┼─────────────────────────┼─────────────────────────────────┘
           librados                 librados
           aio_write               aio_write
                                  
┌─────────────────────┐  ┌─────────────────────┐
  EC POOL (HDD)          INDEX POOL (SSD)   
  k=6, m=2               repl-3             
                                            
  CRUSH: 1 OSD/node      CRUSH: 3 SSD OSDs 
  × 8 nodes              từ 3 nodes khác nhau
└────────┬────────────┘  └────────┬────────────┘
                                 
                                 
┌─────────────────────┐  ┌─────────────────────┐
  PRIMARY OSD (HDD)      PRIMARY OSD (SSD)  
  EC encode:             Ghi index entry    
  4MB  8 shards         Replicate  2 SSD  
  (6 data + 2 parity)    secondaries        
  Gửi shards                               
  7 secondary OSDs                          
└────────┬────────────┘  └────────┬────────────┘
                                 
                                 
┌─────────────────────┐  ┌─────────────────────┐
  BlueStore (HDD)        BlueStore (SSD)    
  WAL  RocksDB         WAL + Data trên    
  Data extent            cùng SSD device    
└────────┬────────────┘  └────────┬────────────┘
                                 
                                 
┌─────────────────────┐  ┌─────────────────────┐
  Linux Block Layer      Linux Block Layer  
  mq-deadline sched      none scheduler     
  NCQ 32 depth           NVMe queue         
└────────┬────────────┘  └────────┬────────────┘
                                 
                                 
┌─────────────────────┐  ┌─────────────────────┐
  HDD Physical           SSD / NVMe NAND    
  Actuator  Platter     FTL  NAND pages   
  Magnetic write                            
└─────────────────────┘  └─────────────────────┘

TẦNG 1 — Client & AWS CLI

1.1 aws s3 cp film.mp4 s3://mybucket/ thực sự làm gì?

AWS CLI phát hiện file 1 TiB (1,099,511,627,776 bytes) vượt ngưỡng multipart_threshold (mặc định 8 MiB). CLI tự động chuyển sang Multipart Upload thay vì single PUT.

Quy trình cụ thể:

  1. Initiate Multipart Upload: CLI gửi POST /mybucket/film.mp4?uploads tới RGW endpoint. RGW trả về UploadId — một chuỗi unique identifier cho toàn bộ upload session.
  2. Tính toán parts: Với multipart_chunksize mặc định = 8 MiB, file 1 TiB cần:
  3. 1 TiB / 8 MiB = 1,048,576 / 8 = 131,072 parts
  4. Tuy nhiên S3 API giới hạn tối đa 10,000 parts.
  5. AWS CLI tự động tăng chunksize để đảm bảo ≤ 10,000 parts.
  6. Chunksize mới: ceil(1 TiB / 10,000) = ceil(1,099,511,627,776 / 10,000) ≈ 109,951,163 bytes ≈ ~104.86 MiB
  7. Thực tế CLI round lên: thường chọn ~128 MiB hoặc tính ra khoảng 107 MiB tùy version.
  8. Giả sử chunksize = 107 MiB: số parts ≈ ceil(1,048,576 MiB / 107 MiB) ≈ 9,799 parts (part cuối nhỏ hơn).
  9. Concurrency: AWS CLI dùng max_concurrent_requests mặc định = 10 threads. Tức là tối đa 10 parts upload song song tại cùng thời điểm.
  10. Upload từng part: Mỗi part là một PUT /mybucket/film.mp4?partNumber=N&uploadId=XXX.
  11. Complete Multipart Upload: Sau khi tất cả parts hoàn thành, CLI gửi POST /mybucket/film.mp4?uploadId=XXX kèm danh sách tất cả (partNumber, ETag) → RGW ghép logical object.

1.2 HTTP Headers quan trọng

Mỗi PUT part request có:

PUT /mybucket/film.mp4?partNumber=1&uploadId=abc123 HTTP/1.1
Host: rgw-endpoint.example.com
Content-Length: 112197632              ← kích thước part (~107 MiB)
Content-MD5: dGhpcyBpcyBhIGJhc2U2NA== ← Base64(MD5(part body)) để RGW verify integrity
x-amz-content-sha256: <hex-sha256>    ← SHA-256 của body (bắt buộc với SigV4)
Expect: 100-continue                  ← CLI gửi header trước, chờ RGW "100 Continue"
                                         rồi mới stream body → tránh gửi ~107 MiB
                                         rồi mới bị reject
Authorization: AWS4-HMAC-SHA256 ...   ← SigV4 signature
Content-Type: application/octet-stream
  • Content-MD5: RGW tính MD5 body nhận được, so sánh với header. Nếu mismatch → 400 BadDigest. Đây là tầng integrity đầu tiên.
  • x-amz-content-sha256: Với Signature V4, toàn bộ payload phải được hash SHA-256. Với file lớn, có thể dùng UNSIGNED-PAYLOAD nhưng CLI mặc định tính full SHA-256 (tốn CPU client).
  • Expect: 100-continue: Client gửi headers trước, RGW kiểm tra auth/quota/bucket exist, nếu OK trả HTTP/1.1 100 Continue, client mới bắt đầu stream body. Tránh lãng phí bandwidth.

1.3 Retry Logic & Timeout

  • Retry: AWS CLI dùng exponential backoff + jitter: lần 1 đợi ~1s, lần 2 ~2s, lần 3 ~4s… tối đa 5 retries (configurable max_attempts).
  • Connect timeout: mặc định 60s.
  • Read timeout: mặc định 60s. Với HDD chậm, nếu RGW mất >60s xử lý 1 part → client timeout → retry.
  • Adaptive retry mode (nếu bật retry_mode=adaptive): CLI tự throttle khi nhận 503/throttling responses.
  • Tại sao HDD chậm ảnh hưởng: Nếu cluster đang bận (scrubbing, recovery), write latency HDD tăng từ ~10ms lên hàng trăm ms → RGW response chậm → client có thể timeout ở những parts cuối.

TẦNG 2 — Ceph RGW (RADOS Gateway)

2.1 RGW tiếp nhận multipart request

Frontend: Ceph RGW hiện đại (Quincy, Reef, Squid) mặc định dùng Beast frontend (dựa trên Boost.Beast/Boost.Asio). Civetweb đã deprecated.

Thread model Beast:

  • Beast chạy trên Boost.Asio event loop.
  • rgw_thread_pool_size (mặc định 512) — số worker threads xử lý requests.
  • Mỗi incoming HTTP connection được gán cho 1 thread từ pool.
  • Với 10 concurrent parts từ CLI → 10 threads đồng thời xử lý.

Luồng xử lý 1 part upload:

HTTP PUT part arrives
   Beast parses HTTP headers
   Auth: verify SigV4 signature (keystone/internal user DB)
   Check bucket exists, quota not exceeded
   100 Continue response (nếu Expect header có)
   Stream body vào RGW buffer
   Stripe body thành RADOS objects
   Ghi song song: data  EC pool, index  SSD pool
   Khi c hai hoàn thành  200 OK + ETag (MD5 ca part)

2.2 Stripe: từ 1 Part thành N RADOS Objects

rgw_obj_stripe_size mặc định = 4 MiB (4,194,304 bytes).

Mỗi multipart part (~107 MiB) được stripe thành:

  • ceil(107 MiB / 4 MiB) = ceil(26.75) = 27 RADOS objects (26 × 4 MiB + 1 × phần dư ~3 MiB)

Tổng RADOS objects cho toàn bộ 1 TiB file:

  • 1 TiB = 1,048,576 MiB
  • 1,048,576 / 4 = 262,144 RADOS objects

2.3 Naming Convention

RADOS object names cho multipart upload tuân theo format:

<bucket_marker>_<object_key>.<upload_id>.N_M

Trong đó:

  • bucket_marker: unique ID của bucket (ví dụ: default.4128.1)
  • object_key: tên file = film.mp4
  • upload_id: multipart upload ID
  • N: part number (1-based, 1 → 9799)
  • M: stripe index bên trong part (0-based, 0 → 26)

Ví dụ cụ thể:

default.4128.1__shadow_film.mp4.2~abc123.1_0   ← part 1, stripe 0 (bytes 0 - 4MB)
default.4128.1__shadow_film.mp4.2~abc123.1_1   ← part 1, stripe 1 (bytes 4MB - 8MB)
...
default.4128.1__shadow_film.mp4.2~abc123.1_26  ← part 1, stripe 26 (bytes 104MB - 107MB)
default.4128.1__shadow_film.mp4.2~abc123.5000_0 ← part 5000, stripe 0

Object đầu tiên (head object) chứa thêm metadata (content-type, ACL, custom headers) trong xattrs.

2.4 Hai luồng ghi song song

RGW Thread (xử lý 1 part)
  │
  ├──── librados::aio_write(data_obj, EC_pool)  ──→  EC Pool (HDD)
  │       async, non-blocking
  │
  ├──── librados::aio_write(index_obj, index_pool) ──→ Index Pool (SSD)
  │       async, non-blocking
  │
  └──── wait for BOTH completions
          → return 200 OK to client

Luồng 1 — Data: Mỗi RADOS object 4 MiB được gửi tới EC pool. librados hash object name → PG → CRUSH tìm primary OSD → gửi OSD op.

Luồng 2 — Index: Bucket index ghi lại metadata entry cho object/part: tên, size, ETag, timestamp. Index entry nhỏ (vài trăm bytes) nhưng phải atomic và consistent.

2.5 Tại sao tách Index Pool ra SSD riêng?

Vấn đề nếu index chung HDD:

  • Bucket listing (GET /mybucket/) yêu cầu scan index. Trên HDD, random read latency ~10ms/IO → listing bucket có hàng triệu object trở nên cực chậm.
  • Mỗi object write cũng kèm index update. Nếu index trên HDD, write latency bị cộng thêm ~10-15ms cho index IO. Trên SSD, index update chỉ ~100-200µs.
  • Index contention: Nhiều RGW threads đồng thời ghi index vào cùng bucket index object → nếu trên HDD, lock contention + HDD seek = bottleneck nghiêm trọng.
  • Latency mismatch: Data write đã chịu HDD latency rồi. Nếu index cũng trên HDD → cả hai đều chậm → total latency tăng gấp đôi. Tách SSD cho index = index write hoàn thành gần như tức thì, tổng latency chỉ bị bottleneck bởi data write (chấp nhận được).

2.6 librados Async I/O

// Pseudocode cho RGW ghi 1 RADOS object
librados::AioCompletion *data_comp = 
    librados::Rados::aio_create_completion(callback_data, callback_complete);
librados::AioCompletion *idx_comp = 
    librados::Rados::aio_create_completion(callback_idx, callback_complete);

// Ghi data object vào EC pool
ec_io_ctx.aio_write("default.4128.1__shadow_film.mp4...1_0",
                     data_comp,
                     data_bufferlist,   // 4 MiB
                     4194304,           // len
                     0);                // offset

// Ghi index entry vào index pool  
idx_io_ctx.aio_operate("mybucket.bucket.index.0",
                       idx_comp,
                       &index_write_op);   // cls_rgw_bucket_link_olh()

// Đợi cả hai hoàn thành
data_comp->wait_for_complete();
idx_comp->wait_for_complete();

// Kiểm tra return value
if (data_comp->get_return_value() < 0 || idx_comp->get_return_value() < 0) {
    // Error handling, retry
}

RGW dùng completion callbacks — khi OSD trả ACK, callback được gọi trong context của librados messenger thread, signal condition variable → RGW thread tiếp tục xử lý.


TẦNG 3 — RADOS, CRUSH & Placement Groups

3.1 CRUSH Map với 2 loại Pool

CRUSH Hierarchy:
━━━━━━━━━━━━━━━━
root default
├── node-01  (host)
│   ├── osd.0  (HDD 14TB)
│   ├── osd.1  (HDD 14TB)
│   ├── ...
│   ├── osd.23 (HDD 14TB)
│   ├── osd.192 (SSD 2TB)   ← index OSD
│   └── osd.193 (SSD 2TB)   ← index OSD
├── node-02  (host)
│   ├── osd.24 (HDD 14TB)
│   ├── ...
│   ├── osd.47 (HDD 14TB)
│   ├── osd.194 (SSD 2TB)
│   └── osd.195 (SSD 2TB)
├── ...
└── node-08 (host)
    ├── osd.168 (HDD 14TB)
    ├── ...
    ├── osd.191 (HDD 14TB)
    ├── osd.206 (SSD 2TB)
    └── osd.207 (SSD 2TB)

Tổng: 192 HDD OSDs + 16 SSD OSDs = 208 OSDs

CRUSH Rule cho EC Pool (HDD):

rule ec_hdd_rule {
    id 1
    type erasure
    step take default class hdd        chỉ chọn HDD OSDs
    step chooseleaf indep 8 type host  8 OSDs, mỗi OSD từ 1 host khác nhau
    step emit
}
  • chooseleaf indep: thuật toán lựa chọn cho EC — “indep” nghĩa là mỗi shard được chọn độc lập, nếu 1 OSD fail thì chỉ shard đó được remap, không ảnh hưởng shard khác.
  • type host: failure domain = host → đảm bảo 8 shards nằm trên 8 nodes khác nhau.
  • Với 8 nodes và k+m=8 → mỗi PG sẽ có đúng 1 shard trên mỗi node.

CRUSH Rule cho Index Pool (SSD):

rule ssd_repl_rule {
    id 2
    type replicated
    step take default class ssd        chỉ chọn SSD OSDs
    step chooseleaf firstn 3 type host  3 replicas, mỗi replica từ 1 host khác
    step emit
}
  • firstn 3: chọn 3 SSD OSDs từ 3 nodes khác nhau.
  • Tại sao firstn chứ không indep? Replicated pool dùng firstn — khi 1 OSD fail, các replica shift xuống, đơn giản hơn indep.

3.2 Object → PG Mapping

Công thức:

PG_id = hash(object_name) % pg_num
  • Hash function: CRUSH hash (dựa trên rjenkins1 — Robert Jenkins’ hash) hoặc xxhash (Ceph versions mới).
  • pg_num cho EC pool: thường tính theo công thức:

pg_num ≈ (total_OSDs × 100) / k+m

Với 192 HDD OSDs, k+m=8:

pg_num ≈ 192 × 100 / 8 = 2400 → round lên power-of-2 gần nhất (nếu chưa bật pg_autoscale) = 2048 hoặc 4096

Giả sử pg_num = 2048 cho EC pool. - Với 262,144 RADOS objects, chúng phân bố đều vào 2,048 PGs → mỗi PG nhận trung bình ~128 objects từ file này. Nhưng vì hash đều, thực tế mỗi PG có thể nhận 100-160 objects (distribution gần uniform). - Sau khi có PG, CRUSH map PG → 8 OSDs (1 per node). OSD đầu tiên = primary.

3.3 EC 6+2: Toán học Reed-Solomon

Galois Field GF(2⁸):

  • Mọi phép tính EC diễn ra trong GF(2⁸) = GF(256), trường hữu hạn với 256 phần tử (0-255).
  • Phép cộng = XOR, phép nhân = polynomial multiplication modulo irreducible polynomial (thường dùng x⁸ + x⁴ + x³ + x² + 1 = 0x11D).
  • Mỗi byte data là 1 phần tử trong GF(256).

Encoding Matrix:

Encoding matrix (8 × 6) cho k=6, m=2:

        D0  D1  D2  D3  D4  D5    (6 data shards)
       ┌                        ┐
  S0   │  1   0   0   0   0   0 │  → Data shard 0 (= D0 nguyên vẹn)
  S1   │  0   1   0   0   0   0 │  → Data shard 1 (= D1 nguyên vẹn)
  S2   │  0   0   1   0   0   0 │  → Data shard 2 (= D2 nguyên vẹn)
  S3   │  0   0   0   1   0   0 │  → Data shard 3 (= D3 nguyên vẹn)
  S4   │  0   0   0   0   1   0 │  → Data shard 4 (= D4 nguyên vẹn)
  S5   │  0   0   0   0   0   1 │  → Data shard 5 (= D5 nguyên vẹn)
  P0   │ α⁰  α¹  α²  α³  α⁴  α⁵│  → Parity shard 0 (linear combination)
  P1   │ α⁰  α²  α⁴  α⁶  α⁸ α¹⁰│  → Parity shard 1 (khác combination)
       └                        ┘

(α = generator của GF(2⁸), thường α = 2)

Phần trên (6×6 identity matrix) đảm bảo 6 data shards = dữ liệu gốc (systematic code). Phần dưới (2×6) tạo 2 parity shards.

Tính parity:

P0[byte_i] = α⁰·D0[byte_i]  α¹·D1[byte_i]  α²·D2[byte_i]  ...  α⁵·D5[byte_i]
P1[byte_i] = α⁰·D0[byte_i]  α²·D1[byte_i]  α⁴·D2[byte_i]  ...  α¹⁰·D5[byte_i]

Tất cả phép nhân và cộng trong GF(2⁸): nhân dùng log/antilog table, cộng = XOR.

3.4 Ví dụ cụ thể: 1 RADOS Object 4 MiB

RADOS object: 4 MiB = 4,194,304 bytes

Chia thành k=6 data shards:
  Mỗi data shard = 4,194,304 / 6 = 699,050.67 bytes
  → Padding: 4,194,304 không chia hết cho 6
  → Padding lên bội số gần nhất: 699,052 × 6 = 4,194,312 (thêm 8 bytes padding)
  → Mỗi data shard: 699,052 bytes ≈ 682.67 KiB

Tính 2 parity shards:
  Mỗi parity shard = 699,052 bytes (cùng kích thước data shard)

Tổng shards: 8
Tổng bytes ghi: 8 × 699,052 = 5,592,416 bytes ≈ 5.33 MiB
Overhead ratio: 5.33 / 4.0 = 1.333× (= 8/6 = 1.333...)

3.5 Encoding diễn ra ở đâu?

Encoding diễn ra tại Primary OSD, không phải client.

Luồng:

Client (RGW via librados)
  │
  │  Gửi FULL 4 MiB object tới Primary OSD
  │  (qua OSD messenger, TCP)
  ▼
Primary OSD
  │
  │  1. Nhận toàn bộ 4 MiB vào buffer
  │  2. Chia thành 6 data chunks × ~699 KB
  │  3. Tính 2 parity chunks (Reed-Solomon GF(2⁸))
  │     → Dùng ISA-L (Intel Storage Acceleration Library) hoặc Jerasure
  │     → ISA-L dùng SIMD (AVX2/AVX512) để tăng tốc GF multiply
  │  4. Giữ lại shard mình (shard 0) 
  │  5. Gửi 7 shards còn lại tới 7 secondary OSDs
  │
  ▼
7 Secondary OSDs nhận mỗi OSD 1 shard (~699 KB)

3.6 Tổng bytes ghi vật lý cho 1 TiB file

Data path (EC pool, HDD):

Raw data:                    1 TiB = 1,099,511,627,776 bytes
RADOS objects:               262,144 objects × 4 MiB
EC overhead (8/6):           1 TiB × (8/6) = 1.333 TiB ≈ 1,466 GiB
Mỗi node ghi:               1.333 TiB / 8 nodes ≈ 170.67 GiB/node
Mỗi HDD OSD:                ~170.67 GiB (chỉ 1 OSD/node tham gia mỗi PG,
                             nhưng objects phân tán nhiều PGs → nhiều HDD 
                             OSDs trên cùng node cùng nhận data)
                             Thực tế: phân bố đều 24 HDD → ~7.1 GiB/HDD/node

Chính xác hơn: mỗi shard ~699 KB, tổng 262,144 objects × 8 shards = 2,097,152 shards. Mỗi node nhận 2,097,152/8 = 262,144 shards. Phân bố đều 24 HDD/node → mỗi HDD nhận ~10,922 shards ≈ 7.1 GiB.

Index path (repl-3, SSD):

Số index entries ≈ số multipart parts = ~9,799 entries (1 entry per part trong bucket index)
+ 262,144 internal shadow object entries (index cho mỗi RADOS stripe)
  (thực tế: RGW chỉ index per-part, không per-stripe trong bucket index)
  → Chính xác: ~9,800 entries trong bucket index

Mỗi entry ≈ 200-500 bytes (key + metadata)
Tổng index data: ~9,800 × 300 bytes ≈ 2.8 MiB
Replicated 3×: 2.8 MiB × 3 = 8.4 MiB trên SSD

Write amplification tổng thể:

Application data:          1 TiB
Actual HDD writes:         1.333 TiB  (EC overhead)
BlueStore WAL writes:      +1.333 TiB (data written to WAL first, then to data area = 2×)
                            Deferred write: nếu object  min_alloc_size, ghi trực tiếp
                            HDD min_alloc_size = 64 KB; shard ~699 KB > 64 KB
                             thể dùng direct write, bypass WAL cho data portion
                            Nhưng RocksDB metadata vẫn qua WAL
Total HDD write amp:       ~1.4-1.5× tổng (bao gồm RocksDB compaction)
Total SSD write amp:       ~8.4 MiB × ~2-3 (RocksDB compaction)  25 MiB (negligible)

Overall storage efficiency: 1 TiB data  ~1.45 TiB physical HDD writes

TẦNG 4 — OSD Process & EC Write Pipeline

4.1 Sơ đồ EC Shard Distribution trên 8 Nodes

EC 6+2 Shard Distribution cho 1 RADOS Object (4 MiB):
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

  Node-1       Node-2       Node-3       Node-4
┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐
│ OSD.x      OSD.y      OSD.z      OSD.w    │
│ (PRIMARY)                                 │
│                                           │
│ Shard 0    Shard 1    Shard 2    Shard 3  │
│ (Data D0)  (Data D1)  (Data D2)  (Data D3)│
│ 699 KB     699 KB     699 KB     699 KB   │
│ HDD #3     HDD #17    HDD #8     HDD #22  │
└──────────┘ └──────────┘ └──────────┘ └──────────┘

  Node-5       Node-6       Node-7       Node-8
┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐
│ OSD.a      OSD.b      OSD.c      OSD.d    │
│                                           │
│ Shard 4    Shard 5    Shard 6    Shard 7  │
│ (Data D4)  (Data D5) (Parity P0) (Parity P1)│
│ 699 KB     699 KB     699 KB     699 KB   │
│ HDD #11    HDD #5     HDD #19    HDD #2   │
└──────────┘ └──────────┘ └──────────┘ └──────────┘

(HDD # = HDD c th trên node đó, do CRUSH chn)

4.2 Primary OSD — Chi tiết xử lý

Primary OSD (giả sử Node-1, OSD.x) nhận write op từ RGW:

1. Nhận OSD op WRITE(object_name, data=4MiB) từ librados messenger
2. Map  PG  kiểm tra PG primary = chính mình
3. Lấy PG lock (per-PG write queue, serialized)
4. EC Encode:
   a. Split 4 MiB thành 6 data chunks
   b. ISA-L ec_encode_data():
      - Input: 6 × 699KB data buffers
      - Output: 2 × 699KB parity buffers  
      - Encode table: precomputed từ Vandermonde/Cauchy matrix
      - ISA-L dùng AVX2: throughput ~10-20 GB/s trên modern Xeon
      - Thời gian encode 4 MiB: ~0.2-0.5ms (negligible so với HDD latency)
5. Tạo 8 sub-write ops, mỗi cái chứa 1 shard
6. Gửi 7 shard ops tới 7 secondary OSDs qua OSD Messenger (async TCP)
7. Tự ghi shard 0 vào local BlueStore
8. Đợi tất cả 8 shards (kể cả local) hoàn thành
9. Khi ALL 8 ACKs nhận  gửi ACK về RGW (librados)

4.3 Secondary OSD — Chi tiết xử lý

Mỗi secondary OSD (7 cái):

1. Nhận sub-write op từ primary qua OSD Messenger
   - Payload: 1 shard (~699 KB) + metadata
2. Xác nhận PG mapping đúng, epoch đúng
3. Ghi shard vào BlueStore (chi tiết  Tầng 5)
4. Khi BlueStore commit  gửi ACK về primary OSD

4.4 Write Pipeline — Sequence Diagram

Thời gian →

RGW         Primary OSD     Secondary OSD 1-7    BlueStore(Primary)    BlueStore(Sec 1-7)
 │               │                │                    │                     │
 │──WRITE 4MB──→│                │                    │                     │
 │               │                │                    │                     │
 │               │ EC encode      │                    │                     │
 │               │ (0.3ms)        │                    │                     │
 │               │                │                    │                     │
 │               │──shard 1-7───→│                    │                     │
 │               │  (async TCP)   │                    │                     │
 │               │                │                    │                     │
 │               │──shard 0─────────────────────────→│                     │
 │               │                │                    │                     │
 │               │                │──shard to──────────────────────────────→│
 │               │                │  BlueStore         │                     │
 │               │                │                    │                     │
 │               │                │                    │ WAL write           │ WAL write
 │               │                │                    │ (sequential)        │ (sequential)
 │               │                │                    │ ~5-10ms HDD         │ ~5-10ms HDD
 │               │                │                    │                     │
 │               │                │                    │ RocksDB meta        │ RocksDB meta
 │               │                │                    │ ~2-5ms              │ ~2-5ms
 │               │                │                    │                     │
 │               │                │←────────ACK────────────────────────────│
 │               │←───────────────│ ACKs (all 7)       │                     │
 │               │←──────────────local ACK────────────│                     │
 │               │                │                    │                     │
 │←────ACK──────│                │                    │                     │
 │               │                │                    │                     │

Total latency per RADOS object: ~15-25ms (dominated by slowest HDD among 8)

4.5 Khi nào “committed”?

BlueStore era (không có journal truyền thống):

  • Ceph OSD (BlueStore) báo “applied” (có thể đọc lại) khi data nằm trong WAL/deferred hoặc data area.
  • Báo “committed” (durable, survive power loss) khi WAL entry được fsync xuống block device.
  • Với HDD: fdatasync() hoặc dùng FUA/O_DSYNC để đảm bảo data xuống platter (không chỉ trong HDD write cache).
  • Primary ACK về RGW chỉ sau khi TẤT CẢ 8 shards committed (không phải chỉ applied).

4.6 PG State Machine & Slow OSD

Bình thường: PG ở trạng thái active+clean.

Khi ghi:

active+clean → (write arrives) → active+clean (write completes)

Nếu 1 HDD OSD chậm (slow OSD):

  • Primary đợi tất cả 8 ACKs. Nếu 1 secondary OSD chậm (HDD degraded, ~100ms+ thay vì ~15ms), toàn bộ write bị chậm theo “tail latency” problem.
  • osd_heartbeat_grace (mặc định 20s): nếu OSD không heartbeat trong 20s → MON đánh dấu OSD down.
  • PG chuyển sang active+degraded — writes vẫn tiếp tục nhưng thiếu 1 shard.
  • osd_op_complaint_time (mặc định 30s): log warning nếu 1 op mất >30s.
  • min_write cho EC pool (tùy config, mặc định cần tất cả k+m): nếu 1 shard fail, write có thể phải retry sau khi PG re-peer.

4.7 Scrubbing & Deep Scrub trên HDD

Scrub (shallow): Kiểm tra metadata consistency — so sánh object metadata giữa các replicas/shards. Không đọc full data.

  • Interval: osd_scrub_min_interval = 1 ngày (mặc định).
  • Impact nhẹ.

Deep scrub: Đọc TOÀN BỘ data, tính checksum, so sánh.

  • Interval: osd_deep_scrub_interval = 1 tuần (mặc định).
  • Với 1 HDD 14TB:

Deep scrub throughput: ~100-150 MB/s sequential read (giới hạn bởi HDD) Thời gian: 14 TB / 130 MB/s ≈ 107,692 seconds ≈ 30 giờ

Thực tế: deep scrub chạy per-PG, throttled. osd_scrub_sleep (mặc định 0) thêm delay giữa scrubs. - Impact tới write throughput: Deep scrub đọc HDD → tranh giành bandwidth với write I/O → write latency tăng 20-50% khi deep scrub chạy song song.


TẦNG 5 — BlueStore trên HDD

5.1 BlueStore Layout trên 1 HDD 14TB

HDD 14TB Physical Layout:
━━━━━━━━━━━━━━━━━━━━━━━━━

 ┌─────────────────────────────────────────┐
 │            Raw Block Device             │
 │            /dev/sdX (14 TB)             │
 │                                         │
 │  ┌────────────┐ ┌────────────────────┐  │
 │  │ BlueFS     │ │    Data Space      │  │
 │  │ partition  │ │    (phần lớn       │  │
 │  │            │ │     14TB)          │  │
 │  │ ┌────────┐ │ │                    │  │
 │  │ │RocksDB │ │ │  Raw extents      │  │
 │  │ │  WAL   │ │ │  chứa object      │  │
 │  │ │  DB    │ │ │  data (shards)    │  │
 │  │ │  SST   │ │ │                    │  │
 │  │ └────────┘ │ │  Managed by        │  │
 │  │            │ │  BitmapAllocator   │  │
 │  └────────────┘ └────────────────────┘  │
 │                                         │
 └─────────────────────────────────────────┘

RocksDB WAL/DB location cho HDD OSD:

Câu hỏi quan trọng: 2 SSD per node dùng cho index pool → RocksDB WAL/DB của HDD OSD đặt ở đâu?

Trả lời — Có 2 kịch bản:

Kịch bản A (Tối ưu, phổ biến): Dùng chung SSD cho cả index OSD và BlueStore DB/WAL của HDD OSDs.

  • Mỗi SSD 2TB được phân vùng: phần lớn cho index OSD data, một phần nhỏ cho BlueStore DB/WAL partitions của các HDD OSDs trên cùng node.
  • Ví dụ: mỗi SSD chia ra: 1.5TB cho index OSD + 500GB cho BlueStore DB partitions của 12 HDD OSDs (mỗi HDD OSD được ~40GB DB trên SSD).
  • WAL + DB trên SSD giúp RocksDB compaction nhanh, metadata lookup cải thiện đáng kể.

Kịch bản B (Đề bài nói SSD “dành riêng cho index pool”): RocksDB WAL và DB đặt ngay trên HDD chính.

  • BlueFS quản lý vùng nhỏ trên cùng HDD cho RocksDB.
  • Performance hệ quả: RocksDB operations (WAL sync, compaction, metadata lookup) phải share HDD seek với data I/O → latency tăng 30-50%.
  • Đây là worst-case nhưng vẫn hoạt động. BlueStore được thiết kế cho trường hợp này.

Với đề bài “2 SSD dành riêng cho index pool”: Giả sử Kịch bản B — RocksDB WAL/DB trên HDD.

5.2 Write Path BlueStore (cho 1 shard ~699 KB)

Shard data (~699 KB) arrives at BlueStore
  │
  ├─→ Kiểm tra size vs min_alloc_size
  │   HDD min_alloc_size = 64 KB (mặc định cho HDD)
  │   699 KB > 64 KB → đủ lớn cho DIRECT WRITE (bypass WAL cho data)
  │
  ├─→ Deferred Write path (cho large writes trên HDD):
  │
  │   STEP 1: Allocate extents
  │   ├── BitmapAllocator tìm contiguous extent ≥ 699 KB
  │   ├── Trả về (offset, length) trên raw block device
  │   └── Bitmap updated in-memory
  │
  │   STEP 2: Write data TRỰC TIẾP vào allocated extent
  │   ├── pwrite64(/dev/sdX, shard_data, 699KB, allocated_offset)
  │   ├── O_DIRECT: bypass page cache
  │   ├── Sequential write → HDD throughput tốt (~150-200 MB/s)
  │   └── KHÔNG fsync ngay (deferred)
  │
  │   STEP 3: Write metadata + WAL entry cho transaction
  │   ├── RocksDB WriteBatch:
  │   │   ├── onode: object metadata (size, checksum, extent map)
  │   │   ├── extent map: (object offset → block offset, length)
  │   │   └── WAL/deferred op entry: "data at offset X, length Y"
  │   ├── WAL fsync (sequential append → HDD ~5-8ms)
  │   └── Sau WAL sync → transaction "committed" (durable)
  │
  │   STEP 4: ACK gửi về primary OSD ← TẠI THỜI ĐIỂM NÀY
  │
  │   STEP 5 (async, sau ACK):
  │   ├── fsync data extent (đảm bảo data trên platter)
  │   ├── Xóa deferred op entry trong RocksDB
  │   └── Nếu crash trước step 5: replay deferred ops khi restart
  │

Tại sao large write bypass WAL?

  • WAL trên HDD = ghi data 2 lần vào cùng HDD (write amplification 2×).
  • Với shard 699 KB, ghi trực tiếp vào data area + chỉ ghi metadata vào WAL = chỉ 1× data write.
  • Trade-off: cần deferred mechanism để đảm bảo atomicity (nếu crash giữa data write và metadata commit → deferred op replay khi recovery).

5.3 Allocator

BitmapAllocator (mặc định từ Nautilus):

  • Chia toàn bộ 14TB thành blocks (min_alloc_size = 64KB cho HDD).
  • Bitmap: 14TB / 64KB = ~224 million bits ≈ 28 MB bitmap in memory.
  • Cấp phát contiguous extent khi có thể (best-fit hoặc next-fit).
  • Block size 64KB: HDD hoạt động tốt nhất với writes ≥ 64KB (tránh partial-track writes, maximize sequential throughput).

5.4 Fragmentation trên HDD

  • SSD: fragmentation gần như không ảnh hưởng (no seek time, random read ≈ sequential read).
  • HDD: mỗi fragment = 1 seek (~8-12ms) + rotational latency (~4ms) = ~12-16ms penalty.
  • Nếu 1 shard 699 KB bị fragment thành 10 extents → 10 seeks × 12ms = 120ms thay vì 5ms sequential.
  • BlueStore cố gắng cấp phát contiguous, nhưng sau nhiều write/delete cycles → fragmentation tăng.
  • Mitigation: bluestore_extent_map_shard_max_size giới hạn extent map, BlueStore prefer large contiguous allocations.

5.5 Checksum

  • BlueStore tính CRC32C per block (mỗi 64KB block trên HDD).
  • Shard 699 KB → ceil(699/64) = 11 blocks → 11 CRC32C values (mỗi 4 bytes) = 44 bytes checksum.
  • Checksums lưu trong RocksDB onode metadata.
  • Khi đọc: tính CRC32C data đọc được, so sánh với stored checksum. Mismatch → bit rot detected → trigger repair từ parity shards.

TẦNG 6 — Linux Kernel, Block Layer & HDD Driver

6.1 I/O Interface

BlueStore mở raw block device:

fd = open("/dev/sdX", O_RDWR | O_DIRECT);  // bypass page cache

I/O submission:

  • BlueStore sử dụng libaio (Linux native AIO) hoặc io_uring (nếu kernel ≥ 5.1 và config cho phép).
  • io_uring (preferred trên Ceph Reef+): zero-copy, submission queue / completion queue → ít syscall overhead.
  • libaio: io_submit() batch nhiều IOs → 1 syscall.
// libaio path:
struct iocb cb;
io_prep_pwrite(&cb, fd, shard_buf, 699*1024, allocated_offset);
io_submit(ctx, 1, &cb);
// ... later:
io_getevents(ctx, 1, 1, &events, &timeout);

// io_uring path:
struct io_uring_sqe *sqe = io_uring_get_sqe(&ring);
io_uring_prep_write(sqe, fd, shard_buf, 699*1024, allocated_offset);
io_uring_submit(&ring);
// ... later:
io_uring_wait_cqe(&ring, &cqe);

6.2 Kernel Block Layer Path

userspace:  io_submit() / io_uring_submit()
               
               
kernel:     VFS layer (bypassed  O_DIRECT trên raw device)
               
               
            Block Layer (block/blk-mq.c)
               
               ├── I/O Scheduler
                  ├── mq-deadline (thường dùng cho HDD)
                     ├── Chia requests thành read/write queues
                     ├── Deadline: mỗi request  deadline (read: 500ms, write: 5s)
                     ├── Merge adjacent requests
                     └── Dispatch theo sector order (minimize seeks) 
                         nhưng respect deadlines
                  
                  └── Tại sao KHÔNG dùng "none" cho HDD?
                      "none" = FIFO, không reorder
                       HDD phải seek theo thứ tự random
                       Throughput giảm 3-5×
                      SSD dùng "none"  random = sequential performance
               
               
            Request Merge:
               - Requests gần nhau trên disk  merge thành 1 large request
               - bio merge: (LBA 1000, 64KB) + (LBA 1008, 64KB)  (LBA 1000, 128KB)

               
            SCSI/SATA subsystem
               
               
            Host Bus Adapter (HBA) driver
               - AHCI driver (SATA) hoặc mpt3sas/hpsa (SAS)
               - Tạo SCSI/ATA command
               
               
            Hardware: HBA  SATA/SAS cable  HDD controller

6.3 NCQ và SCSI Commands

SATA HDD:

WRITE FPDMA QUEUED (NCQ command)
  - LBA: sector address ( dụ: LBA 0x1A3F0000)
  - Sector count: 699KB / 512B = 1398 sectors (hoặc /4096 = 171 sectors nếu 4Kn)
  - Tag: 0-31 (NCQ depth = 32)

SAS HDD:

WRITE(16)
  - LBA: 8-byte address
  - Transfer length: sector count
  - Command queue depth: 128-256 (cao hơn SATA NCQ)

NCQ reordering: HDD firmware nhận tối đa 32 commands (SATA NCQ), sắp xếp lại theo vị trí sector trên platter → giảm seek. Ví dụ: 5 writes ở LBA 100, 50000, 200, 49999, 300 → firmware reorder: 100, 200, 300, 49999, 50000 → arm chỉ cần sweep 1 lần.


TẦNG 7 — HDD Physical (Cơ học + Từ tính)

7.1 Cấu tạo vật lý HDD 14TB

Seagate Exos X16 14TB / WD Ultrastar DC HC530 14TB (đại diện):

┌─────────────────────────────────────┐
        HDD 14TB Enterprise          
                                     
  Platters:       8 platters         
                  (16 surfaces)      
  Platter size:   ~875 GB/platter    
  RPM:            7200               
  Interface:      SATA 6Gb/s hoặc   
                  SAS 12Gb/s         
                                     
  Tracks/surface: ~500,000+          
  Sectors/track:  varies (ZBR)       
    Outer zone:   ~2000 sectors      
    Inner zone:   ~1000 sectors      
  Sector size:    512B (512e) hoặc   
                  4096B (4Kn)        
                                     
  Cache:          256 MB DRAM        
  Recording:      CMR (PMR)         
  Areal density:  ~1.08 Tbit/in²    
                                     
  Sustained rate: ~250-270 MB/s      
                  (outer zone)       
                  ~150-170 MB/s      
                  (inner zone)       
  Average seek:   8-9 ms (read)     
                  9-10 ms (write)    
  Rotational lat: 4.17 ms average   
                  (= 60/7200/2 sec)  
└─────────────────────────────────────┘

7.2 Quá trình ghi vật lý chi tiết

Khi HDD controller nhận lệnh WRITE cho 1 shard (~699 KB):

STEP 1: Actuator Seek
━━━━━━━━━━━━━━━━━━━━
  - Voice coil motor (VCM) nhn dòng đin  to t trường
  - T trường tương tác vi nam châm vĩnh cu  đẩy actuator arm
  - Arm pivot quanh trc, đưa head assembly ti track đích
  - Seek time ph thuc khong cách:
    - Short seek (vài track): ~1-2 ms
    - Average seek (1/3 stroke): ~8-9 ms  
    - Full stroke (innerouter): ~15-20 ms
  - Servo tracking: head đọc servo marks (embedded trên mi track)
    để fine-tune v trí  track following

STEP 2: Rotational Latency
━━━━━━━━━━━━━━━━━━━━━━━━━━
  - Platter quay 7200 RPM = 120 vòng/giây = 1 vòng/8.33 ms
  - Average rotational latency = 8.33/2 = 4.17 ms
  - Ch sector bt đầu quay ti dưới write head

STEP 3: Data Encoding
━━━━━━━━━━━━━━━━━━━━
  - HDD controller chip (System-on-Chip) x lý:
    a) CRC/ECC calculation: mi sector 4096 bytes  thêm ~100 bytes ECC
       (LDPC  Low-Density Parity-Check code trên HDD enterprise hin đại)
    b) Scrambling: XOR data vi pseudo-random sequence để tránh
       long runs of same bit  tt cho magnetic recording
    c) RLL encoding (Run-Length Limited):
       - Đảm bo t l "0" liên tiếp không quá dài
       - Giúp clock recovery circuit duy trì đồng b       - Tăng bit density trên track

STEP 4: Magnetic Write
━━━━━━━━━━━━━━━━━━━━━━
  - Write head: thin-film inductive head (Perpendicular Magnetic Recording - PMR)
  - PMR: t trường ghi VUÔNG GÓC vi b mt platter
     bit density cao hơn longitudinal recording
  - Write current: ~40-60 mA to t trường ~1 Tesla ti gap
  - T trường flip hướng t hóa ca magnetic grains trên platter
  - Mi bit = 1 grain cluster (~20-50 grains)
  - Bit size: ~20-30 nm (perpendicular direction)
  - Track pitch: ~50-70 nm

  ┌──────────────────────────────────────────────────────────┐
                    Write Head Detail                                                                                           ┌─── Write pole (narrow, focused field)                                                                            ~~~~~~~~ gap  t trường tp trung ti gap                                                                               └─── Return pole (wide, diffuse field)                                                                              ═══════════════════════════════════════════════════          ↑↑↓↓↑↑↑↓↓↑↓   magnetic domains trên platter              (mi ↑↓ = 1 bit transition)                                                                                            Platter coating: CoCrPt alloy thin film (~15-20nm)          Substrate: aluminum or glass                              └──────────────────────────────────────────────────────────┘

STEP 5: Write Verify (optional, thường OFF cho performance)
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
  - Enterprise HDD có th verify write bng read-after-write
  - Thường disabled vì tn thêm 1 revolution (~8ms)
  - Thay vào đó, da vào ECC khi read

Vi 699 KB data (gi s sector 4KB):
  699 KB / 4 KB = ~175 sectors
  Nếu contiguous trên cùng tracks:
    - Sectors/track (outer zone)  2000 × 4KB = 8 MB/track
    - 699 KB < 1 track  ghi xong trong < 1 revolution
    - Write time: ~0.5 ms (699KB / 150 MB/s inner hoc 699KB / 250 MB/s outer)
  Total: seek (~9ms) + rotational (~4ms) + write (~0.5ms)  13.5 ms

7.3 Zone Bit Recording (ZBR)

┌─────────────────────────────────────────────────┐
              Platter (top view)                  
                                                  
         Inner zones                              
         (fewer sectors/track)    ┌───┐           
         ~1000 sectors/track                    
         ~150 MB/s                             
              ┌──────────────┐                  
                                     Spindle  
                  Zone 0                      
                 (inner)                      
                                └───┘           
              └──────────────┘                    
            ┌──────────────────┐                  
                 Zone 5                         
                (middle)                        
              ~1500 sect/trk                    
              ~200 MB/s                         
            └──────────────────┘                  
          ┌──────────────────────┐                
                Zone 10                         
               (outer)                          
            ~2000 sectors/track                 
            ~250 MB/s                           
          └──────────────────────┘                
                                                  
└─────────────────────────────────────────────────┘

Tại sao outer tracks nhanh hơn?
  - RPM constant  linear velocity  outer track > inner track
  - Circumference outer > inner (2πr, r lớn hơn)
  - Cùng RPM, head "quét" qua nhiều bits hơn mỗi giây  outer track
  -  ZBR cho phép nhiều sectors hơn  outer tracks
  -  Data transfer rate outer zone ~1.5-1.7× inner zone

7.4 Write Cache & FUA

Write cache HDD enterprise (256MB DRAM):

Write arrives → HDD controller
  │
  ├─→ Write Cache ENABLED (default cho enterprise):
  │     1. Data copy vào DRAM cache → immediate ACK tới host
  │     2. Controller flush cache → platter (lazy, batched)
  │     Nguy hiểm: power loss = data loss nếu không flush
  │
  ├─→ FUA (Force Unit Access) bit set:
  │     1. Data phải hit platter trước khi ACK
  │     2. Bypass write cache (hoặc write-through)
  │     3. Chậm hơn: phải đợi seek + rotational + physical write
  │
  └─→ Cache FLUSH command (FLUSH CACHE EXT):
        1. Force tất cả dirty data trong cache → platter
        2. ACK sau khi tất cả flushed

Ceph BlueStore và FUA:

  • BlueStore dùng O_DIRECT (bypass OS page cache) nhưng không nhất thiết dùng FUA trên mọi write.
  • BlueStore dùng fdatasync() sau WAL writes → đảm bảo WAL durable.
  • fdatasync() triggers FLUSH CACHE command tới HDD → tất cả data pending trong HDD DRAM cache flush xuống platter.
  • BlueStore config bluestore_sync_submit_transaction: nếu true → sync commit; nếu false → async commit rồi fdatasync.
  • Enterprise HDD với power-loss protection (capacitor): có thể enable write cache mà vẫn safe — capacitor cung cấp đủ năng lượng để flush cache khi mất điện (~5-10ms worth of power).

7.5 Enterprise HDD Power Loss Protection

┌─────────────────────────────────────────┐
│  Enterprise HDD (Exos X16)              │
│                                         │
│  ┌──────────┐    ┌──────────────────┐   │
│  │ DRAM     │    │ Capacitor Array  │   │
│  │ Cache    │←───│ (super capacitor)│   │
│  │ 256 MB   │    │ ~1-5 F           │   │
│  └────┬─────┘    │ ~3-5V            │   │
│       │          │                  │   │
│       │          │ Khi mất điện:    │   │
│       │          │ cấp năng lượng   │   │
│       │          │ để flush cache   │   │
│       │          │ (~5ms đủ flush   │   │
│       │          │  256MB @ ~50GB/s │   │
│       │          │  internal rate)  │   │
│       ▼          └──────────────────┘   │
│  ┌──────────┐                           │
│  │ Platters │                           │
│  └──────────┘                           │
│                                         │
│  → Ceph CAN enable write cache vì      │
│    data loss risk mitigated             │
└─────────────────────────────────────────┘

TẦNG 8 — Index Pool Path (Song song với Data Path)

8.1 Sơ đồ Index Pool Repl-3 trên SSD

Index Pool Replication (3×) trên SSD:
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

RGW librados aio_operate(bucket_index_obj)
  │
  ▼
CRUSH → chọn 3 SSD OSDs từ 3 nodes khác nhau
  │
  ▼
┌─────────────┐    ┌─────────────┐    ┌─────────────┐
│  Node-2     │    │  Node-5     │    │  Node-7     │
│  SSD OSD.194│    │  SSD OSD.200│    │  SSD OSD.204│
│  (PRIMARY)  │    │ (SECONDARY) │    │ (SECONDARY) │
│             │    │             │    │             │
│  ┌────────┐ │    │  ┌────────┐ │    │  ┌────────┐ │
│  │Bucket  │ │    │  │Bucket  │ │    │  │Bucket  │ │
│  │Index   │ │    │  │Index   │ │    │  │Index   │ │
│  │Object  │ │    │  │Object  │ │    │  │Object  │ │
│  │(replica│ │    │  │(replica│ │    │  │(replica│ │
│  │  #1)   │ │    │  │  #2)   │ │    │  │  #3)   │ │
│  └────────┘ │    │  └────────┘ │    │  └────────┘ │
│             │    │             │    │             │
│  SSD 2TB   │    │  SSD 2TB   │    │  SSD 2TB   │
│  NVMe/SATA  │    │  NVMe/SATA  │    │  NVMe/SATA  │
└─────────────┘    └─────────────┘    └─────────────┘
      │                  │                  │
      │ Write latency:   │                  │
      │ ~100-200 µs      │                  │
      │ (NVMe: ~50 µs)   │                  │
      ▼                  ▼                  ▼
   Primary ghi local  Primary gửi       Primary gửi
   + repl tới 2       data tới Sec-1    data tới Sec-2
   secondaries         ghi local         ghi local
                       ACK → Primary     ACK → Primary

   Khi cả 3 committed → ACK về RGW

8.2 BlueStore trên SSD

SSD OSD BlueStore khác HDD OSD:

  • WAL + DB + Data đều trên cùng SSD: không cần tách vì SSD random I/O performance tốt.
  • min_alloc_size = 4 KB (vs 64 KB trên HDD): SSD tốt với small writes.
  • RocksDB performance: compaction nhanh vì random write trên SSD ~50µs vs HDD ~12ms.
  • No seek penalty: mọi access pattern đều ~50-200µs.

8.3 Bucket Index Sharding

Với 1 TiB multipart upload → ~9,800 parts:

Vấn đề: Nếu bucket index là 1 single RADOS object → tất cả part completions ghi vào cùng 1 object → lock contention → serial writes → bottleneck.

Giải pháp — rgw_bucket_index_max_aiobucket index sharding:

  • bucket_index_max_aio mặc định = 128: tối đa 128 concurrent index operations.
  • Index sharding: rgw_override_bucket_index_max_shards (mặc định: thường 1 hoặc auto).
  • Mỗi bucket index có thể chia thành N shards (ví dụ 16 hoặc 32).
  • Object name hash % N → xác định shard nào.
  • Mỗi shard = 1 RADOS object riêng → phân tán lock contention.
  • Với 9,800 parts và 16 shards → ~612 entries/shard → manageable.

Dynamic resharding (mặc định ON từ Luminous):

  • Khi 1 shard vượt rgw_max_objs_per_shard (mặc định 150,000) → tự động split.
  • Với ~9,800 entries: chưa cần reshard.

8.4 Latency Comparison

Latency cho 1 RADOS object write:
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

Index Write (SSD, repl-3):
  Network client→primary:    ~0.1 ms
  Primary BlueStore SSD:     ~0.2 ms
  Replicate to 2 secondaries:
    Network:                 ~0.1 ms
    Secondary BlueStore SSD: ~0.2 ms each (parallel)
  ─────────────────────────────────
  Total index write:         ~0.5-0.8 ms

Data Write (HDD, EC 6+2):
  Network client→primary:    ~0.1 ms
  EC encode (ISA-L):         ~0.3 ms
  Ship 7 shards (parallel):  ~0.1 ms network
  8× BlueStore HDD:          ~10-15 ms each (parallel, bottleneck)
  ─────────────────────────────────
  Total data write:          ~12-18 ms

RGW đợi cái nào?
  → RGW đợi CẢ HAI hoàn thành
  → Data write (HDD) LUÔN chậm hơn index write (SSD) ~20-30×
  → Bottleneck = HDD data write
  → Index write "free" (hoàn thành trong khi đợi data write)

Bảng số liệu tổng hợp

Metric Giá trị
File size 1 TiB = 1,099,511,627,776 bytes
Multipart part size (auto-adjusted) ~107 MiB
Số parts ~9,799 parts (part cuối nhỏ hơn)
Số RADOS objects (4 MiB stripe) 262,144 objects
Số shards ghi xuống HDD 262,144 × 8 = 2,097,152 shards
Kích thước mỗi shard ~699 KB (682.67 KiB)
Tổng bytes ghi HDD (tất cả nodes) 1.333 TiB ≈ 1,466 GiB
Bytes ghi mỗi node (8 nodes) ~183.3 GiB
Bytes ghi mỗi HDD OSD (ước lượng) ~7.1 GiB (phân bố đều 24 HDD/node)
Số index entries (bucket index) ~9,800 entries (1 per part)
Tổng index data (raw) ~2.8 MiB
Tổng index data (repl-3) ~8.4 MiB trên SSD
Write latency per RADOS object ~12-18 ms (HDD bottleneck)
Estimated end-to-end upload time ~25-45 phút (xem phân tích dưới)
Throughput bottleneck HDD write IOPS + tail latency (slowest shard among 8)

Ước tính thời gian upload end-to-end

Per-RADOS-object write latency:            ~15 ms (average)
Số RADOS objects:                           262,144
Nếu sequential (1 object at a time):       262,144 × 15 ms = 3,932 s  65 min

Nhưng RGW pipeline CONCURRENT writes:
  - 10 parts upload song song (CLI concurrency)
  - Mỗi part = 27 stripes, pipeline liên tục
  - Effective concurrency: ~10-20 RADOS writes đồng thời
  - RADOS objects phân tán nhiều PGs  nhiều primary OSDs  parallel

Effective throughput = f(HDD aggregate bandwidth):
  - Mỗi HDD: ~150-200 MB/s sequential write
  - 8 nodes × ~5-10 HDD active (not all 24): ~8-16 GB/s aggregate
  - Nhưng bị giới hạn bởi: network, RGW CPU, EC encoding
  - Realistic cluster throughput cho 1 client: ~500 MB/s - 2 GB/s

  1 TiB / 1 GB/s  1,024 seconds  17 phút
  1 TiB / 500 MB/s  2,048 seconds  34 phút

Estimated: ~20-40 phút (phụ thuộc network, HDD load, cluster health)

Failure Scenario: 1 HDD OSD chết giữa chừng

Kịch bản

Node-4, OSD.w (chứa shard 3 — data D3) bị fail khi đã ghi được 60% file (khoảng 157,000/262,144 RADOS objects).

Diễn biến chi tiết

Timeline:
━━━━━━━━

T=0:    OSD.w trên Node-4 ngừng respond (HDD fail, kernel I/O error)

T=~5s:  OSD.w heartbeat miss  peers (OSDs lân cận) báo MON
        (osd_heartbeat_interval = 6s, osd_heartbeat_grace = 20s)

T=~20s: MON đánh dấu OSD.w = DOWN sau khi vượt grace period
        MON cập nhật OSDMap epoch++
        Broadcast OSDMap mới tới tất cả OSDs  clients

T=~20s: Tất cả PGs  shard trên OSD.w chuyển trạng thái:
        active+clean  active+degraded
        (thiếu 1 shard nhưng vẫn  đủ k=6 data + m-1=1 parity 
         = vẫn đủ để đọc/ghi)

T=~20s: CRUSH remap: mỗi PG bị nh hưởng chọn 1 OSD mới từ Node-4
        (hoặc node khác nếu Node-4 down hoàn toàn)
         PG: active+degraded+remapped

T=~21s: Peering: primary OSD + OSDs mới trao đổi PG log
         xác định objects nào thiếu shard

T=~30s: Recovery bắt đầu:
        - Với EC 6+2: cần đọc k=6 shards bất kỳ (từ 7 OSD còn sống)
        - Decode (Reed-Solomon): giải hệ 6 phương trình GF(2)
          để reconstruct shard bị mất
        - Ghi shard mới tới OSD thay thế
        - Recovery throttled: osd_recovery_max_active (mặc định 3)
           tối đa 3 PGs recovery song song per OSD

Impact tới write đang diễn ra

Objects đã ghi TRƯỚC khi OSD.w die:
  → Tất cả 8 shards đã committed → an toàn (durable)
  → Recovery sẽ re-create shard bị mất lên OSD mới

Objects ĐANG ghi lúc OSD.w die:
  → Primary OSD đợi ACK từ OSD.w → timeout
  → Primary retry / fail op → RGW nhận error → retry
  → Sau OSDMap update: PG remapped → write op retry 
    tới OSD mới trong PG → thành công

Objects CHƯA ghi:
  → RGW tiếp tục ghi bình thường
  → PG active+degraded: write chỉ cần k+m-1=7 shards
     (thiếu 1 shard, EC pool vẫn accept writes nhưng
      reduced redundancy: chỉ chịu thêm 0 failures 
      thay vì 1 nếu m=2 → m_effective=1 khi degraded)

Recovery toán học

Mất shard 3 (D3). Còn: D0, D1, D2, D4, D5, P0, P1 (7 shards, cần 6)

Chọn 6 shards bất kỳ, ví dụ: D0, D1, D2, D4, D5, P0

Encoding matrix submatrix (6×6):
  [ 1  0  0  0  0 ]     [ D0 ]     [ D0 ]
  [ 0  1  0  0  0 ]     [ D1 ]     [ D1 ]
  [ 0  0  1  0  0 ]  ×  [ D2 ]  =  [ D2 ]
  [ 0  0  0  1  0 ]     [ D4 ]     [ D4 ]
  [ 0  0  0  0  1 ]     [ D5 ]     [ D5 ]
  [ α⁰ α¹ α² α⁴ α⁵]               [ P0' ] ← P0 without D3 contribution

→ Giải hệ: D3 = (P0 ⊕ α⁰·D0 ⊕ α¹·D1 ⊕ α²·D2 ⊕ α⁴·D4 ⊕ α⁵·D5) / α³

Thực tế: invert submatrix × received_shards → recover lost shard
ISA-L ec_encode_data() thực hiện nhanh (~10-20 GB/s throughput)

Recovery time ước tính

Data cần recovery trên OSD.w: ~7.1 GiB shards (portion of 262,144 shards)
Recovery = đọc 6 shards + decode + ghi 1 shard mới

Recovery throughput (per PG):
  - Đọc 6 × 699KB từ 6 OSDs (parallel): ~10-15 ms (HDD read)
  - Decode: ~0.05 ms
  - Ghi 699KB tới new OSD: ~10-15 ms
  - Per-object: ~20-30 ms

Nhưng throttled: osd_recovery_max_active = 3 PGs
  - Mỗi PG có ~128 objects
  - Recovery 1 PG: 128 × 25 ms = 3.2 seconds
  - 3 PGs parallel → effective PG throughput: ~1 PG/second
  - Total PGs affected: depends on pg_num mapping to OSD.w
    (~2048 PGs, mỗi PG trải 8 OSDs, OSD.w host 1 shard per PG)
    → OSD.w involved in: 2048 × (1/8) ≈ 256 PGs (ước tính)
    Thực tế: mỗi OSD host nhiều PGs. 
    Với 24 HDD OSDs/node, ~2048 PGs / 192 OSDs × 8 shards ≈ 
    mỗi HDD OSD host ~85 PGs
    → Recovery 85 PGs × 3.2s/PG / 3 parallel ≈ 91 seconds ≈ 1.5 phút

  (Nhưng trong thực tế recovery background bị throttle để không 
   ảnh hưởng client I/O → có thể 5-15 phút)

Trade-off Analysis: Tại sao mỗi quyết định thiết kế?

EC k=6, m=2 vs Replication 3×

Aspect EC 6+2 Repl 3×
Storage overhead 1.33× 3.0×
Raw capacity 1 TiB 1.33 TiB 3.0 TiB
Failure tolerance 2 OSD failures 2 OSD failures
Write latency Higher (encode + scatter 8 shards) Lower (just replicate)
Read latency Higher (need k shards) Lower (any 1 replica)
CPU overhead EC encoding (ISA-L mitigates) Negligible
Phù hợp Large objects, cold/warm storage Hot data, small random I/O

Tại sao EC cho data pool? File 1 TiB = cold/warm data, sequential access. EC tiết kiệm 2.25× storage cost. Latency overhead không quan trọng cho large sequential writes.

Index trên SSD vs HDD

Aspect Index trên SSD Index trên HDD
Latency mỗi index op ~0.2 ms ~12 ms
Bucket listing 10K objects ~50 ms ~2,000 ms
Concurrent write contention Manageable (SSD handles parallel) Severe (HDD seek storm)
Cost +2 SSD × 8 nodes = 16 SSDs Saved
Lý do chọn Critical for RGW performance Only for extreme budget constraints

Replication 3× cho Index vs EC

Index objects nhỏ (KB-range), random access, read-heavy (bucket listing). EC penalty cho small random reads quá lớn (cần đọc k shards). Replication 3× cho phép đọc từ bất kỳ 1 replica → low latency.

rgw_obj_stripe_size = 4 MiB

  • Quá nhỏ (ví dụ 64KB): hàng triệu RADOS objects → metadata overhead explosion, PG overload.
  • Quá lớn (ví dụ 64MB): partial overwrites tốn kém (phải rewrite toàn bộ stripe), memory buffer lớn.
  • 4 MiB: cân bằng giữa metadata overhead và granularity. EC encode 4 MiB nhanh (~0.3ms). Shard size ~699KB phù hợp HDD block I/O.

Failure domain = node (không phải rack)

Với chỉ 8 nodes (1 rack?), dùng failure domain = node đảm bảo mất 1 node = mất tối đa 1 shard per PG → vẫn trong khả năng chịu lỗi m=2. Nếu dùng failure domain = rack mà chỉ có 1 rack → không tận dụng được. Node-level là hợp lý nhất.

HDD min_alloc_size = 64KB

  • SSD: 4KB OK (flash page aligned).
  • HDD: 64KB tối ưu — nhỏ hơn gây fragmentation, HDD cần sequential I/O ≥ track sector size để đạt bandwidth tốt. 64KB = ~16 sectors (4K) = đủ lớn để HDD sequential throughput tốt mà không waste space quá nhiều cho small objects.