Upload
duyhai-doan
View
344
Download
0
Embed Size (px)
Citation preview
#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 ? ☞ [email protected] ☞ @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
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
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: 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
1
2 3 (X, t1)(X, t1)
(X, t1)
coordinateur
INSERT/UPDATEY
(Y, t2)
#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
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
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, j e p e u x v o i r t o u t e s l e s 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)
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 positifsVrais 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