Conflict-free
Replicated
Data
Types

Юрий Сыровецкий

Consistency
Availability
Partition
          tolerance

P

C        A

C (a)

 
 
  • Деньги
  • Правовые документы
  • иные критичные к «истине» приложения
  • Что угодно, если помещается в ДЦ
  • Почти что угодно, если помещается на Земле

A (c)

  • Совместная работа
  • Игры
  • Социалочки
  • Мобильные приложения
  • UI
  • Космические системы

Как решать конфликты?

OT

(Operational Transformation)

Как сделать правильно-то?

y \diamond z = z \diamond y \\ \text{-- commutativity}
x \diamond x = x \\ \text{-- idempotence}
(x \diamond y) \diamond z = x \diamond (y \diamond z) \\ \text{-- associativity}

Convergent
Replicated
Data
Types

(CvRDT, state-based)

Grow-only counter

\begin{aligned} \text{G-counter} &= (\N, \mathrm{max}) \\ \mathrm{max}(x, \mathrm{max}(y, z)) &= \mathrm{max}(\mathrm{max}(x, y), z) \\ \mathrm{max}(x, y) &= \mathrm{max}(y, x) \\ \mathrm{max}(x, x) &= x \end{aligned}

Счётчик лайков

Сервера: \(1..n\)

Лайки: \([c_1,..,c_n]\)

Product

A, B \in \mathrm{CvRDT} \implies A \times B \in \mathrm{CvRDT} \\ (x_{\tiny A}, x_{\tiny B}) \overset{A \times B} \diamond (y_{\tiny A}, y_{\tiny B}) = (x_{\tiny A} \overset{A} \diamond x_{\tiny B}, y_{\tiny A} \overset{B} \diamond y_{\tiny B})

Positive/Negative counter

\text{PN-counter} = \text{G-counter} ^ 2

Last Write Wins

\text{LWW}⟨A⟩ = (\text{Time} \times A, \text{max}_\text{Time})

Grow-only set

\text{G-set}⟨S⟩ = (2^S, \cup) \\ \begin{aligned} x \cup (y \cup z) &= (x \cup y) \cup z \\ x \cup y &= y \cup x \\ x \cup x &= x \end{aligned}

Observed-Remove set

S \in \text{OR-set}⟨A⟩ \xLeftrightarrow{\text{def}} \\ \begin{cases} S \sub \mathrm{Tag} \times (A \cup \{\bot\}) ,\\ \forall (t_1,\_), (t_2,\_) \in S : t_1 \neq t_2 \end{cases} \\~\\ \text{OR-set}⟨A⟩ = \text{Map}⟨\text{Tag}, A \cup \{\bot\}⟩
-- Haskell
type Tag = Timestamp
type ORSet a = Map Tag (Maybe a)

// Rust
type Tag = Timestamp;
type ORSet<T> = Map<Tag, Option<T>>;
\mathrm{add}(s, x) = \left| \begin{array}{l} t \larr \mathrm{new\_tag} \\ \text{insert}(s, t, x) \end{array} \right. \\~\\ \mathrm{remove}(s, t) = \text{update}(s, t, \bot) \\~\\ \begin{aligned} (t_1,x_1) \diamond (t_2,x_2) &= (t_1,x_1), (t_2,x_2) \\ (t,x) \diamond (t,\bot) &= (t,\bot) \end{aligned}

Где взять уникальные Tag?

$$\text{LamportTime} = \text{LocalTime} × \text{NodeId}$$

 

strictly monotonic

unique, ordered

causality:

$$\text{add}(t_i) ≺ \text{remove}(t_i)$$

a \prec b \implies a_\text{time} < b_\text{time}

А строки-то как?

Replicated Growable Array

\begin{aligned} \mathrm{RGA}⟨A⟩ = \big\{ & [(t_1, a_1), \dots, (t_n, a_n)] \\ \big| & t_i \in \mathrm{Tag}, t_i \neq t_j, \\ & a_i \in A \cup \{\bot\} \big\} \end{aligned}
\mathrm{RGA}⟨A⟩ = \text{OrderedMap}⟨\text{Tag}, (A ∪ \{⊥\})⟩
-- Haskell
type Tag = Timestamp
type RGA a = [(Tag, Maybe a)]

// Rust
type Tag = Timestamp;
type RGA<T> = Vec<(Tag, Option<T>)>;
\begin{aligned} \text{remove}&(\bar a,t_i) = \\ (\text{let} ~& [\dots,(t_i,a_i),\dots] = \bar a \\ \mathrm{in} ~& [\dots,(t_i,\bot),\dots]) \end{aligned}
\text{add}(\bar a, t_i, a') = \\ \left| \begin{array}{l} t' \larr \text{new\_tag} \\ \text{let} ~ [\dots, (t_i, a_i), (t_{i+1}, a_{i+1}), \dots] = \bar a \\ [\dots, (t_i, a_i), (t', a'), (t_{i+1}, a_{i+1}), \dots] \end{array} \right.

Известный писатель, врач и пушкинист Викентий Вересаев вспоминал: «В одной одесской газете при описании коронации – не помню, Александра III или Николая II, – было напечатано:

Митрополит возложил на голову Его Императорского Величества ворону.

В следующем выпуске газеты якобы появилась заметка:

В предыдущем номере нашей газеты, в отчёте о священном короновании Их Императорских Величеств, вкралась одна чрезвычайно досадная опечатка. Напечатано: «Митрополит возложил на голову Его Императорского Величества ворону» – читай: «корову».

\text{Multimap}⟨k, v⟩ = \text{OR-Set}\big⟨(k, v)\big⟩
\text{get}(\text{Multimap}⟨k, v⟩, k) \rarr \text{Set}⟨v⟩
\text{fold}⟨v : \text{Monoid}⟩\big(\text{Set}⟨v⟩\big) \rarr v
\text{Object} = \text{Multimap}⟨\text{String}, \text{Object}⟩

Commutative
Replicated
Data
Types

(CmRDT, op-based)

\big(State, Op, (≺), (⋅)\big) ∈ \text{CmRDT} \\ ~\\ \forall s ∈ State : \\ \forall op_1, op_2 ∈ Op : op_1 ∥ op_2 \implies \\ s⋅op_1⋅op_2 = s⋅op_2⋅op_1 \\ ~\\ op_1 ∥ op_2 \xLeftrightarrow{\text{def}} op_1 ⊀ op_2 ∧ op_2 ⊀ op_1
\begin{aligned} State &= 2^A \\ Op &= A \\ S⋅a &= S \cup \{a\} \end{aligned}

Op-based G-set⟨A⟩

\begin{aligned} State = Op &= \text{Time} \times A \\ (⋅) &= \text{max}_\text{Time} \end{aligned}

Op-based LWW

G-counter

PN-counter

\begin{aligned} State &= \N \\ Op &= \{1\} \\ (⋅) &= (+) \end{aligned}
\begin{aligned} State &= \Z \\ Op &= \{+1, -1\} \\ (⋅) &= (+) \end{aligned}

Op-based OR-Set

\begin{alignedat}{2} State & = & & \text{Map}⟨\text{Tag}, A ∪ \{⊥\}⟩ \\ Op & = & \big\{ & (\text{Add}, new:\text{Tag}, a:A), \\ & & & (\text{Remove}, target:\text{Tag})\big\} \\ \end{alignedat} \\~\\ \begin{aligned} S⋅(\text{Add}, new, a) &= \text{insert}(S, new, a) \\ S⋅(\text{Remove}, target) &= \text{update}(S, target, ⊥) \end{aligned}
-- Haskell
type Tag = Timestamp
type State a = Map Tag (Maybe a)
data Op a = Add Tag a | Remove Tag

// Rust
type Tag = Timestamp;
type State<T> = Map<Tag, Option<T>>;
enum Op<T> {
    Add(Tag, T),
    Remove(Tag),
}

Op-based RGA

\begin{alignedat}{2} State & = & & \text{OrderedMap}⟨\text{Tag} \times (A ∪ \{⊥\})⟩ \\ Op & = & \big\{ & (\text{Insert}, after, new : \text{Tag}, a : A), \\ & & & (\text{InsertBegin}, new : \text{Tag}, a : A), \\ & & & (\text{Remove}, target : \text{Tag})\} \\ \end{alignedat}
-- Haskell
type State a = [(Tag, Maybe a)]
data Op a
    = InsertBegin Tag a
    | Insert Tag Tag a
    | Remove Tag

// Rust
type State<T> = Vec<(Tag, Option<T>)>;
enum Op<T> {
    InsertBegin(Tag, T),
    Insert(Tag, Tag, T),
    Remove(Tag),
}

Replicated
Object
Notation

  • Обучу CRDT
  • Помогу спроектировать распределённое приложение
  • Реализую фичи в RON для вас
  • Twitter: @cblp_su
  • Telegram: @cblp
  • E-mail: crdt@cblp.su

CRDT

By cblpsu

CRDT

  • 103
Loading comments...

More from cblpsu