1.Lỗi không index làm delete query bị slow.
-
Bối cảnh: Chức năng login có lưu table lock tạm, để xác định số lần nhập sai
-
Nguyên nhân: Khi không có index, truy vấn xóa sẽ phải quét qua toàn bộ bảng để tìm và xóa các hàng thỏa mãn điều kiện, dẫn đến tình trạng lock toàn bộ bảng trong quá trình thực hiện.
A locking read, an UPDATE, or a DELETE generally set record locks on every index record that is scanned in the processing of an SQL statement. (MySQL :: MySQL 8.0 Reference Manual :: 15.7.3 Locks Set by Different SQL Statements in InnoDB)
2.Lỗi muti process tranh nhau barcode, duplicate.
-
Bối cảnh: Mỗi user khi đăng kí sẽ được cấp barcode độc nhất, và có thuật toán sinh ra liên tục trong chuỗi.
-
Nguyên nhân:
Mỗi user được cấp 1 barcode unique, nhưng việc check distinct lại dựa vào 1 câu query DB , lúc DB bị slow sẽ gây ra việc check không đúng, sinh ra việc trùng.
- Giải pháp: Việc check dựa vào redis, sẽ nhanh và chính xác hơn, sửa lại thuật toán thành 1 vòng while tranh nhau barcode trong chuỗi mà không cần đợi nhau.
3. Lỗi duplicate request gây gửi mail trùng.
-
Bối cảnh: Khi thực hiện checkout thành công từ webview của APP, mail gửi order thành công tới user bị duplicate.
-
Nguyên nhân: Có một lỗi chưa xác định được là từ APP, hay từ hạ tầng, nhưng lại gửi duplicate request Request handle được viết ở controller chưa check trường hợp bị gọi nhiều lần
-
Solution: Nguyên cứu kĩ hơn các yếu tố đã gọi duplicate. Các function ở controller, đặc biệt là API, khi handle request nên tính đến yếu tố duplicate.
Ref: Idempotence - Wikipedia
4. Lỗi query elasticsearch dùng query script.
-
Bối cảnh:
- Issue 1: Khi call API search ES, query sử dụng script dynamic ⇒ Vượt số lượng compile script ⇒ Search fail
- Issue 2: Khi get list product, cần check điều kiện còn stock hay không, nhưng sau khi implement thêm script check stock => ES sometime quá tải => chết site
-
Nguyên nhân:
- Issue 1: Vượt max_compilations_rate (default default 50/5m) do query sử dụng nhiều script
- Issue 2: Data node của ES chỉ có thread pool search là 1000. Nhiều document product lại chứa khá nhiều location stock (max là 833). Script query trước đây loop qua mỗi item stock của product để check điều kiện còn stock => dẫn tới query chậm, hết pool => thi thoảng die query ES => chết site
-
Script query dùng cơ chế vòng for để check tất cả document, không sử dụng được index của các column trong ES
Performance: Script queries can be slower compared to queries written in the Query DSL. This is because scripts need to be dynamically compiled and executed at runtime, which incurs additional overhead. Query DSL can leverage various internal optimizations and caching mechanisms.
- Script queries: Queries that need to do linear scans to identify matches.
- By default, Elasticsearch indexes all data in every field and each indexed field has a dedicated, optimized data structure. For example, text fields are stored in inverted indices, and numeric and geo fields are stored in BKD trees. The ability to use the per-field data structures to assemble and return search results is what makes Elasticsearch so fast. (https://www.elastic.co/guide/en/elasticsearch/reference/current/documents-indices.htm)
Trước:
Sau:
- Giải pháp:
Nâng max_compilations_rate lên 1000/1m Thay vì dùng script dynamic thì sử store script (register script với ES) Nâng spec ES từ 2 node lên 4 node + setting replica. Chuyển qua query DSL. Query DSL: Tận dụng được việc các column đã được index đầy đủ, việc check điều kiện stock diễn ra nhanh hơn. Thay vì script query sẽ không dùng được index.
5. Lỗi API nhưng lại set session, gây ra tràn memory redis.
-
Bối cảnh: Sau khi release APP, memory redis tăng từ từ lên 99.9%, redis gần như tràn memory
-
Nguyên nhân:
API dùng session để lưu access token cho việc giao tiếp giữa lớp authenticator và controller. Nhưng API lại không sử dụng cookie, nên mỗi lần request là tạo thêm session key trong redis => tràn memory
- Solution: Loại bỏ việc dùng session trong các API
6. ECCUBE không chuyển query sang node read
-
Bối cảnh: 99% query được dùng trên node WRITE, rất ít dùng trên node READ
-
Nguyên nhân:
Trong ECCUBE, tất cả request được wrap trong 1 câu transaction của query DB, dù request đó chỉ toàn query READ Đối với doctrine2, các query được viết trong 1 transaction default sẽ được chuyển sang node WRITE
- Solution: Viết class custom cho MasterSlaveConnection, thêm method lock() và forceConnect() để bắt buộc chọn connection READ.
Kết:
Trên đây là những issue mình đã gặp trong giai đoạn release vừa qua, nhiều vấn đề không mới, nhưng dễ quên trong việc check. Hy vọng sẽ giúp ích cho mọi người trong thời gian tới 😀