Advertisement
Advertisement

More Related Content

Advertisement

JPA avec Cassandra, grâce à Achilles

  1. https://github.com/doanduyhai/Achilles JPA avec Cassandra ? Mission pas impossible ! 1
  2. 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
  3. https://github.com/doanduyhai/Achilles Achilles Présentation 3
  4. 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
  5. https://github.com/doanduyhai/Achilles Pourquoi Achilles ? • Voulez-vous écrire ça ? 5
  6. https://github.com/doanduyhai/Achilles Pourquoi Achilles ? • ou ça ? 6
  7. 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
  8. 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
  9. 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;
  10. 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>
  11. 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)) ?
  12. 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
  13. 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
  14. 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} … …
  15. 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
  16. 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
  17. 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
  18. 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
  19. 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 ???
  20. 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
  21. 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
  22. 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
  23. 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
  24. https://github.com/doanduyhai/Achilles Démo Comment faire un clone de Twitter avec Achilles 24
  25. 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
  26. 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
  27. 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
  28. 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
  29. 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
  30. 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
  31. 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; }
  32. 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)
  33. 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)
  34. https://github.com/doanduyhai/Achilles Le future Que nous réserve Achilles dans quelques mois ? 34
  35. 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
  36. 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
  37. 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é
  38. 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,{'ddoan@test.com','dhdoan@gmail.com'},['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
  39. https://github.com/doanduyhai/Achilles Achilles est open-source donc … 39 We need you !!! github.com/doanduyhai/Achilles
Advertisement