jpa avec cassandra, grâce à achilles

39
https://github.com/doanduyhai/Achilles JPA avec Cassandra ? Mission pas impossible ! 1

Upload: ippon-technologies

Post on 14-Jun-2015

1.344 views

Category:

Documents


4 download

TRANSCRIPT

Page 1: JPA avec Cassandra, grâce à Achilles

https://github.com/doanduyhai/Achilles

JPA avec Cassandra ? Mission pas impossible !

1

Page 2: JPA avec Cassandra, grâce à Achilles

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

Page 3: JPA avec Cassandra, grâce à Achilles

https://github.com/doanduyhai/Achilles

Achilles

Présentation

3

Page 4: JPA avec Cassandra, grâce à Achilles

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

Page 5: JPA avec Cassandra, grâce à Achilles

https://github.com/doanduyhai/Achilles

Pourquoi Achilles ?

• Voulez-vous écrire ça ?

5

Page 6: JPA avec Cassandra, grâce à Achilles

https://github.com/doanduyhai/Achilles

Pourquoi Achilles ? • ou ça ?

6

Page 7: JPA avec Cassandra, grâce à Achilles

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

Page 8: JPA avec Cassandra, grâce à Achilles

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

Page 9: JPA avec Cassandra, grâce à Achilles

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;

Page 10: JPA avec Cassandra, grâce à Achilles

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>

Page 11: JPA avec Cassandra, grâce à Achilles

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)) ?

Page 12: JPA avec Cassandra, grâce à Achilles

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

Page 13: JPA avec Cassandra, grâce à Achilles

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

Page 14: JPA avec Cassandra, grâce à Achilles

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} … …

Page 15: JPA avec Cassandra, grâce à Achilles

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

Page 16: JPA avec Cassandra, grâce à Achilles

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

Page 17: JPA avec Cassandra, grâce à Achilles

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

Page 18: JPA avec Cassandra, grâce à Achilles

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

Page 19: JPA avec Cassandra, grâce à Achilles

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 ???

Page 20: JPA avec Cassandra, grâce à Achilles

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

Page 21: JPA avec Cassandra, grâce à Achilles

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

Page 22: JPA avec Cassandra, grâce à Achilles

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

Page 23: JPA avec Cassandra, grâce à Achilles

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

Page 24: JPA avec Cassandra, grâce à Achilles

https://github.com/doanduyhai/Achilles

Démo

Comment faire un clone de Twitter avec Achilles

24

Page 25: JPA avec Cassandra, grâce à Achilles

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

Page 26: JPA avec Cassandra, grâce à Achilles

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

Page 27: JPA avec Cassandra, grâce à Achilles

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

Page 28: JPA avec Cassandra, grâce à Achilles

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

Page 29: JPA avec Cassandra, grâce à Achilles

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

Page 30: JPA avec Cassandra, grâce à Achilles

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

Page 31: JPA avec Cassandra, grâce à Achilles

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; }

Page 32: JPA avec Cassandra, grâce à Achilles

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)

Page 33: JPA avec Cassandra, grâce à Achilles

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)

Page 34: JPA avec Cassandra, grâce à Achilles

https://github.com/doanduyhai/Achilles

Le future

Que nous réserve Achilles dans quelques mois ?

34

Page 35: JPA avec Cassandra, grâce à Achilles

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

Page 36: JPA avec Cassandra, grâce à Achilles

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

Page 37: JPA avec Cassandra, grâce à Achilles

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é

Page 38: JPA avec Cassandra, grâce à Achilles

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

Page 39: JPA avec Cassandra, grâce à Achilles

https://github.com/doanduyhai/Achilles

Achilles est open-source donc …

39

We need you !!!

github.com/doanduyhai/Achilles