chicstay, chicgo, checkevent + API production ngày 24/08Tuần trước dựng xong bộ máy đồng bộ. Tuần này là mở rộng độ phủ và siết lại độ tin cậy: thêm 76 phòng vào diện đọc được lịch, và bịt bốn lỗi mà hệ thống vẫn báo "ổn" trong lúc nói sai.
commit=None cho cả 6 lần deploy gần nhất — không ai biết production đang chạy code nào, và 466 bài kiểm không chạy ở đâu cả.Sang tuần thứ tư — chicgo_data vẫn đứng, và tuần này là con số 0. Enrich chết 24 ngày (cuối 31/07), crawl dừng 15 ngày (cuối 09/08), và không một lượt BD confirm nào trong cả tuần (lượt cuối 15/08). Chi tiết mục 8.
| Nhà | Phòng | Nguồn | Ghi chú |
|---|---|---|---|
| ALÀ HOMESTAY | +42 | pms:ezcloud | PMS thật — trả thẳng khoảng bận kèm mã đơn, giàu hơn hẳn nhóm Google Sheet |
| GBOX UPROAD | +29 | pms:gsheet | Bố cục lưới 7 ngày × 24 giờ — kiểu bảng thứ sáu phải đọc |
| LAZY HOME | +5 | pms:gsheet | Bố cục khối màu 30 phút — mỗi phòng một file riêng |
Cả ba đều từng nằm trong nhóm "chưa nối được". Điều mở khoá không phải kỹ thuật mà là BD đi hỏi: LAZY đưa 5 link sheet, ALÀ đưa tài khoản PMS. Đây là lý do một đề xuất bỏ 6 home đã không được đưa ra — xem mục 1.3.
Bản export của chicdata không mang mã phòng ezCloud — availability_ref rỗng cả 54 phòng, không một RoomId nào của ezCloud xuất hiện trong file. Đã kiểm kỹ trước khi tự dựng, vì có sẵn thì dùng cái đó luôn tốt hơn mọi suy luận.
Ghép bằng tên phòng cho 0/54: chicdata ghi 243t2-hugge, 201-Flora; ezCloud ghi 243T2, 235P201. Phải dùng hai tín hiệu độc lập và bắt đồng thuận cả hai. Kiểm chéo bằng bốn nguồn độc lập với thứ đã dùng để ghép: cột địa chỉ trong sheet gốc khớp cả 10 cơ sở và đúng số phòng từng cơ sở; tầng bên ezCloud cho mỗi cơ sở một cụm riêng không chồng chéo; song ánh 42↔42; và 29 bài kiểm tự động cho bộ tách số phòng.
12 phòng cố ý bỏ trống — mơ hồ thì để trống, vì ghép sai một dòng là đắp lịch nhà này sang phòng nhà khác.
Một ca giải được sau đó: loại phòng 237GM-CV — "TS" là Trích Sài nhưng "GM" thì không ai đọc được. Bảng loại phòng có cặp song song 47TS-KV = "47 Trích Sài - Không View" và 47TS-GM = "47 Trích Sài - Gác Mái". Từ nhận diện loại trừ (đủ để nghi) thành nhận diện dương (đủ để ghi) — vì ezCloud có tới ba cặp phòng 601/602 cùng sống, đoán nhầm là ghép sang cơ sở khác.
| Bẫy | Nếu làm ẩu |
|---|---|
| LAZY — Sheets API chỉ trả màu ở ô GÓC TRÊN của vùng gộp | Mọi lượt khách co lại còn 30 phút. Nhưng 3/80 lượt lại tô từng ô mà không gộp — chỉ đọc vùng gộp thì mất trắng những lượt đó. Phải hiểu cả hai kiểu, và chúng cùng tồn tại trong một nhà |
| LAZY — chỉ nhận đúng màu xanh đã biết | TULIP 501 và BEAR ROOM có dải ô tô ĐEN đúng hình dạng một lượt khách; whitelist màu xanh thì hai dải đó đọc thành phòng trống. Nay ô nào không mang màu nền trống đều coi là có khách, màu lạ ghi thành cảnh báo thay vì nuốt im |
GBOX — _hex() trả 000000 cho ô KHÔNG có định dạng |
Trùng đúng chuỗi với màu đen thật. Google hay cắt bớt ô rỗng cuối hàng, nên mọi ô bị cắt sẽ hoá thành có khách |
| Tên tab do người gõ | " THÁNG 6/2026" thừa dấu cách, "Tháng 1/2026" viết hoa khác. So khớp tuyệt đối thì cả nhà đứng im vì đúng một dấu cách — và lỗi đó trông y hệt "chủ home chưa mở tháng" |
Luật đọc GBOX được đo trên bảng thật, không suy đoán: nhãn giờ N phủ (N−1):00 → N:00, đo bằng ranh giới ô xám ở hàng tiêu đề mà chủ home tô theo giờ hiện tại — lúc 09:38 xám phủ đúng nhãn 1..9. Ghi rõ lưu ý cho người sửa sau: phép thử "khách vắt qua nửa đêm" không phân biệt được hai cách đọc, cả hai đều cho chuỗi liền mạch.
Định đưa danh sách 6 home "không sync nổi" để bỏ hẳn. Kiểm lại bằng chứng thì nó không đứng vững, nên không đưa.
Lý do định bỏ bắt nguồn từ cột "Check lịch trống" trong bảng gốc ghi "nhắn trực tiếp với home". Nhưng cột đó ghi y hệt cho SUN (đang chạy 7/7 qua Google Sheet), LAZY (đang chạy 5/5) và ALÀ (đọc được 148 khoảng bận từ ezCloud). Nhãn ấy chỉ có nghĩa "chưa ai hỏi chủ nhà", không có nghĩa "chủ nhà không có gì" — và chính LAZY với ALÀ vừa chứng minh điều đó ngay trong tuần này.
Thứ duy nhất nên bỏ: 7 dòng của hồ sơ Amoureux trùng. Đó không phải phòng — dòng thứ bảy ghi nguyên văn "TRỤC 04 VÀ 05 GỘP THÀNH CĂN 2 NGỦ", tức một ghi chú bị nhập nhầm thành tên phòng. Sáu trong bảy dòng đã lọt qua vòng duyệt bán.
Một lượt đặt được KiotViet trả thành nhiều dòng cho cùng một phòng: dòng thật mang quantity > 0, cạnh nó là dòng bóng quantity = 0 mang giờ trả muộn hơn vài tiếng. Adapter lấy cả hai, nên phòng bị báo bận tới giờ trả của dòng bóng trong khi khách đã đi — mất bán, im lặng.
Đo trên gian hàng Amoureux: 213/1.003 khoảng là dòng bóng, trên 94 phòng, đẻ ra 51 cặp khoảng bận đè nhau trong DB.
Không lọc thẳng quantity = 0: có 19 nhóm mà cả nhóm đều bằng 0 — bỏ đi là mất dấu một lượt khách đang ở. Nên chọn trong từng nhóm, ưu tiên dòng thật, nhóm nào không có dòng thật thì vẫn giữ. Thêm một lớp nữa cho các dòng lệch ở giờ nhận. Kết quả: 1.003 → 813 khoảng, chồng nhau cùng mã đơn 62 → 0. Hai đơn khác nhau đè nhau thì giữ nguyên — đó là chủ home đặt trùng thật, không được che đi.
/v1/rooms — đầu API cấp cho ChicStay — tính status chỉ bằng "có khoảng bận nào không". Không có khoảng bận thì trả free, bất kể lần cuối đọc được lịch của phòng đó là bao giờ.
Đo lúc 12:50 ngày 24/08: 153 phòng "free", trong đó 9 phòng có ảnh chụp cũ 12–45 tiếng. Dashboard cùng thời điểm báo 144 — nó đã trừ đúng 9 phòng ấy ra từ lâu. Chú thích trong code còn ghi luật này "chép đúng của api_rooms.py", mà bên đó chưa bao giờ thật sự làm.
freshness.stale vẫn luôn có trong payload, nhưng nó nằm ở nhánh khác: bên gọi đọc status thấy "free" thì không có lý do gì mở tiếp ra xem.
Sửa: gộp độ tươi vào chính status, kèm unknown_reason (unlinked / never_synced / stopped / stale) để bên gọi biết đi hỏi ai. Bận vẫn là bận khi dữ liệu cũ — đó là bằng chứng dương, ảnh chụp cũ không xoá nó đi; chỉ "trống" mới bị hạ xuống "chưa biết".
Truy tiếp vì sao 8 trong 9 phòng đó đứng im: sáng 24/08 chủ AimeeHouse mở tab tháng 9 với hàng tiêu đề kiểu mới. Cửa sổ đồng bộ 30 ngày chạm tới tab đó, nó trượt chữ ký bố cục, và cả cơ sở 8 phòng biến mất khỏi ảnh chụp — trong khi tab tháng 8 vẫn đọc tốt nguyên vẹn. Tái hiện được: cửa sổ 2 ngày trả 19 phòng, cửa sổ 8 ngày trở lên trả 11.
Ràng buộc duy nhất của inventory_blocks neo vào khung giờ chứ không neo vào block_id, và Postgres xét nó theo từng câu lệnh chứ không đợi hết mẻ ghi. Nên dòng mới xin đúng khung giờ mà dòng cũ đang rời khỏi sẽ đâm vào dòng cũ nếu được chèn trước.
Phòng 407 TTT ngày 24/08: đơn DP002089 xin 23/08 20:00 → 24/08 09:00 đúng lúc DP001202 rời khung đó sang 20:00 → 11:00. Vì cả mẻ nằm trong một giao dịch nên không thay đổi nào được ghi, kể cả những thay đổi chẳng liên quan.
Sửa: nhường chỗ trước khi chèn — đóng mọi dòng đang mở giữ đúng khung giờ mà một dòng sắp chèn vào nhưng khác block_id. Đóng thừa vẫn an toàn vì chính mẻ chèn ngay sau mở lại. Test phải tách "đóng vì PMS không còn" khỏi "đóng để nhường chỗ" — gộp hai loại thì không phân biệt được mất dữ liệu với dời chỗ.
Luật chống trùng cũ đúng theo lý do: cùng một lý do thì im lặng vĩnh viễn. Nghe hợp lý cho tới khi gặp sự cố kéo dài.
Amoureux hỏng 01:45 → 09:51 ngày 24/08: 493 lượt hỏng liên tiếp, hệ thống gửi đúng MỘT thẻ lúc gần 2 giờ sáng rồi im. Không ai thấy thẻ đó, và cả buổi sáng không ai biết nhà lớn nhất hệ thống đang chết. Nó chỉ lộ ra vì tôi tình cờ soi bảng lượt chạy khi nghiệm thu một việc khác.
Tổng thiệt hại của lỗi 2.3, đo trên DB: 915 lượt hỏng ngày 23/08 + 594 lượt ngày 24/08 = 1.509 lượt đồng bộ hỏng trong hai ngày trước khi được tìm ra. Đó là cái giá của một luật cảnh báo sai.
Trước tuần này: mọi bản lên production đi bằng lệnh tay từ máy cá nhân. Railway ghi branch=None commit=None cho cả 6 lần deploy gần nhất — không ai biết production đang chạy code nào. 466 bài kiểm không chạy ở đâu cả. Code chưa commit vẫn lên được production, và sáng 24/08 đã lên thật.
/health trả ĐÚNG commit vừa đẩy — "deploy thành công" mới chỉ là build xong, container cũ còn phục vụ thêm vài phút nữa."ok": true.Cổng nghiệm thu bắt lỗi ngay ở lần chạy đầu: /health trả commit="unknown" dù CI đã ghi dấu vân tay — vì lệnh tải lên của Railway loại trừ file nằm trong .gitignore, nên dấu vân tay bị bỏ lại đúng ở bước cần nó nhất. Đây chính là thứ cổng này sinh ra để bắt: một bản deploy "thành công" nhưng không truy được về commit nào.
Kiểm chứng hôm nay: /health trên production trả commit: 5fb78794e48c — đúng commit mới nhất của dịch vụ. Truy được, lần đầu tiên.
Thêm một nhà dùng Google Sheet vốn không đụng gì tới bí mật (sheet công khai, đọc bằng khoá dùng chung) nhưng vẫn phải sửa tay cục JSON chứa credential của mọi nhà đang chạy trên Railway. Sửa nhầm là cả 11 nhà cùng chết, nên thao tác đó bị khoá lại — và GBOX cùng ALÀ đã nằm chờ nhiều ngày chỉ vì thế.
Tách theo đúng ranh giới thật: phần không bí mật đi theo repo, qua đúng cổng test và deploy như mọi thay đổi khác. Hai chốt chặn: (1) chỉ nguồn không-có-credential được khai ở repo — nguồn mang mật khẩu/token bị chặn ngay tại cửa, vì một token lỡ dán vào repo là lộ vĩnh viễn trong lịch sử git; (2) secret luôn đè repo, không bao giờ ngược lại — sửa gấp lúc sự cố phải có tác dụng ngay, không phải chờ một vòng deploy.
Một endpoint trả 200 kèm thân rỗng, thiếu trường, hay báo "trống" trong khi phòng đang có khách thì vẫn là 200 — và đó đúng là kiểu hỏng nguy hiểm nhất ở đây vì không ai nhìn thấy.
Một chi tiết có chủ ý: chỉ gọi /availability đúng một lượt vào nhà nhẹ nhất — đường đó đi thẳng sang hệ thống của chủ home, nện vào là tạo tải thật lên đối tác và đốt hạn mức chung của nhóm Google Sheet. Bộ kiểm thử của mình không được phép làm phiền đối tác.
Bốn tầng kiểm thử trên production. Đáng kể nhất: những lượt hỏng vẫn được ghi là "Google treo file" hoá ra là HTTP 429 — hết hạn mức đọc. Tức là vấn đề của phía mình gọi quá dày, chứ không phải sheet chủ nhà có vấn đề. Chẩn đoán sai này nằm trong tài liệu từ trước, và nó khiến người trực đi gọi nhầm người.
Bản đầu xếp "429 vì hết hạn mức" lên đầu — vì tôi vớ đúng lúc nó bùng lên trong chính bài nện tải của mình. Gộp cả kỳ theo nguyên nhân thì khác hẳn:
| Nguyên nhân lượt hỏng (2 ngày) | Số lượt | Số nhà |
|---|---|---|
| Quá hạn chờ | 133 | 7 |
| Hết hạn mức Google (429) | 23 | 4 |
| Google lỗi phía họ (5xx) | 5 | 5 |
Quá hạn chờ áp đảo, và gần như toàn bộ 23 lượt 429 nổ ra trong lúc test chứ không phải vận hành bình thường. Bản trước đã kết luận sai vì nhìn 12 dòng cuối thay vì gộp cả kỳ — nay công cụ soi có thêm mục "hỏng vì cái gì", vì 200 lượt hỏng vì hết hạn mức và 200 lượt hỏng vì đổi quyền là hai việc hoàn toàn khác nhau.
Điểm chung của cả ba loại: đều là lỗi tạm thời, đều tự khỏi nếu thử lại, và trước nay không thử lại lần nào — một cái nấc của Google là mất trắng cả lượt. Nay thử lại tới 3 lần cho cả ba loại. Lỗi không tạm thời (403 đổi quyền, 404 xoá file) vẫn hỏng ngay: thử lại vô ích và chỉ tốn thêm hạn mức.
Hạn chờ hạ từ 20 giây xuống 8. Đo thật: sheet khoẻ trả trong 0,4–2 giây, nên quá 8 giây gần như chắc chắn là đã kẹt chứ không phải đang chậm. Chờ 20 giây một lần rồi bỏ cuộc thì tệ hơn hẳn thử ba lần, mỗi lần 8 giây. Đo được trước/sau: bắn 10 lượt hỏi lịch LAZY liên tiếp, số lượt đọc đủ 5 cơ sở đi từ 0/10 lên 8/10.
smoke.py giả định mọi tài khoản luôn trả ít nhất một cơ sở; Google treo hẳn một file thì nó chết bằng IndexError và chín tài khoản còn lại không được kiểm lượt nào. Một bộ kiểm thử chết đúng lúc đó thì tệ hơn là không có.Trần chịu tải, ghi lại để sau khỏi đoán: đường đọc nặng bão hoà ở ~20 request/giây, quá đó thì xếp hàng chứ không hỏng — 0 lỗi ở mọi mức tới 50 request song song.
Lấy trạng thái sống của cả 11 tài khoản, so từng khoảng bận với DB, cửa sổ 14 ngày, hai bên lọc cùng điều kiện.
Bốn chỗ lệch đều lành: một khách gia hạn tại chỗ (lượt sync phút sau đã bắt kịp); một khoá dài 265 ngày qua Airbnb iCal dài hơn cửa sổ hỏi nên bị cắt khi so; một khoảng tháng 9 của Fish VCC do trang Apps Script hỏng tháng đó.
Ghi lại một lỗi của chính phép đo. Lần chạy đầu tôi báo "15 khoảng PMS có mà DB thiếu" — SAI. Do lọc DB theo "chưa kết thúc" nhưng quên lọc phía PMS cùng điều kiện, nên 14 lượt khách đã trả phòng sáng đó bị đếm nhầm thành thiếu. Số đúng là 1.
| Ngày | Chạy được | Một phần | Hỏng | % lỗi |
|---|---|---|---|---|
| 18/08 | 12.859 | 50 | 7 | 0,05% |
| 19/08 | 13.103 | 65 | 2 | 0,02% |
| 20/08 | 14.653 | 142 | 7 | 0,05% |
| 21/08 | 15.748 | 89 | 3 | 0,02% |
| 22/08 | 15.614 | 224 | 2 | 0,01% |
| 23/08 | 14.897 | 7 | 936 | 5,91% |
| 24/08 | 9.749 | 1.029 | 610 | 5,36% |
Hai ngày cuối là sự cố Amoureux ở mục 2.3 (đã sửa) và các lượt PARTIAL của AimeeHouse do chủ home đổi bố cục tab tháng 9. Kiểm lại 60 phút gần nhất hôm nay: 11/12 tài khoản 60/60 lượt OK; riêng AimeeHouse 60/60 PARTIAL — đúng như thiết kế, vì tháng 9 của nhà đó đang hỏng bố cục còn 19/19 phòng vẫn tươi.
161 phòng ghép khít, 96 phòng mang cờ "cần soi". Đọc cho đúng: cờ này không có nghĩa là ghép sai — nó có nghĩa là máy khớp tên một phần chứ không khít, nên muốn có người xác nhận. Đã soi tay ba phòng Amoureux: ghép đúng cả ba.
Nhưng để 96 phòng treo cờ mãi thì cờ mất tác dụng — không ai phân biệt được cái nào thật sự đáng ngờ. Đây là việc BD rà, đã đưa vào danh sách.
Archive brand: ops dừng 4 brand (13 cơ sở, 72 phòng) — cần biến mất khỏi dashboard và file export gửi ra ngoài, nhưng không xoá dữ liệu. Cố ý không lọc ở các endpoint tra theo ID tường minh: link cũ và nhật ký audit vẫn mở được bản ghi đã archive thay vì 404 — ẩn khỏi danh sách khác với xoá khỏi hệ thống.
22 phòng của ALÀ HOMESTAY đứng ở 0 ảnh suốt các lượt sync trước với cùng một lỗi "không đọc được danh sách file". Không phải folder trống, cũng không phải IP bị chặn: ops share riêng cho tài khoản công ty chứ không share công khai, mà công cụ chỉ biết cào trang folder public. Folder private trả HTTP 200 kèm trang đăng nhập nên nhìn y hệt mọi thất bại khác.
Thêm đường đọc qua Drive API với danh tính người dùng — khoá API không thay được chỗ này: key chỉ mở được tài nguyên công khai, đúng những gì cách cũ đã làm được rồi. Cào public vẫn là đường chính, API chỉ vào cuộc khi cào hỏng.
Một chi tiết đắt giá: bản đầu của available() chỉ đòi file cấu hình chứ không đòi token nằm sẵn trên đĩa — và nó treo cả bộ test: một folder hỏng giữa lượt sync là mở trình duyệt xin quyền rồi đứng chờ vô thời hạn, giữa lượt 400 ảnh thì không ai hiểu vì sao. Xin quyền nay tách hẳn thành một lệnh chạy tay một lần.
| Chỉ số | Tuần trước (18/08) | Hiện tại (24/08) | Thay đổi |
|---|---|---|---|
| Lượt enrich cuối cùng | 31/07 11:30 | 31/07 11:30 | 24 ngày chết |
need_to_enrich | 37.232 | 37.232 | 0 |
enriched | 23.891 | 23.891 | 0 |
| Địa điểm mới cuối cùng | 09/08 22:22 | 09/08 22:22 | 15 ngày dừng |
Hàng đợi từ khoá pending | 320 | 320 | 0 |
| BD confirm trong tuần | 18 lượt | 0 lượt | lượt cuối 15/08 |
Mọi con số đều đứng yên tuyệt đối — kể cả hàng chờ, vốn tuần trước còn nhích 199. Không có gì mới vào, không có gì được xử lý, và đội BD không confirm một địa điểm nào trong cả tuần.
Việc duy nhất chạm vào chicgo tuần này là một công cụ phục vụ BD: export toàn bộ 816 địa điểm Go2Joy ra xlsx — giá, ảnh, đánh giá, mọi trường JSONB được trải phẳng sang cột tiếng Việt (loại hình, nhu cầu khách, giờ mở cửa, giá min/max/avg, link mạng xã hội) thay vì đổ JSON thô vào ô. Chỉ đọc, không ghi.
| Chỉ số | Tuần trước | Hiện tại |
|---|---|---|
| Event | 2.012 | 2.022 |
| Đã đẩy thành công | 1.980 | 1.990 |
| Bị từ chối / đang chờ | 30 / 2 | 30 / 2 |
| Tổ chức | 796 | 798 |
| Lượt crawl (7 ngày) | 166 | 168 |
vẫn bị chặn Luồng học bổng sang tuần thứ tư. 384 bản ghi vẫn ở needs_review, mốc kiểm cuối cùng vẫn là 16/06. App key production thiếu quyền scholarships:read/write/publish, cần admin CheckEvent cấp — việc chờ người khác, không tự gỡ được.
Xếp theo số phòng gỡ được trên công sức bỏ ra — phần lớn nút thắt còn lại nằm ở phía BD và chủ home, không phải ở code.
| Phòng gỡ được | Việc | Ai làm |
|---|---|---|
| 29 | Duyệt bán GBOX UPROAD — lịch đã đọc được sẵn, chỉ chờ QA. Rẻ nhất trong tất cả | QA |
| 47 | Khảo sát LIBRÉ, NOBLE, MƠ, HC xem có bảng lịch không — LAZY và ALÀ tuần này chứng minh "nhắn trực tiếp" không có nghĩa là không có gì | BD |
| 16 | Thuyết phục LẶNG và TL đổi sang bảng mẫu chuẩn | BD |
| 15 | Hỏi Amoureux: Mễ Trì, Yên Hoà, 409 TTT còn hoạt động không (nguồn của 8 ảnh chụp mồ côi) | BD |
| 12 | Hỏi ALÀ về 253 Trích Sài và "218t4" trùng tên | BD |
| 2 | Hỏi LAZY: ô màu đen ở TULIP 501 / BEAR ROOM nghĩa là gì. Đây là cảnh báo nổ nhiều nhất hệ thống (217 lượt/3 ngày) và chưa ai xử lý. Đang coi là có khách — hướng an toàn, nhưng nếu chỉ là màu trang trí thì đang khoá oan hai phòng | BD |
stagging → main — main đang đi sau 154 commit, nên data.chicmedia.vn vẫn chạy bản cũ. Dịch vụ đồng bộ đã bám stagging; hai nhánh càng để lâu càng khó gộp.scholarships:read/write/publish.