Cette session est principalement destinée aux développeurs. Au fil du code que nous écrirons, nous utiliserons les principales API de DynamoDB : création de table, création d’index, lecture et écriture d’items, requêtes, scans, etc. Nous utiliserons pour cela le SDK Java
✚ Inscrivez-vous aux mardis du cloud, deux webinaires mensuels en français et en direct : http://amzn.to/2lvragO
✚ Rendez-vous sur notre site internet : http://amzn.to/2ktrf5g
✚ Suivez-nous sur Twitter : https://twitter.com/aws_actus
Transcript
Nous revoilà, bienvenue à ceux qui nous ont peut-être rejoints à l'instant, et pour les autres, j'espère que vous avez pris un petit café, un petit thé, un petit coca, quelque chose pour vous énergiser, puisque là on va plonger dans le code. Voilà, il était temps, trop de slides tue le slide. J'espère que la police que j'ai choisie est suffisamment grande. Si jamais vous avez du mal à lire, n'hésitez pas à hurler dans le chat et Hugo me relèvera l'information. Je pense que la taille là doit être à peu près correcte.
Le but de cette deuxième session, c'est de vous montrer comment concrètement on utilise DynamoDB. Alors je vais le faire avec l'API Java, mais comme je disais tout à l'heure, pas d'inquiétude, on va pas faire d'acrobaties Java, et si vous connaissez d'autres langages, vous n'aurez aucun mal à vous raccrocher à ce que je raconte. Le but du jeu, ça va être de créer une table qui va contenir des films, et on va essayer de balayer un petit peu toutes les opérations. Donc la création de la table, la création d'un index, l'écriture d'un item, la lecture d'un item, l'effacement, le requêtage. Enfin, on va essayer de faire tout ça. Et on va le faire de deux façons différentes. On va le faire en utilisant les deux niveaux de l'API DynamoDB.
Le premier niveau, qui est un niveau low level, où on va vraiment manipuler du clé-valeur, on va vraiment manipuler des items. Ça va ressembler à ce que j'ai décrit tout à l'heure lors du premier webinar, au prix d'une certaine complexité, donc ne vous inquiétez pas, c'est la vue bas niveau clé-valeur de DynamoDB. Et dans un second temps, on va utiliser l'API haut niveau de DynamoDB, qui vous verrez est bien plus adaptée à la lecture et à l'écriture d'objets, ce qui tombe bien puisqu'on est en train de faire du Java. On va utiliser ce qu'on appelle le DynamoDB Mapper, qui est finalement une abstraction qui nous permet de lire et d'écrire des objets Java automatiquement dans des tables en n'ayant pas à se soucier de leur représentation. Un peu à l'instar d'un ORM comme Hibernate ou d'autres ORM de ce style.
Alors j'aurais pu utiliser le terme d'ORM ici parce que le R veut dire relational et bien sûr, nous n'avons pas de relation. Mais c'est la même idée. Donc on va, dans un premier temps, faire du clé-valeur, dans un second temps, on va manipuler des POJOs et les lire et les écrire. Et bien sûr, vous allez trouver que la deuxième façon est beaucoup plus simple, plus claire et plus lisible, mais parfois on utilise DynamoDB pour stocker réellement du clé-valeur, et il est important de connaître la première API.
Ok, alors n'hésitez pas à envoyer vos questions, je pense que vous allez en avoir, ça va être un peu dense. Avant de se lancer dans le code, une dernière précision, et je suis toujours étonné que finalement ce point est souvent peu connu. Il existe une version locale de DynamoDB que vous pouvez télécharger et que vous pouvez exécuter localement. Donc il suffit d'aller vous chercher « local DynamoDB » dans Google, vous allez le trouver. C'est un point jar que vous allez télécharger et que vous allez lancer, comme ça, tout simplement. Ou on peut aussi le démarrer via le plugin AWS dans Eclipse, si vous faites partie des vieux comme moi qui utilisent encore Eclipse.
Mais voilà, quoi qu'il en soit, vous pouvez exécuter localement sur votre poste une version de Dynamo qui respecte évidemment l'API du service, et ce qui vous permet de tester localement et de tester en n'étant pas connecté à votre compte et en faisant toutes les bêtises possibles et surtout gratuitement puisque là vous ne créez aucune ressource dans le cloud. Donc c'est une bonne façon de faire, c'est très simple à mettre en œuvre. Il suffit donc de télécharger cette version locale, de la lancer via Java comme indiqué ici, et puis de vous y connecter. Donc au lieu de vous connecter à l'endpoint de l'Irlande, comme je vais le faire, vous vous connectez localement sur le port 8000, et puis ensuite tout le reste est identique.
Pour ma part, je dispose d'une connexion réseau qui devrait fonctionner, donc je vais utiliser directement le service hébergé en Irlande. Mais gardez ça à l'esprit, vous pouvez travailler localement sur votre poste avec Dynamo, en ayant finalement uniquement cette ligne de connexion qui est différente. Bon, allez, c'est parti.
Alors, première étape, première chose à faire, c'est de se connecter. Dans le SDK Java, on va trouver, quels que soient les services, ces abstractions qui vont s'appeler bla bla bla client builder, à qui vous indiquez la région, build, et vous récupérez le client. Donc le client, c'est tout simplement l'objet qui va vous permettre d'invoquer les API. Alors il y a un petit cas particulier pour le batch get, mais on y reviendra tout à l'heure. Donc première étape, récupérer une référence sur un client DynamoDB, ici dans la région irakon. On pourrait le faire partout ailleurs, mais pourquoi pas en Irlande aujourd'hui.
Première étape, on va créer la table. On va aller voir ce que fait cette méthode. Je crois que c'est la pire, donc je me suis dit qu'on allait commencer par celle-là, histoire de sensibiliser, de se faire peur tout de suite. Alors, respirez un grand coup. Elle est effectivement un peu compliquée parce qu'elle a pas mal de définitions. Et histoire de la compliquer encore plus, j'y ai rajouté un index secondaire. On va le faire un petit peu à l'envers. On va commencer par le bas.
Donc l'API DynamoDB qui permet de créer une table, c'est create table if not exist. Donc si la table n'existe pas déjà, je la crée. En passant le client en référence et une create table request. Donc si vous avez déjà un peu joué avec l'API des SDKWS, vous savez qu'on trouve à peu près systématiquement ce pattern où on met tous les paramètres de la requête dans un objet blabla request et souvent on récupère un objet blabla response. Ça dépend des cas, mais la plupart du temps c'est comme ça. L'API DynamoDB ne va pas faire exception. Et donc on va devoir construire cet objet create table request.
Alors qu'est-ce que c'est que le create table request ? Le create table request, ce sont les paramètres dont on a besoin pour créer la table. Souvenez-vous de ce qu'on a fait tout à l'heure dans la console, on a passé un nom de table, on a défini une clé de partitionnement et on a défini la capacité de lecture et d'écriture, et on aurait pu définir des index. Ça tombe bien parce qu'on va faire exactement la même chose ici. Donc dans ma table request, je vais créer une table request, je vais indiquer le nom de la table. Alors c'est une constante qui vaut MovieTable. Je définis une clé. Alors ici, elle s'appelle la HKEY, mais c'est vraiment la clé de partitionnement. Donc c'est la clé qui va découper les items, enfin répartir les items entre les différents dynamos. Donc ici, j'ai choisi de prendre le titre. Donc ça sera une chaîne de caractères.
Et j'ajoute quelques attributs à ma table avec un objet attribute definitions qui est défini là. Donc mes attributs, ce sont un titre qui est donc de type string, une note pour le film, ça va être des étoiles, un nombre d'étoiles en fonction du film, donc là aussi string, et une date de sortie, qui est l'année, je crois, l'année de sortie, qui elle va être un nombre. Donc, avec ces attributs, et avec un index secondaire, je vais créer ma table. Et dans l'index secondaire, alors ici je crée un index que j'appelle rating index. Et donc cet index secondaire, il a le droit d'avoir une clé de partitionnement différente, c'est bien souvent pour ça qu'on utilise un index secondaire, un GSI.
Donc ici, je vais utiliser je vais créer un index sur la note parce que j'ai envie de chercher les films qui ont cinq étoiles, les films qui ont quatre étoiles, etc. Ce que je ne peux pas faire facilement avec le titre ici. Et donc dans cet index, je vais mettre la note et la date de sortie. D'accord ? Donc si on résume, je crée une table qui a comme clé de partitionnement un attribut qui s'appelle title, qui est défini comme string dans le attribute definitions. J'ajoute un index secondaire qui s'appelle rating index. Cet index secondaire, il va contenir deux attributs : un attribut rating qui est la clé de hachage, d'accord, qui est de type string, et un attribut release date qui va me servir de clé de tri, de range key, et qui est un nombre.
Vous voyez, c'est pour ça que j'ai besoin de ce attribute definition, c'est parce que c'est ça qui me permet de faire le lien entre le nom des attributs et leur type. Et lorsque je crée l'index, et que je crée la table, je ne reprécise pas le type des attributs, je précise simplement quel est leur rôle. Donc ici, j'ai une clé pour la table, deux clés pour l'index, et donc j'ai bien besoin de définir les trois attributs. Ça marche ? Et puis dans les deux cas, je vais définir ma capacité de lecture et d'écriture, donc là je provisionne à 1, donc j'ai quasiment, j'ai pas de trafic, donc je prends le minimum, donc une unité de lecture et une unité d'écriture pour la table et idem pour l'index. Ok ?
Bon, voilà, donc on retrouve tous les concepts qu'on a évoqués tout à l'heure, et on voit bien qu'on a les clés de hachage, les clés de tri, etc. Ok ? Donc voilà ce qu'on peut dire pour ça. D'accord ? Donc une fois qu'on a fait ça, on a notre table request qui est prête à être exécutée, et donc on la passe à create table if not exist, et on crée la requête. Ok ? Donc là, on va attendre un petit peu puisqu'il faut peut-être 10 secondes ou 20 secondes pour créer la table. Donc on a une méthode qui est super pratique qui s'appelle wait until active qui va bloquer tant que la table n'est pas à l'état active dans Dynamo et tant qu'on n'est pas capable de s'en servir. Bonne petite méthode sympa à connaître. Ok ? Lorsque la table est prête, elle va débloquer. On va afficher un message et donc on va retourner un objet de table, ok ? Qu'on va ensuite utiliser pour faire nos put et nos get. Ok ?
Bon. Donc voilà, voilà comment on crée la table. Exemple un peu compliqué parce que je voulais vous montrer comment rajouter également un index. Vous voyez que s'il n'y avait pas l'index, on aurait eu besoin uniquement. Dans Attribute Definition, on aurait pu enlever ces deux lignes-là. On aurait pu enlever tout ça et on aurait pu enlever tout ça. Donc, dans une requête, une création de table toute simple, vous voyez que c'est 3-4 lignes de Java. Et une fois qu'on a compris ce qu'on fait, ce n'est pas trop difficile. Dernier point, ici j'ai créé le GSI en même temps que la table, mais on aurait pu le rajouter après. Ce qui est un des avantages du GSI par rapport au LSI, on peut le construire ou le détruire indépendamment de la table à laquelle il est lié. Voilà pour la création.
Alors avançons. Ensuite on a une méthode printTableDescription. On va voir ce que fait ce truc là. Donc là c'est beaucoup plus simple. printTableDescription ça va simplement décrire les propriétés de la table. Et donc là on construit une describeTableRequest à laquelle on passe le nom de la table. On appelle l'API describe table et on récupère une table description. On verra ça tout à l'heure lorsqu'on exécutera le code. Et là, sans surprise, on va voir les clés qui ont été définies, etc. Si on voulait le faire en ligne de commande, on ferait aws-dynamo-db-describe-table--tab. C'est exactement la même chose. On peut récupérer ça.
Ensuite on va commencer à faire des trucs utiles. On va regarder comment se servir de putItem. PutItem, comme son nom l'indique, c'est l'API qui permet de stocker un nouvel item dans une table. On va aller voir cette méthode addMoviesToTable. Ici, on va donc ajouter quelques films. J'ai choisi les différents films de Star Wars. Et j'ai une petite méthode qui s'appelle « putMovie » à laquelle je passe le nom de la table et puis les différents attributs que j'ai envie d'écrire. « putMovie », on va regarder ce que ça fait. « putMovie » ça prend la table, un premier string qui est donc la série à laquelle le film appartient, ici c'est Star Wars, le titre du film, l'année de sortie, la note, le nombre d'étoiles, et puis un string set, donc une liste de tailles variables, enfin un ensemble de tailles variables qui sont les personnages du film.
Et donc là, eh bien que vais-je faire ? Je vais créer un nouvel item. Donc « new item », je vais indiquer la clé primaire qui est le titre et puis les autres attributs. D'accord ? Donc le seul attribut indispensable ici, c'est le titre puisque j'ai écrit dans la table. La seule clé que j'ai définie sur cette table, c'est le titre. Et puis ensuite, les attributs. Et on l'a vu tout à l'heure, ça pourrait être variable, il pourrait être vide, ça ne posera pas de problème. Donc j'ai mes builders pour construire mon item ici. Et puis ensuite, je fais tout simplement table.putItem avec l'item que je viens de construire. Ça, ça va me retourner un résultat et c'est ça que je vais afficher.
Donc, vous voyez, là, on est vraiment dans une clé-valeur. On ne raisonne pas sur des objets. On fait un put, tout simplement. Et ce put, alors certes, c'est un objet item ici parce qu'on développe en Java. Mais vous voyez qu'il n'y a pas de notion de... Ce n'est pas un objet movie. Voilà, ça, c'est ce qu'on fera tout à l'heure. Donc, on fait vraiment put item. On rajoute une ligne dans la table. Et donc, je vais faire ça 4 ou 5 fois pour les différents épisodes de Star Wars. Et vous voyez que pour mon string set ici, alors ici j'ajoute 7 personnages, ici aussi, et pour le retour du Jedi 1, 2, 3, 4, 5, 6, 7, 8, donc un peu nombre différent, et pour la menace fantôme, je n'en rajoute que 1, 2, 3, 4, 5.
Donc vous voyez, on peut avoir des attributs différents, des attributs de taille différentes. Pas de problème, on ne se pose pas la question du schéma, de ce qu'on peut écrire. Une fois de plus, le seul paramètre indispensable, c'est celui-ci. C'est le titre du film qui me sert de clé de partitionnement. Donc, normalement, on aura réussi à ajouter ces quelques films. Ensuite on va essayer d'en récupérer un. Donc on va utiliser l'API GetItem. On va essayer de récupérer les informations sur le retour du Jedi. Donc PrintMovie, voilà ce que ça fait. Donc PrintMovie, sans surprise, ça va faire GetItem, donc table.getItem et on passe le nom de la clé, le nom de l'attribut qui sert de clé de partitionnement, donc title, et la valeur de cette clé.
Donc ici je lui dis tu me donnes, s'il te plaît, l'item dont la clé est The Return of the Jedi. Et si tout va bien, je le récupère. Donc je récupère mon item et je l'affiche. Je l'affiche en joli JSON, ce qui n'est pas une tâche impossible. Donc vous voyez là aussi, on a vu put à l'instant, on voit get. Ce sont les deux opérations les plus fréquentes. Donc vous voyez, autant la création de la table, c'est un petit peu compliqué parce qu'il faut passer la définition des clés, la définition des attributs, les index s'ils existent, etc. Autant l'accès à la table ça pourrait pas être plus simple. C'est table.getItem, table.putItem avec le paramètre associé. Super simple.
Continuons. Donc là on a réussi à afficher normalement notre film. Alors maintenant, imaginons qu'on ait envie de récupérer plusieurs films d'un coup. Alors on pourrait faire un premier get item et un deuxième get item. Mais on a une API qui s'appelle batch get item qui va nous permettre de récupérer plusieurs items d'un coup. En une opération, donc a priori avec une latence inférieure à celle de deux appels consécutifs de getItem. Donc allons-y, printMoviesByTitles, qu'est-ce que ça fait ? Alors donc ça prend le client, la table et puis un ensemble variable de titres.
Donc qu'est-ce qu'on fait ici ? On va créer un objet barbare qui s'appelle table keys and attributes qui va contenir le nom de l'attribut qui sert de clé, donc ici c'est title, et puis un tableau contenant l'ensemble des clés qu'on a envie de lire. Ok et donc ce tableau, on va le passer à batch getItem. Donc là, on lui dit « Dans la table en question, récupère-moi tous les items dont l'attribut « title » est présent dans ce tableau. » Donc ici, j'en ai deux, je crois. Et donc, je vais récupérer un résultat sur lequel je vais pouvoir récupérer mes différents items et puis les afficher, tout simplement.
Alors, il faut noter que si jamais on n'a pas trouvé certains des items, eh bien, on va le savoir, en fait. On va récupérer une liste partielle et on va avoir ce getUnprocessedKeys qui va nous dire, il y a au moins un élément qui n'a pas été trouvé. Donc on n'aura pas d'erreur, mais on aura un résultat partiel. Voilà, donc vous comprenez le principe, toujours le même. Je veux faire un get sur cet attribut-là. Voilà la liste des valeurs que j'ai envie de récupérer. Et donc dans mon résultat de batch get item, je vais récupérer un ensemble d'items qui correspondent au titre que j'ai passé ici. Et je les affiche. Je ne vous montre pas cette méthode-là, ça n'a aucun intérêt.
Donc batch get, la capacité en un coup à récupérer plusieurs items. Alors, continuons. Donc là, on a vu le pull, on a vu le get, on a vu le batch get, maintenant on pourrait voir le update. Alors imaginons que j'ai envie de rajouter un personnage à... j'ai envie de rajouter Jabba au retour du Jedi. Donc je vais faire ça, j'ai une méthode addCharacterToMovie. Et donc là, eh bien il va falloir que j'ajoute ce personnage. Donc... Je vais commencer par faire un get. Je vais récupérer l'item, comme tout à l'heure, dont le title est ce que j'ai passé en paramètres. Je vais récupérer dans cet item la liste des personnages qui sont présents.
Il n'y a pas vraiment une liste, il y a un ensemble des personnages qui sont présents. J'ajoute à cet ensemble le nouveau personnage, donc ici je vais ajouter Jabba. Et là maintenant j'ai un nouvel item, cet objet item qui s'appelle Movie qui a été modifié, et il va falloir que je l'update. Et donc pour faire ça, je vais utiliser la syntaxe de mise à jour de DynamoDB qui n'est pas hyper compliquée. Mais ici, l'API Java est un peu fouillie, il faut être honnête. Donc ce que j'ai envie de faire, en fait, c'est ça. Donc j'ai envie de dire, sur mon item, je veux que tu mettes à jour un attribut, qui est donc l'attribut characters.
Donc ce que je veux faire, c'est set characters, donc l'attribut characters, égale le nouvel ensemble de characters que j'ai défini en ajoutant ici Jabba. Donc ce qu'ils appellent l'update expression, c'est ça. Et donc ce qui est relativement non intuitif dans ce truc, c'est qu'on ne peut pas utiliser les noms d'attributs et on ne peut pas utiliser les noms de variables. On utilise des placeholders, on utilise des espèces de variables qu'on est obligé de définir ici. Donc pour mon attribut, j'ai donc une name, ce qu'on appelle une name map, donc dièse caractère qui est présent là. En fait c'est bien l'attribut caractère, donc l'attribut qui est présent dans la table, et la value map, c'est-à-dire ce que je vais écrire dans cet attribut, c'est le 2.characters, ça va être bien l'ensemble des personnages que j'ai mis à jour.
Donc, ça aurait été sans doute plus simple de pouvoir faire cet attribute characters égale cette variable-là. Bon. D'ailleurs, une bonne raison pour laquelle ça ne marche pas comme ça, donc on est obligé d'utiliser ces espèces de variables qu'on va substituer via la nameMap pour le nom de l'attribut et la valueMap pour la valeur de l'attribut. Donc l'update expression, la nameMap pour remplacer ça, la valueMap pour remplacer ça, la clé primaire comme d'habitude parce qu'on désigne toujours l'item par sa clé de partitionnement. Et voilà, ça, c'est ma requête. Ensuite, je fais « Update item ».
Si on résume, j'ai lu mon item pour récupérer tous ses attributs. Je modifie un de ses attributs en ajoutant un personnage. Ensuite, je veux faire en gros « Set l'attribut égale à la nouvelle valeur. Et pour faire ça, je suis obligé de passer par une name map et une value map. Et ensuite, l'update est tout simple. Je ne vous cache pas que ça prend un peu de temps au début. Une fois qu'on a compris le principe, c'est plus simple. Ne vous acharnez pas ici à mettre le nom de l'attribut et ici le nom de cette variable. Ça ne marche pas. Il vous faut une name map, il vous faut une value map, une value map pour faire ces substitutions. Une fois qu'on a compris ça, l'update est tout simple.
Alors ceci étant dit, l'update est une opération qui n'est pas la plus performante de DynamoDB et vous verrez sans doute dans la doc qu'on recommande souvent finalement d'insérer un nouvel item et d'effacer l'ancien et de le remplacer par un nouveau. Ici je voulais absolument montrer l'update et montrer comment on fait une update d'un seul champ. Vous voyez, ça méritait d'être montré, c'est pas hyper intuitif. Donc voilà pour l'API update. On continue.
Alors on a vu put, get, batch get, update. Maintenant on va voir comment requêter. Alors il y a deux façons de requêter dans Dynamo. Il y a une API qui s'appelle query qui permet de faire des vraies requêtes, mais limitée aux index. En tout cas limitée aux clés définies soit dans la table soit dans les index. Et on a une API qui s'appelle scan qui permet de balayer une table en utilisant d'autres attributs que ceux des clés, avec des performances qui sont évidemment bien plus mauvaises. Donc de manière générale, on préfère faire des requêtes.
Alors on va regarder ici, on va faire une requête sur l'index. On va chercher tous les films notés une étoile. Ok ? Donc voyons comment marche ce truc-là. Donc, on va appeler une méthode qui va nous trouver tous les films qui ont une étoile. Ça va nous retourner une item collection, qu'on va afficher. D'accord ? Voilà, c'est le petit item collection, c'est juste une collection, il n'y a pas grand-chose à dire dessus. Ce qui est intéressant, c'est ce qui se passe dans ça. C'est là qu'on va voir notre API query. Donc on va aller voir ça.
Donc, find movies by rating. On va récupérer, donc on a la table. Ici, on va... On va faire une requête sur l'index, le GSI qu'on a créé puisque dans notre table la clé de partitionnement c'est le titre. Ce n'est pas sur la table qu'on va requêter, c'est bien sur l'index. Donc on récupère l'index. Sur cet index on va appeler l'API Query avec un objet qui va être une QuerySpec que je construis avec cette petite méthode qui est juste en dessous. Donc cette query c'est quoi ? C'est rating égale une valeur. Et un peu comme tout à l'heure, on est obligé d'utiliser une value map.
Et donc je vais substituer ce 2.rating à le paramètre que j'ai passé tout à l'heure. Donc ça sera la chaîne de caractères qui contient une étoile. Donc une query, c'est tout simplement ça. C'est une expression. Alors ici, c'est une query qui est très simple. Je vérifie qu'un attribut est égal à une valeur. On peut faire des queries un petit peu plus évoluées. Je voulais me limiter à des choses à peu près compréhensibles. Donc je vais chercher tous les items dont l'attribut rating vaut ce que j'ai passé en paramètres. Une fois de plus, je substitue ce deux points rating à la chaîne de caractère que j'ai passée, qui est cette unique étoile en l'occurrence.
Donc ça, ça me construit mon objet QuerySpec, et c'est ça que je passe à Query. Donc si on avait requêté sur la table directement, si on avait cherché par exemple des titres, on aurait fait table.query avec une QuerySpec définie décrivant la requête qu'on voulait faire. Mais une fois de plus ici, c'est bien sur l'index qu'on query, donc il faut le faire sur l'index. Donc voilà comment on fait une requête. Vous voyez comment ça va le principe, il y a toujours cet objet de description de l'opération qu'on remplit avec la clé de partitionnement, les différents paramètres, etc. Donc ça, ça va me retourner... et m'afficher la liste des films à une étoile.
Autre exemple ici, je voudrais afficher la liste des films 5 étoiles qui sont sortis entre 1975 et 1982. Donc là, je vais utiliser toujours mon index avec la clé de partitionnement qui contiendra donc les cinq étoiles qui est la clé de mon index et puis j'utiliserai ma range key avec ces deux valeurs ces deux bornes et je vais essayer de trouver tout ce qui est entre les deux. Donc allons voir cette fonction là. Alors la voici. Donc comme tout à l'heure, je récupère mon index. Je vais faire index.query avec une query spec qui va être construite avec la note, l'année de départ et l'année de fin.
Allons voir cette méthode, allons voir comment la query spec est construite. Comme tout à l'heure, donc une query spec avec une expression qui va être ici rating égale 5 étoiles. Une fois de plus j'utilise la value map pour faire la substitution. Donc le rating égale à ça et la date de sortie contenue entre vstart et vend. Avec vstart et vend substitué à start here et end here que je passe en parallèle. Donc vous voyez la logique, une fois qu'on a compris le truc, finalement c'est pas si méchant. On écrit une expression avec des placeholders pour les valeurs et on substitue ces placeholders avec cet objet value map.
C'est là qu'on fait la correspondance entre la variable et son contenu effectif. Donc ça, ça me construit ma query spec. C'est ça que je passe à mon API query. Et puis je récupère comme tout à l'heure une item collection qui s'appelle movies et que je vais afficher. D'accord ? Donc là, j'ai utilisé mon index et j'ai utilisé à la fois sa clé de partitionnement et sa sort key. Voilà. On a presque terminé. On a fini pour Query, on va voir Scan.
Scan, c'est une opération qui permet de faire des recherches, qui va balayer toute la table, qui n'est pas performante dès que les tables sont un peu volumineuses, mais on peut chercher sur n'importe quel attribut. Ici, par exemple, je vais chercher tous les films de la série Star Wars. Ensuite, je vais chercher tous les films dans lesquels Yoda est présent. Ok, donc regardons comment faire ça, et ça, on va retrouver le même principe que tout à l'heure. Donc là, cette fois, je vais scanner sur la table, donc c'est bien l'objet table qui me sert pour invoquer l'API scan, et je vais avoir une scan expression, d'accord, que je construis ici.
Voilà, et comme tout à l'heure, c'est vraiment la même logique que le query. Donc je vais créer une scanSpec. L'expression, c'est l'attribut series égale une valeur, qui est cette valeur passée en paramètres, et que je substitue avec la value map. Pour la recherche des personnages, c'est la même chose. ScanSpec avec characters, je crée une scan spec. Ici, j'utilise une fonction qui s'appelle contains, qui me permet de vérifier que ce personnage est bien contenu dans mon attribut characters, et j'utilise une value map pour substituer ça aux paramètres que j'ai passés à la méthode.
Donc voilà l'idée. Le print movies by characters, c'est exactement la même chose, c'est table.scan avec la scan specification construite. Et ensuite, toujours pareil, un item collection et j'affiche. Il faut un peu de temps pour rentrer là-dedans. Il y a un peu de complexité liée à la façon dont Java manipule le truc. Ce n'est pas notoirement plus simple ou plus compliqué avec les autres langages. Après, si on est familier de Java, ça va. Si on l'est moins, il faut se battre un peu. Mais au moins, maintenant, vous avez des exemples de tout ça. Il ne faut pas hésiter à expérimenter. Une fois qu'on a compris la philosophie du truc, ça va assez vite.
Dernier exemple, on va regarder « Delete item ». On va effacer tous les mauvais films. Et donc, « Delete bad movies », qu'est-ce que ça fait ? Alors, « Delete bad movies », ça cherche tous les films à une étoile. Donc là, je fais faire un… c'est la même méthode que tout à l'heure. Donc là, je fais une query sur l'index en utilisant sa clé de partitionnement qui est le « rating ». Donc, je vais récupérer une « item collection » qui va compter tous les items notés une étoile. Et puis tout simplement, je vais itérer sur cette collection et je vais appeler « Delete item » avec le titre du film que je vais effacer.
Vous voyez, comme toujours, sur la table, la seule façon que j'ai d'adresser un item, c'est d'utiliser sa clé de partitionnement qui pour moi est le titre. Donc je récupère le titre de chacun des films notés une étoile et j'appelle « Delete item » en utilisant cette clé. Donc ça va supprimer l'item associé. Et puis pour finir, je crois que je réaffiche les films notés une étoile pour m'assurer qu'il n'y en a plus. Je les réaffiche et normalement, il ne devrait pas y en avoir.
Donc voilà les principales API de DynamoDB. Donc put pour stocker un item, get pour le récupérer. On ne peut le récupérer qu'en utilisant la clé de partitionnement, qui peut être soit celle de la table, soit celle d'un index. On peut faire batch get, pour récupérer en un coup plusieurs items. On passe la liste des valeurs de clés qu'on veut récupérer, et on va récupérer ça d'un coup. Il y a un batch put, que je vous ai épargné, mais qui marche de la même façon. Il y a update, qui est une API qu'on utilise peu souvent, mais je voulais quand même vous la montrer.
Il peut servir dans certains cas où on va modifier un seul attribut, donc on ne touche pas à toute la ligne, on ne modifie qu'un seul attribut de la table. Voilà bien que je le répète, dans la doc on va trouver le fait qu'il vaut mieux faire delete et put que de faire update. Bon, voilà, je vous laisserai lire ça en détail. Et puis ensuite, donc les deux façons d'aller lire des attributs, donc la façon rapide, donc qui est l'API Query qui va se servir des clés de la clé de partitionnement, voire de la clé de la range key de la sortie si elle existe. Ici je l'ai fait sur l'index, j'aurais pu le faire sur la table en ne cherchant qu'un titre, donc ça ça va être rapide, voilà, on va aller taper directement sur les items possédant les bons critères.
Si on veut sortir des clés qui sont définies et quand même aller chercher des items, on peut utiliser l'API scan, mais attention, là, on va faire un balayage de toute la table, donc sur des petites tables, ça ne pose pas vraiment de souci, mais ça consomme pas mal de capacité de lecture puisqu'on balaie toute la table. Donc, si vous le faites trop, vous pouvez vraiment nuire à la performance de votre table. Et puis pour des tables importantes, ça peut être très long. Il est très fortement déconseillé de faire des scans sur des grosses tables.
Mais vous avez toujours cette possibilité. Si vous êtes amené à faire ça très souvent, si j'étais amené très souvent à chercher des items en fonction de la série, il vaudrait bien mieux recréer un index, un GSI avec cet attribut comme clé et là je bénéficierai de performances bien meilleures. Épisodiquement, c'est ok de faire du scan mais pas régulièrement, vous verrez que ça a lieu aux performances. Et puis, delete lorsqu'on veut faire le ménage. La console est à côté. Oui, saurons. Alors, s'il vous plaît, exécutez-le. Voilà.
Nous sommes en train de... Qu'est-ce qu'il fait ? Voilà, c'est parti. Il a eu envie de me recompiler soudainement. Il est en train de créer la table. Vous voyez, cela prend une dizaine de secondes. C'est le fameux wait until active. Il l'a fait même une deuxième fois. Merci. J'ai cliqué plusieurs fois. Il crée la table. Vous voyez, cela prend quelques instants.
Nous avons une description imprimable, donc le résultat de l'API describe table où je vois les attributs, la capacité, l'ARN, le fait que j'ai un index, les propriétés de l'index, etc. Je vois toutes les informations sur ma table. Ensuite, j'ai ajouté mes films, donc j'ai fait mes quatre put items. Ensuite, je fais un get item sur Le Retour du Jedi, donc j'affiche sans surprise les différents attributs de mon item. Ensuite, je fais un batch get item où je lui demande d'afficher New Hope et Le Retour du Jedi. Je récupère donc les deux items d'un coup et les affiche.
Je fais l'update. Où est Le Retour du Jedi ? Oui, Le Retour du Jedi. On voit qu'il n'y a pas de Jaba, on est d'accord. Et là, on l'a ajouté. Donc, on a ajouté Jaba, on le réaffiche, et on voit qu'effectivement Jaba apparaît dans l'attribut characters. L'update n'a porté que sur cet attribut, je n'ai fait aucune écriture sur les autres champs.
Ensuite, j'affiche les films à une étoile en faisant une requête avec query sur mon index. Il n'y en a qu'un, La Menace Fantôme. J'espère qu'on sera tous d'accord sur le fait qu'il vaut une étoile. J'aurais dû mettre zéro même. Ensuite, une deuxième requête sur les films sortis entre 75 et 82. Notez 5 étoiles. Il me sort un film, il m'en sort deux.
Ensuite, je fais un scan, donc opération plus lente, mais là sur cette toute petite table, cela n'a aucun impact. Je vais récupérer tous les films de la série Star Wars, donc normalement je vais tous les voir, même La Menace Fantôme. Ensuite, je veux tous les films avec Yoda. J'ai dû insérer Yoda deux fois, donc je récupère mes deux films avec Yoda. C'est mon scan sur l'attribut characters qui contient Yoda.
Ensuite, je fais delete item. J'efface les films notés une étoile. Je fais juste le delete. Pour vérifier, j'affiche les films qui n'ont qu'une étoile, et il n'y en a plus. Il n'y a plus La Menace Fantôme, à ma grande satisfaction. Voilà le résultat de tout ça. C'est super.
Nous avons vu l'API bas niveau, c'est magnifique, mais pour des données structurées, c'est un peu compliqué. Cette API bas niveau est vraiment à réserver pour du clé-valeur pur, des données de session, quelque chose qui rentre vraiment dans du key value. Lorsqu'on manipule des objets, on n'aura pas envie de faire ça, on aura envie d'utiliser l'API dite de haut niveau via le DynamoDB mapper. C'est beaucoup plus simple.
Pour commencer, je vais définir mon POJO avec mes attributs. Si vous avez déjà fait de l'ORM, vous retrouverez vos marques. J'ai une classe que j'appelle DynamoDBMovie, dans laquelle je vais définir mes attributs. J'ai mon titre, que j'annote comme étant ma HKEY. J'ai la release, la série qui est juste un attribut normal, les personnages qui est juste un attribut normal, et ma rating qui est la hash key de mon index secondaire avec la notation. J'ai release date qui est la range key de mon index avec une notation. Vous voyez que j'ai déclaré les mêmes informations, mais avec une représentation beaucoup plus simple.
Voilà l'objet qui va être écrit dans ma base. J'ai un constructeur tout ce qu'il y a de plus simple, et c'est vraiment un POJO bête et méchant. Tout ce qui le différencie d'une classe Java normale, ce sont ces quelques annotations qui définissent les attributs. Grâce à ça, je vais pouvoir lire et écrire ces objets directement, sans passer par des API bas niveau.
J'ai commencé par me connecter à la région Irlande pour récupérer mon client DynamoDB. Je vais créer un mapper qui va me permettre de charger, lire et écrire mes objets. Et c'est tout. Vous allez me dire, mais on crée pas la table dans ce cas-là. Il y a deux raisons pour lesquelles on ne la crée pas : elle existe déjà et les deux API sont compatibles, donc on peut accéder à la même table de manière cohérente, qu'on utilise les clés-valeurs ou le mapper. Je vais réutiliser la table qui existe déjà. Voilà, MovieTable, elle est là. Si je clique dessus, je dois voir mes items. Voilà, magnifique.
Néanmoins, il y a une méthode super pratique qui, sur la base de la déclaration du POJO, va vous construire la requête nécessaire à la création de la table. Je vais vous montrer comment ça marche. C'est dans ma méthode printCreateTable. Voilà, et donc en fait ça se fait en une ligne : mapper.generateCreateTableRequest, on lui passe la classe du POJO, donc ici DynamoDBMovie, et sur la base de ça, il va me construire une CreateTableRequest qui est le strict équivalent de ce que j'ai fait péniblement à la main. C'est vraiment sympa. Vous annotez simplement votre POJO, vous lui dites de générer la requête pour créer la table, et vous l'avez en une ligne.
Ensuite, vous allez voir que pour lire et écrire un film, c'est super simple. Je vais ajouter un film dans la table, The Last Jedi, qui sort à la fin de l'année, et puis je vais le relire. J'ai cette petite méthode putMovie qui fait exactement ce qu'on s'attendrait qu'elle fasse. Je crée un nouveau DynamoDBMovie sur la base des paramètres que j'ai passés, et je fais mapper.saveMovie. Terminé. C'est vraiment un comportement type ORM. On passe le POJO et il est écrit dans la table.
L'opération symétrique, c'est l'écriture d'un POJO dans la table et sa lecture. Donc loadMovie, c'est exactement ce qu'on s'attendrait à trouver. On lui dit mapper, le type de l'objet, donc ici on veut lire un item et l'instancier dans un DynamoDBMovie, et on lui passe la clé de partitionnement, qui est ici le titre. C'est toujours la même table, donc c'est indispensable de passer la clé de partitionnement pour accéder aux bons items et instancier DynamoDBMovie.
Pour un query, on va chercher tous les films 5 étoiles. Donc, mapper.query avec une query spec. On crée une query expression où on dit, je cherche un film qui a ce rating-là, on construit l'expression, on passe la clé, et point important, on est en train de faire une lecture sur un GSI, donc on n'a pas de consistance forte, on a juste de l'éventuelle consistency. On est obligé de préciser que la lecture n'est pas cohérente à l'instant T. Il est possible qu'on lise l'ancienne valeur si elle n'a pas encore été répercutée sur l'index.
Le scan, c'est la même chose, on va avoir une expression, un placeholder, etc. Mais ce qu'il faut retenir, c'est que le code est beaucoup plus simple et plus proche de ce que l'application fait. L'application veut lire et écrire des films en base, elle ne veut pas trop se soucier de leur représentation, et c'est le mapper qui fait la conversion entre le format de la table DynamoDB et le format objet qui correspond à notre DynamoDBMovie.
On va exécuter ça et vérifier que ça fonctionne. Voilà, donc je vois ma CreateTableRequest. C'est ce que je pourrais passer à un appel CreateTable pour créer la table si elle n'existait pas encore. Ensuite, j'ajoute le nouveau film, The Last Jedi. Je ne doute pas qu'il aura 5 étoiles. Je le recharge. Ok, et je l'affiche, c'est bien ce que j'ai lu, c'est bien ce que j'ai écrit, tout va bien.
Si j'affiche tous les films 5 étoiles, je vois que j'ai les films de tout à l'heure, bien qu'ils aient été écrits en format clé-valeurs, ils sont correctement instanciés sous forme de POJO. Ici, c'est bien des to-strings sur le POJO. Ensuite, on cherche tous les films de la série Star Wars, donc on va avoir tous les films de tout à l'heure. Si je consulte ma table, je vois que j'ai bien ajouté The Last Jedi, il est là.
Nous avons cette dualité entre la représentation clé-valeurs, très puissante, très granulaire, mais un peu complexe lorsqu'on veut faire des choses évoluées. Pour du strict clé-valeur, généralement, c'est plutôt simple. Et puis, cette vue plus haut niveau, type ORM, qui nous permet facilement de prendre des objets Java simples, de les annoter et de les lire et écrire avec une seule ligne de code. Voilà, vous avez les deux possibilités avec DynamoDB.
Le code est sur GitHub. Vous le trouverez dans un repo qui s'appelle aws, et un répertoire qui s'appelle java-backends. Vous y trouverez le code qui correspond à la présentation mentionnée tout à l'heure, le comparatif des différents backends AWS. Spécifiquement, vous trouverez le code de DynamoDB. Si vous trouvez des bugs, n'hésitez pas à me les envoyer.
Nicolas, quelle est la taille maximum d'une base DynamoDB ? A priori, il n'y a pas de limite. À chaque fois qu'on a 10Go de données, on crée une nouvelle partition. On grossit par partition de 10Go. Il n'y a pas de taille maximum sur une base DynamoDB.
Nicolas, est-ce qu'il y a un outil de migration de base de données, Oracle, Sybase, etc., vers DynamoDB ? Oui, il y a le Database Migration Service (DMS) qui permet de migrer du on-premise vers AWS ou réciproquement. Avec DMS, on peut migrer depuis une base vers DynamoDB.
Fred, est-ce que les clés des LSI et des GSI doivent être uniques ? Oui, la clé de partitionnement, la H-key, doit être unique puisque c'est celle qui identifie l'item. Si vous essayez d'insérer deux items avec la même clé, l'API vous le refusera.
Virginie, si on a le choix, est-ce qu'il vaut mieux faire une ligne avec plein d'attributs ou plein de lignes avec peu d'attributs ? Ce qui dicte ça, c'est la nature de l'item. Un item est identifié par une clé unique, donc on appelle la hash key ou la partition key. Les attributs associés à cette partition key doivent être regroupés. Le choix n'est pas tant le nombre d'attributs, mais l'atomicité du truc. Quelles sont les requêtes que vous allez faire et quels sont les attributs que vous voulez récupérer ensemble sur la base d'une même clé ?
Dans le cas où la table ne contient qu'une ligne, est-ce qu'il y a une différence entre query ou scan ? Non, si vous faites cet appel une fois par seconde ou une fois par jour, il n'y a aucune différence. Si vous le faites 10 000 fois par seconde, oui, il y aura une différence. Query reste la bonne façon de faire.
Mathieu, est-ce qu'on peut cumuler DAX et le VPC ? Oui, je pense que oui, car quand on crée le cluster DAX, on indique dans quel VPC et quel subnet on le crée.
Mohamed, quid des régions qui n'ont que 2AZ, comme Francfort ? Nous avons annoncé au Summit de Berlin que nous allions ouvrir une troisième AZ à Francfort. Francfort aura donc trois AZ.
William, est-ce qu'il est possible de suivre les éléments supprimés par TTL ? Je ne suis pas sûr, mais il faudrait regarder si le stream inclut les éléments supprimés par TTL.
Jérôme, quel est le nom de l'outil utilisé ? Je pense que Jérôme fait référence à Eclipse. Le AWS Explorer est le plugin pour Eclipse. AWS Toolkit for Eclipse est facile à installer.
Yassir, DynamoDB local peut-il être utilisé dans un environnement de production ? Non, je serais prudent. Bien que possible, DynamoDB local ne bénéficie pas de la scalabilité, de la haute disponibilité, et de la sécurité de l'infrastructure DynamoDB. C'est adapté pour des environnements de tests locaux.
Christophe, est-ce qu'il y a une limite sur le nombre d'index qu'on peut créer sur une table ? Par table, la limite par défaut est de 5 LSI et 5 GSI. Cette limite est augmentable via une requête au support.
Fred, le coût d'insertion et de scan est-il proportionnel au nombre d'items lus ? Oui, le scan est l'équivalent d'un full scan sur une table relationnelle. Le coût en termes de capacité est tout ce qu'on lit.
Laurent, est-ce qu'on peut intégrer l'utilisateur IAM dans le code ? Oui, l'accès aux credentials se fait via un rôle IAM, des credentials stockés localement, ou implicitement via les credentials configurés dans l'environnement.
Jean-Michel, est-ce que l'usage du TTL est payant ? Non, c'est inclus dans le service.
Gero, à quand la région FR ? C'est bientôt.
Franck, est-ce qu'il y a un bon client pour explorer des données de DynamoDB, type SQL Developer ou Toad ? Je ne connais pas de client spécifique, mais il pourrait y avoir des projets open source. Si vous en connaissez un, partagez-le.
Nous avons répondu à beaucoup de questions. Merci de nous avoir suivis en ce webinaire de rentrée. J'espère que vous avez appris plein de choses et que vous avez envie d'explorer DynamoDB plus avant. On se retrouve fin septembre pour deux webinars sur les outils de dev et du DevOps, notamment CodeStar.
Je serai à Montréal les 13, 14 et 15 septembre. Il y a un user group AWS à Montréal le 14 septembre. Si vous avez envie de faire connaissance, rendez-vous le 14 septembre à Montréal au User Group AWS.
On a fini pour aujourd'hui. Merci beaucoup, bonne rentrée, et bon courage pour la reprise du boulot. On se retrouve fin septembre pour de nouvelles aventures.