Binary Lake Map & Lake Doctor
Cài đặt trong src/engine/map.rs. Lake Map lưu một payload Arrow IPC đã biên dịch sẵn trong tệp hệ thống .br_map.bazan tại gốc hồ dữ liệu. Nó lưu metadata cho mọi định dạng, vị trí row-group/column-chunk của Parquet, stripe ORC, block OCF của Avro, block object MsgPack, block dòng logic XLSX, checkpoint byte theo block dòng NDJSON/JSONL, block object của mảng JSON, block dòng CSV/TSV/PSV/TXT và ordinal RecordBatch của Arrow IPC/Feather. Chỉ .br_map.bazan được load; .br_map.ipc bị bỏ qua như tên sidecar cũ. Lake Doctor vẫn kiểm tra metadata tệp hiện tại trên đĩa để phát hiện drift.
Vòng đời danh mục
stateDiagram-v2
direction LR
state "Đang dựng danh mục" as Building
state "Đã lưu .br_map.bazan" as Saved
state "HEALTHY" as Healthy
state "DRIFT_DETECTED" as Drift
state "HEALED" as Healed
[*] --> Building: br.lake.create_map()
Building --> Saved: save_lake_map_ipc()
Saved --> Healthy: doctor · entry khớp hết
Saved --> Drift: doctor · modified / unindexed / missing
Healthy --> Drift: tệp thay đổi trên đĩa
Drift --> Healed: doctor(auto_heal=True)
Healed --> Healthy: danh mục đồng bộ trở lại
build_lake_map()duyệt thư mục (quadiscover_data_files), đọc schema/số dòng và thống kê min/max toàn file. Kiểu được hỗ trợ và độ chính xác số được ghi trong đặc tả Lake Map.- Khoảng cách checkpoint theo dòng cấu hình bằng
checkpoint_stride_rowskhi tạo map; mặc định là65,536và phải lớn hơn 0. Ví dụ:br.lake.create_map("data", checkpoint_stride_rows=16_384). Thiết lập này áp dụng cho checkpoint dòng/object NDJSON/JSON, file delimited, block MsgPack và block dòng logic XLSX. Nó không chia nhỏ row group native của Parquet, stripe ORC, block OCF Avro hoặc batch Arrow IPC. - Tạo Lake Map và Lake Doctor dùng chung tập file được discovery theo extension: gồm extension built-in và dynamic extension đang được đăng ký, bỏ qua file không có extension. Có thể đọc trực tiếp file extensionless bằng magic-byte sniffing, nhưng file đó nằm ngoài phạm vi map/doctor theo thư mục. Dynamic handler cần được đăng ký khi tạo map và chạy doctor; nếu không, file dùng extension đó sẽ không được discovery, entry cũ bị báo thiếu và auto-heal sẽ loại khỏi map.
- Với Parquet, builder còn đọc footer và lưu khoảng dòng, khoảng byte nén của từng row group cùng khoảng byte của từng column chunk. Đây không phải một byte offset cho từng dòng logic.
- Với NDJSON, builder lưu block dòng theo stride đã cấu hình cùng dòng logic đầu tiên, byte đầu tiên và độ dài byte. Nó không tạo offset cho từng dòng.
- Với
.jsonvà.jsonl, byte đầu tiên không phải whitespace chọn locator:[lưu block object top-level trong mảng theo stride đã cấu hình; các trường hợp khác lưu checkpoint dòng theo stride đó. Scanner mảng hiểu nesting, chuỗi quote và escape..ndjsonluôn dùng checkpoint dòng. - Với ORC, builder lấy ordinal, khoảng dòng, byte offset và kích thước stripe từ footer metadata của ORC. Khi slice, reader ORC dùng byte range để bỏ qua các stripe trước đó.
- Với Avro, builder đọc header Object Container File và lưu khoảng dòng, byte offset, kích thước block vật lý và payload nén của từng data block. Khi slice, hệ thống phát lại header rồi seek reader Avro tới block tương ứng.
- Với MsgPack, builder decode các object top-level để tìm boundary block theo stride đã cấu hình và lưu checkpoint byte. Khi slice, hệ thống lấy schema từ map đầu tiên, seek tới checkpoint rồi decode tiếp.
- Với XLSX, builder ghi block dòng dữ liệu logic theo stride đã cấu hình cho worksheet đầu tiên. XML worksheet thường được nén Deflate trong ZIP nên checkpoint không có byte offset vật lý; khi slice,
XlsxRowsbắt đầu từ block logic đã chọn, nhưng Calamine vẫn materialize toàn bộ range worksheet trước đó. - Với Arrow IPC/Feather, builder lưu ordinal và khoảng dòng của từng RecordBatch. Khi đọc, hệ thống dùng random access batch index native của Arrow IPC thay vì offset cho từng dòng.
- Với CSV, builder quét ranh giới record có hiểu quote và lưu block dòng theo stride đã cấu hình. Checkpoint không bao giờ cắt giữa quoted field hoặc newline bên trong field; cùng scanner được dùng cho TSV, PSV và TXT phân cách bằng dấu chấm phẩy.
save_lake_map_ipc()serialize bản đồ;load_lake_map_ipc()đọc ngược qua memory map.
Schema trên đĩa
| Cột | Kiểu Arrow | Mô tả |
|---|---|---|
rel_path |
Utf8 |
Đường dẫn tương đối so với gốc lake |
size_bytes |
UInt64 |
Dung lượng tệp |
mtime_ms |
Int64 |
Thời điểm sửa đổi tính bằng mili-giây từ Unix epoch |
total_rows |
UInt64 |
Số dòng |
stats_json |
Utf8 |
JSON: {min, max, min_str, max_str} từng cột kèm số dòng |
first_global_row |
UInt64 |
Dòng bắt đầu khi sắp xếp file theo đường dẫn tương đối |
row_groups_json |
Utf8 |
Vị trí row group Parquet, stripe ORC, block OCF Avro, block object MsgPack, block dòng logic XLSX, block dòng NDJSON/JSONL/JSON-array và CSV/TSV/PSV/TXT hoặc RecordBatch Arrow IPC/Feather; [] với định dạng khác |
content_hash |
Utf8 (nullable) |
Digest BLAKE3 khi tạo map với fingerprint="blake3"; nếu không thì null |
Struct tổng hợp cũng mang theo total_files, total_rows, total_bytes. Stride checkpoint và chính sách fingerprint đã chọn được lưu trong metadata schema Arrow.
Phân giải vị trí
Với entry Parquet còn khỏe, slice_rows và slice_cols tìm row group giao với khoảng cần đọc rồi truyền các group đó cho reader. Nếu file nguồn có OffsetIndex, Parquet Arrow reader có thể dùng index nạp từ file để bỏ qua page bên trong các group được chọn; page location đã serialize trong map không được truyền vào reader. Mapped slice_cols chuyển cột được chọn xuống Parquet, Arrow IPC/Feather và ORC reader. Reader delimited trên đường mapped áp dụng cột được chọn khi parse, nhưng vẫn phải đọc byte của từng record được chọn để tìm ranh giới field. Các row reader khác (Avro, MsgPack, họ JSON và XLSX) decode các dòng được chọn trước rồi mới project cột.
Slice ORC seek tới byte offset của stripe chứa dòng rồi để ORC reader đọc stripe đó và các stripe sau. Slice Avro phát lại header OCF, seek tới byte offset của block chứa dòng rồi decode tiếp. Slice MsgPack seek tới checkpoint block object và decode tiếp bằng schema từ map đầu tiên. Slice XLSX bắt đầu XlsxRows tại block dòng logic chứa offset, nhưng Calamine đã materialize worksheet đầu tiên nên đây không phải seek byte vật lý trong ZIP.
NDJSON dùng checkpoint dòng. .json và .jsonl dùng checkpoint object mảng khi byte đầu tiên không phải whitespace là [ và dùng checkpoint dòng trong các trường hợp khác. Định dạng Delimited seek tới checkpoint dòng hiểu quote. Arrow IPC/Feather tìm RecordBatch chứa dòng qua native batch index. Các locator block, stripe, row-group và batch này vẫn cần reader decode tiếp các dòng trong range được chọn. br.lake.locate_row() phân giải global-row từ map đã lưu mà không decode dữ liệu nguồn, nhưng không kiểm tra map còn mới hay không. Hãy chạy Lake Doctor sau khi thêm, xóa hoặc sửa file nguồn; dùng fingerprint BLAKE3 cùng Doctor nếu cần phát hiện nội dung đổi nhưng giữ nguyên dung lượng và mtime. Khi resolve slice, hệ thống chỉ so size và mtime; không hash lại toàn bộ tệp. Dynamic handler override dùng handler đã đăng ký và không có locator built-in; map thiếu hoặc stale sẽ fallback về reader thông thường và không dùng map nếu metadata được kiểm tra cho thấy tệp đã thay đổi.
Lake Doctor
doctor_lake_map(dir_path, auto_heal) so sánh hiện trạng đĩa với danh mục:
| Trường báo cáo | Ý nghĩa |
|---|---|
status |
"HEALTHY" | "DRIFT_DETECTED" | "HEALED" |
total_files |
Số tệp thấy trên đĩa |
healthy_count |
Entry khớp các kiểm tra catalog (đường dẫn + dung lượng + mtime; thêm content hash nếu fingerprint="blake3") |
modified_files |
Đường dẫn đã biết nhưng không đạt kiểm tra freshness đã cấu hình (size/mtime, và BLAKE3 nếu bật) |
unindexed_files |
Tệp mới chưa có trong danh mục |
missing_files |
Entry trong danh mục nhưng tệp không còn trên đĩa |
healed |
Có chạy chữa lành hay không |
Với fingerprint="metadata" mặc định, phân loại drift so sánh đường dẫn đã discovery, dung lượng và thời gian sửa đổi; không phát hiện đổi nội dung cùng dung lượng nếu mtime được giữ nguyên. Với fingerprint="blake3", Lake Doctor cũng xác minh BLAKE3 content hash đã lưu. Slice chỉ kiểm tra đường dẫn, dung lượng và mtime; không hash toàn bộ file nguồn. Khi auto_heal=True, file mới và file đã sửa được đọc lại để dựng entry map. HEALTHY nghĩa là các kiểm tra đã cấu hình đều khớp, không phải baseline bảo đảm toàn vẹn nội dung bên ngoài.
Chữa lành dựng lại danh sách entry từ những gì còn tồn tại (bỏ missing_files, làm mới thống kê cho entry modified/unindexed) rồi ghi lại .br_map.bazan. Nếu đọc một file lỗi, thao tác dừng thay vì tạo catalog thiếu. Status chuyển thành "HEALED". Không có auto_heal=True thì báo cáo thuần túy chẩn đoán.
import basaltic_red as br
report = br.lake.doctor("data", auto_heal=False)
if report["status"] != "HEALTHY":
report = br.lake.doctor("data", auto_heal=True)
Chữ ký đầy đủ: br.lake.* · chi tiết bố cục nhị phân: Đặc tả Lake Map.