JPA avec Cassandra, grâce à Achilles
-
Upload
ippon-technologies -
Category
Documents
-
view
1.342 -
download
4
Transcript of JPA avec Cassandra, grâce à Achilles
https://github.com/doanduyhai/Achilles
JPA avec Cassandra ? Mission pas impossible !
1
https://github.com/doanduyhai/Achilles
• Développeur Java freelance full-stack
• Bidouilleur Cassandra, créateur d’Achilles
• En mission chez
• doanduyhai.wordpress.com
• @doanduyhai
2
Duy Hai DOAN
https://github.com/doanduyhai/Achilles
Achilles
Présentation
3
https://github.com/doanduyhai/Achilles
Achilles
• Framework de persistance JPA pour C*
• Support des annotations/opérations JPA
• Extensions spécifiques à C*
• 2 implémentations : Thrift & CQL3*
4
https://github.com/doanduyhai/Achilles
Pourquoi Achilles ?
• Voulez-vous écrire ça ?
5
https://github.com/doanduyhai/Achilles
Pourquoi Achilles ? • ou ça ?
6
https://github.com/doanduyhai/Achilles
Pourquoi Achilles? • Librairies trop bas niveau (Hector, Thrift)
• Virage vers SQL avec CQL3 par Datastax
• Object mapper nécessaire pour lier entités/requêtes
• Concepts JPA: bien connus et maîtrisés
7
https://github.com/doanduyhai/Achilles
Mapping d’entités • Annotations JPA
– @Entity, @Table
– @Id, @EmbededId, @Column, @JoinColumn
– @OneToOne, @OneToMany …
• Spécifiques à Cassandra – @WideRow
– @Consistency
– @Key (composite)
– @Lazy
8
https://github.com/doanduyhai/Achilles 9
Mapping d’entités @Entity public class UserBean @Id private Long id; @Column private String name; @Lazy @Column private List<Long> friendIds; @Column private WideMap<UUID, String> tweets; @ManyToOne @JoinColumn private UserBean referer;
https://github.com/doanduyhai/Achilles
Types spécifiques Cassandra
• WideMap<K,V>: miroir d’une column family
– KeyValue<K,V>: représente une colonne
– KeyValueIterator<K,V>: itérateur
• Counter: compteur distribué
10
puissance alfred batgirl catwoman lois_lane robin
1 6 7 1 8
WideMap<String,Integer>
KeyValue<String,Integer>
https://github.com/doanduyhai/Achilles 11
Entity Manager
• find(Entity.class,pk)
– retourne entité « managed » (proxy)
• getReference(Entity.class,pk)*
– retourne proxy sans chargement initial
– peut servir pour du direct-update
• persist(entity)
– écrase/met à jour toute valeur existante
– persist(User(fn,ln,age)) puis persist(User(fn,ln)) ?
https://github.com/doanduyhai/Achilles
Entity Manager
• merge(entity) – dirty check + flush à C*
– retourne entité « managed » si transient
– entité retournée == entité mergée
• remove(entity) – effacer + cascade delete des types de C*
• refresh(entity) – recharge l’entité
– identique à find()
12
https://github.com/doanduyhai/Achilles
Persistance avec Thrift
• Persistance des champs « simples »
13
10 firstname lastname age
DuyHai DOAN 32
• Persistance des listes
10 0 1 2 3
Java Cassandra Scala Angular JS
https://github.com/doanduyhai/Achilles 14
Persistance avec Thrift
• Persistance des Sets
10 1265231 68754 546546
#Cassandra #Achilles #Tatami
• Persistance des Maps
10 113131 6543213 51313 65465464
{language:Java} {database:Cassandra} … …
https://github.com/doanduyhai/Achilles
Dirty check • Interception seulement sur getter (internal calls ) • Pas d’update atomique des collections/maps • Dirty check des collections/maps coûteux en Thrift
Impl – (read + delete) + write pour mettre à jour (pas de slice
delete) – optimisation possible pour Set & Map
– list.add(2,peter) ??? – faible cardinalité conseillée (10 -20)
15
friends 0 1 2 3 4
bob alice john helen richard
https://github.com/doanduyhai/Achilles
Lazy Loading
• @Lazy, typiquement collections/maps
• Champs chargés à l’appel du getter
• Chargement complet des collections/maps par slice query
• Champs de jointure toujours lazy, par choix de conception
16
https://github.com/doanduyhai/Achilles
Jointures • Besoin de cohérence forte au détriment de la
performance
• Join collections/maps -> – Optimisation avec MultiGet Slice Query au
chargement • 1 lecture pour n clés primaires dans la collection/map
• 1 multiget slice query pour charger les n jointures (n lectures côté C*)
– Batch mutation pour sauvegarder
– Jointure possible sur les WideMap
17
https://github.com/doanduyhai/Achilles
Cascading
• No cascade
• PERSIST – écrase l’entité existante
• MERGE – écrase les colonnes existantes si modifiées
• REFRESH -> rien
• REMOVE -> interdit/non supporté/mal
• ALL -> tout sauf REMOVE
18
https://github.com/doanduyhai/Achilles
WideMap API
19
@Entity
@Table(name=«user»)
class User
@Id
private Long userId;
@Column
private String firstname;
…..
@Column(table=« tweet »)
WideMap<UUID,String> tweets;
10 067e6162-3b6f-… 54947df8-0e9e-… 38400000-8cf0-…
Hello world! #IppEvent avec #Achilles Ceci est un tweet...
10 firstname lastname age
Julien DUBOIS ???
https://github.com/doanduyhai/Achilles
WideMap API • insert(K key, V value, int ttl) • V get(K key) • remove(K key) • List<KeyValue<K, V>> find(K start, K end, int count) • List<KeyValue<K, V>> findBoundsExclusive(K start, K end,
int count) • List<KeyValue<K, V>> findReverse(K start, K end, int count) • List<KeyValue<K,V>> findFirst(int count) • List<KeyValue<K,V>> findLast(int count) • KeyValueIterator<K,V> iterator(int count) • ...
20
https://github.com/doanduyhai/Achilles
WideRow
• Entité avec une primary Key + WideMap
• Représente directement une column family
21
@Entity @WideRow public class TweetLine @Id private Long id; @Column private WideMap<UUID, String> tweets;
10 uuid1 uuid2 uuid3 uuid4 uuid5
Test Hellow World Démo Achilles CQL3 Ipp Event
https://github.com/doanduyhai/Achilles
Counter API
• get()
• incr(), incr(n)
• decr(), decr(n)
• Pas de suppression de compteur, c’est mal
• WideMap<xxx,Counter> possible -> column family counter dédiée
22
@Id private Long userId; @Column Counter tweetsCount;
"User:10" tweetsCount friendsCount followersCount
150 1000 420
"Tweet:067e6162-3b6f" likesCount retweetCount
5000 15000
https://github.com/doanduyhai/Achilles
Usage
23
user.getTweets().insert(uuid, «content »);
List<KeyValue<UUID,String>> first10 = user.getTweets().findFirst(10);
List<String> last10Tweets = user.getTweets().findLastValues(10);
user.setTweets(xxx);
• WideMap
Long tweetCount = user.getTweetCount().get();
user.getTweetCount().incr(3);
user.setTweetCount(xxx);
• Counter
https://github.com/doanduyhai/Achilles
Démo
Comment faire un clone de Twitter avec Achilles
24
https://github.com/doanduyhai/Achilles
Compromis
• La lecture des tweets doit être très très rapide
• L’envoi des tweets doit être relativement rapide
• La suppression des tweets peut être lente
25
https://github.com/doanduyhai/Achilles
Scénario 1
• Modéliser un utilisateur
• Modéliser la liste des amis/suiveurs
• Implémenter un compteur d’amis/suiveurs
• Implémenter la fonctionnalité « A suit B »
• Donner la liste des amis/suiveurs d’un utilisateur
• Donner les détails sur un utilisateur
26
https://github.com/doanduyhai/Achilles
Scénario 2
• Modéliser un « Tweet »
• Implémenter l’envoi de tweet
• Dupliquer le tweet
– dans la liste des tweets propres à l’utilisateur
– dans la timeline de l’utilisateur
– dans la timeline de tous ses suiveurs
27
https://github.com/doanduyhai/Achilles
Scénario 3
• Supprimer un tweet
– de la liste des tweets de l’utilisateur
– de la timeline de l’utilisateur
– de la timeline de tous ses suiveurs
28
https://github.com/doanduyhai/Achilles
Scénario 4
• Implémenter la gestion des hashtags
• Créer une tag-line pour les tags
• Gérer l’effacement du tweet de la tagline lorsque le tweet est effacé
29
https://github.com/doanduyhai/Achilles
Scénario 5
• Donner la possibilité de mettre un tweet en « favori » -> favoriteline
• Détecter les personnes citées dans les tweets (@login) -> mentionline
• Gérer l’effacement de la favoriteline et de la mentionline lorsque le tweet est effacé
30
https://github.com/doanduyhai/Achilles
Clés Composite
• Interface marqueur MultiKey
• Annotation dédiée @Key(order=x)
31
@JoinColumn private WideMap<UserIndex, User> user; public class UserIndex implements MultiKey { @Key(order=1) private String login; @Key(order=2) private Long id; }
https://github.com/doanduyhai/Achilles
Consistency Level • Par annotation
32
@Consistency(read=ONE,write=QUORUM) public class User @Column @Consistency(read=ONE,write=QUORUM) private Counter tweetsCount; @Column @Consistency(read=ONE,write=THREE) private WideMap<UUID,String> tweets; • Au runtime
– persist(tweet,QUORUM) – merge(user,ALL) – tweetWideMap.insert(uuid1, «a tweet», ONE)
https://github.com/doanduyhai/Achilles
TTL
33
• Sur les opérations JPA
– persist(entity, ttl)
– persist(entity, ttl, writeCL)
– merge(entity, ttl)
– merge(entity, ttl, writeCL)
• Sur les WideMap
– user.getTweets().insert(uuid, «content», ttl)
• Pas de TTL sur les counters (pas de tombstones)
https://github.com/doanduyhai/Achilles
Le future
Que nous réserve Achilles dans quelques mois ?
34
https://github.com/doanduyhai/Achilles
Projection • Support complet de CQL3
– @NamedQuery, query data mapper
– Prepared statement (perf)
– Batches « atomiques »
– Row key composite (clustering)
• Interceptors (@PrePersist,@PreRemove…)
• Listeners
• Bean Validation (JSR 303)
• Secondary index
• Callable, Future<>…
35
https://github.com/doanduyhai/Achilles
Retour d’expérience CQL3
• PreparedStatement (select fn,ln,age from User where userId=?)
– Stocké côté serveur (100.000 max) et par Node
– Seules les valeurs sont envoyées à l’exécution
– Valeurs nulles possible -> effacer le champ
– Pas possible de binder CL, TTL, Timestamp, possible dans C* 2.0
– Pas de batch de PS, possible dans C* 2.0
– Pas de IN (?) binding pour faire du MultiGet
36
https://github.com/doanduyhai/Achilles 37
Retour d’expérience CQL3 • List/Set/Map démystifié
– Nouveaux serializers:
• ColumnToCollectionType
• ListType, SetType,MapType
– List: index = timeUUID généré
• insertion rapide avec append/prepend
• ré-écriture + décalage si insertion par index
• read-before-write pour enlever un élément quelconque
– Set: valeur stockée directement dans ColumnName
– Map: columnName contient la clé
https://github.com/doanduyhai/Achilles
Retour d’expérience CQL3 CREATE TABLE users (
user_id int PRIMARY KEY, emails set<text>, top_places list<text>, todo map<int, text>
);
INSERT INTO users(user_id,emails,top_places,todo)
VALUES(1,{'[email protected]', '[email protected]'},['Paris', 'Chatillon’],{1: 'Demo Achilles', 2:'Implement CQL3'});
RowKey: 1
=> (column=, value=, timestamp=1362010585307000)
=> (column=emails:64646f616e40746573742e636f6d, value=, timestamp=1362010585307000)
=> (column=emails:6468646f616e40676d61696c2e636f6d, value=, timestamp=1362010585307000)
=> (column=todo:00000001, value=44656d6f20416368696c6c6573, timestamp=1362010585307000)
=> (column=todo:00000002, value=496d706c656d656e742043514c33, timestamp=1362010585307000)
=> (column=top_places:16ae51c0813c11e289730dd4d81b4c6b, value=5061726973, timestamp=….)
=> (column=top_places:16ae51c1813c11e289730dd4d81b4c6b, value=43686174696c6c6f6e, timestamp=…)
38
https://github.com/doanduyhai/Achilles
Achilles est open-source donc …
39
We need you !!!
github.com/doanduyhai/Achilles