Insiden anjloknya harga saham Berkshire Hathaway kelas A sebesar hampir 100% pada Juni 2024 akibat anomali Consolidated Tape Association (CTA) menegaskan kerapuhan sistem finansial saat likuiditas lenyap mendadak. Pada engine order book, fenomena flash crash bukan hanya soal volatilitas harga, melainkan lonjakan throughput pembatalan pesanan (cancel burst), penghapusan likuiditas level dalam, dan eksekusi bertubi-tubi dalam fraksi milidetik. Menjalankan verifikasi skenario ini langsung di pipeline Continuous Integration (CI) memastikan regresi performa dan degradasi memori terdeteksi sebelum masuk produksi.
Anatomi Masalah: Flash Crash dan Beban Ekstrem
Skenario pasar ekstrem memiliki pola pesan yang berbeda dibanding operasi normal:
- Asimetri Pesan: Rasio pembatalan terhadap eksekusi melonjak drastis. Pasar didominasi pesan
CancelOrderdan order jual agresif (Market Sell). - Liquidity Vacuum: Order buy limit di harga wajar terkuras habis, memaksa matching engine mengevaluasi rentang harga (tick range) yang jauh lebih lebar.
- Memory Churn: Pembatalan dan penambahan order frekuensi tinggi menyebabkan fragmentasi memori atau alokasi heap berlebih jika struktur data order book tidak menggunakan pre-allocated memory pool.
Arsitektur Pengujian Deterministik
Runner CI umumnya berbagi sumber daya CPU (noisy neighbor) dan berjalan di lingkungan virtual. Melakukan stress testing berbasis waktu absolut dinding (wall-clock time) seperti mewajibkan latency di bawah 50 mikrodetik di GitHub Actions akan menghasilkan flaky test. Solusinya adalah memisahkan pengujian menjadi dua metrik:
- Deterministik State & Invariants: Memastikan integritas buku pesanan, saldo akun, dan validitas eksekusi melalui throughput deterministik berbasis replay log event.
- Bounded Degradation & Tail Latency: Menilai waktu pemrosesan menggunakan CPU cycle counter relatif atau batas atas (p99/p99.9) yang diberi toleransi margin deviasi CI.
Struktur Data dan Backpressure Ring Buffer
Matching engine performa tinggi umumnya menerima pesanan via lock-free queue (misalnya Disruptor pattern atau bounded SPSC/MPMC channel). Ketika buffer penuh akibat antrean matching memuncak, engine harus menerapkan backpressure eksplisit (seperti menolak pesan dengan kode error terstandarisasi atau memblokir pengirim secara kooperatif), bukan mengalami unbounded memory leak.
Implementasi Simulasi Beban
Berikut implementasi engine order book minimal berbasis Go beserta stress test harness yang mensimulasikan penipisan likuiditas ekstrem (liquidity dry-up):
package engine_test
import (
"sync"
"sync/atomic"
"testing"
"time"
)
type Side byte
const (
Buy Side = iota
Sell
)
type Order struct {
ID uint64
Price uint64
Qty uint64
Side Side
}
type OrderBook struct {
mu sync.Mutex
bids map[uint64]uint64 // Price -> Qty
asks map[uint64]uint64
processedOps uint64
}
func NewOrderBook() *OrderBook {
return &OrderBook{
bids: make(map[uint64]uint64),
asks: make(map[uint64]uint64),
}
}
func (ob *OrderBook) Process(o Order) {
ob.mu.Lock()
defer ob.mu.Unlock()
if o.Side == Buy {
ob.bids[o.Price] += o.Qty
} else {
// Matching sederhana: kurangi dari bid jika harga cocok
rem := o.Qty
for bPrice, bQty := range ob.bids {
if bPrice >= o.Price && rem > 0 {
if bQty <= rem {
rem -= bQty
delete(ob.bids, bPrice)
} else {
ob.bids[bPrice] -= rem
rem = 0
}
}
}
if rem > 0 {
ob.asks[o.Price] += rem
}
}
atomic.AddUint64(&ob.processedOps, 1)
}
Berikut adalah harness stress testing yang menguji ketahanan pipeline antrean terhadap ledakan order penjualan skala masif:
func TestStressFlashCrashPipeline(t *testing.T) {
ob := NewOrderBook()
numOrders := 100_000
queueCapacity := 1_024
orderQueue := make(chan Order, queueCapacity)
// Inisialisasi likuiditas awal (Order Bids tipis)
for i := 1; i <= 1_000; i++ {
ob.Process(Order{ID: uint64(i), Price: uint64(100 + (i % 50)), Qty: 10, Side: Buy})
}
var droppedOrders uint64
var acceptedOrders uint64
latencies := make([]time.Duration, numOrders)
// Engine Consumer Loop
stopConsumer := make(chan struct{})
go func() {
for o := range orderQueue {
ob.Process(o)
}
close(stopConsumer)
}()
// Producer: Simulasi serbuan order agresif (Market Dump)
start := time.Now()
for i := 0; i < numOrders; i++ {
t0 := time.Now()
order := Order{
ID: uint64(2000 + i),
Price: uint64(50 + (i % 60)), // Melibas level harga buy
Qty: 5,
Side: Sell,
}
select {
case orderQueue <- order:
latencies[acceptedOrders] = time.Since(t0)
acceptedOrders++
default:
// Backpressure aktif: tolak order jika ring buffer penuh
atomic.AddUint64(&droppedOrders, 1)
}
}
close(orderQueue)
<-stopConsumer
totalDuration := time.Since(start)
// Assertion Kriteria Kegagalan CI
if acceptedOrders == 0 {
t.Fatal("Critical: Engine mengalami starvation total")
}
dropRatio := float64(droppedOrders) / float64(numOrders)
throughput := float64(acceptedOrders) / totalDuration.Seconds()
t.Logf("Throughput: %.2f ops/sec, Dropped: %d (%.2f%%)", throughput, droppedOrders, dropRatio*100)
// Kriteria 1: Engine harus memproses minimal batas minimum throughput di CI
if throughput < 50_000 {
t.Errorf("Degradasi throughput terdeteksi: %.2f ops/sec < 50,000", throughput)
}
// Kriteria 2: Backpressure harus menolak ketika penuh, bukan deadlock
if dropRatio > 0.95 {
t.Errorf("Antrean macet: Rasio drop order terlalu tinggi: %.2f%%", dropRatio*100)
}
}
Mitigasi Flaky Test di Environment CI
Untuk menjaga eksekusi tes tetap reliabel di pipeline otomatis (seperti GitHub Actions atau GitLab CI), terapkan strategi berikut:
- Gunakan Percentile Ketimbang Nilai Absolut: Hindari
max_latency < 1ms. Gunakanp95 < target * ci_multiplieruntuk mengakomodasi jitter CPU pada shared runner. - Karantina Benchmark dari Unit Test: Jalankan stress testing pada tahap pipeline tersendiri menggunakan runner dengan alokasi CPU dedicated. Gunakan tagging atau build constraints (seperti
//go:build integration). - Memory Leak Detection: Ambil snapshot alokasi memori (
runtime.ReadMemStatsatauvalgrind/sanitizer) sebelum dan sesudah injeksi beban. Periksa pertumbuhan alokasi yang tidak terlepas meski antrean telah dikosongkan.
Integrasi Pipeline CI
Contoh konfigurasi GitHub Actions workflow untuk mengeksekusi suite pengujian stres order book:
name: OrderBook Stress Test
on:
push:
branches: [ main ]
pull_request:
jobs:
stress-test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-go@v5
with:
go-version: '1.22'
- name: Run Race Detector
run: go test -race -short ./...
- name: Run Deterministic Stress Scenarios
run: go test -v -run=TestStressFlashCrashPipeline -timeout 3m ./...
env:
GOGC: "100"
Pengujian stres yang deterministik mencegah regresi performa masuk ke rantai produksi, memastikan sistem perdagangan tetap tangguh bahkan di tengah volatilitas ekstrem.
Komentar
0 komentar
Masuk ke akun kamu untuk ikut berkomentar.
Belum ada komentar
Jadilah yang pertama ikut berdiskusi!