Bỏ qua tới nội dung
Kiên

Cái report cãi nhau với database

· 3 phút đọc

1 con số · 2 câu query

Một report tháng và câu query vận hành trả ra hai con số khác nhau cho cùng một chỉ số kinh doanh, và bên nào cũng đúng theo cách của mình. Vấn đề không bao giờ nằm ở phép cộng — nó nằm ở chỗ một định nghĩa có tới hai bản cài đặt, và không ai làm chủ bản nào cả.

  • chất lượng dữ liệu
  • reporting
  • ownership
  • sql
Nội dung
  1. Hai câu trả lời đều đúng
  2. Vì sao chúng trôi khỏi nhau
  3. Định nghĩa thứ ba
  4. Cuộc cãi nhau kết thúc thế nào
  5. Điều rút ra

Hai câu trả lời đều đúng

Câu hỏi đến theo đúng cái cách mà loại câu hỏi này luôn đến: sao report nói một đằng mà hệ thống nói một nẻo? Tổng của một tháng bên phía reporting không khớp với con số mà câu query vận hành trả ra cho cùng kỳ đó. Phải có ai đó sai chứ.

Không ai sai cả. Report aggregate trên cái search index mình nuôi riêng cho reporting, với bộ filter của nó và cách hiểu riêng của nó về việc row nào được tính — đơn ở những status nhất định, timestamp gom bucket theo kiểu của nó. Câu query vận hành thì chạy thẳng vào transactional store, với một danh sách status khác và một quan niệm khác về việc một đơn thuộc về tháng nào. Mỗi bên cài đặt một cách đọc hợp lý của cùng một cụm từ kinh doanh. Hai cách đọc hợp lý, hai con số.

Hai cái tổng lệch nhau bao nhiêu tiền — mình chưa từng ghi lại. Lúc đó cuộc tranh luận xoay quanh việc tin bên nào, và khi chuyện đó ngã ngũ rồi thì chẳng ai quay lại đo cái khoảng lệch nữa. Thứ mình đếm được là số bản cài đặt của cái định nghĩa: hai, tức là thừa đúng một.

Vì sao chúng trôi khỏi nhau

Gần như chắc chắn là hai câu query đã từng khớp nhau, vào cái ngày report được viết ra. Đó chính là chỗ khiến kiểu hỏng này im lặng đến vậy. Hai bản cài đặt của cùng một định nghĩa bắt đầu trôi khỏi nhau ngay khoảnh khắc một trong hai bị sửa — thêm một status vào filter bên này mà quên bên kia, fix timezone ở đúng một phía — mà report thì bị sửa thường xuyên hơn schema rất nhiều. Mỗi lần chỉnh report một chút, hợp lý trong phạm vi của nó, là một lần fork định nghĩa kinh doanh trong im lặng: được review như một thay đổi reporting, không bao giờ như một thay đổi định nghĩa, vì chẳng có gì đánh dấu nó là như thế.

Report là thứ mềm hơn, nên report là thứ dịch chuyển. Câu query vận hành đứng yên. Vài tháng sau hai bên đã khác nhau thấy rõ, và tới lúc đó không một commit message nào ở cả hai phía nói "đây là chỗ chúng ta fork ý nghĩa của doanh thu".

Định nghĩa thứ ba

Trong lúc hai câu query đang được đem ra so, một cái spreadsheet xuất hiện — export từ một trong hai bên, filter tay để "làm sạch số liệu", rồi được chuyền đi như bản chuẩn. Đó là khoảnh khắc một cuộc cãi nhau hai phe biến thành ba phe. Một bản export là snapshot của một bản cài đặt tại một thời điểm; thao tác filter tay đầu tiên đè lên nó tạo ra định nghĩa thứ ba — thứ không tồn tại trong codebase nào, không có lịch sử, và không thể chạy lại được. Từ đó trở đi, một phần cuộc tranh luận là về những row mà ai đó đã xoá trong spreadsheet và không còn kể tên ra được nữa.

Cuộc cãi nhau kết thúc thế nào

Không phải bằng tranh luận. Cãi nhau về số tổng là cuộc cãi nhau không thể thắng ở tầng số tổng, vì bên nào cũng đang mô tả đúng câu query của chính mình. Lối ra là một thao tác cơ học: tính lại cả hai con số từ row gốc, giữ lại hai tập row, rồi diff chúng. Cái diff là một danh sách hữu hạn những đơn hàng cụ thể, và từng đơn trong đó phán xử được riêng lẻ — row này có được tính không, có hay không? Mỗi row được phán xử hoặc lộ ra một cái filter đang sai, hoặc chốt được một quy tắc chưa từng được viết xuống. Một buổi chiều ngồi diff kết thúc được cuộc bất đồng mà thảo luận đã nuôi sống lâu hơn thế nhiều — lâu hơn bao nhiêu thì, thú thật, mình không theo dõi.

Bản fix trụ lại được không phải là câu query đã sửa. Nó là việc chọn chỗ cho cái định nghĩa sống: một đường aggregate duy nhất mà cả report lẫn màn hình vận hành cùng gọi vào, hoặc một derived store được rebuild từ nguồn theo lịch và được audit lại với nguồn — cách nào cũng được, miễn là có đúng một, và mọi lần sửa nó được review đúng với bản chất của nó.

Điều rút ra

Một con số kinh doanh cần một người chủ và đúng một bản cài đặt. Mỗi bản cài đặt thêm vào là một cuộc cãi nhau trong tương lai không có trọng tài, hẹn sẵn vào lần trôi tiếp theo.

Một bản export có filter tay là một bản cài đặt mới. Chỉ là nó không có source control, và không có tác giả nào còn nhớ mình đã filter cái gì.

Phân xử tranh chấp số tổng ở tầng row. Tính lại cả hai từ nguồn rồi diff hai tập row. Số tổng chỉ cãi nhau được; row thì quyết được.