Skip to content

feat: self-describing blocks for index recovery#99

Merged
dkorbelainen merged 2 commits into
developfrom
feat/self-describing-blocks
Jun 9, 2026
Merged

feat: self-describing blocks for index recovery#99
dkorbelainen merged 2 commits into
developfrom
feat/self-describing-blocks

Conversation

@dkorbelainen

@dkorbelainen dkorbelainen commented Jun 8, 2026

Copy link
Copy Markdown
Collaborator

Problem

A storage_block carries no key. When a B-tree index node is lost/corrupted, its blocks become orphans — bytes recoverable, but file owner and position unknown. compio_repair dumps them as unordered orphan_*.bin. Full index reconstruction from blocks alone was impossible.

Change

Blocks become self-describing: each v2 block stores its owning {hash,pos} key (new signatures 173/174), folded into the block checksum.

  • file: v2 read/write, checksum covers back-ref, meta_size_for(sig)
  • allocator / reader / remove_file: version-aware on-disk footprint so mixed v1/v2 archives deallocate correctly
  • repair: re-attribute orphan v2 blocks via back-ref instead of orphan_*.bin
  • block read: verify block hash matches looked-up key (catches index pointing at the wrong file)
  • v1 blocks (171/172) still read unchanged — backward compatible

Cost

16 bytes per block (~0.1% at 16KB block size). No new syscalls, compression/checksum dominate CPU. No structural change.

Storage blocks now carry their owning {hash,pos} key (v2 layout,
signatures 173/174), folded into the block checksum. This lets
compio_repair rebuild files from blocks alone when index nodes are
lost, instead of dumping unattributable orphan_*.bin.

- file: v2 read/write, checksum covers back-ref, meta_size_for(sig)
- allocator/reader/remove_file: version-aware on-disk footprint so
  mixed v1/v2 archives deallocate correctly
- repair: re-attribute orphan v2 blocks via back-ref
- block read: verify block hash matches looked-up key (catches
  index pointing at the wrong file)
- v1 blocks (171/172) still read unchanged (backward compatible)

Overhead: 16 bytes per block (~0.1% at 16KB block size).
@dkorbelainen dkorbelainen changed the title feat(blocks): self-describing blocks for index recovery feat: self-describing blocks for index recovery Jun 8, 2026
@dkorbelainen

Copy link
Copy Markdown
Collaborator Author
  • Формат необратим, наверно пока сильно рисковое нововведение, возможно в будущем стоит прятать за флагом/версией хедера.
  • pos может устареть в back-ref после insert/erase-сдвигов. Для write-once это неважно, для insert/erase-heavy сироты при recovery могут лечь не на своё место.

В общем наверно пока не стоит.
@mozhaa

@dkorbelainen dkorbelainen requested a review from mozhaa June 8, 2026 07:42

@mozhaa mozhaa left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ну насчёт того что формат меняется это ладно, но то что pos может быть устаревшим это да, хотя тут как будто ничего и не поделать. Проблем то это не создаёт, просто и не решает полностью проблему того, что делать при потере индекса. Так что можно и вмерджить

Blocks rebuilt purely from self-describing back-refs can carry stale
absolute positions: lazy add_to_range shifts never rewrite evicted
blocks, so their on-disk pos drifts while sort order stays intact. The
logical stream is densely tiled, so the correct offset of each block is
the running sum of preceding block sizes. When no surviving index node
anchors a file, re-derive every position this way, recovering the file
byte-for-byte regardless of drift. Partial-index files keep their true
offsets untouched.

Co-authored-by: mozhaa <mozhay2005@gmail.com>
@dkorbelainen

Copy link
Copy Markdown
Collaborator Author

Добавил частичное решение проблемы с pos
При сборке файла только из back-ref'ов (полная потеря индекс-узлов) позиции теперь не берутся из stale-значения, а пересчитываются тайлингом: поток плотный и непрерывный, поэтому offset каждого блока = накопительная сумма размеров предыдущих. Порядок блоков сохраняется при любых insert/erase сдвигах, так что восстановление байт-в-байт независимо от дрейфа. Это закрывает массовый случай (дрейф абсолютного pos у вытесненных блоков больших файлов). Пути с выжившими узлами не тронуты, там pos остаётся верным.

Но это не вся беда, осталось еще это:
Редкая инверсия порядка (erase в начале файла, потом append, может поставить stale-pos старого блока выше нового) и соотв. недетектируемая неполнота набора (потерянный целиком блок). Полное решение это стабильный fractional order-key вместо абсолютного pos или типа того.

Поэтому пока так, а об этом надо будет подумать позже.

@dkorbelainen dkorbelainen merged commit 4a2ee23 into develop Jun 9, 2026
3 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants