{"id":1847,"date":"2024-10-23T17:02:54","date_gmt":"2024-10-23T15:02:54","guid":{"rendered":"https:\/\/www.loicmathieu.fr\/wordpress\/?p=1847"},"modified":"2024-10-24T13:04:37","modified_gmt":"2024-10-24T11:04:37","slug":"jooq-tip-ne-convertissez-pas-jsonb-en-string","status":"publish","type":"post","link":"https:\/\/www.loicmathieu.fr\/wordpress\/fr\/informatique\/jooq-tip-ne-convertissez-pas-jsonb-en-string\/","title":{"rendered":"jOOQ tip: ne convertissez pas JSONB en String"},"content":{"rendered":"<p>Il y a quelques semaines, en investiguant de possibles am\u00e9liorations de performance pour le backend JDBC de <a href=\"https:\/\/kestra.io\/\" rel=\"noopener\" target=\"_blank\">Kestra<\/a>, j&rsquo;ai remarqu\u00e9 qu&rsquo;une m\u00e9thode qu&rsquo;on utilisait pour mapper une entit\u00e9 \u00e0 persister en base de donn\u00e9es dans sa repr\u00e9sentation <code>JSONB<\/code> prenait beaucoup de temps dans nos profiles CPU.<\/p>\n<p>Dans le flame graph suivant, on peut voir que la m\u00e9thode <code>JdbcQueue.map()<\/code> compte pour plus de 21% des \u00e9chantillons et la m\u00e9thode <code>Repository.map()<\/code> pour 3.2% des \u00e9chantillons d&rsquo;un profile CPU utilisant <a href=\"https:\/\/github.com\/async-profiler\/async-profiler\" rel=\"noopener\" target=\"_blank\">async-profiler<\/a>.<\/p>\n<img data-recalc-dims=\"1\" loading=\"lazy\" decoding=\"async\" src=\"https:\/\/i0.wp.com\/loicmathieu.fr\/wordpress\/wp-content\/uploads\/366507373-c44206a5-0085-43cc-902e-97756319b0ea.png?resize=640%2C461&#038;ssl=1\" alt=\"\" width=\"640\" height=\"461\" class=\"alignnone size-large wp-image-1853\" srcset=\"https:\/\/i0.wp.com\/loicmathieu.fr\/wordpress\/wp-content\/uploads\/366507373-c44206a5-0085-43cc-902e-97756319b0ea.png?resize=1024%2C737&amp;ssl=1 1024w, https:\/\/i0.wp.com\/loicmathieu.fr\/wordpress\/wp-content\/uploads\/366507373-c44206a5-0085-43cc-902e-97756319b0ea.png?resize=300%2C216&amp;ssl=1 300w, https:\/\/i0.wp.com\/loicmathieu.fr\/wordpress\/wp-content\/uploads\/366507373-c44206a5-0085-43cc-902e-97756319b0ea.png?resize=768%2C553&amp;ssl=1 768w, https:\/\/i0.wp.com\/loicmathieu.fr\/wordpress\/wp-content\/uploads\/366507373-c44206a5-0085-43cc-902e-97756319b0ea.png?resize=375%2C270&amp;ssl=1 375w, https:\/\/i0.wp.com\/loicmathieu.fr\/wordpress\/wp-content\/uploads\/366507373-c44206a5-0085-43cc-902e-97756319b0ea.png?w=1166&amp;ssl=1 1166w\" sizes=\"auto, (max-width: 640px) 100vw, 640px\" \/>\n<p>Ces deux m\u00e9thodes permettent de mapper une colonne de type <code>JSONB<\/code> dans le type cible de l&rsquo;entit\u00e9. Pour cela, on utilise un object mapper de <a href=\"https:\/\/github.com\/FasterXML\/jackson\" rel=\"noopener\" target=\"_blank\">Jackson<\/a>. Le code peut se simplifier en :<\/p>\n<pre>\nMAPPER.readValue(record.get(\"value\", String.class), MyEntity.class);\n<\/pre>\n<p>Instinctivement, je me dis qu&rsquo;il faudrait plut\u00f4t lire le record en <code>JSONB<\/code> qu&rsquo;en <code>String<\/code>, je r\u00e9alise donc le changement suivant :<\/p>\n<pre>\nMAPPER.readValue(record.get(\"value\", JSONB.class).data(), MyEntity.class);\n<\/pre>\n<p>Et le r\u00e9sultat est imm\u00e9diat, une nette am\u00e9lioration des performances !<\/p>\n<p><code>JdbcQueue.map()<\/code> passe de plus de 21% des \u00e9chantillons \u00e0 un petit 4.6% et la m\u00e9thode <code>Repository.map()<\/code> de 3.2% des \u00e9chantillons \u00e0 2.7%. Dans le test que je r\u00e9alise, la queue de Kestra est fortement sollicit\u00e9e ce qui explique que l&rsquo;impact soit beaucoup plus fort sur ce composant.<\/p>\n<img data-recalc-dims=\"1\" loading=\"lazy\" decoding=\"async\" src=\"https:\/\/i0.wp.com\/loicmathieu.fr\/wordpress\/wp-content\/uploads\/366507464-11f17f92-ed72-4609-ad3c-adb393bc88c7.png?resize=640%2C456&#038;ssl=1\" alt=\"\" width=\"640\" height=\"456\" class=\"alignnone size-large wp-image-1852\" srcset=\"https:\/\/i0.wp.com\/loicmathieu.fr\/wordpress\/wp-content\/uploads\/366507464-11f17f92-ed72-4609-ad3c-adb393bc88c7.png?resize=1024%2C730&amp;ssl=1 1024w, https:\/\/i0.wp.com\/loicmathieu.fr\/wordpress\/wp-content\/uploads\/366507464-11f17f92-ed72-4609-ad3c-adb393bc88c7.png?resize=300%2C214&amp;ssl=1 300w, https:\/\/i0.wp.com\/loicmathieu.fr\/wordpress\/wp-content\/uploads\/366507464-11f17f92-ed72-4609-ad3c-adb393bc88c7.png?resize=768%2C547&amp;ssl=1 768w, https:\/\/i0.wp.com\/loicmathieu.fr\/wordpress\/wp-content\/uploads\/366507464-11f17f92-ed72-4609-ad3c-adb393bc88c7.png?resize=379%2C270&amp;ssl=1 379w, https:\/\/i0.wp.com\/loicmathieu.fr\/wordpress\/wp-content\/uploads\/366507464-11f17f92-ed72-4609-ad3c-adb393bc88c7.png?w=1179&amp;ssl=1 1179w\" sizes=\"auto, (max-width: 640px) 100vw, 640px\" \/>\n<p>Content par l&rsquo;am\u00e9lioration, j&rsquo;ouvre une PR que je merge assez rapidement : <a href=\"https:\/\/github.com\/kestra-io\/kestra\/pull\/4899\/files\" rel=\"noopener\" target=\"_blank\"><a href=\"https:\/\/github.com\/kestra-io\/kestra\/pull\/4899\/files\">https:\/\/github.com\/kestra-io\/kestra\/pull\/4899\/files<\/a><\/a>.<\/p>\n<p>Mais, apr\u00e8s r\u00e9flexion je me dis que j&rsquo;aimerais bien savoir le pourquoi ?<\/p>\n<p>J&rsquo;ouvre donc mon IDE favoris et parcourt le code :<\/p>\n<ul><li><code>Record.get(String, Class)<\/code> est impl\u00e9ment\u00e9 par <code>AbstractRecord.get(String, Class)<\/code>.<\/li>\n\n<li><code>AbstractRecord.get(String, Class)<\/code> m\u00e8ne \u00e0 <code>AbstractRecord.get(int, Class)<\/code> o\u00f9 une m\u00e9thode <code>converterOrFail()<\/code> est appel\u00e9e. Ah!, une conversion, \u00e7a peut expliquer l&rsquo;impact en performance !<\/li>\n\n<li>La conversion se fait via un <code>ConvertedProvider<\/code> qui fournit un <code>Converter<\/code>.<\/li>\n\n<li>Apr\u00e8s une recherche rapide, les convertisseurs utilisent la classe utilitaire <code>Convert<\/code> pour la conversion de type, on s&rsquo;approche !<\/li>\n\n<li><code>Convert.from()<\/code>, en ligne 716, convertit n&rsquo;importe quel type en String via appel de sa m\u00e9thode <code>toString()<\/code>.<\/li>\n<\/ul>\n<pre>\n\/\/ All types can be converted into String\nelse if (toClass == String.class) {\n    if (from instanceof EnumType e)\n        return (U) e.getLiteral();\n    }\n    return (U) from.toString();\n}\n<\/pre>\n<p>On arrive donc \u00e0 la m\u00e9thode <code>JSONB.toString()<\/code> qui utilise <code>JSONValue.toJSONString(parsed())<\/code>pour normaliser la repr\u00e9sentation JSON et permettre l\u2019\u00e9galit\u00e9 entre deux JSON de structure diff\u00e9rente, mais avec les m\u00eames attributs.<\/p>\n<p>Cette m\u00e9thode est clairement document\u00e9e comme \u00e9tant une m\u00e9thode \u00e0 \u00e9viter dans un contexte sensible \u00e0 la performance et c&rsquo;est clairement pr\u00e9cis\u00e9 dans la JavaDoc.<\/p>\n<blockquote>\nuses a normalised representation of the JSON content, meaning that two equivalent JSON documents are considered equal. This impacts both behaviour and performance!\n<\/blockquote>\n<p>Je connais donc maintenant le pourquoi !<\/p>\n<p>Par acquit de conscience, je me suis pench\u00e9 sur notre impl\u00e9mentation MySQL qui utilise le type <code>JSON<\/code>, celui-ci n\u2019effectue pas de normalisation et retourne directement le JSON sous-jasent via <code>String.valueOf(data)<\/code>, elle ne souffre en cons\u00e9quence pas du m\u00eame souci de performance.<\/p>\n<p>Voil\u00e0 toute l&rsquo;histoire, j&rsquo;esp\u00e8re que cette plong\u00e9e dans le code de <a href=\"https:\/\/www.jooq.org\/\" rel=\"noopener\" target=\"_blank\">jOOQ<\/a> vous as int\u00e9ress\u00e9 autant que moi ;)<\/p>\n<p><strong>Mise \u00e0 jour<\/strong> : apr\u00e8s publication de cet article sur les r\u00e9seaux sociaux, <a href=\"https:\/\/x.com\/JavaOOQ\/status\/1849366704373059603\" rel=\"noopener\" target=\"_blank\">jOOQ a r\u00e9pondu<\/a> pointant vers deux issues GitHub \u00e0 ce propos :<\/p>\n<ol><li><a href=\"https:\/\/github.com\/jOOQ\/jOOQ\/issues\/15751\" rel=\"noopener\" target=\"_blank\">Revert JSONB::toString to produce JSONB::data<\/a><\/li>\n\n<li><a href=\"https:\/\/github.com\/jOOQ\/jOOQ\/issues\/14954\" rel=\"noopener\" target=\"_blank\">Implement faster JSONB normalisation<\/a><\/li>\n<\/ol>\n<p>La deuxi\u00e8me issue vaut la peine d&rsquo;\u00eatre lue, elle pose le probl\u00e8me de la normalisation, \u00e9voque des pistes d&rsquo;am\u00e9lioration et les limites du parser JSON actuel. Un probl\u00e8me qui pourrait sembler simple au premier abord est souvent beaucoup plus compliqu\u00e9 qu&rsquo;il n&rsquo;y parait.<\/p>\n<p>Pour participer \u00e0 la discussion, j&rsquo;ai ouvert une issue <a href=\"https:\/\/github.com\/jOOQ\/jOOQ\/issues\/17497\" rel=\"noopener\" target=\"_blank\">Switch JSONB record convertion for String to use the data() method<\/a> qui propose de fixer ce cas pr\u00e9cis de la conversion sans devoir attendre de fixer le probl\u00e8me plus g\u00e9n\u00e9rale de la performance de la m\u00e9thode <code>toString()<\/code>.<\/p>\n<p><\/p>","protected":false},"excerpt":{"rendered":"<p>Il y a quelques semaines, en investiguant de possibles am\u00e9liorations de performance pour le backend JDBC de Kestra, j&rsquo;ai remarqu\u00e9 qu&rsquo;une m\u00e9thode qu&rsquo;on utilisait pour mapper une entit\u00e9 \u00e0 persister en base de donn\u00e9es dans sa repr\u00e9sentation JSONB prenait beaucoup de temps dans nos profiles CPU. Dans le flame graph suivant, on peut voir que la m\u00e9thode JdbcQueue.map() compte pour plus de 21% des \u00e9chantillons et la m\u00e9thode Repository.map() pour 3.2% des \u00e9chantillons d&rsquo;un profile CPU utilisant async-profiler. Ces deux&#8230;<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https:\/\/www.loicmathieu.fr\/wordpress\/fr\/informatique\/jooq-tip-ne-convertissez-pas-jsonb-en-string\/\">Lire la suite<span class=\"screen-reader-text\"> Lire la suite<\/span><\/a><\/p><\/p>","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"jetpack_post_was_ever_published":false,"_jetpack_newsletter_access":"","_jetpack_dont_email_post_to_subs":false,"_jetpack_newsletter_tier_id":0,"_jetpack_memberships_contains_paywalled_content":false,"_jetpack_memberships_contains_paid_content":false,"activitypub_content_warning":"","activitypub_content_visibility":"","activitypub_max_image_attachments":4,"activitypub_interaction_policy_quote":"anyone","activitypub_status":"","footnotes":"","jetpack_publicize_message":"","jetpack_publicize_feature_enabled":true,"jetpack_social_post_already_shared":true,"jetpack_social_options":{"image_generator_settings":{"template":"highway","default_image_id":0,"font":"","enabled":false},"version":2}},"categories":[9],"tags":[11,223,159,187],"class_list":["post-1847","post","type-post","status-publish","format-standard","hentry","category-informatique","tag-java","tag-jooq","tag-performance","tag-profiling"],"aioseo_notices":[],"jetpack_publicize_connections":[],"jetpack_featured_media_url":"","jetpack_sharing_enabled":true,"jetpack_likes_enabled":true,"jetpack-related-posts":[{"id":1459,"url":"https:\/\/www.loicmathieu.fr\/wordpress\/fr\/informatique\/profiler-un-pod-dans-kubernetes-avec-kubectl-flame\/","url_meta":{"origin":1847,"position":0},"title":"Profiler un pod dans Kubernetes avec kubectl flame","author":"admin","date":"mercredi  4 mai 2022","format":false,"excerpt":"Kubectl flame est un plugin pour kubectl qui vous permet de profiler des applications en production avec un faible co\u00fbt en g\u00e9n\u00e9rant des FlameGraphs. C'est un projet Yahoo. Il s'installe via krew, le gestionnaire de plugin pour kubectl, et permet de g\u00e9n\u00e9rer des FlameGraphs pour des applications en Go, Java\u2026","rel":"","context":"Dans &quot;informatique&quot;","block_context":{"text":"informatique","link":"https:\/\/www.loicmathieu.fr\/wordpress\/fr\/category\/informatique\/"},"img":{"alt_text":"","src":"https:\/\/i0.wp.com\/loicmathieu.fr\/wordpress\/wp-content\/uploads\/cpu-mysql-updated.png?resize=350%2C200&ssl=1","width":350,"height":200,"srcset":"https:\/\/i0.wp.com\/loicmathieu.fr\/wordpress\/wp-content\/uploads\/cpu-mysql-updated.png?resize=350%2C200&ssl=1 1x, https:\/\/i0.wp.com\/loicmathieu.fr\/wordpress\/wp-content\/uploads\/cpu-mysql-updated.png?resize=525%2C300&ssl=1 1.5x, https:\/\/i0.wp.com\/loicmathieu.fr\/wordpress\/wp-content\/uploads\/cpu-mysql-updated.png?resize=700%2C400&ssl=1 2x"},"classes":[]},{"id":1153,"url":"https:\/\/www.loicmathieu.fr\/wordpress\/fr\/informatique\/profiler-une-image-native-graalvm-avec-perf\/","url_meta":{"origin":1847,"position":1},"title":"Profiler une image native GraalVM avec perf","author":"admin","date":"lundi 16 novembre 2020","format":false,"excerpt":"L'outil GraalVM native-image permet de g\u00e9n\u00e9rer un ex\u00e9cutable natif (ou image native) depuis votre application Java. Cet ex\u00e9cutable natif va d\u00e9marrer tr\u00e8s rapidement et avoir une empreinte m\u00e9moire beaucoup plus faible qu'une application Java traditionnelle; au prix de performances en pic r\u00e9duites et d'un temps de cr\u00e9ation de ce package\u2026","rel":"","context":"Dans &quot;informatique&quot;","block_context":{"text":"informatique","link":"https:\/\/www.loicmathieu.fr\/wordpress\/fr\/category\/informatique\/"},"img":{"alt_text":"","src":"https:\/\/i0.wp.com\/loicmathieu.fr\/wordpress\/wp-content\/uploads\/flamegraph-2.png?resize=350%2C200&ssl=1","width":350,"height":200,"srcset":"https:\/\/i0.wp.com\/loicmathieu.fr\/wordpress\/wp-content\/uploads\/flamegraph-2.png?resize=350%2C200&ssl=1 1x, https:\/\/i0.wp.com\/loicmathieu.fr\/wordpress\/wp-content\/uploads\/flamegraph-2.png?resize=525%2C300&ssl=1 1.5x, https:\/\/i0.wp.com\/loicmathieu.fr\/wordpress\/wp-content\/uploads\/flamegraph-2.png?resize=700%2C400&ssl=1 2x"},"classes":[]},{"id":1946,"url":"https:\/\/www.loicmathieu.fr\/wordpress\/fr\/informatique\/java-25-quoi-de-neuf\/","url_meta":{"origin":1847,"position":2},"title":"Java 25 : Quoi de neuf?","author":"admin","date":"vendredi  4 juillet 2025","format":false,"excerpt":"Maintenant que Java 25 est features complete (Rampdown Phase One au jour d\u2019\u00e9criture de l\u2019article), c\u2019est le moment de faire le tour des fonctionnalit\u00e9s qu\u2019apporte cette nouvelle version, \u00e0 nous, les d\u00e9veloppeurs. Cet article fait partie d\u2019une suite d\u2019article sur les nouveaut\u00e9s des derni\u00e8res versions de Java, pour ceux qui\u2026","rel":"","context":"Dans &quot;informatique&quot;","block_context":{"text":"informatique","link":"https:\/\/www.loicmathieu.fr\/wordpress\/fr\/category\/informatique\/"},"img":{"alt_text":"","src":"","width":0,"height":0},"classes":[]},{"id":1180,"url":"https:\/\/www.loicmathieu.fr\/wordpress\/fr\/informatique\/benchmark-conversion-de-long-en-byte\/","url_meta":{"origin":1847,"position":3},"title":"Benchmark : conversion de long en byte[]","author":"admin","date":"mardi  8 d\u00e9cembre 2020","format":false,"excerpt":"J'utilise beaucoup Kafka ces derniers temps, et dans Kafka, beaucoup de choses sont des tableaux de bytes, m\u00eame les headers ! Comme j'ai de nombreux composants qui s'\u00e9changent des messages, j'ai ajout\u00e9 des headers pour aider au suivi des messages, et entre autres un header timestamp qui a comme valeur\u2026","rel":"","context":"Dans &quot;informatique&quot;","block_context":{"text":"informatique","link":"https:\/\/www.loicmathieu.fr\/wordpress\/fr\/category\/informatique\/"},"img":{"alt_text":"","src":"","width":0,"height":0},"classes":[]},{"id":1258,"url":"https:\/\/www.loicmathieu.fr\/wordpress\/fr\/informatique\/profiler-une-application-java-dans-un-conteneur-deploye-dans-kubernetes-avec-jfr-java-flight-recorder\/","url_meta":{"origin":1847,"position":4},"title":"Profiler une application Java dans un conteneur d\u00e9ploy\u00e9 dans kubernetes avec JFR &#8211; Java Flight Recorder","author":"admin","date":"lundi 12 avril 2021","format":false,"excerpt":"La plupart des clients chez lesquels j'interviens aujourd'hui utilisent Kubernetes pour d\u00e9ployer leurs applications. Bien que Kubernetes soit un super outil, si on a besoin de ce type d'outil (qui est une discussion dans laquelle je ne rentrerais pas ici), il peut apporter une certaine complexit\u00e9 quant aux moyens de\u2026","rel":"","context":"Dans &quot;informatique&quot;","block_context":{"text":"informatique","link":"https:\/\/www.loicmathieu.fr\/wordpress\/fr\/category\/informatique\/"},"img":{"alt_text":"","src":"","width":0,"height":0},"classes":[]},{"id":1330,"url":"https:\/\/www.loicmathieu.fr\/wordpress\/fr\/informatique\/devoxx-france-2021-ledition-9-3-4\/","url_meta":{"origin":1847,"position":5},"title":"Devoxx France 2021 &#8211; l&rsquo;\u00e9dition 9 3\/4","author":"admin","date":"vendredi  1 octobre 2021","format":false,"excerpt":"Cette semaine, c'est Devoxx France. Et pour la premi\u00e8re fois depuis pas mal de temps, je sors de chez moi, et j'y vais ! Je vous \u00e9cris ces mots dans le train de retour de la deuxi\u00e8me journ\u00e9e, pas de troisi\u00e8me pour moi cette ann\u00e9e. J'ai assist\u00e9 \u00e0 quelques talks,\u2026","rel":"","context":"Dans &quot;informatique&quot;","block_context":{"text":"informatique","link":"https:\/\/www.loicmathieu.fr\/wordpress\/fr\/category\/informatique\/"},"img":{"alt_text":"","src":"","width":0,"height":0},"classes":[]}],"_links":{"self":[{"href":"https:\/\/www.loicmathieu.fr\/wordpress\/fr\/wp-json\/wp\/v2\/posts\/1847","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.loicmathieu.fr\/wordpress\/fr\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.loicmathieu.fr\/wordpress\/fr\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.loicmathieu.fr\/wordpress\/fr\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.loicmathieu.fr\/wordpress\/fr\/wp-json\/wp\/v2\/comments?post=1847"}],"version-history":[{"count":12,"href":"https:\/\/www.loicmathieu.fr\/wordpress\/fr\/wp-json\/wp\/v2\/posts\/1847\/revisions"}],"predecessor-version":[{"id":1862,"href":"https:\/\/www.loicmathieu.fr\/wordpress\/fr\/wp-json\/wp\/v2\/posts\/1847\/revisions\/1862"}],"wp:attachment":[{"href":"https:\/\/www.loicmathieu.fr\/wordpress\/fr\/wp-json\/wp\/v2\/media?parent=1847"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.loicmathieu.fr\/wordpress\/fr\/wp-json\/wp\/v2\/categories?post=1847"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.loicmathieu.fr\/wordpress\/fr\/wp-json\/wp\/v2\/tags?post=1847"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}