mirror of https://github.com/voidlizard/hbs2
288 lines
14 KiB
Plaintext
288 lines
14 KiB
Plaintext
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
|
||
на скачивание, и когда он скачается - импортируют объекты
|
||
из него.
|
||
|