SlideShare une entreprise Scribd logo
1  sur  49
Télécharger pour lire hors ligne
Doing multi-criteria queries on a
Cassandra application
Ippon Technologies © 2015
Qui sommes-nous?
Jérôme Mainaud
○ Architecte Java chez Ippon Technologies
○ DataStax Solution Architect Certifié
Julien Dubois
○ 15 ans d’expérience en Java
○ Directeur de l’innovation chez Ippon Technologies
Ippon Technologies
○ Expertise Java & Big Data: consulting, formation, hébergement
○ 200 personnes
○ Paris, Bordeaux, Nantes, Richmond (USA)
Ippon Technologies © 2015
Sommaire
1. Intro
2. Stack technique
3. Configuration du cluster
4. Application d’exemple
5. Recherche multi-critère
6. Utilisation de CQL3 avec Java 8
7. Limitations
8. Résultats
Ippon Technologies © 2014
Stack technique
Ippon Technologies © 2015
Stack technique
● JHipster
○ Générateur d’applications Spring Boot + AngularJS
○ Supporte JPA, MongoDB et… Cassandra!
● Nous a permis de générer l’application très rapidement
○ Squelette d’application prêt en 5 minutes
○ Ajout de tables avec leur mapping
○ Configuration, build, gestion des logs, etc.
○ Tests Gatling prêts à l’emploi
Plus d’informations sur http://jhipster.github.io/
Ippon Technologies © 2015
Stack technique
● Spring Boot
○ Basé sur Spring
○ Convention over configuration
○ Nombreux “starters” prêts à l’emploi
● Services Web
○ CXF vs Spring MVC REST
● Cassandra
○ DataStax Enterprise
Ippon Technologies © 2015
Paramétrage du Driver
● Configuration Spring Boot
○ Nous avons réalisé notre propre configuration du DataStax Java
Driver
○ Intégration dans la configuration standard de Spring Boot, utilisant
un fichier YAML
● Intégrée dans Spring Boot 1.3
○ Ce code a été proposé à Pivotal, et intégré dans Spring Boot 1.3
● Améliorée par PR
○ JHipster a été amélioré depuis, et propose toujours une
configuration plus complète que celle de Spring Boot
Ippon Technologies © 2015
Développement des Repositories
● DataStax Java driver utilisé dans un Repository Spring
@Repository
public class UserRepository {
@Inject private Session session;
private PreparedStatement findOneByEmailStmt;
@PostConstruct
public void init() {
findOneByEmailStmt = session.prepare(
"SELECT id FROM user_by_email WHERE email = :email");
}
public Optional<User> findOneByEmail(String email) { … }
Ippon Technologies © 2014
Configuration du cluster
Ippon Technologies © 2015
Hardware
● Hébergement chez Ippon Hosting
● 8 noeuds équivalents
○ 16 Go de RAM
○ Deux disques durs SSD de 256 Go en RAID 0
● 6 noeuds pour le cluster Cassandra, 2 noeuds pour l’
application
Ippon Technologies © 2015
DataStax Enterprise
● Utilisation de DataStax Enterprise
● OpsCenter nous a été d’une très grande aide
○ Monitoring
○ Services automatisés
○ Gestion du cluster
Ippon Technologies © 2014
Application exemple
Système de gestion de factures
Ippon Technologies © 2015
Modèle conceptuel
Ippon Technologies © 2015
Modèle physique
Ippon Technologies © 2015
create table invoice (
invoice_id timeuuid,
user_id uuid static,
firstname text static,
lastname text static,
invoice_date timestamp static,
payment_date timestamp static,
total_amount decimal static,
delivery_address text static,
delivery_city text static,
delivery_zipcode text static,
item_id timeuuid,
item_label text,
item_price decimal,
item_qty int,
item_total decimal,
primary key (invoice_id, item_id)
);
Table
Ippon Technologies © 2014
Recherche multi-critère
Ippon Technologies © 2015
Recherche multi-critères
Critères obligatoires
○ User (implicite)
○ Date de la facture (plage de dates)
Critères supplémentaires
○ Nom du client
○ Prénom du client
○ Ville
○ Code postal
Ippon Technologies © 2015
Utiliser Solr ?
Ippon Technologies © 2015
Utiliser Solr ?
● Intégré dans DataStax Enterprise
● Mise à jour atomique et automatique
● Recherche documentaire
Ippon Technologies © 2015
Utiliser Solr ?
On cherche sur des colonnes statiques
Solr ne les gère pas
On cherche des partitions
Solr retourne des lignes
Ippon Technologies © 2015
Utiliser Solr ?
On cherche sur des colonnes statiques
Solr ne les gère pas
On cherche des partitions
Solr retourne des lignes
Ippon Technologies © 2015
Index secondaires ?
● Ne répondent qu’aux cas de recherche sur un seul champ
● Délicats à utiliser avec de bonnes performances
Ippon Technologies © 2015
Tables d’index
Utilisation de tables d’index
○ Clé de partition : Les critères primaire et un critère secondaire
■ user_id
■ date de facturation (tronqué à la date)
■ le critère secondaire
○ Clustering columns : l’identifiant de la facture
Ippon Technologies © 2015
Recherche
Q1
Q2
A D J M
A C J L M
A J M
Fusion applicative
en mémoire
Recherches en parallèle
Ippon Technologies © 2015
Recherche
Une page de résultat (id)
8f5b69ee-0ad0-11e5-a6c0-1697f925ec7b
8f5b6d4a-0ad0-11e5-a6c0-1697f925ec7b
8f5b6e9e-0ad0-11e5-a6c0-1697f925ec7b
b3db1a30-0ad0-11e5-a6c0-1697f925ec7b
b3db1c88-0ad0-11e5-a6c0-1697f925ec7b
b3db202a-0ad0-11e5-a6c0-1697f925ec7b
b3db219c-0ad0-11e5-a6c0-1697f925ec7b
cac5be94-0ad0-11e5-a6c0-1697f925ec7b
cac5c006-0ad0-11e5-a6c0-1697f925ec7b
cac5c150-0ad0-11e5-a6c0-1697f925ec7b
N recherches unitaires en parallèle
Ippon Technologies © 2015
Recherche
Recherche sur une plage de dates
○ boucle sur les jours en s’arrêtant
dès qu’on a une page
de résultat
Ippon Technologies © 2015
Recherche
Nombre de requêtes
○ Pour chaque jour dans la plage de dates
■ 1 requête par critère secondaire (partition by query)
○ 1 requête par élément trouvé (partition by query)
Complexité de la recherche
○ partitions by query
Exemple: 3 critères, 3 jours, 100 par pages
○ nombre de requêtes ≤ 3 × 3 + 100 = 109
Ippon Technologies © 2014
JAVA
Ippon Technologies © 2015
Index — instances
@Repository
public class InvoiceByLastNameRepository extends IndexRepository<String> {
public InvoiceByLastNameRepository() {
super("invoice_by_lastname", "lastname",
Invoice::getLastName, Criteria::getLastName);
}
}
@Repository
public class InvoiceByFirstNameRepository extends IndexRepository<String> {
public InvoiceByFirstNameRepository() {
super("invoice_by_firstname", "firstname",
Invoice::getFirstName, Criteria::getFirstName);
}
}
Ippon Technologies © 2015
Index — classe parente
public class IndexRepository<T> {
@Inject
private Session session;
private final String tableName;
private final String valueName;
private final Function<Invoice, T> valueGetter;
private final Function<Criteria, T> criteriumGetter;
private PreparedStatement insertStmt;
private PreparedStatement findStmt;
private PreparedStatement findWithOffsetStmt;
@PostConstruct
public void init() { /* initialise les PreparedStatements */ }
Ippon Technologies © 2015
Index — Écriture
@Override
public void insert(Invoice invoice) {
T value = valueGetter.apply(invoice);
if (value != null) {
session.execute(
insertStmt.bind(
invoice.getUserId(),
Dates.toDate(invoice.getInvoiceDay()),
value,
invoice.getId()));
}
}
Ippon Technologies © 2015
Index — Écriture
insertStmt = session.prepare(
QueryBuilder.insertInto(tableName)
.value("user_id", bindMarker())
.value("invoice_day", bindMarker())
.value(valueName, bindMarker())
.value("invoice_id", bindMarker())
);
public static Date toDate(LocalDate date) {
return date == null ? null :
Date.from(date.atStartOfDay().atZone(ZoneOffset.systemDefault()).toInstant());
}
Ippon Technologies © 2015
Index — Recherche
@Override
public CompletableFuture<Iterator<UUID>> find(Criteria criteria, LocalDate day, UUID offset) {
T criterium = criteriumGetter.apply(criteria);
if (criterium == null) {
return CompletableFuture.completedFuture(null);
}
BoundStatement stmt;
if (invoiceIdOffset == null) {
stmt = findStmt.bind(criteria.getUserId(), Dates.toDate(day), criterium);
} else {
stmt = findWithOffsetStmt.bind(criteria.getUserId(), Dates.toDate(day), criterium, offset);
}
return Jdk8.completableFuture(session.executeAsync(stmt))
.thenApply(rs -> Iterators.transform(rs.iterator(), row -> row.getUUID(0)));
}
Ippon Technologies © 2015
Index — Recherche
findWithOffsetStmt = session.prepare(
QueryBuilder.select()
.column("invoice_id")
.from(tableName)
.where(eq("user_id", bindMarker()))
.and(eq("invoice_day", bindMarker()))
.and(eq(valueName, bindMarker()))
.and(lte("invoice_id", bindMarker()))
);
Ippon Technologies © 2015
Index — Recherche (Guava to Java 8)
public static <T> CompletableFuture<T> completableFuture(ListenableFuture<T> guavaFuture) {
CompletableFuture<T> future = new CompletableFuture<>();
Futures.addCallback(guavaFuture, new FutureCallback<T>() {
@Override
public void onSuccess(V result) {
future.complete(result);
}
@Override
public void onFailure(Throwable t) {
future.completeExceptionally(t);
}
});
return future;
}
Ippon Technologies © 2014
JAVA
Service de recherche
Ippon Technologies © 2015
Service — Class
@Service
public class InvoiceSearchService {
@Inject
private InvoiceRepository invoiceRepository;
@Inject
private InvoiceByDayRepository byDayRepository;
@Inject
private InvoiceByLastNameRepository byLastNameRepository;
@Inject
private InvoiceByFirstNameRepository byLastNameRepository;
@Inject
private InvoiceByCityRepository byCityRepository;
@Inject
private InvoiceByZipCodeRepository byZipCodeRepository;
Ippon Technologies © 2015
Service — recherche
public ResultPage findByCriteria(Criteria criteria) {
return byDateInteval(criteria, (crit, day, offset) -> {
CompletableFuture<Iterator<UUID>> futureUuidIt;
if (crit.hasIndexedCriteria()) {
/*
* ... Recherche multi-critère à voir dans la prochaine diapo ...
*/
} else {
futureUuidIt = byDayRepository.find(crit.getUserId(), day, offset);
}
return futureUuidIt;
});
}
Ippon Technologies © 2015
Service — recherche
CompletableFuture<Iterator<UUID>>[] futures = Stream.<IndexRepository> of(
byLastNameRepository, byFirstNameRepository,
byCityRepository, byZipCodeRepository)
.map(repo -> repo.find(crit, day, offset))
.toArray(CompletableFuture[]::new);
futureUuidIt = CompletableFuture.allOf(futures).thenApply(v ->
Iterators.intersection(TimeUUIDComparator.desc,
Stream.of(futures)
.map(CompletableFuture::join)
.filter(Objects::nonNull)
.collect(Collectors.toList())));
Ippon Technologies © 2015
Service — Comparaison des UUIDs
/**
* Comparateur de TimeUUID équivalent à celui de Cassandra:
* @see org.apache.cassandra.db.marshal.TimeUUIDType#compare()
*/
public enum TimeUUIDComparator implements Comparator<UUID> {
desc {
@Override
public int compare(UUID o1, UUID o2) {
long delta = o2.timestamp() - o1.timestamp();
if (delta != 0)
return Ints.saturatedCast(delta);
return o2.compareTo(o1);
}
};
}
Ippon Technologies © 2015
Service — Boucle sur les jours
@FunctionalInterface
private static interface DayQuery {
CompletableFuture<Iterator<UUID>> find(Criteria criteria, LocalDate day, UUID invoiceIdOffset);
}
private ResultPage byDateInteval(Criteria criteria, DayQuery dayQuery) {
int limit = criteria.getLimit();
List<Invoice> resultList = new ArrayList<>(limit);
LocalDate dayOffset = criteria.getDayOffset();
UUID invoiceIdOffset = criteria.getInvoiceIdOffset();
/* ... Boucle sur les jours ; à voir dans la prochaine diapo ... */
return new ResultPage(resultList);
}
Ippon Technologies © 2015
Service — Boucle sur les jours
LocalDate day = criteria.getLastDay();
do {
Iterator<UUID> uuidIt = dayQuery.find(criteria, day, invoiceIdOffset).join();
limit -= loadInvoices(resultList, uuidIt, criteria, limit);
if (uuidIt.hasNext()) {
return new ResultPage(resultList, day, uuidIt.next());
}
day = day.minusDays(1);
invoiceIdOffset = null;
} while (!day.isBefore(criteria.getFirstDay()));
Ippon Technologies © 2015
Service — chargement des factures
private int loadInvoices(List<Invoice> resultList, Iterator<UUID> uuidIt,
Criteria criteria, int limit) {
List<CompletableFuture<Invoice>> futureList = new ArrayList<>(limit);
for (int i = 0; i < limit && uuidIt.hasNext(); ++i) {
futureList.add(invoiceRepository.findOne(uuidIt.next()));
}
futureList.stream()
.map(CompletableFuture::join)
.forEach(resultList::add);
return futureList.size();
}
Ippon Technologies © 2014
Limitations
Ippon Technologies © 2015
Limitations
La recherche ne fonctionne que sur un texte précis
○ Pas de recherche “plein texte”
○ Comme dans une base de données classique
La pagination ne donne pas le nombre total de pages de
résultats
Ce mécanisme ne peut fonctionner que s’il existe des critères
obligatoires fortement discriminants (ici: user_id et invoice_day)
Ippon Technologies © 2014
Résultats
Ippon Technologies © 2015
Résultats métier
● Gestion d’un an de données, sans limite
○ Nous comptons monter à 3 ans
○ Ancien système: limité à 3 mois
● Obtention des résultats en “temps réel”
○ Les données sont immédiatement disponibles
○ Ancien système: 24h de retard
● Coûts nettement plus bas
Ippon Technologies © 2015
Résultats techniques
● Les tests Gatling ont montré que nous pouvions tenir
5000 utilisateurs concurrents
○ Sur des requêtes complexes, avec multiples critères et pagination
● Nous avons aussi démontré que le cluster était scalable
linéairement
○ En termes de volumétrie: ajout de nouveaux disques (passage en
JBOD)
○ En termes de performance: ajout de nouveaux noeuds à chaud
Ippon Technologies © 2014
Merci à tous!

Contenu connexe

Similaire à Ippon: Doing multi-criteria queries on a Cassandra application (Français)

From Idea to the Cloud, a JHipster Story
From Idea to the Cloud, a JHipster StoryFrom Idea to the Cloud, a JHipster Story
From Idea to the Cloud, a JHipster StorySteve Houël
 
20171122 01 - REX : Intégration et déploiement continu chez Engie
20171122 01 - REX : Intégration et déploiement continu chez Engie20171122 01 - REX : Intégration et déploiement continu chez Engie
20171122 01 - REX : Intégration et déploiement continu chez EngieLeClubQualiteLogicielle
 
PyConFR - testons en python
PyConFR - testons en pythonPyConFR - testons en python
PyConFR - testons en pythongburet
 
Iot tunisia forum 2017 le duo gagnant io t et big data un véritable levier de...
Iot tunisia forum 2017 le duo gagnant io t et big data un véritable levier de...Iot tunisia forum 2017 le duo gagnant io t et big data un véritable levier de...
Iot tunisia forum 2017 le duo gagnant io t et big data un véritable levier de...IoT Tunisia
 
La Duck Conf - Continuous Security : Secure a DevOps World!
La Duck Conf - Continuous Security : Secure a DevOps World!La Duck Conf - Continuous Security : Secure a DevOps World!
La Duck Conf - Continuous Security : Secure a DevOps World!OCTO Technology
 
Cas d'étude - Zabbix Toulouse #1 - ZUG
Cas d'étude - Zabbix Toulouse #1 - ZUGCas d'étude - Zabbix Toulouse #1 - ZUG
Cas d'étude - Zabbix Toulouse #1 - ZUGZabbix User Group
 
GDG Rennes - Bootcamp Initiation Android - Théorie
GDG Rennes - Bootcamp Initiation Android -  ThéorieGDG Rennes - Bootcamp Initiation Android -  Théorie
GDG Rennes - Bootcamp Initiation Android - ThéorieHoracio Gonzalez
 
Présentation soutenance
Présentation soutenancePrésentation soutenance
Présentation soutenanceshurongliu
 
Ms Experiences 16 - Analyze and Improve your workflows
Ms Experiences 16 - Analyze and Improve your workflowsMs Experiences 16 - Analyze and Improve your workflows
Ms Experiences 16 - Analyze and Improve your workflowsAlexandre Joly
 
Monitoring de conteneurs en production - Jonathan Raffre & Jean-Pascal Thiery
Monitoring de conteneurs en production - Jonathan Raffre & Jean-Pascal ThieryMonitoring de conteneurs en production - Jonathan Raffre & Jean-Pascal Thiery
Monitoring de conteneurs en production - Jonathan Raffre & Jean-Pascal ThieryParis Container Day
 
Cours Devops Sparks.pptx.pdf
Cours Devops Sparks.pptx.pdfCours Devops Sparks.pptx.pdf
Cours Devops Sparks.pptx.pdfboulonvert
 
Université de la performance - Devoxx France
Université de la performance - Devoxx FranceUniversité de la performance - Devoxx France
Université de la performance - Devoxx FranceMarc Bojoly
 
Automatisez, visualisez et améliorez vos processus d’entreprise avec Nintex
Automatisez, visualisez et améliorez vos processus d’entreprise avec Nintex Automatisez, visualisez et améliorez vos processus d’entreprise avec Nintex
Automatisez, visualisez et améliorez vos processus d’entreprise avec Nintex Microsoft Technet France
 
Evaluer et justifier le crédit d'impôt recherche
Evaluer et justifier le crédit d'impôt rechercheEvaluer et justifier le crédit d'impôt recherche
Evaluer et justifier le crédit d'impôt rechercheNetPME
 
Dw531 g formation-ibm-puredata-system-for-analytics-nps-nouveautes
Dw531 g formation-ibm-puredata-system-for-analytics-nps-nouveautesDw531 g formation-ibm-puredata-system-for-analytics-nps-nouveautes
Dw531 g formation-ibm-puredata-system-for-analytics-nps-nouveautesCERTyou Formation
 
Think tank présentation
Think tank   présentationThink tank   présentation
Think tank présentationJacky Galicher
 
Bonita 7.10 - Nathalie Cotté - Bonitaday Paris 2019
Bonita 7.10 - Nathalie Cotté - Bonitaday Paris 2019Bonita 7.10 - Nathalie Cotté - Bonitaday Paris 2019
Bonita 7.10 - Nathalie Cotté - Bonitaday Paris 2019Bonitasoft
 
Web perf Silicon Comte
Web perf Silicon ComteWeb perf Silicon Comte
Web perf Silicon ComteSilicon Comté
 

Similaire à Ippon: Doing multi-criteria queries on a Cassandra application (Français) (20)

From Idea to the Cloud, a JHipster Story
From Idea to the Cloud, a JHipster StoryFrom Idea to the Cloud, a JHipster Story
From Idea to the Cloud, a JHipster Story
 
20171122 01 - REX : Intégration et déploiement continu chez Engie
20171122 01 - REX : Intégration et déploiement continu chez Engie20171122 01 - REX : Intégration et déploiement continu chez Engie
20171122 01 - REX : Intégration et déploiement continu chez Engie
 
PyConFR - testons en python
PyConFR - testons en pythonPyConFR - testons en python
PyConFR - testons en python
 
Iot tunisia forum 2017 le duo gagnant io t et big data un véritable levier de...
Iot tunisia forum 2017 le duo gagnant io t et big data un véritable levier de...Iot tunisia forum 2017 le duo gagnant io t et big data un véritable levier de...
Iot tunisia forum 2017 le duo gagnant io t et big data un véritable levier de...
 
La Duck Conf - Continuous Security : Secure a DevOps World!
La Duck Conf - Continuous Security : Secure a DevOps World!La Duck Conf - Continuous Security : Secure a DevOps World!
La Duck Conf - Continuous Security : Secure a DevOps World!
 
Perf university
Perf universityPerf university
Perf university
 
Cas d'étude - Zabbix Toulouse #1 - ZUG
Cas d'étude - Zabbix Toulouse #1 - ZUGCas d'étude - Zabbix Toulouse #1 - ZUG
Cas d'étude - Zabbix Toulouse #1 - ZUG
 
GDG Rennes - Bootcamp Initiation Android - Théorie
GDG Rennes - Bootcamp Initiation Android -  ThéorieGDG Rennes - Bootcamp Initiation Android -  Théorie
GDG Rennes - Bootcamp Initiation Android - Théorie
 
Présentation soutenance
Présentation soutenancePrésentation soutenance
Présentation soutenance
 
Ms Experiences 16 - Analyze and Improve your workflows
Ms Experiences 16 - Analyze and Improve your workflowsMs Experiences 16 - Analyze and Improve your workflows
Ms Experiences 16 - Analyze and Improve your workflows
 
Monitoring de conteneurs en production - Jonathan Raffre & Jean-Pascal Thiery
Monitoring de conteneurs en production - Jonathan Raffre & Jean-Pascal ThieryMonitoring de conteneurs en production - Jonathan Raffre & Jean-Pascal Thiery
Monitoring de conteneurs en production - Jonathan Raffre & Jean-Pascal Thiery
 
Cours Devops Sparks.pptx.pdf
Cours Devops Sparks.pptx.pdfCours Devops Sparks.pptx.pdf
Cours Devops Sparks.pptx.pdf
 
Université de la performance - Devoxx France
Université de la performance - Devoxx FranceUniversité de la performance - Devoxx France
Université de la performance - Devoxx France
 
Automatisez, visualisez et améliorez vos processus d’entreprise avec Nintex
Automatisez, visualisez et améliorez vos processus d’entreprise avec Nintex Automatisez, visualisez et améliorez vos processus d’entreprise avec Nintex
Automatisez, visualisez et améliorez vos processus d’entreprise avec Nintex
 
Evaluer et justifier le crédit d'impôt recherche
Evaluer et justifier le crédit d'impôt rechercheEvaluer et justifier le crédit d'impôt recherche
Evaluer et justifier le crédit d'impôt recherche
 
La parallélisation au service de l'optimisation
La parallélisation au service de l'optimisationLa parallélisation au service de l'optimisation
La parallélisation au service de l'optimisation
 
Dw531 g formation-ibm-puredata-system-for-analytics-nps-nouveautes
Dw531 g formation-ibm-puredata-system-for-analytics-nps-nouveautesDw531 g formation-ibm-puredata-system-for-analytics-nps-nouveautes
Dw531 g formation-ibm-puredata-system-for-analytics-nps-nouveautes
 
Think tank présentation
Think tank   présentationThink tank   présentation
Think tank présentation
 
Bonita 7.10 - Nathalie Cotté - Bonitaday Paris 2019
Bonita 7.10 - Nathalie Cotté - Bonitaday Paris 2019Bonita 7.10 - Nathalie Cotté - Bonitaday Paris 2019
Bonita 7.10 - Nathalie Cotté - Bonitaday Paris 2019
 
Web perf Silicon Comte
Web perf Silicon ComteWeb perf Silicon Comte
Web perf Silicon Comte
 

Plus de DataStax Academy

Forrester CXNYC 2017 - Delivering great real-time cx is a true craft
Forrester CXNYC 2017 - Delivering great real-time cx is a true craftForrester CXNYC 2017 - Delivering great real-time cx is a true craft
Forrester CXNYC 2017 - Delivering great real-time cx is a true craftDataStax Academy
 
Introduction to DataStax Enterprise Graph Database
Introduction to DataStax Enterprise Graph DatabaseIntroduction to DataStax Enterprise Graph Database
Introduction to DataStax Enterprise Graph DatabaseDataStax Academy
 
Introduction to DataStax Enterprise Advanced Replication with Apache Cassandra
Introduction to DataStax Enterprise Advanced Replication with Apache CassandraIntroduction to DataStax Enterprise Advanced Replication with Apache Cassandra
Introduction to DataStax Enterprise Advanced Replication with Apache CassandraDataStax Academy
 
Cassandra on Docker @ Walmart Labs
Cassandra on Docker @ Walmart LabsCassandra on Docker @ Walmart Labs
Cassandra on Docker @ Walmart LabsDataStax Academy
 
Cassandra 3.0 Data Modeling
Cassandra 3.0 Data ModelingCassandra 3.0 Data Modeling
Cassandra 3.0 Data ModelingDataStax Academy
 
Cassandra Adoption on Cisco UCS & Open stack
Cassandra Adoption on Cisco UCS & Open stackCassandra Adoption on Cisco UCS & Open stack
Cassandra Adoption on Cisco UCS & Open stackDataStax Academy
 
Data Modeling for Apache Cassandra
Data Modeling for Apache CassandraData Modeling for Apache Cassandra
Data Modeling for Apache CassandraDataStax Academy
 
Production Ready Cassandra
Production Ready CassandraProduction Ready Cassandra
Production Ready CassandraDataStax Academy
 
Cassandra @ Netflix: Monitoring C* at Scale, Gossip and Tickler & Python
Cassandra @ Netflix: Monitoring C* at Scale, Gossip and Tickler & PythonCassandra @ Netflix: Monitoring C* at Scale, Gossip and Tickler & Python
Cassandra @ Netflix: Monitoring C* at Scale, Gossip and Tickler & PythonDataStax Academy
 
Cassandra @ Sony: The good, the bad, and the ugly part 1
Cassandra @ Sony: The good, the bad, and the ugly part 1Cassandra @ Sony: The good, the bad, and the ugly part 1
Cassandra @ Sony: The good, the bad, and the ugly part 1DataStax Academy
 
Cassandra @ Sony: The good, the bad, and the ugly part 2
Cassandra @ Sony: The good, the bad, and the ugly part 2Cassandra @ Sony: The good, the bad, and the ugly part 2
Cassandra @ Sony: The good, the bad, and the ugly part 2DataStax Academy
 
Standing Up Your First Cluster
Standing Up Your First ClusterStanding Up Your First Cluster
Standing Up Your First ClusterDataStax Academy
 
Real Time Analytics with Dse
Real Time Analytics with DseReal Time Analytics with Dse
Real Time Analytics with DseDataStax Academy
 
Introduction to Data Modeling with Apache Cassandra
Introduction to Data Modeling with Apache CassandraIntroduction to Data Modeling with Apache Cassandra
Introduction to Data Modeling with Apache CassandraDataStax Academy
 
Enabling Search in your Cassandra Application with DataStax Enterprise
Enabling Search in your Cassandra Application with DataStax EnterpriseEnabling Search in your Cassandra Application with DataStax Enterprise
Enabling Search in your Cassandra Application with DataStax EnterpriseDataStax Academy
 
Advanced Data Modeling with Apache Cassandra
Advanced Data Modeling with Apache CassandraAdvanced Data Modeling with Apache Cassandra
Advanced Data Modeling with Apache CassandraDataStax Academy
 

Plus de DataStax Academy (20)

Forrester CXNYC 2017 - Delivering great real-time cx is a true craft
Forrester CXNYC 2017 - Delivering great real-time cx is a true craftForrester CXNYC 2017 - Delivering great real-time cx is a true craft
Forrester CXNYC 2017 - Delivering great real-time cx is a true craft
 
Introduction to DataStax Enterprise Graph Database
Introduction to DataStax Enterprise Graph DatabaseIntroduction to DataStax Enterprise Graph Database
Introduction to DataStax Enterprise Graph Database
 
Introduction to DataStax Enterprise Advanced Replication with Apache Cassandra
Introduction to DataStax Enterprise Advanced Replication with Apache CassandraIntroduction to DataStax Enterprise Advanced Replication with Apache Cassandra
Introduction to DataStax Enterprise Advanced Replication with Apache Cassandra
 
Cassandra on Docker @ Walmart Labs
Cassandra on Docker @ Walmart LabsCassandra on Docker @ Walmart Labs
Cassandra on Docker @ Walmart Labs
 
Cassandra 3.0 Data Modeling
Cassandra 3.0 Data ModelingCassandra 3.0 Data Modeling
Cassandra 3.0 Data Modeling
 
Cassandra Adoption on Cisco UCS & Open stack
Cassandra Adoption on Cisco UCS & Open stackCassandra Adoption on Cisco UCS & Open stack
Cassandra Adoption on Cisco UCS & Open stack
 
Data Modeling for Apache Cassandra
Data Modeling for Apache CassandraData Modeling for Apache Cassandra
Data Modeling for Apache Cassandra
 
Coursera Cassandra Driver
Coursera Cassandra DriverCoursera Cassandra Driver
Coursera Cassandra Driver
 
Production Ready Cassandra
Production Ready CassandraProduction Ready Cassandra
Production Ready Cassandra
 
Cassandra @ Netflix: Monitoring C* at Scale, Gossip and Tickler & Python
Cassandra @ Netflix: Monitoring C* at Scale, Gossip and Tickler & PythonCassandra @ Netflix: Monitoring C* at Scale, Gossip and Tickler & Python
Cassandra @ Netflix: Monitoring C* at Scale, Gossip and Tickler & Python
 
Cassandra @ Sony: The good, the bad, and the ugly part 1
Cassandra @ Sony: The good, the bad, and the ugly part 1Cassandra @ Sony: The good, the bad, and the ugly part 1
Cassandra @ Sony: The good, the bad, and the ugly part 1
 
Cassandra @ Sony: The good, the bad, and the ugly part 2
Cassandra @ Sony: The good, the bad, and the ugly part 2Cassandra @ Sony: The good, the bad, and the ugly part 2
Cassandra @ Sony: The good, the bad, and the ugly part 2
 
Standing Up Your First Cluster
Standing Up Your First ClusterStanding Up Your First Cluster
Standing Up Your First Cluster
 
Real Time Analytics with Dse
Real Time Analytics with DseReal Time Analytics with Dse
Real Time Analytics with Dse
 
Introduction to Data Modeling with Apache Cassandra
Introduction to Data Modeling with Apache CassandraIntroduction to Data Modeling with Apache Cassandra
Introduction to Data Modeling with Apache Cassandra
 
Cassandra Core Concepts
Cassandra Core ConceptsCassandra Core Concepts
Cassandra Core Concepts
 
Enabling Search in your Cassandra Application with DataStax Enterprise
Enabling Search in your Cassandra Application with DataStax EnterpriseEnabling Search in your Cassandra Application with DataStax Enterprise
Enabling Search in your Cassandra Application with DataStax Enterprise
 
Bad Habits Die Hard
Bad Habits Die Hard Bad Habits Die Hard
Bad Habits Die Hard
 
Advanced Data Modeling with Apache Cassandra
Advanced Data Modeling with Apache CassandraAdvanced Data Modeling with Apache Cassandra
Advanced Data Modeling with Apache Cassandra
 
Advanced Cassandra
Advanced CassandraAdvanced Cassandra
Advanced Cassandra
 

Ippon: Doing multi-criteria queries on a Cassandra application (Français)

  • 1. Doing multi-criteria queries on a Cassandra application
  • 2. Ippon Technologies © 2015 Qui sommes-nous? Jérôme Mainaud ○ Architecte Java chez Ippon Technologies ○ DataStax Solution Architect Certifié Julien Dubois ○ 15 ans d’expérience en Java ○ Directeur de l’innovation chez Ippon Technologies Ippon Technologies ○ Expertise Java & Big Data: consulting, formation, hébergement ○ 200 personnes ○ Paris, Bordeaux, Nantes, Richmond (USA)
  • 3. Ippon Technologies © 2015 Sommaire 1. Intro 2. Stack technique 3. Configuration du cluster 4. Application d’exemple 5. Recherche multi-critère 6. Utilisation de CQL3 avec Java 8 7. Limitations 8. Résultats
  • 4. Ippon Technologies © 2014 Stack technique
  • 5. Ippon Technologies © 2015 Stack technique ● JHipster ○ Générateur d’applications Spring Boot + AngularJS ○ Supporte JPA, MongoDB et… Cassandra! ● Nous a permis de générer l’application très rapidement ○ Squelette d’application prêt en 5 minutes ○ Ajout de tables avec leur mapping ○ Configuration, build, gestion des logs, etc. ○ Tests Gatling prêts à l’emploi Plus d’informations sur http://jhipster.github.io/
  • 6. Ippon Technologies © 2015 Stack technique ● Spring Boot ○ Basé sur Spring ○ Convention over configuration ○ Nombreux “starters” prêts à l’emploi ● Services Web ○ CXF vs Spring MVC REST ● Cassandra ○ DataStax Enterprise
  • 7. Ippon Technologies © 2015 Paramétrage du Driver ● Configuration Spring Boot ○ Nous avons réalisé notre propre configuration du DataStax Java Driver ○ Intégration dans la configuration standard de Spring Boot, utilisant un fichier YAML ● Intégrée dans Spring Boot 1.3 ○ Ce code a été proposé à Pivotal, et intégré dans Spring Boot 1.3 ● Améliorée par PR ○ JHipster a été amélioré depuis, et propose toujours une configuration plus complète que celle de Spring Boot
  • 8. Ippon Technologies © 2015 Développement des Repositories ● DataStax Java driver utilisé dans un Repository Spring @Repository public class UserRepository { @Inject private Session session; private PreparedStatement findOneByEmailStmt; @PostConstruct public void init() { findOneByEmailStmt = session.prepare( "SELECT id FROM user_by_email WHERE email = :email"); } public Optional<User> findOneByEmail(String email) { … }
  • 9. Ippon Technologies © 2014 Configuration du cluster
  • 10. Ippon Technologies © 2015 Hardware ● Hébergement chez Ippon Hosting ● 8 noeuds équivalents ○ 16 Go de RAM ○ Deux disques durs SSD de 256 Go en RAID 0 ● 6 noeuds pour le cluster Cassandra, 2 noeuds pour l’ application
  • 11. Ippon Technologies © 2015 DataStax Enterprise ● Utilisation de DataStax Enterprise ● OpsCenter nous a été d’une très grande aide ○ Monitoring ○ Services automatisés ○ Gestion du cluster
  • 12. Ippon Technologies © 2014 Application exemple Système de gestion de factures
  • 13. Ippon Technologies © 2015 Modèle conceptuel
  • 14. Ippon Technologies © 2015 Modèle physique
  • 15. Ippon Technologies © 2015 create table invoice ( invoice_id timeuuid, user_id uuid static, firstname text static, lastname text static, invoice_date timestamp static, payment_date timestamp static, total_amount decimal static, delivery_address text static, delivery_city text static, delivery_zipcode text static, item_id timeuuid, item_label text, item_price decimal, item_qty int, item_total decimal, primary key (invoice_id, item_id) ); Table
  • 16. Ippon Technologies © 2014 Recherche multi-critère
  • 17. Ippon Technologies © 2015 Recherche multi-critères Critères obligatoires ○ User (implicite) ○ Date de la facture (plage de dates) Critères supplémentaires ○ Nom du client ○ Prénom du client ○ Ville ○ Code postal
  • 18. Ippon Technologies © 2015 Utiliser Solr ?
  • 19. Ippon Technologies © 2015 Utiliser Solr ? ● Intégré dans DataStax Enterprise ● Mise à jour atomique et automatique ● Recherche documentaire
  • 20. Ippon Technologies © 2015 Utiliser Solr ? On cherche sur des colonnes statiques Solr ne les gère pas On cherche des partitions Solr retourne des lignes
  • 21. Ippon Technologies © 2015 Utiliser Solr ? On cherche sur des colonnes statiques Solr ne les gère pas On cherche des partitions Solr retourne des lignes
  • 22. Ippon Technologies © 2015 Index secondaires ? ● Ne répondent qu’aux cas de recherche sur un seul champ ● Délicats à utiliser avec de bonnes performances
  • 23. Ippon Technologies © 2015 Tables d’index Utilisation de tables d’index ○ Clé de partition : Les critères primaire et un critère secondaire ■ user_id ■ date de facturation (tronqué à la date) ■ le critère secondaire ○ Clustering columns : l’identifiant de la facture
  • 24. Ippon Technologies © 2015 Recherche Q1 Q2 A D J M A C J L M A J M Fusion applicative en mémoire Recherches en parallèle
  • 25. Ippon Technologies © 2015 Recherche Une page de résultat (id) 8f5b69ee-0ad0-11e5-a6c0-1697f925ec7b 8f5b6d4a-0ad0-11e5-a6c0-1697f925ec7b 8f5b6e9e-0ad0-11e5-a6c0-1697f925ec7b b3db1a30-0ad0-11e5-a6c0-1697f925ec7b b3db1c88-0ad0-11e5-a6c0-1697f925ec7b b3db202a-0ad0-11e5-a6c0-1697f925ec7b b3db219c-0ad0-11e5-a6c0-1697f925ec7b cac5be94-0ad0-11e5-a6c0-1697f925ec7b cac5c006-0ad0-11e5-a6c0-1697f925ec7b cac5c150-0ad0-11e5-a6c0-1697f925ec7b N recherches unitaires en parallèle
  • 26. Ippon Technologies © 2015 Recherche Recherche sur une plage de dates ○ boucle sur les jours en s’arrêtant dès qu’on a une page de résultat
  • 27. Ippon Technologies © 2015 Recherche Nombre de requêtes ○ Pour chaque jour dans la plage de dates ■ 1 requête par critère secondaire (partition by query) ○ 1 requête par élément trouvé (partition by query) Complexité de la recherche ○ partitions by query Exemple: 3 critères, 3 jours, 100 par pages ○ nombre de requêtes ≤ 3 × 3 + 100 = 109
  • 29. Ippon Technologies © 2015 Index — instances @Repository public class InvoiceByLastNameRepository extends IndexRepository<String> { public InvoiceByLastNameRepository() { super("invoice_by_lastname", "lastname", Invoice::getLastName, Criteria::getLastName); } } @Repository public class InvoiceByFirstNameRepository extends IndexRepository<String> { public InvoiceByFirstNameRepository() { super("invoice_by_firstname", "firstname", Invoice::getFirstName, Criteria::getFirstName); } }
  • 30. Ippon Technologies © 2015 Index — classe parente public class IndexRepository<T> { @Inject private Session session; private final String tableName; private final String valueName; private final Function<Invoice, T> valueGetter; private final Function<Criteria, T> criteriumGetter; private PreparedStatement insertStmt; private PreparedStatement findStmt; private PreparedStatement findWithOffsetStmt; @PostConstruct public void init() { /* initialise les PreparedStatements */ }
  • 31. Ippon Technologies © 2015 Index — Écriture @Override public void insert(Invoice invoice) { T value = valueGetter.apply(invoice); if (value != null) { session.execute( insertStmt.bind( invoice.getUserId(), Dates.toDate(invoice.getInvoiceDay()), value, invoice.getId())); } }
  • 32. Ippon Technologies © 2015 Index — Écriture insertStmt = session.prepare( QueryBuilder.insertInto(tableName) .value("user_id", bindMarker()) .value("invoice_day", bindMarker()) .value(valueName, bindMarker()) .value("invoice_id", bindMarker()) ); public static Date toDate(LocalDate date) { return date == null ? null : Date.from(date.atStartOfDay().atZone(ZoneOffset.systemDefault()).toInstant()); }
  • 33. Ippon Technologies © 2015 Index — Recherche @Override public CompletableFuture<Iterator<UUID>> find(Criteria criteria, LocalDate day, UUID offset) { T criterium = criteriumGetter.apply(criteria); if (criterium == null) { return CompletableFuture.completedFuture(null); } BoundStatement stmt; if (invoiceIdOffset == null) { stmt = findStmt.bind(criteria.getUserId(), Dates.toDate(day), criterium); } else { stmt = findWithOffsetStmt.bind(criteria.getUserId(), Dates.toDate(day), criterium, offset); } return Jdk8.completableFuture(session.executeAsync(stmt)) .thenApply(rs -> Iterators.transform(rs.iterator(), row -> row.getUUID(0))); }
  • 34. Ippon Technologies © 2015 Index — Recherche findWithOffsetStmt = session.prepare( QueryBuilder.select() .column("invoice_id") .from(tableName) .where(eq("user_id", bindMarker())) .and(eq("invoice_day", bindMarker())) .and(eq(valueName, bindMarker())) .and(lte("invoice_id", bindMarker())) );
  • 35. Ippon Technologies © 2015 Index — Recherche (Guava to Java 8) public static <T> CompletableFuture<T> completableFuture(ListenableFuture<T> guavaFuture) { CompletableFuture<T> future = new CompletableFuture<>(); Futures.addCallback(guavaFuture, new FutureCallback<T>() { @Override public void onSuccess(V result) { future.complete(result); } @Override public void onFailure(Throwable t) { future.completeExceptionally(t); } }); return future; }
  • 36. Ippon Technologies © 2014 JAVA Service de recherche
  • 37. Ippon Technologies © 2015 Service — Class @Service public class InvoiceSearchService { @Inject private InvoiceRepository invoiceRepository; @Inject private InvoiceByDayRepository byDayRepository; @Inject private InvoiceByLastNameRepository byLastNameRepository; @Inject private InvoiceByFirstNameRepository byLastNameRepository; @Inject private InvoiceByCityRepository byCityRepository; @Inject private InvoiceByZipCodeRepository byZipCodeRepository;
  • 38. Ippon Technologies © 2015 Service — recherche public ResultPage findByCriteria(Criteria criteria) { return byDateInteval(criteria, (crit, day, offset) -> { CompletableFuture<Iterator<UUID>> futureUuidIt; if (crit.hasIndexedCriteria()) { /* * ... Recherche multi-critère à voir dans la prochaine diapo ... */ } else { futureUuidIt = byDayRepository.find(crit.getUserId(), day, offset); } return futureUuidIt; }); }
  • 39. Ippon Technologies © 2015 Service — recherche CompletableFuture<Iterator<UUID>>[] futures = Stream.<IndexRepository> of( byLastNameRepository, byFirstNameRepository, byCityRepository, byZipCodeRepository) .map(repo -> repo.find(crit, day, offset)) .toArray(CompletableFuture[]::new); futureUuidIt = CompletableFuture.allOf(futures).thenApply(v -> Iterators.intersection(TimeUUIDComparator.desc, Stream.of(futures) .map(CompletableFuture::join) .filter(Objects::nonNull) .collect(Collectors.toList())));
  • 40. Ippon Technologies © 2015 Service — Comparaison des UUIDs /** * Comparateur de TimeUUID équivalent à celui de Cassandra: * @see org.apache.cassandra.db.marshal.TimeUUIDType#compare() */ public enum TimeUUIDComparator implements Comparator<UUID> { desc { @Override public int compare(UUID o1, UUID o2) { long delta = o2.timestamp() - o1.timestamp(); if (delta != 0) return Ints.saturatedCast(delta); return o2.compareTo(o1); } }; }
  • 41. Ippon Technologies © 2015 Service — Boucle sur les jours @FunctionalInterface private static interface DayQuery { CompletableFuture<Iterator<UUID>> find(Criteria criteria, LocalDate day, UUID invoiceIdOffset); } private ResultPage byDateInteval(Criteria criteria, DayQuery dayQuery) { int limit = criteria.getLimit(); List<Invoice> resultList = new ArrayList<>(limit); LocalDate dayOffset = criteria.getDayOffset(); UUID invoiceIdOffset = criteria.getInvoiceIdOffset(); /* ... Boucle sur les jours ; à voir dans la prochaine diapo ... */ return new ResultPage(resultList); }
  • 42. Ippon Technologies © 2015 Service — Boucle sur les jours LocalDate day = criteria.getLastDay(); do { Iterator<UUID> uuidIt = dayQuery.find(criteria, day, invoiceIdOffset).join(); limit -= loadInvoices(resultList, uuidIt, criteria, limit); if (uuidIt.hasNext()) { return new ResultPage(resultList, day, uuidIt.next()); } day = day.minusDays(1); invoiceIdOffset = null; } while (!day.isBefore(criteria.getFirstDay()));
  • 43. Ippon Technologies © 2015 Service — chargement des factures private int loadInvoices(List<Invoice> resultList, Iterator<UUID> uuidIt, Criteria criteria, int limit) { List<CompletableFuture<Invoice>> futureList = new ArrayList<>(limit); for (int i = 0; i < limit && uuidIt.hasNext(); ++i) { futureList.add(invoiceRepository.findOne(uuidIt.next())); } futureList.stream() .map(CompletableFuture::join) .forEach(resultList::add); return futureList.size(); }
  • 44. Ippon Technologies © 2014 Limitations
  • 45. Ippon Technologies © 2015 Limitations La recherche ne fonctionne que sur un texte précis ○ Pas de recherche “plein texte” ○ Comme dans une base de données classique La pagination ne donne pas le nombre total de pages de résultats Ce mécanisme ne peut fonctionner que s’il existe des critères obligatoires fortement discriminants (ici: user_id et invoice_day)
  • 46. Ippon Technologies © 2014 Résultats
  • 47. Ippon Technologies © 2015 Résultats métier ● Gestion d’un an de données, sans limite ○ Nous comptons monter à 3 ans ○ Ancien système: limité à 3 mois ● Obtention des résultats en “temps réel” ○ Les données sont immédiatement disponibles ○ Ancien système: 24h de retard ● Coûts nettement plus bas
  • 48. Ippon Technologies © 2015 Résultats techniques ● Les tests Gatling ont montré que nous pouvions tenir 5000 utilisateurs concurrents ○ Sur des requêtes complexes, avec multiples critères et pagination ● Nous avons aussi démontré que le cluster était scalable linéairement ○ En termes de volumétrie: ajout de nouveaux disques (passage en JBOD) ○ En termes de performance: ajout de nouveaux noeuds à chaud
  • 49. Ippon Technologies © 2014 Merci à tous!