Phần lớn thứ mình đã ship chạy trên một relational database, Redis, một queue, và Node với TypeScript. Danh sách đó không phải một triết lý, càng không phải gu. Nó là một cái cược rằng thứ thật sự làm mình đau không phải lúc viết code — mà là 3 giờ sáng thứ Ba, khi code đang chạy và có cái gì đó bên dưới thì không.
Nhàm chán mua được gì
Một công nghệ mình đã vận hành vài năm thì đi kèm sẵn một danh mục các kiểu hỏng nằm trong đầu. Mình biết trông nó ra sao khi relational store vẫn khoẻ mà connection pool đã cạn, và cái đó khác thế nào với việc bản thân store đang ốm. Mình biết Redis làm gì khi bộ nhớ đầy, vì đã nhìn một lượt search làm nó đầy. Mình biết trong các bảo đảm của queue, cái nào là thật và cái nào là mình tự giả định. Chẳng có mẩu kiến thức nào trong đó nghe oai, và tất cả đều chịu lực đúng vào lúc không tra cứu được gì.
Thứ thứ hai nó mua được là người vào sau. Trên một nền tảng chừng 90 tích hợp, tháng đầu của một bạn mới đã dành hết cho domain — supplier nào hiểu "một cái phòng" theo nghĩa gì, vì sao lượt search này bất đồng bộ còn lượt kia thì không. Mỗi mảnh hạ tầng lạ đều cộng thêm vào hoá đơn đó và giành chỗ với phần thật sự khó. Một stack mà người ta đã biết nghĩa là sự chú ý của họ đổ vào vấn đề chứ không đổ vào công cụ.
Và thứ ba: đồ nhàm chán debug được bằng những công cụ ai cũng đã có sẵn. Query chậm thì đọc execution plan. Queue ùn thì đếm job. Không có bước nào bắt phải đi chế cái máy đo trước đã.
Đúng một lần cái lựa chọn lạ là đúng
Định danh khách sạn không vừa với bất kỳ điều nào ở trên, và đáng để nói chính xác vì sao, bởi "dữ liệu của bọn mình phức tạp lắm" không phải một lập luận.
Cùng một toà nhà mang một mã khác nhau ở mỗi supplier. Nếu khách sạn của supplier A khớp với của B, và của B khớp với của C, thì của A khớp với của C — định danh có tính bắc cầu. Cách làm mặc định là một bảng mapping từng cặp: mỗi dòng nói mã này bằng mã kia. Nó chạy tốt cho tới khi bạn hỏi đúng câu hỏi quan trọng nhất, là cho tôi mọi mã mà toà nhà này đang mang. Câu đó là một cú self-join với độ sâu không biết trước. Không phải join to, mà là join không biết: số bước nhảy phụ thuộc vào chuỗi bằng chứng tình cờ nối tới đâu, và cứ thêm một supplier là chuỗi lại dài ra. Bạn không viết được câu query một lần rồi biết giá của nó, và không chặn được nó nếu không cắt độ sâu — mà cắt độ sâu là lặng lẽ quyết định rằng vài khách sạn thật ra không phải cùng một khách sạn.
Một graph store trả lời câu đó một cách tự nhiên. Traversal độ sâu thay đổi từ một node không phải mẹo dùng công cụ; nó chính là công cụ. Nên các lượt tra định danh chạy trên Neo4j, còn bản thân các dòng dữ liệu vẫn nằm chỗ của dòng dữ liệu: một bảng định danh khoảng 1,9 triệu dòng với chừng 46 cột mã supplier nằm trong relational store, còn graph lo phần traversal. Quyết định đó ra đời năm 2022 và giữ tới giờ, qua mọi chiến dịch bắc cầu dựng trên nó.
Nó cũng thu tiền thuê. Bây giờ có hai store phải khớp nhau, tức là một store dẫn xuất và do đó là một khoản nợ — cả một họ lỗi mới, thứ không tồn tại hồi còn một store. Cho chọn lại mình vẫn chọn thế, và mình sẽ không giả vờ rằng nó miễn phí.
Bài test mình dùng
Phải là hình dạng của bài toán không vừa — chứ không phải kích thước.
Nếu một store nhàm chán chỉ đơn giản là chậm hơn, đó không phải lập luận cho một store mới. Đó là lập luận cho một cái index, một lớp cache, một cột phi chuẩn hoá, hoặc một cái máy to hơn — và cả bốn thứ đó đều đảo ngược được trong một buổi chiều bởi một người không có mặt trong buổi họp ban đầu. Gần như mọi đề xuất "phải dùng X vì nó nhanh hơn" mà mình từng nghe đều là một vấn đề schema hoặc query đang mặc đồ hoá trang.
Công cụ lạ thắng khi câu hỏi bạn cần hỏi không diễn đạt nổi một cách sạch sẽ bằng công cụ nhàm chán — khi cách cài đặt tự nhiên là một đệ quy không chặn được, hoặc một query mà bạn không nói ra được giá của nó, hoặc một vòng lặp ở tầng ứng dụng đang viết lại thứ mà engine chuyên dụng coi là thao tác chính của nó. Định danh bắc cầu đạt tiêu chuẩn đó. Gần như mọi thứ khác từng có người rủ mình đem về thì không.
Nói thẳng giới hạn của lập luận: mình chưa bao giờ benchmark một bản cài bằng bảng mapping từng cặp với bản graph ở độ sâu thật của production. Lý lẽ đứng trên hình dạng của câu query, không đứng trên một con số, và ai muốn công vào quyết định đó thì đường nứt nằm ở đây.
Ba câu hỏi trước khi nhận thêm bất cứ thứ gì vào stack
3 giờ sáng thì ai vận hành nó? Không phải ai đang hào hứng với nó — mà ai bị gọi dậy. Nếu đáp án là một người mê nó, thì bạn không nhận về một công nghệ, bạn nhận về một con người, mà người thì đi nghỉ và đổi việc.
Backup kiểu gì, và ai đã từng restore thật chưa? Một bản backup chưa ai restore chỉ là một niềm tin. Cái store thứ hai trong thiết kế định danh đúng là chỗ câu hỏi này cắn, vì rất dễ coi bản mirror là thứ vứt đi được — cho tới ngày việc dựng lại nó nằm trên đường găng.
Rollback thì nó ra sao? Deploy có đi lùi; mỗi lần push là một lần bump version, và một bản lỗi thì bị revert. Code rollback thì sạch. Dữ liệu thì không, và một store thứ hai đã bị bản mới ghi vào thì không rollback tí nào trừ khi có người thiết kế cho chuyện đó. Nếu câu hỏi này chỉ nhận lại một ánh mắt trống rỗng thì công nghệ đó chưa sẵn sàng để nhận về, dù nó vừa với hình dạng bài toán tới đâu.
Nhàm chán không phải một đức tính. Nó là một mặc định mà bạn phải có khả năng tự cãi để thoát ra — một lần — bằng một lý do viết được thành một câu về hình dạng của bài toán, và bốn năm sau vẫn bảo vệ được khi người viết ra nó đã đi chỗ khác.