Giải phẫu cú suy sụp tinh thần lúc 2 giờ sáng
(zarar.dev)- Vào khoảng 10 giờ tối, trên jumpcomedy.com, tất cả lệnh gọi HTTP POST của RTK Query đều thất bại, khiến các chức năng của trang bị hỏng; vì mọi thứ vẫn bình thường ở môi trường local nên rất khó truy tìm nguyên nhân
- Trong lúc khiếu nại của khách hàng ngày càng nhiều, người vận hành phải một mình xử lý sự cố mà không có hỗ trợ production, SRE, kỹ sư senior hay quản lý
- TypeError liên quan đến
fetchtrên trình duyệt không trở thành manh mối trực tiếp, nhất là khi GET và DELETE vẫn hoạt động bình thường - Kiểm tra Sentry, cơ sở dữ liệu production, Cloudflare, bản cập nhật Chrome và rollback về các phiên bản cũ cũng không có thay đổi; khi thêm PostHog api_key vốn để trống ở local thì sự cố được tái hiện
- Sau khi gỡ PostHog, các chức năng hoạt động bình thường trở lại; sau đó, các issue GitHub của PostHog và Redux Toolkit xác nhận cùng một sự cố, cho thấy nguyên nhân là ảnh hưởng từ công cụ bên ngoài
Áp lực do sự cố gây ra
- Từ khoảng 10 giờ tối, trên jumpcomedy.com, tất cả lệnh gọi HTTP POST dựa trên RTK Query đều thất bại, khiến các chức năng chính không hoạt động đúng
- Dù gần đây có triển khai một số thay đổi, nhưng không có nội dung nào đáng được xem là nguyên nhân; vấn đề cũng không tái hiện trong môi trường local, khiến việc truy vết càng khó hơn
- Đã nhờ trợ giúp trên Discord của NextJS và Vercel nhưng không nhận được phản hồi, và cũng không có đội hỗ trợ production nào để chuyển giao xử lý sự cố
- Email từ khách hàng tiếp tục dồn lại
- Hỏi về việc không thể thay đổi giá sự kiện
- Hỏi về việc không thể gỡ mã khuyến mãi
- Vì các khách hàng doanh nghiệp nhỏ đang phụ thuộc vào dịch vụ, người vận hành cảm thấy xấu hổ, buồn bã, bất lực và mắc hội chứng kẻ mạo danh
Quá trình debug và xác nhận nguyên nhân
- Lỗi trên trình duyệt là một TypeError nói rằng
fetchđược thực thi với một request object đã được sử dụng, nhưng nó không chỉ ra nguyên nhân thực sự - Đã thêm nhiều
console.log()và breakpoint để kiểm tra header, độ dài API token, thứ tự gọi, v.v., nhưng không dẫn đến nguyên nhân rõ ràng - Có nghi ngờ khả năng do bản cập nhật Chrome, nhưng lỗi cũng tái hiện trên Firefox và Edge nên không phải vấn đề riêng của trình duyệt
- Dù quay lại các phiên bản cũ, lỗi vẫn tiếp diễn
- Vẫn lỗi ở phiên bản một tháng trước
- Vẫn lỗi ở phiên bản ba tháng trước
- Vẫn lỗi ở phiên bản một năm trước
- Để thu hẹp khác biệt giữa local và production, đã kiểm tra nhiều khả năng
- Gỡ Sentry khỏi production: không thay đổi
- Kết nối local với DB production: không thay đổi
- Tắt Cloudflare: không thay đổi
- Ở local, để tiết kiệm chi phí, PostHog api_key đang được để trống; khi thêm nó vào thì cùng một vấn đề được tái hiện
- Ở commit tiếp theo, sau khi gỡ PostHog, mọi chức năng hoạt động bình thường
- Vấn đề tương tự sau đó cũng được xác nhận qua các issue trên GitHub
1 bình luận
Ý kiến trên Hacker News
Sau khi làm SRE 1 năm ở một công ty toàn cầu lớn, tôi đã thoát được khỏi chế độ “hoảng loạn” mà bài viết nói tới
Từ góc nhìn kinh doanh, mọi vấn đề đều có thể trông như tận thế và rất dễ rơi vào hoảng loạn trong tình huống đó, nhưng thực tế hiếm khi tệ đến mức ấy, và ngay cả khi có tệ thì phần lớn vẫn vượt qua an toàn
Trong những tình huống như vậy, điều cốt lõi là dừng lại 5~10 phút và vẽ lại bức tranh tình hình cho rõ ràng nhất có thể trước khi lao vào sửa ngay. Nỗi sợ cản trở phán đoán lý trí, và nếu bấm nút loạn xạ trong trạng thái hoảng loạn thì vấn đề có thể càng rối hơn. Mẹo của tôi là tạt nước thật lạnh lên mặt và tay để cắt đứt vòng lặp sợ hãi
Sau khi trải qua vài lần như vậy, bạn sẽ có cảm giác rằng mọi chuyện ổn hơn tưởng tượng, và có được sự tự tin rằng mình từng xử lý tình huống xấu trước đây, nên biết rằng ngay cả khi không có ai để cầu cứu thì vẫn có thể tự xoay xở
Ví dụ như phần mềm đã mua nhưng không làm được gì vì bố trí nhân sự hay cấu hình thất bại, nhân viên mất hàng nghìn giờ mỗi năm vì trải nghiệm người dùng tệ và các yêu cầu vô nghĩa, năng lực chẳng làm ra tính năng nào thì bị bỏ mặc, các cuộc họp vô bổ lãng phí thời gian mỗi ngày, có những tính năng chỉ tồn tại để đáp ứng yêu cầu kiểm toán, hay các lãnh đạo cấp cao liên tục đốt tiền công ty
Downtime không hề có vẻ tệ hơn những chuyện đó, nhưng lại kéo theo nhiều sự chú ý và hoảng loạn hơn rất nhiều. Cảm giác giống như so sánh khủng bố với bệnh tim. Công ty không quan tâm đến giấc ngủ hay sức khỏe tinh thần của bạn, và sẽ ép được đến đâu thì ép. Không hẳn là ác ý, nhưng ở khía cạnh này thì giống như kẻ bắt nạt: bạn lùi đến đâu họ sẽ lấn đến đó
Một trong những châm ngôn lập trình của tôi là “cấm hắc thuật”. Nếu bạn không hiểu vì sao nó hoạt động thì chưa thể coi là xong
Tôi cũng nhìn ứng phó sự cố theo cách đó. Nếu ai đó không thể giải thích một cách nhất quán vì sao đề xuất của họ sẽ tạo ra tác động, thì tôi nghĩ không nên thực hiện. Có thể sẽ có lúc rốt cuộc bạn chỉ còn cách bóp cò, nhưng nhìn lại thì hình như tôi chưa từng gặp trường hợp nào thực sự như vậy
Khá sốc khi thấy những lãnh đạo cấp cao vốn rất điềm tĩnh ngày thường lại bắt đầu ném ra các phương án sửa chữa ngẫu nhiên trong lúc xảy ra sự cố
Hãy tưởng tượng thiệt hại còn lớn hơn bao nhiêu nếu không có ai ngăn lập trình viên solo này lại và bảo đừng cố làm cho
fetchthành xu hướngĐúng là nỗi sợ làm cản trở phán đoán lý trí, và tôi muốn bổ sung rằng nỗi sợ cũng lây lan cực mạnh. Khi người trực tiếp làm việc thấy lãnh đạo, quản lý hay đồng nghiệp hoảng loạn, họ cũng thường hoảng theo. May mắn là VP của tôi luôn bình tĩnh và ưu tiên sự rõ ràng hơn hành động
Tôi không chắc đây có phải là sụp đổ tinh thần hay không, và nó có thể tạo ấn tượng sai cho những người thực sự từng trải qua sự sụp đổ do căng thẳng liên quan đến công việc kỹ thuật
Với tôi thì chỉ có đúng một lần, và đó là một cơn lo âu cấp tính. Tôi thực sự may mắn vì có vợ ở bên giải thích tình hình và giúp tôi hiểu mình đang trải qua điều gì. Vợ tôi đã trải qua nhiều lần, còn với tôi đó là lần đầu và may thay cũng là lần cuối
Chuyện như vậy có thể xảy ra với con người, và bản thân nó không có gì sai cả. Điều cực kỳ quan trọng là phải thật sự thấm rằng điều đó không có nghĩa bạn có khuyết điểm hay yếu đuối
Với tôi, thứ cuối cùng giúp tôi dừng lại là Xanax, và vì nó giúp tôi ngủ được nên tôi thấy đáng để luôn để sẵn trong tầm tay
Điều tôi muốn nói là: suy nghĩ xâm nhập và những trạng thái thực sự mất kiểm soát, làm tê liệt chức năng như cơn lo âu hay cơn hoảng loạn là hai chuyện khác nhau. Nếu chuyện đó xảy ra thì bạn sẽ không thể làm việc, và như thế cũng không sao
Không phải mọi sự sụp đổ đều biểu hiện dưới dạng cơn hoảng loạn hay cơn lo âu. Nó có thể bộc lộ theo cách đó, nhưng đó không phải cách duy nhất. Căng thẳng biểu hiện rất khác nhau ở mỗi người, thậm chí với từng tác nhân gây căng thẳng khác nhau
Vì không thể biết người đó thực sự đã trải qua điều gì trong đầu, nên gần như không thể “chẩn đoán” từ bên ngoài. Dù có thể không phải một cơn hoảng loạn hoàn toàn, nhưng nghe vẫn giống như đã bị tê liệt chức năng trong vài giờ
Nếu tìm trên mạng thì có vẻ Xanax có thể gây nghiện
https://www.drugs.com/xanax.html
Có vẻ đây không phải loại thuốc nên dùng một cách nhẹ nhàng
Rất có thể chúng ta sẽ thấy một nhóm lớn những người gia nhập ngành công nghệ vào giữa những năm 2000 chết dần vì các bệnh liên quan đến căng thẳng
Với một người đã sống cả đời cùng chứng lo âu nặng, ý nghĩ chỉ một viên thuốc gây nghiện có thể xóa sạch tất cả khiến tôi thấy đáng sợ. Tôi có cảm giác mình sẽ phải bám vào nó suốt đời
Căng thẳng của người này bắt nguồn từ đúng một dòng code của PostHog. Commit bị revert là cái này: https://github.com/PostHog/posthog-js/pull/1371/commits/7598...
Ở đây có hai bài học. Thứ nhất, một khi đã triển khai thì bạn phải sở hữu nó. Vì vậy càng triển khai ít càng tốt, và nên giữ dependency ở mức tối thiểu. Thứ hai, phải đặt những thứ không quan trọng ra ngoài đường dẫn quan trọng. Không thể để máy nén điều hòa hỏng mà làm động cơ ngừng chạy. Trên trình duyệt thì điều này rất khó đạt được, nhưng vẫn đáng để cố gắng
Tệ hơn nữa, có vẻ như PostHog cập nhật động một phần mã của chính nó trong lúc chạy, thay vì đóng gói chúng tại thời điểm build
Trong tài liệu có một tùy chọn nâng cao để đưa mọi dependency vào bản build. Tôi hiểu vì sao họ làm vậy, và có thể tôi đã hiểu nhầm, nhưng từ góc nhìn người dùng, tôi kỳ vọng lazy load mã thực thi phải là một tùy chọn tối ưu hóa chứ không phải mặc định. Theo tôi chỉ nên dùng khi việc bundle đầy đủ gây ra độ trễ phân phối nghiêm trọng
Có vẻ bug nằm trong
window.fetchđã bị monkey-patchhttps://github.com/PostHog/posthog-js/blob/759829c67fcb8720f...
Bài học lớn nhất ở đây là: nếu bạn làm một thư viện phổ biến mà lại monkey-patch hàm toàn cục, thì test phải thật sự tốt
“Cứ bọc lời gọi PostHog trong try/catch cho chắc” và “Vì PostHog mà theo đúng nghĩa đen không thể gửi POST request bằng
fetch()” là hai chuyện hoàn toàn khác nhaufetch, vừa có vấn đề mock quá mức: https://github.com/PostHog/posthog-js/blob/main/src/_tests...Toàn bộ hàm fetch và XHR đều bị mock để không làm gì cả, nên dĩ nhiên sẽ không bắt được các vấn đề phát sinh khi tương tác với native bên dưới hoặc thư viện khác. Họ còn cấu hình cả Cypress mà tôi vẫn không hiểu vì sao lại muốn mock API của trình duyệt
Nếu được tích hợp hợp lý, tôi nghĩ trong tình huống xấu nhất chỉ nên là việc xử lý event giám sát bị lỗi mà thôi
Việc PostHog vá một hàm toàn cục rất quan trọng đáng ra phải là tính năng được tài liệu hóa rõ ràng. Như vậy người dùng mới biết điều đó, và có thể hợp lý cân nhắc nó khi debug những vấn đề bề ngoài rất khó giải thích
Ví dụ, Heap Analytics đến giờ này, tính đến tháng này, vẫn còn đụng vào cái gì đó bên trong Hotwire khiến Hotwire ngẫu nhiên vỡ hoàn toàn và mọi cú click đều biến thành tải lại toàn trang. Theo kinh nghiệm của tôi, nó ảnh hưởng tới 30–60% số lần tải trang. Có thể sửa được, nhưng tôi đã phải debug hơn 50 giờ mới buộc được Heap tải sau toàn bộ JavaScript của Hotwire
Như những người khác đã nói, bug dẫn đến màn căng thẳng nửa đêm này là do thay đổi đúng một dòng trong thư viện PostHog[0]
Tôi xem đây là lời nhắc về tầm quan trọng của việc đặt tên biến thật chính xác
Đoạn
res = await originalFetch(url, init)trông đủ vô hại. Nhưng như khai báo TypeScript cho thấy, tham sốurlkhông nhất thiết là URL:url: URL | RequestInfoNếu nó không phải URL mà là một đối tượng RequestInfo thì sẽ có vấn đề, vì ở phần đầu của hàm, một đối tượng Request đã được tạo ra và đã bị “tiêu thụ”, nên không thể dùng lại ở đây
Nếu tên tham số là
urlOrRequestInfohay gì đó chính xác hơn, thì có lẽ sẽ khó bỏ sót vấn đề trong thay đổi này hơnĐây chỉ là suy đoán hơn nhiều, nhưng vì linear types từ linear logic có thể hình thức hóa việc một giá trị đã bị “tiêu thụ”, nên có lẽ một hệ thống kiểu phù hợp có thể ngăn được loại bug này
[0] https://github.com/PostHog/posthog-js/pull/1351/commits/2497...
Chỉ cần nhìn vào ngữ nghĩa ownership của những ngôn ngữ như Rust là thấy. Nó không phải thứ bất khả xâm phạm, và đặc biệt sẽ đỡ hơn khi có kinh nghiệm, nhưng vẫn đủ nặng để trở thành điều mà người học phàn nàn nhiều nhất
Đó là một bài viết vừa căng thẳng vừa buồn cười. Nhưng phần tự trách bản thân thì lại quá quen thuộc
Tôi đang vận hành các ứng dụng iOS/macOS khá thành công, và từng đẩy một bản phát hành làm hỏng hoàn toàn hơn 350 nghìn bản cài đặt. Không hoàn toàn là lỗi của riêng tôi, nhưng đó là sản phẩm của tôi nên cũng chẳng khác biệt mấy
Cảm giác toát mồ hôi lạnh và xấu hổ lúc đó thực sự rất nặng nề. Chưa kể còn là App Store nên bản sửa cũng phải qua quy trình xét duyệt, kéo dài thêm thời gian. May là 30 phút sau khi nộp thì nó vào review, và chỉ vài phút sau đã được duyệt
Khi xây dựng sự nghiệp và chuyển sang vai trò lãnh đạo, tôi nhận ra đó là trải nghiệm cực kỳ có giá trị. Có thể ngày trước tôi từng căng thẳng vì nó, nhưng giờ ký ức đã quá xa nên chẳng còn chạm tới tôi nữa. Bây giờ tôi chắc chắn không còn stress vì chuyện đó
Có thể gây tranh cãi, nhưng thỉnh thoảng tôi vẫn cho phép thành viên đầu sự nghiệp làm hỏng môi trường production. Tất nhiên là khi tôi thấy trước được và tin chắc chúng tôi có thể khôi phục nhanh
Chuyện cần tạo không gian để được thất bại là điều ai cũng nói, nhưng nhiều lãnh đạo lại vạch ranh giới ở những thất bại có ảnh hưởng tới khách hàng thật. Nếu bạn không làm thứ quá hệ trọng như phần mềm hạ cánh máy bay — điều vốn là tình huống rất phổ biến và may mắn — thì đội ngũ vẫn nên được trải nghiệm sự cố production, kể cả cái giá là ai đó ở Spokane, bang Washington không dùng được sản phẩm trong vài phút
Cảm ơn vì đã viết bài này. Tôi đặc biệt thích đọc cách mọi người vượt qua những thử thách như vậy dưới áp lực, thường là xuyên đêm
Không chỉ có phần hậu kiểm kỹ thuật, mà còn có cả góc nhìn con người vốn thường bị xóa khỏi những câu chuyện như thế, nên tôi thấy nó càng hay hơn. Kiểu tự sự kỹ thuật này là thứ chỉ các nhà phát triển nhỏ/làm một mình hoặc nhà sáng lập mới có thể thoải mái chia sẻ
Chỉ nhìn vào cách lần theo vấn đề cũng thấy trước hết đây là một lập trình viên. Anh ấy đi vào mã của mình, rồi đi vào log. Cả hai đều hợp lý và cả hai đều có thể là nguyên nhân, nhưng lại bỏ lỡ manh mối quan trọng nhất mình có: “trên localhost thì nó hoạt động”
Nếu là SRE, DevOps, kỹ sư nền tảng, hay bất kỳ chức danh nào được gắn vào ngày hôm đó, tôi có lẽ đã tập trung vào sự khác biệt giữa hệ thống hoạt động và hệ thống không hoạt động. Tôi sẽ thêm từng khác biệt vào rồi gỡ ra, hoặc gỡ ra rồi thêm lại, cho đến khi có thứ gì đó chạy được
Tôi nhìn thấy hai điều. 1) Có một môi trường đang hoạt động. 2) Môi trường thất bại cũng vốn từng hoạt động rồi mới bắt đầu thất bại
Điều đó không có nghĩa là cách của tôi ưu việt hơn. Tôi chỉ muốn làm rõ sự khác biệt trong cách nhìn vấn đề. Cả hai bên đều thu hẹp phạm vi dựa trên những gì mình biết. Tôi hiểu hệ thống, còn bạn hiểu mã
Hồi lâu trước khi làm kỹ thuật viên điện tử, đã có một đống bo mạch xử lý của Perkin Elmer 7/32 bị tháo khỏi hệ thống. Đó là những bo mạch hỏng, có nhiều revision khác nhau, và với mỗi bo chỉ có sơ đồ mạch của đúng một revision
Tôi nghĩ là vô vọng, nhưng một kỹ thuật viên lớn tuổi và dày dạn hơn đã chỉ cho tôi một cách
Cắm bo tốt vào bộ mở rộng, rồi cho chương trình chẩn đoán bị lỗi chạy vòng lặp. Dùng oscilloscope kiểm tra và ghi lại tất cả các chân trên đầu nối. Đổi sang bo hỏng và lặp lại
Tín hiệu nào khác đi? Lần ngược theo tín hiệu đó. Nếu sơ đồ mạch không khớp, thì dùng volt kế và mắt thường để vẽ lại sơ đồ phản ánh đúng phần dây nối thực tế
Ông ấy gọi cách này là “thẻ tốt - thẻ xấu”, và nó thực sự hiệu quả. Tôi sẽ không nói là nó hiệu quả về chi phí, nhưng chúng tôi đã sửa được mọi bo mạch, và khả năng xử lý sự cố mạch điện tử số của tôi tăng lên rất nhiều
Đây là kiểu công việc giống “lính cứu hỏa”. Vì là chờ hệ thống hỏng rồi mới xử lý, nên việc 2 kỹ thuật viên dành 1 tuần cho một bo mạch cũng không thành vấn đề
“Thử quay lại phiên bản của một tháng trước xem. Không được. Ba tháng trước thì sao? Không được. Vẫn lỗi. Một năm trước thì sao? Hoàn toàn không được.”
Chỉ quay lại mã của mình, trong khi vẫn tiếp tục dùng bản cập nhật PostHog đã hỏng đúng vào ngày đó sao? Bài học của tôi là phải có khả năng quay lui mọi thứ, bao gồm cả các dependency
https://github.com/PostHog/posthog/issues/24471#issuecomment...
Dù vậy vẫn có lựa chọn tự bundle trực tiếp
https://github.com/PostHog/posthog/issues/24471#issuecomment...
Tác giả bài gốc đã xử lý tốt. Mặt tích cực của những sự cố kiểu này là chúng mang lại cả đống bài học đáng giá
Đây là một bài viết hay, giúp nhớ lại rằng phía sau dịch vụ luôn là con người, đồng thời cũng cho thấy rõ quá trình debug
Trên thực tế, áp lực không khiến bạn debug vấn đề nhanh hơn. Thường thì nó chỉ cản trở suy nghĩ. Cần cố gắng phớt lờ kết quả hết mức có thể và giữ bình tĩnh nhất có thể
Phần lớn chúng ta, dù mức độ khác nhau, đều từng trải qua tình huống tương tự. Dĩ nhiên, áp lực khi điều hành chính công ty mình sẽ đặc biệt lớn