Testing concurrent code with testing/synctest
Bài tóm tắt Go 2025 3. Quy mô lớn Độ khó: Trung bình
Go · 2025 · 3. Quy mô lớn · Độ khó: Trung bình — bài gốc
Bài của đội ngôn ngữ Go giới thiệu một package thử nghiệm trong Go 1.24 (bật bằng
GOEXPERIMENT=synctest, chưa thuộc cam kết tương thích). Ghi lại vì nó đưa ý "mô phỏng tất định" vào runtime của ngôn ngữ.
Bối cảnh
- Test code đồng thời thường phải chứng minh một điều không xảy ra (hàm chưa được gọi trước khi context huỷ). Cách hay dùng là ngủ một khoảng rồi kiểm: 10 ms thì chậm khi cộng dồn nhiều test, mà vẫn flaky vì CI dùng chung có thể khựng vài giây. Theo bài, chỉ chọn được nhanh hoặc tin cậy, không được cả hai.
- Test code dùng thời gian thật thì chậm và flaky; dùng đồng hồ giả thì phải viết code nhận clock tuỳ chọn và tránh hàm của package
time.
Thay đổi chính
- Package chỉ có hai hàm.
synctest.Runchạy một hàm trong goroutine mới; nó và mọi goroutine nó sinh ra nằm trong một bubble.synctest.Waitchờ tới khi mọi goroutine trong bubble đều bị chặn bởi goroutine khác trong bubble. - Trong bubble, package
timechạy trên đồng hồ giả: thời gian chỉ tiến khi mọi goroutine đều bị chặn bền (durably blocked); khi đóWaittrả về nếu đang có người chờ, nếu không thì thời gian nhảy tới mốc gần nhất có thể đánh thức một goroutine, và nếu không còn mốc nào thì bubble deadlock vàRunpanic. - "Chặn bền" chỉ gồm các thao tác chỉ goroutine trong bubble mới gỡ được: kênh tạo trong bubble,
selecttoàn nhánh chặn bền,time.Sleep,sync.Cond.Wait,sync.WaitGroup.Wait. Mutex không tính (có thể bị giữ bởi goroutine ngoài bubble, như cache toàn cục trongreflect); I/O mạng thật không tính, nên test client/server dùng mạng trong bộ nhớ (net.Pipe). Thao tác trên kênh của bubble từ bên ngoài thì panic. - Race detector hiểu
Wait: thiếu một lầnWaitthì báo data race đúng chỗ. Ví dụ trong bài: test hành viExpect: 100-continuecủanet/httptất định, không cần sửa code được test.
Vì sao
- Mục tiêu theo bài: test code đồng thời đơn giản, nhanh và tin cậy mà không bắt code production phải thiết kế quanh clock giả.
Bài học cho SWE
- Flaky do thời gian là vấn đề của môi trường chạy, không chỉ của từng test: một runtime điều khiển được thời gian và biết khi nào mọi thứ đã đứng yên thì loại được cả lớp lỗi.
- Ranh giới của mô phỏng phải nói rõ thứ gì nằm ngoài (mutex dùng chung, I/O thật); ở đó vẫn phải thay bằng bản giả.
Liên quan
- Kiểm thử độ tin cậy (chaos, fault injection, mô phỏng tất định)
- TigerBeetle 2026 (mô phỏng tất định cả hệ) · Reddit 2024 · Go 2023
#Go#testing/synctest#concurrency#fake clock#flaky test#bubble#durably blocked#race detector#net.Pipe
Được nhắc tới trong
Công ty (1)