From 1428324f1759db42596f4b8a4e16c383c4786a3b Mon Sep 17 00:00:00 2001 From: Dmitry Zuikov Date: Wed, 3 May 2023 06:13:43 +0300 Subject: [PATCH] hbs2-git-problem document added --- docs/hbs2-git-problem/Makefile | 8 + docs/hbs2-git-problem/hbs2-git-problem.tex | 627 +++++++++++++++++++++ 2 files changed, 635 insertions(+) create mode 100644 docs/hbs2-git-problem/Makefile create mode 100644 docs/hbs2-git-problem/hbs2-git-problem.tex diff --git a/docs/hbs2-git-problem/Makefile b/docs/hbs2-git-problem/Makefile new file mode 100644 index 00000000..9886f8f9 --- /dev/null +++ b/docs/hbs2-git-problem/Makefile @@ -0,0 +1,8 @@ +.PHONY: all clean + +all: + xelatex hbs2-git-problem.tex < /dev/null + xelatex hbs2-git-problem.tex < /dev/null + +clean: + rm -f *.aux *.log *.nav *.out *.snm *.vrb *.toc diff --git a/docs/hbs2-git-problem/hbs2-git-problem.tex b/docs/hbs2-git-problem/hbs2-git-problem.tex new file mode 100644 index 00000000..9a41f6e1 --- /dev/null +++ b/docs/hbs2-git-problem/hbs2-git-problem.tex @@ -0,0 +1,627 @@ +\documentclass[11pt,a4paper]{article} + +\usepackage{polyglossia} +\usepackage{xltxtra} +\usepackage[margin=2cm,a4paper]{geometry}% http://ctan.org/pkg/geometry +\usepackage{pdfpages} +\usepackage{graphicx} +\usepackage[ddmmyyyy]{datetime} +\usepackage{booktabs} +\usepackage{enumitem} +\usepackage{amssymb} +\usepackage{amsmath} +\usepackage{bm} +\usepackage[nomessages]{fp} +\usepackage{caption} +\usepackage{url} +\usepackage{indentfirst} +\usepackage[parfill]{parskip} +\usepackage[ colorlinks=true + , linkcolor=black + , anchorcolor=black + , citecolor=black + , filecolor=black + , menucolor=black + , runcolor=black + , urlcolor=blue]{hyperref} +\usepackage{tikz} +\usetikzlibrary{arrows,snakes,shapes,backgrounds,positioning,calc} +\usepackage{marvosym} +\usepackage{pifont} +\usepackage{fontspec} +\usepackage{fontawesome5} + +\setmainlanguage{russian} +\defaultfontfeatures{Ligatures=TeX,Mapping=tex-text} +\setmainfont{Liberation Serif} +\newfontfamily\cyrillicfont{Liberation Serif}[Script=Cyrillic] + +\setlist{noitemsep} +\setlength{\intextsep}{2cm} + +\renewcommand{\dateseparator}{.} +\renewcommand*\contentsname{Содержание} + +\graphicspath{ {img/}} + +\title{Проблема репозитория HBS2 GIT} + +\begin{document} + +\maketitle + +\section{Определения} + +\begin{description}[itemsep=10pt] + + \item[Объект] Любой объект, сохраняемый и передаваемый в системе. Файл, + структура данных и т.п. + + \item[Блок] Адресуемые (имеющие криптографический хэш) данные. Данные это последовательность байт. + Может быть любого размера, типичный размер: не более 256K. Зашифрованный блок может + быть больше, так как при шифровании в него добавятся данные алгоритма шифрования. + Блок может быть и меньше 256K, допустим блок это маленький объект. Тогда размер блока + будет равен размеру объекта. + \par \textbf{Note:} при шифровании данные можно предварительно сжимать. + + + \item[Чанк] Нумерованая часть \textit{блока}. Для каждого блока известного размера можно построить + единственное разбиение на чанки заданного размера. Размер чанка выбирается таким + образом, что бы его можно было поместить в UDP пакет безопасного размера. + Безопасного это такого, что можно не заботиться о фрагментации. Считается безопасным + размер UDP пакета 508 байт, на практике MTU всегда 1500 и либо фрагментацией + занимается сетевой стек и делает это хорошо, либо MTU других размеров уже не + встречаются, в любом случае практических проблем с размером чанка = 1400 не + встретилось. В системе размер чанка принят за 1200 --- 1400 байт. Чанк используется + в протоколе передачи блоков: так как по UDP мы не можем запросить блок целиком, мы + запрашиваем его по частям, каждая такая часть и есть чанк. В протоколе существует + возможность запрашивать не каждый чанк одельно, а последовательностями, именуемыми + burst. + + \item[BlockSize] Размер блока. Может быть любой, не должен быть слишком + маленьким, но и слишком большим. Типично в районе 256K. + + \item[ChunkSize] Максимальный рамер чанка. Должен иметь размер, не превышающий + максимального безопасного размера для UDP (1200 --- 1400 + байт) (с учетом заголовка пакета). + + \item[Burst] Последовательность чанков в протоколе. Клиент запрашивает не по каждому чанку + отдельно, а сериями по N чанков, начиная с k-го. Каждая такая последовательность + и есть burst. + + \item[Merkle~Tree] Любой крупный объект, данные или последовательность объектов + разбивается на блоки при помощи построения его Merkle дерева + (в нашей интерпретации). Дерево имеет два параметра: + количество элементов в каждом узле, и размер (в каком-то виде) каждого + элемента. Построение дерева происходит в два этапа: сначала мы производим + разбиение, учитывая эти два параметра, затем --- обходим получившееся дерево, + сериализуем каждый узел (пишем блоки в \textit{хранилище}), и из полученных + при сериализации хэшей строим уже дерево Меркля (Merkle~Tree). + Дерево может состоять из узлов типа (Merkle~Tree) либо Leaf + - указание на блок (данных). + + \item[Annotated~Merkle~Tree] Merkle~Tree с аннотацией в виде либо короткой + строки, либо ссылкой на блок с метаинформацией. + + \item[Peer] Пир. Участник сети, авторизованный своим криптографическим ключом + подписи, и поддерживающий протоколы системы. + + \item[RTT] Round trip time, время на посылку запроса и получение + ответа (в сети). Можно рассматривать на уровне прикладного протокола HBS2, + можно на уровне транспортного (TCP). Обычно имеется ввиду уровень протокола + HBS2. + + \item[Reflog] Механизм реализации изменяемой ссылки. Каждая ссылка + определяется неким публичным ключом. Для каждой ссылки можно опубликовать + транзакцию, короткий (ChunkSize) блок данных, который может либо содержать + произвольные данные, либо \textit{ссылку} на блок или Merkle~Tree. В случае, + если внутри ссылка -- то данные ссылки будут скачаны пиром автоматически + рекурсивно. Все транзакции Reflog упорядочиваются по их хэшам в виде списка, + который сохраняется в виде Merkle~Tree. Таким образом, значение ссылки типа + Reflog это хэш Merkle~Tree упорядоченного по хэшам списка транзакций. + +\end{description} + +\section{Проблема} + +Медленная передача большого количества маленьких объектов (блоков). +Приводит к очень медленному первоначальному скачиванию репозитория. +Обновления затем происходят с приемлемой скоростью. + +Связано с тем, что в текущем протоколе для каждого передаваемого адресуемого +блока нам нужно: + +\begin{enumerate} + \item Запросить размер (выяснить его наличие) + \item Запросить его чанки +\end{enumerate} + +Таким образом, скачивание одного маленького объекта занимает минимум: +\mbox{$ 2 \times RTT + C + T$}, где C - время обработки запроса на обеих +сторонах, T - время передачи самих данных. + +На практике это означает, что например при типичном $ RTT = 50ms $ мы доcтигаем +скорости 6 блоков (чанков) в секунду, что даём нам не более 9Kb/s, реально в +районе 6 --- 8. Каковая скорость и наблюдается экспериментально. + + +Текущее устройство репозитория hbs2-git: + +\begin{figure}[h!] +\centering + \begin{tikzpicture}[every node/.append style={font=\scriptsize}] + + \node[ rectangle split + , rectangle split parts=3 + , draw + , font=\scriptsize + , text width=3cm + , label={above:Reflog} + ] (reflog) + { + Transaction~N + \nodepart{two}... + \nodepart{three}Transaction~1 + }; + + + + \node[ rectangle split + , rectangle split parts=4 + , draw + , font=\scriptsize + , text width=3cm + , right=2cm of reflog.north east, anchor=north west + , label={above:Object~list} + ] (objlist) + { + \nodepart{one}HEAD~Object + \nodepart{two}Git Object~1 + \nodepart{three}... + \nodepart{four}Git object~N + }; + + \node[draw,rectangle,below=2cm of objlist,xshift=-3cm,minimum + height=1cm,label={below:Git Object}] + (merkle) {Annotated~Merkle~Tree}; + + \draw[-latex] (reflog.15) -- (objlist.155); + \draw[-latex] (objlist.205) to [bend right,looseness=1] (merkle.north); + + +\end{tikzpicture} +\end{figure} + + +\begin{description}[itemsep=10pt] + \item[Reflog] Это Merkle~Tree списка \textit{транзакций}. + \item[Транзакция] Это небольшой объект (размера не больше ChunkSize), + произвольного содержания, в случае HBS2 Git -- со ссылкой на Merkle~Tree + списка объектов. + \item[Object list] Список всех достижимых объектов репозитория (транзитивное + замыкание всех объектов для коммита) + HEAD (список ссылок в смысле git и их значений) + \item[Git Object] Сохраненный в виде Annotated~Merkle~Tree объект git. В + аннотации указан тип объекта (commit, blob, tree) +\end{description} + +Данное устройство было выбрано как + +\begin{enumerate} + \item Наиболее простое и очевидное + \item Минимизирующее оверхед по данным на уровне git объектов -- т.е уже + имеющиеся git объекты никогда не будут скачиваться повторно + \item Любая транзакция описывает полный, консистентный репозиторий, т.е можно + не иметь полного набора транзакций, но иметь, тем не менее, полный + репозиторий. + \item Каждый когда-либо созданный git object доступен в сети пиров по хэшу его + Merkle~Tree, что казалось довольно удобно (но практически бесполезно) +\end{enumerate} + +Несмотря на стремление к минимизации оверхеда по данным, данное устройство его, +тем не менее, создаёт в другом месте: + +Каждое Merkle~Tree это минимум один дополнительный блок -- т.е блок на +Merkle~Tree + блок на блок данных. + +Как можно видеть, данное устройство располагает к образованию большого +количества маленьких объектов: сами объекты git как правило маленькие, +транзакции = маленькие объекты (ChunkSize). + +Большое число: 10---20 тысяч для репозитория размером порядка 600 коммитов. + +Что, если взять скорость 5 блоков (чанков) в секунду (из оценки выше), приводит +к нас с показателю 4000 секунд на 20'000 блоков, что примерно соответствует +наблюдаемой картине (в реальности чуть лучше). + +Несмотря на то, что последующие скачивания будут выполняться быстро (объекты не +будут выкачиваться повторно, будут скачаны только недостающие объекты, их +немного), первоначальное развертывание репозитория происходит неприемлемо +медленно. + +Это и есть проблема. + +\section{Возможные решения} + +Что мы в принципе хотим добиться: + +\begin{enumerate} + \item Быстрое скачивание всего репозитория; + \item Минимизировать оверхед по данным, т.е что бы одни и те же объекты + (разбитые на блоки), по возможности, скачивались один раз. +\end{enumerate} + +\subsection{Большой лог} + +Основная идея --- не порождать большое число маленьких объектов, как в текущем +дизайне, вместо этого ввести \textit{лог}, куда будут упорядоченно +писаться объекты git, каждый объект только один раз. + +Такой лог представляет собой просто большой файл, который может передаваться +обычными механизмами с максимально высокой скоростью ( десятки Mb/s ). + +Данное решение имеет недостатки: + +\begin{itemize} + \item[-] Более нет соответствия $ git object \leftrightarrow merkle tree $ + + \item[-] При отсутствии выравнивания, логи разных форков репозиториев + не сойдутся никогда, следовательно, всегда будет перевыкачиваться + различающаяся часть и эта часть будет расти со временем + + \item[-] При наличии <<крупного>> (256K) выравнивания оверхед по данным возрастает + на порядки ( ~ 3Gb для числа объектов $ \approx 12000$ ) + + \item[-] При наличии <<мелкого>> выравнивания оверхед всё равно + существенный ( $ \approx 50\% $ для блока размером 1K ), + но растёт число мелких объектов и соответствующим падением скорости + передачи. +\end{itemize} + +\subsubsection{Большой лог всех объектов} + +В случае выровненной записи --- получаем описанные выше недостатки. + +В случае невыровненной записи --- получаем расхождение логов у разных писателей +(форков), соответствовать будет лишь начальная часть. + +Можно ввести специальную дисциплину работы с логом и мерж лога, тогда можно +будет их переупорядочивать у всех, и логи будут периодически сходиться. Высокая +сложность реализации и много неоднозначностей. + +Можно писать лог каждый раз при push, тогда для одинаковых git репозиториев +будут одинаковые логи. Очень медленно и время будет расти с ростом репозитория. + +\textbf{Плюсы:} + +простая реализация, быстрое первоначальное развертывание. + +\textbf{Минусы:} + +всё остальное. + +\subsubsection{Отсортированный по времени/высоте сегментированный лог} + +Как можно видеть, ситуация с большим логом объектов, даже отсортированных по +высоте/времени может приводить к перевыкачиванию значительной части лога в +ситуации, когда кто-то отредактировал историю так, что новые объекты появились в +начале лога, что допустимо в git. В случае больших репозиториев это приведёт к +скачиванию большого количества данных и хранению этих данных. Что бы реже +сталкиваться с подобной проблемой, будем строить стейт следующим образом: + +\begin{figure}[h!] +\centering + \begin{tikzpicture}[every node/.append style={font=\scriptsize}] + + \node[ rectangle split + , rectangle split parts=3 + , draw + , font=\scriptsize + , text width=3cm + , label={above:Reflog} + ] (reflog) + { + Transaction~N + \nodepart{two}... + \nodepart{three}Transaction~1 + }; + + + \node[ draw + , right = 2cm of reflog.north east, anchor=north west + , rectangle split, rectangle split horizontal, rectangle split parts=4 + , label={below:PUSH log} + ] (pushlog) + { \nodepart{one}$S_1$ + \nodepart{two}... + \nodepart{three}$S_n$ + \nodepart{four}HEAD + }; + + \draw[-latex] (reflog.12) -- (pushlog) node[midway,above] {merkle~tree}; + +\end{tikzpicture} +\end{figure} + + + +\begin{itemize} + \item[-] Каждая транзация $T_x$ содержит + отсортированные по времени (высоте) и по хэшу объекты репозитория + \item[-] $S(R_x) = \bigcup$ объектов из всех транзакций Reflog + \item[-] Берем объекты, отсутствующие в $S(R_x)$, сортируем по времени/высоте и затем хэшам + и пишем в лог в сжатом виде можно сжать весь лог) + \item[-] Объект HEAD пишем в тот же лог, начало или конец (TBD) + \item[-] Публикуем транзацию $T_y$, которая содержит ссылку на Merkle~Tree лога из предыдущего + пункта +\end{itemize} + +\textbf{Следствия:} + +\begin{itemize} + \item[-] Стейт $S(R_x)$ каждой ссылки $R_x$ обновляется инкрементально + \item[-] Каждое обновление (транзакция $T_x$) соответствует push в соответствующий + \textit{git~remote} + \item[-] Каждое обновление содержит объекты, отсутствующие в $S(R_x)$, отсортированные + детерменированным образом, зависимости только состояния репозитория git + \item[-] Каждый сегмент лога, кроме первого, как правило, сравнительно небольшой -- содержит + только новые объекты, созданные пользователем (commit, tree, blob) + \item[-] В рамках одной ссылки логи перевыкачиваться не будут, как логи (содержимое + Merkle~Tree транзаций $T_x$ создаются детерменированным образом в завимости только + содержимого репозитория git + \item[-] Частичные логи могут иногда совпадать, так что они не всегда будут дублироваться между + ссылками. Различные кейсы рассмотрим далее отдельно +\end{itemize} + +\textbf{Ситуация 1:} + +\begin{itemize} + \item[-] Алиса делает \textit{EXPORT} существующего репозитория в свою ссылку $R_a$ + \item[-] Боб делает \textit{git~clone} данного репозитория (ссылки $R_a$) + \item[-] Боб создаёт новую ссылку $R_b$ + \item[-] Боб делает \textit{EXPORT} репозитория в эту ссылку + и сообщает ссылку $R_b$ Алисе + \item[-] Боб делает изменения и делает \textit{git~push} в ссылку $R_b$ +\end{itemize} + +В этом случае: + + +\begin{enumerate} + \item Создаётся лог, содержащий объекты $S(R_a)$ --- все объекты репозитория, + в порядке создания/высоты и далее в порядке хэшей + \item Публикуется транзация $T_x$ со ссылкой на Merkle~Tree лога объектов + \item Боб принимает данную транзацию, объектов $S(R_a)$ --- данных объектов + у него еще нет, следовательно дублирования нет + \item Объекты $S(R_a)$ распаковываются в репозиторий git + \item Создаётся новая ссылка $R_b$. При \textit{EXPORT} создаётся + первоначальная транзакция, которая ровно такая же, как и $T_x$, + так как содержимое лога определяется только объектами репозитория git, + а они все из $S(R_a)$ + \item Боб делает свои изменения и делает \textit{git~push} + \item Создается $T_{b1}$, содержащая объекты $S(R_{b1})$, отсутствующие в + $S(R_a)$ + \item Алиса получает $T_{b1}$, содержащая объекты $S(R_{b1})$ и импортирует + эти объекты в свой репозиторий git. +\end{enumerate} + +На шаге (5) мог не выполниться \textit{EXPORT}, так как его выполнение зависит +от пользователя. В этом случае на шаге (7) будет создана $T_{b1}$ содержащая все +объекты репозитория, включая те, что были созданы Бобом на шаге (6). В этом +случае, какое-то количество объектов в зависимости от гранулярности разбиения +(BlockSize), попадут в те блоки, что уже есть у Алисы в $S(R_a)$. И чем больше +был размер лога $S(R_a)$, тем больше объектов Алиса не будет скачивать повторно, +так как порядок следования определяется только объектами в репозитории, Боб не +переписывал историю и его объекты будут следовать за объектами Алисы. + +Если же Боб переписал историю, что, в целом, возможно, то Алиса не будет +повторно выкачивать только часть лога до изменений Боба. + +Таким образом видно, что при создании ссылки $R_b$ нужно форсировать операцию +EXPORT. + +Как можно её форсировать, ведь Боб может и не создавать ссылку, или создавать +её в любой момент времени. + +Например, при операции PUSH нужно каким-то образом получить все объекты, +которые шли раньше изменений Боба и создать транзакцию $T_{b0}$ в которой +будут эти объекты. + +Как система должна понять, что этот \textit{git~push} --- не первоначальная +инициация репозитория, а апдейт? Например, так: при \textit{FETCH} +в случае отсутствия транзакций создаётся (и публикуется) первая транзакция. +Тогда в большей части случаев она будет соответствовать инициации репозитория, +и при последующем \textit{PUSH} или \textit{EXPORT} в создаваемую транзакцию +попадут только те объекты, которые создал владелец новой ссылки (Боб в нашем +случае). + + + +\subsubsection{Merkle~Tree+лог с переменным разбиением} + +\begin{figure}[h!] +\centering + \begin{tikzpicture}[every node/.append style={font=\scriptsize}] + + \node[ rectangle split + , rectangle split parts=3 + , draw + , font=\scriptsize + , text width=3cm + , label={above:Reflog} + ] (reflog) + { + Transaction~N + \nodepart{two}... + \nodepart{three}Transaction~1 + }; + + + \node[ rectangle split + , rectangle split parts=5 + , draw + , font=\scriptsize + , text width=3cm + , right=2cm of reflog.north east, anchor=north west + , label={above:Object~list} + ] (objlist) + { + \nodepart{one}HEAD~Object + \nodepart{two}SmallObjectLog + \nodepart{three}... + \nodepart{four}BigGitObject + \nodepart{five}... + }; + + + \node[draw,rectangle,below=1cm of objlist.north east,xshift=2cm,minimum + height=1cm,label={[xshift=1.2cm]above:Small~Object~Log}] + (smallmerkle) {Merkle~Tree}; + + \node[right=0.1cm of smallmerkle,text width=4.2cm,font=\scriptsize] + (smallmerklenote) {С переменным шагом разбиения:\\один объект=один лист}; + + \node[ rectangle split + , rectangle split parts=4 + , draw + , font=\scriptsize + , text width=3cm + , below=1cm of smallmerkle + , label={below:SmallObjectLog} + ] (objlog) + { + \nodepart{one}SmallObject~1 + \nodepart{two}... + \nodepart{three}SmallObject~N + \nodepart{four}... + }; + + \node[draw,rectangle,below=2cm of objlist,xshift=-3cm,minimum + height=1cm,label={below:Git Object}] + (merkle) {Annotated~Merkle~Tree}; + + \draw[-latex] (reflog.15) -- (objlist.155); + + \draw[-latex] (objlist.200) to [bend right,looseness=1] (merkle.north); + + \draw[-latex] (objlist.12) -| (smallmerkle.north); + + \draw[-latex] (smallmerkle.south) -- (objlog.north); + +\end{tikzpicture} +\end{figure} + + + +Несмотря на то, что сейчас дерево разбивается на блоки фиксированного размера, +необходимости в этом нет. Это означает, что мы можем генерировать Merkle~Tree +таким образом, что каждому маленькому объекту будет соответствовать один объект +(лист) Merkle~Tree (т.е они не будут разбиваться: мы сначала генерируем лог +<<маленьких>> объектов, затем разбиваем его, принимая во внимание размер каждой +секции (заголовок+объект), так что каждая секция --- ровно один лист (хэш) +Merkle~Tree. + +\textbf{Требование:} Каждый <<маленький>> объект должен быть достаточно +маленьким для того, что бы можно было его безопасно читать в память. + +<<Большие>> объекты передаются ровно так, как сейчас, то есть каждый отдельно. +<<Большие>> это, допустим, от 1Mb. + + +\textbf{Плюсы:} + +\begin{itemize} + \item[+] Выполняется требование отсутствия оверхеда по данным + \item[+] Нет множества <<мелких>> объектов: лог <<большой>>, + отдельные объекты тоже <<большие>>. +\end{itemize} + +\textbf{Минусы:} + +\begin{itemize} + \item[-] Передаваться будет дольше, чем <<большой>> лог + \item[-] Средняя сложность реализации + \item[-] <<Переменное разбиение>> $ => $ объекты git маленькие $ => $ много + маленьких объектов $ => $ \textbf{не работает} +\end{itemize} + +\subsection{Ускорение скачивания мелких объектов} + +Невредно сделать в любом случае, однако полезность данного механизма может +варьироваться, а сложность может быть велика. Мы рассматриваем в первую очередь +ограничения UDP, так как в настоящий момент по TCP ходят в точности такие же +протоколы, как и по UDP. Это может быть изменено, но потребуется поддержка +механизмов, которые будут динамически разрешать или запрещать протоколы в +зависимости от типов транспорта. Это возможно, сложность --- ниже средней. + +\subsubsection{Пакетные запросы} + +Поскольку мы можем передать по UDP 1200 --- 1400 байт в одной датаграмме, +мы можем запросить $ 1200 / 32 \approx 32 $ объекта за один раз. + +Но если речь идёт о UDP, то ответ, очевидно, не может быть получен в одной +датаграмме, значит, нужен какой-то механизм стриминга/сборки ответа из кусков, +и уже имеющийся механизм не подходит, так как ответ создаётся динамически и +отсутствует в хранилище. Таким образом, его надо или создавать в хранилище (и +увеличивать число данных, причём, мусора), либо как-то создавать временный +ответ и скачивать его текущими механизмами. + +Это возможно, но неизящно: + +\begin{enumerate} + \item Получить запрос + \item Сформировать ответ (записать данные на /tmp допустим) + \item Посчитать хэш ответа ( как блока(!) ) + \item Добавить хэш в специальную таблицу <<временные объекты>> + \item Сообщить хэш и размер запрашивающей стороне + \item Запрашивающая сторона получает блок обычным способом + \item При обработке запроса сервер смотрит, не является ли блок временным + объектом и читает его, а не хранилище +\end{enumerate} + +Кроме того, ускорение может получиться не столь значительным, так как +мы можем не знать все объекты заранее, и что бы их узнать, надо сначала +скачать другие объекты. + +Из наблюдаемых явлений видно, что в какой-то момент времени очередь запросов +становится весьма большой ( $ \approx 10K $ объектов ), следовательно, можно +запрашивать объекты пачками, следовательно, будет иметь место ускорение в $ +\approx 32 $ раза, что приведёт к скорости отдачи мелких объектов в районе +160Kb/s. Что не бог весть что, на самом деле. Текущие скорости отдачи +"нормальных" объектов составляют около ширины канала, десятки мегабайт в +секунду. + +\subsubsection{Стриминг Merkle~Tree} + +Специальная команда, в ответ на которую передаются все блоки Merkle~Tree, не +дожидаясь запросов. + +Проблематичная реализация на UDP, в виду возможного реордеринга и потери +пакетов, а имеющийся текущий механизм, устойчивый к реордерингу и потерям, +неприменим. + +Вернее, он применим, если мы создадим механизм <<временного>> хранилища -- +аналогичного обычному, но существующему лишь некоторое время. Тогда +любые промежуточные объекты могут быть созданы, существовать в +течение сессии или некоторого времени и впоследствии удалены. + +Можно и не создавать временное хранилище, а помечать объекты, как временные +и рекурсивно удалять через некоторое время. + +Можно создавать временный объект <<транзитивного замыкания>> для дерева, +сообщать его хэш, и давать клиенту скачивать его обычным образом. + +Можно видеть, что любые варианты пакетного скачивания объектов ведут к +работе <<сервера>> и расходованию его ресурсов, т.е при незначительном +усилии со строны клиента (один запрос) сервер вынужден сканировать десятки +тысяч объектов и создавать временные объекты потенциально любых размеров, +что открывает путь для намеренных и ненамеренных атак на пира. + +Если у нас есть надёжный транспортный протокол (TCP) то можно стримить данные, +не создавая временных структур, тогда затраты локальных ресурсов будут ниже, +но тем ме менее, возможность DoS остаётся: одна команда со стороны <<клиента>> +приводит к несопоставимо большой вычислительной работе <<сервера>> и передачи +большого объема данных (много больше исходного запроса). + +\end{document} + +