hbs2/docs/notes/bundles.txt

288 lines
14 KiB
Plaintext
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

NOTE: bundle-exchange-protocol
Проблема:
Долгое скачивание множества маленьких объектов, особенно при
работе со структурами, которые определяются через множество
ссылок ([HashRef]). Такой список сам по себе разбит на блоки,
блоки на чанки, то, на что он ссылается так же разбито, каждый
блок это несколько запросов в pull-модели (узнать размер,
запросить чанки и т.д).
Решения:
Данная проблема уже встречалась в hbs2-git и была решена
переходом от модели "один объект git --- одно дерево merkle" к
модели "один журналов push git --- одно дерево merkle". Таким
образом, стейт тут является объединением множества журналов,
каждый из которых содержит объекты git, отсутствующие в
известных журналах (предыдущий стейт).
Это радикально ускорило синхронизацию, однако ввело оверхед по
данным, и мы потеряли связь 1-1 между объектом git и объектом
(деревом?) hbs2.
Так же данная проблема встретилась в реализации QBLF при
большом потоке транзакций и будет встречаться всегда, когда мы
выражаем состояние, как множество ссылок на мелкие объекты.
Более общим решением видится введение некоего примитива bundle,
который будет собирать объекты в журнал, состоящий из секций,
каждая секция соответствует отдельному блоку. По факту, такой
журнал собирается из списка HashRef, HashRef указывает на
конкретный блоб.
Данный подход имеет смысл, когда требуется передать множество
мелких объектов. Передача же крупных объектов таким образом так
же возможна, однако она даёт возможность для злоупотреблений
(блоки огромного размера вместо деревьев). В принципе, бороться
с этим можно, так как в каждой секции у нас есть размер, и
можно принудительно объекты большого размера сохранять, как
деревья (?), однако тогда мы больше не увидим хэша данного
объекта, который передан в секции. Который, однако, является
опциональным.
Это ставит перед нами вопрос, как скачивать эти бандлы.
Текущй алгоритм скачивания парсит блоки, и если блок
является деревом меркля списка ссылок (MTree [HashRef]),
то он попытается скачать и каждый блок, на который указывает
HashRef.
Если **перед** этим мы узнаем, что для MTRee [HashRef] есть
бандл, мы можем выкачать и импортировать его.
Если объектов много --- то это плюс. Но если их мало/средне,
то может возникнуть ситуация, что мы скачаем и объекты, и их
журнал.
Как с этим бороться без очень сложных настроек/взаимоотношений
процессов --- пока непонятно.
Частное решение для QBLF может быть таким: стейт есть хэш
конкретного Bundle.
Тогда мы:
Новое состояние:
- создаём bundle
- рассылаем анонс стейта как обычно
Мерж:
- читаем все бандлы (состояния) --
тут у нас есть и транзакции и их хэши
- делаем новый бандл после мержа транзакций
из всех бандлов
Остальное:
- как было
Плюсы:
- мы не дублируем данные, если не хотим (можем не
импортировать объекты из бандлов)
- можем импортировать, но только при переходе в
состояние
- скачивание объектов будет быстрое
- алгоритм мержа усложнится незначительно
- не нужно сейчас решать, что делать с протоколом
обмена бандлами и как его дизайнить
Минусы:
- это опять, скорее, частное решение, но за счёт
этого мы избегаем оверхедов
- переписывание всего журнала при переходе
к новому стейту?
- возможен подъем всего стейта в память (а не по одной
транзакции, более того -- сейчас так и будет)
Более общее решение:
- Создаём новый стейт, как [HashRef]
- Для него --- создаём и анонсируем бандл
- Бандл скачивается отдельно и самостоятельно
- После импорта бандла его можно, в принципе, удалять
- Можно подержать какое-то время, что бы раздать другим
пирам
- Бандл, вероятно, скачается быстрее, чем будет рекурсивно
скачиваться меркл-дерево множества ссылок
- Можно попробовать вообще не качать дерево, а качать только
его бандл, а само дерево из него реконструировать (???),
тогда уходим от оверхеда при скачивании
- Получив анонс стейта, нам бы как-то узнать хэш его бандла, что бы
скачать бандл.
- Анонсы бандлов можно, всё ж таки, рассылать через Notify,
это не заденет остальные части системы и не надо будет
делать новый протокол
- Но этот протокол может был бы полезен в других
частях
Плюсы:
- Стейт остаётся [HashRef], то есть не требует
подъема больших объектов в память
- Алгоритм merge вообще не меняется
- В других кейсах может пригодиться
Минусы:
- Явно сложнее
- Со ссылками возникают вопросы византийского поведения:
например, ноды могут фальсифицировать ссылки или просто
возвращать мусор
- Если в ключ ссылки включить публичный ключ пира
(актора?), то можно хотя бы убедиться, что данная ссылка
целостна. И тогда у того, кто её распространяет --- не
получится её фальсифицировать, только игнорировать.
Можно вставлять не в ключ, а в значение, и его
подписывать. Таким образом, это обычный SignedBox.
- В контексте QBLF это повлияет только на скорость
распространения стейта, т.е если мы не достигаем
консенсуса по значению ссылки -- то рано или поздно
журнал выкачается и так
- Если это ссылка неизвестно на что, то откуда
SomeRefProto узнает, что значение надо скачивать
и импортировать?
Отвечаем: Это будет, допустим, (SignedBox Bundle)
и протокол, который качает, допустим, попробует
распарсить и если это Bundle - то качать его HashRef.
- А как он, например, его будет импортировать, когда
Bundle скачается?
Отвечаем: допустим, получили Bundle -- запускаем
DownloadMonitor с нужной периодичностью, время от
времени поллим, как поняли, что скачалось ---
запускаем импорт, т.к. процесс импорта передаём
в этот самый монитор (ой мама). Что-то подобное
в любом случае надо бы/надо было сделать
Другое более общее решение:
У нас уже есть механизм BlockAnnounce. Если мы принимаем
BlockAnnounce, то там можно добавить блок типа "Значение ссылки:
ссылка на бандл". Тогда, если мы получили такой блок - мы можем
добавить его в Detect, и если там бандл - то качать этот самый
бандл.
Это избавит нас от необходимости делать полную реализацию
протокола обмена ссылками, который довольно спорный пока что.
Итак, что мы добавляем: новый тип, возможно, "Ссылка на bundle"
. тип этот помещаем в блок, например.
Получив этот блок, например, даунлоадер начинает его скачивать,
а так же, допустим, вешает монитор - что бы импортировать
объекты по завершению процесса.
Тогда мы решаем в рамках текущего BlockAnnounce, с применением
тех политик, что там используются, что не помешает нам
реализовать механизм обмена ссылками, буде до него дойдет.
Возникает, кстати, проблема, что делать с шифрованием.
Ответ -- или класть уже зашифрованные блоки (а откуда ключ
брать для расшифровки?)
Или ввести обёртку EncryptedBundle или сразу сделать у
Bundle метаинформацию, и там ссылку на ключ передавать
TODO: bundle-announce
TODO: bundle-value-block
TODO: bundle-value-block-to-detect
TODO: bundle-value-block-to-downlooad-bundle
TODO: bundle-value-block-to-imoport-on-downloaded
Базовая реализация бандлов
Основная идея:
Берём список HashRef, каждый HashRef может ссылаться
на какие-то объекты и создаем журнал (замыкание),
куда складываем все нужные объекты.
Это может быть нужно для ускорения передачи
большого числа маленьких объектов там, где это
нужно.
Минусы: создаются объекты-обёртки,
дублируются данные в бандлах и БД.
Решение:
TODO: introduce-bundle-ttl
удалять бандлы и BundleRefValue,
которые никто не трогал дольше, чем
ttl.
Делаем bundle:
```
hbs2 bundle create < bundle-refs
<SOME-HASH>
```
В bundle-refs список хэшей (HashRef) в текстовом виде
Делаем ссылку на bundle:
```
hbs2 bundle create-ref -k some.key <SOME-HASH>
<SOME-OTHER-HASH>
```
Создаем объект типа BundleRefValue и подписывает его
неким ключом.
Идея в том, что даже если мы не создатели Bundle и BundleRefValue,
мы можем его сохранять и распространять, сохраняя оригинальную
подпись.
Клиенты уже сами решают, доверяют они ключу подписанта или нет.
Распространение bundle:
```
hbs2-peer announce <SOME-OTHER-HASH>
```
Теперь те, кто вообще слушают анонсы от пира --- получат
блок BundleRefValue, проверят подпись, поставят Bundle
на скачивание, и когда он скачается - импортируют объекты
из него.