#DevoxxFR
Algorithmes distribués
pour le Big Data
saison 2
1
#DevoxxFR
Un peu de moi
Duy Hai DOAN
Evangéliste technique & consultant Apache Cassandra
•  talks, meetups, confs
•  projet open-source (Achilles, Apache Zeppelin)
•  Des questions sur Apache Cassandra/Apache Zeppelin ?
☞ duy_hai.doan@datastax.com
☞ @doanduyhai
2
#DevoxxFR
Datastax
•  Fondé en Avril 2010
•  Plus gros pourvoyeur de contributeur à Apache Cassandra™
•  Bureaux européens à Londres, Paris et Berlin
•  Datastax Enterprise = OSS Cassandra + fonctionnalités+++
3
#DevoxxFR
CRDT
Marc Shapiro – 2011
#DevoxxFR
Le défi
5
Dans un système distribué,
définir les conditions suffisantes à respecter
pour garantir la convergence des données
potentiellement désynchronisées
#DevoxxFR
Le défi
6
Dans un système distribué,
définir les conditions suffisantes à respecter
pour garantir la convergence des données
potentiellement désynchronisées
#DevoxxFR
Le défi
7
Dans un système distribué,
définir les conditions suffisantes à respecter
pour garantir la convergence des données
potentiellement désynchronisées
#DevoxxFR
Le défi
8
Dans un système distribué,
définir les conditions suffisantes à respecter
pour garantir la convergence des données
potentiellement désynchronisées
#DevoxxFR
CRDT
9
Conflict-free Replicated Data Types
Définit les conditions suffisantes pour un système
"strong eventually consistent"
Autorise une disponibilité extrême, N-1 nœuds en panne
sur N nœuds au total
#DevoxxFR 10
Types de CRDT
À état: Convergent Replicated Data Types (CvRDT)
Par fonction: Commutative Replicated Data Types (CmRDT)
#DevoxxFR
CvRDT: conditions d’application
11
Tous les réplicas sont connectés (en général)
Échange d’état au moins 1 fois, sur un médium ponctuellement
fiable
L’ensemble des états forme un demi-treillis borné
Tous les changements d’état transitionnent vers un nouvel état en
suivant l’ordre partiel
#DevoxxFR
CvRDT: conditions d’application
12
Tous les réplicas sont connectés (en général)
Échange d’état au moins 1 fois, sur un médium ponctuellement
fiable
L’ensemble des états forme un demi-treillis borné
Tous les changements d’état transitionnent vers un nouvel état en
suivant l’ordre partiel
#DevoxxFR
CvRDT: conditions d’application
13
Tous les réplicas sont connectés (en général)
Échange d’état au moins 1 fois, sur un médium ponctuellement
fiable
L’ensemble des états forme un demi-treillis borné
Tous les changements d’état transitionnent vers un nouvel état en
suivant l’ordre partiel
Ensemble d’éléments partiellement ordonnés
ayant un borne supérieure (merci Wikipedia)
#DevoxxFR
CvRDT: conditions d’application
14
Tous les réplicas sont connectés (en général)
Échange d’état au moins 1 fois, sur un médium ponctuellement
fiable
L’ensemble des états forme un demi-treillis borné
Tous les changements d’état transitionnent vers un nouvel état en
suivant l’ordre partiel
#DevoxxFR
Définition formelle
#DevoxxFR
CvRDT: définition
16
A join semilattice (or just semilattice hereafter) is a partial order ≤v
equipped with a least upper bound (LUB) ⊔v, defined as follows:
Definition 2.4 m = x ⊔v y is a least upper bound of {x, y} under ≤v iff
x ≤v m and
y ≤v m and
there is no m′ ≤v m such that x ≤v m′ and y ≤v m′
It follows from the definition that ⊔v is: commutative: x ⊔v y =v y ⊔v x;
idempotent: x ⊔v x =v x; and associative: (x⊔v y)⊔v z =v x⊔v (y⊔v z).
#DevoxxFR
CvRDT: spécification
17
#DevoxxFR
CvRDT: exemple G-Set
18
Payload
Set S, initial value S := { }
Query(e)
e ∈ S ?
Update
add(e): S := S ∪ {e}
Merge(S’)
S := S ∪ S’
relation d’ordre ≤v = ∈
opérateur ⊔v = ∪
l’union ensembliste ∪ est
commutative, associative et
idempotente
#DevoxxFR
relation d’ordre ≤v = ∈
opérateur ⊔v = ∪
l’union ensembliste ∪ est
commutative, associative et
idempotente
CvRDT: exemple G-Set
19
Payload
Set S, valeur initiale S := { }
Query(e)
e ∈ S ?
Update
add(e): S := S ∪ {e}
Merge(S’)
S := S ∪ S’
Problème: comment
gérer les suppressions
d’éléments dans
le Set ?
#DevoxxFR
CvRDT: 2P-Set
20
Payload
Set A, R initial value A := { } , R := { }
Query(e)
(e ∈ A) ∧ (e ∉ R) ?
Update
add(e): A := A ∪ {e}, remove(e): R := R ∪ {e}
Merge(A’, R’)
A := A ∪ A’, R := R ∪ R’
#DevoxxFR
CvRDT: LWW-Register
21
Payload
(X, timestamp t), (∅, 0)
Query(e)
e = X ?
Update
assign(e): X := e, t := now()
Merge((X’, t’))
If t’ < t then (X, t) else (X’, t’) à take value of max(timestamp)
#DevoxxFR
Quelles applications ?
22
Systèmes "eventually consistent"
Compteurs distribués
Graphes orientés a-cycliques
Shopping Cart
#DevoxxFR
Quelques implémentations
23
Cassandra Last Write Win
Riak
GearPump RealTime Streaming Engine (LightBend)
SoundCloud Roshi
#DevoxxFR
Cassandra LWW
H
A
E
D
B C
G F
coordinateur
INSERT/UPDATE
1
2 3
(X, t1)
(X, t1)
(X, t1)
X
#DevoxxFR
Cassandra LWW
H
A
E
D
B C
G F
1
2 3 (X, t1)(X, t1)
(X, t1)
coordinateur
INSERT/UPDATE
Y
(Y, t2)
#DevoxxFR
Cassandra LWW
H
A
E
D
B C
G F
coordinateur
SELECT
1
2 3
(X, t1)(Y, t2)
(X, t1)
Y
#DevoxxFR
Cassandra LWW
Et si t1 == t2 ? (précision timestamp à la ms) 
Les DELETE sont prioritaires sur les INSERT/UPDATE
Comparer les valeurs par l’ordre de leur type (String, Date …) et
prendre la valeur la plus élevée
#DevoxxFR
Et si t1 == t2 ? (précision timestamp à la ms) 
Les DELETE sont prioritaires sur les INSERT/UPDATE
Comparer les valeurs par l’ordre de leur type (String, Date …) et
prendre la valeur la plus élevée
Cette règle est-elle ?
- commutative
- associative
- idempotente
Cassandra LWW
#DevoxxFR
Cassandra LWW
Associativité
[("toto", t1), ("titi", t1)], ("tata",t1) à [("toto", t1), ("tata",t1)] à ("toto", t1) 
("toto", t1), [("titi", t1), ("tata",t1)] à [("toto", t1), ("titi",t1)] à ("toto", t1)
Commutativité
("toto", t1), ("tata",t1) à ("toto", t1)
("tata", t1), ("toto",t1) à ("toto", t1)
Idempotence
("toto", t1), ("toto",t1) à ("toto", t1)
#DevoxxFR
Q & R
! "
#DevoxxFR
RAMP Transactions
Peter Bailis – 2014
#DevoxxFR
Le défi
32
Dans un système distribué,
définir un algorithme
garantissant des lectures atomiques
sur des opérations multi-partitions
#DevoxxFR
Le défi
33
Dans un système distribué,
définir un algorithme
garantissant des lectures atomiques
sur des opérations multi-partitions
#DevoxxFR
Le défi
34
Dans un système distribué,
définir un algorithme
garantissant des lectures atomiques
sur des opérations multi-partitions
Pas de garantie d’isolation ou
d’écriture atomique
#DevoxxFR
Le défi
35
Dans un système distribué,
définir un algorithme
garantissant des lectures atomiques
sur des opérations multi-partitions
#DevoxxFR
Solutions existantes
36
Lock global
Multi-version concurrency control
Optimistic concurrency control
(Google F1)
#DevoxxFR
Un peu de théorie
37
Serializability
Repeatable Read
Cursor Stability
Read Commited
Read Uncommited
Snapshot Isolation Linearizability
Causal
PRAM (Pipelined RAM)
RYW (Read Your Write)
Eventual Consistency
#DevoxxFR
Un peu de théorie
38
Serializability
Repeatable Read
Cursor Stability
Read Commited
Read Uncommited
Snapshot Isolation Linearizability
Causal
PRAM (Pipelined RAM)
RYW (Read Your Write)
Eventual Consistency
Coordination
synchrone
Sans
Coordination
#DevoxxFR
Un peu de théorie
39
Serializability
Repeatable Read
Cursor Stability
Read Commited
Read Uncommited
Snapshot Isolation Linearizability
Causal
PRAM (Pipelined RAM)
RYW (Read Your Write)
Eventual Consistency
Coordination
synchrone
Sans
Coordination
RAMP Transactions
#DevoxxFR
Read Atomic Multi-Partitions Transaction
Fournit une "visibilité atomique"
☞ Soit toutes les mises à jour d’une transaction sont
visibles, soit aucune ne l’est
RAMP Transaction
40
#DevoxxFR
Visibilité atomique par l’exemple
41
P1 P2
WRITE X = 1
WRITE Y = 1
READ X = 1
READ Y = 1
READ X = ∅
READ Y = ∅
Ou
#DevoxxFR
Visibilité atomique par l’exemple
42
P1 P2
WRITE X = 1
WRITE Y = 1
READ X = 1
READ Y = 1
READ X = ∅
READ Y = ∅
Ou
✘
✘
#DevoxxFR
Idépendance de Partition
☞ les clients n’ont besoin de contacter que les partitions
impliquées dans la transaction
Idépendance de Synchronisation
☞ la transaction d’un client ne peut bloquer les autres
clients.
☞ si le client peut accéder aux partitions de la
transaction, la transaction sera réussie à terme.
Garanties RAMP Transaction
43
#DevoxxFR
2PC avec méta-données + multi-versioning limité
Implémentation
44
#DevoxxFR
RAMP – Fast (Prepare)
45
Writer P1 P2
WRITE <X = 1,t1, {Y}>
Data=[ ], lastcommit=[ ]Data=[ ], lastcommit=[ ]WRITE X = 1
WRITE Y = 1
Prepare
Data=[<X = 1,t1, {Y}>]
WRITE <Y = 1,t1, {X}>
Prepare
Data=[<Y = 1,t1, {X}>]
#DevoxxFR
RAMP – Fast (Prepare)
46
Writer P1 P2
Data=[<Y = 1,t1, {X}>],
lastcommit=[ ]
Data=[<X = 1,t1, {Y}>],
lastcommit=[ ]
Prepared
Prepared
#DevoxxFR
RAMP – Fast (Commit)
47
Writer P1 P2
t1
Commit
lastcommit=[<X,t1>]
Commit
Data=[<Y = 1,t1, {X}>]Data=[<X = 1,t1, {Y}>]
t1 lastcommit=[<Y,t1>]
#DevoxxFR
RAMP – Fast (Commit)
48
Writer P1 P2
Data=[<Y = 1,t1, {X}>],
lastcommit=[<Y,t1>]
Data=[<X = 1,t1, {Y}>],
lastcommit=[<X,t1>]
Committed
Committed
#DevoxxFR
RAMP – Fast (Get)
49
Reader P1 P2
(X, ∅)
Get
lastcommit=[ <X,t1>]
Get
Data=[<Y = 1,t1, {X}>]Data=[<X = 1,t1, {Y}>]
(Y, ∅)
lastcommit=[ <Y,t1>]
#DevoxxFR
RAMP – Fast (Get)
50
Reader P1 P2
<X = 1,t1, {Y}>
lastcommit=[ <X,t1>]
Data=[<Y = 1,t1, {X}>]Data=[<X = 1,t1, {Y}>]
<Y = 1,t1, {X}>
lastcommit=[ <Y,t1>]
Max(t1, t1) = t1
Xt1 = 1
Yt1 = 1 ✔︎
#DevoxxFR
RAMP – Fast (Read & Write)
51
Writer1 P1 P2
WRITE <X = 1,t1, {Y}>
Data=[ ], lastcommit=[ ]Data=[ ], lastcommit=[ ]WRITE X = 1
WRITE Y = 1
Prepare
Data=[<X = 1,t1, {Y}>]
WRITE <Y = 1,t1, {X}>
Prepare
Data=[<Y = 1,t1, {X}>]
#DevoxxFR
RAMP – Fast (Read & Write)
52
Writer1 P1 P2
Data=[<Y = 1,t1, {X}>],
lastcommit=[ ]
Data=[<X = 1,t1, {Y}>],
lastcommit=[ ]
Prepared
Prepared
#DevoxxFR
RAMP – Fast (Read & Write)
53
Writer1 P1 P2
t1
Commit
lastcommit=[<X,t1>]
Data=[<Y = 1,t1, {X}>]Data=[<X = 1,t1, {Y}>]
lastcommit=[]
Reader1
(X, ∅)
Get
Get
(Y, ∅)
<X = 1,t1, {Y}>
<Y = ∅, 0>
lastCommit(X) = t1
lastCommit(Y) = 0
#DevoxxFR
RAMP – Fast (Read & Write)
54
Writer1 P1 P2
lastcommit=[<X,t1>]
Data=[<Y = 1,t1, {X}>]Data=[<X = 1,t1, {Y}>]
lastcommit=[]
Reader1
Max(t1, 0) = t1
Xt1 = 1 ,Yt1 = ?
Get
<Y = 1,t1, {X}>
(Y, t1)
Lookup t1 from Data
Max(t1, 0) = t1
Xt1 = 1,Yt1 = 1 ✔︎
Commit
t1 lastcommit=[<Y,t1>]
#DevoxxFR
RAMP – Fast (Read & Write)
55
Writer1 P1 P2
lastcommit=[<X,t1>]
Data=[<Y = 1,t1, {X}>]Data=[<X = 1,t1, {Y}>]
lastcommit=[]
Reader1
Max(t1, 0) = t1
Xt1 = 1 ,Yt1 = ?
Get
<Y = 1,t1, {X}>
(Y, t1)
Lookup t1 from Data
Max(t1, 0) = t1
Xt1 = 1,Yt1 = 1 ✔︎
Commit
t1 lastcommit=[<Y,t1>]
Si je vois une partition validée,
je peux voir toutes les
partitions liées, validées avec le
même timestamp ! (Tout)
#DevoxxFR
RAMP – Fast (Read & Write)
56
Writer1 P1 P2
lastcommit=[]
Data=[<Y = 1,t1, {X}>]Data=[<X = 1,t1, {Y}>]
lastcommit=[]
Reader1
(X, ∅)
Get
Get
(Y, ∅)
<X = ∅, 0>
<Y = ∅, 0>
lastCommit(X) = O
lastCommit(Y) = 0
#DevoxxFR
RAMP – Fast (Read & Write)
57
Writer1 P1 P2
lastcommit=[]
Data=[<Y = 1,t1, {X}>]Data=[<X = 1,t1, {Y}>]
lastcommit=[]
Reader1
(X, ∅)
Get
Get
(Y, ∅)
<X = ∅, 0>
<Y = ∅, 0>
lastCommit(X) = O
lastCommit(Y) = 0
Si aucune partition n’est encore
validée, toutes les partitions
liées retournent leur ancienne
valeur ! (Rien)
#DevoxxFR
RAMP – Fast (Cas d’erreur)
58
#DevoxxFR
RAMP – Fast (Cas d’erreur)
59
Le client tombe après commit(X, t1)
☞ processus de maintenance pour valider les autres
partitions pas encore validées (force-commit)
Le client tombe après le dernier prepare
☞ processus de maintenance pour nettoyer Data[ ]
après un timeout
#DevoxxFR
RAMP – Fast (Cas d’erreur)
60
Le client fait un rollback(ts) après le dernier prepare
☞ enlever les valeurs écrites à ts dans Data [ ]
#DevoxxFR
RAMP – Fast (Coût Disque)
61
La taille des méta-données est linéairement
proportionnelle au nombre de partitions impliquées dans
la transaction
0
1
2
3
4
5
6
7
8
Taille
méta
données
1 2 3 4 5 6 7 8
Nb de partitions
#DevoxxFR
RAMP – Fast Résumé
62
Algo RTT écriture RTT lecture (au mieux) RTT lecture (au pire) Taille méta-données
RAMP – Fast 2 1 2 O(#partitions)
#DevoxxFR
RAMP – Small (Prepare)
63
Writer P1 P2
WRITE <X = 1,t1>
Data=[ ], lastcommit=[ ]Data=[ ], lastcommit=[ ]WRITE X = 1
WRITE Y = 1
Prepare
Data=[<X = 1,t1]
WRITE <Y = 1,t1>
Prepare
Data=[<Y = 1,t1>]
#DevoxxFR
RAMP – Small (Prepare)
64
Writer P1 P2
Data=[<Y = 1,t1>],
lastcommit=[ ]
Data=[<X = 1,t1>],
lastcommit=[ ]
Prepared
Prepared
🔍 🔍
#DevoxxFR
RAMP – Small (Commit)
65
Writer P1 P2
t1
Commit
lastcommit=[<X,t1>]
Commit
Data=[<Y = 1,t1>]Data=[<X = 1,t1]
t1 lastcommit=[<Y,t1>]
#DevoxxFR
RAMP – Small (Commit)
66
Writer P1 P2
Data=[<Y = 1,t1>],
lastcommit=[<Y,t1>]
Data=[<X = 1,t1>],
lastcommit=[<X,t1>]
Committed
Committed
#DevoxxFR
RAMP – Small (Get round1)
67
Reader P1 P2
(X, ∅)
Get
Get
(Y, ∅)
lastcommit=[ <X,t1>]
Data=[<Y = 1,t1>,
<Y = 2,t2>]
Data=[<X = 1,t1>,
<X = 2, t2>]
lastcommit=[ <Y,t2>]
🔍 🔍
#DevoxxFR
RAMP – Small (Get round1)
68
Reader P1 P2
t1
lastcommit=[ <X,t1>]
Data=[<Y = 1,t1>,
<Y = 2,t2>]
Data=[<X = 1,t1>,
<X = 2, t2>]
t2
lastcommit=[ <Y,t2>]
#DevoxxFR
RAMP – Small (Get round2)
69
Reader P1 P2
(X, {t1,t2})
Get
Get
(Y, {t1,t2})
lastcommit=[ <X,t1>]
Data=[<Y = 1,t1>,
<Y = 2,t2>]
Data=[<X = 1,t1>,
<X = 2, t2>]
lastcommit=[ <Y,t2>]
#DevoxxFR
RAMP – Small (Get round2)
70
Reader P1 P2
lastcommit=[ <X,t1>]
Data=[<Y = 1,t1>,
<Y = 2,t2>]
Data=[<X = 1,t1>,
<X = 2, t2>]
lastcommit=[ <Y,t2>]
tmatch= {t | t∈(t1, t2)∧X∈Data
return X | tX = max(tmatch)
tmatch= {t | t∈(t1, t2)∧Y∈Data
return Y | tY = max(tmatch)
X = 2
Y = 2
#DevoxxFR
RAMP – Small Résumé
71
Algo RTT écriture RTT lecture (au mieux) RTT lecture (au pire) Taille méta-données
RAMP – Fast 2 1 2 O(#partitions)
RAMP – Small 2 2 2 0(1)
#DevoxxFR
RAMP – Small Résumé
72
Algo RTT écriture RTT lecture (au mieux) RTT lecture (au pire) Taille méta-données
RAMP – Fast 2 1 2 O(#partitions)
RAMP – Small 2 2 2 0(1)
Peut-on faire
mieux ?
#DevoxxFR
Bloom Filter Résumé
73
1 0 0 1* 0 0 1 0 1 1
Écriture X Y
Lecture Z ?
Faux positifs
Vrais négatifs
#DevoxxFR
RAMP – Hybrid (Prepare)
74
Writer P1 P2
WRITE <X = 1,t1, BFY>
Data=[ ], lastcommit=[ ]Data=[ ], lastcommit=[ ]WRITE X = 1
WRITE Y = 1
Prepare
Data=[<X = 1,t1, BFY>]
WRITE <Y = 1,t1, BFX>
Prepare
Data=[<Y = 1,t1, BFX>]
#DevoxxFR
RAMP – Hybrid (Get)
75
Reader P1 P2
(X, ∅)
Get
lastcommit=[ <X,t1>]
Get
Data=[<Y = 1,t1, BFX>]Data=[<X = 1,t1, BFY>]
(Y, ∅)
lastcommit=[ <Y,t1>]
#DevoxxFR
RAMP – Hybrid (Get)
76
Reader P1 P2
<X = 1,t1, BFY>
lastcommit=[ <X,t1>]
Data=[<Y = 1,t1, BFX>]Data=[<X = 1,t1, BFY>]
<Y = 1,t1, BFX>
lastcommit=[ <Y,t1>]
Max(t1, t1) = t1
Xt1 = 1
Yt1 = 1 ✔︎
#DevoxxFR
RAMP – Hybrid (Get)
77
Reader P1 P2
<X = 2,t2, BFY>
lastcommit=[ <X,t1>]
Data=[<Y = 1,t1, BFX>,
<Y = 2,t2, BFX>]
Data=[<X = 1,t1, BFY>,
<X = 2,t2, BFY>]
<Y = 1,t2, BFX>
lastcommit=[ <Y,t2>]
t2 > t1 ∧ X ∈ BFX ?
(X, t2)
Get
#DevoxxFR
RAMP – Hybrid (Get)
78
Reader P1 P2
<X = 2,t2, BFY>
lastcommit=[ <X,t1>]
Data=[<Y = 1,t1, BFX>,
<Y = 2,t2, BFX>]
Data=[<X = 1,t1, BFY>,
<X = 2,t2, BFY>]
<Y = 1,t2, BFX>
lastcommit=[ <Y,t2>]
t2 > t1 ∧ X ∈ BFX ?
(X, t2)
Get Faux positif possible!!!
#DevoxxFR
RAMP – Hybrid Résumé
79
Algo RTT écriture RTT lecture (au mieux) RTT lecture (au pire) Taille méta-données
RAMP – Fast 2 1 2 O(#partitions)
RAMP – Small 2 2 2 0(1)
RAMP – Hybrid 2 1 + 𝜀 2 Taille Bloom Filter
𝜀 = taux de faux positif
Proportionnel à la taille
du Bloom Filter
#DevoxxFR
Détails d’implémentation
80
Garbage collector
☞ limiter une transaction à N secondes. Effacer les
versions anciennes après N secondes
Gestion du timestamp
☞ mécanisme pour garantir une croissance monotone
stricte
#DevoxxFR
Détails d’implémentation
81
Réplication
☞ Attendre un ack pour N réplicas d’une partition (N
configurable)
Tâche de maintenance
☞ pour forcer un commit sur les partitions impliquées
dans une transaction ayant au moins 1 partition validée
#DevoxxFR
Quelles applications ?
82
Intégrité et contraintes de clés étrangères
Index secondaires globaux
Vues matérialisées
#DevoxxFR
Quelques implémentations
83
∅
#DevoxxFR
Quelques implémentations
84
∅
#DevoxxFR
Q & R
! "
#DevoxxFR
Merci
86
@doanduyhai
duy_hai.doan@datastax.com
http://academy.datastax.com/

Algorithme distribués pour big data saison 2 @DevoxxFR 2016

  • 1.
  • 2.
    #DevoxxFR Un peu demoi Duy Hai DOAN Evangéliste technique & consultant Apache Cassandra •  talks, meetups, confs •  projet open-source (Achilles, Apache Zeppelin) •  Des questions sur Apache Cassandra/Apache Zeppelin ? ☞ duy_hai.doan@datastax.com ☞ @doanduyhai 2
  • 3.
    #DevoxxFR Datastax •  Fondé enAvril 2010 •  Plus gros pourvoyeur de contributeur à Apache Cassandra™ •  Bureaux européens à Londres, Paris et Berlin •  Datastax Enterprise = OSS Cassandra + fonctionnalités+++ 3
  • 4.
  • 5.
    #DevoxxFR Le défi 5 Dans unsystème distribué, définir les conditions suffisantes à respecter pour garantir la convergence des données potentiellement désynchronisées
  • 6.
    #DevoxxFR Le défi 6 Dans unsystème distribué, définir les conditions suffisantes à respecter pour garantir la convergence des données potentiellement désynchronisées
  • 7.
    #DevoxxFR Le défi 7 Dans unsystème distribué, définir les conditions suffisantes à respecter pour garantir la convergence des données potentiellement désynchronisées
  • 8.
    #DevoxxFR Le défi 8 Dans unsystème distribué, définir les conditions suffisantes à respecter pour garantir la convergence des données potentiellement désynchronisées
  • 9.
    #DevoxxFR CRDT 9 Conflict-free Replicated DataTypes Définit les conditions suffisantes pour un système "strong eventually consistent" Autorise une disponibilité extrême, N-1 nœuds en panne sur N nœuds au total
  • 10.
    #DevoxxFR 10 Types deCRDT À état: Convergent Replicated Data Types (CvRDT) Par fonction: Commutative Replicated Data Types (CmRDT)
  • 11.
    #DevoxxFR CvRDT: conditions d’application 11 Tousles réplicas sont connectés (en général) Échange d’état au moins 1 fois, sur un médium ponctuellement fiable L’ensemble des états forme un demi-treillis borné Tous les changements d’état transitionnent vers un nouvel état en suivant l’ordre partiel
  • 12.
    #DevoxxFR CvRDT: conditions d’application 12 Tousles réplicas sont connectés (en général) Échange d’état au moins 1 fois, sur un médium ponctuellement fiable L’ensemble des états forme un demi-treillis borné Tous les changements d’état transitionnent vers un nouvel état en suivant l’ordre partiel
  • 13.
    #DevoxxFR CvRDT: conditions d’application 13 Tousles réplicas sont connectés (en général) Échange d’état au moins 1 fois, sur un médium ponctuellement fiable L’ensemble des états forme un demi-treillis borné Tous les changements d’état transitionnent vers un nouvel état en suivant l’ordre partiel Ensemble d’éléments partiellement ordonnés ayant un borne supérieure (merci Wikipedia)
  • 14.
    #DevoxxFR CvRDT: conditions d’application 14 Tousles réplicas sont connectés (en général) Échange d’état au moins 1 fois, sur un médium ponctuellement fiable L’ensemble des états forme un demi-treillis borné Tous les changements d’état transitionnent vers un nouvel état en suivant l’ordre partiel
  • 15.
  • 16.
    #DevoxxFR CvRDT: définition 16 A joinsemilattice (or just semilattice hereafter) is a partial order ≤v equipped with a least upper bound (LUB) ⊔v, defined as follows: Definition 2.4 m = x ⊔v y is a least upper bound of {x, y} under ≤v iff x ≤v m and y ≤v m and there is no m′ ≤v m such that x ≤v m′ and y ≤v m′ It follows from the definition that ⊔v is: commutative: x ⊔v y =v y ⊔v x; idempotent: x ⊔v x =v x; and associative: (x⊔v y)⊔v z =v x⊔v (y⊔v z).
  • 17.
  • 18.
    #DevoxxFR CvRDT: exemple G-Set 18 Payload SetS, initial value S := { } Query(e) e ∈ S ? Update add(e): S := S ∪ {e} Merge(S’) S := S ∪ S’ relation d’ordre ≤v = ∈ opérateur ⊔v = ∪ l’union ensembliste ∪ est commutative, associative et idempotente
  • 19.
    #DevoxxFR relation d’ordre ≤v= ∈ opérateur ⊔v = ∪ l’union ensembliste ∪ est commutative, associative et idempotente CvRDT: exemple G-Set 19 Payload Set S, valeur initiale S := { } Query(e) e ∈ S ? Update add(e): S := S ∪ {e} Merge(S’) S := S ∪ S’ Problème: comment gérer les suppressions d’éléments dans le Set ?
  • 20.
    #DevoxxFR CvRDT: 2P-Set 20 Payload Set A,R initial value A := { } , R := { } Query(e) (e ∈ A) ∧ (e ∉ R) ? Update add(e): A := A ∪ {e}, remove(e): R := R ∪ {e} Merge(A’, R’) A := A ∪ A’, R := R ∪ R’
  • 21.
    #DevoxxFR CvRDT: LWW-Register 21 Payload (X, timestampt), (∅, 0) Query(e) e = X ? Update assign(e): X := e, t := now() Merge((X’, t’)) If t’ < t then (X, t) else (X’, t’) à take value of max(timestamp)
  • 22.
    #DevoxxFR Quelles applications ? 22 Systèmes"eventually consistent" Compteurs distribués Graphes orientés a-cycliques Shopping Cart
  • 23.
    #DevoxxFR Quelques implémentations 23 Cassandra LastWrite Win Riak GearPump RealTime Streaming Engine (LightBend) SoundCloud Roshi
  • 24.
    #DevoxxFR Cassandra LWW H A E D B C GF coordinateur INSERT/UPDATE 1 2 3 (X, t1) (X, t1) (X, t1) X
  • 25.
    #DevoxxFR Cassandra LWW H A E D B C GF 1 2 3 (X, t1)(X, t1) (X, t1) coordinateur INSERT/UPDATE Y (Y, t2)
  • 26.
    #DevoxxFR Cassandra LWW H A E D B C GF coordinateur SELECT 1 2 3 (X, t1)(Y, t2) (X, t1) Y
  • 27.
    #DevoxxFR Cassandra LWW Et sit1 == t2 ? (précision timestamp à la ms)  Les DELETE sont prioritaires sur les INSERT/UPDATE Comparer les valeurs par l’ordre de leur type (String, Date …) et prendre la valeur la plus élevée
  • 28.
    #DevoxxFR Et si t1== t2 ? (précision timestamp à la ms)  Les DELETE sont prioritaires sur les INSERT/UPDATE Comparer les valeurs par l’ordre de leur type (String, Date …) et prendre la valeur la plus élevée Cette règle est-elle ? - commutative - associative - idempotente Cassandra LWW
  • 29.
    #DevoxxFR Cassandra LWW Associativité [("toto", t1),("titi", t1)], ("tata",t1) à [("toto", t1), ("tata",t1)] à ("toto", t1)  ("toto", t1), [("titi", t1), ("tata",t1)] à [("toto", t1), ("titi",t1)] à ("toto", t1) Commutativité ("toto", t1), ("tata",t1) à ("toto", t1) ("tata", t1), ("toto",t1) à ("toto", t1) Idempotence ("toto", t1), ("toto",t1) à ("toto", t1)
  • 30.
  • 31.
  • 32.
    #DevoxxFR Le défi 32 Dans unsystème distribué, définir un algorithme garantissant des lectures atomiques sur des opérations multi-partitions
  • 33.
    #DevoxxFR Le défi 33 Dans unsystème distribué, définir un algorithme garantissant des lectures atomiques sur des opérations multi-partitions
  • 34.
    #DevoxxFR Le défi 34 Dans unsystème distribué, définir un algorithme garantissant des lectures atomiques sur des opérations multi-partitions Pas de garantie d’isolation ou d’écriture atomique
  • 35.
    #DevoxxFR Le défi 35 Dans unsystème distribué, définir un algorithme garantissant des lectures atomiques sur des opérations multi-partitions
  • 36.
    #DevoxxFR Solutions existantes 36 Lock global Multi-versionconcurrency control Optimistic concurrency control (Google F1)
  • 37.
    #DevoxxFR Un peu dethéorie 37 Serializability Repeatable Read Cursor Stability Read Commited Read Uncommited Snapshot Isolation Linearizability Causal PRAM (Pipelined RAM) RYW (Read Your Write) Eventual Consistency
  • 38.
    #DevoxxFR Un peu dethéorie 38 Serializability Repeatable Read Cursor Stability Read Commited Read Uncommited Snapshot Isolation Linearizability Causal PRAM (Pipelined RAM) RYW (Read Your Write) Eventual Consistency Coordination synchrone Sans Coordination
  • 39.
    #DevoxxFR Un peu dethéorie 39 Serializability Repeatable Read Cursor Stability Read Commited Read Uncommited Snapshot Isolation Linearizability Causal PRAM (Pipelined RAM) RYW (Read Your Write) Eventual Consistency Coordination synchrone Sans Coordination RAMP Transactions
  • 40.
    #DevoxxFR Read Atomic Multi-PartitionsTransaction Fournit une "visibilité atomique" ☞ Soit toutes les mises à jour d’une transaction sont visibles, soit aucune ne l’est RAMP Transaction 40
  • 41.
    #DevoxxFR Visibilité atomique parl’exemple 41 P1 P2 WRITE X = 1 WRITE Y = 1 READ X = 1 READ Y = 1 READ X = ∅ READ Y = ∅ Ou
  • 42.
    #DevoxxFR Visibilité atomique parl’exemple 42 P1 P2 WRITE X = 1 WRITE Y = 1 READ X = 1 READ Y = 1 READ X = ∅ READ Y = ∅ Ou ✘ ✘
  • 43.
    #DevoxxFR Idépendance de Partition ☞les clients n’ont besoin de contacter que les partitions impliquées dans la transaction Idépendance de Synchronisation ☞ la transaction d’un client ne peut bloquer les autres clients. ☞ si le client peut accéder aux partitions de la transaction, la transaction sera réussie à terme. Garanties RAMP Transaction 43
  • 44.
    #DevoxxFR 2PC avec méta-données+ multi-versioning limité Implémentation 44
  • 45.
    #DevoxxFR RAMP – Fast(Prepare) 45 Writer P1 P2 WRITE <X = 1,t1, {Y}> Data=[ ], lastcommit=[ ]Data=[ ], lastcommit=[ ]WRITE X = 1 WRITE Y = 1 Prepare Data=[<X = 1,t1, {Y}>] WRITE <Y = 1,t1, {X}> Prepare Data=[<Y = 1,t1, {X}>]
  • 46.
    #DevoxxFR RAMP – Fast(Prepare) 46 Writer P1 P2 Data=[<Y = 1,t1, {X}>], lastcommit=[ ] Data=[<X = 1,t1, {Y}>], lastcommit=[ ] Prepared Prepared
  • 47.
    #DevoxxFR RAMP – Fast(Commit) 47 Writer P1 P2 t1 Commit lastcommit=[<X,t1>] Commit Data=[<Y = 1,t1, {X}>]Data=[<X = 1,t1, {Y}>] t1 lastcommit=[<Y,t1>]
  • 48.
    #DevoxxFR RAMP – Fast(Commit) 48 Writer P1 P2 Data=[<Y = 1,t1, {X}>], lastcommit=[<Y,t1>] Data=[<X = 1,t1, {Y}>], lastcommit=[<X,t1>] Committed Committed
  • 49.
    #DevoxxFR RAMP – Fast(Get) 49 Reader P1 P2 (X, ∅) Get lastcommit=[ <X,t1>] Get Data=[<Y = 1,t1, {X}>]Data=[<X = 1,t1, {Y}>] (Y, ∅) lastcommit=[ <Y,t1>]
  • 50.
    #DevoxxFR RAMP – Fast(Get) 50 Reader P1 P2 <X = 1,t1, {Y}> lastcommit=[ <X,t1>] Data=[<Y = 1,t1, {X}>]Data=[<X = 1,t1, {Y}>] <Y = 1,t1, {X}> lastcommit=[ <Y,t1>] Max(t1, t1) = t1 Xt1 = 1 Yt1 = 1 ✔︎
  • 51.
    #DevoxxFR RAMP – Fast(Read & Write) 51 Writer1 P1 P2 WRITE <X = 1,t1, {Y}> Data=[ ], lastcommit=[ ]Data=[ ], lastcommit=[ ]WRITE X = 1 WRITE Y = 1 Prepare Data=[<X = 1,t1, {Y}>] WRITE <Y = 1,t1, {X}> Prepare Data=[<Y = 1,t1, {X}>]
  • 52.
    #DevoxxFR RAMP – Fast(Read & Write) 52 Writer1 P1 P2 Data=[<Y = 1,t1, {X}>], lastcommit=[ ] Data=[<X = 1,t1, {Y}>], lastcommit=[ ] Prepared Prepared
  • 53.
    #DevoxxFR RAMP – Fast(Read & Write) 53 Writer1 P1 P2 t1 Commit lastcommit=[<X,t1>] Data=[<Y = 1,t1, {X}>]Data=[<X = 1,t1, {Y}>] lastcommit=[] Reader1 (X, ∅) Get Get (Y, ∅) <X = 1,t1, {Y}> <Y = ∅, 0> lastCommit(X) = t1 lastCommit(Y) = 0
  • 54.
    #DevoxxFR RAMP – Fast(Read & Write) 54 Writer1 P1 P2 lastcommit=[<X,t1>] Data=[<Y = 1,t1, {X}>]Data=[<X = 1,t1, {Y}>] lastcommit=[] Reader1 Max(t1, 0) = t1 Xt1 = 1 ,Yt1 = ? Get <Y = 1,t1, {X}> (Y, t1) Lookup t1 from Data Max(t1, 0) = t1 Xt1 = 1,Yt1 = 1 ✔︎ Commit t1 lastcommit=[<Y,t1>]
  • 55.
    #DevoxxFR RAMP – Fast(Read & Write) 55 Writer1 P1 P2 lastcommit=[<X,t1>] Data=[<Y = 1,t1, {X}>]Data=[<X = 1,t1, {Y}>] lastcommit=[] Reader1 Max(t1, 0) = t1 Xt1 = 1 ,Yt1 = ? Get <Y = 1,t1, {X}> (Y, t1) Lookup t1 from Data Max(t1, 0) = t1 Xt1 = 1,Yt1 = 1 ✔︎ Commit t1 lastcommit=[<Y,t1>] Si je vois une partition validée, je peux voir toutes les partitions liées, validées avec le même timestamp ! (Tout)
  • 56.
    #DevoxxFR RAMP – Fast(Read & Write) 56 Writer1 P1 P2 lastcommit=[] Data=[<Y = 1,t1, {X}>]Data=[<X = 1,t1, {Y}>] lastcommit=[] Reader1 (X, ∅) Get Get (Y, ∅) <X = ∅, 0> <Y = ∅, 0> lastCommit(X) = O lastCommit(Y) = 0
  • 57.
    #DevoxxFR RAMP – Fast(Read & Write) 57 Writer1 P1 P2 lastcommit=[] Data=[<Y = 1,t1, {X}>]Data=[<X = 1,t1, {Y}>] lastcommit=[] Reader1 (X, ∅) Get Get (Y, ∅) <X = ∅, 0> <Y = ∅, 0> lastCommit(X) = O lastCommit(Y) = 0 Si aucune partition n’est encore validée, toutes les partitions liées retournent leur ancienne valeur ! (Rien)
  • 58.
    #DevoxxFR RAMP – Fast(Cas d’erreur) 58
  • 59.
    #DevoxxFR RAMP – Fast(Cas d’erreur) 59 Le client tombe après commit(X, t1) ☞ processus de maintenance pour valider les autres partitions pas encore validées (force-commit) Le client tombe après le dernier prepare ☞ processus de maintenance pour nettoyer Data[ ] après un timeout
  • 60.
    #DevoxxFR RAMP – Fast(Cas d’erreur) 60 Le client fait un rollback(ts) après le dernier prepare ☞ enlever les valeurs écrites à ts dans Data [ ]
  • 61.
    #DevoxxFR RAMP – Fast(Coût Disque) 61 La taille des méta-données est linéairement proportionnelle au nombre de partitions impliquées dans la transaction 0 1 2 3 4 5 6 7 8 Taille méta données 1 2 3 4 5 6 7 8 Nb de partitions
  • 62.
    #DevoxxFR RAMP – FastRésumé 62 Algo RTT écriture RTT lecture (au mieux) RTT lecture (au pire) Taille méta-données RAMP – Fast 2 1 2 O(#partitions)
  • 63.
    #DevoxxFR RAMP – Small(Prepare) 63 Writer P1 P2 WRITE <X = 1,t1> Data=[ ], lastcommit=[ ]Data=[ ], lastcommit=[ ]WRITE X = 1 WRITE Y = 1 Prepare Data=[<X = 1,t1] WRITE <Y = 1,t1> Prepare Data=[<Y = 1,t1>]
  • 64.
    #DevoxxFR RAMP – Small(Prepare) 64 Writer P1 P2 Data=[<Y = 1,t1>], lastcommit=[ ] Data=[<X = 1,t1>], lastcommit=[ ] Prepared Prepared 🔍 🔍
  • 65.
    #DevoxxFR RAMP – Small(Commit) 65 Writer P1 P2 t1 Commit lastcommit=[<X,t1>] Commit Data=[<Y = 1,t1>]Data=[<X = 1,t1] t1 lastcommit=[<Y,t1>]
  • 66.
    #DevoxxFR RAMP – Small(Commit) 66 Writer P1 P2 Data=[<Y = 1,t1>], lastcommit=[<Y,t1>] Data=[<X = 1,t1>], lastcommit=[<X,t1>] Committed Committed
  • 67.
    #DevoxxFR RAMP – Small(Get round1) 67 Reader P1 P2 (X, ∅) Get Get (Y, ∅) lastcommit=[ <X,t1>] Data=[<Y = 1,t1>, <Y = 2,t2>] Data=[<X = 1,t1>, <X = 2, t2>] lastcommit=[ <Y,t2>] 🔍 🔍
  • 68.
    #DevoxxFR RAMP – Small(Get round1) 68 Reader P1 P2 t1 lastcommit=[ <X,t1>] Data=[<Y = 1,t1>, <Y = 2,t2>] Data=[<X = 1,t1>, <X = 2, t2>] t2 lastcommit=[ <Y,t2>]
  • 69.
    #DevoxxFR RAMP – Small(Get round2) 69 Reader P1 P2 (X, {t1,t2}) Get Get (Y, {t1,t2}) lastcommit=[ <X,t1>] Data=[<Y = 1,t1>, <Y = 2,t2>] Data=[<X = 1,t1>, <X = 2, t2>] lastcommit=[ <Y,t2>]
  • 70.
    #DevoxxFR RAMP – Small(Get round2) 70 Reader P1 P2 lastcommit=[ <X,t1>] Data=[<Y = 1,t1>, <Y = 2,t2>] Data=[<X = 1,t1>, <X = 2, t2>] lastcommit=[ <Y,t2>] tmatch= {t | t∈(t1, t2)∧X∈Data return X | tX = max(tmatch) tmatch= {t | t∈(t1, t2)∧Y∈Data return Y | tY = max(tmatch) X = 2 Y = 2
  • 71.
    #DevoxxFR RAMP – SmallRésumé 71 Algo RTT écriture RTT lecture (au mieux) RTT lecture (au pire) Taille méta-données RAMP – Fast 2 1 2 O(#partitions) RAMP – Small 2 2 2 0(1)
  • 72.
    #DevoxxFR RAMP – SmallRésumé 72 Algo RTT écriture RTT lecture (au mieux) RTT lecture (au pire) Taille méta-données RAMP – Fast 2 1 2 O(#partitions) RAMP – Small 2 2 2 0(1) Peut-on faire mieux ?
  • 73.
    #DevoxxFR Bloom Filter Résumé 73 10 0 1* 0 0 1 0 1 1 Écriture X Y Lecture Z ? Faux positifs Vrais négatifs
  • 74.
    #DevoxxFR RAMP – Hybrid(Prepare) 74 Writer P1 P2 WRITE <X = 1,t1, BFY> Data=[ ], lastcommit=[ ]Data=[ ], lastcommit=[ ]WRITE X = 1 WRITE Y = 1 Prepare Data=[<X = 1,t1, BFY>] WRITE <Y = 1,t1, BFX> Prepare Data=[<Y = 1,t1, BFX>]
  • 75.
    #DevoxxFR RAMP – Hybrid(Get) 75 Reader P1 P2 (X, ∅) Get lastcommit=[ <X,t1>] Get Data=[<Y = 1,t1, BFX>]Data=[<X = 1,t1, BFY>] (Y, ∅) lastcommit=[ <Y,t1>]
  • 76.
    #DevoxxFR RAMP – Hybrid(Get) 76 Reader P1 P2 <X = 1,t1, BFY> lastcommit=[ <X,t1>] Data=[<Y = 1,t1, BFX>]Data=[<X = 1,t1, BFY>] <Y = 1,t1, BFX> lastcommit=[ <Y,t1>] Max(t1, t1) = t1 Xt1 = 1 Yt1 = 1 ✔︎
  • 77.
    #DevoxxFR RAMP – Hybrid(Get) 77 Reader P1 P2 <X = 2,t2, BFY> lastcommit=[ <X,t1>] Data=[<Y = 1,t1, BFX>, <Y = 2,t2, BFX>] Data=[<X = 1,t1, BFY>, <X = 2,t2, BFY>] <Y = 1,t2, BFX> lastcommit=[ <Y,t2>] t2 > t1 ∧ X ∈ BFX ? (X, t2) Get
  • 78.
    #DevoxxFR RAMP – Hybrid(Get) 78 Reader P1 P2 <X = 2,t2, BFY> lastcommit=[ <X,t1>] Data=[<Y = 1,t1, BFX>, <Y = 2,t2, BFX>] Data=[<X = 1,t1, BFY>, <X = 2,t2, BFY>] <Y = 1,t2, BFX> lastcommit=[ <Y,t2>] t2 > t1 ∧ X ∈ BFX ? (X, t2) Get Faux positif possible!!!
  • 79.
    #DevoxxFR RAMP – HybridRésumé 79 Algo RTT écriture RTT lecture (au mieux) RTT lecture (au pire) Taille méta-données RAMP – Fast 2 1 2 O(#partitions) RAMP – Small 2 2 2 0(1) RAMP – Hybrid 2 1 + 𝜀 2 Taille Bloom Filter 𝜀 = taux de faux positif Proportionnel à la taille du Bloom Filter
  • 80.
    #DevoxxFR Détails d’implémentation 80 Garbage collector ☞limiter une transaction à N secondes. Effacer les versions anciennes après N secondes Gestion du timestamp ☞ mécanisme pour garantir une croissance monotone stricte
  • 81.
    #DevoxxFR Détails d’implémentation 81 Réplication ☞ Attendreun ack pour N réplicas d’une partition (N configurable) Tâche de maintenance ☞ pour forcer un commit sur les partitions impliquées dans une transaction ayant au moins 1 partition validée
  • 82.
    #DevoxxFR Quelles applications ? 82 Intégritéet contraintes de clés étrangères Index secondaires globaux Vues matérialisées
  • 83.
  • 84.
  • 85.
  • 86.