[{"data":1,"prerenderedAt":29164},["ShallowReactive",2],{"posts-page-1":3,"posts-count":5315},[4,1779,2087,3513,7752,15250,17381,23310,24899,26090],{"id":5,"title":6,"author":7,"body":8,"category":1682,"categorySlug":1683,"date":1684,"description":14,"excerpt":1685,"extension":1764,"location":1765,"meta":1766,"navigation":1767,"path":1768,"published":1767,"seo":1769,"slug":1770,"stem":1771,"tags":1772,"timeToRead":1777,"__hash__":1778},"posts\u002Fposts\u002FWoodstock\u002F2026-04-26_woodstock_v2.md","Woodstock Backup v2.0.0 - La réécriture complète en Rust","Ulrich Vandenhekke",{"type":9,"value":10,"toc":1645},"minimark",[11,15,18,21,115,118,127,132,137,140,147,151,159,166,169,173,180,188,191,195,202,206,209,212,216,223,227,231,238,245,249,252,340,354,358,366,394,397,404,408,419,429,433,436,439,443,447,453,456,459,462,465,469,472,476,479,485,488,492,502,505,528,531,542,548,552,556,559,781,784,818,822,825,879,882,886,889,896,899,906,909,916,919,926,943,950,953,957,961,964,970,985,988,992,995,999,1002,1021,1572,1575,1578,1581,1587,1594,1600,1607,1618,1622,1625,1628,1642],[12,13,14],"p",{},"Bonjour à tous,",[12,16,17],{},"Six ans. Il m'aura fallu six ans entre la première version de Woodstock Backup et cette v2. Si vous m'aviez dit en 2020\nque je passerais la moitié de la décennie à réécrire trois fois le même logiciel de sauvegarde... j'aurais quand même\nfoncé tête baissée. C'est ma façon de faire. Me voilà donc avec une version 2 stable,\nentièrement réécrite en Rust, qui tourne en production sur ma petite infrastructure depuis plus d'un an. Et je suis\nvraiment content du résultat. 😄",[12,19,20],{},"Pour ceux qui me lisent depuis longtemps, voici un récapitulatif des articles qui ont précédé celui-ci :",[22,23,24,40],"table",{},[25,26,27],"thead",{},[28,29,30,34,37],"tr",{},[31,32,33],"th",{},"Article",[31,35,36],{},"Date",[31,38,39],{},"Sujet",[41,42,43,59,73,87,101],"tbody",{},[28,44,45,53,56],{},[46,47,48],"td",{},[49,50,52],"a",{"href":51},"\u002Fpost\u002Fwoodstock","Woodstock Backup v1.0.0",[46,54,55],{},"2020-09-20",[46,57,58],{},"Présentation du projet, prototype TypeScript + rsync",[28,60,61,67,70],{},[46,62,63],{},[49,64,66],{"href":65},"\u002Fpost\u002Fwoodstock_brtfs","Woodstock Backup - Btrfs",[46,68,69],{},"2021-01-12",[46,71,72],{},"Abandon de Btrfs, écriture d'un pool custom",[28,74,75,81,84],{},[46,76,77],{},[49,78,80],{"href":79},"\u002Fpost\u002Fwoodstock_protocol_language_sauvegarde","Woodstock Backup - Protocole et Langage de sauvegarde",[46,82,83],{},"2021-04-18",[46,85,86],{},"Protocole gRPC maison",[28,88,89,95,98],{},[46,90,91],{},[49,92,94],{"href":93},"\u002Fpost\u002Fwoodstock_rust","Woodstock Backup - Optimiser Node.js avec Rust",[46,96,97],{},"2023-05-10",[46,99,100],{},"NAPI-RS et bindings Rust pour réduire la consommation mémoire",[28,102,103,109,112],{},[46,104,105],{},[49,106,108],{"href":107},"\u002Fpost\u002Fpr_backuppc_pool","Woodstock Backup - Reverse engineering de BackupPC",[46,110,111],{},"2024-05-07",[46,113,114],{},"Migration du pool BackupPC vers Woodstock",[12,116,117],{},"Pour les nouveaux, je résume : Woodstock Backup est mon logiciel de sauvegarde personnel, centralisé, qui sauvegarde\ntoutes les machines de mon réseau local et mes serveurs distants sur un NAS. L'idée de départ était simple. Le résultat\nest... un peu plus complexe. :)",[12,119,120],{},[121,122],"img",{"alt":123,"src":124,"className":125},"Pool of chunks","\u002FWoodstock\u002Fv2_splash.png",[126],"img-center",[128,129,131],"h2",{"id":130},"le-long-chemin-de-2020-à-2026","Le long chemin de 2020 à 2026",[133,134,136],"h3",{"id":135},"prototype-1-typescript-rsync-btrfs-2020","Prototype 1 : TypeScript + rsync + Btrfs (2020)",[12,138,139],{},"Tout a commencé avec un prototype en TypeScript qui utilisait rsync pour copier les fichiers et Btrfs pour les\nsnapshots incrémentaux. L'idée était élégante sur le papier : rsync calcule le delta, Btrfs déduplique, tout le monde\nest content.",[12,141,142,143,146],{},"En pratique, je me suis rapidement retrouvé avec des problèmes de stabilité de Btrfs lorsque le nombre de snapshots\ndevient élevé et que le système de fichiers approche de la saturation. J'en ai ",[49,144,145],{"href":65},"parlé en détail dans mon article sur\nBtrfs",". En résumé : abandonné.",[133,148,150],{"id":149},"prototype-2-typescript-grpc-pool-custom-2021","Prototype 2 : TypeScript + gRPC + pool custom (2021)",[12,152,153,154,158],{},"L'abandon de Btrfs m'a forcé à écrire mon propre pool de stockage basé sur le principe de ",[155,156,157],"strong",{},"Content-Addressable\nStorage"," (CAS) : chaque bloc de fichier (chunk) est identifié par son hash, et si deux fichiers partagent des blocs\nidentiques, ces blocs ne sont stockés qu'une seule fois. La déduplication est donc native.",[12,160,161,162,165],{},"Pour transférer les fichiers entre les machines à sauvegarder et le serveur, j'ai abandonné rsync et développé ",[49,163,164],{"href":79},"mon\npropre protocole basé sur gRPC",". gRPC s'appuie sur HTTP\u002F2, offre une\ncompression et un TLS natifs, et les bibliothèques existent pour tous les langages. Parfait.",[12,167,168],{},"Ce prototype, toujours en TypeScript, fonctionnait. Mais le moteur JavaScript de Node.js a ses limites. En particulier,\nla représentation en mémoire des objets JavaScript est beaucoup plus gourmande que celle d'un langage compilé. Pour un\noutil qui doit gérer des millions de fichiers, c'est un vrai problème.",[133,170,172],{"id":171},"lère-des-bindings-rust-2023","L'ère des bindings Rust (2023)",[12,174,175,176,179],{},"L'idée de cette phase était séduisante : garder toute la partie cœur en Rust pour les performances, et conserver le\nTypeScript pour la couche GraphQL et la logique applicative. Rust pour ce qui est critique, TypeScript pour ce qui est\nlisible et rapide à écrire. C'est ce que j'ai ",[49,177,178],{"href":93},"décrit dans mon article de 2023",".",[12,181,182,183,187],{},"Bien sûr, je me trompais. En pratique, il fallait des bindings d'exposition NAPI-RS pour chaque interface entre les\ndeux langages, des DTOs côté TypeScript qui doublonnaient les structures Rust, et surtout, plus le temps passait, plus\nle cœur métier migrait vers Rust — laissant de moins en moins de code côté TypeScript. La complexité était réelle :\ngérer des équivalents d'",[184,185,186],"code",{},"Observable"," ou du streaming en passant par des bindings NAPI-RS n'est pas trivial, même si\nles dernières versions de NAPI-RS ont depuis apporté des solutions à ces problèmes.",[12,189,190],{},"Le tout en restant performant malgré le coût du passage par les bindings TypeScript. C'était jouable, mais c'était\nde la jonglerie.",[133,192,194],{"id":193},"la-migration-du-pool-backuppc-2024","La migration du pool BackupPC (2024)",[12,196,197,198,201],{},"En parallèle, j'ai travaillé sur la migration de mon pool BackupPC existant vers le format Woodstock. La première\napproche envisagée consistait à monter les sauvegardes BackupPC via FUSE et à les lire comme un système de fichiers\nnormal. J'ai finalement opté pour une approche plus directe : ",[49,199,200],{"href":107},"lire directement le format interne de BackupPC",",\nce qui impliquait un travail de rétro-ingénierie non négligeable. La migration a été un succès, et j'ai pu enfin\nabandonner complètement BackupPC.",[133,203,205],{"id":204},"la-décision-de-tout-réécrire-en-rust-2024","La décision de tout réécrire en Rust (2024)",[12,207,208],{},"À ce stade, la situation était devenue évidente : la partie TypeScript avait tellement rétréci qu'il ne restait plus\ngrand-chose dedans. Un serveur NestJS (framework Node.js) pour l'API, une interface web en Vue.js, et quelques couches\nde glue — le reste était déjà en Rust. Maintenir deux écosystèmes pour si peu de code TypeScript n'avait plus aucun\nsens.",[12,210,211],{},"J'ai donc décidé de sauter le pas : supprimer les bindings, réécrire intégralement le backend en Rust. Plus de\nNAPI-RS, plus de DTOs en double, plus de jonglerie entre deux compilateurs. Du Rust pur, de bout en bout.",[133,213,215],{"id":214},"woodstock-backup-v2-la-version-stable-2026","Woodstock Backup v2 : la version stable (2026)",[12,217,218,219,222],{},"La version ",[184,220,221],{},"2.0.0"," est en production depuis plus d'un an maintenant, et je la considère comme stable. Les sauvegardes tournent tous les jours, les restaurations\nfonctionnent, et je dors tranquille.",[128,224,226],{"id":225},"architecture-de-woodstock-backup-v2","Architecture de Woodstock Backup v2",[133,228,230],{"id":229},"vue-densemble-le-modèle-pull","Vue d'ensemble : le modèle Pull",[12,232,233,234,237],{},"L'architecture reste fondée sur le ",[155,235,236],{},"modèle pull"," : c'est le serveur de sauvegarde qui initie les connexions vers les\npériphériques, et non l'inverse. Cela offre une garantie de sécurité importante : un périphérique compromis ne peut\npas écrire de données arbitraires dans le serveur de sauvegarde.",[239,240,241],"center",{},[121,242],{"alt":243,"src":244},"Architecture Pull - Woodstock v2","https:\u002F\u002Fwww.plantuml.com\u002Fplantuml\u002Fpng\u002FVLJDRjD04BxxAOPm81M47hZbK9N-g19AfLAZ7b2aQB8UnzkiPvsTtRJSE25nHJm0Hy9h-4tw9E3ysaxAnPPs_hxvlfav5O_EXzn4Btn6EK6Edfp6A1hRH-Z4vEOK72G4Wc5E4tG9TU3bG4yoVsO2HG05Eg-LBf0zYCee2OPSw_tUZaSFratt3CfeOZ_2Ge-agjMsDmm9UXoZ47G-sE0OpP2Vlls0QsITadZg00hShqmDznjh3Po_ZuVSFJCufNUlFujFZfR-XRKc8avWR1_NNT-K2wUBhFhE0a7vgzQystH_vOYuXVP1Hkk64gJSSWD4p5X8Pdq5mhjKZk_YU0L1rfQc-nVnvU-SXfmGf5fbcfmitLFPuMNh2UoSN8qfw4DCpeCX0KUpKFxn970NwEsz3BbxUnb_EhvoM6GV1sz0a0Km-EmeYjhmeNUoBn3q8VyqYE7fwqyGFM4qb1Dxi6mqqx5Dq-eVHTjHgBBiz8S-N9GBPOXLHc2mHksGkqB6CXG6cJLFZY9KNi_HKts0Qhbw9tkKGn_EBJCzQiimkRqvNw8TSbUpzhfLiLPWJxf3P6o4geguSa4GUFlMa7MNTwljDhPt0gb07mQaV71KxPPvXMvi7OaYLhGBJYLA1NhDofl1WDfSH0dLWo9ZRG4tw9GDJY0XsNd2FcMzja83BPuQOT0LxmZpfIe0JGtMA_UFbVAxSdhLLTXk8d6oGMI30vLXjLKg2po577aMaFrUpWEwNb2ErJ8aOHLyi9K6LLkATn4x6PuPdcSpjqcwvBdLzTJD1ggxKgdbhPhYX20f5qaeZ9w5Sj6wG_yXT7tee77dLepM9DyEMUrjRw0FmhlMiZnmLSMg5qUfie4RcMgxTqgSXDpy1G00",[133,246,248],{"id":247},"les-composants-rust","Les composants Rust",[12,250,251],{},"L'ensemble de la solution est écrit en Rust. Côté serveur, quatre microservices ; côté client, un démon déployé sur\nchaque machine à sauvegarder :",[22,253,254,267],{},[25,255,256],{},[28,257,258,261,264],{},[31,259,260],{},"Composant",[31,262,263],{},"Déployé sur",[31,265,266],{},"Rôle",[41,268,269,282,298,313,325],{},[28,270,271,276,279],{},[46,272,273],{},[184,274,275],{},"api_server",[46,277,278],{},"Serveur",[46,280,281],{},"API REST\u002FGraphQL pour l'interface Vue.js — hors du chemin de sauvegarde",[28,283,284,289,291],{},[46,285,286],{},[184,287,288],{},"client_api_server",[46,290,278],{},[46,292,293,294,297],{},"Reçoit les connexions mTLS de ",[184,295,296],{},"ws_client_daemon"," pour le signalement online\u002Foffline",[28,299,300,305,307],{},[46,301,302],{},[184,303,304],{},"job_worker",[46,306,278],{},[46,308,309,310,312],{},"Exécute les sauvegardes : connexion gRPC vers ",[184,311,296],{},", transfert des chunks, déduplication",[28,314,315,320,322],{},[46,316,317],{},[184,318,319],{},"scheduler",[46,321,278],{},[46,323,324],{},"Gère le planning, déclenche les jobs selon les règles définies",[28,326,327,331,334],{},[46,328,329],{},[184,330,296],{},[46,332,333],{},"Chaque périphérique",[46,335,336,337,339],{},"Reçoit les connexions de ",[184,338,304],{},", crée les snapshots, envoie les chunks",[12,341,342,343,349,350,353],{},"La gestion des jobs de sauvegarde est assurée par ",[49,344,348],{"href":345,"rel":346},"https:\u002F\u002Fgithub.com\u002Fgeofmureithi\u002Fapalis",[347],"nofollow","Apalis",", qui s'appuie sur\n",[155,351,352],{},"Redis\u002FValkey"," comme backend de file d'attente. Apalis remplace ici BullMQ, qui remplissait ce rôle dans l'ancienne\nversion NestJS. C'est une architecture simple et fiable, qui permet également de distribuer les workers si un jour\nl'infrastructure venait à grossir.",[133,355,357],{"id":356},"le-pool-de-stockage-cas-blake3zstd","Le pool de stockage : CAS Blake3+Zstd",[12,359,360,361,365],{},"Le cœur du système est le pool CAS (",[362,363,364],"em",{},"Content-Addressable Storage","). Son fonctionnement est le suivant :",[367,368,369,377,384,387],"ol",{},[370,371,372,373,376],"li",{},"Chaque fichier est découpé en ",[155,374,375],{},"chunks"," de taille variable.",[370,378,379,380,383],{},"Chaque chunk est haché avec ",[155,381,382],{},"Blake3"," (un algorithme de hachage moderne, très rapide).",[370,385,386],{},"Si le hash du chunk est déjà présent dans le pool, il n'est pas retransféré ni re-stocké.",[370,388,389,390,393],{},"Si le chunk est nouveau, il est compressé avec ",[155,391,392],{},"Zstd"," avant d'être écrit sur disque.",[12,395,396],{},"La déduplication est donc réalisée au niveau des chunks, et non au niveau des fichiers entiers. Cela signifie que si\nun fichier de 10 Go n'a été modifié qu'à 1 %, seul 1 % sera retransféré et stocké.",[12,398,399,400,403],{},"Le pool maintient également un ",[155,401,402],{},"compteur de références"," (refcount) par chunk : quand une sauvegarde est supprimée,\nles chunks qui ne sont plus référencés sont supprimés du pool. Ce mécanisme de garbage collection permet de garder le\npool propre sans intervention manuelle.",[133,405,407],{"id":406},"sauvegardes-windows-vss-natif","Sauvegardes Windows : VSS natif",[12,409,410,411,414,415,418],{},"Depuis la version ",[184,412,413],{},"alpha.57",", le client Windows utilise le ",[155,416,417],{},"Volume Shadow Copy Service (VSS)"," de Windows pour créer\nun snapshot cohérent du système de fichiers avant la sauvegarde. Cela permet de sauvegarder des fichiers verrouillés\n(comme les bases de données, les fichiers de profil Outlook, etc.) sans erreur.",[12,420,421,422,424,425,428],{},"Plus besoin de rsync, de Cygwin, ou d'outils tiers : le client ",[184,423,296],{}," est un binaire Rust natif pour\nWindows, compilé avec la cible ",[184,426,427],{},"x86_64-pc-windows-msvc",", qui utilise les APIs Win32 directement. C'est\nnettement plus propre que l'ancienne approche.",[133,430,432],{"id":431},"sauvegardes-linux-snapshots-btrfs","Sauvegardes Linux : snapshots Btrfs",[12,434,435],{},"Sur les machines Linux dont le système de fichiers est Btrfs, le client crée un snapshot en lecture seule avant de\nlancer la sauvegarde. Cela garantit la cohérence des données sauvegardées, même si des fichiers sont modifiés pendant\nla sauvegarde.",[12,437,438],{},"Contrairement au premier prototype qui utilisait Btrfs côté serveur (ce qui causait les problèmes évoqués plus haut),\nici les snapshots sont créés côté client et uniquement pour la durée de la sauvegarde. C'est une utilisation beaucoup\nplus conservative de Btrfs.",[128,440,442],{"id":441},"les-défis-techniques","Les défis techniques",[133,444,446],{"id":445},"le-refcounting-un-problème-de-cohérence","Le refcounting : un problème de cohérence",[12,448,449,450,452],{},"Le plus grand défi de cette réécriture a été l'implémentation correcte du ",[155,451,402],{}," du pool CAS.",[12,454,455],{},"Le problème est le suivant : lorsqu'une sauvegarde est en cours, des chunks sont ajoutés au pool et leur refcount est\nincrémenté. Si la sauvegarde est interrompue (coupure réseau, arrêt du serveur, etc.), le pool peut se retrouver dans\nun état incohérent : des chunks présents dans le pool sans être référencés par aucune sauvegarde complète.",[12,457,458],{},"C'est la solution la plus complexe et qui a nécessité le plus de travail pour être implémentée de manière fiable. En\neffet, il faut s'assurer que le comptage de référence est bon si on ne veux pas se retrouver à éliminer des chunks encore référencés.",[12,460,461],{},"Afin de garantir que le comptage de référence est bon, un outil de récupération a été développé : il analyse le pool et les manifestes des sauvegardes, et corrige les refcounts en cas d'incohérence. Il n'est normallement nécessaire qu'en cas de\ncrash du serveur (coupure de courant, etc.) pendant une sauvegarde.",[12,463,464],{},"Actuellement la structure du pool repose sur le système de fichiers. L'inconvénient est à aujourd'hui la durée\nd'execution du fsck qui peut être très longue (taille du pool). En échange les opérations de lecture\u002Fécriture sont très\nrapides.",[133,466,468],{"id":467},"les-breaking-changes","Les BREAKING CHANGES",[12,470,471],{},"Par rapport à la version 1, on est sur une réécriture complète. Vu le peu de monde qui utilise cette version 1, il n'y a\npas de migration prévue. La version 2 est un nouveau projet, avec une nouvelle API, et des changements majeurs dans la façon dont les sauvegardes sont gérées.",[133,473,475],{"id":474},"windows-sans-rsync-binaire-natif-cross-compilé","Windows sans rsync : binaire natif cross-compilé",[12,477,478],{},"Dans la version 1, le client Windows était un serveur rsyncd couplé à Cygwin. C'était fonctionnel, mais peu élégant,\ndifficile à installer, et les permissions NTFS n'étaient pas correctement sauvegardées.",[12,480,481,482,484],{},"Dans la version 2, le client Windows est un binaire Rust compilé en cross-compilation depuis Linux avec la cible\n",[184,483,427],{}," et le linker LLVM\u002FClang. Le binaire est distribué seul, sans dépendance. Il se connecte au\nserveur de sauvegarde via gRPC mTLS, crée un snapshot VSS, parcourt le système de fichiers et envoie les chunks.",[12,486,487],{},"Les ACLs NTFS, les attributs étendus, les points de jonction et les liens symboliques Windows sont correctement\ngérés. C'est un vrai progrès.",[133,489,491],{"id":490},"la-sécurité-mtls-de-bout-en-bout","La sécurité : mTLS de bout en bout",[12,493,494,495,497,498,501],{},"Toutes les communications impliquant ",[184,496,296],{}," sont chiffrées et authentifiées par ",[155,499,500],{},"mutual TLS (mTLS)",".\nChaque périphérique possède un certificat client signé par une autorité de certification interne au serveur Woodstock.",[12,503,504],{},"Deux canaux mTLS distincts :",[506,507,508,518],"ul",{},[370,509,510,517],{},[155,511,512,514,515],{},[184,513,304],{}," ↔ ",[184,516,296],{}," (gRPC) : le canal de sauvegarde, à l'initiative du serveur.",[370,519,520,527],{},[155,521,522,524,525],{},[184,523,296],{}," → ",[184,526,288],{}," (REST) : le canal de présence, à l'initiative du client, qui lui permet\nde signaler son statut online\u002Foffline.",[12,529,530],{},"Cela garantit que :",[506,532,533,536,539],{},[370,534,535],{},"Les données sauvegardées sont chiffrées en transit.",[370,537,538],{},"Seul le serveur Woodstock peut déclencher une sauvegarde sur un périphérique enregistré.",[370,540,541],{},"Un périphérique ne peut pas usurper l'identité d'un autre.",[12,543,544,545,547],{},"À noter : ",[184,546,275],{},", qui sert l'interface Vue.js, ne dispose pas encore d'authentification. C'est prévu, mais pas encore fait (voir plus bas).",[128,549,551],{"id":550},"en-production-depuis-plus-dun-an","En production depuis plus d'un an",[133,553,555],{"id":554},"les-machines-sauvegardées","Les machines sauvegardées",[12,557,558],{},"Voici un tableau récapitulatif de mes neuf machines sauvegardées au 26 avril 2026 :",[22,560,561,582],{},[25,562,563],{},[28,564,565,568,570,573,576,579],{},[31,566,567],{},"Machine",[31,569,266],{},[31,571,572],{},"Sauvegardes",[31,574,575],{},"Fichiers",[31,577,578],{},"Taille brute",[31,580,581],{},"Compressé",[41,583,584,606,628,650,672,694,716,737,759],{},[28,585,586,591,594,597,600,603],{},[46,587,588],{},[184,589,590],{},"localhost",[46,592,593],{},"NAS local (Debian)",[46,595,596],{},"43",[46,598,599],{},"13 346",[46,601,602],{},"1,3 Go",[46,604,605],{},"0,8 Go",[28,607,608,613,616,619,622,625],{},[46,609,610],{},[184,611,612],{},"pc-eve",[46,614,615],{},"PC principal de la famille (Windows)",[46,617,618],{},"211",[46,620,621],{},"563 333",[46,623,624],{},"473 Go",[46,626,627],{},"413 Go",[28,629,630,635,638,641,644,647],{},[46,631,632],{},[184,633,634],{},"pc-m-eve",[46,636,637],{},"Ordinateur portable familial (Windows)",[46,639,640],{},"26",[46,642,643],{},"146 243",[46,645,646],{},"21,6 Go",[46,648,649],{},"13,5 Go",[28,651,652,657,660,663,666,669],{},[46,653,654],{},[184,655,656],{},"pc-m-ulrich",[46,658,659],{},"Mon portable personnel (Linux)",[46,661,662],{},"53",[46,664,665],{},"43 185",[46,667,668],{},"125 Go",[46,670,671],{},"64,5 Go",[28,673,674,679,682,685,688,691],{},[46,675,676],{},[184,677,678],{},"pc-ulrich",[46,680,681],{},"Mon PC de bureau (Linux)",[46,683,684],{},"322",[46,686,687],{},"1 385 001",[46,689,690],{},"108 Go",[46,692,693],{},"76,7 Go",[28,695,696,701,704,707,710,713],{},[46,697,698],{},[184,699,700],{},"pc-alex-linux",[46,702,703],{},"PC Linux secondaire",[46,705,706],{},"4",[46,708,709],{},"210 006",[46,711,712],{},"25 Go",[46,714,715],{},"14,5 Go",[28,717,718,723,726,729,732,735],{},[46,719,720],{},[184,721,722],{},"pc-alex-windows",[46,724,725],{},"PC Windows secondaire",[46,727,728],{},"360",[46,730,731],{},"429 368",[46,733,734],{},"147 Go",[46,736,690],{},[28,738,739,744,747,750,753,756],{},[46,740,741],{},[184,742,743],{},"server",[46,745,746],{},"Serveur dédié OVH principal",[46,748,749],{},"450",[46,751,752],{},"243 074",[46,754,755],{},"810 Go",[46,757,758],{},"752 Go",[28,760,761,766,769,772,775,778],{},[46,762,763],{},[184,764,765],{},"server-ovh-6",[46,767,768],{},"Second serveur OVH",[46,770,771],{},"472",[46,773,774],{},"493 441",[46,776,777],{},"194 Go",[46,779,780],{},"173 Go",[12,782,783],{},"Quelques observations :",[506,785,786,798,805],{},[370,787,788,792,793,797],{},[155,789,790],{},[184,791,743],{}," et ",[155,794,795],{},[184,796,765],{}," ont le plus grand nombre de sauvegardes (450 et 472). Ce sont des serveurs qui\ntournent 24\u002F7, avec des données critiques.",[370,799,800,804],{},[155,801,802],{},[184,803,678],{}," cumule plus de 1,3 million de fichiers sauvegardés, ce qui en fait la machine avec le plus grand\nnombre de fichiers individuels. Mon répertoire de développement est manifestement très fragmenté. 😄",[370,806,807,808,812,813,817],{},"La compression Zstd est particulièrement efficace sur ",[155,809,810],{},[184,811,656],{}," (49 % d'économie) et beaucoup moins\nsur ",[155,814,815],{},[184,816,743],{}," (7 %), ce qui s'explique par la nature des données stockées : données de développement vs.\ndonnées déjà compressées (archives, images Docker, etc.).",[133,819,821],{"id":820},"le-pool-global","Le pool global",[12,823,824],{},"Le pool CAS central agrège toutes les sauvegardes :",[22,826,827,837],{},[25,828,829],{},[28,830,831,834],{},[31,832,833],{},"Statistique",[31,835,836],{},"Valeur",[41,838,839,847,855,863,871],{},[28,840,841,844],{},[46,842,843],{},"Chunks uniques",[46,845,846],{},"3 365 564",[28,848,849,852],{},[46,850,851],{},"Références totales",[46,853,854],{},"60 888 193",[28,856,857,860],{},[46,858,859],{},"Espace pool compressé",[46,861,862],{},"1,95 To",[28,864,865,868],{},[46,866,867],{},"Espace disque total",[46,869,870],{},"9,6 To",[28,872,873,876],{},[46,874,875],{},"Espace disque utilisé",[46,877,878],{},"6,4 To",[12,880,881],{},"Le ratio références\u002Fchunks (60,9 M \u002F 3,4 M ≈ 18x) illustre l'efficacité de la déduplication : chaque chunk unique est\nen moyenne référencé 18 fois par différentes sauvegardes. L'historique long des sauvegardes explique cette valeur\nélevée.",[133,883,885],{"id":884},"interface-web","Interface web",[12,887,888],{},"L'interface web Vue.js 3 \u002F Vuetify permet de visualiser l'état de l'infrastructure, du pool, et de l'historique des\nsauvegardes de chaque machine.",[12,890,891],{},[121,892],{"alt":893,"src":894,"className":895},"Page Devices - liste des machines sauvegardées","\u002FWoodstock\u002Fv2_devices.png",[126],[12,897,898],{},"La page principale liste les neuf machines avec leur état, le nombre de sauvegardes, la taille brute et compressée.",[12,900,901],{},[121,902],{"alt":903,"src":904,"className":905},"Page Pool - statistiques du pool CAS","\u002FWoodstock\u002Fv2_pool.png",[126],[12,907,908],{},"La page pool affiche les statistiques globales du stockage : occupation disque, nombre de chunks, nombre de\nréférences, et l'évolution depuis le mois précédent.",[12,910,911],{},[121,912],{"alt":913,"src":914,"className":915},"Page Backups - détail des sauvegardes d'une machine","\u002FWoodstock\u002Fv2_host_detail.png",[126],[12,917,918],{},"La page de détail d'une machine liste l'historique complet de ses sauvegardes avec la date, la durée, et la liste des\npartitions sauvegardées.",[12,920,921],{},[121,922],{"alt":923,"src":924,"className":925},"Liste des sauvegardes avec état, durée et politique de rétention","\u002FWoodstock\u002Fv2_backup_list.png",[126],[12,927,928,929,932,933,932,936,939,940,179],{},"Chaque entrée de la liste indique le numéro de sauvegarde, la date de démarrage, la durée, le nombre de fichiers\ntotaux et nouveaux, les fichiers modifiés et supprimés, le compteur d'erreurs, et la politique de rétention\nappliquée : ",[155,930,931],{},"Horaire",", ",[155,934,935],{},"Quotidien",[155,937,938],{},"Hebdo"," ou ",[155,941,942],{},"Mensuel",[12,944,945],{},[121,946],{"alt":947,"src":948,"className":949},"Navigation dans les fichiers d'une sauvegarde","\u002FWoodstock\u002Fv2_backup_browse.png",[126],[12,951,952],{},"En cliquant sur une sauvegarde, on accède à la vue détaillée : statistiques complètes (fichiers, tailles, durée,\nvitesse), partitions sauvegardées avec leur type (Btrfs ou VSS), et un explorateur de fichiers permettant de\nnaviguer dans l'arborescence et de télécharger ou restaurer individuellement n'importe quel fichier.",[128,954,956],{"id":955},"perspectives","Perspectives",[133,958,960],{"id":959},"archivage-hors-site-sur-disque-usb","Archivage hors-site sur disque USB",[12,962,963],{},"Une fonctionnalité que je veux mettre en place depuis la v1 est l'archivage de la dernière version des sauvegardes\nsur un disque dur USB, qui est ensuite stocké hors-site. L'idée est d'avoir une copie physique des données dans un\nautre lieu en cas de sinistre (incendie, vol, etc.).",[12,965,966,967,179],{},"Dans la version 1 avec BackupPC, j'avais un script qui utilisait le connecteur FUSE de BackupPC pour monter les\nsauvegardes et les synchroniser avec rsync vers le disque USB. Ce script posait des problèmes avec les gros fichiers\net les permissions Windows, comme je l'avais mentionné ",[49,968,969],{"href":51},"dans le tout premier article",[12,971,972,973,976,977,980,981,984],{},"Dans Woodstock v2, l'outil en ligne de commande ",[184,974,975],{},"ws_console"," dispose d'une commande ",[184,978,979],{},"mount"," qui permet de monter\nune sauvegarde comme un système de fichiers FUSE. Il sera donc possible de faire un ",[184,982,983],{},"rsync"," depuis ce point de\nmontage vers le disque USB, avec une gestion correcte des permissions et des gros fichiers.",[12,986,987],{},"Ce n'est pas encore automatisé, mais c'est la prochaine chose que je veux mettre en place.",[133,989,991],{"id":990},"ajout-dun-format-de-stockage","Ajout d'un format de stockage",[12,993,994],{},"J'envisage également de pouvoir stocker directement le pool sur un bucket S3 ou compatible (SeaweedFS, RustFS). Avant de me lancer dans cette implémentation, je veux d'abord tester\nles performances d'un tel choix.",[128,996,998],{"id":997},"comparaison-avec-les-solutions-existantes","Comparaison avec les solutions existantes",[12,1000,1001],{},"Avant de conclure, voici une comparaison honnête avec les alternatives réalistes. Si un autre outil correspond mieux à vos besoins, utilisez-le. Sans rancune.",[12,1003,1004,1005,932,1008,932,1011,932,1014,792,1017,1020],{},"Les outils couverts : ",[155,1006,1007],{},"Restic",[155,1009,1010],{},"BorgBackup",[155,1012,1013],{},"BackupPC",[155,1015,1016],{},"URBackup",[155,1018,1019],{},"Kopia",". Ils représentent les principales solutions open-source pour une infrastructure self-hostée multi-machines — ce qui correspond grosso modo au problème que Woodstock cherche à résoudre.",[22,1022,1023,1043],{},[25,1024,1025],{},[28,1026,1027,1030,1033,1035,1037,1039,1041],{},[31,1028,1029],{},"Critère",[31,1031,1032],{},"Woodstock v2",[31,1034,1007],{},[31,1036,1010],{},[31,1038,1013],{},[31,1040,1016],{},[31,1042,1019],{},[41,1044,1045,1069,1094,1117,1142,1166,1190,1214,1238,1261,1285,1309,1333,1358,1383,1408,1427,1447,1469,1490,1510,1530,1549],{},[28,1046,1047,1052,1055,1058,1061,1064,1067],{},[46,1048,1049],{},[155,1050,1051],{},"Langage",[46,1053,1054],{},"Rust",[46,1056,1057],{},"Go",[46,1059,1060],{},"Python + C",[46,1062,1063],{},"Perl",[46,1065,1066],{},"C++",[46,1068,1057],{},[28,1070,1071,1076,1079,1082,1085,1088,1091],{},[46,1072,1073],{},[155,1074,1075],{},"Licence",[46,1077,1078],{},"MIT",[46,1080,1081],{},"BSD-2",[46,1083,1084],{},"BSD",[46,1086,1087],{},"GPL v2+",[46,1089,1090],{},"AGPLv3+",[46,1092,1093],{},"Apache 2.0",[28,1095,1096,1101,1104,1107,1110,1113,1115],{},[46,1097,1098],{},[155,1099,1100],{},"Développement actif",[46,1102,1103],{},"✅ 2026",[46,1105,1106],{},"✅ 2025",[46,1108,1109],{},"✅ 2024",[46,1111,1112],{},"❌ 2020¹",[46,1114,1103],{},[46,1116,1106],{},[28,1118,1119,1124,1127,1130,1133,1136,1139],{},[46,1120,1121],{},[155,1122,1123],{},"Modèle de synchronisation",[46,1125,1126],{},"Pull (initié par le serveur)",[46,1128,1129],{},"Push (le client pousse)",[46,1131,1132],{},"Push (SSH ou local)",[46,1134,1135],{},"Pull (rsync\u002Ftar\u002FSMB)",[46,1137,1138],{},"Pull-like (découverte LAN)",[46,1140,1141],{},"Push \u002F mode serveur",[28,1143,1144,1149,1152,1155,1158,1161,1163],{},[46,1145,1146],{},[155,1147,1148],{},"Agent requis sur le client",[46,1150,1151],{},"✅ daemon",[46,1153,1154],{},"❌ binaire unique",[46,1156,1157],{},"❌ accès SSH",[46,1159,1160],{},"❌ rsync\u002FSMB",[46,1162,1151],{},[46,1164,1165],{},"❌ \u002F ✅ serveur optionnel",[28,1167,1168,1173,1176,1179,1182,1185,1187],{},[46,1169,1170],{},[155,1171,1172],{},"Planificateur intégré",[46,1174,1175],{},"✅ (Apalis + Redis)",[46,1177,1178],{},"❌ (cron\u002Fsystemd)",[46,1180,1181],{},"❌ (cron\u002FBorgmatic)",[46,1183,1184],{},"✅",[46,1186,1184],{},[46,1188,1189],{},"✅ (mode serveur)",[28,1191,1192,1197,1200,1203,1206,1209,1211],{},[46,1193,1194],{},[155,1195,1196],{},"Backends cloud\u002Fdistants",[46,1198,1199],{},"❌ self-hosted uniquement",[46,1201,1202],{},"✅ S3, B2, SFTP, Azure, GCS, rclone…",[46,1204,1205],{},"⚠️ SSH \u002F BorgBase",[46,1207,1208],{},"❌ disque local uniquement",[46,1210,1199],{},[46,1212,1213],{},"✅ S3, Azure, GCS, B2, SFTP, rclone…",[28,1215,1216,1221,1224,1227,1230,1233,1236],{},[46,1217,1218],{},[155,1219,1220],{},"Format de stockage",[46,1222,1223],{},"Pool CAS, fichiers sur disque",[46,1225,1226],{},"Pack files CAS",[46,1228,1229],{},"Journal de segments + index",[46,1231,1232],{},"Pool MD5 (niveau fichier) + reverse deltas",[46,1234,1235],{},"Snapshots de fichiers + images de blocs",[46,1237,1226],{},[28,1239,1240,1245,1248,1250,1253,1256,1259],{},[46,1241,1242],{},[155,1243,1244],{},"Granularité de déduplication",[46,1246,1247],{},"Chunk (CDC)",[46,1249,1247],{},[46,1251,1252],{},"Chunk (BUZHASH CDC)",[46,1254,1255],{},"Fichier (MD5 fichier complet, sans hardlinks en v4)",[46,1257,1258],{},"Fichier",[46,1260,1247],{},[28,1262,1263,1268,1271,1274,1277,1280,1283],{},[46,1264,1265],{},[155,1266,1267],{},"Dédup cross-machines",[46,1269,1270],{},"✅ (un pool partagé)",[46,1272,1273],{},"⚠️ uniquement si dépôt partagé",[46,1275,1276],{},"⚠️ uniquement au sein d'un dépôt",[46,1278,1279],{},"✅ (pool MD5 partagé)",[46,1281,1282],{},"✅ (niveau fichier)",[46,1284,1276],{},[28,1286,1287,1292,1294,1297,1300,1303,1306],{},[46,1288,1289],{},[155,1290,1291],{},"Compression",[46,1293,392],{},[46,1295,1296],{},"Zstd (depuis 0.14)",[46,1298,1299],{},"lz4, zstd, zlib, lzma",[46,1301,1302],{},"gzip \u002F bzip2",[46,1304,1305],{},"lzo \u002F zstd (optionnel)",[46,1307,1308],{},"zstd, lz4, gzip…",[28,1310,1311,1316,1319,1322,1325,1327,1330],{},[46,1312,1313],{},[155,1314,1315],{},"Chiffrement au repos",[46,1317,1318],{},"❌ pool en clair",[46,1320,1321],{},"✅ AES-256-CTR + Poly1305 (obligatoire)",[46,1323,1324],{},"✅ AES-256-CTR + HMAC (optionnel)",[46,1326,1318],{},[46,1328,1329],{},"⚠️ optionnel",[46,1331,1332],{},"✅ AES-256-GCM ou ChaCha20 (optionnel)",[28,1334,1335,1340,1343,1346,1349,1352,1355],{},[46,1336,1337],{},[155,1338,1339],{},"Chiffrement en transit",[46,1341,1342],{},"✅ mTLS (gRPC + REST)",[46,1344,1345],{},"⚠️ dépend du backend",[46,1347,1348],{},"⚠️ SSH ou local",[46,1350,1351],{},"⚠️ SSH ou SMB (optionnel)",[46,1353,1354],{},"⚠️ TLS optionnel",[46,1356,1357],{},"⚠️ TLS en mode serveur (optionnel)",[28,1359,1360,1365,1368,1371,1374,1377,1380],{},[46,1361,1362],{},[155,1363,1364],{},"Vérification d'intégrité",[46,1366,1367],{},"Hash Blake3 par chunk",[46,1369,1370],{},"SHA-256 par blob",[46,1372,1373],{},"HMAC-SHA256 \u002F BLAKE2b",[46,1375,1376],{},"MD5 fichier complet (v4+)",[46,1378,1379],{},"Hash",[46,1381,1382],{},"BLAKE2B ou SHA256",[28,1384,1385,1390,1393,1396,1399,1402,1405],{},[46,1386,1387],{},[155,1388,1389],{},"Modèle d'authentification",[46,1391,1392],{},"mTLS par appareil (CA interne)",[46,1394,1395],{},"Mot de passe + fichiers clés",[46,1397,1398],{},"Clés SSH + passphrase",[46,1400,1401],{},"Clés SSH",[46,1403,1404],{},"Certificats clients optionnels",[46,1406,1407],{},"Mot de passe + TLS optionnel",[28,1409,1410,1415,1417,1419,1421,1423,1425],{},[46,1411,1412],{},[155,1413,1414],{},"Linux",[46,1416,1184],{},[46,1418,1184],{},[46,1420,1184],{},[46,1422,1184],{},[46,1424,1184],{},[46,1426,1184],{},[28,1428,1429,1434,1437,1439,1441,1443,1445],{},[46,1430,1431],{},[155,1432,1433],{},"macOS",[46,1435,1436],{},"❌²",[46,1438,1184],{},[46,1440,1184],{},[46,1442,1184],{},[46,1444,1184],{},[46,1446,1184],{},[28,1448,1449,1454,1457,1459,1462,1465,1467],{},[46,1450,1451],{},[155,1452,1453],{},"Windows (natif)",[46,1455,1456],{},"✅ (binaire MSVC)",[46,1458,1184],{},[46,1460,1461],{},"❌ (WSL2 uniquement)",[46,1463,1464],{},"⚠️ (rsync\u002FCygwin ou SMB)",[46,1466,1184],{},[46,1468,1184],{},[28,1470,1471,1476,1478,1481,1484,1486,1488],{},[46,1472,1473],{},[155,1474,1475],{},"Snapshots VSS (Windows)",[46,1477,1184],{},[46,1479,1480],{},"✅ (depuis 0.12)",[46,1482,1483],{},"❌",[46,1485,1483],{},[46,1487,1184],{},[46,1489,1184],{},[28,1491,1492,1497,1499,1502,1504,1506,1508],{},[46,1493,1494],{},[155,1495,1496],{},"Snapshots Btrfs (Linux)",[46,1498,1184],{},[46,1500,1501],{},"❌ (hooks manuels)",[46,1503,1501],{},[46,1505,1483],{},[46,1507,1483],{},[46,1509,1483],{},[28,1511,1512,1517,1519,1521,1523,1525,1528],{},[46,1513,1514],{},[155,1515,1516],{},"Sauvegarde image \u002F bare-metal",[46,1518,1483],{},[46,1520,1483],{},[46,1522,1483],{},[46,1524,1483],{},[46,1526,1527],{},"✅ (Windows + Linux)",[46,1529,1483],{},[28,1531,1532,1537,1539,1541,1543,1545,1547],{},[46,1533,1534],{},[155,1535,1536],{},"Montage FUSE",[46,1538,1184],{},[46,1540,1184],{},[46,1542,1184],{},[46,1544,1483],{},[46,1546,1483],{},[46,1548,1184],{},[28,1550,1551,1555,1558,1561,1564,1567,1569],{},[46,1552,1553],{},[155,1554,885],{},[46,1556,1557],{},"✅ (sans auth pour l'instant)",[46,1559,1560],{},"❌ (tiers : Restic Browser)",[46,1562,1563],{},"❌ (tiers : Vorta)",[46,1565,1566],{},"✅ (CGI\u002FPerl)",[46,1568,1184],{},[46,1570,1571],{},"✅ (KopiaUI)",[12,1573,1574],{},"¹ La dernière version de BackupPC date de juin 2020. La base de code est stable mais n'est plus maintenue.",[12,1576,1577],{},"² La prise en charge de macOS n'est pas encore implémentée dans Woodstock.",[12,1579,1580],{},"Quelques points à souligner :",[12,1582,1583,1584,1586],{},"Le ",[155,1585,236],{}," (Woodstock, BackupPC, URBackup) signifie qu'un client compromis ne peut pas écrire de données arbitraires sur le serveur de sauvegarde. Le modèle push (Restic, Borg, Kopia) est plus simple à mettre en place, mais accorde davantage de confiance à chaque client.",[12,1588,1589,1590,1593],{},"La ",[155,1591,1592],{},"déduplication au niveau des chunks"," signifie que si un fichier de 10 Go est modifié d'1 Ko, seul cet 1 Ko est transféré et stocké. La déduplication au niveau fichier (BackupPC, URBackup) signifie que si un fichier change, le nouveau fichier entier est stocké dans le pool. Pour BackupPC spécifiquement, rsync gère le transfert de manière efficiente — seuls les blocs modifiés transitent sur le réseau — mais comme la correspondance dans le pool est basée sur un MD5 du fichier complet, un fichier modifié génère toujours une nouvelle entrée complète dans le pool. Pour les gros fichiers mutables — bases de données, disques de machines virtuelles, fichiers PST Outlook — la différence d'efficacité de stockage est considérable.",[12,1595,1589,1596,1599],{},[155,1597,1598],{},"déduplication cross-machines"," est moins courante. Si dix machines possèdent chacune une copie du même installeur de 500 Mo, Woodstock ne le stocke qu'une seule fois. Restic et Borg ne dédupliquent qu'au sein d'un même dépôt — pour obtenir une déduplication cross-machines, il faudrait mettre toutes les machines dans le même dépôt, ce qui crée des problèmes de contention de verrous et de contrôle d'accès.",[12,1601,1602,1603,1606],{},"La principale faiblesse de Woodstock : ",[155,1604,1605],{},"le pool n'est pas chiffré au repos",". Si quelqu'un obtient un accès physique ou système à votre NAS, il peut lire vos données de sauvegarde directement. L'approche de Restic — tout chiffrer, de manière obligatoire — est clairement plus solide si quelqu'un met la main sur votre disque. Sur un NAS de LAN privé que vous contrôlez physiquement, c'est un compromis que j'accepte. Pour un stockage distant ou dans le cloud, c'est une vraie limitation. Il est possible de contrer ce problème en chiffrant le disque lui-même (LUKS, BitLocker, etc.), mais c'est une couche supplémentaire à gérer.",[12,1608,1609,1610,1613,1614,1617],{},"Si vous avez besoin d'une ",[155,1611,1612],{},"restauration bare-metal",", URBackup est le seul outil de cette liste qui le fait nativement. Si vous avez besoin de ",[155,1615,1616],{},"BorgBackup sur Windows",", c'est impossible sans WSL2.",[128,1619,1621],{"id":1620},"conclusion","Conclusion",[12,1623,1624],{},"Ce projet m'a appris énormément de choses : Rust, gRPC, mTLS, la gestion de pools CAS, le VSS Windows, les snapshots\nBtrfs, la gestion de jobs distribués avec Redis, et probablement une dizaine d'autres sujets que j'ai oubliés depuis.",[12,1626,1627],{},"Est-ce que c'était raisonnable de passer six ans à construire son propre logiciel de sauvegarde alors qu'il en existe\ndes dizaines ? Bien sur que oui 😄.",[12,1629,1630,1631,1636,1637,179],{},"Le logiciel est disponible sur ",[49,1632,1635],{"href":1633,"rel":1634},"https:\u002F\u002Fgogs.shadoware.org\u002FShadowareOrg\u002Fwoodstock-backup",[347],"mon instance Gitea"," et la\ndocumentation est sur ",[49,1638,1641],{"href":1639,"rel":1640},"https:\u002F\u002Fwoodstock.shadoware.org",[347],"woodstock.shadoware.org",[12,1643,1644],{},"À bientôt !",{"title":1646,"searchDepth":1647,"depth":1647,"links":1648},"",2,[1649,1658,1665,1671,1676,1680,1681],{"id":130,"depth":1647,"text":131,"children":1650},[1651,1653,1654,1655,1656,1657],{"id":135,"depth":1652,"text":136},3,{"id":149,"depth":1652,"text":150},{"id":171,"depth":1652,"text":172},{"id":193,"depth":1652,"text":194},{"id":204,"depth":1652,"text":205},{"id":214,"depth":1652,"text":215},{"id":225,"depth":1647,"text":226,"children":1659},[1660,1661,1662,1663,1664],{"id":229,"depth":1652,"text":230},{"id":247,"depth":1652,"text":248},{"id":356,"depth":1652,"text":357},{"id":406,"depth":1652,"text":407},{"id":431,"depth":1652,"text":432},{"id":441,"depth":1647,"text":442,"children":1666},[1667,1668,1669,1670],{"id":445,"depth":1652,"text":446},{"id":467,"depth":1652,"text":468},{"id":474,"depth":1652,"text":475},{"id":490,"depth":1652,"text":491},{"id":550,"depth":1647,"text":551,"children":1672},[1673,1674,1675],{"id":554,"depth":1652,"text":555},{"id":820,"depth":1652,"text":821},{"id":884,"depth":1652,"text":885},{"id":955,"depth":1647,"text":956,"children":1677},[1678,1679],{"id":959,"depth":1652,"text":960},{"id":990,"depth":1652,"text":991},{"id":997,"depth":1647,"text":998},{"id":1620,"depth":1647,"text":1621},"Woodstock","woodstock","2026-04-26",{"type":9,"value":1686},[1687,1689,1691,1693,1757,1759],[12,1688,14],{},[12,1690,17],{},[12,1692,20],{},[22,1694,1695,1705],{},[25,1696,1697],{},[28,1698,1699,1701,1703],{},[31,1700,33],{},[31,1702,36],{},[31,1704,39],{},[41,1706,1707,1717,1727,1737,1747],{},[28,1708,1709,1713,1715],{},[46,1710,1711],{},[49,1712,52],{"href":51},[46,1714,55],{},[46,1716,58],{},[28,1718,1719,1723,1725],{},[46,1720,1721],{},[49,1722,66],{"href":65},[46,1724,69],{},[46,1726,72],{},[28,1728,1729,1733,1735],{},[46,1730,1731],{},[49,1732,80],{"href":79},[46,1734,83],{},[46,1736,86],{},[28,1738,1739,1743,1745],{},[46,1740,1741],{},[49,1742,94],{"href":93},[46,1744,97],{},[46,1746,100],{},[28,1748,1749,1753,1755],{},[46,1750,1751],{},[49,1752,108],{"href":107},[46,1754,111],{},[46,1756,114],{},[12,1758,117],{},[12,1760,1761],{},[121,1762],{"alt":123,"src":124,"className":1763},[126],"md","Lille, France",{"planet":1767},true,"\u002Fpost\u002Fwoodstock_v2",{"title":6,"description":14},"woodstock_v2","posts\u002FWoodstock\u002F2026-04-26_woodstock_v2",[1683,1773,1774,1775,1776],"backup","sauvegarde","rust","grpc",22,"gu0ISxCDyWPA2zrYBtgiI1k0uw4o8BK0cd-5g09z838",{"id":1780,"title":1781,"author":7,"body":1782,"category":2057,"categorySlug":2058,"date":2059,"description":14,"excerpt":2060,"extension":1764,"location":1765,"meta":2074,"navigation":1767,"path":2075,"published":1767,"seo":2076,"slug":2077,"stem":2078,"tags":2079,"timeToRead":2085,"__hash__":2086},"posts\u002Fposts\u002FProgrammation\u002F2026-01-29-sportequipement-vibe-coding.md","SportEquipement - Application de suivi de matériel sportif avec Vibe Coding",{"type":9,"value":1783,"toc":2049},[1784,1786,1789,1792,1795,1801,1805,1808,1811,1825,1829,1832,1839,1842,1845,1852,1855,1858,1865,1875,1879,1882,1885,1899,1902,1925,1928,1931,1938,1941,1944,1947,1950,1953,1956,1960,1963,1966,1969,1974,1977,1980,1983,1986,1990,1993,1996,1999,2002,2009,2011,2014,2017,2020,2027,2034,2037,2040,2043,2046],[12,1785,14],{},[12,1787,1788],{},"Pour ceux qui me connaissent, vous savez que je fais un peu de vélo. Et comme tout cycliste moderne, j'utilise Strava.\nC'est l'outil de référence pour suivre ses sorties, comparer ses segments et analyser sa forme. Strava propose bien une\nfonctionnalité de \"Suivi d'équipement\" (pour savoir combien de kilomètres ont vos chaussures ou votre cadre), mais...\nc'est sommaire.",[12,1790,1791],{},"Si la version Web permet d'ajouter des composants (chaîne, cassette, etc.), l'application mobile a tout simplement\n\"oublié\" cette fonctionnalité.",[12,1793,1794],{},"J'ai eu quelques problèmes avec ma chaîne qui s'use en 2 000 km ou des plaquettes de frein à changer. Bref j'avais envie\nd'avoir des stats un peu précises sur le suivi de mon matériel. De la même manière je cours régulièrement et je voulais\nsavoir quand mes baskets atteignaient leur durée de vie maximale (ça, ça marche bien sur Strava).",[12,1796,1797,1798,179],{},"Pour améliorer ce suivi j'ai décidé de créer ma propre solution : ",[155,1799,1800],{},"SportEquipement",[128,1802,1804],{"id":1803},"sportequipement-cest-quoi","SportEquipement : C'est quoi ?",[12,1806,1807],{},"L'idée est simple : une application mobile, connectée à Strava et à Android Health Connect, qui récupère mes activités\net incrémente l'usure de chaque composant défini.",[12,1809,1810],{},"Pour la première version, je voulais aussi qu'elle soit \"Local First\". L'application tourne entièrement sur le téléphone.\nPas de serveur, application open source, et mes données restent chez moi. (Bon, j'ajouterai peut-être dans un futur un\nserveur pour synchroniser mes différents appareils et sécuriser les tokens Strava, mais chaque chose en son temps).",[12,1812,1813,1814,1819,1820,179],{},"L'application est d'ailleurs disponible sur le ",[49,1815,1818],{"href":1816,"rel":1817},"https:\u002F\u002Fplay.google.com\u002Fstore\u002Fapps\u002Fdetails?id=org.shadoware.sportequipment&hl=fr",[347],"Play Store"," et le code source est sur mon ",[49,1821,1824],{"href":1822,"rel":1823},"https:\u002F\u002Fgogs.shadoware.org\u002FShadowareOrg\u002FSportsEquipment",[347],"Gitea perso",[128,1826,1828],{"id":1827},"quelques-écrans","Quelques écrans",[12,1830,1831],{},"Cet écran montre la liste des équipements qui ont été ajoutés. On peut voir l'état d'usure de chaque équipement et si\nl'équipement est usé ou un de ces composants est usé on a une alerte visuelle.",[12,1833,1834],{},[121,1835],{"alt":1836,"className":1837,"src":1838},"Liste des équipements",[126],"\u002FProgrammation\u002Fsportequipement\u002Fimage1.png",[12,1840,1841],{},"Sur l'écran détail d'un équipement, on y voit les informations détaillées de l'équipement mais aussi la liste des\ncomposants et l'usure des composants.",[12,1843,1844],{},"Un composant peut être usé par le kilométrage (ex: chaîne, pneu) ou par le temps (ex: liquide de frein).",[12,1846,1847],{},[121,1848],{"alt":1849,"className":1850,"src":1851},"Liste des composants \u002F Détail",[126],"\u002FProgrammation\u002Fsportequipement\u002Fimage2.png",[12,1853,1854],{},"Sur l'écran d'activité on récupère la liste des activités synchronisées depuis Strava ou Health Connect (pour l'instant\nune fois par jour). On peut créer des activités manuellement aussi si besoin. C'est l'ajout ou la suppression d'une activité\nqui impacte l'usure des équipements.",[12,1856,1857],{},"Les activités sont rattachées aux équipements via des types d'activités (l'équipement est attaché à un type d'activité,\net l'activité aussi).",[12,1859,1860],{},[121,1861],{"alt":1862,"className":1863,"src":1864},"Liste des activités",[126],"\u002FProgrammation\u002Fsportequipement\u002Fimage3.png",[12,1866,1867,1868,1871,1872,179],{},"Mais si je vous écris aujourd'hui, ce n'est pas seulement pour vous présenter l'outil. C'est surtout pour vous raconter\n",[362,1869,1870],{},"comment"," je l'ai développé. J'ai voulu tester le fameux ",[155,1873,1874],{},"\"Vibe Coding\"",[128,1876,1878],{"id":1877},"développement-en-vibe-coding","Développement en Vibe Coding",[12,1880,1881],{},"Je souhaitais développer mon application rapidement. Je voulais également avoir ce que les agents IA avaient dans leurs\nneurones. En effet, j'ai une licence Github Copilot que j'utilisais principalement en mode auto-completion de code, un\npeu pour discuter, et parfois pour modifier du code sur un projet déjà existant. J'ai déjà \"Vibe Codé\" des scripts sans\nimpact dont le but était de faire des exécutions one-shot.",[12,1883,1884],{},"Sur ce projet, je me suis dit :",[1886,1887,1888,1896],"blockquote",{},[12,1889,1890,1891,1895],{},"Je ne suis plus développeur, je suis le chef de projet et le product owner. Je décris la fonctionnalité que je veux, et\nl'IA code pour moi. Je teste et si ça ne compile pas ou si ça ne marche pas comme je veux je fais un retour ",[1892,1893,1894],"del",{},"au\ndeveloppeur"," à l'IA.",[12,1897,1898],{},"A la fin je regarderai le code généré et je verrai ce que l'IA vaut.",[12,1900,1901],{},"J'ai donc commencé à mettre différents types d'instructions :",[506,1903,1904,1911,1918],{},[370,1905,1906],{},[49,1907,1910],{"href":1908,"rel":1909},"https:\u002F\u002Fgogs.shadoware.org\u002FShadowareOrg\u002FSportsEquipment\u002Fsrc\u002Fbranch\u002Fmain\u002F.github\u002Fcopilot-instructions.md",[347],"Expert en Kotlin et développement Android",[370,1912,1913],{},[49,1914,1917],{"href":1915,"rel":1916},"https:\u002F\u002Fgogs.shadoware.org\u002FShadowareOrg\u002FSportsEquipment\u002Fsrc\u002Fbranch\u002Fmain\u002F.github\u002Fprompts\u002Fui_ux.prompt.md",[347],"Agent expert en UI\u002FUX",[370,1919,1920],{},[49,1921,1924],{"href":1922,"rel":1923},"https:\u002F\u002Fgogs.shadoware.org\u002FShadowareOrg\u002FSportsEquipment\u002Fsrc\u002Fbranch\u002Fmain\u002F.github\u002Fprompts\u002Fdocumentation.prompt.md",[347],"Expert en documentation technique",[12,1926,1927],{},"Ces différents experts sont inspirés de prompts trouvés sur Internet et adaptés, modifiés et\u002Fou générés à l'aide de Github\nCopilot également.",[12,1929,1930],{},"Ensuite j'ai décrit mon projet en quelques lignes dans le prompt. Je lui ai demandé de me faire un plan, puis de le\ndévelopper.",[12,1932,1933,1934,1937],{},"Les deux premières semaines ont été grisantes. J'ai développé ",[155,1935,1936],{},"80% du logiciel"," en un temps record. Je regardais l'IA\ncoder, je cliquais sur le bouton continuer ou approuver (les outils MCP, le build, ...) et je jouais aux jeux vidéo pendant ce\ntemps.",[12,1939,1940],{},"Pour info les MCP c'est \"Model Context Protocol\", cela permet à Github Copilot d'accéder à des outils comme\nl'execution des commandes, la recherche sur internet, ...",[12,1942,1943],{},"Ma routine était simple : je demandais une fonctionnalité, l'IA générait le code, je compilais (pendant ce temps je\nfaisais autre chose : jeux vidéos, un autre programme ...). Une fois terminé, je lançais android studio, je compilais,\nje testais.",[12,1945,1946],{},"Si ça marchait, je passais à la fonctionnalité suivante. Si ça ne marchait pas, je donnais un retour à l'IA en lui\nexpliquant le problème (erreur de compilation, bug, comportement non conforme) et c'était reparti pour un tour.",[12,1948,1949],{},"Régulièrement je devais réinitialiser la conversation, je demandais donc à l'IA de mettre à jour le plan pour reprendre\ndans une nouvelle conversation. Généralement, je faisais cela quand l'IA commençait à faire n'importe quoi (oubli de ce\nsur quoi on travaillait initialement, pataugeait dans la semoule, ...). Les prémisses du pétage de plombs de l'IA passaient\nsouvent par un passage du français à l'anglais dans les réponses. La signification était probablement la perte du\ncontexte initial et des instructions de démarrage.",[12,1951,1952],{},"Au début, Copilot m'a pondu une architecture logicielle complexe, découpée en couches bien distinctes. Sur le papier,\nc'était beau (ça vient des instructions expert en développement kotlin qui demande une Clean architecture avec une\nséparation : Domain, Data, Feature, Core).",[12,1954,1955],{},"De mon coté, pour un projet perso, si j'avais dû développer le projet moi-même de zéro, j'aurais probablement fait\nbeaucoup plus simple et direct. Probablement une application avec des vues, une couche service et une couche DAO dans le\nmême projet. Mais là, c'était l'IA qui gérait l'architecture, donc je laissais faire. J'avançais à l'aveugle.",[128,1957,1959],{"id":1958},"et-là-cest-le-drame","Et là, c'est le drame",[12,1961,1962],{},"Au bout de deux semaines, patatras. Je reçois une notification : je n'ai plus de crédits GitHub Copilot pour les\nrequêtes premium. Panne sèche.",[12,1964,1965],{},"J'ai bien tenté de reprendre avec un GPT4.1 ou GPT4o qui sont inclus en illimité, mais quand on a testé Gemini 3 ou\nClaude Sonnet 4.... qu'est que GPT4 est con....",[12,1967,1968],{},"Je me retrouve seul face à mon IDE, sans l'aide de copilot. J'étais le 15 du mois et il me restait 2 semaines avant le\nrenouvellement. Je me suis dit :",[1886,1970,1971],{},[12,1972,1973],{},"Bon, c'est l'occasion de regarder un peu ce qu'il m'a écrit en détail et de faire une revue de code.",[12,1975,1976],{},"J'ai ouvert les fichiers. Et là... j'ai eu du sang qui m'a coulé des yeux, la cervelle qui m'est sortie par les oreilles ...",[12,1978,1979],{},"Le MCD (Modèle Conceptuel de Données) était inutilement complexe. Il y avait de la duplication de code partout. Pour\nfaire la même chose, l'IA avait parfois utilisé trois méthodes différentes à trois endroits du code, sans aucune\ncohérence globale. Il y avait du code mort, des variables inutilisées, des détours techniques incompréhensibles.",[12,1981,1982],{},"C'était fonctionnel, oui. Mais c'était sale. Très sale.",[12,1984,1985],{},"J'ai donc commencé à lister tous les problèmes architecturaux que j'ai vus dans le code (et attention il y en a\nprobablement d'autres). La suppression du code mort, je l'ai faite moi-même car à chaque fois l'IA n'était pas capable\nde le détecter si je ne lui donne pas le nom de la méthode.",[128,1987,1989],{"id":1988},"le-grand-nettoyage","Le grand nettoyage",[12,1991,1992],{},"Dès que mes crédits ont été renouvelés, j'ai voulu tout nettoyer et donner à l'IA l'ensemble de ma revue de code.\nOptimiste, j'ai donné le grand listing de tout ce qui n'allait pas et de demander à l'IA une correction globale.",[12,1994,1995],{},"Échec total. C'était trop pour elle. Elle se perdait dans les corrections, me disait que tout était fait, mais seule une\npartie était terminée....",[12,1997,1998],{},"J'ai dû changer de stratégie. J'ai repris mon \"Vibe Coding\", mais cette fois-ci pour réparer les dégâts, problème par\nproblème, couche par couche (domaine, puis data, puis feature, puis core).",[12,2000,2001],{},"Puis je suis repassé en mode petite itération pour terminer les dernières fonctionnalités, corriger les derniers bugs. Et\nj'ai parfois dû mettre les mains dans le cambouis moi-même pour corriger des problèmes que l'IA n'arrivait pas à\nrésoudre.",[12,2003,2004,2005,2008],{},"Résultat des courses : j'avais fait 80% du projet (le prototype sale) en 2 semaines. Il m'a fallu ",[155,2006,2007],{},"un mois entier","\npour faire les 20% restants et nettoyer la base de code pour la rendre un peu plus propre.",[128,2010,1621],{"id":1620},[12,2012,2013],{},"Aujourd'hui, l'application est sur le store Google, elle fonctionne bien, et elle répond à mon besoin initial.",[12,2015,2016],{},"Mais je ressors de cette expérience avec un sentiment mitigé. J'ai un étrange détachement vis-à-vis de ce projet.",[12,2018,2019],{},"J'ai l'impression d'avoir codé avec un stagiaire qui a de très bonnes connaissances théoriques sur l'architecture logicielle\nmais qui sans contrôle te crée une dette technique immense. Par contre il a l'avantage d'écrire très vite. Ce projet qui\na été fait en un mois - un mois et demi, m'aurait pris beaucoup plus de temps (sachant que je ne développe en perso que\nle soir et le week-end).",[12,2021,2022,2023,2026],{},"D'habitude, quand je code un logiciel, c'est mon \"bébé\". Je connais chaque ligne, chaque astuce, chaque défaut. Là...\nj'ai l'impression que ce n'est pas ",[155,2024,2025],{},"mon"," projet. Je me sens comme le Chef de Projet ou le client qui a passé commande.",[12,2028,2029,2030,2033],{},"Pour les petits scripts, les projets ",[155,2031,2032],{},"utilitaires",", l'intelligence artificielle est super pratique. Elle permet de\nconstruire rapidement un prototype fonctionnel. Mais sans maîtrise, le code n'est pas maintenable dans le temps (même\npar l'IA elle-même). Et je ne parle pas non plus des problématiques de sécurité, si j'avais un serveur sur cette\napplication.",[12,2035,2036],{},"Pour les projets de passionnés, ceux qu'on fait \"pour l'art\" ou pour le plaisir d'apprendre, je ne suis pas sûr de\nréitérer l'expérience aussi poussée. J'aime développer et c'est important pour moi de toucher la ligne de code.",[12,2038,2039],{},"J'ai aussi une autre inquiétude par rapport à l'avenir du métier de développeur. Un bon développeur pour l'instant est\ntoujours nécessaire pour superviser l'intelligence artificielle.",[12,2041,2042],{},"Mais les juniors qui arrivent sur le marché du travail vont-ils apprendre les bonnes pratiques de développement s'ils\nse reposent trop sur l'IA pour coder à leur place ? Est-ce que les juniors vont trouver du travail, même s'ils sont\ncompétents ?",[12,2044,2045],{},"Actuellement un développeur qui n'utilise pas l'IA risque de se retrouver à un moment dépassé sur le marché du travail.\nCe que j'aimais avec le développement c'est qu'avant n'importe qui pouvait faire du développement avec n'importe quel\nPC. Il y avait un coût d'apprentissage, tout le monde n'est pas fait pour faire du développement, mais c'était accessible\nfinancièrement aux personnes motivées.",[12,2047,2048],{},"Maintenant, il faut soit une très grosse carte graphique (et même là ce ne sont pas des modèles ultra performants), ou\nun abonnement payant. Il y a quelques versions gratuites mais elles sont là pour attirer. L'IA rend le développement plus\naccessible au niveau apprentissage, mais moins accessible financièrement et surtout dépendant d'un fournisseur.",{"title":1646,"searchDepth":1647,"depth":1647,"links":2050},[2051,2052,2053,2054,2055,2056],{"id":1803,"depth":1647,"text":1804},{"id":1827,"depth":1647,"text":1828},{"id":1877,"depth":1647,"text":1878},{"id":1958,"depth":1647,"text":1959},{"id":1988,"depth":1647,"text":1989},{"id":1620,"depth":1647,"text":1621},"Programmation","programmation","2026-01-29",{"type":9,"value":2061},[2062,2064,2066,2068,2070],[12,2063,14],{},[12,2065,1788],{},[12,2067,1791],{},[12,2069,1794],{},[12,2071,1797,2072,179],{},[155,2073,1800],{},{"planet":1767},"\u002Fpost\u002Fsportequipement-vibe-coding",{"title":1781,"description":14},"sportequipement-vibe-coding","posts\u002FProgrammation\u002F2026-01-29-sportequipement-vibe-coding",[2080,2081,2082,2083,2084],"developpement","ia","vibe coding","rex","android",10,"UjPK0msj1uJoiDCVdG4PiDDZUq_PLpB0i2SzFIOesPI",{"id":2088,"title":2089,"author":7,"body":2090,"category":2057,"categorySlug":2058,"date":3363,"description":14,"excerpt":3364,"extension":1764,"location":1765,"meta":3504,"navigation":1767,"path":3505,"published":1767,"seo":3506,"slug":3507,"stem":3508,"tags":3509,"timeToRead":2241,"__hash__":3512},"posts\u002Fposts\u002FProgrammation\u002F2024-06-14-max-old-space.md","Quelle est la valeur par défaut de max-old-space-size dans NodeJS ?",{"type":9,"value":2091,"toc":3361},[2092,2094,2101,2104,2285,2288,2294,2309,2312,2315,2351,2354,2359,2368,2371,2374,2385,2600,2609,2618,2788,2796,2938,2954,3251,3254,3280,3286,3289,3292,3295,3354,3357],[12,2093,14],{},[12,2095,2096,2097,2100],{},"Lors de nos développements en NodeJS, il arrive parfois que l'on se retrouve confronté à des erreurs. Certaines de ces\nerreurs ne se reproduisent pas en local, mais uniquement sur un environnement distant. C'est ce qui nous est arrivé\nrécemment lors de l'exécution de la commande ",[184,2098,2099],{},"npm ci"," sur la chaîne de déploiement continue.",[12,2102,2103],{},"L'erreur que nous avons rencontrée est la suivante :",[2105,2106,2110],"pre",{"className":2107,"code":2108,"language":2109,"meta":1646,"style":1646},"language-shell shiki shiki-themes one-dark-pro","\u003C--- Last few GCs --->\n\n[14040:0x56930a0]    96150 ms: Mark-Compact 2012.5 (2093.0) -> 2011.8 (2092.0) MB, 902.72 \u002F 17.13 ms  (average mu = 0.416, current mu = 0.217) allocation failure; scavenge might not succeed\n[14040:0x56930a0]    97002 ms: Mark-Compact 2019.6 (2092.0) -> 2019.1 (2112.8) MB, 843.10 \u002F 0.00 ms  (average mu = 0.251, current mu = 0.011) allocation failure; scavenge might not succeed\n\n\u003C--- JS stacktrace --->\n\nFATAL ERROR: Reached heap limit Allocation failed - JavaScript heap out of memory\n","shell",[184,2111,2112,2121,2126,2183,2228,2233,2239,2244],{"__ignoreMap":1646},[2113,2114,2117],"span",{"class":2115,"line":2116},"line",1,[2113,2118,2120],{"class":2119},"sn6KH","\u003C--- Last few GCs --->\n",[2113,2122,2123],{"class":2115,"line":1647},[2113,2124,2125],{"emptyLinePlaceholder":1767},"\n",[2113,2127,2128,2131,2135,2138,2141,2144,2147,2151,2154,2157,2160,2162,2164,2168,2171,2174,2177,2180],{"class":2115,"line":1652},[2113,2129,2130],{"class":2119},"[14040:0x56930a0]    96150 ms: Mark-Compact 2012.5 (",[2113,2132,2134],{"class":2133},"sVbv2","2093.0",[2113,2136,2137],{"class":2119},") -> 2011.8 (",[2113,2139,2140],{"class":2133},"2092.0",[2113,2142,2143],{"class":2119},") MB, 902.72 \u002F 17.13 ms  (",[2113,2145,2146],{"class":2133},"average",[2113,2148,2150],{"class":2149},"subq3"," mu",[2113,2152,2153],{"class":2149}," =",[2113,2155,2156],{"class":2149}," 0.416,",[2113,2158,2159],{"class":2149}," current",[2113,2161,2150],{"class":2149},[2113,2163,2153],{"class":2149},[2113,2165,2167],{"class":2166},"sVC51"," 0.217",[2113,2169,2170],{"class":2119},") allocation failure; ",[2113,2172,2173],{"class":2133},"scavenge",[2113,2175,2176],{"class":2149}," might",[2113,2178,2179],{"class":2149}," not",[2113,2181,2182],{"class":2149}," succeed\n",[2113,2184,2186,2189,2191,2194,2197,2200,2202,2204,2206,2209,2211,2213,2215,2218,2220,2222,2224,2226],{"class":2115,"line":2185},4,[2113,2187,2188],{"class":2119},"[14040:0x56930a0]    97002 ms: Mark-Compact 2019.6 (",[2113,2190,2140],{"class":2133},[2113,2192,2193],{"class":2119},") -> 2019.1 (",[2113,2195,2196],{"class":2133},"2112.8",[2113,2198,2199],{"class":2119},") MB, 843.10 \u002F 0.00 ms  (",[2113,2201,2146],{"class":2133},[2113,2203,2150],{"class":2149},[2113,2205,2153],{"class":2149},[2113,2207,2208],{"class":2149}," 0.251,",[2113,2210,2159],{"class":2149},[2113,2212,2150],{"class":2149},[2113,2214,2153],{"class":2149},[2113,2216,2217],{"class":2166}," 0.011",[2113,2219,2170],{"class":2119},[2113,2221,2173],{"class":2133},[2113,2223,2176],{"class":2149},[2113,2225,2179],{"class":2149},[2113,2227,2182],{"class":2149},[2113,2229,2231],{"class":2115,"line":2230},5,[2113,2232,2125],{"emptyLinePlaceholder":1767},[2113,2234,2236],{"class":2115,"line":2235},6,[2113,2237,2238],{"class":2119},"\u003C--- JS stacktrace --->\n",[2113,2240,2242],{"class":2115,"line":2241},7,[2113,2243,2125],{"emptyLinePlaceholder":1767},[2113,2245,2247,2250,2253,2256,2259,2262,2265,2268,2271,2274,2276,2279,2282],{"class":2115,"line":2246},8,[2113,2248,2249],{"class":2133},"FATAL",[2113,2251,2252],{"class":2149}," ERROR:",[2113,2254,2255],{"class":2149}," Reached",[2113,2257,2258],{"class":2149}," heap",[2113,2260,2261],{"class":2149}," limit",[2113,2263,2264],{"class":2149}," Allocation",[2113,2266,2267],{"class":2149}," failed",[2113,2269,2270],{"class":2149}," -",[2113,2272,2273],{"class":2149}," JavaScript",[2113,2275,2258],{"class":2149},[2113,2277,2278],{"class":2149}," out",[2113,2280,2281],{"class":2149}," of",[2113,2283,2284],{"class":2149}," memory\n",[12,2286,2287],{},"Cette erreur se produit lorsque la mémoire allouée pour NodeJS est insuffisante (le garbage collector de NodeJS n'arrive\npas à nettoyer la mémoire). Pour résoudre ce problème, l'une des possibilités est d'augmenter la mémoire allouée à NodeJS.",[12,2289,2290,2291,2293],{},"Une autre possibilité est de comprendre pourquoi le programme a besoin d'autant de mémoire et de le corriger, car\nle problème peut venir d'une fuite de mémoire. Dans notre cas, c'est la commande ",[184,2292,2099],{}," qui est en cause. Et la\nconsommation de mémoire dépend du nombre de paquets que nous avons en dépendance (et qui eux-mêmes en ont).",[12,2295,2296,2297,2300,2301,2304,2305,2308],{},"Pour augmenter la mémoire allouée à NodeJS, il faut ajouter l'option ",[184,2298,2299],{},"--max-old-space-size"," à la commande ",[184,2302,2303],{},"node",".\nOn peut également modifier la variable d'environnement ",[184,2306,2307],{},"NODE_OPTIONS"," pour définir la taille de la mémoire allouée à\nNodeJS.",[12,2310,2311],{},"Lors de la modification de la mémoire allouée à NodeJS, il est important de prendre en compte la mémoire disponible sur\nla machine et la mémoire utilisée par les autres processus.",[12,2313,2314],{},"Voici un exemple pour augmenter la mémoire allouée à NodeJS à 4 Go :",[2105,2316,2320],{"className":2317,"code":2318,"language":2319,"meta":1646,"style":1646},"language-bash shiki shiki-themes one-dark-pro","export NODE_OPTIONS=--max-old-space-size=4096\nnpm ci\n","bash",[184,2321,2322,2343],{"__ignoreMap":1646},[2113,2323,2324,2328,2332,2336,2338,2340],{"class":2115,"line":2116},[2113,2325,2327],{"class":2326},"seHd6","export",[2113,2329,2331],{"class":2330},"sVyAn"," NODE_OPTIONS",[2113,2333,2335],{"class":2334},"sjrmR","=",[2113,2337,2299],{"class":2330},[2113,2339,2335],{"class":2334},[2113,2341,2342],{"class":2166},"4096\n",[2113,2344,2345,2348],{"class":2115,"line":1647},[2113,2346,2347],{"class":2133},"npm",[2113,2349,2350],{"class":2149}," ci\n",[12,2352,2353],{},"Mais avant d'augmenter la mémoire allouée à NodeJS, nous allons nous poser une question :",[1886,2355,2356],{},[12,2357,2358],{},"Pourquoi ai-je le problème sur la chaîne de déploiement et pas sur mon poste en local, malgré le fait que la version\nde NodeJS soit identique ?",[12,2360,2361,2362,2367],{},"Pour répondre à cette question, nous avons commencé par une petite recherche sur le grand réseau. Nous avons trouvé un\narticle très intéressant sur le site ",[49,2363,2366],{"href":2364,"rel":2365},"https:\u002F\u002Fmedium.com\u002Fgeekculture\u002Fnode-js-default-memory-settings-3c0fe8a9ba1",[347],"Medium",".\nDans cet article, une expérimentation a été faite pour déterminer la mémoire allouée par défaut à NodeJS en fonction de\nla version de NodeJS.",[12,2369,2370],{},"Cela montre que dans la version de NodeJS 20 que nous utilisons, la limite devrait être de 4Go par défaut. Mais\npourquoi avons-nous cette erreur, sachant que dans le message d'erreur, la mémoire allouée est de 2Go ?",[12,2372,2373],{},"Pour trouver plus d'informations, nous avons dû nous plonger dans le code source de NodeJS, qui embarque lui-même le code\nsource du moteur V8. Nous allons nous concentrer sur la dernière version de NodeJS.",[12,2375,2376,2377,2380,2381,2384],{},"Pour trouver plus d'informations, nous avons essayé de rechercher dans le code les endroits qui parlent de max_old_space_size. Cette\nrecherche m'a amené à la fonction ",[184,2378,2379],{},"MaxOldGenerationSize"," dans le fichier ",[184,2382,2383],{},"deps\u002Fv8\u002Fsrc\u002Fheap\u002Fheap.cc"," :",[2105,2386,2390],{"className":2387,"code":2388,"language":2389,"meta":1646,"style":1646},"language-cpp shiki shiki-themes one-dark-pro","\u002F\u002F deps\u002Fv8\u002Fsrc\u002Fheap\u002Fheap.cc\n\n\u002F\u002F line 362\nsize_t Heap::MaxOldGenerationSize(uint64_t physical_memory) {\n  size_t max_size = V8HeapTrait::kMaxSize;\n  \u002F\u002F Increase the heap size from 2GB to 4GB for 64-bit systems with physical\n  \u002F\u002F memory at least 16GB. The theshold is set to 15GB to accomodate for some\n  \u002F\u002F memory being reserved by the hardware.\n  constexpr bool x64_bit = Heap::kHeapLimitMultiplier >= 2;\n  if (v8_flags.huge_max_old_generation_size && x64_bit &&\n      (physical_memory \u002F GB) >= 15) {\n    DCHECK_EQ(max_size \u002F GB, 2u);\n    max_size *= 2;\n  }\n  return std::min(max_size, AllocatorLimitOnMaxOldGenerationSize());\n}\n","cpp",[184,2391,2392,2398,2402,2407,2434,2447,2452,2457,2462,2488,2512,2531,2554,2567,2573,2594],{"__ignoreMap":1646},[2113,2393,2394],{"class":2115,"line":2116},[2113,2395,2397],{"class":2396},"sV9Aq","\u002F\u002F deps\u002Fv8\u002Fsrc\u002Fheap\u002Fheap.cc\n",[2113,2399,2400],{"class":2115,"line":1647},[2113,2401,2125],{"emptyLinePlaceholder":1767},[2113,2403,2404],{"class":2115,"line":1652},[2113,2405,2406],{"class":2396},"\u002F\u002F line 362\n",[2113,2408,2409,2412,2416,2419,2421,2424,2427,2431],{"class":2115,"line":2185},[2113,2410,2411],{"class":2326},"size_t",[2113,2413,2415],{"class":2414},"sU0A5"," Heap",[2113,2417,2418],{"class":2119},"::",[2113,2420,2379],{"class":2133},[2113,2422,2423],{"class":2119},"(",[2113,2425,2426],{"class":2326},"uint64_t",[2113,2428,2430],{"class":2429},"s_ZVi"," physical_memory",[2113,2432,2433],{"class":2119},") {\n",[2113,2435,2436,2439,2442,2444],{"class":2115,"line":2230},[2113,2437,2438],{"class":2326},"  size_t",[2113,2440,2441],{"class":2119}," max_size ",[2113,2443,2335],{"class":2326},[2113,2445,2446],{"class":2119}," V8HeapTrait::kMaxSize;\n",[2113,2448,2449],{"class":2115,"line":2235},[2113,2450,2451],{"class":2396},"  \u002F\u002F Increase the heap size from 2GB to 4GB for 64-bit systems with physical\n",[2113,2453,2454],{"class":2115,"line":2241},[2113,2455,2456],{"class":2396},"  \u002F\u002F memory at least 16GB. The theshold is set to 15GB to accomodate for some\n",[2113,2458,2459],{"class":2115,"line":2246},[2113,2460,2461],{"class":2396},"  \u002F\u002F memory being reserved by the hardware.\n",[2113,2463,2465,2468,2471,2474,2476,2479,2482,2485],{"class":2115,"line":2464},9,[2113,2466,2467],{"class":2326},"  constexpr",[2113,2469,2470],{"class":2326}," bool",[2113,2472,2473],{"class":2119}," x64_bit ",[2113,2475,2335],{"class":2326},[2113,2477,2478],{"class":2119}," Heap::kHeapLimitMultiplier ",[2113,2480,2481],{"class":2326},">=",[2113,2483,2484],{"class":2166}," 2",[2113,2486,2487],{"class":2119},";\n",[2113,2489,2490,2493,2496,2499,2501,2504,2507,2509],{"class":2115,"line":2085},[2113,2491,2492],{"class":2326},"  if",[2113,2494,2495],{"class":2119}," (",[2113,2497,2498],{"class":2414},"v8_flags",[2113,2500,179],{"class":2119},[2113,2502,2503],{"class":2330},"huge_max_old_generation_size",[2113,2505,2506],{"class":2334}," &&",[2113,2508,2473],{"class":2119},[2113,2510,2511],{"class":2334},"&&\n",[2113,2513,2515,2518,2521,2524,2526,2529],{"class":2115,"line":2514},11,[2113,2516,2517],{"class":2119},"      (physical_memory ",[2113,2519,2520],{"class":2326},"\u002F",[2113,2522,2523],{"class":2119}," GB) ",[2113,2525,2481],{"class":2326},[2113,2527,2528],{"class":2166}," 15",[2113,2530,2433],{"class":2119},[2113,2532,2534,2537,2540,2542,2545,2548,2551],{"class":2115,"line":2533},12,[2113,2535,2536],{"class":2133},"    DCHECK_EQ",[2113,2538,2539],{"class":2119},"(max_size ",[2113,2541,2520],{"class":2326},[2113,2543,2544],{"class":2119}," GB, ",[2113,2546,2547],{"class":2166},"2",[2113,2549,2550],{"class":2330},"u",[2113,2552,2553],{"class":2119},");\n",[2113,2555,2557,2560,2563,2565],{"class":2115,"line":2556},13,[2113,2558,2559],{"class":2119},"    max_size ",[2113,2561,2562],{"class":2326},"*=",[2113,2564,2484],{"class":2166},[2113,2566,2487],{"class":2119},[2113,2568,2570],{"class":2115,"line":2569},14,[2113,2571,2572],{"class":2119},"  }\n",[2113,2574,2576,2579,2582,2585,2588,2591],{"class":2115,"line":2575},15,[2113,2577,2578],{"class":2326},"  return",[2113,2580,2581],{"class":2119}," std::",[2113,2583,2584],{"class":2133},"min",[2113,2586,2587],{"class":2119},"(max_size, ",[2113,2589,2590],{"class":2133},"AllocatorLimitOnMaxOldGenerationSize",[2113,2592,2593],{"class":2119},"());\n",[2113,2595,2597],{"class":2115,"line":2596},16,[2113,2598,2599],{"class":2119},"}\n",[12,2601,2602,2603,792,2606,179],{},"Pour pouvoir rassembler les morceaux, nous devons donc connaître ",[184,2604,2605],{},"V8HeapTrait::kMaxSize",[184,2607,2608],{},"Heap::kHeapLimitMultiplier",[12,2610,2611,2612,2614,2615,2384],{},"La valeur de ",[184,2613,2605],{}," est définie dans le fichier ",[184,2616,2617],{},"deps\u002Fv8\u002Fsrc\u002Fheap\u002Fheap-controller.h",[2105,2619,2621],{"className":2387,"code":2620,"language":2389,"meta":1646,"style":1646},"\u002F\u002F deps\u002Fv8\u002Fsrc\u002Fheap\u002Fheap-controller.h\n\n\u002F\u002F line 16\nstruct BaseControllerTrait {\n  static constexpr size_t kMinSize = 128u * Heap::kHeapLimitMultiplier * MB;\n  static constexpr size_t kMaxSize = 1024u * Heap::kHeapLimitMultiplier * MB;\n\n  static constexpr double kMinGrowingFactor = 1.1;\n  static constexpr double kMaxGrowingFactor = 4.0;\n  static constexpr double kConservativeGrowingFactor = 1.3;\n  static constexpr double kTargetMutatorUtilization = 0.97;\n};\n",[184,2622,2623,2628,2632,2637,2648,2680,2706,2710,2729,2747,2765,2783],{"__ignoreMap":1646},[2113,2624,2625],{"class":2115,"line":2116},[2113,2626,2627],{"class":2396},"\u002F\u002F deps\u002Fv8\u002Fsrc\u002Fheap\u002Fheap-controller.h\n",[2113,2629,2630],{"class":2115,"line":1647},[2113,2631,2125],{"emptyLinePlaceholder":1767},[2113,2633,2634],{"class":2115,"line":1652},[2113,2635,2636],{"class":2396},"\u002F\u002F line 16\n",[2113,2638,2639,2642,2645],{"class":2115,"line":2185},[2113,2640,2641],{"class":2326},"struct",[2113,2643,2644],{"class":2414}," BaseControllerTrait",[2113,2646,2647],{"class":2119}," {\n",[2113,2649,2650,2653,2656,2659,2662,2664,2667,2669,2672,2674,2677],{"class":2115,"line":2230},[2113,2651,2652],{"class":2326},"  static",[2113,2654,2655],{"class":2326}," constexpr",[2113,2657,2658],{"class":2326}," size_t",[2113,2660,2661],{"class":2119}," kMinSize ",[2113,2663,2335],{"class":2326},[2113,2665,2666],{"class":2166}," 128",[2113,2668,2550],{"class":2330},[2113,2670,2671],{"class":2326}," *",[2113,2673,2478],{"class":2119},[2113,2675,2676],{"class":2326},"*",[2113,2678,2679],{"class":2119}," MB;\n",[2113,2681,2682,2684,2686,2688,2691,2693,2696,2698,2700,2702,2704],{"class":2115,"line":2235},[2113,2683,2652],{"class":2326},[2113,2685,2655],{"class":2326},[2113,2687,2658],{"class":2326},[2113,2689,2690],{"class":2119}," kMaxSize ",[2113,2692,2335],{"class":2326},[2113,2694,2695],{"class":2166}," 1024",[2113,2697,2550],{"class":2330},[2113,2699,2671],{"class":2326},[2113,2701,2478],{"class":2119},[2113,2703,2676],{"class":2326},[2113,2705,2679],{"class":2119},[2113,2707,2708],{"class":2115,"line":2241},[2113,2709,2125],{"emptyLinePlaceholder":1767},[2113,2711,2712,2714,2716,2719,2722,2724,2727],{"class":2115,"line":2246},[2113,2713,2652],{"class":2326},[2113,2715,2655],{"class":2326},[2113,2717,2718],{"class":2326}," double",[2113,2720,2721],{"class":2119}," kMinGrowingFactor ",[2113,2723,2335],{"class":2326},[2113,2725,2726],{"class":2166}," 1.1",[2113,2728,2487],{"class":2119},[2113,2730,2731,2733,2735,2737,2740,2742,2745],{"class":2115,"line":2464},[2113,2732,2652],{"class":2326},[2113,2734,2655],{"class":2326},[2113,2736,2718],{"class":2326},[2113,2738,2739],{"class":2119}," kMaxGrowingFactor ",[2113,2741,2335],{"class":2326},[2113,2743,2744],{"class":2166}," 4.0",[2113,2746,2487],{"class":2119},[2113,2748,2749,2751,2753,2755,2758,2760,2763],{"class":2115,"line":2085},[2113,2750,2652],{"class":2326},[2113,2752,2655],{"class":2326},[2113,2754,2718],{"class":2326},[2113,2756,2757],{"class":2119}," kConservativeGrowingFactor ",[2113,2759,2335],{"class":2326},[2113,2761,2762],{"class":2166}," 1.3",[2113,2764,2487],{"class":2119},[2113,2766,2767,2769,2771,2773,2776,2778,2781],{"class":2115,"line":2514},[2113,2768,2652],{"class":2326},[2113,2770,2655],{"class":2326},[2113,2772,2718],{"class":2326},[2113,2774,2775],{"class":2119}," kTargetMutatorUtilization ",[2113,2777,2335],{"class":2326},[2113,2779,2780],{"class":2166}," 0.97",[2113,2782,2487],{"class":2119},[2113,2784,2785],{"class":2115,"line":2533},[2113,2786,2787],{"class":2119},"};\n",[12,2789,2790,2791,2614,2793,2384],{},"Enfin, la valeur de ",[184,2792,2608],{},[184,2794,2795],{},"deps\u002Fv8\u002Fsrc\u002Fheap\u002Fheap.h",[2105,2797,2799],{"className":2387,"code":2798,"language":2389,"meta":1646,"style":1646},"\u002F\u002F deps\u002Fv8\u002Fsrc\u002Fheap\u002Fheap.h\n\n\u002F\u002F line 306\n#if V8_OS_ANDROID\n  \u002F\u002F Don't apply pointer multiplier on Android since it has no swap space and\n  \u002F\u002F should instead adapt it's heap size based on available physical memory.\n  static const int kPointerMultiplier = 1;\n  static const int kHeapLimitMultiplier = 1;\n#else\n  static const int kPointerMultiplier = kTaggedSize \u002F 4;\n  \u002F\u002F The heap limit needs to be computed based on the system pointer size\n  \u002F\u002F because we want a pointer-compressed heap to have larger limit than\n  \u002F\u002F an ordinary 32-bit which that is constrained by 2GB virtual address space.\n  static const int kHeapLimitMultiplier = kSystemPointerSize \u002F 4;\n#endif\n",[184,2800,2801,2806,2810,2815,2823,2828,2833,2853,2870,2875,2897,2902,2907,2912,2933],{"__ignoreMap":1646},[2113,2802,2803],{"class":2115,"line":2116},[2113,2804,2805],{"class":2396},"\u002F\u002F deps\u002Fv8\u002Fsrc\u002Fheap\u002Fheap.h\n",[2113,2807,2808],{"class":2115,"line":1647},[2113,2809,2125],{"emptyLinePlaceholder":1767},[2113,2811,2812],{"class":2115,"line":1652},[2113,2813,2814],{"class":2396},"\u002F\u002F line 306\n",[2113,2816,2817,2820],{"class":2115,"line":2185},[2113,2818,2819],{"class":2326},"#if",[2113,2821,2822],{"class":2133}," V8_OS_ANDROID\n",[2113,2824,2825],{"class":2115,"line":2230},[2113,2826,2827],{"class":2396},"  \u002F\u002F Don't apply pointer multiplier on Android since it has no swap space and\n",[2113,2829,2830],{"class":2115,"line":2235},[2113,2831,2832],{"class":2396},"  \u002F\u002F should instead adapt it's heap size based on available physical memory.\n",[2113,2834,2835,2837,2840,2843,2846,2848,2851],{"class":2115,"line":2241},[2113,2836,2652],{"class":2326},[2113,2838,2839],{"class":2326}," const",[2113,2841,2842],{"class":2326}," int",[2113,2844,2845],{"class":2119}," kPointerMultiplier ",[2113,2847,2335],{"class":2326},[2113,2849,2850],{"class":2166}," 1",[2113,2852,2487],{"class":2119},[2113,2854,2855,2857,2859,2861,2864,2866,2868],{"class":2115,"line":2246},[2113,2856,2652],{"class":2326},[2113,2858,2839],{"class":2326},[2113,2860,2842],{"class":2326},[2113,2862,2863],{"class":2119}," kHeapLimitMultiplier ",[2113,2865,2335],{"class":2326},[2113,2867,2850],{"class":2166},[2113,2869,2487],{"class":2119},[2113,2871,2872],{"class":2115,"line":2464},[2113,2873,2874],{"class":2326},"#else\n",[2113,2876,2877,2879,2881,2883,2885,2887,2890,2892,2895],{"class":2115,"line":2085},[2113,2878,2652],{"class":2326},[2113,2880,2839],{"class":2326},[2113,2882,2842],{"class":2326},[2113,2884,2845],{"class":2119},[2113,2886,2335],{"class":2326},[2113,2888,2889],{"class":2119}," kTaggedSize ",[2113,2891,2520],{"class":2326},[2113,2893,2894],{"class":2166}," 4",[2113,2896,2487],{"class":2119},[2113,2898,2899],{"class":2115,"line":2514},[2113,2900,2901],{"class":2396},"  \u002F\u002F The heap limit needs to be computed based on the system pointer size\n",[2113,2903,2904],{"class":2115,"line":2533},[2113,2905,2906],{"class":2396},"  \u002F\u002F because we want a pointer-compressed heap to have larger limit than\n",[2113,2908,2909],{"class":2115,"line":2556},[2113,2910,2911],{"class":2396},"  \u002F\u002F an ordinary 32-bit which that is constrained by 2GB virtual address space.\n",[2113,2913,2914,2916,2918,2920,2922,2924,2927,2929,2931],{"class":2115,"line":2569},[2113,2915,2652],{"class":2326},[2113,2917,2839],{"class":2326},[2113,2919,2842],{"class":2326},[2113,2921,2863],{"class":2119},[2113,2923,2335],{"class":2326},[2113,2925,2926],{"class":2119}," kSystemPointerSize ",[2113,2928,2520],{"class":2326},[2113,2930,2894],{"class":2166},[2113,2932,2487],{"class":2119},[2113,2934,2935],{"class":2115,"line":2575},[2113,2936,2937],{"class":2326},"#endif\n",[12,2939,2611,2940,2943,2944,2947,2948,2951,2952,2384],{},[184,2941,2942],{},"kHeapLimitMultiplier"," dépend elle-même de ",[184,2945,2946],{},"kSystemPointerSize",", qui n'est pas définie dans ce fichier. C'est\ndans le fichier ",[184,2949,2950],{},"deps\u002Fv8\u002Fsrc\u002Fbase\u002Fplatform\u002Fplatform.h"," que nous allons trouver la valeur de ",[184,2953,2946],{},[2105,2955,2957],{"className":2387,"code":2956,"language":2389,"meta":1646,"style":1646},"\u002F\u002F deps\u002Fv8\u002Fsrc\u002Fbase\u002Fplatform\u002Fplatform.h\n\n\u002F\u002F line 84\nV8_INLINE intptr_t InternalGetExistingThreadLocal(intptr_t index) {\n  const intptr_t kTibInlineTlsOffset = 0xE10;\n  const intptr_t kTibExtraTlsOffset = 0xF94;\n  const intptr_t kMaxInlineSlots = 64;\n  const intptr_t kMaxSlots = kMaxInlineSlots + 1024;\n  const intptr_t kSystemPointerSize = sizeof(void*);\n  DCHECK(0 \u003C= index && index \u003C kMaxSlots);\n  USE(kMaxSlots);\n  if (index \u003C kMaxInlineSlots) {\n    return static_cast\u003Cintptr_t>(\n        __readfsdword(kTibInlineTlsOffset + kSystemPointerSize * index));\n  }\n  intptr_t extra = static_cast\u003Cintptr_t>(__readfsdword(kTibExtraTlsOffset));\n  if (!extra) return 0;\n  return *reinterpret_cast\u003Cintptr_t*>(extra + kSystemPointerSize *\n                                                  (index - kMaxInlineSlots));\n}\n",[184,2958,2959,2964,2968,2973,2991,3012,3030,3046,3066,3086,3113,3121,3133,3147,3164,3168,3190,3211,3234,3246],{"__ignoreMap":1646},[2113,2960,2961],{"class":2115,"line":2116},[2113,2962,2963],{"class":2396},"\u002F\u002F deps\u002Fv8\u002Fsrc\u002Fbase\u002Fplatform\u002Fplatform.h\n",[2113,2965,2966],{"class":2115,"line":1647},[2113,2967,2125],{"emptyLinePlaceholder":1767},[2113,2969,2970],{"class":2115,"line":1652},[2113,2971,2972],{"class":2396},"\u002F\u002F line 84\n",[2113,2974,2975,2978,2981,2984,2986,2988],{"class":2115,"line":2185},[2113,2976,2977],{"class":2119},"V8_INLINE ",[2113,2979,2980],{"class":2326},"intptr_t",[2113,2982,2983],{"class":2133}," InternalGetExistingThreadLocal",[2113,2985,2423],{"class":2119},[2113,2987,2980],{"class":2326},[2113,2989,2990],{"class":2119}," index) {\n",[2113,2992,2993,2996,2999,3002,3004,3007,3010],{"class":2115,"line":2230},[2113,2994,2995],{"class":2326},"  const",[2113,2997,2998],{"class":2326}," intptr_t",[2113,3000,3001],{"class":2119}," kTibInlineTlsOffset ",[2113,3003,2335],{"class":2326},[2113,3005,3006],{"class":2330}," 0x",[2113,3008,3009],{"class":2166},"E10",[2113,3011,2487],{"class":2119},[2113,3013,3014,3016,3018,3021,3023,3025,3028],{"class":2115,"line":2235},[2113,3015,2995],{"class":2326},[2113,3017,2998],{"class":2326},[2113,3019,3020],{"class":2119}," kTibExtraTlsOffset ",[2113,3022,2335],{"class":2326},[2113,3024,3006],{"class":2330},[2113,3026,3027],{"class":2166},"F94",[2113,3029,2487],{"class":2119},[2113,3031,3032,3034,3036,3039,3041,3044],{"class":2115,"line":2241},[2113,3033,2995],{"class":2326},[2113,3035,2998],{"class":2326},[2113,3037,3038],{"class":2119}," kMaxInlineSlots ",[2113,3040,2335],{"class":2326},[2113,3042,3043],{"class":2166}," 64",[2113,3045,2487],{"class":2119},[2113,3047,3048,3050,3052,3055,3057,3059,3062,3064],{"class":2115,"line":2246},[2113,3049,2995],{"class":2326},[2113,3051,2998],{"class":2326},[2113,3053,3054],{"class":2119}," kMaxSlots ",[2113,3056,2335],{"class":2326},[2113,3058,3038],{"class":2119},[2113,3060,3061],{"class":2326},"+",[2113,3063,2695],{"class":2166},[2113,3065,2487],{"class":2119},[2113,3067,3068,3070,3072,3074,3076,3079,3081,3084],{"class":2115,"line":2464},[2113,3069,2995],{"class":2326},[2113,3071,2998],{"class":2326},[2113,3073,2926],{"class":2119},[2113,3075,2335],{"class":2326},[2113,3077,3078],{"class":2326}," sizeof",[2113,3080,2423],{"class":2119},[2113,3082,3083],{"class":2326},"void*",[2113,3085,2553],{"class":2119},[2113,3087,3088,3091,3093,3096,3099,3102,3105,3107,3110],{"class":2115,"line":2085},[2113,3089,3090],{"class":2133},"  DCHECK",[2113,3092,2423],{"class":2119},[2113,3094,3095],{"class":2166},"0",[2113,3097,3098],{"class":2326}," \u003C=",[2113,3100,3101],{"class":2119}," index ",[2113,3103,3104],{"class":2334},"&&",[2113,3106,3101],{"class":2119},[2113,3108,3109],{"class":2326},"\u003C",[2113,3111,3112],{"class":2119}," kMaxSlots);\n",[2113,3114,3115,3118],{"class":2115,"line":2514},[2113,3116,3117],{"class":2133},"  USE",[2113,3119,3120],{"class":2119},"(kMaxSlots);\n",[2113,3122,3123,3125,3128,3130],{"class":2115,"line":2533},[2113,3124,2492],{"class":2326},[2113,3126,3127],{"class":2119}," (index ",[2113,3129,3109],{"class":2326},[2113,3131,3132],{"class":2119}," kMaxInlineSlots) {\n",[2113,3134,3135,3138,3141,3144],{"class":2115,"line":2556},[2113,3136,3137],{"class":2326},"    return",[2113,3139,3140],{"class":2119}," static_cast",[2113,3142,3143],{"class":2326},"\u003Cintptr_t>",[2113,3145,3146],{"class":2119},"(\n",[2113,3148,3149,3152,3155,3157,3159,3161],{"class":2115,"line":2569},[2113,3150,3151],{"class":2133},"        __readfsdword",[2113,3153,3154],{"class":2119},"(kTibInlineTlsOffset ",[2113,3156,3061],{"class":2326},[2113,3158,2926],{"class":2119},[2113,3160,2676],{"class":2326},[2113,3162,3163],{"class":2119}," index));\n",[2113,3165,3166],{"class":2115,"line":2575},[2113,3167,2572],{"class":2119},[2113,3169,3170,3173,3176,3178,3180,3182,3184,3187],{"class":2115,"line":2596},[2113,3171,3172],{"class":2326},"  intptr_t",[2113,3174,3175],{"class":2119}," extra ",[2113,3177,2335],{"class":2326},[2113,3179,3140],{"class":2119},[2113,3181,3143],{"class":2326},[2113,3183,2423],{"class":2119},[2113,3185,3186],{"class":2133},"__readfsdword",[2113,3188,3189],{"class":2119},"(kTibExtraTlsOffset));\n",[2113,3191,3193,3195,3197,3200,3203,3206,3209],{"class":2115,"line":3192},17,[2113,3194,2492],{"class":2326},[2113,3196,2495],{"class":2119},[2113,3198,3199],{"class":2334},"!",[2113,3201,3202],{"class":2119},"extra) ",[2113,3204,3205],{"class":2326},"return",[2113,3207,3208],{"class":2166}," 0",[2113,3210,2487],{"class":2119},[2113,3212,3214,3216,3218,3221,3224,3227,3229,3231],{"class":2115,"line":3213},18,[2113,3215,2578],{"class":2326},[2113,3217,2671],{"class":2326},[2113,3219,3220],{"class":2119},"reinterpret_cast",[2113,3222,3223],{"class":2326},"\u003Cintptr_t*>",[2113,3225,3226],{"class":2119},"(extra ",[2113,3228,3061],{"class":2326},[2113,3230,2926],{"class":2119},[2113,3232,3233],{"class":2326},"*\n",[2113,3235,3237,3240,3243],{"class":2115,"line":3236},19,[2113,3238,3239],{"class":2119},"                                                  (index ",[2113,3241,3242],{"class":2326},"-",[2113,3244,3245],{"class":2119}," kMaxInlineSlots));\n",[2113,3247,3249],{"class":2115,"line":3248},20,[2113,3250,2599],{"class":2119},[12,3252,3253],{},"Nous avons ainsi tous les éléments pour reconstituer le puzzle :",[506,3255,3256,3261,3270],{},[370,3257,3258,3260],{},[184,3259,2946],{}," permet de définir la taille d'un pointeur. Sur un système 64 bits, la taille d'un pointeur est\nde 8 octets, alors que sur un système 32 bits, la taille d'un pointeur est de 4 octets.",[370,3262,3263,3265,3266,3269],{},[184,3264,2942],{}," possède la valeur de 1 sur Android, mais pour les autres systèmes, la valeur est de\n",[184,3267,3268],{},"kSystemPointerSize \u002F 4",", soit 2 sur un système 64 bits et 1 sur un système 32 bits.",[370,3271,3272,3275,3276,3279],{},[184,3273,3274],{},"kMaxSize"," vaut ",[184,3277,3278],{},"1024 * kHeapLimitMultiplier * MB",", soit 1024 MB sur un système 32 bits et 2048 MB sur un système 64\nbits.",[12,3281,3282,3283,3285],{},"Enfin, en lisant le code de la fonction ",[184,3284,2379],{},", on comprend que la mémoire allouée à NodeJS est de 2Go\npar défaut.",[12,3287,3288],{},"Mais si le système est un système 64 bits (pointeur de 8 octets) et que la mémoire disponible sur la machine est\nsupérieure à 15Go, alors la mémoire allouée est multipliée par 2, soit 4Go.",[12,3290,3291],{},"Cela explique pourquoi sur nos postes de développement surpuissants, nous n'avons pas l'erreur car la mémoire allouée est de\n4Go par défaut. Mais sur la chaîne de déploiement, la mémoire allouée est de 2Go par défaut, car la machine ne possède\nque 8Go (ce qui est largement suffisant).",[12,3293,3294],{},"Voici un petit tableau récapitulatif:",[22,3296,3297,3313],{},[25,3298,3299],{},[28,3300,3301,3304,3307,3310],{},[31,3302,3303],{},"Système",[31,3305,3306],{},"Taille du pointeur",[31,3308,3309],{},"Mémoire disponible",[31,3311,3312],{},"Mémoire allouée par défaut",[41,3314,3315,3329,3342],{},[28,3316,3317,3320,3323,3326],{},[46,3318,3319],{},"32 bits",[46,3321,3322],{},"4 octets",[46,3324,3325],{},"OSEF",[46,3327,3328],{},"2Go",[28,3330,3331,3334,3337,3340],{},[46,3332,3333],{},"64 bits",[46,3335,3336],{},"8 octets",[46,3338,3339],{},"\u003C 15Go",[46,3341,3328],{},[28,3343,3344,3346,3348,3351],{},[46,3345,3333],{},[46,3347,3336],{},[46,3349,3350],{},">= 15Go",[46,3352,3353],{},"4Go",[12,3355,3356],{},"J'espère que cet article vous a permis de faire un petit voyage dans le code source de NodeJS et de\ncomprendre comment est calculée la valeur par défaut de max-old-space-size.",[3358,3359,3360],"style",{},"html pre.shiki code .sn6KH, html code.shiki .sn6KH{--shiki-default:#ABB2BF}html pre.shiki code .sVbv2, html code.shiki .sVbv2{--shiki-default:#61AFEF}html pre.shiki code .subq3, html code.shiki .subq3{--shiki-default:#98C379}html pre.shiki code .sVC51, html code.shiki .sVC51{--shiki-default:#D19A66}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html pre.shiki code .seHd6, html code.shiki .seHd6{--shiki-default:#C678DD}html pre.shiki code .sVyAn, html code.shiki .sVyAn{--shiki-default:#E06C75}html pre.shiki code .sjrmR, html code.shiki .sjrmR{--shiki-default:#56B6C2}html pre.shiki code .sV9Aq, html code.shiki .sV9Aq{--shiki-default:#7F848E;--shiki-default-font-style:italic}html pre.shiki code .sU0A5, html code.shiki .sU0A5{--shiki-default:#E5C07B}html pre.shiki code .s_ZVi, html code.shiki .s_ZVi{--shiki-default:#E06C75;--shiki-default-font-style:italic}",{"title":1646,"searchDepth":1647,"depth":1647,"links":3362},[],"2024-06-14",{"type":9,"value":3365},[3366,3368,3372,3374,3502],[12,3367,14],{},[12,3369,2096,3370,2100],{},[184,3371,2099],{},[12,3373,2103],{},[2105,3375,3376],{"className":2107,"code":2108,"language":2109,"meta":1646,"style":1646},[184,3377,3378,3382,3386,3424,3462,3466,3470,3474],{"__ignoreMap":1646},[2113,3379,3380],{"class":2115,"line":2116},[2113,3381,2120],{"class":2119},[2113,3383,3384],{"class":2115,"line":1647},[2113,3385,2125],{"emptyLinePlaceholder":1767},[2113,3387,3388,3390,3392,3394,3396,3398,3400,3402,3404,3406,3408,3410,3412,3414,3416,3418,3420,3422],{"class":2115,"line":1652},[2113,3389,2130],{"class":2119},[2113,3391,2134],{"class":2133},[2113,3393,2137],{"class":2119},[2113,3395,2140],{"class":2133},[2113,3397,2143],{"class":2119},[2113,3399,2146],{"class":2133},[2113,3401,2150],{"class":2149},[2113,3403,2153],{"class":2149},[2113,3405,2156],{"class":2149},[2113,3407,2159],{"class":2149},[2113,3409,2150],{"class":2149},[2113,3411,2153],{"class":2149},[2113,3413,2167],{"class":2166},[2113,3415,2170],{"class":2119},[2113,3417,2173],{"class":2133},[2113,3419,2176],{"class":2149},[2113,3421,2179],{"class":2149},[2113,3423,2182],{"class":2149},[2113,3425,3426,3428,3430,3432,3434,3436,3438,3440,3442,3444,3446,3448,3450,3452,3454,3456,3458,3460],{"class":2115,"line":2185},[2113,3427,2188],{"class":2119},[2113,3429,2140],{"class":2133},[2113,3431,2193],{"class":2119},[2113,3433,2196],{"class":2133},[2113,3435,2199],{"class":2119},[2113,3437,2146],{"class":2133},[2113,3439,2150],{"class":2149},[2113,3441,2153],{"class":2149},[2113,3443,2208],{"class":2149},[2113,3445,2159],{"class":2149},[2113,3447,2150],{"class":2149},[2113,3449,2153],{"class":2149},[2113,3451,2217],{"class":2166},[2113,3453,2170],{"class":2119},[2113,3455,2173],{"class":2133},[2113,3457,2176],{"class":2149},[2113,3459,2179],{"class":2149},[2113,3461,2182],{"class":2149},[2113,3463,3464],{"class":2115,"line":2230},[2113,3465,2125],{"emptyLinePlaceholder":1767},[2113,3467,3468],{"class":2115,"line":2235},[2113,3469,2238],{"class":2119},[2113,3471,3472],{"class":2115,"line":2241},[2113,3473,2125],{"emptyLinePlaceholder":1767},[2113,3475,3476,3478,3480,3482,3484,3486,3488,3490,3492,3494,3496,3498,3500],{"class":2115,"line":2246},[2113,3477,2249],{"class":2133},[2113,3479,2252],{"class":2149},[2113,3481,2255],{"class":2149},[2113,3483,2258],{"class":2149},[2113,3485,2261],{"class":2149},[2113,3487,2264],{"class":2149},[2113,3489,2267],{"class":2149},[2113,3491,2270],{"class":2149},[2113,3493,2273],{"class":2149},[2113,3495,2258],{"class":2149},[2113,3497,2278],{"class":2149},[2113,3499,2281],{"class":2149},[2113,3501,2284],{"class":2149},[3358,3503,3360],{},{"planet":1767},"\u002Fpost\u002Fmax-old-space",{"title":2089,"description":14},"max-old-space","posts\u002FProgrammation\u002F2024-06-14-max-old-space",[3510,3511],"javascript","nodejs","rKoBJuRBkLYyVgc8etB7XZPEzRbfpMzHtNOUEWqjpL8",{"id":3514,"title":108,"author":7,"body":3515,"category":2057,"categorySlug":2058,"date":111,"description":7729,"excerpt":7730,"extension":1764,"location":1765,"meta":7746,"navigation":1767,"path":107,"published":1767,"seo":7747,"slug":7748,"stem":7749,"tags":7750,"timeToRead":4961,"__hash__":7751},"posts\u002Fposts\u002FWoodstock\u002F2024-03-14_pr_backuppc_pool.md",{"type":9,"value":3516,"toc":7711},[3517,3526,3529,3532,3538,3542,3545,3554,3562,3571,3579,3582,3585,3588,3591,3594,3597,3600,3604,3610,3618,3634,3643,3647,3650,3715,3719,3722,3730,3733,3737,3753,3760,3768,3771,3780,3783,3790,3793,3800,3803,3806,3810,3816,3819,3822,3827,3830,3834,3847,3853,4060,4063,4072,4078,4087,4090,4093,4356,4374,4382,4407,4670,4673,5323,5345,5351,5365,5835,5841,6030,6034,6037,6040,6055,6423,6430,6437,6440,6444,6447,6466,6474,6477,6481,6484,6492,6495,6498,6512,6521,6531,6591,6594,6665,6668,6956,6966,6973,7105,7112,7259,7268,7271,7278,7281,7288,7291,7475,7478,7482,7485,7494,7497,7501,7508,7517,7640,7646,7649,7660,7663,7679,7683,7686,7694,7700,7703,7705,7708],[12,3518,3519,3520,3525],{},"Une partie de cet article a été publiée sur ",[49,3521,3524],{"href":3522,"rel":3523},"https:\u002F\u002Flinuxfr.org\u002Fusers\u002Fsan_guickoo\u002Fjournaux\u002Fpullrequest-d-une-application-en-rust",[347],"LinuxFR",".\nAprès avoir reçu quelques retours, j'ai décidé de publier une version modifiée et améliorée de cet article sur mon blog.",[12,3527,3528],{},"Je remercie donc la communauté de LinuxFR pour ses retours. 😄",[12,3530,3531],{},"Les commentaires sur le code ou sur l'article sont les bienvenus.",[12,3533,3534],{},[121,3535],{"src":3536,"alt":1646,"className":3537},"\u002FWoodstock\u002Fhdd_unsplash.jpg",[126],[128,3539,3541],{"id":3540},"le-commencement","Le commencement",[12,3543,3544],{},"Actuellement, j'utilise BackupPC pour sauvegarder mes données. BackupPC est un logiciel de sauvegarde qui se connecte à\ndifférents ordinateurs via SSH et utilise rsync pour sauvegarder les données. Il fonctionne parfaitement avec des\nordinateurs Linux et un peu moins bien sur des ordinateurs Windows où il faut installer un rsyncd\u002FCygwin (les données ne\nsont pas protégées par SSH dans ce cas).",[12,3546,3547,3548,3553],{},"Par ailleurs, je développe ",[49,3549,3552],{"href":3550,"rel":3551},"https:\u002F\u002Fshadoware.org\u002Fpost\u002Fwoodstock",[347],"mon propre logiciel de sauvegarde",". C'est un défi\npersonnel que je me suis lancé pour répondre à mes propres besoins (et aussi pour le plaisir).",[12,3555,3556,3557,179],{},"Mon premier prototype est écrit en TypeScript et utilise rsync couplé avec Btrfs pour faire des sauvegardes\nincrémentales. Malheureusement, quelques problèmes liés à l'utilisation de Btrfs m'ont fait abandonner ce prototype\n(problème avec la création d'un grand nombre de snapshots et un système de fichier un peu trop plein). J'en parle dans\n",[49,3558,3561],{"href":3559,"rel":3560},"https:\u002F\u002Fshadoware.org\u002Fpost\u002Fwoodstock_brtfs",[347],"un article sur mon blog",[12,3563,3564,3565,3570],{},"Je me suis donc orienté vers l'écriture de mon propre pool de stockage de données. L'écriture dans ce pool de stockage\nm'oblige à écrire mon propre logiciel de synchronisation. BackupPC a fait le choix de rester sur rsync et donc à créer\nun ",[49,3566,3569],{"href":3567,"rel":3568},"https:\u002F\u002Fgithub.com\u002Fbackuppc\u002Frsync-bpc",[347],"fork de rsync"," capable de se connecter en rsync sur les machines à\nsauvegarder, tout en étant capable d'écrire le résultat dans le pool de backuppc, au format de backuppc. Pour ma part,\nj'ai choisi de développer mon protocole basé sur GRPC (et donc HTTP\u002F2).",[12,3572,3573,3574,179],{},"J'ai donc fait un second prototype, toujours en TypeScript, pour tester mon idée. Je suis satisfait du résultat, mais\nles limites du moteur JavaScript font de ce prototype un simple prototype. Là aussi, j'en parle dans\n",[49,3575,3578],{"href":3576,"rel":3577},"https:\u002F\u002Fshadoware.org\u002Fpost\u002Fwoodstock_rust",[347],"un autre article de mon blog",[12,3580,3581],{},"Je vais donc réécrire la partie la plus importante de ce programme en Rust. Pourquoi Rust ? Dans ma jeunesse (enfin,\nj'avais entre 20 et 30 ans), j'adorais programmer en C\u002FC++. Je me souviens d'avoir programmé un petit IDE en C++ avec\nQt. Lors du développement en C++, il m'arrivait parfois de me retrouver avec des fuites de mémoire, des erreurs de\nsegmentation, ainsi que des problèmes de concurrence d'accès aux données.",[12,3583,3584],{},"C++ m'a beaucoup appris sur le fonctionnement d'une machine, la gestion de la mémoire, la gestion du multithreading,\netc. Tout le monde devrait commencer par ce langage 😄.",[12,3586,3587],{},"Rust est un langage qui a été conçu pour éviter ces problèmes. Après avoir lu la documentation, j'ai adoré le concept.\nDu coup, j'ai décidé que ce serait une très bonne idée d'apprendre à l'utiliser. (Surtout qu'on en entend beaucoup\nparler en ce moment).",[12,3589,3590],{},"Vous trouvez que je digresse beaucoup ? C'est possible.",[12,3592,3593],{},"Revenons à notre programme.",[12,3595,3596],{},"Je me suis dit qu'avant d'écrire la gestion de mon pool de stockage en Rust, je voulais faire un script qui me permette\nde migrer le contenu de mon pool de stockage de BackupPC vers mon nouveau pool de stockage. Et de le faire en Rust.",[12,3598,3599],{},"Pour cela, j'ai besoin de comprendre comment fonctionne le pool de stockage de BackupPC. Le faire avec le code source\nde BackupPC sera amusant et pas trop compliqué.",[128,3601,3603],{"id":3602},"description-du-pool-de-stockage-de-backuppc","Description du pool de stockage de BackupPC",[12,3605,3606],{},[121,3607],{"src":3608,"alt":3609},"https:\u002F\u002Fbackuppc.github.io\u002Fbackuppc\u002Fimages\u002Flogos\u002Flogo.svg","BackupPC Logo",[12,3611,1589,3612,3617],{},[49,3613,3616],{"href":3614,"rel":3615},"https:\u002F\u002Fbackuppc.github.io\u002Fbackuppc\u002FBackupPC.html",[347],"documentation de BackupPC"," décrit déjà pas mal de choses :",[506,3619,3620,3627],{},[370,3621,3622],{},[49,3623,3626],{"href":3624,"rel":3625},"https:\u002F\u002Fbackuppc.github.io\u002Fbackuppc\u002FBackupPC.html#Storage-layout",[347],"Comment sont stockés les fichiers dans le pool de stockage ?",[370,3628,3629],{},[49,3630,3633],{"href":3631,"rel":3632},"https:\u002F\u002Fbackuppc.github.io\u002Fbackuppc\u002FBackupPC.html#Compressed-file-format",[347],"Une description succincte du format des fichiers compressés",[12,3635,3636,3637,3642],{},"Ensuite, pour obtenir les détails, il faut aller lire le ",[49,3638,3641],{"href":3639,"rel":3640},"https:\u002F\u002Fgithub.com\u002Fbackuppc\u002Fbackuppc-xs",[347],"code source en C"," qui\nsert de liaison avec le code en Perl. BackupPC est originellement écrit en Perl (entièrement, si je ne me trompe pas,\npour la version 3). La version 4 a été partiellement réécrite en C pour la partie qui gère le pool de stockage. Cette\npartie est utilisée par le rsync modifié et par la partie Perl à travers la bibliothèque de bindings.",[133,3644,3646],{"id":3645},"la-constitution-du-pool","La constitution du pool",[12,3648,3649],{},"Le pool de stockage de BackupPC est principalement constitué de plusieurs dossiers :",[506,3651,3652,3700,3706],{},[370,3653,3654,3657,3658],{},[184,3655,3656],{},"pc"," : Ce dossier répertorie les sauvegardes des différentes machines, organisées par machine puis par sauvegarde.\n",[506,3659,3660,3665],{},[370,3661,3662],{},[184,3663,3664],{},"host1",[370,3666,3667,3670,3671,3673,3674],{},[184,3668,3669],{},"host2"," : Le dossier de la machine ",[184,3672,3669],{},".\n",[506,3675,3676,3682,3688,3692,3697],{},[370,3677,3678,3681],{},[184,3679,3680],{},"backups"," : Un fichier au format TSV (Tab Separated Values) qui liste les sauvegardes de la machine avec leurs\ninformations.",[370,3683,3684,3687],{},[184,3685,3686],{},"1"," : La sauvegarde où les dossiers sont représentés à l'aide du système de fichiers, et les fichiers sont listés\ndans des fichiers d'attributs. Le hash du fichier d'attribut dans le pool est contenu dans le nom du fichier\n(exemple : attrib_33fe8f9ae2f5cedbea63b9d3ea767ac0).",[370,3689,3690],{},[184,3691,2547],{},[370,3693,3694],{},[184,3695,3696],{},"3",[370,3698,3699],{},"...",[370,3701,3702,3705],{},[184,3703,3704],{},"pool"," : Dans ce dossier, les fichiers sont stockés en utilisant leur hash MD5. Les fichiers sont répartis sur deux\nniveaux dans 128 dossiers dont le nom est constitué des 7 premiers bits des deux premiers octets. Si deux fichiers ont\nle même MD5, un nombre est accolé au hash pour les différencier.",[370,3707,3708,3711,3712,3714],{},[184,3709,3710],{},"cpool"," : La structure de ce dossier est la même que celle du dossier ",[184,3713,3704],{},". Il s'agit d'un pool de stockage\ncompressé.",[133,3716,3718],{"id":3717},"le-format-des-fichiers-compressés","Le format des fichiers compressés",[12,3720,3721],{},"Voici la description du fichier compressé selon la documentation (traduction libre) :",[1886,3723,3724,3727],{},[12,3725,3726],{},"Le format de fichier compressé est généré par Compress::Zlib::deflate avec une modification mineure, mais\nimportante. Comme Compress::Zlib::inflate gonfle entièrement son argument en mémoire, il pourrait consommer de\ngrandes quantités de mémoire s'il décompressait un fichier très compressé. Par exemple, un fichier de 200 Mo de 0x0\nbytes se compresse à environ 200 Ko. Si Compress::Zlib::inflate était appelé avec ce seul tampon de 200 Ko, il\naurait besoin d'allouer 200 Mo de mémoire pour retourner le résultat.",[12,3728,3729],{},"BackupPC surveille l'efficacité de la compression d'un fichier. Si un gros fichier a un taux de compression très élevé\n(ce qui signifie qu'il utilisera beaucoup de mémoire lorsqu'il sera décompressé), BackupPC appelle la méthode flush(), qui\ntermine proprement la compression en cours. BackupPC commence alors une nouvelle section et ajoute simplement le\nfichier de sortie. Ainsi, le format de fichier compressé de BackupPC est une ou plusieurs sections\u002Fflushes\nconcaténées. Les ratios spécifiques que BackupPC utilise sont que si un morceau de 6 Mo se compresse à moins de 64\nKo, alors un flush sera effectué.",[12,3731,3732],{},"Donc, dans notre cas, nous devons être capables de lire un fichier compressé comme une suite de fichiers compressés\nconcaténés les uns après les autres.",[133,3734,3736],{"id":3735},"le-format-des-fichiers-dattributs","Le format des fichiers d'attributs",[12,3738,3739,3744,3745,3748,3749,3752],{},[49,3740,3743],{"href":3741,"rel":3742},"https:\u002F\u002Fbackuppc.github.io\u002Fbackuppc\u002FBackupPC.html#Attribute-file-format",[347],"Dans la documentation",", il est expliqué que\ndans la version 4, on retrouve un fichier ",[184,3746,3747],{},"attrib_33fe8f9ae2f5cedbea63b9d3ea767ac0"," dans les différents dossiers de la\nmachine (",[184,3750,3751],{},"__TOPDIR__\u002Fpc\u002Fhost1",").\nLe nom du fichier contient le hash du fichier d'attributs que l'on peut retrouver dans le pool de stockage.",[12,3754,3755,3756,3759],{},"Pour accéder à un fichier du pool de stockage, il faut aller dans le dossier ",[184,3757,3758],{},"__TOPDIR__\u002Fpc"," et lire le nom du fichier\nd'attribut pour retrouver le hash du fichier compressé correspondant au nom du dossier que l'on veut lire. Enfin, il\nfaut lire le fichier compressé pour obtenir les données.",[12,3761,3762,3763,179],{},"Le contenu du fichier d'attribut n'est pas décrit. Cependant, on peut le retrouver dans le fichier\n",[49,3764,3767],{"href":3765,"rel":3766},"https:\u002F\u002Fgithub.com\u002Fbackuppc\u002Fbackuppc-xs\u002Fblob\u002Fmaster\u002Fbpc_attrib.c#L626",[347],"bpc_attribs.c",[12,3769,3770],{},"Le fichier d'attribut est encodé en binaire. Il s'agit d'une suite de varint (entier encodé sur un nombre variable\nd'octets).",[12,3772,3773,3774,3779],{},"Un ",[49,3775,3778],{"href":3776,"rel":3777},"https:\u002F\u002Fen.wikipedia.org\u002Fwiki\u002FVariable-length_quantity",[347],"Varint"," est un entier qui est encodé sur un nombre variable\nd'octets. Le premier bit de chaque octet indique si l'entier est terminé ou s'il faut lire un autre octet. Le reste des\nbits de l'octet représente l'entier.",[12,3781,3782],{},"Voici une représentation d'un Varint depuis Wikipedia :",[12,3784,3785],{},[121,3786],{"src":3787,"alt":3788,"className":3789},"https:\u002F\u002Fupload.wikimedia.org\u002Fwikipedia\u002Fcommons\u002Fthumb\u002Fc\u002Fc6\u002FUintvar_coding.svg\u002F1920px-Uintvar_coding.svg.png","Image représentant un Varint (Source : Wikipedia)",[126],[12,3791,3792],{},"D'après le code source de BackupPC, le fichier d'attribut est encodé de la manière suivante :",[12,3794,3795,3796,3799],{},"Le fichier commence par un numéro magique ",[184,3797,3798],{},"0x17565353",". Ensuite, pour chaque fichier, on retrouve les attributs du\nfichier. Pour chaque attribut, on retrouve les xattrs.",[121,3801],{"src":3802},"https:\u002F\u002Fwww.plantuml.com\u002Fplantuml\u002Fpng\u002FXL8nJWCn4Epl5TC11H8eEf2kIa530HqIhHDlxbRosSUkVKgUb7VaOyW7KKWuGsLxPpIptjcbHYDnPtfNTMGiIOExxVrZ_L0lwaLbOXHUfQXD1TnYDC8-Dd31jucIm2RuqETZm-kEIIe0q2ZissOEEYhuqA-4OA_8HpdiIM49SJSGjjdpZ3kLBStgM1EfvD47ItXxVNul4HBR4jIMeMZOkQ8f--nw0g4cZTGQiOVz-GHu99FolzQX7uHKEVZ_naLmJ5unT3lbZqGAJG8tFvkVANL6kqlnLTfiSjuN6AvVdcgao8xriCSGlXo4eeGqai0QhxaoXE1k9gKfisQw_hac17SC_9jywg98-Ar6S0QZSST7KNEEioHlyxf_0W00",[12,3804,3805],{},"Il ne me reste donc plus qu'à reproduire tout cela en Rust.",[128,3807,3809],{"id":3808},"le-développement-en-rust","Le développement en Rust",[12,3811,3812],{},[121,3813],{"src":3814,"alt":1054,"className":3815},"\u002FWoodstock\u002Frust_unsplash.jpg",[126],[12,3817,3818],{},"Pour le développement de ce programme, j'ai établi certaines limites. Je me suis concentré uniquement sur la lecture des\nfichiers du pool qui sont en version 4. Ma version de BackupPC est la version 4, et j'ai migré l'intégralité du pool de\nstockage depuis la version 3.",[12,3820,3821],{},"Je n'ai donc pas de données en version 3 pour tester mon programme.",[12,3823,3824],{},[155,3825,3826],{},"Si vous souhaitez tester mon programme ou l'utiliser, sachez qu'il n'est pas compatible avec la version 3 de BackupPC,\nni avec la version 4 si votre pool est un mélange de versions V3 et V4.",[12,3828,3829],{},"Si le besoin se fait sentir plus tard, il sera toujours possible d'améliorer tout cela.",[133,3831,3833],{"id":3832},"les-fichiers-compressés","Les fichiers compressés",[12,3835,3836,3837,3840,3841,3844,3845,179],{},"J'ai donc commencé par développer un programme qui, à partir d'un hash, est capable de décompresser un fichier de ce\npool. Pour décompresser un fichier BackupPC compressé avec ",[184,3838,3839],{},"zlib",", j'ai choisi d'utiliser la bibliothèque ",[184,3842,3843],{},"flate2",", qui\nest une alternative Rust à la bibliothèque standard ",[184,3846,3839],{},[12,3848,3849,3850,3852],{},"La bibliothèque ",[184,3851,3843],{}," permet de décompresser un fichier en utilisant la notion de BufReader en Rust. La version\nsimplifiée pour décompresser un fichier du pool est donc la suivante :",[2105,3854,3857],{"className":3855,"code":3856,"language":1775,"meta":1646,"style":1646},"language-rust shiki shiki-themes one-dark-pro","use flate2::bufread::ZlibDecoder;\nuse std::fs::File;\n\nfn main() {\n    let f = File::open(\"33fe8f9ae2f5cedbea63b9d3ea767ac0\").unwrap();\n    let b = BufReader::new(f);\n    let mut z = ZlibEncoder::new(b);\n    let mut buffer = Vec::new();\n    z.read_to_end(&mut buffer).unwrap();\n    println!(\"{:?}\", buffer);\n}\n",[184,3858,3859,3879,3898,3902,3913,3945,3969,3995,4015,4039,4056],{"__ignoreMap":1646},[2113,3860,3861,3864,3867,3869,3872,3874,3877],{"class":2115,"line":2116},[2113,3862,3863],{"class":2326},"use",[2113,3865,3866],{"class":2414}," flate2",[2113,3868,2418],{"class":2119},[2113,3870,3871],{"class":2414},"bufread",[2113,3873,2418],{"class":2119},[2113,3875,3876],{"class":2414},"ZlibDecoder",[2113,3878,2487],{"class":2119},[2113,3880,3881,3883,3886,3888,3891,3893,3896],{"class":2115,"line":1647},[2113,3882,3863],{"class":2326},[2113,3884,3885],{"class":2414}," std",[2113,3887,2418],{"class":2119},[2113,3889,3890],{"class":2414},"fs",[2113,3892,2418],{"class":2119},[2113,3894,3895],{"class":2414},"File",[2113,3897,2487],{"class":2119},[2113,3899,3900],{"class":2115,"line":1652},[2113,3901,2125],{"emptyLinePlaceholder":1767},[2113,3903,3904,3907,3910],{"class":2115,"line":2185},[2113,3905,3906],{"class":2326},"fn",[2113,3908,3909],{"class":2133}," main",[2113,3911,3912],{"class":2119},"() {\n",[2113,3914,3915,3918,3921,3923,3926,3928,3931,3933,3936,3939,3942],{"class":2115,"line":2230},[2113,3916,3917],{"class":2326},"    let",[2113,3919,3920],{"class":2330}," f",[2113,3922,2153],{"class":2334},[2113,3924,3925],{"class":2414}," File",[2113,3927,2418],{"class":2119},[2113,3929,3930],{"class":2133},"open",[2113,3932,2423],{"class":2119},[2113,3934,3935],{"class":2149},"\"33fe8f9ae2f5cedbea63b9d3ea767ac0\"",[2113,3937,3938],{"class":2119},").",[2113,3940,3941],{"class":2133},"unwrap",[2113,3943,3944],{"class":2119},"();\n",[2113,3946,3947,3949,3952,3954,3957,3959,3962,3964,3967],{"class":2115,"line":2235},[2113,3948,3917],{"class":2326},[2113,3950,3951],{"class":2330}," b",[2113,3953,2153],{"class":2334},[2113,3955,3956],{"class":2414}," BufReader",[2113,3958,2418],{"class":2119},[2113,3960,3961],{"class":2133},"new",[2113,3963,2423],{"class":2119},[2113,3965,3966],{"class":2330},"f",[2113,3968,2553],{"class":2119},[2113,3970,3971,3973,3976,3979,3981,3984,3986,3988,3990,3993],{"class":2115,"line":2241},[2113,3972,3917],{"class":2326},[2113,3974,3975],{"class":2326}," mut",[2113,3977,3978],{"class":2330}," z",[2113,3980,2153],{"class":2334},[2113,3982,3983],{"class":2414}," ZlibEncoder",[2113,3985,2418],{"class":2119},[2113,3987,3961],{"class":2133},[2113,3989,2423],{"class":2119},[2113,3991,3992],{"class":2330},"b",[2113,3994,2553],{"class":2119},[2113,3996,3997,3999,4001,4004,4006,4009,4011,4013],{"class":2115,"line":2246},[2113,3998,3917],{"class":2326},[2113,4000,3975],{"class":2326},[2113,4002,4003],{"class":2330}," buffer",[2113,4005,2153],{"class":2334},[2113,4007,4008],{"class":2414}," Vec",[2113,4010,2418],{"class":2119},[2113,4012,3961],{"class":2133},[2113,4014,3944],{"class":2119},[2113,4016,4017,4020,4022,4025,4028,4031,4033,4035,4037],{"class":2115,"line":2464},[2113,4018,4019],{"class":2330},"    z",[2113,4021,179],{"class":2119},[2113,4023,4024],{"class":2133},"read_to_end",[2113,4026,4027],{"class":2119},"(&",[2113,4029,4030],{"class":2326},"mut",[2113,4032,4003],{"class":2330},[2113,4034,3938],{"class":2119},[2113,4036,3941],{"class":2133},[2113,4038,3944],{"class":2119},[2113,4040,4041,4044,4046,4049,4051,4054],{"class":2115,"line":2085},[2113,4042,4043],{"class":2133},"    println!",[2113,4045,2423],{"class":2119},[2113,4047,4048],{"class":2149},"\"{:?}\"",[2113,4050,932],{"class":2119},[2113,4052,4053],{"class":2330},"buffer",[2113,4055,2553],{"class":2119},[2113,4057,4058],{"class":2115,"line":2514},[2113,4059,2599],{"class":2119},[12,4061,4062],{},"Malheureusement, ce code ne fonctionne pas à tous les coups. Pour les plus petits fichiers de mon pool, cela fonctionne\ntrès bien, mais quand on se retrouve avec les fichiers volumineux que BackupPC a décidé de découper, cela ne fonctionne\nplus.",[12,4064,4065,4066,4071],{},"De plus, dans certains cas, BackupPC ",[49,4067,4070],{"href":4068,"rel":4069},"https:\u002F\u002Fgithub.com\u002Fbackuppc\u002Fbackuppc-xs\u002Fblob\u002Fmaster\u002Fbpc_fileZIO.c#L214",[347],"remplace certains octets","\npour indiquer que l'on est au début d'un nouveau bloc de données zlib, ou pour indiquer que l'on a ajouté à la fin une\nchecksum md4.",[12,4073,4074,4075,4077],{},"Comme certains fichiers peuvent être volumineux, je ne peux pas faire un ",[184,4076,4024],{}," pour lire le fichier en\nmémoire et le modifier.",[12,4079,4080,4081,4086],{},"Le fichier ",[49,4082,4085],{"href":4083,"rel":4084},"https:\u002F\u002Fgithub.com\u002Fbackuppc\u002Fbackuppc-xs\u002Fblob\u002Fmaster\u002Fbpc_fileZIO.c",[347],"bpc_fileZIO.c"," permet de comprendre\ncomment sont encodés les fichiers compressés.",[12,4088,4089],{},"Lors de la lecture d'un fichier compressé, si la lecture de la compression s'arrête et qu'il reste encore des octets\nà lire, alors il faut recommencer une nouvelle décompression.",[12,4091,4092],{},"On peut aussi voir le bout de code suivant :",[2105,4094,4098],{"className":4095,"code":4096,"language":4097,"meta":1646,"style":1646},"language-c shiki shiki-themes one-dark-pro","\u002F\u002F https:\u002F\u002Fgithub.com\u002Fbackuppc\u002Fbackuppc-xs\u002Fblob\u002Fmaster\u002Fbpc_fileZIO.c#L219C15-L237C18\nif ( fd->strm.next_in[0] == 0xd6 || fd->strm.next_in[0] == 0xd7 ) {\n    \u002F*\n     * Flag 0xd6 or 0xd7 means this is a compressed file with\n     * appended md4 block checksums for rsync.  Change\n     * the first byte back to 0x78 and proceed.\n     *\u002F\n    fd->strm.next_in[0] = 0x78;\n} else if ( fd->strm.next_in[0] == 0xb3 ) {\n    \u002F*\n     * Flag 0xb3 means this is the start of the rsync\n     * block checksums, so consider this as EOF for\n     * the compressed file.  Also seek the file so\n     * it is positioned at the 0xb3.\n     *\u002F\n    fd->eof = 1;\n    \u002F* TODO: check return status *\u002F\n    lseek(fd->fd, -fd->strm.avail_in, SEEK_CUR);\n    fd->strm.avail_in = 0;\n}\n","c",[184,4099,4100,4105,4166,4171,4176,4181,4186,4191,4220,4254,4258,4263,4268,4273,4278,4282,4297,4302,4334,4352],{"__ignoreMap":1646},[2113,4101,4102],{"class":2115,"line":2116},[2113,4103,4104],{"class":2396},"\u002F\u002F https:\u002F\u002Fgithub.com\u002Fbackuppc\u002Fbackuppc-xs\u002Fblob\u002Fmaster\u002Fbpc_fileZIO.c#L219C15-L237C18\n",[2113,4106,4107,4110,4113,4116,4119,4122,4125,4127,4130,4133,4135,4138,4141,4144,4146,4148,4150,4152,4154,4156,4158,4160,4163],{"class":2115,"line":1647},[2113,4108,4109],{"class":2326},"if",[2113,4111,4112],{"class":2119}," ( fd",[2113,4114,4115],{"class":2326},"->",[2113,4117,4118],{"class":2119},"strm.",[2113,4120,4121],{"class":2330},"next_in",[2113,4123,4124],{"class":2119},"[",[2113,4126,3095],{"class":2166},[2113,4128,4129],{"class":2119},"] ",[2113,4131,4132],{"class":2326},"==",[2113,4134,3006],{"class":2330},[2113,4136,4137],{"class":2166},"d6",[2113,4139,4140],{"class":2334}," ||",[2113,4142,4143],{"class":2119}," fd",[2113,4145,4115],{"class":2326},[2113,4147,4118],{"class":2119},[2113,4149,4121],{"class":2330},[2113,4151,4124],{"class":2119},[2113,4153,3095],{"class":2166},[2113,4155,4129],{"class":2119},[2113,4157,4132],{"class":2326},[2113,4159,3006],{"class":2330},[2113,4161,4162],{"class":2166},"d7",[2113,4164,4165],{"class":2119}," ) {\n",[2113,4167,4168],{"class":2115,"line":1652},[2113,4169,4170],{"class":2396},"    \u002F*\n",[2113,4172,4173],{"class":2115,"line":2185},[2113,4174,4175],{"class":2396},"     * Flag 0xd6 or 0xd7 means this is a compressed file with\n",[2113,4177,4178],{"class":2115,"line":2230},[2113,4179,4180],{"class":2396},"     * appended md4 block checksums for rsync.  Change\n",[2113,4182,4183],{"class":2115,"line":2235},[2113,4184,4185],{"class":2396},"     * the first byte back to 0x78 and proceed.\n",[2113,4187,4188],{"class":2115,"line":2241},[2113,4189,4190],{"class":2396},"     *\u002F\n",[2113,4192,4193,4196,4198,4201,4203,4205,4207,4209,4211,4213,4215,4218],{"class":2115,"line":2246},[2113,4194,4195],{"class":2414},"    fd",[2113,4197,4115],{"class":2119},[2113,4199,4200],{"class":2414},"strm",[2113,4202,179],{"class":2119},[2113,4204,4121],{"class":2330},[2113,4206,4124],{"class":2119},[2113,4208,3095],{"class":2166},[2113,4210,4129],{"class":2119},[2113,4212,2335],{"class":2326},[2113,4214,3006],{"class":2330},[2113,4216,4217],{"class":2166},"78",[2113,4219,2487],{"class":2119},[2113,4221,4222,4225,4228,4231,4233,4235,4237,4239,4241,4243,4245,4247,4249,4252],{"class":2115,"line":2464},[2113,4223,4224],{"class":2119},"} ",[2113,4226,4227],{"class":2326},"else",[2113,4229,4230],{"class":2326}," if",[2113,4232,4112],{"class":2119},[2113,4234,4115],{"class":2326},[2113,4236,4118],{"class":2119},[2113,4238,4121],{"class":2330},[2113,4240,4124],{"class":2119},[2113,4242,3095],{"class":2166},[2113,4244,4129],{"class":2119},[2113,4246,4132],{"class":2326},[2113,4248,3006],{"class":2330},[2113,4250,4251],{"class":2166},"b3",[2113,4253,4165],{"class":2119},[2113,4255,4256],{"class":2115,"line":2085},[2113,4257,4170],{"class":2396},[2113,4259,4260],{"class":2115,"line":2514},[2113,4261,4262],{"class":2396},"     * Flag 0xb3 means this is the start of the rsync\n",[2113,4264,4265],{"class":2115,"line":2533},[2113,4266,4267],{"class":2396},"     * block checksums, so consider this as EOF for\n",[2113,4269,4270],{"class":2115,"line":2556},[2113,4271,4272],{"class":2396},"     * the compressed file.  Also seek the file so\n",[2113,4274,4275],{"class":2115,"line":2569},[2113,4276,4277],{"class":2396},"     * it is positioned at the 0xb3.\n",[2113,4279,4280],{"class":2115,"line":2575},[2113,4281,4190],{"class":2396},[2113,4283,4284,4286,4288,4291,4293,4295],{"class":2115,"line":2596},[2113,4285,4195],{"class":2414},[2113,4287,4115],{"class":2119},[2113,4289,4290],{"class":2330},"eof",[2113,4292,2153],{"class":2326},[2113,4294,2850],{"class":2166},[2113,4296,2487],{"class":2119},[2113,4298,4299],{"class":2115,"line":3192},[2113,4300,4301],{"class":2396},"    \u002F* TODO: check return status *\u002F\n",[2113,4303,4304,4307,4309,4312,4314,4316,4318,4320,4322,4324,4326,4328,4331],{"class":2115,"line":3213},[2113,4305,4306],{"class":2133},"    lseek",[2113,4308,2423],{"class":2119},[2113,4310,4311],{"class":2414},"fd",[2113,4313,4115],{"class":2119},[2113,4315,4311],{"class":2330},[2113,4317,932],{"class":2119},[2113,4319,3242],{"class":2326},[2113,4321,4311],{"class":2414},[2113,4323,4115],{"class":2119},[2113,4325,4200],{"class":2414},[2113,4327,179],{"class":2119},[2113,4329,4330],{"class":2330},"avail_in",[2113,4332,4333],{"class":2119},", SEEK_CUR);\n",[2113,4335,4336,4338,4340,4342,4344,4346,4348,4350],{"class":2115,"line":3236},[2113,4337,4195],{"class":2414},[2113,4339,4115],{"class":2119},[2113,4341,4200],{"class":2414},[2113,4343,179],{"class":2119},[2113,4345,4330],{"class":2330},[2113,4347,2153],{"class":2326},[2113,4349,3208],{"class":2166},[2113,4351,2487],{"class":2119},[2113,4353,4354],{"class":2115,"line":3248},[2113,4355,2599],{"class":2119},[12,4357,4358,4359,939,4362,4365,4366,4369,4370,4373],{},"Si, en début de flux, on trouve ",[184,4360,4361],{},"0xd6",[184,4363,4364],{},"0xd7",", il faut changer le premier octet en ",[184,4367,4368],{},"0x78"," et continuer la lecture. En\nfin de flux, si on trouve ",[184,4371,4372],{},"0xb3",", on considère que c'est la fin du fichier compressé. (De notre côté, nous ne nous\nintéresserons pas à la partie checksum md4).",[12,4375,3849,4376,4378,4379,4381],{},[184,4377,3843],{}," ne permet pas de faire cela simplement. J'ai donc dû lire le code de ",[184,4380,3843],{}," pour comprendre\ncomment je pouvais agir pour lire un fichier compressé de BackupPC.",[12,4383,4384,4391,4392,4394,4395,4398,4399,4402,4403,4406],{},[49,4385,4388,4389],{"href":4386,"rel":4387},"https:\u002F\u002Fgithub.com\u002Frust-lang\u002Fflate2-rs\u002Fblob\u002Fmain\u002Fsrc\u002Fbufreader.rs#L73",[347],"Le code suivant de la bibliothèque ",[184,4390,3843],{},"\npermet de voir que ",[184,4393,3843],{}," a besoin, dans son constructeur, d'un ",[184,4396,4397],{},"BufRead"," pour lire un fichier compressé. Il utilise\nla méthode ",[184,4400,4401],{},"fill_buf"," pour remplir un tampon et la méthode ",[184,4404,4405],{},"consume"," pour consommer le tampon.",[2105,4408,4410],{"className":3855,"code":4409,"language":1775,"meta":1646,"style":1646},"\u002F\u002F https:\u002F\u002Fgithub.com\u002Frust-lang\u002Fflate2-rs\u002Fblob\u002Fmain\u002Fsrc\u002Fbufreader.rs#L73\nimpl\u003CR: Read> Read for BufReader\u003CR> {\n    fn read(&mut self, buf: &mut [u8]) -> io::Result\u003Cusize> {\n        \u002F\u002F If we don't have any buffered data and we're doing a massive read\n        \u002F\u002F (larger than our internal buffer), bypass our internal buffer\n        \u002F\u002F entirely.\n        if self.pos == self.cap && buf.len() >= self.buf.len() {\n            return self.inner.read(buf);\n        }\n        let nread = {\n            let mut rem = self.fill_buf()?;\n            rem.read(buf)?\n        };\n        self.consume(nread);\n        Ok(nread)\n    }\n}\n",[184,4411,4412,4417,4450,4499,4504,4509,4514,4555,4574,4579,4591,4612,4628,4633,4649,4661,4666],{"__ignoreMap":1646},[2113,4413,4414],{"class":2115,"line":2116},[2113,4415,4416],{"class":2396},"\u002F\u002F https:\u002F\u002Fgithub.com\u002Frust-lang\u002Fflate2-rs\u002Fblob\u002Fmain\u002Fsrc\u002Fbufreader.rs#L73\n",[2113,4418,4419,4422,4424,4427,4430,4433,4436,4438,4441,4443,4445,4447],{"class":2115,"line":1647},[2113,4420,4421],{"class":2326},"impl",[2113,4423,3109],{"class":2119},[2113,4425,4426],{"class":2414},"R",[2113,4428,4429],{"class":2119},": ",[2113,4431,4432],{"class":2414},"Read",[2113,4434,4435],{"class":2119},"> ",[2113,4437,4432],{"class":2414},[2113,4439,4440],{"class":2326}," for",[2113,4442,3956],{"class":2414},[2113,4444,3109],{"class":2119},[2113,4446,4426],{"class":2414},[2113,4448,4449],{"class":2119},"> {\n",[2113,4451,4452,4455,4458,4460,4462,4465,4467,4470,4473,4475,4478,4481,4484,4487,4489,4492,4494,4497],{"class":2115,"line":1652},[2113,4453,4454],{"class":2326},"    fn",[2113,4456,4457],{"class":2133}," read",[2113,4459,4027],{"class":2119},[2113,4461,4030],{"class":2326},[2113,4463,4464],{"class":2414}," self",[2113,4466,932],{"class":2119},[2113,4468,4469],{"class":2330},"buf",[2113,4471,4472],{"class":2119},": &",[2113,4474,4030],{"class":2326},[2113,4476,4477],{"class":2119}," [",[2113,4479,4480],{"class":2414},"u8",[2113,4482,4483],{"class":2119},"]) -> ",[2113,4485,4486],{"class":2414},"io",[2113,4488,2418],{"class":2119},[2113,4490,4491],{"class":2414},"Result",[2113,4493,3109],{"class":2119},[2113,4495,4496],{"class":2414},"usize",[2113,4498,4449],{"class":2119},[2113,4500,4501],{"class":2115,"line":2185},[2113,4502,4503],{"class":2396},"        \u002F\u002F If we don't have any buffered data and we're doing a massive read\n",[2113,4505,4506],{"class":2115,"line":2230},[2113,4507,4508],{"class":2396},"        \u002F\u002F (larger than our internal buffer), bypass our internal buffer\n",[2113,4510,4511],{"class":2115,"line":2235},[2113,4512,4513],{"class":2396},"        \u002F\u002F entirely.\n",[2113,4515,4516,4519,4521,4524,4526,4528,4531,4533,4536,4538,4541,4544,4546,4548,4551,4553],{"class":2115,"line":2241},[2113,4517,4518],{"class":2326},"        if",[2113,4520,4464],{"class":2414},[2113,4522,4523],{"class":2119},".pos ",[2113,4525,4132],{"class":2334},[2113,4527,4464],{"class":2414},[2113,4529,4530],{"class":2119},".cap ",[2113,4532,3104],{"class":2334},[2113,4534,4535],{"class":2330}," buf",[2113,4537,179],{"class":2119},[2113,4539,4540],{"class":2133},"len",[2113,4542,4543],{"class":2119},"() ",[2113,4545,2481],{"class":2334},[2113,4547,4464],{"class":2414},[2113,4549,4550],{"class":2119},".buf.",[2113,4552,4540],{"class":2133},[2113,4554,3912],{"class":2119},[2113,4556,4557,4560,4562,4565,4568,4570,4572],{"class":2115,"line":2246},[2113,4558,4559],{"class":2326},"            return",[2113,4561,4464],{"class":2414},[2113,4563,4564],{"class":2119},".inner.",[2113,4566,4567],{"class":2133},"read",[2113,4569,2423],{"class":2119},[2113,4571,4469],{"class":2330},[2113,4573,2553],{"class":2119},[2113,4575,4576],{"class":2115,"line":2464},[2113,4577,4578],{"class":2119},"        }\n",[2113,4580,4581,4584,4587,4589],{"class":2115,"line":2085},[2113,4582,4583],{"class":2326},"        let",[2113,4585,4586],{"class":2330}," nread",[2113,4588,2153],{"class":2334},[2113,4590,2647],{"class":2119},[2113,4592,4593,4596,4598,4601,4603,4605,4607,4609],{"class":2115,"line":2514},[2113,4594,4595],{"class":2326},"            let",[2113,4597,3975],{"class":2326},[2113,4599,4600],{"class":2330}," rem",[2113,4602,2153],{"class":2334},[2113,4604,4464],{"class":2414},[2113,4606,179],{"class":2119},[2113,4608,4401],{"class":2133},[2113,4610,4611],{"class":2119},"()?;\n",[2113,4613,4614,4617,4619,4621,4623,4625],{"class":2115,"line":2533},[2113,4615,4616],{"class":2330},"            rem",[2113,4618,179],{"class":2119},[2113,4620,4567],{"class":2133},[2113,4622,2423],{"class":2119},[2113,4624,4469],{"class":2330},[2113,4626,4627],{"class":2119},")?\n",[2113,4629,4630],{"class":2115,"line":2556},[2113,4631,4632],{"class":2119},"        };\n",[2113,4634,4635,4638,4640,4642,4644,4647],{"class":2115,"line":2569},[2113,4636,4637],{"class":2414},"        self",[2113,4639,179],{"class":2119},[2113,4641,4405],{"class":2133},[2113,4643,2423],{"class":2119},[2113,4645,4646],{"class":2330},"nread",[2113,4648,2553],{"class":2119},[2113,4650,4651,4654,4656,4658],{"class":2115,"line":2575},[2113,4652,4653],{"class":2414},"        Ok",[2113,4655,2423],{"class":2119},[2113,4657,4646],{"class":2330},[2113,4659,4660],{"class":2119},")\n",[2113,4662,4663],{"class":2115,"line":2596},[2113,4664,4665],{"class":2119},"    }\n",[2113,4667,4668],{"class":2115,"line":3192},[2113,4669,2599],{"class":2119},[12,4671,4672],{},"J'ai donc commencé par développer un adaptateur qui s'intercale entre le fichier et le décompresseur. Cet adaptateur\na pour objectif de reproduire le comportement de BackupPC et de remplacer les octets en début de fichier.",[2105,4674,4676],{"className":3855,"code":4675,"language":1775,"meta":1646,"style":1646},"struct InterpretAdapter\u003CR: BufRead> {\n    inner: R,\n    first: bool,\n    temp: Option\u003CVec\u003Cu8>>,\n}\n\nimpl\u003CR: BufRead> InterpretAdapter\u003CR> {\n    fn new(inner: R) -> Self {\n        Self {\n            inner,\n            first: true,\n            temp: None,\n        }\n    }\n\n    fn reset(&mut self) {\n        self.first = true;\n        self.temp = None;\n    }\n}\n...\nimpl\u003CR: BufRead> BufRead for InterpretAdapter\u003CR> {\n    fn fill_buf(&mut self) -> io::Result\u003C&[u8]> {\n        if self.temp.is_none() {\n            let buf = self.inner.fill_buf()?;\n            let mut buf = buf.to_vec();\n\n            if self.first && !buf.is_empty() {\n                self.first = false;\n\n                if buf[0] == 0xd6 || buf[0] == 0xd7 {\n                    buf[0] = 0x78;\n                } else if buf[0] == 0xb3 {\n                    \u002F\u002F EOF\n                    buf = Vec::new();\n                }\n            }\n\n            self.temp = Some(buf);\n        }\n\n        Ok(self.temp.as_ref().unwrap())\n    }\n\n    fn consume(&mut self, amt: usize) {\n        if amt > 0 {\n            self.temp = None;\n            self.inner.consume(amt);\n        }\n    }\n}\n",[184,4677,4678,4695,4707,4719,4741,4745,4749,4772,4796,4803,4810,4822,4834,4838,4842,4846,4861,4875,4889,4893,4897,4903,4929,4959,4974,4991,5011,5016,5040,5055,5060,5096,5115,5140,5146,5161,5167,5173,5178,5197,5202,5207,5230,5235,5240,5265,5280,5293,5308,5313,5318],{"__ignoreMap":1646},[2113,4679,4680,4682,4685,4687,4689,4691,4693],{"class":2115,"line":2116},[2113,4681,2641],{"class":2326},[2113,4683,4684],{"class":2414}," InterpretAdapter",[2113,4686,3109],{"class":2119},[2113,4688,4426],{"class":2414},[2113,4690,4429],{"class":2119},[2113,4692,4397],{"class":2414},[2113,4694,4449],{"class":2119},[2113,4696,4697,4700,4702,4704],{"class":2115,"line":1647},[2113,4698,4699],{"class":2330},"    inner",[2113,4701,4429],{"class":2119},[2113,4703,4426],{"class":2414},[2113,4705,4706],{"class":2119},",\n",[2113,4708,4709,4712,4714,4717],{"class":2115,"line":1652},[2113,4710,4711],{"class":2330},"    first",[2113,4713,4429],{"class":2119},[2113,4715,4716],{"class":2414},"bool",[2113,4718,4706],{"class":2119},[2113,4720,4721,4724,4726,4729,4731,4734,4736,4738],{"class":2115,"line":2185},[2113,4722,4723],{"class":2330},"    temp",[2113,4725,4429],{"class":2119},[2113,4727,4728],{"class":2414},"Option",[2113,4730,3109],{"class":2119},[2113,4732,4733],{"class":2414},"Vec",[2113,4735,3109],{"class":2119},[2113,4737,4480],{"class":2414},[2113,4739,4740],{"class":2119},">>,\n",[2113,4742,4743],{"class":2115,"line":2230},[2113,4744,2599],{"class":2119},[2113,4746,4747],{"class":2115,"line":2235},[2113,4748,2125],{"emptyLinePlaceholder":1767},[2113,4750,4751,4753,4755,4757,4759,4761,4763,4766,4768,4770],{"class":2115,"line":2241},[2113,4752,4421],{"class":2326},[2113,4754,3109],{"class":2119},[2113,4756,4426],{"class":2414},[2113,4758,4429],{"class":2119},[2113,4760,4397],{"class":2414},[2113,4762,4435],{"class":2119},[2113,4764,4765],{"class":2414},"InterpretAdapter",[2113,4767,3109],{"class":2119},[2113,4769,4426],{"class":2414},[2113,4771,4449],{"class":2119},[2113,4773,4774,4776,4779,4781,4784,4786,4788,4791,4794],{"class":2115,"line":2246},[2113,4775,4454],{"class":2326},[2113,4777,4778],{"class":2133}," new",[2113,4780,2423],{"class":2119},[2113,4782,4783],{"class":2330},"inner",[2113,4785,4429],{"class":2119},[2113,4787,4426],{"class":2414},[2113,4789,4790],{"class":2119},") -> ",[2113,4792,4793],{"class":2414},"Self",[2113,4795,2647],{"class":2119},[2113,4797,4798,4801],{"class":2115,"line":2464},[2113,4799,4800],{"class":2414},"        Self",[2113,4802,2647],{"class":2119},[2113,4804,4805,4808],{"class":2115,"line":2085},[2113,4806,4807],{"class":2330},"            inner",[2113,4809,4706],{"class":2119},[2113,4811,4812,4815,4817,4820],{"class":2115,"line":2514},[2113,4813,4814],{"class":2330},"            first",[2113,4816,4429],{"class":2119},[2113,4818,4819],{"class":2166},"true",[2113,4821,4706],{"class":2119},[2113,4823,4824,4827,4829,4832],{"class":2115,"line":2533},[2113,4825,4826],{"class":2330},"            temp",[2113,4828,4429],{"class":2119},[2113,4830,4831],{"class":2414},"None",[2113,4833,4706],{"class":2119},[2113,4835,4836],{"class":2115,"line":2556},[2113,4837,4578],{"class":2119},[2113,4839,4840],{"class":2115,"line":2569},[2113,4841,4665],{"class":2119},[2113,4843,4844],{"class":2115,"line":2575},[2113,4845,2125],{"emptyLinePlaceholder":1767},[2113,4847,4848,4850,4853,4855,4857,4859],{"class":2115,"line":2596},[2113,4849,4454],{"class":2326},[2113,4851,4852],{"class":2133}," reset",[2113,4854,4027],{"class":2119},[2113,4856,4030],{"class":2326},[2113,4858,4464],{"class":2414},[2113,4860,2433],{"class":2119},[2113,4862,4863,4865,4868,4870,4873],{"class":2115,"line":3192},[2113,4864,4637],{"class":2414},[2113,4866,4867],{"class":2119},".first ",[2113,4869,2335],{"class":2334},[2113,4871,4872],{"class":2166}," true",[2113,4874,2487],{"class":2119},[2113,4876,4877,4879,4882,4884,4887],{"class":2115,"line":3213},[2113,4878,4637],{"class":2414},[2113,4880,4881],{"class":2119},".temp ",[2113,4883,2335],{"class":2334},[2113,4885,4886],{"class":2414}," None",[2113,4888,2487],{"class":2119},[2113,4890,4891],{"class":2115,"line":3236},[2113,4892,4665],{"class":2119},[2113,4894,4895],{"class":2115,"line":3248},[2113,4896,2599],{"class":2119},[2113,4898,4900],{"class":2115,"line":4899},21,[2113,4901,4902],{"class":2119},"...\n",[2113,4904,4905,4907,4909,4911,4913,4915,4917,4919,4921,4923,4925,4927],{"class":2115,"line":1777},[2113,4906,4421],{"class":2326},[2113,4908,3109],{"class":2119},[2113,4910,4426],{"class":2414},[2113,4912,4429],{"class":2119},[2113,4914,4397],{"class":2414},[2113,4916,4435],{"class":2119},[2113,4918,4397],{"class":2414},[2113,4920,4440],{"class":2326},[2113,4922,4684],{"class":2414},[2113,4924,3109],{"class":2119},[2113,4926,4426],{"class":2414},[2113,4928,4449],{"class":2119},[2113,4930,4932,4934,4937,4939,4941,4943,4945,4947,4949,4951,4954,4956],{"class":2115,"line":4931},23,[2113,4933,4454],{"class":2326},[2113,4935,4936],{"class":2133}," fill_buf",[2113,4938,4027],{"class":2119},[2113,4940,4030],{"class":2326},[2113,4942,4464],{"class":2414},[2113,4944,4790],{"class":2119},[2113,4946,4486],{"class":2414},[2113,4948,2418],{"class":2119},[2113,4950,4491],{"class":2414},[2113,4952,4953],{"class":2119},"\u003C&[",[2113,4955,4480],{"class":2414},[2113,4957,4958],{"class":2119},"]> {\n",[2113,4960,4962,4964,4966,4969,4972],{"class":2115,"line":4961},24,[2113,4963,4518],{"class":2326},[2113,4965,4464],{"class":2414},[2113,4967,4968],{"class":2119},".temp.",[2113,4970,4971],{"class":2133},"is_none",[2113,4973,3912],{"class":2119},[2113,4975,4977,4979,4981,4983,4985,4987,4989],{"class":2115,"line":4976},25,[2113,4978,4595],{"class":2326},[2113,4980,4535],{"class":2330},[2113,4982,2153],{"class":2334},[2113,4984,4464],{"class":2414},[2113,4986,4564],{"class":2119},[2113,4988,4401],{"class":2133},[2113,4990,4611],{"class":2119},[2113,4992,4994,4996,4998,5000,5002,5004,5006,5009],{"class":2115,"line":4993},26,[2113,4995,4595],{"class":2326},[2113,4997,3975],{"class":2326},[2113,4999,4535],{"class":2330},[2113,5001,2153],{"class":2334},[2113,5003,4535],{"class":2330},[2113,5005,179],{"class":2119},[2113,5007,5008],{"class":2133},"to_vec",[2113,5010,3944],{"class":2119},[2113,5012,5014],{"class":2115,"line":5013},27,[2113,5015,2125],{"emptyLinePlaceholder":1767},[2113,5017,5019,5022,5024,5026,5028,5031,5033,5035,5038],{"class":2115,"line":5018},28,[2113,5020,5021],{"class":2326},"            if",[2113,5023,4464],{"class":2414},[2113,5025,4867],{"class":2119},[2113,5027,3104],{"class":2334},[2113,5029,5030],{"class":2334}," !",[2113,5032,4469],{"class":2330},[2113,5034,179],{"class":2119},[2113,5036,5037],{"class":2133},"is_empty",[2113,5039,3912],{"class":2119},[2113,5041,5043,5046,5048,5050,5053],{"class":2115,"line":5042},29,[2113,5044,5045],{"class":2414},"                self",[2113,5047,4867],{"class":2119},[2113,5049,2335],{"class":2334},[2113,5051,5052],{"class":2166}," false",[2113,5054,2487],{"class":2119},[2113,5056,5058],{"class":2115,"line":5057},30,[2113,5059,2125],{"emptyLinePlaceholder":1767},[2113,5061,5063,5066,5068,5070,5072,5074,5076,5079,5081,5083,5085,5087,5089,5091,5094],{"class":2115,"line":5062},31,[2113,5064,5065],{"class":2326},"                if",[2113,5067,4535],{"class":2330},[2113,5069,4124],{"class":2119},[2113,5071,3095],{"class":2166},[2113,5073,4129],{"class":2119},[2113,5075,4132],{"class":2334},[2113,5077,5078],{"class":2166}," 0xd6",[2113,5080,4140],{"class":2334},[2113,5082,4535],{"class":2330},[2113,5084,4124],{"class":2119},[2113,5086,3095],{"class":2166},[2113,5088,4129],{"class":2119},[2113,5090,4132],{"class":2334},[2113,5092,5093],{"class":2166}," 0xd7",[2113,5095,2647],{"class":2119},[2113,5097,5099,5102,5104,5106,5108,5110,5113],{"class":2115,"line":5098},32,[2113,5100,5101],{"class":2330},"                    buf",[2113,5103,4124],{"class":2119},[2113,5105,3095],{"class":2166},[2113,5107,4129],{"class":2119},[2113,5109,2335],{"class":2334},[2113,5111,5112],{"class":2166}," 0x78",[2113,5114,2487],{"class":2119},[2113,5116,5118,5121,5123,5125,5127,5129,5131,5133,5135,5138],{"class":2115,"line":5117},33,[2113,5119,5120],{"class":2119},"                } ",[2113,5122,4227],{"class":2326},[2113,5124,4230],{"class":2326},[2113,5126,4535],{"class":2330},[2113,5128,4124],{"class":2119},[2113,5130,3095],{"class":2166},[2113,5132,4129],{"class":2119},[2113,5134,4132],{"class":2334},[2113,5136,5137],{"class":2166}," 0xb3",[2113,5139,2647],{"class":2119},[2113,5141,5143],{"class":2115,"line":5142},34,[2113,5144,5145],{"class":2396},"                    \u002F\u002F EOF\n",[2113,5147,5149,5151,5153,5155,5157,5159],{"class":2115,"line":5148},35,[2113,5150,5101],{"class":2330},[2113,5152,2153],{"class":2334},[2113,5154,4008],{"class":2414},[2113,5156,2418],{"class":2119},[2113,5158,3961],{"class":2133},[2113,5160,3944],{"class":2119},[2113,5162,5164],{"class":2115,"line":5163},36,[2113,5165,5166],{"class":2119},"                }\n",[2113,5168,5170],{"class":2115,"line":5169},37,[2113,5171,5172],{"class":2119},"            }\n",[2113,5174,5176],{"class":2115,"line":5175},38,[2113,5177,2125],{"emptyLinePlaceholder":1767},[2113,5179,5181,5184,5186,5188,5191,5193,5195],{"class":2115,"line":5180},39,[2113,5182,5183],{"class":2414},"            self",[2113,5185,4881],{"class":2119},[2113,5187,2335],{"class":2334},[2113,5189,5190],{"class":2414}," Some",[2113,5192,2423],{"class":2119},[2113,5194,4469],{"class":2330},[2113,5196,2553],{"class":2119},[2113,5198,5200],{"class":2115,"line":5199},40,[2113,5201,4578],{"class":2119},[2113,5203,5205],{"class":2115,"line":5204},41,[2113,5206,2125],{"emptyLinePlaceholder":1767},[2113,5208,5210,5212,5214,5217,5219,5222,5225,5227],{"class":2115,"line":5209},42,[2113,5211,4653],{"class":2414},[2113,5213,2423],{"class":2119},[2113,5215,5216],{"class":2414},"self",[2113,5218,4968],{"class":2119},[2113,5220,5221],{"class":2133},"as_ref",[2113,5223,5224],{"class":2119},"().",[2113,5226,3941],{"class":2133},[2113,5228,5229],{"class":2119},"())\n",[2113,5231,5233],{"class":2115,"line":5232},43,[2113,5234,4665],{"class":2119},[2113,5236,5238],{"class":2115,"line":5237},44,[2113,5239,2125],{"emptyLinePlaceholder":1767},[2113,5241,5243,5245,5248,5250,5252,5254,5256,5259,5261,5263],{"class":2115,"line":5242},45,[2113,5244,4454],{"class":2326},[2113,5246,5247],{"class":2133}," consume",[2113,5249,4027],{"class":2119},[2113,5251,4030],{"class":2326},[2113,5253,4464],{"class":2414},[2113,5255,932],{"class":2119},[2113,5257,5258],{"class":2330},"amt",[2113,5260,4429],{"class":2119},[2113,5262,4496],{"class":2414},[2113,5264,2433],{"class":2119},[2113,5266,5268,5270,5273,5276,5278],{"class":2115,"line":5267},46,[2113,5269,4518],{"class":2326},[2113,5271,5272],{"class":2330}," amt",[2113,5274,5275],{"class":2334}," >",[2113,5277,3208],{"class":2166},[2113,5279,2647],{"class":2119},[2113,5281,5283,5285,5287,5289,5291],{"class":2115,"line":5282},47,[2113,5284,5183],{"class":2414},[2113,5286,4881],{"class":2119},[2113,5288,2335],{"class":2334},[2113,5290,4886],{"class":2414},[2113,5292,2487],{"class":2119},[2113,5294,5296,5298,5300,5302,5304,5306],{"class":2115,"line":5295},48,[2113,5297,5183],{"class":2414},[2113,5299,4564],{"class":2119},[2113,5301,4405],{"class":2133},[2113,5303,2423],{"class":2119},[2113,5305,5258],{"class":2330},[2113,5307,2553],{"class":2119},[2113,5309,5311],{"class":2115,"line":5310},49,[2113,5312,4578],{"class":2119},[2113,5314,5316],{"class":2115,"line":5315},50,[2113,5317,4665],{"class":2119},[2113,5319,5321],{"class":2115,"line":5320},51,[2113,5322,2599],{"class":2119},[12,5324,5325,5326,5329,5330,5333,5334,5336,5337,5339,5340,792,5342,5344],{},"Cet adaptateur s'intercale entre le ",[184,5327,5328],{},"BufReader"," et le ",[184,5331,5332],{},"ZLibDecoder",". Du point de vue du ",[184,5335,5332],{},", il doit\nse comporter comme un ",[184,5338,5328],{}," et donc implémenter les méthodes ",[184,5341,4401],{},[184,5343,4405],{},". Dans ces méthodes, je dois\nretourner un tampon.",[12,5346,5347,5348,5350],{},"Je ne peux pas modifier directement le tampon du ",[184,5349,5328],{}," car ce dernier est une référence en lecture seule. Je\ncopie donc le contenu (je n'ai pas trouvé de meilleure manière de faire cela).",[12,5352,5353,5354,5357,5358,5361,5362,5364],{},"Enfin, on peut développer le ",[184,5355,5356],{},"Reader"," qui sera capable de lire les fichiers compressés. Pour cela, j'ai d'abord\nimplémenté une méthode ",[184,5359,5360],{},"read_some_bytes"," qui permet de lire un certain nombre d'octets. Si jamais on arrive à la fin\nd'une section, alors la méthode peut retourner moins que la taille du tampon. Généralement, cela arrive en fin de\nfichier. Quand j'ai utilisé directement cette méthode en tant que méthode ",[184,5363,4567],{},", je me suis retrouvé avec des\ncorruptions.",[2105,5366,5368],{"className":3855,"code":5367,"language":1775,"meta":1646,"style":1646},"pub struct BackupPCReader\u003CR: Read> {\n    decoder: Option\u003CZlibDecoder\u003CInterpretAdapter\u003CBufReader\u003CR>>>>,\n}\n...\nfn read_some_bytes(&mut self, buf: &mut [u8]) -> io::Result\u003Cusize> {\n    loop {\n        let decoder = self.decoder.as_mut();\n        if decoder.is_none() {\n            return Ok(0);\n        }\n\n        let decoder_read_result = decoder.unwrap().read(buf);\n\n        let count = match decoder_read_result {\n            Ok(count) => {\n                count\n            }\n            Err(e) => {\n                return Err(e);\n            }\n        };\n\n        if count != 0 {\n            return Ok(count);\n        }\n\n        if count == 0 {\n            let decoder = self.decoder.take();\n            if let Some(decoder) = decoder {\n                let mut reader = decoder.into_inner();\n                \u002F\u002F S'il reste encore des octets à lire dans reader alors on continue, sinon on s'arrête\n                if reader.fill_buf()?.is_empty() {\n                    return Ok(0);\n                }\n                reader.reset();\n\n                self.decoder = Some(ZlibDecoder::new(reader));\n            }\n        }\n    }\n}\n",[184,5369,5370,5391,5419,5423,5427,5466,5473,5492,5504,5517,5521,5525,5550,5554,5570,5583,5588,5592,5604,5618,5622,5626,5630,5643,5655,5659,5663,5676,5693,5716,5737,5742,5759,5772,5776,5788,5792,5819,5823,5827,5831],{"__ignoreMap":1646},[2113,5371,5372,5375,5378,5381,5383,5385,5387,5389],{"class":2115,"line":2116},[2113,5373,5374],{"class":2326},"pub",[2113,5376,5377],{"class":2326}," struct",[2113,5379,5380],{"class":2414}," BackupPCReader",[2113,5382,3109],{"class":2119},[2113,5384,4426],{"class":2414},[2113,5386,4429],{"class":2119},[2113,5388,4432],{"class":2414},[2113,5390,4449],{"class":2119},[2113,5392,5393,5396,5398,5400,5402,5404,5406,5408,5410,5412,5414,5416],{"class":2115,"line":1647},[2113,5394,5395],{"class":2330},"    decoder",[2113,5397,4429],{"class":2119},[2113,5399,4728],{"class":2414},[2113,5401,3109],{"class":2119},[2113,5403,3876],{"class":2414},[2113,5405,3109],{"class":2119},[2113,5407,4765],{"class":2414},[2113,5409,3109],{"class":2119},[2113,5411,5328],{"class":2414},[2113,5413,3109],{"class":2119},[2113,5415,4426],{"class":2414},[2113,5417,5418],{"class":2119},">>>>,\n",[2113,5420,5421],{"class":2115,"line":1652},[2113,5422,2599],{"class":2119},[2113,5424,5425],{"class":2115,"line":2185},[2113,5426,4902],{"class":2119},[2113,5428,5429,5431,5434,5436,5438,5440,5442,5444,5446,5448,5450,5452,5454,5456,5458,5460,5462,5464],{"class":2115,"line":2230},[2113,5430,3906],{"class":2326},[2113,5432,5433],{"class":2133}," read_some_bytes",[2113,5435,4027],{"class":2119},[2113,5437,4030],{"class":2326},[2113,5439,4464],{"class":2414},[2113,5441,932],{"class":2119},[2113,5443,4469],{"class":2330},[2113,5445,4472],{"class":2119},[2113,5447,4030],{"class":2326},[2113,5449,4477],{"class":2119},[2113,5451,4480],{"class":2414},[2113,5453,4483],{"class":2119},[2113,5455,4486],{"class":2414},[2113,5457,2418],{"class":2119},[2113,5459,4491],{"class":2414},[2113,5461,3109],{"class":2119},[2113,5463,4496],{"class":2414},[2113,5465,4449],{"class":2119},[2113,5467,5468,5471],{"class":2115,"line":2235},[2113,5469,5470],{"class":2326},"    loop",[2113,5472,2647],{"class":2119},[2113,5474,5475,5477,5480,5482,5484,5487,5490],{"class":2115,"line":2241},[2113,5476,4583],{"class":2326},[2113,5478,5479],{"class":2330}," decoder",[2113,5481,2153],{"class":2334},[2113,5483,4464],{"class":2414},[2113,5485,5486],{"class":2119},".decoder.",[2113,5488,5489],{"class":2133},"as_mut",[2113,5491,3944],{"class":2119},[2113,5493,5494,5496,5498,5500,5502],{"class":2115,"line":2246},[2113,5495,4518],{"class":2326},[2113,5497,5479],{"class":2330},[2113,5499,179],{"class":2119},[2113,5501,4971],{"class":2133},[2113,5503,3912],{"class":2119},[2113,5505,5506,5508,5511,5513,5515],{"class":2115,"line":2464},[2113,5507,4559],{"class":2326},[2113,5509,5510],{"class":2414}," Ok",[2113,5512,2423],{"class":2119},[2113,5514,3095],{"class":2166},[2113,5516,2553],{"class":2119},[2113,5518,5519],{"class":2115,"line":2085},[2113,5520,4578],{"class":2119},[2113,5522,5523],{"class":2115,"line":2514},[2113,5524,2125],{"emptyLinePlaceholder":1767},[2113,5526,5527,5529,5532,5534,5536,5538,5540,5542,5544,5546,5548],{"class":2115,"line":2533},[2113,5528,4583],{"class":2326},[2113,5530,5531],{"class":2330}," decoder_read_result",[2113,5533,2153],{"class":2334},[2113,5535,5479],{"class":2330},[2113,5537,179],{"class":2119},[2113,5539,3941],{"class":2133},[2113,5541,5224],{"class":2119},[2113,5543,4567],{"class":2133},[2113,5545,2423],{"class":2119},[2113,5547,4469],{"class":2330},[2113,5549,2553],{"class":2119},[2113,5551,5552],{"class":2115,"line":2556},[2113,5553,2125],{"emptyLinePlaceholder":1767},[2113,5555,5556,5558,5561,5563,5566,5568],{"class":2115,"line":2569},[2113,5557,4583],{"class":2326},[2113,5559,5560],{"class":2330}," count",[2113,5562,2153],{"class":2334},[2113,5564,5565],{"class":2326}," match",[2113,5567,5531],{"class":2330},[2113,5569,2647],{"class":2119},[2113,5571,5572,5575,5577,5580],{"class":2115,"line":2575},[2113,5573,5574],{"class":2414},"            Ok",[2113,5576,2423],{"class":2119},[2113,5578,5579],{"class":2330},"count",[2113,5581,5582],{"class":2119},") => {\n",[2113,5584,5585],{"class":2115,"line":2596},[2113,5586,5587],{"class":2330},"                count\n",[2113,5589,5590],{"class":2115,"line":3192},[2113,5591,5172],{"class":2119},[2113,5593,5594,5597,5599,5602],{"class":2115,"line":3213},[2113,5595,5596],{"class":2414},"            Err",[2113,5598,2423],{"class":2119},[2113,5600,5601],{"class":2330},"e",[2113,5603,5582],{"class":2119},[2113,5605,5606,5609,5612,5614,5616],{"class":2115,"line":3236},[2113,5607,5608],{"class":2326},"                return",[2113,5610,5611],{"class":2414}," Err",[2113,5613,2423],{"class":2119},[2113,5615,5601],{"class":2330},[2113,5617,2553],{"class":2119},[2113,5619,5620],{"class":2115,"line":3248},[2113,5621,5172],{"class":2119},[2113,5623,5624],{"class":2115,"line":4899},[2113,5625,4632],{"class":2119},[2113,5627,5628],{"class":2115,"line":1777},[2113,5629,2125],{"emptyLinePlaceholder":1767},[2113,5631,5632,5634,5636,5639,5641],{"class":2115,"line":4931},[2113,5633,4518],{"class":2326},[2113,5635,5560],{"class":2330},[2113,5637,5638],{"class":2334}," !=",[2113,5640,3208],{"class":2166},[2113,5642,2647],{"class":2119},[2113,5644,5645,5647,5649,5651,5653],{"class":2115,"line":4961},[2113,5646,4559],{"class":2326},[2113,5648,5510],{"class":2414},[2113,5650,2423],{"class":2119},[2113,5652,5579],{"class":2330},[2113,5654,2553],{"class":2119},[2113,5656,5657],{"class":2115,"line":4976},[2113,5658,4578],{"class":2119},[2113,5660,5661],{"class":2115,"line":4993},[2113,5662,2125],{"emptyLinePlaceholder":1767},[2113,5664,5665,5667,5669,5672,5674],{"class":2115,"line":5013},[2113,5666,4518],{"class":2326},[2113,5668,5560],{"class":2330},[2113,5670,5671],{"class":2334}," ==",[2113,5673,3208],{"class":2166},[2113,5675,2647],{"class":2119},[2113,5677,5678,5680,5682,5684,5686,5688,5691],{"class":2115,"line":5018},[2113,5679,4595],{"class":2326},[2113,5681,5479],{"class":2330},[2113,5683,2153],{"class":2334},[2113,5685,4464],{"class":2414},[2113,5687,5486],{"class":2119},[2113,5689,5690],{"class":2133},"take",[2113,5692,3944],{"class":2119},[2113,5694,5695,5697,5700,5702,5704,5707,5710,5712,5714],{"class":2115,"line":5042},[2113,5696,5021],{"class":2326},[2113,5698,5699],{"class":2326}," let",[2113,5701,5190],{"class":2414},[2113,5703,2423],{"class":2119},[2113,5705,5706],{"class":2330},"decoder",[2113,5708,5709],{"class":2119},") ",[2113,5711,2335],{"class":2334},[2113,5713,5479],{"class":2330},[2113,5715,2647],{"class":2119},[2113,5717,5718,5721,5723,5726,5728,5730,5732,5735],{"class":2115,"line":5057},[2113,5719,5720],{"class":2326},"                let",[2113,5722,3975],{"class":2326},[2113,5724,5725],{"class":2330}," reader",[2113,5727,2153],{"class":2334},[2113,5729,5479],{"class":2330},[2113,5731,179],{"class":2119},[2113,5733,5734],{"class":2133},"into_inner",[2113,5736,3944],{"class":2119},[2113,5738,5739],{"class":2115,"line":5062},[2113,5740,5741],{"class":2396},"                \u002F\u002F S'il reste encore des octets à lire dans reader alors on continue, sinon on s'arrête\n",[2113,5743,5744,5746,5748,5750,5752,5755,5757],{"class":2115,"line":5098},[2113,5745,5065],{"class":2326},[2113,5747,5725],{"class":2330},[2113,5749,179],{"class":2119},[2113,5751,4401],{"class":2133},[2113,5753,5754],{"class":2119},"()?.",[2113,5756,5037],{"class":2133},[2113,5758,3912],{"class":2119},[2113,5760,5761,5764,5766,5768,5770],{"class":2115,"line":5117},[2113,5762,5763],{"class":2326},"                    return",[2113,5765,5510],{"class":2414},[2113,5767,2423],{"class":2119},[2113,5769,3095],{"class":2166},[2113,5771,2553],{"class":2119},[2113,5773,5774],{"class":2115,"line":5142},[2113,5775,5166],{"class":2119},[2113,5777,5778,5781,5783,5786],{"class":2115,"line":5148},[2113,5779,5780],{"class":2330},"                reader",[2113,5782,179],{"class":2119},[2113,5784,5785],{"class":2133},"reset",[2113,5787,3944],{"class":2119},[2113,5789,5790],{"class":2115,"line":5163},[2113,5791,2125],{"emptyLinePlaceholder":1767},[2113,5793,5794,5796,5799,5801,5803,5805,5807,5809,5811,5813,5816],{"class":2115,"line":5169},[2113,5795,5045],{"class":2414},[2113,5797,5798],{"class":2119},".decoder ",[2113,5800,2335],{"class":2334},[2113,5802,5190],{"class":2414},[2113,5804,2423],{"class":2119},[2113,5806,3876],{"class":2414},[2113,5808,2418],{"class":2119},[2113,5810,3961],{"class":2133},[2113,5812,2423],{"class":2119},[2113,5814,5815],{"class":2330},"reader",[2113,5817,5818],{"class":2119},"));\n",[2113,5820,5821],{"class":2115,"line":5175},[2113,5822,5172],{"class":2119},[2113,5824,5825],{"class":2115,"line":5180},[2113,5826,4578],{"class":2119},[2113,5828,5829],{"class":2115,"line":5199},[2113,5830,4665],{"class":2119},[2113,5832,5833],{"class":2115,"line":5204},[2113,5834,2599],{"class":2119},[12,5836,5837,5838,5840],{},"J'ai donc rédigé une méthode ",[184,5839,4567],{}," qui remplit le tampon tant que le nombre d'octets lus est inférieur à la taille du\ntampon. On ne retourne 0 que dans le cas où la lecture du fichier est terminée.",[2105,5842,5844],{"className":3855,"code":5843,"language":1775,"meta":1646,"style":1646},"fn read(&mut self, buf: &mut [u8]) -> io::Result\u003Cusize> {\n    let mut total_bytes_read = 0;\n\n    while total_bytes_read \u003C buf.len() {\n        let bytes_to_read = &mut buf[total_bytes_read..];\n        let bytes_read = self.read_some_bytes(bytes_to_read)?;\n        total_bytes_read += bytes_read;\n\n        if bytes_read == 0 {\n            break;\n        }\n    }\n\n    Ok(total_bytes_read)\n}\n",[184,5845,5846,5884,5899,5903,5921,5945,5968,5980,5984,5996,6003,6007,6011,6015,6026],{"__ignoreMap":1646},[2113,5847,5848,5850,5852,5854,5856,5858,5860,5862,5864,5866,5868,5870,5872,5874,5876,5878,5880,5882],{"class":2115,"line":2116},[2113,5849,3906],{"class":2326},[2113,5851,4457],{"class":2133},[2113,5853,4027],{"class":2119},[2113,5855,4030],{"class":2326},[2113,5857,4464],{"class":2414},[2113,5859,932],{"class":2119},[2113,5861,4469],{"class":2330},[2113,5863,4472],{"class":2119},[2113,5865,4030],{"class":2326},[2113,5867,4477],{"class":2119},[2113,5869,4480],{"class":2414},[2113,5871,4483],{"class":2119},[2113,5873,4486],{"class":2414},[2113,5875,2418],{"class":2119},[2113,5877,4491],{"class":2414},[2113,5879,3109],{"class":2119},[2113,5881,4496],{"class":2414},[2113,5883,4449],{"class":2119},[2113,5885,5886,5888,5890,5893,5895,5897],{"class":2115,"line":1647},[2113,5887,3917],{"class":2326},[2113,5889,3975],{"class":2326},[2113,5891,5892],{"class":2330}," total_bytes_read",[2113,5894,2153],{"class":2334},[2113,5896,3208],{"class":2166},[2113,5898,2487],{"class":2119},[2113,5900,5901],{"class":2115,"line":1652},[2113,5902,2125],{"emptyLinePlaceholder":1767},[2113,5904,5905,5908,5910,5913,5915,5917,5919],{"class":2115,"line":2185},[2113,5906,5907],{"class":2326},"    while",[2113,5909,5892],{"class":2330},[2113,5911,5912],{"class":2334}," \u003C",[2113,5914,4535],{"class":2330},[2113,5916,179],{"class":2119},[2113,5918,4540],{"class":2133},[2113,5920,3912],{"class":2119},[2113,5922,5923,5925,5928,5930,5933,5935,5937,5939,5942],{"class":2115,"line":2230},[2113,5924,4583],{"class":2326},[2113,5926,5927],{"class":2330}," bytes_to_read",[2113,5929,2153],{"class":2334},[2113,5931,5932],{"class":2119}," &",[2113,5934,4030],{"class":2326},[2113,5936,4535],{"class":2330},[2113,5938,4124],{"class":2119},[2113,5940,5941],{"class":2330},"total_bytes_read",[2113,5943,5944],{"class":2119},"..];\n",[2113,5946,5947,5949,5952,5954,5956,5958,5960,5962,5965],{"class":2115,"line":2235},[2113,5948,4583],{"class":2326},[2113,5950,5951],{"class":2330}," bytes_read",[2113,5953,2153],{"class":2334},[2113,5955,4464],{"class":2414},[2113,5957,179],{"class":2119},[2113,5959,5360],{"class":2133},[2113,5961,2423],{"class":2119},[2113,5963,5964],{"class":2330},"bytes_to_read",[2113,5966,5967],{"class":2119},")?;\n",[2113,5969,5970,5973,5976,5978],{"class":2115,"line":2241},[2113,5971,5972],{"class":2330},"        total_bytes_read",[2113,5974,5975],{"class":2334}," +=",[2113,5977,5951],{"class":2330},[2113,5979,2487],{"class":2119},[2113,5981,5982],{"class":2115,"line":2246},[2113,5983,2125],{"emptyLinePlaceholder":1767},[2113,5985,5986,5988,5990,5992,5994],{"class":2115,"line":2464},[2113,5987,4518],{"class":2326},[2113,5989,5951],{"class":2330},[2113,5991,5671],{"class":2334},[2113,5993,3208],{"class":2166},[2113,5995,2647],{"class":2119},[2113,5997,5998,6001],{"class":2115,"line":2085},[2113,5999,6000],{"class":2326},"            break",[2113,6002,2487],{"class":2119},[2113,6004,6005],{"class":2115,"line":2514},[2113,6006,4578],{"class":2119},[2113,6008,6009],{"class":2115,"line":2533},[2113,6010,4665],{"class":2119},[2113,6012,6013],{"class":2115,"line":2556},[2113,6014,2125],{"emptyLinePlaceholder":1767},[2113,6016,6017,6020,6022,6024],{"class":2115,"line":2569},[2113,6018,6019],{"class":2414},"    Ok",[2113,6021,2423],{"class":2119},[2113,6023,5941],{"class":2330},[2113,6025,4660],{"class":2119},[2113,6027,6028],{"class":2115,"line":2575},[2113,6029,2599],{"class":2119},[133,6031,6033],{"id":6032},"les-fichiers-dattributs","Les fichiers d'attributs",[12,6035,6036],{},"Les fichiers d'attributs sont d'abord des fichiers compressés en utilisant la méthode décrite précédemment. J'ai donc\nutilisé le Reader que j'ai écrit précédemment pour décompresser les fichiers au fur et à mesure de leur lecture.",[12,6038,6039],{},"Je me suis ensuite attaqué au décodage des fichiers d'attributs. En réécrivant le code, j'ai découvert que certains\nnombres étaient mal encodés, ce qui provoquait des erreurs de décodage que j'ai dû gérer avec plus de flexibilité.",[12,6041,6042,6043,6045,6046,6048,6049,6054],{},"Pour lire un ",[184,6044,3778],{},", j'ai ajouté un trait au trait ",[184,6047,4432],{},". Je me suis basé sur le ",[49,6050,6053],{"href":6051,"rel":6052},"https:\u002F\u002Fgithub.com\u002Fbackuppc\u002Fbackuppc-xs\u002Fblob\u002Fmaster\u002Fbpc_attrib.c#L548",[347],"code C de BackupPC",",\ntout en modifiant la philosophie pour répondre au fonctionnement d'un Reader Rust.",[2105,6056,6058],{"className":3855,"code":6057,"language":1775,"meta":1646,"style":1646},"pub trait VarintRead: Read {\n    fn read_varint(&mut self) -> io::Result\u003Cu64> {\n        let mut result = 0;\n        let mut shift = 0;\n\n        loop {\n            let mut buf: [u8; 1] = [0u8; 1];\n            self.read_exact(&mut buf)?;\n\n            let byte = buf[0];\n            let val = (byte & 0x7F) as u64;\n            if shift >= 64 || val \u003C\u003C shift >> shift != val {\n                eprintln!(\"Varint too large: probably corrupted data\");\n                return Err(io::Error::new(\n                    io::ErrorKind::InvalidData,\n                    \"Varint too large: probably corrupted data\",\n                ));\n            }\n\n            result |= val \u003C\u003C shift;\n            if byte & 0x80 == 0 {\n                return Ok(result);\n            }\n            shift += 7;\n        }\n    }\n}\n",[184,6059,6060,6076,6104,6119,6134,6138,6145,6180,6197,6201,6218,6248,6279,6291,6312,6329,6336,6341,6345,6349,6365,6382,6395,6399,6411,6415,6419],{"__ignoreMap":1646},[2113,6061,6062,6064,6067,6070,6072,6074],{"class":2115,"line":2116},[2113,6063,5374],{"class":2326},[2113,6065,6066],{"class":2326}," trait",[2113,6068,6069],{"class":2414}," VarintRead",[2113,6071,4429],{"class":2119},[2113,6073,4432],{"class":2414},[2113,6075,2647],{"class":2119},[2113,6077,6078,6080,6083,6085,6087,6089,6091,6093,6095,6097,6099,6102],{"class":2115,"line":1647},[2113,6079,4454],{"class":2326},[2113,6081,6082],{"class":2133}," read_varint",[2113,6084,4027],{"class":2119},[2113,6086,4030],{"class":2326},[2113,6088,4464],{"class":2414},[2113,6090,4790],{"class":2119},[2113,6092,4486],{"class":2414},[2113,6094,2418],{"class":2119},[2113,6096,4491],{"class":2414},[2113,6098,3109],{"class":2119},[2113,6100,6101],{"class":2414},"u64",[2113,6103,4449],{"class":2119},[2113,6105,6106,6108,6110,6113,6115,6117],{"class":2115,"line":1652},[2113,6107,4583],{"class":2326},[2113,6109,3975],{"class":2326},[2113,6111,6112],{"class":2330}," result",[2113,6114,2153],{"class":2334},[2113,6116,3208],{"class":2166},[2113,6118,2487],{"class":2119},[2113,6120,6121,6123,6125,6128,6130,6132],{"class":2115,"line":2185},[2113,6122,4583],{"class":2326},[2113,6124,3975],{"class":2326},[2113,6126,6127],{"class":2330}," shift",[2113,6129,2153],{"class":2334},[2113,6131,3208],{"class":2166},[2113,6133,2487],{"class":2119},[2113,6135,6136],{"class":2115,"line":2230},[2113,6137,2125],{"emptyLinePlaceholder":1767},[2113,6139,6140,6143],{"class":2115,"line":2235},[2113,6141,6142],{"class":2326},"        loop",[2113,6144,2647],{"class":2119},[2113,6146,6147,6149,6151,6153,6156,6158,6161,6163,6165,6167,6169,6171,6173,6175,6177],{"class":2115,"line":2241},[2113,6148,4595],{"class":2326},[2113,6150,3975],{"class":2326},[2113,6152,4535],{"class":2330},[2113,6154,6155],{"class":2119},": [",[2113,6157,4480],{"class":2414},[2113,6159,6160],{"class":2119},"; ",[2113,6162,3686],{"class":2166},[2113,6164,4129],{"class":2119},[2113,6166,2335],{"class":2334},[2113,6168,4477],{"class":2119},[2113,6170,3095],{"class":2166},[2113,6172,4480],{"class":2414},[2113,6174,6160],{"class":2119},[2113,6176,3686],{"class":2166},[2113,6178,6179],{"class":2119},"];\n",[2113,6181,6182,6184,6186,6189,6191,6193,6195],{"class":2115,"line":2246},[2113,6183,5183],{"class":2414},[2113,6185,179],{"class":2119},[2113,6187,6188],{"class":2133},"read_exact",[2113,6190,4027],{"class":2119},[2113,6192,4030],{"class":2326},[2113,6194,4535],{"class":2330},[2113,6196,5967],{"class":2119},[2113,6198,6199],{"class":2115,"line":2464},[2113,6200,2125],{"emptyLinePlaceholder":1767},[2113,6202,6203,6205,6208,6210,6212,6214,6216],{"class":2115,"line":2085},[2113,6204,4595],{"class":2326},[2113,6206,6207],{"class":2330}," byte",[2113,6209,2153],{"class":2334},[2113,6211,4535],{"class":2330},[2113,6213,4124],{"class":2119},[2113,6215,3095],{"class":2166},[2113,6217,6179],{"class":2119},[2113,6219,6220,6222,6225,6227,6229,6232,6235,6238,6240,6243,6246],{"class":2115,"line":2514},[2113,6221,4595],{"class":2326},[2113,6223,6224],{"class":2330}," val",[2113,6226,2153],{"class":2334},[2113,6228,2495],{"class":2119},[2113,6230,6231],{"class":2330},"byte",[2113,6233,6234],{"class":2119}," & ",[2113,6236,6237],{"class":2166},"0x7F",[2113,6239,5709],{"class":2119},[2113,6241,6242],{"class":2326},"as",[2113,6244,6245],{"class":2414}," u64",[2113,6247,2487],{"class":2119},[2113,6249,6250,6252,6254,6257,6259,6261,6263,6266,6268,6271,6273,6275,6277],{"class":2115,"line":2533},[2113,6251,5021],{"class":2326},[2113,6253,6127],{"class":2330},[2113,6255,6256],{"class":2334}," >=",[2113,6258,3043],{"class":2166},[2113,6260,4140],{"class":2334},[2113,6262,6224],{"class":2330},[2113,6264,6265],{"class":2334}," \u003C\u003C",[2113,6267,6127],{"class":2330},[2113,6269,6270],{"class":2334}," >>",[2113,6272,6127],{"class":2330},[2113,6274,5638],{"class":2334},[2113,6276,6224],{"class":2330},[2113,6278,2647],{"class":2119},[2113,6280,6281,6284,6286,6289],{"class":2115,"line":2556},[2113,6282,6283],{"class":2133},"                eprintln!",[2113,6285,2423],{"class":2119},[2113,6287,6288],{"class":2149},"\"Varint too large: probably corrupted data\"",[2113,6290,2553],{"class":2119},[2113,6292,6293,6295,6297,6299,6301,6303,6306,6308,6310],{"class":2115,"line":2569},[2113,6294,5608],{"class":2326},[2113,6296,5611],{"class":2414},[2113,6298,2423],{"class":2119},[2113,6300,4486],{"class":2414},[2113,6302,2418],{"class":2119},[2113,6304,6305],{"class":2414},"Error",[2113,6307,2418],{"class":2119},[2113,6309,3961],{"class":2133},[2113,6311,3146],{"class":2119},[2113,6313,6314,6317,6319,6322,6324,6327],{"class":2115,"line":2575},[2113,6315,6316],{"class":2414},"                    io",[2113,6318,2418],{"class":2119},[2113,6320,6321],{"class":2414},"ErrorKind",[2113,6323,2418],{"class":2119},[2113,6325,6326],{"class":2414},"InvalidData",[2113,6328,4706],{"class":2119},[2113,6330,6331,6334],{"class":2115,"line":2596},[2113,6332,6333],{"class":2149},"                    \"Varint too large: probably corrupted data\"",[2113,6335,4706],{"class":2119},[2113,6337,6338],{"class":2115,"line":3192},[2113,6339,6340],{"class":2119},"                ));\n",[2113,6342,6343],{"class":2115,"line":3213},[2113,6344,5172],{"class":2119},[2113,6346,6347],{"class":2115,"line":3236},[2113,6348,2125],{"emptyLinePlaceholder":1767},[2113,6350,6351,6354,6357,6359,6361,6363],{"class":2115,"line":3248},[2113,6352,6353],{"class":2330},"            result",[2113,6355,6356],{"class":2334}," |=",[2113,6358,6224],{"class":2330},[2113,6360,6265],{"class":2334},[2113,6362,6127],{"class":2330},[2113,6364,2487],{"class":2119},[2113,6366,6367,6369,6371,6373,6376,6378,6380],{"class":2115,"line":4899},[2113,6368,5021],{"class":2326},[2113,6370,6207],{"class":2330},[2113,6372,6234],{"class":2119},[2113,6374,6375],{"class":2166},"0x80",[2113,6377,5671],{"class":2334},[2113,6379,3208],{"class":2166},[2113,6381,2647],{"class":2119},[2113,6383,6384,6386,6388,6390,6393],{"class":2115,"line":1777},[2113,6385,5608],{"class":2326},[2113,6387,5510],{"class":2414},[2113,6389,2423],{"class":2119},[2113,6391,6392],{"class":2330},"result",[2113,6394,2553],{"class":2119},[2113,6396,6397],{"class":2115,"line":4931},[2113,6398,5172],{"class":2119},[2113,6400,6401,6404,6406,6409],{"class":2115,"line":4961},[2113,6402,6403],{"class":2330},"            shift",[2113,6405,5975],{"class":2334},[2113,6407,6408],{"class":2166}," 7",[2113,6410,2487],{"class":2119},[2113,6412,6413],{"class":2115,"line":4976},[2113,6414,4578],{"class":2119},[2113,6416,6417],{"class":2115,"line":4993},[2113,6418,4665],{"class":2119},[2113,6420,6421],{"class":2115,"line":5013},[2113,6422,2599],{"class":2119},[12,6424,6425,6426,6429],{},"Au cours de mes tests pour lire les fichiers d'attributs du pool, j'ai rencontré de nombreuses erreurs de décodage (avec\nle message ci-dessus : ",[362,6427,6428],{},"Varint too large: probably corrupted data","). Ce qui est rassurant, c'est que cela n'est pas lié\nà mon algorithme. BackupPC rencontre le même problème lors du décodage de ses propres fichiers (ce qui se traduit dans\nson cas par une date en janvier 1970) :",[12,6431,6432],{},[121,6433],{"src":6434,"alt":6435,"className":6436},"\u002FWoodstock\u002F2024-03-14_pr_backuppc_pool_1.png","Fichier corrompu",[126],[12,6438,6439],{},"La question est donc : mes données sont-elles corrompues ou s'agit-il d'un bug de BackupPC ? Je ne vais pas obtenir de\nréponse à cette question.",[133,6441,6443],{"id":6442},"la-lecture-des-machines-et-des-sauvegardes","La lecture des machines et des sauvegardes",[12,6445,6446],{},"La partie la plus ardue est terminée.",[506,6448,6449,6454,6460],{},[370,6450,6451,6452,179],{},"Pour lister les machines, il suffit de lister les dossiers du répertoire ",[184,6453,3758],{},[370,6455,6456,6457,179],{},"Pour lister les sauvegardes, il faut lire le fichier texte (tsv) ",[184,6458,6459],{},"__TOPDIR__\u002Fpc\u002F\u003Cmachine>\u002Fbackups",[370,6461,6462,6463,179],{},"Pour lister les dossiers partagés, il faut lister les dossiers du répertoire ",[184,6464,6465],{},"__TOPDIR__\u002Fpc\u002F\u003Cmachine>\u002F\u003Cbackup>",[12,6467,6468,6469,179],{},"J'ai écrit ces trois méthodes dans ce fichier, que je vous invite à consulter. Il n'est pas très complexe : ",[49,6470,6473],{"href":6471,"rel":6472},"https:\u002F\u002Fgogs.shadoware.org\u002Fphoenix\u002Fbackuppc_pool\u002Fsrc\u002Fbranch\u002Fmaster\u002Fsrc\u002Fhosts.rs",[347],"hosts.rs",[12,6475,6476],{},"Je peux donc affirmer que j'ai réussi à lire les fichiers du pool de BackupPC. Pour mieux visualiser le résultat, j'ai\ndécidé de me lancer un défi supplémentaire : écrire un pilote FUSE pour visualiser le pool de stockage.",[133,6478,6480],{"id":6479},"le-driver-fuse","Le driver FUSE",[12,6482,6483],{},"L'étape suivante consiste donc à développer un pilote FUSE qui intègre tout cela. Il s'agit de lire les fichiers\nd'attributs pour reconstituer la structure du système de fichiers (qui sont dans le pool et compressés), et de lire les\nfichiers compressés pour les données.",[12,6485,6486,6487,179],{},"Pour gérer la partie du système de fichiers, j'ai utilisé la bibliothèque ",[49,6488,6491],{"href":6489,"rel":6490},"https:\u002F\u002Fdocs.rs\u002Ffuser\u002Flatest\u002Ffuser\u002Findex.html",[347],"fuser",[12,6493,6494],{},"Le cœur du système ayant été développé, la partie système de fichiers consiste principalement à décoder le chemin fourni\npar FUSE, à décompressé et lire le fichier d'attributs. Dans le cas où l'utilisateur souhaite lire le contenu d'un\nfichier, il faut décompresser le fichier compressé.",[12,6496,6497],{},"J'ai donc la partie système de fichiers qui gère les requêtes FUSE et les transmet à la partie vue (avec un système de\ncache), et la partie vue qui prend un chemin et le transforme dans le format suivant en appelant les différentes\nméthodes précédentes :",[506,6499,6500,6503,6506,6509],{},[370,6501,6502],{},"nom de l'hôte",[370,6504,6505],{},"numéro de sauvegarde",[370,6507,6508],{},"chemin du partage",[370,6510,6511],{},"chemin",[12,6513,6514,6515,6520],{},"Le décodage est réalisé dans le fichier ",[49,6516,6519],{"href":6517,"rel":6518},"https:\u002F\u002Fgogs.shadoware.org\u002Fphoenix\u002Fbackuppc_pool\u002Fsrc\u002Fbranch\u002Fmaster\u002Fsrc\u002Fview.rs",[347],"view.rs",".\nPour cette partie, j'ai ajouté des tests unitaires. Ces tests unitaires m'ont permis d'itérer plus rapidement pour\nconstruire le système de fichiers (sans avoir à monter ce dernier à chaque fois).",[12,6522,6523,6524,6526,6527,6530],{},"On appelle la méthode avec le chemin découpé à chaque ",[184,6525,2520],{}," pour obtenir les différents éléments du chemin (sous forme de\n",[184,6528,6529],{},"&[&str]",").:",[2105,6532,6534],{"className":3855,"code":6533,"language":1775,"meta":1646,"style":1646},"pub fn list(&self, path: &[&str]) -> Result\u003CVec\u003CFileAttributes>> {\n    match path.len() {\n",[184,6535,6536,6577],{"__ignoreMap":1646},[2113,6537,6538,6540,6543,6546,6548,6550,6552,6555,6558,6561,6563,6565,6567,6569,6571,6574],{"class":2115,"line":2116},[2113,6539,5374],{"class":2326},[2113,6541,6542],{"class":2326}," fn",[2113,6544,6545],{"class":2133}," list",[2113,6547,4027],{"class":2119},[2113,6549,5216],{"class":2414},[2113,6551,932],{"class":2119},[2113,6553,6554],{"class":2330},"path",[2113,6556,6557],{"class":2119},": &[&",[2113,6559,6560],{"class":2414},"str",[2113,6562,4483],{"class":2119},[2113,6564,4491],{"class":2414},[2113,6566,3109],{"class":2119},[2113,6568,4733],{"class":2414},[2113,6570,3109],{"class":2119},[2113,6572,6573],{"class":2414},"FileAttributes",[2113,6575,6576],{"class":2119},">> {\n",[2113,6578,6579,6582,6585,6587,6589],{"class":2115,"line":1647},[2113,6580,6581],{"class":2326},"    match",[2113,6583,6584],{"class":2330}," path",[2113,6586,179],{"class":2119},[2113,6588,4540],{"class":2133},[2113,6590,3912],{"class":2119},[12,6592,6593],{},"On commence d'abord par examiner le nombre d'éléments dans le chemin. Si nous n'avons aucun élément, cela signifie que\nnous sommes à la racine et que nous souhaitons obtenir la liste des machines.",[2105,6595,6597],{"className":3855,"code":6596,"language":1775,"meta":1646,"style":1646},"        0 => {\n            let hosts = self.hosts.list_hosts()?;\n            Ok(hosts.into_iter().map(FileAttributes::from_host).collect())\n        }\n",[184,6598,6599,6607,6626,6661],{"__ignoreMap":1646},[2113,6600,6601,6604],{"class":2115,"line":2116},[2113,6602,6603],{"class":2166},"        0",[2113,6605,6606],{"class":2119}," => {\n",[2113,6608,6609,6611,6614,6616,6618,6621,6624],{"class":2115,"line":1647},[2113,6610,4595],{"class":2326},[2113,6612,6613],{"class":2330}," hosts",[2113,6615,2153],{"class":2334},[2113,6617,4464],{"class":2414},[2113,6619,6620],{"class":2119},".hosts.",[2113,6622,6623],{"class":2133},"list_hosts",[2113,6625,4611],{"class":2119},[2113,6627,6628,6630,6632,6635,6637,6640,6642,6645,6647,6649,6651,6654,6656,6659],{"class":2115,"line":1652},[2113,6629,5574],{"class":2414},[2113,6631,2423],{"class":2119},[2113,6633,6634],{"class":2330},"hosts",[2113,6636,179],{"class":2119},[2113,6638,6639],{"class":2133},"into_iter",[2113,6641,5224],{"class":2119},[2113,6643,6644],{"class":2133},"map",[2113,6646,2423],{"class":2119},[2113,6648,6573],{"class":2414},[2113,6650,2418],{"class":2119},[2113,6652,6653],{"class":2330},"from_host",[2113,6655,3938],{"class":2119},[2113,6657,6658],{"class":2133},"collect",[2113,6660,5229],{"class":2119},[2113,6662,6663],{"class":2115,"line":2185},[2113,6664,4578],{"class":2119},[12,6666,6667],{},"Si nous avons un seul élément, alors c'est le nom de la machine. Nous souhaitons donc récupérer la liste des sauvegardes\nassociées à cette machine.",[2105,6669,6671],{"className":3855,"code":6670,"language":1775,"meta":1646,"style":1646},"        1 => {\n            let backups = self.hosts.list_backups(path[0]);\n            match backups {\n                Ok(backups) => Ok(backups\n                    .into_iter()\n                    .map(|a| FileAttributes::from_backup(&a))\n                    .collect()),\n                Err(err) => {\n                    \u002F\u002F If the file isn't found, it's because we should return empty vec\n                    if let Some(io_err) = err.downcast_ref::\u003Cstd::io::Error>() {\n                        if io_err.kind() == std::io::ErrorKind::NotFound {\n                            Ok(Vec::new())\n                        } else {\n                            Err(err)\n                        }\n                    } else {\n                        Err(err)\n                    }\n                }\n            }\n        }\n",[184,6672,6673,6680,6707,6716,6736,6746,6776,6785,6797,6802,6845,6879,6894,6903,6914,6919,6928,6939,6944,6948,6952],{"__ignoreMap":1646},[2113,6674,6675,6678],{"class":2115,"line":2116},[2113,6676,6677],{"class":2166},"        1",[2113,6679,6606],{"class":2119},[2113,6681,6682,6684,6687,6689,6691,6693,6696,6698,6700,6702,6704],{"class":2115,"line":1647},[2113,6683,4595],{"class":2326},[2113,6685,6686],{"class":2330}," backups",[2113,6688,2153],{"class":2334},[2113,6690,4464],{"class":2414},[2113,6692,6620],{"class":2119},[2113,6694,6695],{"class":2133},"list_backups",[2113,6697,2423],{"class":2119},[2113,6699,6554],{"class":2330},[2113,6701,4124],{"class":2119},[2113,6703,3095],{"class":2166},[2113,6705,6706],{"class":2119},"]);\n",[2113,6708,6709,6712,6714],{"class":2115,"line":1652},[2113,6710,6711],{"class":2326},"            match",[2113,6713,6686],{"class":2330},[2113,6715,2647],{"class":2119},[2113,6717,6718,6721,6723,6725,6728,6731,6733],{"class":2115,"line":2185},[2113,6719,6720],{"class":2414},"                Ok",[2113,6722,2423],{"class":2119},[2113,6724,3680],{"class":2330},[2113,6726,6727],{"class":2119},") => ",[2113,6729,6730],{"class":2414},"Ok",[2113,6732,2423],{"class":2119},[2113,6734,6735],{"class":2330},"backups\n",[2113,6737,6738,6741,6743],{"class":2115,"line":2230},[2113,6739,6740],{"class":2119},"                    .",[2113,6742,6639],{"class":2133},[2113,6744,6745],{"class":2119},"()\n",[2113,6747,6748,6750,6752,6754,6757,6759,6761,6764,6766,6769,6771,6773],{"class":2115,"line":2235},[2113,6749,6740],{"class":2119},[2113,6751,6644],{"class":2133},[2113,6753,2423],{"class":2119},[2113,6755,6756],{"class":2334},"|",[2113,6758,49],{"class":2330},[2113,6760,6756],{"class":2334},[2113,6762,6763],{"class":2414}," FileAttributes",[2113,6765,2418],{"class":2119},[2113,6767,6768],{"class":2133},"from_backup",[2113,6770,4027],{"class":2119},[2113,6772,49],{"class":2330},[2113,6774,6775],{"class":2119},"))\n",[2113,6777,6778,6780,6782],{"class":2115,"line":2241},[2113,6779,6740],{"class":2119},[2113,6781,6658],{"class":2133},[2113,6783,6784],{"class":2119},"()),\n",[2113,6786,6787,6790,6792,6795],{"class":2115,"line":2246},[2113,6788,6789],{"class":2414},"                Err",[2113,6791,2423],{"class":2119},[2113,6793,6794],{"class":2330},"err",[2113,6796,5582],{"class":2119},[2113,6798,6799],{"class":2115,"line":2464},[2113,6800,6801],{"class":2396},"                    \u002F\u002F If the file isn't found, it's because we should return empty vec\n",[2113,6803,6804,6807,6809,6811,6813,6816,6818,6820,6823,6825,6828,6831,6834,6836,6838,6840,6842],{"class":2115,"line":2085},[2113,6805,6806],{"class":2326},"                    if",[2113,6808,5699],{"class":2326},[2113,6810,5190],{"class":2414},[2113,6812,2423],{"class":2119},[2113,6814,6815],{"class":2330},"io_err",[2113,6817,5709],{"class":2119},[2113,6819,2335],{"class":2334},[2113,6821,6822],{"class":2330}," err",[2113,6824,179],{"class":2119},[2113,6826,6827],{"class":2133},"downcast_ref",[2113,6829,6830],{"class":2119},"::\u003C",[2113,6832,6833],{"class":2414},"std",[2113,6835,2418],{"class":2119},[2113,6837,4486],{"class":2414},[2113,6839,2418],{"class":2119},[2113,6841,6305],{"class":2414},[2113,6843,6844],{"class":2119},">() {\n",[2113,6846,6847,6850,6853,6855,6858,6860,6862,6864,6866,6868,6870,6872,6874,6877],{"class":2115,"line":2514},[2113,6848,6849],{"class":2326},"                        if",[2113,6851,6852],{"class":2330}," io_err",[2113,6854,179],{"class":2119},[2113,6856,6857],{"class":2133},"kind",[2113,6859,4543],{"class":2119},[2113,6861,4132],{"class":2334},[2113,6863,3885],{"class":2414},[2113,6865,2418],{"class":2119},[2113,6867,4486],{"class":2414},[2113,6869,2418],{"class":2119},[2113,6871,6321],{"class":2414},[2113,6873,2418],{"class":2119},[2113,6875,6876],{"class":2414},"NotFound",[2113,6878,2647],{"class":2119},[2113,6880,6881,6884,6886,6888,6890,6892],{"class":2115,"line":2533},[2113,6882,6883],{"class":2414},"                            Ok",[2113,6885,2423],{"class":2119},[2113,6887,4733],{"class":2414},[2113,6889,2418],{"class":2119},[2113,6891,3961],{"class":2133},[2113,6893,5229],{"class":2119},[2113,6895,6896,6899,6901],{"class":2115,"line":2556},[2113,6897,6898],{"class":2119},"                        } ",[2113,6900,4227],{"class":2326},[2113,6902,2647],{"class":2119},[2113,6904,6905,6908,6910,6912],{"class":2115,"line":2569},[2113,6906,6907],{"class":2414},"                            Err",[2113,6909,2423],{"class":2119},[2113,6911,6794],{"class":2330},[2113,6913,4660],{"class":2119},[2113,6915,6916],{"class":2115,"line":2575},[2113,6917,6918],{"class":2119},"                        }\n",[2113,6920,6921,6924,6926],{"class":2115,"line":2596},[2113,6922,6923],{"class":2119},"                    } ",[2113,6925,4227],{"class":2326},[2113,6927,2647],{"class":2119},[2113,6929,6930,6933,6935,6937],{"class":2115,"line":3192},[2113,6931,6932],{"class":2414},"                        Err",[2113,6934,2423],{"class":2119},[2113,6936,6794],{"class":2330},[2113,6938,4660],{"class":2119},[2113,6940,6941],{"class":2115,"line":3213},[2113,6942,6943],{"class":2119},"                    }\n",[2113,6945,6946],{"class":2115,"line":3236},[2113,6947,5166],{"class":2119},[2113,6949,6950],{"class":2115,"line":3248},[2113,6951,5172],{"class":2119},[2113,6953,6954],{"class":2115,"line":4899},[2113,6955,4578],{"class":2119},[12,6957,6958,6959,6962,6963,6965],{},"Si nous avons plus de deux éléments, alors nous souhaitons retourner au moins le partage, voire les fichiers à\nsauvegarder. Le problème est que le nom du partage ",[184,6960,6961],{},"share"," peut lui-même contenir des ",[184,6964,2520],{},". Il peut donc être composé de\nplusieurs éléments.",[12,6967,6968,6969,6972],{},"Le but de la méthode ",[184,6970,6971],{},"list_shares_of"," est de retourner le partage si ce dernier est un partage valide, ainsi que la\npartie du chemin qui suit le partage.",[2105,6974,6976],{"className":3855,"code":6975,"language":1775,"meta":1646,"style":1646},"        _ => {\n            let (shares, selected_share, share_size) =\n                self.list_shares_of(path[0], path[1].parse::\u003Cu32>().unwrap_or(0), &path[2..])?;\n\n            let shares = shares.into_iter().map(FileAttributes::from_share).collect();\n",[184,6977,6978,6985,7009,7067,7071],{"__ignoreMap":1646},[2113,6979,6980,6983],{"class":2115,"line":2116},[2113,6981,6982],{"class":2330},"        _",[2113,6984,6606],{"class":2119},[2113,6986,6987,6989,6991,6994,6996,6999,7001,7004,7006],{"class":2115,"line":1647},[2113,6988,4595],{"class":2326},[2113,6990,2495],{"class":2119},[2113,6992,6993],{"class":2330},"shares",[2113,6995,932],{"class":2119},[2113,6997,6998],{"class":2330},"selected_share",[2113,7000,932],{"class":2119},[2113,7002,7003],{"class":2330},"share_size",[2113,7005,5709],{"class":2119},[2113,7007,7008],{"class":2334},"=\n",[2113,7010,7011,7013,7015,7017,7019,7021,7023,7025,7028,7030,7032,7034,7037,7040,7042,7045,7048,7051,7053,7055,7058,7060,7062,7064],{"class":2115,"line":1652},[2113,7012,5045],{"class":2414},[2113,7014,179],{"class":2119},[2113,7016,6971],{"class":2133},[2113,7018,2423],{"class":2119},[2113,7020,6554],{"class":2330},[2113,7022,4124],{"class":2119},[2113,7024,3095],{"class":2166},[2113,7026,7027],{"class":2119},"], ",[2113,7029,6554],{"class":2330},[2113,7031,4124],{"class":2119},[2113,7033,3686],{"class":2166},[2113,7035,7036],{"class":2119},"].",[2113,7038,7039],{"class":2133},"parse",[2113,7041,6830],{"class":2119},[2113,7043,7044],{"class":2414},"u32",[2113,7046,7047],{"class":2119},">().",[2113,7049,7050],{"class":2133},"unwrap_or",[2113,7052,2423],{"class":2119},[2113,7054,3095],{"class":2166},[2113,7056,7057],{"class":2119},"), &",[2113,7059,6554],{"class":2330},[2113,7061,4124],{"class":2119},[2113,7063,2547],{"class":2166},[2113,7065,7066],{"class":2119},"..])?;\n",[2113,7068,7069],{"class":2115,"line":2185},[2113,7070,2125],{"emptyLinePlaceholder":1767},[2113,7072,7073,7075,7078,7080,7082,7084,7086,7088,7090,7092,7094,7096,7099,7101,7103],{"class":2115,"line":2230},[2113,7074,4595],{"class":2326},[2113,7076,7077],{"class":2330}," shares",[2113,7079,2153],{"class":2334},[2113,7081,7077],{"class":2330},[2113,7083,179],{"class":2119},[2113,7085,6639],{"class":2133},[2113,7087,5224],{"class":2119},[2113,7089,6644],{"class":2133},[2113,7091,2423],{"class":2119},[2113,7093,6573],{"class":2414},[2113,7095,2418],{"class":2119},[2113,7097,7098],{"class":2330},"from_share",[2113,7100,3938],{"class":2119},[2113,7102,6658],{"class":2133},[2113,7104,3944],{"class":2119},[12,7106,7107,7108,7111],{},"Une fois que nous avons le nom de la machine, le numéro de la sauvegarde et le partage, nous pouvons appeler la méthode\n",[184,7109,7110],{},"list_file_from_dir"," pour récupérer les fichiers du sous-dossier du partage.",[2105,7113,7115],{"className":3855,"code":7114,"language":1775,"meta":1646,"style":1646},"            match selected_share {\n                None => Ok(shares),\n                Some(selected_share) => self.list_file_from_dir(\n                    path[0],\n                    path[1].parse::\u003Cu32>().unwrap_or(0),\n                    &selected_share,\n                    &path[(2 + share_size)..].join(\"\u002F\"),\n                ),\n            }\n        }\n    }\n}\n",[184,7116,7117,7126,7143,7162,7174,7200,7209,7238,7243,7247,7251,7255],{"__ignoreMap":1646},[2113,7118,7119,7121,7124],{"class":2115,"line":2116},[2113,7120,6711],{"class":2326},[2113,7122,7123],{"class":2330}," selected_share",[2113,7125,2647],{"class":2119},[2113,7127,7128,7131,7134,7136,7138,7140],{"class":2115,"line":1647},[2113,7129,7130],{"class":2414},"                None",[2113,7132,7133],{"class":2119}," => ",[2113,7135,6730],{"class":2414},[2113,7137,2423],{"class":2119},[2113,7139,6993],{"class":2330},[2113,7141,7142],{"class":2119},"),\n",[2113,7144,7145,7148,7150,7152,7154,7156,7158,7160],{"class":2115,"line":1652},[2113,7146,7147],{"class":2414},"                Some",[2113,7149,2423],{"class":2119},[2113,7151,6998],{"class":2330},[2113,7153,6727],{"class":2119},[2113,7155,5216],{"class":2414},[2113,7157,179],{"class":2119},[2113,7159,7110],{"class":2133},[2113,7161,3146],{"class":2119},[2113,7163,7164,7167,7169,7171],{"class":2115,"line":2185},[2113,7165,7166],{"class":2330},"                    path",[2113,7168,4124],{"class":2119},[2113,7170,3095],{"class":2166},[2113,7172,7173],{"class":2119},"],\n",[2113,7175,7176,7178,7180,7182,7184,7186,7188,7190,7192,7194,7196,7198],{"class":2115,"line":2230},[2113,7177,7166],{"class":2330},[2113,7179,4124],{"class":2119},[2113,7181,3686],{"class":2166},[2113,7183,7036],{"class":2119},[2113,7185,7039],{"class":2133},[2113,7187,6830],{"class":2119},[2113,7189,7044],{"class":2414},[2113,7191,7047],{"class":2119},[2113,7193,7050],{"class":2133},[2113,7195,2423],{"class":2119},[2113,7197,3095],{"class":2166},[2113,7199,7142],{"class":2119},[2113,7201,7202,7205,7207],{"class":2115,"line":2235},[2113,7203,7204],{"class":2119},"                    &",[2113,7206,6998],{"class":2330},[2113,7208,4706],{"class":2119},[2113,7210,7211,7213,7215,7218,7220,7223,7225,7228,7231,7233,7236],{"class":2115,"line":2241},[2113,7212,7204],{"class":2119},[2113,7214,6554],{"class":2330},[2113,7216,7217],{"class":2119},"[(",[2113,7219,2547],{"class":2166},[2113,7221,7222],{"class":2119}," + ",[2113,7224,7003],{"class":2330},[2113,7226,7227],{"class":2119},")..].",[2113,7229,7230],{"class":2133},"join",[2113,7232,2423],{"class":2119},[2113,7234,7235],{"class":2149},"\"\u002F\"",[2113,7237,7142],{"class":2119},[2113,7239,7240],{"class":2115,"line":2246},[2113,7241,7242],{"class":2119},"                ),\n",[2113,7244,7245],{"class":2115,"line":2464},[2113,7246,5172],{"class":2119},[2113,7248,7249],{"class":2115,"line":2085},[2113,7250,4578],{"class":2119},[2113,7252,7253],{"class":2115,"line":2514},[2113,7254,4665],{"class":2119},[2113,7256,7257],{"class":2115,"line":2533},[2113,7258,2599],{"class":2119},[12,7260,7261,7262,7267],{},"Enfin, pour la partie FUSE, je vous invite à consulter le fichier ",[49,7263,7266],{"href":7264,"rel":7265},"https:\u002F\u002Fgogs.shadoware.org\u002Fphoenix\u002Fbackuppc_pool\u002Fsrc\u002Fbranch\u002Fmaster\u002Fsrc\u002Ffilesystem.rs",[347],"filesystem.rs",".\nCe fichier fait simplement le lien entre la partie FUSE et la partie vue.",[12,7269,7270],{},"Sur la partie FUSE, j'ai ajouté un cache pour éviter de lire plusieurs fois le même fichier (d'attributs) dans le pool.",[12,7272,7273,7274,7277],{},"Une fois le système de fichiers construit, il était nécessaire de le tester avec des tests réels. Le premier test a\nconsisté à parcourir l'ensemble du système de fichiers pour vérifier si la partie attribut fonctionnait correctement.\nPour cela, j'ai utilisé le programme ",[184,7275,7276],{},"filelight",". Ce programme permet de parcourir le système de fichiers et de\nvisualiser la taille des fichiers.",[12,7279,7280],{},"Voici un exemple sur une partie de la sauvegarde :",[12,7282,7283],{},[121,7284],{"src":7285,"alt":7286,"className":7287},"\u002FWoodstock\u002Ffilelight_localhost.png","Filelight",[126],[12,7289,7290],{},"Enfin, pour tester la capacité du programme à lire tous les fichiers du système de fichiers, j'ai écrit un petit script\nshell.",[2105,7292,7294],{"className":2317,"code":7293,"language":2319,"meta":1646,"style":1646},"#!\u002Fbin\u002Fbash\n\n# Fichiers de sortie\noutput_file=\"md5sums.txt\"\nerror_file=\"errors.txt\"\n\n# Parcourir tous les fichiers du système de fichiers\nfind \u002Fhome\u002Fphoenix\u002Ftmp\u002Ftest -type f -print0 | while IFS= read -r -d '' file; do\n    # Essayer de calculer le hash MD5 du fichier\n    if md5sum \"$file\" >> \"$output_file\"; then\n        echo \"Processed $file\"\n    else\n        # Si le fichier ne peut pas être lu, écrire le nom du fichier dans le fichier d'erreur\n        echo \"Failed to process $file\" >> \"$error_file\"\n    fi\ndone\n",[184,7295,7296,7301,7305,7310,7320,7330,7334,7339,7385,7390,7422,7435,7440,7445,7465,7470],{"__ignoreMap":1646},[2113,7297,7298],{"class":2115,"line":2116},[2113,7299,7300],{"class":2396},"#!\u002Fbin\u002Fbash\n",[2113,7302,7303],{"class":2115,"line":1647},[2113,7304,2125],{"emptyLinePlaceholder":1767},[2113,7306,7307],{"class":2115,"line":1652},[2113,7308,7309],{"class":2396},"# Fichiers de sortie\n",[2113,7311,7312,7315,7317],{"class":2115,"line":2185},[2113,7313,7314],{"class":2330},"output_file",[2113,7316,2335],{"class":2334},[2113,7318,7319],{"class":2149},"\"md5sums.txt\"\n",[2113,7321,7322,7325,7327],{"class":2115,"line":2230},[2113,7323,7324],{"class":2330},"error_file",[2113,7326,2335],{"class":2334},[2113,7328,7329],{"class":2149},"\"errors.txt\"\n",[2113,7331,7332],{"class":2115,"line":2235},[2113,7333,2125],{"emptyLinePlaceholder":1767},[2113,7335,7336],{"class":2115,"line":2241},[2113,7337,7338],{"class":2396},"# Parcourir tous les fichiers du système de fichiers\n",[2113,7340,7341,7344,7347,7350,7352,7355,7358,7361,7364,7366,7368,7371,7374,7377,7380,7382],{"class":2115,"line":2246},[2113,7342,7343],{"class":2133},"find",[2113,7345,7346],{"class":2149}," \u002Fhome\u002Fphoenix\u002Ftmp\u002Ftest",[2113,7348,7349],{"class":2166}," -type",[2113,7351,3920],{"class":2149},[2113,7353,7354],{"class":2166}," -print0",[2113,7356,7357],{"class":2119}," | ",[2113,7359,7360],{"class":2326},"while",[2113,7362,7363],{"class":2330}," IFS",[2113,7365,2335],{"class":2334},[2113,7367,4457],{"class":2334},[2113,7369,7370],{"class":2166}," -r",[2113,7372,7373],{"class":2166}," -d",[2113,7375,7376],{"class":2149}," ''",[2113,7378,7379],{"class":2149}," file",[2113,7381,6160],{"class":2119},[2113,7383,7384],{"class":2326},"do\n",[2113,7386,7387],{"class":2115,"line":2464},[2113,7388,7389],{"class":2396},"    # Essayer de calculer le hash MD5 du fichier\n",[2113,7391,7392,7395,7398,7401,7404,7407,7410,7412,7415,7417,7419],{"class":2115,"line":2085},[2113,7393,7394],{"class":2326},"    if",[2113,7396,7397],{"class":2133}," md5sum",[2113,7399,7400],{"class":2149}," \"",[2113,7402,7403],{"class":2330},"$file",[2113,7405,7406],{"class":2149},"\"",[2113,7408,7409],{"class":2119}," >> ",[2113,7411,7406],{"class":2149},[2113,7413,7414],{"class":2330},"$output_file",[2113,7416,7406],{"class":2149},[2113,7418,6160],{"class":2119},[2113,7420,7421],{"class":2326},"then\n",[2113,7423,7424,7427,7430,7432],{"class":2115,"line":2514},[2113,7425,7426],{"class":2334},"        echo",[2113,7428,7429],{"class":2149}," \"Processed ",[2113,7431,7403],{"class":2330},[2113,7433,7434],{"class":2149},"\"\n",[2113,7436,7437],{"class":2115,"line":2533},[2113,7438,7439],{"class":2326},"    else\n",[2113,7441,7442],{"class":2115,"line":2556},[2113,7443,7444],{"class":2396},"        # Si le fichier ne peut pas être lu, écrire le nom du fichier dans le fichier d'erreur\n",[2113,7446,7447,7449,7452,7454,7456,7458,7460,7463],{"class":2115,"line":2569},[2113,7448,7426],{"class":2334},[2113,7450,7451],{"class":2149}," \"Failed to process ",[2113,7453,7403],{"class":2330},[2113,7455,7406],{"class":2149},[2113,7457,7409],{"class":2119},[2113,7459,7406],{"class":2149},[2113,7461,7462],{"class":2330},"$error_file",[2113,7464,7434],{"class":2149},[2113,7466,7467],{"class":2115,"line":2575},[2113,7468,7469],{"class":2326},"    fi\n",[2113,7471,7472],{"class":2115,"line":2596},[2113,7473,7474],{"class":2326},"done\n",[12,7476,7477],{},"J'ai récemment commencé à développer un autre programme en Rust qui utilise la notion de multithreading.",[133,7479,7481],{"id":7480},"les-avantages-de-rust","Les avantages de Rust",[12,7483,7484],{},"En travaillant sur ce petit programme Rust, j'ai apprécié plusieurs aspects du langage. Rust offre la possibilité\nd'écrire du code de bas niveau sans avoir à gérer la mémoire comme en C, ce qui m'a procuré un sentiment de sécurité. De\nplus, la documentation de Rust est bien conçue et m'a permis de progresser rapidement.",[12,7486,7487,7488,7493],{},"J'ai également trouvé la syntaxe de Rust très lisible, à condition de ne pas utiliser les durées de vie. La présence de\nnombreux packages (crates) sur ",[49,7489,7492],{"href":7490,"rel":7491},"https:\u002F\u002Fcrates.io\u002F",[347],"crates.io"," facilite le développement rapide de fonctionnalités.",[12,7495,7496],{},"Clippy, un outil de vérification du code Rust, m'a été très utile. Il donne des conseils pour améliorer le code, ce qui\nm'a aidé à progresser, même s'il me reste encore du chemin à parcourir.",[133,7498,7500],{"id":7499},"les-défis-rencontrés-avec-rust","Les défis rencontrés avec Rust",[12,7502,7503,7504,7507],{},"Malgré ces avantages, j'ai rencontré quelques difficultés, probablement dues à ma méconnaissance du langage Rust. Par\nexemple, pour certaines parties du code, je me suis retrouvé avec du code contenant le mot ",[362,7505,7506],{},"unsafe",". J'ai refusé de\nl'utiliser, ce qui a rendu mon code plus complexe.",[12,7509,7510,7511,7514,7515,2384],{},"J'ai un cas particulier pour lequel je n'ai pas trouvé de solution. Dans mon logiciel de sauvegarde, je lis des noms de\nfichier que je stocke en tant que ",[184,7512,7513],{},"[u8]"," en Protobuf dans un fichier. Si je traite ces noms de fichier en tant que\nchaîne de caractères, je me retrouve avec des erreurs potentielles de conversion si le nom de fichier n'est pas en UTF-8\nsous Linux et en UTF-16 sous Windows. Pour stocker donc sous forme d'octet, j'utilise ces méthodes qui m'obligent à\nutiliser ",[184,7516,7506],{},[2105,7518,7520],{"className":3855,"code":7519,"language":1775,"meta":1646,"style":1646},"#[must_use]\npub fn osstr_to_vec(path: &OsStr) -> Vec\u003Cu8> {\n    path.as_encoded_bytes().to_vec()\n}\n\n#[must_use]\npub fn vec_to_osstr(vec: &[u8]) -> OsString {\n    unsafe { OsString::from_encoded_bytes_unchecked(vec.to_owned()) }\n}\n",[184,7521,7522,7527,7555,7571,7575,7579,7583,7609,7636],{"__ignoreMap":1646},[2113,7523,7524],{"class":2115,"line":2116},[2113,7525,7526],{"class":2119},"#[must_use]\n",[2113,7528,7529,7531,7533,7536,7538,7540,7542,7545,7547,7549,7551,7553],{"class":2115,"line":1647},[2113,7530,5374],{"class":2326},[2113,7532,6542],{"class":2326},[2113,7534,7535],{"class":2133}," osstr_to_vec",[2113,7537,2423],{"class":2119},[2113,7539,6554],{"class":2330},[2113,7541,4472],{"class":2119},[2113,7543,7544],{"class":2414},"OsStr",[2113,7546,4790],{"class":2119},[2113,7548,4733],{"class":2414},[2113,7550,3109],{"class":2119},[2113,7552,4480],{"class":2414},[2113,7554,4449],{"class":2119},[2113,7556,7557,7560,7562,7565,7567,7569],{"class":2115,"line":1652},[2113,7558,7559],{"class":2330},"    path",[2113,7561,179],{"class":2119},[2113,7563,7564],{"class":2133},"as_encoded_bytes",[2113,7566,5224],{"class":2119},[2113,7568,5008],{"class":2133},[2113,7570,6745],{"class":2119},[2113,7572,7573],{"class":2115,"line":2185},[2113,7574,2599],{"class":2119},[2113,7576,7577],{"class":2115,"line":2230},[2113,7578,2125],{"emptyLinePlaceholder":1767},[2113,7580,7581],{"class":2115,"line":2235},[2113,7582,7526],{"class":2119},[2113,7584,7585,7587,7589,7592,7594,7597,7600,7602,7604,7607],{"class":2115,"line":2241},[2113,7586,5374],{"class":2326},[2113,7588,6542],{"class":2326},[2113,7590,7591],{"class":2133}," vec_to_osstr",[2113,7593,2423],{"class":2119},[2113,7595,7596],{"class":2330},"vec",[2113,7598,7599],{"class":2119},": &[",[2113,7601,4480],{"class":2414},[2113,7603,4483],{"class":2119},[2113,7605,7606],{"class":2414},"OsString",[2113,7608,2647],{"class":2119},[2113,7610,7611,7614,7617,7619,7621,7624,7626,7628,7630,7633],{"class":2115,"line":2246},[2113,7612,7613],{"class":2326},"    unsafe",[2113,7615,7616],{"class":2119}," { ",[2113,7618,7606],{"class":2414},[2113,7620,2418],{"class":2119},[2113,7622,7623],{"class":2133},"from_encoded_bytes_unchecked",[2113,7625,2423],{"class":2119},[2113,7627,7596],{"class":2330},[2113,7629,179],{"class":2119},[2113,7631,7632],{"class":2133},"to_owned",[2113,7634,7635],{"class":2119},"()) }\n",[2113,7637,7638],{"class":2115,"line":2464},[2113,7639,2599],{"class":2119},[12,7641,7642,7643,7645],{},"Le problème est que dans l'interface, des noms Windows ou Linux peuvent être affichés. Je ne souhaite pas que le\nprogramme plante, mais qu'au pire des cas, il affiche un nom de fichier incorrect. Je ne suis pas sûr que le code écrit\nsoit la meilleure solution. (D'après mes tests, il fonctionne). Une meilleure solution pour convertir un ",[184,7644,7544],{}," en\nString en fonction de l'encodage actuel (la variable d'environnement LANG sous Linux) aurait été pratique.",[12,7647,7648],{},"La gestion de la mémoire, bien que semblant simple au premier abord, devient rapidement complexe lorsqu'on veut faire du\nmultithreading. On se retrouve à mettre des structures dans des Box, des Mutex et des Arc, ce qui complique la syntaxe.",[12,7650,7651,7652,7655,7656,7659],{},"L'introduction de dynamisme, notamment pour la gestion des erreurs, nécessite l'utilisation du mot-clé ",[184,7653,7654],{},"dyn",", ce qui\npeut également devenir complexe. Je me retrouve avec presque toutes mes méthodes qui retournent un\n",[184,7657,7658],{},"Result\u003C???, Box\u003Cdyn Error>>",". Là aussi, je me demande si c'est une bonne pratique.",[12,7661,7662],{},"De plus, Rust semble souvent nous obliger à cloner des objets, alors que j'aurais parfois préféré utiliser simplement la\nréférence de l'objet. Mais comme potentiellement Rust n'arrive pas à déterminer si dans un cas de multithreading la\nvariable sera toujours valide, on doit la cloner. Parfois dans ces cas, je me dis qu'en C, je sais quand j'alloue, quand\nje désalloue, qui a le droit de lire et d'écrire. De mes souvenirs du C et du C++, j'avais plus de contrôle, le code me\nparaissait plus facile à écrire. Maintenant, le moindre oubli, petite erreur, peut être fatal, et Rust nous protège de\ncela. Bref, je pense simplement que je dois encore prendre le temps de me familiariser avec la philosophie de Rust.",[12,7664,7665,7666,932,7669,932,7672,939,7675,7678],{},"Enfin, les erreurs de compilation ont été un gros défi. J'ai passé des heures à essayer de comprendre certaines erreurs,\nà ajuster les durées de vie ou à ajouter ",[184,7667,7668],{},"static",[184,7670,7671],{},"Send",[184,7673,7674],{},"Sync",[184,7676,7677],{},"Clone"," sans savoir quelle était la bonne façon de\nfaire.",[133,7680,7682],{"id":7681},"les-tests-unitaires","Les tests unitaires",[12,7684,7685],{},"J'ai réalisé des tests unitaires pour une partie du développement. Cependant, l'écriture de ces tests a été difficile,\nsurtout en comparaison avec des bibliothèques comme Jest en JavaScript.",[12,7687,7688,7689,179],{},"La création de mocks pour les interfaces et les autres fichiers n'est pas native à Rust et n'est pas une tâche simple.\nJ'ai dû utiliser une bibliothèque dédiée, ",[49,7690,7693],{"href":7691,"rel":7692},"https:\u002F\u002Fdocs.rs\u002Fmockall\u002Flatest\u002Fmockall\u002F",[347],"mockall",[12,7695,7696,7697,179],{},"L'utilisation de mocks m'a contraint à créer des structures, alors qu'initialement j'avais des méthodes. J'ai commencé\navec des méthodes statiques, mais leur gestion avec les mocks ne permet pas de réaliser des tests unitaires en\nparallèle. Pour exécuter les tests unitaires, j'ai dû lancer un test à la fois avec la commande\n",[184,7698,7699],{},"RUST_TEST_THREADS=1 cargo test",[12,7701,7702],{},"Cependant, j'ai réussi à modifier les tests unitaires pour qu'ils puissent s'exécuter en parallèle sans supprimer les\nméthodes statiques.",[128,7704,1621],{"id":1620},[12,7706,7707],{},"Maintenant que j'ai écrit ce petit programme, je vais pouvoir créer mon propre pool de stockage. De votre côté,\nn'hésitez pas à me faire des retours sur le programme. Je suis ouvert à toutes les critiques. De plus, si vous utilisez\nBackupPC (en version 4 uniquement et avec un pool de stockage en version 4), vous pouvez tester mon programme et\nl'utiliser.",[3358,7709,7710],{},"html pre.shiki code .seHd6, html code.shiki .seHd6{--shiki-default:#C678DD}html pre.shiki code .sU0A5, html code.shiki .sU0A5{--shiki-default:#E5C07B}html pre.shiki code .sn6KH, html code.shiki .sn6KH{--shiki-default:#ABB2BF}html pre.shiki code .sVbv2, html code.shiki .sVbv2{--shiki-default:#61AFEF}html pre.shiki code .sVyAn, html code.shiki .sVyAn{--shiki-default:#E06C75}html pre.shiki code .sjrmR, html code.shiki .sjrmR{--shiki-default:#56B6C2}html pre.shiki code .subq3, html code.shiki .subq3{--shiki-default:#98C379}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html pre.shiki code .sV9Aq, html code.shiki .sV9Aq{--shiki-default:#7F848E;--shiki-default-font-style:italic}html pre.shiki code .sVC51, html code.shiki .sVC51{--shiki-default:#D19A66}",{"title":1646,"searchDepth":1647,"depth":1647,"links":7712},[7713,7714,7719,7728],{"id":3540,"depth":1647,"text":3541},{"id":3602,"depth":1647,"text":3603,"children":7715},[7716,7717,7718],{"id":3645,"depth":1652,"text":3646},{"id":3717,"depth":1652,"text":3718},{"id":3735,"depth":1652,"text":3736},{"id":3808,"depth":1647,"text":3809,"children":7720},[7721,7722,7723,7724,7725,7726,7727],{"id":3832,"depth":1652,"text":3833},{"id":6032,"depth":1652,"text":6033},{"id":6442,"depth":1652,"text":6443},{"id":6479,"depth":1652,"text":6480},{"id":7480,"depth":1652,"text":7481},{"id":7499,"depth":1652,"text":7500},{"id":7681,"depth":1652,"text":7682},{"id":1620,"depth":1647,"text":1621},"Une partie de cet article a été publiée sur LinuxFR.\nAprès avoir reçu quelques retours, j'ai décidé de publier une version modifiée et améliorée de cet article sur mon blog.",{"type":9,"value":7731},[7732,7737,7739,7741],[12,7733,3519,7734,3525],{},[49,7735,3524],{"href":3522,"rel":7736},[347],[12,7738,3528],{},[12,7740,3531],{},[12,7742,7743],{},[121,7744],{"src":3536,"alt":1646,"className":7745},[126],{"planet":1767},{"title":108,"description":7729},"pr_backuppc_pool","posts\u002FWoodstock\u002F2024-03-14_pr_backuppc_pool",[1683,1773,1774,3510,3511],"JVBo68qbMHY70AJ8I83XDPaOAeqZhhqQNzs8jDhZhvY",{"id":7753,"title":7754,"author":7,"body":7755,"category":2057,"categorySlug":2058,"date":97,"description":1646,"excerpt":15236,"extension":1764,"location":1765,"meta":15244,"navigation":1767,"path":93,"published":1767,"seo":15245,"slug":15246,"stem":15247,"tags":15248,"timeToRead":3192,"__hash__":15249},"posts\u002Fposts\u002FWoodstock\u002F2023-05-10_woodstock_rust.md","Woodstock Backup - Optimiser la consommation mémoire de Node.js avec Rust",{"type":9,"value":7756,"toc":15226},[7757,7761,7764,7767,7771,7774,9253,9256,9259,9327,9330,9333,9337,9344,9347,9350,9353,9451,9454,9514,9520,9524,9527,9537,11448,11451,11511,11514,11517,11520,11524,11527,11530,11533,11536,11543,11546,11549,11552,13121,13124,13469,13472,13510,13513,13519,13522,13530,13533,13541,14776,14782,15153,15156,15159,15198,15201,15204,15207,15210,15212,15215,15223],[128,7758,7760],{"id":7759},"introduction","Introduction",[12,7762,7763],{},"Node.js est un environnement d'exécution JavaScript côté serveur qui repose sur le moteur JavaScript V8 de Google. Il\nest utilisé pour développer des applications serveur en back-end d'une application web, des outils en ligne de commande\net des applications desktop. Cependant, la consommation de mémoire peut être un problème pour certaines applications\nNode.js, en particulier celles qui manipulent de grandes quantités de données ou des données volumineuses.",[12,7765,7766],{},"Dans cet article, nous allons voir comment optimiser la consommation de mémoire d'une application Node.js en le couplant\navec Rust. Rust est un langage de programmation système qui offre des performances similaires à celles du C++, tout en\noffrant une sécurité de mémoire à la compilation. Rust peut être utilisé pour écrire des bibliothèques C\u002FC++ natives\npour Node.js.",[128,7768,7770],{"id":7769},"problématique","Problématique",[12,7772,7773],{},"J'ai développé un logiciel de sauvegarde appelé Woodstock Backup, écrit en TypeScript. Lors du lancement des\nsauvegardes, il crée une représentation du système de fichier en mémoire et nécessite une grande quantité de mémoire.\nPour illustrer cela, nous avons reproduit notre cas avec le code suivant :",[2105,7775,7779],{"className":7776,"code":7777,"language":7778,"meta":1646,"style":1646},"language-js shiki shiki-themes one-dark-pro","const filesize = require(\"filesize.js\");\nconst fs = require(\"fs\");\n\u002F\u002F Utilisation des méthodes de sérialisation et de désérialisation du moteur V8\nconst { serialize, deserialize } = require(\"v8\");\n\n\u002F\u002F Méthode pour générer une chaîne de caractère contenant des caractères aléatoires\nfunction randomString(size) {\n  const buffer = Buffer.alloc(size);\n  for (let i = 0; i \u003C size; i++) {\n    buffer[i] = Math.floor(Math.random() * 256);\n  }\n  return buffer;\n}\n\n\u002F\u002F Méthode pour générer un nombre aléatoire de la taille d'un nombre de 53 bits\nfunction randomNumber() {\n  return Math.floor(Math.random() * Number.MAX_SAFE_INTEGER);\n}\n\n\u002F\u002F Création d'un objet de test en javascript contenant que des données aléatoires\nconst testObject = () => ({\n  path: randomString(100),\n  stats: {\n    ownerId: { low: randomNumber(), high: randomNumber(), unsigned: true },\n    groupId: { low: randomNumber(), high: randomNumber(), unsigned: true },\n    size: { low: randomNumber(), high: randomNumber(), unsigned: true },\n    compressedSize: {\n      low: randomNumber(),\n      high: randomNumber(),\n      unsigned: true,\n    },\n    lastRead: { low: randomNumber(), high: randomNumber(), unsigned: true },\n    lastModified: { low: randomNumber(), high: randomNumber(), unsigned: true },\n    created: { low: randomNumber(), high: randomNumber(), unsigned: true },\n    mode: { low: randomNumber(), high: randomNumber(), unsigned: true },\n    dev: { low: randomNumber(), high: randomNumber(), unsigned: true },\n    rdev: { low: randomNumber(), high: randomNumber(), unsigned: true },\n    ino: { low: randomNumber(), high: randomNumber(), unsigned: true },\n    nlink: { low: randomNumber(), high: randomNumber(), unsigned: true },\n  },\n  chunks: [randomString(32), randomString(32), randomString(32)],\n  sha256: randomString(32),\n});\n\n\u002F\u002F Lancement du GC pour s'assurer que la mémoire utilisé ne contient que les objets de test\nglobal.gc();\n\u002F\u002F On recupère la mémoire utilisé avant le test\nconst memoryBefore = process.memoryUsage().heapUsed;\n\u002F\u002F On recupère le temps avant le test (pour mesurer le temps de traitement)\nconst time = Date.now();\n\n\u002F\u002F Création des objets. J'ai du lancer plusieurs fois le script pour trouver une valeur qui ne causé pas de crash de \n\u002F\u002F Node.JS pour cause de manque de mémoire\nconst nbObjects = 1_300_000;\nconst testArray = new Array(nbObjects);\nfor (let i = 0; i \u003C nbObjects; i++) {\n  testArray[i] = testObject();\n}\n\n\u002F\u002F Combien de temps à pris la création des objets\nconsole.log(\"Creation time: \", Date.now() - time);\n\u002F\u002F Lancement du GC pour s'assurer que nous n'avons pas d'autres reliquats\nglobal.gc();\n\u002F\u002F Récupération de la mémoire après le test\nconst memoryAfter = process.memoryUsage().heapUsed;\n\nconsole.log(\"Memory consumption: \", filesize.default(memoryAfter - memoryBefore));\nconsole.log(\"Memory consumption by objects: \", filesize.default((memoryAfter - memoryBefore) \u002F nbObjects));\n\n\u002F\u002F Dans la suite on va écrire un fichier contenant le contenu de la mémoire. Cela a été fait initiallement pour \n\u002F\u002F s'assurer que le GC ne supprime pas mes objets car non utilisés.\nconst time2 = Date.now();\n\n\u002F\u002F Remove test file if exist\ntry {\n  fs.unlinkSync(\"test\");\n} catch (e) {}\n\nconst stream = fs.createWriteStream(\"test\");\n\n\u002F\u002F C'est moche, mais c'est pour tester\nstream.on(\"close\", () => {\n  console.log(\"Write to file time: \", Date.now() - time2);\n  \u002F\u002F Size of file test on disk\n  const stats = fs.statSync(\"test\");\n  console.log(\"Size of file on disk: \", filesize.default(stats.size));\n  console.log(\n    \"Size of object in the file\",\n    filesize.default(stats.size \u002F nbObjects)\n  );\n});\n\nfor (const obj of testArray) {\n  stream.write(serialize(obj));\n}\nstream.end();\n","js",[184,7780,7781,7801,7819,7824,7852,7856,7861,7876,7898,7934,7974,7978,7986,7990,7994,7999,8008,8036,8040,8044,8049,8067,8084,8092,8130,8161,8192,8199,8211,8222,8233,8238,8269,8300,8331,8362,8393,8424,8455,8486,8491,8525,8540,8545,8549,8554,8566,8571,8595,8600,8619,8623,8629,8635,8650,8672,8704,8722,8727,8732,8738,8770,8776,8787,8793,8815,8820,8855,8894,8899,8905,8911,8929,8934,8940,8948,8966,8981,8986,9009,9014,9020,9043,9074,9080,9103,9136,9147,9155,9180,9186,9191,9196,9214,9236,9241],{"__ignoreMap":1646},[2113,7782,7783,7786,7789,7791,7794,7796,7799],{"class":2115,"line":2116},[2113,7784,7785],{"class":2326},"const",[2113,7787,7788],{"class":2414}," filesize",[2113,7790,2153],{"class":2334},[2113,7792,7793],{"class":2133}," require",[2113,7795,2423],{"class":2119},[2113,7797,7798],{"class":2149},"\"filesize.js\"",[2113,7800,2553],{"class":2119},[2113,7802,7803,7805,7808,7810,7812,7814,7817],{"class":2115,"line":1647},[2113,7804,7785],{"class":2326},[2113,7806,7807],{"class":2414}," fs",[2113,7809,2153],{"class":2334},[2113,7811,7793],{"class":2133},[2113,7813,2423],{"class":2119},[2113,7815,7816],{"class":2149},"\"fs\"",[2113,7818,2553],{"class":2119},[2113,7820,7821],{"class":2115,"line":1652},[2113,7822,7823],{"class":2396},"\u002F\u002F Utilisation des méthodes de sérialisation et de désérialisation du moteur V8\n",[2113,7825,7826,7828,7830,7833,7835,7838,7841,7843,7845,7847,7850],{"class":2115,"line":2185},[2113,7827,7785],{"class":2326},[2113,7829,7616],{"class":2119},[2113,7831,7832],{"class":2414},"serialize",[2113,7834,932],{"class":2119},[2113,7836,7837],{"class":2414},"deserialize",[2113,7839,7840],{"class":2119}," } ",[2113,7842,2335],{"class":2334},[2113,7844,7793],{"class":2133},[2113,7846,2423],{"class":2119},[2113,7848,7849],{"class":2149},"\"v8\"",[2113,7851,2553],{"class":2119},[2113,7853,7854],{"class":2115,"line":2230},[2113,7855,2125],{"emptyLinePlaceholder":1767},[2113,7857,7858],{"class":2115,"line":2235},[2113,7859,7860],{"class":2396},"\u002F\u002F Méthode pour générer une chaîne de caractère contenant des caractères aléatoires\n",[2113,7862,7863,7866,7869,7871,7874],{"class":2115,"line":2241},[2113,7864,7865],{"class":2326},"function",[2113,7867,7868],{"class":2133}," randomString",[2113,7870,2423],{"class":2119},[2113,7872,7873],{"class":2429},"size",[2113,7875,2433],{"class":2119},[2113,7877,7878,7880,7882,7884,7887,7889,7892,7894,7896],{"class":2115,"line":2246},[2113,7879,2995],{"class":2326},[2113,7881,4003],{"class":2414},[2113,7883,2153],{"class":2334},[2113,7885,7886],{"class":2414}," Buffer",[2113,7888,179],{"class":2119},[2113,7890,7891],{"class":2133},"alloc",[2113,7893,2423],{"class":2119},[2113,7895,7873],{"class":2330},[2113,7897,2553],{"class":2119},[2113,7899,7900,7903,7905,7908,7911,7913,7915,7917,7920,7922,7925,7927,7929,7932],{"class":2115,"line":2464},[2113,7901,7902],{"class":2326},"  for",[2113,7904,2495],{"class":2119},[2113,7906,7907],{"class":2326},"let",[2113,7909,7910],{"class":2330}," i",[2113,7912,2153],{"class":2334},[2113,7914,3208],{"class":2166},[2113,7916,6160],{"class":2119},[2113,7918,7919],{"class":2330},"i",[2113,7921,5912],{"class":2334},[2113,7923,7924],{"class":2330}," size",[2113,7926,6160],{"class":2119},[2113,7928,7919],{"class":2330},[2113,7930,7931],{"class":2334},"++",[2113,7933,2433],{"class":2119},[2113,7935,7936,7939,7941,7943,7945,7947,7950,7952,7955,7957,7960,7962,7965,7967,7969,7972],{"class":2115,"line":2085},[2113,7937,7938],{"class":2330},"    buffer",[2113,7940,4124],{"class":2119},[2113,7942,7919],{"class":2330},[2113,7944,4129],{"class":2119},[2113,7946,2335],{"class":2334},[2113,7948,7949],{"class":2414}," Math",[2113,7951,179],{"class":2119},[2113,7953,7954],{"class":2133},"floor",[2113,7956,2423],{"class":2119},[2113,7958,7959],{"class":2414},"Math",[2113,7961,179],{"class":2119},[2113,7963,7964],{"class":2133},"random",[2113,7966,4543],{"class":2119},[2113,7968,2676],{"class":2334},[2113,7970,7971],{"class":2166}," 256",[2113,7973,2553],{"class":2119},[2113,7975,7976],{"class":2115,"line":2514},[2113,7977,2572],{"class":2119},[2113,7979,7980,7982,7984],{"class":2115,"line":2533},[2113,7981,2578],{"class":2326},[2113,7983,4003],{"class":2330},[2113,7985,2487],{"class":2119},[2113,7987,7988],{"class":2115,"line":2556},[2113,7989,2599],{"class":2119},[2113,7991,7992],{"class":2115,"line":2569},[2113,7993,2125],{"emptyLinePlaceholder":1767},[2113,7995,7996],{"class":2115,"line":2575},[2113,7997,7998],{"class":2396},"\u002F\u002F Méthode pour générer un nombre aléatoire de la taille d'un nombre de 53 bits\n",[2113,8000,8001,8003,8006],{"class":2115,"line":2596},[2113,8002,7865],{"class":2326},[2113,8004,8005],{"class":2133}," randomNumber",[2113,8007,3912],{"class":2119},[2113,8009,8010,8012,8014,8016,8018,8020,8022,8024,8026,8028,8030,8033],{"class":2115,"line":3192},[2113,8011,2578],{"class":2326},[2113,8013,7949],{"class":2414},[2113,8015,179],{"class":2119},[2113,8017,7954],{"class":2133},[2113,8019,2423],{"class":2119},[2113,8021,7959],{"class":2414},[2113,8023,179],{"class":2119},[2113,8025,7964],{"class":2133},[2113,8027,4543],{"class":2119},[2113,8029,2676],{"class":2334},[2113,8031,8032],{"class":2414}," Number",[2113,8034,8035],{"class":2119},".MAX_SAFE_INTEGER);\n",[2113,8037,8038],{"class":2115,"line":3213},[2113,8039,2599],{"class":2119},[2113,8041,8042],{"class":2115,"line":3236},[2113,8043,2125],{"emptyLinePlaceholder":1767},[2113,8045,8046],{"class":2115,"line":3248},[2113,8047,8048],{"class":2396},"\u002F\u002F Création d'un objet de test en javascript contenant que des données aléatoires\n",[2113,8050,8051,8053,8056,8058,8061,8064],{"class":2115,"line":4899},[2113,8052,7785],{"class":2326},[2113,8054,8055],{"class":2133}," testObject",[2113,8057,2153],{"class":2334},[2113,8059,8060],{"class":2119}," () ",[2113,8062,8063],{"class":2326},"=>",[2113,8065,8066],{"class":2119}," ({\n",[2113,8068,8069,8072,8074,8077,8079,8082],{"class":2115,"line":1777},[2113,8070,8071],{"class":2330},"  path",[2113,8073,4429],{"class":2119},[2113,8075,8076],{"class":2133},"randomString",[2113,8078,2423],{"class":2119},[2113,8080,8081],{"class":2166},"100",[2113,8083,7142],{"class":2119},[2113,8085,8086,8089],{"class":2115,"line":4931},[2113,8087,8088],{"class":2330},"  stats",[2113,8090,8091],{"class":2119},": {\n",[2113,8093,8094,8097,8100,8103,8105,8108,8111,8114,8116,8118,8120,8123,8125,8127],{"class":2115,"line":4961},[2113,8095,8096],{"class":2330},"    ownerId",[2113,8098,8099],{"class":2119},": { ",[2113,8101,8102],{"class":2330},"low",[2113,8104,4429],{"class":2119},[2113,8106,8107],{"class":2133},"randomNumber",[2113,8109,8110],{"class":2119},"(), ",[2113,8112,8113],{"class":2330},"high",[2113,8115,4429],{"class":2119},[2113,8117,8107],{"class":2133},[2113,8119,8110],{"class":2119},[2113,8121,8122],{"class":2330},"unsigned",[2113,8124,4429],{"class":2119},[2113,8126,4819],{"class":2166},[2113,8128,8129],{"class":2119}," },\n",[2113,8131,8132,8135,8137,8139,8141,8143,8145,8147,8149,8151,8153,8155,8157,8159],{"class":2115,"line":4976},[2113,8133,8134],{"class":2330},"    groupId",[2113,8136,8099],{"class":2119},[2113,8138,8102],{"class":2330},[2113,8140,4429],{"class":2119},[2113,8142,8107],{"class":2133},[2113,8144,8110],{"class":2119},[2113,8146,8113],{"class":2330},[2113,8148,4429],{"class":2119},[2113,8150,8107],{"class":2133},[2113,8152,8110],{"class":2119},[2113,8154,8122],{"class":2330},[2113,8156,4429],{"class":2119},[2113,8158,4819],{"class":2166},[2113,8160,8129],{"class":2119},[2113,8162,8163,8166,8168,8170,8172,8174,8176,8178,8180,8182,8184,8186,8188,8190],{"class":2115,"line":4993},[2113,8164,8165],{"class":2330},"    size",[2113,8167,8099],{"class":2119},[2113,8169,8102],{"class":2330},[2113,8171,4429],{"class":2119},[2113,8173,8107],{"class":2133},[2113,8175,8110],{"class":2119},[2113,8177,8113],{"class":2330},[2113,8179,4429],{"class":2119},[2113,8181,8107],{"class":2133},[2113,8183,8110],{"class":2119},[2113,8185,8122],{"class":2330},[2113,8187,4429],{"class":2119},[2113,8189,4819],{"class":2166},[2113,8191,8129],{"class":2119},[2113,8193,8194,8197],{"class":2115,"line":5013},[2113,8195,8196],{"class":2330},"    compressedSize",[2113,8198,8091],{"class":2119},[2113,8200,8201,8204,8206,8208],{"class":2115,"line":5018},[2113,8202,8203],{"class":2330},"      low",[2113,8205,4429],{"class":2119},[2113,8207,8107],{"class":2133},[2113,8209,8210],{"class":2119},"(),\n",[2113,8212,8213,8216,8218,8220],{"class":2115,"line":5042},[2113,8214,8215],{"class":2330},"      high",[2113,8217,4429],{"class":2119},[2113,8219,8107],{"class":2133},[2113,8221,8210],{"class":2119},[2113,8223,8224,8227,8229,8231],{"class":2115,"line":5057},[2113,8225,8226],{"class":2330},"      unsigned",[2113,8228,4429],{"class":2119},[2113,8230,4819],{"class":2166},[2113,8232,4706],{"class":2119},[2113,8234,8235],{"class":2115,"line":5062},[2113,8236,8237],{"class":2119},"    },\n",[2113,8239,8240,8243,8245,8247,8249,8251,8253,8255,8257,8259,8261,8263,8265,8267],{"class":2115,"line":5098},[2113,8241,8242],{"class":2330},"    lastRead",[2113,8244,8099],{"class":2119},[2113,8246,8102],{"class":2330},[2113,8248,4429],{"class":2119},[2113,8250,8107],{"class":2133},[2113,8252,8110],{"class":2119},[2113,8254,8113],{"class":2330},[2113,8256,4429],{"class":2119},[2113,8258,8107],{"class":2133},[2113,8260,8110],{"class":2119},[2113,8262,8122],{"class":2330},[2113,8264,4429],{"class":2119},[2113,8266,4819],{"class":2166},[2113,8268,8129],{"class":2119},[2113,8270,8271,8274,8276,8278,8280,8282,8284,8286,8288,8290,8292,8294,8296,8298],{"class":2115,"line":5117},[2113,8272,8273],{"class":2330},"    lastModified",[2113,8275,8099],{"class":2119},[2113,8277,8102],{"class":2330},[2113,8279,4429],{"class":2119},[2113,8281,8107],{"class":2133},[2113,8283,8110],{"class":2119},[2113,8285,8113],{"class":2330},[2113,8287,4429],{"class":2119},[2113,8289,8107],{"class":2133},[2113,8291,8110],{"class":2119},[2113,8293,8122],{"class":2330},[2113,8295,4429],{"class":2119},[2113,8297,4819],{"class":2166},[2113,8299,8129],{"class":2119},[2113,8301,8302,8305,8307,8309,8311,8313,8315,8317,8319,8321,8323,8325,8327,8329],{"class":2115,"line":5142},[2113,8303,8304],{"class":2330},"    created",[2113,8306,8099],{"class":2119},[2113,8308,8102],{"class":2330},[2113,8310,4429],{"class":2119},[2113,8312,8107],{"class":2133},[2113,8314,8110],{"class":2119},[2113,8316,8113],{"class":2330},[2113,8318,4429],{"class":2119},[2113,8320,8107],{"class":2133},[2113,8322,8110],{"class":2119},[2113,8324,8122],{"class":2330},[2113,8326,4429],{"class":2119},[2113,8328,4819],{"class":2166},[2113,8330,8129],{"class":2119},[2113,8332,8333,8336,8338,8340,8342,8344,8346,8348,8350,8352,8354,8356,8358,8360],{"class":2115,"line":5148},[2113,8334,8335],{"class":2330},"    mode",[2113,8337,8099],{"class":2119},[2113,8339,8102],{"class":2330},[2113,8341,4429],{"class":2119},[2113,8343,8107],{"class":2133},[2113,8345,8110],{"class":2119},[2113,8347,8113],{"class":2330},[2113,8349,4429],{"class":2119},[2113,8351,8107],{"class":2133},[2113,8353,8110],{"class":2119},[2113,8355,8122],{"class":2330},[2113,8357,4429],{"class":2119},[2113,8359,4819],{"class":2166},[2113,8361,8129],{"class":2119},[2113,8363,8364,8367,8369,8371,8373,8375,8377,8379,8381,8383,8385,8387,8389,8391],{"class":2115,"line":5163},[2113,8365,8366],{"class":2330},"    dev",[2113,8368,8099],{"class":2119},[2113,8370,8102],{"class":2330},[2113,8372,4429],{"class":2119},[2113,8374,8107],{"class":2133},[2113,8376,8110],{"class":2119},[2113,8378,8113],{"class":2330},[2113,8380,4429],{"class":2119},[2113,8382,8107],{"class":2133},[2113,8384,8110],{"class":2119},[2113,8386,8122],{"class":2330},[2113,8388,4429],{"class":2119},[2113,8390,4819],{"class":2166},[2113,8392,8129],{"class":2119},[2113,8394,8395,8398,8400,8402,8404,8406,8408,8410,8412,8414,8416,8418,8420,8422],{"class":2115,"line":5169},[2113,8396,8397],{"class":2330},"    rdev",[2113,8399,8099],{"class":2119},[2113,8401,8102],{"class":2330},[2113,8403,4429],{"class":2119},[2113,8405,8107],{"class":2133},[2113,8407,8110],{"class":2119},[2113,8409,8113],{"class":2330},[2113,8411,4429],{"class":2119},[2113,8413,8107],{"class":2133},[2113,8415,8110],{"class":2119},[2113,8417,8122],{"class":2330},[2113,8419,4429],{"class":2119},[2113,8421,4819],{"class":2166},[2113,8423,8129],{"class":2119},[2113,8425,8426,8429,8431,8433,8435,8437,8439,8441,8443,8445,8447,8449,8451,8453],{"class":2115,"line":5175},[2113,8427,8428],{"class":2330},"    ino",[2113,8430,8099],{"class":2119},[2113,8432,8102],{"class":2330},[2113,8434,4429],{"class":2119},[2113,8436,8107],{"class":2133},[2113,8438,8110],{"class":2119},[2113,8440,8113],{"class":2330},[2113,8442,4429],{"class":2119},[2113,8444,8107],{"class":2133},[2113,8446,8110],{"class":2119},[2113,8448,8122],{"class":2330},[2113,8450,4429],{"class":2119},[2113,8452,4819],{"class":2166},[2113,8454,8129],{"class":2119},[2113,8456,8457,8460,8462,8464,8466,8468,8470,8472,8474,8476,8478,8480,8482,8484],{"class":2115,"line":5180},[2113,8458,8459],{"class":2330},"    nlink",[2113,8461,8099],{"class":2119},[2113,8463,8102],{"class":2330},[2113,8465,4429],{"class":2119},[2113,8467,8107],{"class":2133},[2113,8469,8110],{"class":2119},[2113,8471,8113],{"class":2330},[2113,8473,4429],{"class":2119},[2113,8475,8107],{"class":2133},[2113,8477,8110],{"class":2119},[2113,8479,8122],{"class":2330},[2113,8481,4429],{"class":2119},[2113,8483,4819],{"class":2166},[2113,8485,8129],{"class":2119},[2113,8487,8488],{"class":2115,"line":5199},[2113,8489,8490],{"class":2119},"  },\n",[2113,8492,8493,8496,8498,8500,8502,8505,8508,8510,8512,8514,8516,8518,8520,8522],{"class":2115,"line":5204},[2113,8494,8495],{"class":2330},"  chunks",[2113,8497,6155],{"class":2119},[2113,8499,8076],{"class":2133},[2113,8501,2423],{"class":2119},[2113,8503,8504],{"class":2166},"32",[2113,8506,8507],{"class":2119},"), ",[2113,8509,8076],{"class":2133},[2113,8511,2423],{"class":2119},[2113,8513,8504],{"class":2166},[2113,8515,8507],{"class":2119},[2113,8517,8076],{"class":2133},[2113,8519,2423],{"class":2119},[2113,8521,8504],{"class":2166},[2113,8523,8524],{"class":2119},")],\n",[2113,8526,8527,8530,8532,8534,8536,8538],{"class":2115,"line":5209},[2113,8528,8529],{"class":2330},"  sha256",[2113,8531,4429],{"class":2119},[2113,8533,8076],{"class":2133},[2113,8535,2423],{"class":2119},[2113,8537,8504],{"class":2166},[2113,8539,7142],{"class":2119},[2113,8541,8542],{"class":2115,"line":5232},[2113,8543,8544],{"class":2119},"});\n",[2113,8546,8547],{"class":2115,"line":5237},[2113,8548,2125],{"emptyLinePlaceholder":1767},[2113,8550,8551],{"class":2115,"line":5242},[2113,8552,8553],{"class":2396},"\u002F\u002F Lancement du GC pour s'assurer que la mémoire utilisé ne contient que les objets de test\n",[2113,8555,8556,8559,8561,8564],{"class":2115,"line":5267},[2113,8557,8558],{"class":2414},"global",[2113,8560,179],{"class":2119},[2113,8562,8563],{"class":2133},"gc",[2113,8565,3944],{"class":2119},[2113,8567,8568],{"class":2115,"line":5282},[2113,8569,8570],{"class":2396},"\u002F\u002F On recupère la mémoire utilisé avant le test\n",[2113,8572,8573,8575,8578,8580,8583,8585,8588,8590,8593],{"class":2115,"line":5295},[2113,8574,7785],{"class":2326},[2113,8576,8577],{"class":2414}," memoryBefore",[2113,8579,2153],{"class":2334},[2113,8581,8582],{"class":2414}," process",[2113,8584,179],{"class":2119},[2113,8586,8587],{"class":2133},"memoryUsage",[2113,8589,5224],{"class":2119},[2113,8591,8592],{"class":2330},"heapUsed",[2113,8594,2487],{"class":2119},[2113,8596,8597],{"class":2115,"line":5310},[2113,8598,8599],{"class":2396},"\u002F\u002F On recupère le temps avant le test (pour mesurer le temps de traitement)\n",[2113,8601,8602,8604,8607,8609,8612,8614,8617],{"class":2115,"line":5315},[2113,8603,7785],{"class":2326},[2113,8605,8606],{"class":2414}," time",[2113,8608,2153],{"class":2334},[2113,8610,8611],{"class":2414}," Date",[2113,8613,179],{"class":2119},[2113,8615,8616],{"class":2133},"now",[2113,8618,3944],{"class":2119},[2113,8620,8621],{"class":2115,"line":5320},[2113,8622,2125],{"emptyLinePlaceholder":1767},[2113,8624,8626],{"class":2115,"line":8625},52,[2113,8627,8628],{"class":2396},"\u002F\u002F Création des objets. J'ai du lancer plusieurs fois le script pour trouver une valeur qui ne causé pas de crash de \n",[2113,8630,8632],{"class":2115,"line":8631},53,[2113,8633,8634],{"class":2396},"\u002F\u002F Node.JS pour cause de manque de mémoire\n",[2113,8636,8638,8640,8643,8645,8648],{"class":2115,"line":8637},54,[2113,8639,7785],{"class":2326},[2113,8641,8642],{"class":2414}," nbObjects",[2113,8644,2153],{"class":2334},[2113,8646,8647],{"class":2166}," 1_300_000",[2113,8649,2487],{"class":2119},[2113,8651,8653,8655,8658,8660,8662,8665,8667,8670],{"class":2115,"line":8652},55,[2113,8654,7785],{"class":2326},[2113,8656,8657],{"class":2414}," testArray",[2113,8659,2153],{"class":2334},[2113,8661,4778],{"class":2326},[2113,8663,8664],{"class":2133}," Array",[2113,8666,2423],{"class":2119},[2113,8668,8669],{"class":2330},"nbObjects",[2113,8671,2553],{"class":2119},[2113,8673,8675,8678,8680,8682,8684,8686,8688,8690,8692,8694,8696,8698,8700,8702],{"class":2115,"line":8674},56,[2113,8676,8677],{"class":2326},"for",[2113,8679,2495],{"class":2119},[2113,8681,7907],{"class":2326},[2113,8683,7910],{"class":2330},[2113,8685,2153],{"class":2334},[2113,8687,3208],{"class":2166},[2113,8689,6160],{"class":2119},[2113,8691,7919],{"class":2330},[2113,8693,5912],{"class":2334},[2113,8695,8642],{"class":2330},[2113,8697,6160],{"class":2119},[2113,8699,7919],{"class":2330},[2113,8701,7931],{"class":2334},[2113,8703,2433],{"class":2119},[2113,8705,8707,8710,8712,8714,8716,8718,8720],{"class":2115,"line":8706},57,[2113,8708,8709],{"class":2330},"  testArray",[2113,8711,4124],{"class":2119},[2113,8713,7919],{"class":2330},[2113,8715,4129],{"class":2119},[2113,8717,2335],{"class":2334},[2113,8719,8055],{"class":2133},[2113,8721,3944],{"class":2119},[2113,8723,8725],{"class":2115,"line":8724},58,[2113,8726,2599],{"class":2119},[2113,8728,8730],{"class":2115,"line":8729},59,[2113,8731,2125],{"emptyLinePlaceholder":1767},[2113,8733,8735],{"class":2115,"line":8734},60,[2113,8736,8737],{"class":2396},"\u002F\u002F Combien de temps à pris la création des objets\n",[2113,8739,8741,8744,8746,8749,8751,8754,8756,8758,8760,8762,8764,8766,8768],{"class":2115,"line":8740},61,[2113,8742,8743],{"class":2414},"console",[2113,8745,179],{"class":2119},[2113,8747,8748],{"class":2133},"log",[2113,8750,2423],{"class":2119},[2113,8752,8753],{"class":2149},"\"Creation time: \"",[2113,8755,932],{"class":2119},[2113,8757,36],{"class":2414},[2113,8759,179],{"class":2119},[2113,8761,8616],{"class":2133},[2113,8763,4543],{"class":2119},[2113,8765,3242],{"class":2334},[2113,8767,8606],{"class":2330},[2113,8769,2553],{"class":2119},[2113,8771,8773],{"class":2115,"line":8772},62,[2113,8774,8775],{"class":2396},"\u002F\u002F Lancement du GC pour s'assurer que nous n'avons pas d'autres reliquats\n",[2113,8777,8779,8781,8783,8785],{"class":2115,"line":8778},63,[2113,8780,8558],{"class":2414},[2113,8782,179],{"class":2119},[2113,8784,8563],{"class":2133},[2113,8786,3944],{"class":2119},[2113,8788,8790],{"class":2115,"line":8789},64,[2113,8791,8792],{"class":2396},"\u002F\u002F Récupération de la mémoire après le test\n",[2113,8794,8796,8798,8801,8803,8805,8807,8809,8811,8813],{"class":2115,"line":8795},65,[2113,8797,7785],{"class":2326},[2113,8799,8800],{"class":2414}," memoryAfter",[2113,8802,2153],{"class":2334},[2113,8804,8582],{"class":2414},[2113,8806,179],{"class":2119},[2113,8808,8587],{"class":2133},[2113,8810,5224],{"class":2119},[2113,8812,8592],{"class":2330},[2113,8814,2487],{"class":2119},[2113,8816,8818],{"class":2115,"line":8817},66,[2113,8819,2125],{"emptyLinePlaceholder":1767},[2113,8821,8823,8825,8827,8829,8831,8834,8836,8839,8841,8844,8846,8849,8851,8853],{"class":2115,"line":8822},67,[2113,8824,8743],{"class":2414},[2113,8826,179],{"class":2119},[2113,8828,8748],{"class":2133},[2113,8830,2423],{"class":2119},[2113,8832,8833],{"class":2149},"\"Memory consumption: \"",[2113,8835,932],{"class":2119},[2113,8837,8838],{"class":2414},"filesize",[2113,8840,179],{"class":2119},[2113,8842,8843],{"class":2133},"default",[2113,8845,2423],{"class":2119},[2113,8847,8848],{"class":2330},"memoryAfter",[2113,8850,2270],{"class":2334},[2113,8852,8577],{"class":2330},[2113,8854,5818],{"class":2119},[2113,8856,8858,8860,8862,8864,8866,8869,8871,8873,8875,8877,8880,8882,8884,8886,8888,8890,8892],{"class":2115,"line":8857},68,[2113,8859,8743],{"class":2414},[2113,8861,179],{"class":2119},[2113,8863,8748],{"class":2133},[2113,8865,2423],{"class":2119},[2113,8867,8868],{"class":2149},"\"Memory consumption by objects: \"",[2113,8870,932],{"class":2119},[2113,8872,8838],{"class":2414},[2113,8874,179],{"class":2119},[2113,8876,8843],{"class":2133},[2113,8878,8879],{"class":2119},"((",[2113,8881,8848],{"class":2330},[2113,8883,2270],{"class":2334},[2113,8885,8577],{"class":2330},[2113,8887,5709],{"class":2119},[2113,8889,2520],{"class":2334},[2113,8891,8642],{"class":2330},[2113,8893,5818],{"class":2119},[2113,8895,8897],{"class":2115,"line":8896},69,[2113,8898,2125],{"emptyLinePlaceholder":1767},[2113,8900,8902],{"class":2115,"line":8901},70,[2113,8903,8904],{"class":2396},"\u002F\u002F Dans la suite on va écrire un fichier contenant le contenu de la mémoire. Cela a été fait initiallement pour \n",[2113,8906,8908],{"class":2115,"line":8907},71,[2113,8909,8910],{"class":2396},"\u002F\u002F s'assurer que le GC ne supprime pas mes objets car non utilisés.\n",[2113,8912,8914,8916,8919,8921,8923,8925,8927],{"class":2115,"line":8913},72,[2113,8915,7785],{"class":2326},[2113,8917,8918],{"class":2414}," time2",[2113,8920,2153],{"class":2334},[2113,8922,8611],{"class":2414},[2113,8924,179],{"class":2119},[2113,8926,8616],{"class":2133},[2113,8928,3944],{"class":2119},[2113,8930,8932],{"class":2115,"line":8931},73,[2113,8933,2125],{"emptyLinePlaceholder":1767},[2113,8935,8937],{"class":2115,"line":8936},74,[2113,8938,8939],{"class":2396},"\u002F\u002F Remove test file if exist\n",[2113,8941,8943,8946],{"class":2115,"line":8942},75,[2113,8944,8945],{"class":2326},"try",[2113,8947,2647],{"class":2119},[2113,8949,8951,8954,8956,8959,8961,8964],{"class":2115,"line":8950},76,[2113,8952,8953],{"class":2414},"  fs",[2113,8955,179],{"class":2119},[2113,8957,8958],{"class":2133},"unlinkSync",[2113,8960,2423],{"class":2119},[2113,8962,8963],{"class":2149},"\"test\"",[2113,8965,2553],{"class":2119},[2113,8967,8969,8971,8974,8976,8978],{"class":2115,"line":8968},77,[2113,8970,4224],{"class":2119},[2113,8972,8973],{"class":2326},"catch",[2113,8975,2495],{"class":2119},[2113,8977,5601],{"class":2330},[2113,8979,8980],{"class":2119},") {}\n",[2113,8982,8984],{"class":2115,"line":8983},78,[2113,8985,2125],{"emptyLinePlaceholder":1767},[2113,8987,8989,8991,8994,8996,8998,9000,9003,9005,9007],{"class":2115,"line":8988},79,[2113,8990,7785],{"class":2326},[2113,8992,8993],{"class":2414}," stream",[2113,8995,2153],{"class":2334},[2113,8997,7807],{"class":2414},[2113,8999,179],{"class":2119},[2113,9001,9002],{"class":2133},"createWriteStream",[2113,9004,2423],{"class":2119},[2113,9006,8963],{"class":2149},[2113,9008,2553],{"class":2119},[2113,9010,9012],{"class":2115,"line":9011},80,[2113,9013,2125],{"emptyLinePlaceholder":1767},[2113,9015,9017],{"class":2115,"line":9016},81,[2113,9018,9019],{"class":2396},"\u002F\u002F C'est moche, mais c'est pour tester\n",[2113,9021,9023,9026,9028,9031,9033,9036,9039,9041],{"class":2115,"line":9022},82,[2113,9024,9025],{"class":2414},"stream",[2113,9027,179],{"class":2119},[2113,9029,9030],{"class":2133},"on",[2113,9032,2423],{"class":2119},[2113,9034,9035],{"class":2149},"\"close\"",[2113,9037,9038],{"class":2119},", () ",[2113,9040,8063],{"class":2326},[2113,9042,2647],{"class":2119},[2113,9044,9046,9049,9051,9053,9055,9058,9060,9062,9064,9066,9068,9070,9072],{"class":2115,"line":9045},83,[2113,9047,9048],{"class":2414},"  console",[2113,9050,179],{"class":2119},[2113,9052,8748],{"class":2133},[2113,9054,2423],{"class":2119},[2113,9056,9057],{"class":2149},"\"Write to file time: \"",[2113,9059,932],{"class":2119},[2113,9061,36],{"class":2414},[2113,9063,179],{"class":2119},[2113,9065,8616],{"class":2133},[2113,9067,4543],{"class":2119},[2113,9069,3242],{"class":2334},[2113,9071,8918],{"class":2330},[2113,9073,2553],{"class":2119},[2113,9075,9077],{"class":2115,"line":9076},84,[2113,9078,9079],{"class":2396},"  \u002F\u002F Size of file test on disk\n",[2113,9081,9083,9085,9088,9090,9092,9094,9097,9099,9101],{"class":2115,"line":9082},85,[2113,9084,2995],{"class":2326},[2113,9086,9087],{"class":2414}," stats",[2113,9089,2153],{"class":2334},[2113,9091,7807],{"class":2414},[2113,9093,179],{"class":2119},[2113,9095,9096],{"class":2133},"statSync",[2113,9098,2423],{"class":2119},[2113,9100,8963],{"class":2149},[2113,9102,2553],{"class":2119},[2113,9104,9106,9108,9110,9112,9114,9117,9119,9121,9123,9125,9127,9130,9132,9134],{"class":2115,"line":9105},86,[2113,9107,9048],{"class":2414},[2113,9109,179],{"class":2119},[2113,9111,8748],{"class":2133},[2113,9113,2423],{"class":2119},[2113,9115,9116],{"class":2149},"\"Size of file on disk: \"",[2113,9118,932],{"class":2119},[2113,9120,8838],{"class":2414},[2113,9122,179],{"class":2119},[2113,9124,8843],{"class":2133},[2113,9126,2423],{"class":2119},[2113,9128,9129],{"class":2414},"stats",[2113,9131,179],{"class":2119},[2113,9133,7873],{"class":2330},[2113,9135,5818],{"class":2119},[2113,9137,9139,9141,9143,9145],{"class":2115,"line":9138},87,[2113,9140,9048],{"class":2414},[2113,9142,179],{"class":2119},[2113,9144,8748],{"class":2133},[2113,9146,3146],{"class":2119},[2113,9148,9150,9153],{"class":2115,"line":9149},88,[2113,9151,9152],{"class":2149},"    \"Size of object in the file\"",[2113,9154,4706],{"class":2119},[2113,9156,9158,9161,9163,9165,9167,9169,9171,9173,9176,9178],{"class":2115,"line":9157},89,[2113,9159,9160],{"class":2414},"    filesize",[2113,9162,179],{"class":2119},[2113,9164,8843],{"class":2133},[2113,9166,2423],{"class":2119},[2113,9168,9129],{"class":2414},[2113,9170,179],{"class":2119},[2113,9172,7873],{"class":2330},[2113,9174,9175],{"class":2334}," \u002F",[2113,9177,8642],{"class":2330},[2113,9179,4660],{"class":2119},[2113,9181,9183],{"class":2115,"line":9182},90,[2113,9184,9185],{"class":2119},"  );\n",[2113,9187,9189],{"class":2115,"line":9188},91,[2113,9190,8544],{"class":2119},[2113,9192,9194],{"class":2115,"line":9193},92,[2113,9195,2125],{"emptyLinePlaceholder":1767},[2113,9197,9199,9201,9203,9205,9208,9210,9212],{"class":2115,"line":9198},93,[2113,9200,8677],{"class":2326},[2113,9202,2495],{"class":2119},[2113,9204,7785],{"class":2326},[2113,9206,9207],{"class":2414}," obj",[2113,9209,2281],{"class":2326},[2113,9211,8657],{"class":2330},[2113,9213,2433],{"class":2119},[2113,9215,9217,9220,9222,9225,9227,9229,9231,9234],{"class":2115,"line":9216},94,[2113,9218,9219],{"class":2414},"  stream",[2113,9221,179],{"class":2119},[2113,9223,9224],{"class":2133},"write",[2113,9226,2423],{"class":2119},[2113,9228,7832],{"class":2133},[2113,9230,2423],{"class":2119},[2113,9232,9233],{"class":2330},"obj",[2113,9235,5818],{"class":2119},[2113,9237,9239],{"class":2115,"line":9238},95,[2113,9240,2599],{"class":2119},[2113,9242,9244,9246,9248,9251],{"class":2115,"line":9243},96,[2113,9245,9025],{"class":2414},[2113,9247,179],{"class":2119},[2113,9249,9250],{"class":2133},"end",[2113,9252,3944],{"class":2119},[12,9254,9255],{},"En estimant rapidement la mémoire que la taille de l'objet aurait dû prendre, je l'estime à environ 432 octets (12\nnombres de 2*64 bits + 1 octet de boolean + 128 caractères pour les chunks et 100 caractères pour le nom).",[12,9257,9258],{},"Le code n'est pas le plus propre, mais le but est ici d'illustrer rapidement la problématique. Voici le résultat de ce\ntest :",[22,9260,9261,9269],{},[25,9262,9263],{},[28,9264,9265,9267],{},[31,9266],{},[31,9268],{},[41,9270,9271,9279,9287,9295,9303,9311,9319],{},[28,9272,9273,9276],{},[46,9274,9275],{},"Nombre d'objets créés",[46,9277,9278],{},"1 300 000",[28,9280,9281,9284],{},[46,9282,9283],{},"Temps de création",[46,9285,9286],{},"15 secondes",[28,9288,9289,9292],{},[46,9290,9291],{},"Consommation mémoire",[46,9293,9294],{},"2,8 Go",[28,9296,9297,9300],{},[46,9298,9299],{},"Consommation moyenne par objet",[46,9301,9302],{},"2,2 Ko",[28,9304,9305,9308],{},[46,9306,9307],{},"Temps d'écriture dans le fichier",[46,9309,9310],{},"91 secondes",[28,9312,9313,9316],{},[46,9314,9315],{},"Taille du fichier sur le disque",[46,9317,9318],{},"1,1 Go",[28,9320,9321,9324],{},[46,9322,9323],{},"Taille moyenne d'un objet dans le fichier",[46,9325,9326],{},"903 octets",[12,9328,9329],{},"Avec ce premier exemple, on peut déjà constater que la consommation mémoire est très importante. En effet, on est à\n2,2Ko par objet au lieu des 432 estimés (soit 5 fois plus). La taille des objets dans le fichier est déjà un peu plus\nraisonnable.",[12,9331,9332],{},"La seule explication au fait que la consommation mémoire soit aussi importante est, je pense, liée au fait que dans V8,\nles structures sont toutes des objets avec des méthodes par défaut et une structure minimaliste (les Buffers également).",[128,9334,9336],{"id":9335},"test-avec-un-bigint","Test avec un BigInt",[12,9338,9339,9340,9343],{},"La question que l'on peut se poser est : pourquoi utiliser la structure ",[184,9341,9342],{},"{ low: Number, high: Number, unsigned: Bool }"," ?",[12,9345,9346],{},"J'utilise cette structure car pour la persistance, j'utilise la librairie protobuf.js qui ne supporte pas les BigInt,\nmais qui utilise à la place la librairie long.js qui utilise cette structure.",[12,9348,9349],{},"J'ai donc effectué un test en remplaçant chaque objet complexe par le nouveau type BigInt de Node.js. Voici le code :",[12,9351,9352],{},"Pour générer le nombre aléatoire, je me base sur un entier de 2*53 bits.",[2105,9354,9356],{"className":7776,"code":9355,"language":7778,"meta":1646,"style":1646},"function randomNumber() {\n  return Math.floor(Math.random() * Number.MAX_SAFE_INTEGER);\n}\n\nfunction randomBigInt() {\n  return (BigInt(randomNumber()) \u003C\u003C 53n) + BigInt(randomNumber());\n}\n",[184,9357,9358,9366,9392,9396,9400,9409,9447],{"__ignoreMap":1646},[2113,9359,9360,9362,9364],{"class":2115,"line":2116},[2113,9361,7865],{"class":2326},[2113,9363,8005],{"class":2133},[2113,9365,3912],{"class":2119},[2113,9367,9368,9370,9372,9374,9376,9378,9380,9382,9384,9386,9388,9390],{"class":2115,"line":1647},[2113,9369,2578],{"class":2326},[2113,9371,7949],{"class":2414},[2113,9373,179],{"class":2119},[2113,9375,7954],{"class":2133},[2113,9377,2423],{"class":2119},[2113,9379,7959],{"class":2414},[2113,9381,179],{"class":2119},[2113,9383,7964],{"class":2133},[2113,9385,4543],{"class":2119},[2113,9387,2676],{"class":2334},[2113,9389,8032],{"class":2414},[2113,9391,8035],{"class":2119},[2113,9393,9394],{"class":2115,"line":1652},[2113,9395,2599],{"class":2119},[2113,9397,9398],{"class":2115,"line":2185},[2113,9399,2125],{"emptyLinePlaceholder":1767},[2113,9401,9402,9404,9407],{"class":2115,"line":2230},[2113,9403,7865],{"class":2326},[2113,9405,9406],{"class":2133}," randomBigInt",[2113,9408,3912],{"class":2119},[2113,9410,9411,9413,9415,9418,9420,9422,9425,9428,9431,9434,9436,9438,9441,9443,9445],{"class":2115,"line":2235},[2113,9412,2578],{"class":2326},[2113,9414,2495],{"class":2119},[2113,9416,9417],{"class":2133},"BigInt",[2113,9419,2423],{"class":2119},[2113,9421,8107],{"class":2133},[2113,9423,9424],{"class":2119},"()) ",[2113,9426,9427],{"class":2334},"\u003C\u003C",[2113,9429,9430],{"class":2166}," 53",[2113,9432,9433],{"class":2326},"n",[2113,9435,5709],{"class":2119},[2113,9437,3061],{"class":2334},[2113,9439,9440],{"class":2133}," BigInt",[2113,9442,2423],{"class":2119},[2113,9444,8107],{"class":2133},[2113,9446,2593],{"class":2119},[2113,9448,9449],{"class":2115,"line":2241},[2113,9450,2599],{"class":2119},[12,9452,9453],{},"Le résultat est le suivant:",[22,9455,9456,9464],{},[25,9457,9458],{},[28,9459,9460,9462],{},[31,9461],{},[31,9463],{},[41,9465,9466,9472,9479,9486,9493,9500,9507],{},[28,9467,9468,9470],{},[46,9469,9275],{},[46,9471,9278],{},[28,9473,9474,9476],{},[46,9475,9283],{},[46,9477,9478],{},"14 secondes",[28,9480,9481,9483],{},[46,9482,9291],{},[46,9484,9485],{},"2,1 Go",[28,9487,9488,9490],{},[46,9489,9299],{},[46,9491,9492],{},"1,7 Ko",[28,9494,9495,9497],{},[46,9496,9307],{},[46,9498,9499],{},"40 secondes",[28,9501,9502,9504],{},[46,9503,9315],{},[46,9505,9506],{},"747,8 Mo",[28,9508,9509,9511],{},[46,9510,9323],{},[46,9512,9513],{},"603 octets",[12,9515,9516,9517,9519],{},"L'utilisation de ",[184,9518,9417],{}," permet de réduire la consommation mémoire de 25% et la taille des objets dans le fichier de\n33%. La taille des objets sur le disque est acceptable. En revanche, la consommation mémoire de Node.js reste trop\nimportante.",[128,9521,9523],{"id":9522},"ecriture-du-même-code-en-rust","Ecriture du même code en Rust",[12,9525,9526],{},"Il est possible d'optimiser certaines parties de l'application grâce à la notion de node_modules natifs. Habituellement,\nces modules sont écrits en C++ en utilisant N-API. Il existe des bindings pour Rust, ce qui permet d'écrire une partie\nde l'application en Rust et de l'utiliser dans Node.JS.",[12,9528,9529,9530,9533,9534,179],{},"Pour comparer les performances de Rust et de Node.js, j'ai écrit le même code en Rust. Pour les nombres, je me suis basé\nsur des entiers de 64 bits et pour le nom de fichier, sur un ",[184,9531,9532],{},"Vec\u003Cu8>",". Je me suis basé sur une table de 32 caractères\npour le ",[184,9535,9536],{},"Sha256",[2105,9538,9540],{"className":3855,"code":9539,"language":1775,"meta":1646,"style":1646},"use humansize::{format_size, make_format, DECIMAL};\nuse procinfo::pid;\nuse rand::{rngs::ThreadRng, Rng};\nuse serde::{Deserialize, Serialize};\nuse std::io::BufWriter;\n\n#[derive(Serialize, Deserialize, Debug)]\nstruct Stats {\n    owner_id: u64,\n    group_id: u64,\n    size: u64,\n    compressed_size: u64,\n    last_read: u64,\n    last_modified: u64,\n    created: u64,\n    mode: u64,\n    dev: u64,\n    rdev: u64,\n    ino: u64,\n    nlink: u64,\n}\n\n#[derive(Serialize, Deserialize, Debug)]\nstruct Sha256 {\n    data: [u8; 32],\n}\n\n#[derive(Serialize, Deserialize, Debug)]\nstruct TestObject {\n    #[serde(with = \"serde_bytes\")]\n    path: Vec\u003Cu8>,\n    stats: Stats,\n\n    chunks: Vec\u003CSha256>,\n    sha256: Sha256,\n}\n\nfn generate_random_number(rng: &mut ThreadRng) -> u64 {\n    rng.gen()\n}\n\nfn generate_random_vect(rng: &mut ThreadRng, size: usize) -> Vec\u003Cu8> {\n    let mut vect: Vec\u003Cu8> = Vec::with_capacity(size);\n    for _ in 0..size {\n        vect.push(rng.gen());\n    }\n    vect\n}\n\nfn generate_random_sha256(rng: &mut ThreadRng) -> Sha256 {\n    let mut sha256 = Sha256 { data: [0; 32] };\n    for i in 0..32 {\n        sha256.data[i] = rng.gen();\n    }\n    sha256\n}\n\nfn main() {\n    let formatter = make_format(DECIMAL);\n    let mut rng = rand::thread_rng();\n    \u002F\u002F Get the memory consumption of the current process\n    let memory = pid::statm_self().unwrap();\n    \u002F\u002F Get the current time in ms\n    let now = std::time::SystemTime::now()\n        .duration_since(std::time::UNIX_EPOCH)\n        .unwrap()\n        .as_millis();\n\n    \u002F\u002F Create a vector with 1_300_000 elements of type TestObject\n    let nb_object = 1_300_000;\n    let mut test_objects: Vec\u003CTestObject> = Vec::with_capacity(nb_object);\n    for _ in 0..nb_object {\n        test_objects.push(TestObject {\n            path: generate_random_vect(&mut rng, 100),\n            stats: Stats {\n                owner_id: generate_random_number(&mut rng),\n                group_id: generate_random_number(&mut rng),\n                size: generate_random_number(&mut rng),\n                compressed_size: generate_random_number(&mut rng),\n                last_read: generate_random_number(&mut rng),\n                last_modified: generate_random_number(&mut rng),\n                created: generate_random_number(&mut rng),\n                mode: generate_random_number(&mut rng),\n                dev: generate_random_number(&mut rng),\n                rdev: generate_random_number(&mut rng),\n                ino: generate_random_number(&mut rng),\n                nlink: generate_random_number(&mut rng),\n            },\n            chunks: vec![\n                generate_random_sha256(&mut rng),\n                generate_random_sha256(&mut rng),\n                generate_random_sha256(&mut rng),\n            ],\n            sha256: generate_random_sha256(&mut rng),\n        });\n    }\n\n    let now_after_creation = std::time::SystemTime::now()\n        .duration_since(std::time::UNIX_EPOCH)\n        .unwrap()\n        .as_millis();\n    \u002F\u002F Calculate the creation time\n    println!(\"Creation time: {} ms\", now_after_creation - now);\n\n    \u002F\u002F Get the memory consumption of the current process\n    let memory_after_creation = pid::statm_self().unwrap();\n    \u002F\u002F Show memory consumption\n    println!(\n        \"Memory consumption after creation: {}\",\n        formatter((memory_after_creation.size - memory.size) * 4096)\n    );\n    \u002F\u002F Show average memory consumption per object\n    println!(\n        \"Average memory consumption per object: {}\",\n        formatter(((memory_after_creation.size - memory.size) * 4096) \u002F nb_object)\n    );\n\n    \u002F\u002F Remove the file test.bin if it exists\n    match std::fs::remove_file(\"test.bin\") {\n        Ok(_) => {}\n        Err(_) => {}\n    }\n\n    \u002F\u002F Serialize all the objects in a file\n    let file = std::fs::File::create(\"test.bin\").unwrap();\n    let mut buffile = BufWriter::new(file);\n    for test_object in test_objects.iter() {\n        bincode::serialize_into(&mut buffile, test_object).unwrap();\n    }\n\n    \u002F\u002F Show time to write\n    let now_after_serialization = std::time::SystemTime::now()\n        .duration_since(std::time::UNIX_EPOCH)\n        .unwrap()\n        .as_millis();\n\n    println!(\n        \"Write to file time: {} ms\",\n        now_after_serialization - now_after_creation\n    );\n\n    \u002F\u002F Get the file size and the average object size in file\n    let metadata = std::fs::metadata(\"test.bin\").unwrap();\n    println!(\n        \"Size of file on disk: {}\",\n        format_size(metadata.len(), DECIMAL)\n    );\n    println!(\n        \"Size of object in the file: {}\",\n        format_size(metadata.len() \u002F nb_object as u64, DECIMAL)\n    );\n}\n",[184,9541,9542,9557,9567,9592,9611,9628,9632,9651,9660,9671,9682,9692,9703,9714,9725,9735,9745,9755,9765,9775,9785,9789,9793,9809,9818,9833,9837,9841,9857,9866,9878,9893,9905,9909,9924,9935,9939,9943,9968,9980,9984,9988,10023,10057,10077,10097,10101,10106,10110,10114,10137,10166,10182,10205,10209,10214,10218,10222,10230,10248,10267,10272,10295,10300,10327,10350,10358,10367,10371,10376,10389,10424,10440,10455,10477,10488,10506,10523,10540,10557,10574,10591,10608,10625,10642,10659,10676,10693,10698,10711,10724,10736,10748,10753,10771,10776,10780,10785,10811,10832,10841,10850,10856,10878,10883,10888,10910,10916,10923,10931,10956,10962,10968,10975,10983,11008,11013,11018,11024,11047,11060,11072,11077,11082,11088,11122,11148,11167,11195,11200,11205,11211,11237,11258,11267,11276,11281,11288,11296,11307,11312,11317,11323,11354,11361,11369,11389,11394,11401,11409,11438,11443],{"__ignoreMap":1646},[2113,9543,9544,9546,9549,9552,9555],{"class":2115,"line":2116},[2113,9545,3863],{"class":2326},[2113,9547,9548],{"class":2414}," humansize",[2113,9550,9551],{"class":2119},"::{format_size, make_format, ",[2113,9553,9554],{"class":2414},"DECIMAL",[2113,9556,2787],{"class":2119},[2113,9558,9559,9561,9564],{"class":2115,"line":1647},[2113,9560,3863],{"class":2326},[2113,9562,9563],{"class":2414}," procinfo",[2113,9565,9566],{"class":2119},"::pid;\n",[2113,9568,9569,9571,9574,9577,9580,9582,9585,9587,9590],{"class":2115,"line":1652},[2113,9570,3863],{"class":2326},[2113,9572,9573],{"class":2414}," rand",[2113,9575,9576],{"class":2119},"::{",[2113,9578,9579],{"class":2414},"rngs",[2113,9581,2418],{"class":2119},[2113,9583,9584],{"class":2414},"ThreadRng",[2113,9586,932],{"class":2119},[2113,9588,9589],{"class":2414},"Rng",[2113,9591,2787],{"class":2119},[2113,9593,9594,9596,9599,9601,9604,9606,9609],{"class":2115,"line":2185},[2113,9595,3863],{"class":2326},[2113,9597,9598],{"class":2414}," serde",[2113,9600,9576],{"class":2119},[2113,9602,9603],{"class":2414},"Deserialize",[2113,9605,932],{"class":2119},[2113,9607,9608],{"class":2414},"Serialize",[2113,9610,2787],{"class":2119},[2113,9612,9613,9615,9617,9619,9621,9623,9626],{"class":2115,"line":2230},[2113,9614,3863],{"class":2326},[2113,9616,3885],{"class":2414},[2113,9618,2418],{"class":2119},[2113,9620,4486],{"class":2414},[2113,9622,2418],{"class":2119},[2113,9624,9625],{"class":2414},"BufWriter",[2113,9627,2487],{"class":2119},[2113,9629,9630],{"class":2115,"line":2235},[2113,9631,2125],{"emptyLinePlaceholder":1767},[2113,9633,9634,9637,9639,9641,9643,9645,9648],{"class":2115,"line":2241},[2113,9635,9636],{"class":2119},"#[derive(",[2113,9638,9608],{"class":2414},[2113,9640,932],{"class":2119},[2113,9642,9603],{"class":2414},[2113,9644,932],{"class":2119},[2113,9646,9647],{"class":2414},"Debug",[2113,9649,9650],{"class":2119},")]\n",[2113,9652,9653,9655,9658],{"class":2115,"line":2246},[2113,9654,2641],{"class":2326},[2113,9656,9657],{"class":2414}," Stats",[2113,9659,2647],{"class":2119},[2113,9661,9662,9665,9667,9669],{"class":2115,"line":2464},[2113,9663,9664],{"class":2330},"    owner_id",[2113,9666,4429],{"class":2119},[2113,9668,6101],{"class":2414},[2113,9670,4706],{"class":2119},[2113,9672,9673,9676,9678,9680],{"class":2115,"line":2085},[2113,9674,9675],{"class":2330},"    group_id",[2113,9677,4429],{"class":2119},[2113,9679,6101],{"class":2414},[2113,9681,4706],{"class":2119},[2113,9683,9684,9686,9688,9690],{"class":2115,"line":2514},[2113,9685,8165],{"class":2330},[2113,9687,4429],{"class":2119},[2113,9689,6101],{"class":2414},[2113,9691,4706],{"class":2119},[2113,9693,9694,9697,9699,9701],{"class":2115,"line":2533},[2113,9695,9696],{"class":2330},"    compressed_size",[2113,9698,4429],{"class":2119},[2113,9700,6101],{"class":2414},[2113,9702,4706],{"class":2119},[2113,9704,9705,9708,9710,9712],{"class":2115,"line":2556},[2113,9706,9707],{"class":2330},"    last_read",[2113,9709,4429],{"class":2119},[2113,9711,6101],{"class":2414},[2113,9713,4706],{"class":2119},[2113,9715,9716,9719,9721,9723],{"class":2115,"line":2569},[2113,9717,9718],{"class":2330},"    last_modified",[2113,9720,4429],{"class":2119},[2113,9722,6101],{"class":2414},[2113,9724,4706],{"class":2119},[2113,9726,9727,9729,9731,9733],{"class":2115,"line":2575},[2113,9728,8304],{"class":2330},[2113,9730,4429],{"class":2119},[2113,9732,6101],{"class":2414},[2113,9734,4706],{"class":2119},[2113,9736,9737,9739,9741,9743],{"class":2115,"line":2596},[2113,9738,8335],{"class":2330},[2113,9740,4429],{"class":2119},[2113,9742,6101],{"class":2414},[2113,9744,4706],{"class":2119},[2113,9746,9747,9749,9751,9753],{"class":2115,"line":3192},[2113,9748,8366],{"class":2330},[2113,9750,4429],{"class":2119},[2113,9752,6101],{"class":2414},[2113,9754,4706],{"class":2119},[2113,9756,9757,9759,9761,9763],{"class":2115,"line":3213},[2113,9758,8397],{"class":2330},[2113,9760,4429],{"class":2119},[2113,9762,6101],{"class":2414},[2113,9764,4706],{"class":2119},[2113,9766,9767,9769,9771,9773],{"class":2115,"line":3236},[2113,9768,8428],{"class":2330},[2113,9770,4429],{"class":2119},[2113,9772,6101],{"class":2414},[2113,9774,4706],{"class":2119},[2113,9776,9777,9779,9781,9783],{"class":2115,"line":3248},[2113,9778,8459],{"class":2330},[2113,9780,4429],{"class":2119},[2113,9782,6101],{"class":2414},[2113,9784,4706],{"class":2119},[2113,9786,9787],{"class":2115,"line":4899},[2113,9788,2599],{"class":2119},[2113,9790,9791],{"class":2115,"line":1777},[2113,9792,2125],{"emptyLinePlaceholder":1767},[2113,9794,9795,9797,9799,9801,9803,9805,9807],{"class":2115,"line":4931},[2113,9796,9636],{"class":2119},[2113,9798,9608],{"class":2414},[2113,9800,932],{"class":2119},[2113,9802,9603],{"class":2414},[2113,9804,932],{"class":2119},[2113,9806,9647],{"class":2414},[2113,9808,9650],{"class":2119},[2113,9810,9811,9813,9816],{"class":2115,"line":4961},[2113,9812,2641],{"class":2326},[2113,9814,9815],{"class":2414}," Sha256",[2113,9817,2647],{"class":2119},[2113,9819,9820,9823,9825,9827,9829,9831],{"class":2115,"line":4976},[2113,9821,9822],{"class":2330},"    data",[2113,9824,6155],{"class":2119},[2113,9826,4480],{"class":2414},[2113,9828,6160],{"class":2119},[2113,9830,8504],{"class":2166},[2113,9832,7173],{"class":2119},[2113,9834,9835],{"class":2115,"line":4993},[2113,9836,2599],{"class":2119},[2113,9838,9839],{"class":2115,"line":5013},[2113,9840,2125],{"emptyLinePlaceholder":1767},[2113,9842,9843,9845,9847,9849,9851,9853,9855],{"class":2115,"line":5018},[2113,9844,9636],{"class":2119},[2113,9846,9608],{"class":2414},[2113,9848,932],{"class":2119},[2113,9850,9603],{"class":2414},[2113,9852,932],{"class":2119},[2113,9854,9647],{"class":2414},[2113,9856,9650],{"class":2119},[2113,9858,9859,9861,9864],{"class":2115,"line":5042},[2113,9860,2641],{"class":2326},[2113,9862,9863],{"class":2414}," TestObject",[2113,9865,2647],{"class":2119},[2113,9867,9868,9871,9873,9876],{"class":2115,"line":5057},[2113,9869,9870],{"class":2119},"    #[serde(with ",[2113,9872,2335],{"class":2334},[2113,9874,9875],{"class":2149}," \"serde_bytes\"",[2113,9877,9650],{"class":2119},[2113,9879,9880,9882,9884,9886,9888,9890],{"class":2115,"line":5062},[2113,9881,7559],{"class":2330},[2113,9883,4429],{"class":2119},[2113,9885,4733],{"class":2414},[2113,9887,3109],{"class":2119},[2113,9889,4480],{"class":2414},[2113,9891,9892],{"class":2119},">,\n",[2113,9894,9895,9898,9900,9903],{"class":2115,"line":5098},[2113,9896,9897],{"class":2330},"    stats",[2113,9899,4429],{"class":2119},[2113,9901,9902],{"class":2414},"Stats",[2113,9904,4706],{"class":2119},[2113,9906,9907],{"class":2115,"line":5117},[2113,9908,2125],{"emptyLinePlaceholder":1767},[2113,9910,9911,9914,9916,9918,9920,9922],{"class":2115,"line":5142},[2113,9912,9913],{"class":2330},"    chunks",[2113,9915,4429],{"class":2119},[2113,9917,4733],{"class":2414},[2113,9919,3109],{"class":2119},[2113,9921,9536],{"class":2414},[2113,9923,9892],{"class":2119},[2113,9925,9926,9929,9931,9933],{"class":2115,"line":5148},[2113,9927,9928],{"class":2330},"    sha256",[2113,9930,4429],{"class":2119},[2113,9932,9536],{"class":2414},[2113,9934,4706],{"class":2119},[2113,9936,9937],{"class":2115,"line":5163},[2113,9938,2599],{"class":2119},[2113,9940,9941],{"class":2115,"line":5169},[2113,9942,2125],{"emptyLinePlaceholder":1767},[2113,9944,9945,9947,9950,9952,9955,9957,9959,9962,9964,9966],{"class":2115,"line":5175},[2113,9946,3906],{"class":2326},[2113,9948,9949],{"class":2133}," generate_random_number",[2113,9951,2423],{"class":2119},[2113,9953,9954],{"class":2330},"rng",[2113,9956,4472],{"class":2119},[2113,9958,4030],{"class":2326},[2113,9960,9961],{"class":2414}," ThreadRng",[2113,9963,4790],{"class":2119},[2113,9965,6101],{"class":2414},[2113,9967,2647],{"class":2119},[2113,9969,9970,9973,9975,9978],{"class":2115,"line":5180},[2113,9971,9972],{"class":2330},"    rng",[2113,9974,179],{"class":2119},[2113,9976,9977],{"class":2133},"gen",[2113,9979,6745],{"class":2119},[2113,9981,9982],{"class":2115,"line":5199},[2113,9983,2599],{"class":2119},[2113,9985,9986],{"class":2115,"line":5204},[2113,9987,2125],{"emptyLinePlaceholder":1767},[2113,9989,9990,9992,9995,9997,9999,10001,10003,10005,10007,10009,10011,10013,10015,10017,10019,10021],{"class":2115,"line":5209},[2113,9991,3906],{"class":2326},[2113,9993,9994],{"class":2133}," generate_random_vect",[2113,9996,2423],{"class":2119},[2113,9998,9954],{"class":2330},[2113,10000,4472],{"class":2119},[2113,10002,4030],{"class":2326},[2113,10004,9961],{"class":2414},[2113,10006,932],{"class":2119},[2113,10008,7873],{"class":2330},[2113,10010,4429],{"class":2119},[2113,10012,4496],{"class":2414},[2113,10014,4790],{"class":2119},[2113,10016,4733],{"class":2414},[2113,10018,3109],{"class":2119},[2113,10020,4480],{"class":2414},[2113,10022,4449],{"class":2119},[2113,10024,10025,10027,10029,10032,10034,10036,10038,10040,10042,10044,10046,10048,10051,10053,10055],{"class":2115,"line":5232},[2113,10026,3917],{"class":2326},[2113,10028,3975],{"class":2326},[2113,10030,10031],{"class":2330}," vect",[2113,10033,4429],{"class":2119},[2113,10035,4733],{"class":2414},[2113,10037,3109],{"class":2119},[2113,10039,4480],{"class":2414},[2113,10041,4435],{"class":2119},[2113,10043,2335],{"class":2334},[2113,10045,4008],{"class":2414},[2113,10047,2418],{"class":2119},[2113,10049,10050],{"class":2133},"with_capacity",[2113,10052,2423],{"class":2119},[2113,10054,7873],{"class":2330},[2113,10056,2553],{"class":2119},[2113,10058,10059,10062,10065,10068,10070,10073,10075],{"class":2115,"line":5237},[2113,10060,10061],{"class":2326},"    for",[2113,10063,10064],{"class":2330}," _",[2113,10066,10067],{"class":2326}," in",[2113,10069,3208],{"class":2166},[2113,10071,10072],{"class":2119},"..",[2113,10074,7873],{"class":2330},[2113,10076,2647],{"class":2119},[2113,10078,10079,10082,10084,10087,10089,10091,10093,10095],{"class":2115,"line":5242},[2113,10080,10081],{"class":2330},"        vect",[2113,10083,179],{"class":2119},[2113,10085,10086],{"class":2133},"push",[2113,10088,2423],{"class":2119},[2113,10090,9954],{"class":2330},[2113,10092,179],{"class":2119},[2113,10094,9977],{"class":2326},[2113,10096,2593],{"class":2119},[2113,10098,10099],{"class":2115,"line":5267},[2113,10100,4665],{"class":2119},[2113,10102,10103],{"class":2115,"line":5282},[2113,10104,10105],{"class":2330},"    vect\n",[2113,10107,10108],{"class":2115,"line":5295},[2113,10109,2599],{"class":2119},[2113,10111,10112],{"class":2115,"line":5310},[2113,10113,2125],{"emptyLinePlaceholder":1767},[2113,10115,10116,10118,10121,10123,10125,10127,10129,10131,10133,10135],{"class":2115,"line":5315},[2113,10117,3906],{"class":2326},[2113,10119,10120],{"class":2133}," generate_random_sha256",[2113,10122,2423],{"class":2119},[2113,10124,9954],{"class":2330},[2113,10126,4472],{"class":2119},[2113,10128,4030],{"class":2326},[2113,10130,9961],{"class":2414},[2113,10132,4790],{"class":2119},[2113,10134,9536],{"class":2414},[2113,10136,2647],{"class":2119},[2113,10138,10139,10141,10143,10146,10148,10150,10152,10155,10157,10159,10161,10163],{"class":2115,"line":5320},[2113,10140,3917],{"class":2326},[2113,10142,3975],{"class":2326},[2113,10144,10145],{"class":2330}," sha256",[2113,10147,2153],{"class":2334},[2113,10149,9815],{"class":2414},[2113,10151,7616],{"class":2119},[2113,10153,10154],{"class":2330},"data",[2113,10156,6155],{"class":2119},[2113,10158,3095],{"class":2166},[2113,10160,6160],{"class":2119},[2113,10162,8504],{"class":2166},[2113,10164,10165],{"class":2119},"] };\n",[2113,10167,10168,10170,10172,10174,10176,10178,10180],{"class":2115,"line":8625},[2113,10169,10061],{"class":2326},[2113,10171,7910],{"class":2330},[2113,10173,10067],{"class":2326},[2113,10175,3208],{"class":2166},[2113,10177,10072],{"class":2119},[2113,10179,8504],{"class":2166},[2113,10181,2647],{"class":2119},[2113,10183,10184,10187,10190,10192,10194,10196,10199,10201,10203],{"class":2115,"line":8631},[2113,10185,10186],{"class":2330},"        sha256",[2113,10188,10189],{"class":2119},".data[",[2113,10191,7919],{"class":2330},[2113,10193,4129],{"class":2119},[2113,10195,2335],{"class":2334},[2113,10197,10198],{"class":2330}," rng",[2113,10200,179],{"class":2119},[2113,10202,9977],{"class":2133},[2113,10204,3944],{"class":2119},[2113,10206,10207],{"class":2115,"line":8637},[2113,10208,4665],{"class":2119},[2113,10210,10211],{"class":2115,"line":8652},[2113,10212,10213],{"class":2330},"    sha256\n",[2113,10215,10216],{"class":2115,"line":8674},[2113,10217,2599],{"class":2119},[2113,10219,10220],{"class":2115,"line":8706},[2113,10221,2125],{"emptyLinePlaceholder":1767},[2113,10223,10224,10226,10228],{"class":2115,"line":8724},[2113,10225,3906],{"class":2326},[2113,10227,3909],{"class":2133},[2113,10229,3912],{"class":2119},[2113,10231,10232,10234,10237,10239,10242,10244,10246],{"class":2115,"line":8729},[2113,10233,3917],{"class":2326},[2113,10235,10236],{"class":2330}," formatter",[2113,10238,2153],{"class":2334},[2113,10240,10241],{"class":2133}," make_format",[2113,10243,2423],{"class":2119},[2113,10245,9554],{"class":2166},[2113,10247,2553],{"class":2119},[2113,10249,10250,10252,10254,10256,10258,10260,10262,10265],{"class":2115,"line":8734},[2113,10251,3917],{"class":2326},[2113,10253,3975],{"class":2326},[2113,10255,10198],{"class":2330},[2113,10257,2153],{"class":2334},[2113,10259,9573],{"class":2414},[2113,10261,2418],{"class":2119},[2113,10263,10264],{"class":2133},"thread_rng",[2113,10266,3944],{"class":2119},[2113,10268,10269],{"class":2115,"line":8740},[2113,10270,10271],{"class":2396},"    \u002F\u002F Get the memory consumption of the current process\n",[2113,10273,10274,10276,10279,10281,10284,10286,10289,10291,10293],{"class":2115,"line":8772},[2113,10275,3917],{"class":2326},[2113,10277,10278],{"class":2330}," memory",[2113,10280,2153],{"class":2334},[2113,10282,10283],{"class":2414}," pid",[2113,10285,2418],{"class":2119},[2113,10287,10288],{"class":2133},"statm_self",[2113,10290,5224],{"class":2119},[2113,10292,3941],{"class":2133},[2113,10294,3944],{"class":2119},[2113,10296,10297],{"class":2115,"line":8778},[2113,10298,10299],{"class":2396},"    \u002F\u002F Get the current time in ms\n",[2113,10301,10302,10304,10307,10309,10311,10313,10316,10318,10321,10323,10325],{"class":2115,"line":8789},[2113,10303,3917],{"class":2326},[2113,10305,10306],{"class":2330}," now",[2113,10308,2153],{"class":2334},[2113,10310,3885],{"class":2414},[2113,10312,2418],{"class":2119},[2113,10314,10315],{"class":2414},"time",[2113,10317,2418],{"class":2119},[2113,10319,10320],{"class":2414},"SystemTime",[2113,10322,2418],{"class":2119},[2113,10324,8616],{"class":2133},[2113,10326,6745],{"class":2119},[2113,10328,10329,10332,10335,10337,10339,10341,10343,10345,10348],{"class":2115,"line":8795},[2113,10330,10331],{"class":2119},"        .",[2113,10333,10334],{"class":2133},"duration_since",[2113,10336,2423],{"class":2119},[2113,10338,6833],{"class":2414},[2113,10340,2418],{"class":2119},[2113,10342,10315],{"class":2414},[2113,10344,2418],{"class":2119},[2113,10346,10347],{"class":2166},"UNIX_EPOCH",[2113,10349,4660],{"class":2119},[2113,10351,10352,10354,10356],{"class":2115,"line":8817},[2113,10353,10331],{"class":2119},[2113,10355,3941],{"class":2133},[2113,10357,6745],{"class":2119},[2113,10359,10360,10362,10365],{"class":2115,"line":8822},[2113,10361,10331],{"class":2119},[2113,10363,10364],{"class":2133},"as_millis",[2113,10366,3944],{"class":2119},[2113,10368,10369],{"class":2115,"line":8857},[2113,10370,2125],{"emptyLinePlaceholder":1767},[2113,10372,10373],{"class":2115,"line":8896},[2113,10374,10375],{"class":2396},"    \u002F\u002F Create a vector with 1_300_000 elements of type TestObject\n",[2113,10377,10378,10380,10383,10385,10387],{"class":2115,"line":8901},[2113,10379,3917],{"class":2326},[2113,10381,10382],{"class":2330}," nb_object",[2113,10384,2153],{"class":2334},[2113,10386,8647],{"class":2166},[2113,10388,2487],{"class":2119},[2113,10390,10391,10393,10395,10398,10400,10402,10404,10407,10409,10411,10413,10415,10417,10419,10422],{"class":2115,"line":8907},[2113,10392,3917],{"class":2326},[2113,10394,3975],{"class":2326},[2113,10396,10397],{"class":2330}," test_objects",[2113,10399,4429],{"class":2119},[2113,10401,4733],{"class":2414},[2113,10403,3109],{"class":2119},[2113,10405,10406],{"class":2414},"TestObject",[2113,10408,4435],{"class":2119},[2113,10410,2335],{"class":2334},[2113,10412,4008],{"class":2414},[2113,10414,2418],{"class":2119},[2113,10416,10050],{"class":2133},[2113,10418,2423],{"class":2119},[2113,10420,10421],{"class":2330},"nb_object",[2113,10423,2553],{"class":2119},[2113,10425,10426,10428,10430,10432,10434,10436,10438],{"class":2115,"line":8913},[2113,10427,10061],{"class":2326},[2113,10429,10064],{"class":2330},[2113,10431,10067],{"class":2326},[2113,10433,3208],{"class":2166},[2113,10435,10072],{"class":2119},[2113,10437,10421],{"class":2330},[2113,10439,2647],{"class":2119},[2113,10441,10442,10445,10447,10449,10451,10453],{"class":2115,"line":8931},[2113,10443,10444],{"class":2330},"        test_objects",[2113,10446,179],{"class":2119},[2113,10448,10086],{"class":2133},[2113,10450,2423],{"class":2119},[2113,10452,10406],{"class":2414},[2113,10454,2647],{"class":2119},[2113,10456,10457,10460,10462,10465,10467,10469,10471,10473,10475],{"class":2115,"line":8936},[2113,10458,10459],{"class":2330},"            path",[2113,10461,4429],{"class":2119},[2113,10463,10464],{"class":2133},"generate_random_vect",[2113,10466,4027],{"class":2119},[2113,10468,4030],{"class":2326},[2113,10470,10198],{"class":2330},[2113,10472,932],{"class":2119},[2113,10474,8081],{"class":2166},[2113,10476,7142],{"class":2119},[2113,10478,10479,10482,10484,10486],{"class":2115,"line":8942},[2113,10480,10481],{"class":2330},"            stats",[2113,10483,4429],{"class":2119},[2113,10485,9902],{"class":2414},[2113,10487,2647],{"class":2119},[2113,10489,10490,10493,10495,10498,10500,10502,10504],{"class":2115,"line":8950},[2113,10491,10492],{"class":2330},"                owner_id",[2113,10494,4429],{"class":2119},[2113,10496,10497],{"class":2133},"generate_random_number",[2113,10499,4027],{"class":2119},[2113,10501,4030],{"class":2326},[2113,10503,10198],{"class":2330},[2113,10505,7142],{"class":2119},[2113,10507,10508,10511,10513,10515,10517,10519,10521],{"class":2115,"line":8968},[2113,10509,10510],{"class":2330},"                group_id",[2113,10512,4429],{"class":2119},[2113,10514,10497],{"class":2133},[2113,10516,4027],{"class":2119},[2113,10518,4030],{"class":2326},[2113,10520,10198],{"class":2330},[2113,10522,7142],{"class":2119},[2113,10524,10525,10528,10530,10532,10534,10536,10538],{"class":2115,"line":8983},[2113,10526,10527],{"class":2330},"                size",[2113,10529,4429],{"class":2119},[2113,10531,10497],{"class":2133},[2113,10533,4027],{"class":2119},[2113,10535,4030],{"class":2326},[2113,10537,10198],{"class":2330},[2113,10539,7142],{"class":2119},[2113,10541,10542,10545,10547,10549,10551,10553,10555],{"class":2115,"line":8988},[2113,10543,10544],{"class":2330},"                compressed_size",[2113,10546,4429],{"class":2119},[2113,10548,10497],{"class":2133},[2113,10550,4027],{"class":2119},[2113,10552,4030],{"class":2326},[2113,10554,10198],{"class":2330},[2113,10556,7142],{"class":2119},[2113,10558,10559,10562,10564,10566,10568,10570,10572],{"class":2115,"line":9011},[2113,10560,10561],{"class":2330},"                last_read",[2113,10563,4429],{"class":2119},[2113,10565,10497],{"class":2133},[2113,10567,4027],{"class":2119},[2113,10569,4030],{"class":2326},[2113,10571,10198],{"class":2330},[2113,10573,7142],{"class":2119},[2113,10575,10576,10579,10581,10583,10585,10587,10589],{"class":2115,"line":9016},[2113,10577,10578],{"class":2330},"                last_modified",[2113,10580,4429],{"class":2119},[2113,10582,10497],{"class":2133},[2113,10584,4027],{"class":2119},[2113,10586,4030],{"class":2326},[2113,10588,10198],{"class":2330},[2113,10590,7142],{"class":2119},[2113,10592,10593,10596,10598,10600,10602,10604,10606],{"class":2115,"line":9022},[2113,10594,10595],{"class":2330},"                created",[2113,10597,4429],{"class":2119},[2113,10599,10497],{"class":2133},[2113,10601,4027],{"class":2119},[2113,10603,4030],{"class":2326},[2113,10605,10198],{"class":2330},[2113,10607,7142],{"class":2119},[2113,10609,10610,10613,10615,10617,10619,10621,10623],{"class":2115,"line":9045},[2113,10611,10612],{"class":2330},"                mode",[2113,10614,4429],{"class":2119},[2113,10616,10497],{"class":2133},[2113,10618,4027],{"class":2119},[2113,10620,4030],{"class":2326},[2113,10622,10198],{"class":2330},[2113,10624,7142],{"class":2119},[2113,10626,10627,10630,10632,10634,10636,10638,10640],{"class":2115,"line":9076},[2113,10628,10629],{"class":2330},"                dev",[2113,10631,4429],{"class":2119},[2113,10633,10497],{"class":2133},[2113,10635,4027],{"class":2119},[2113,10637,4030],{"class":2326},[2113,10639,10198],{"class":2330},[2113,10641,7142],{"class":2119},[2113,10643,10644,10647,10649,10651,10653,10655,10657],{"class":2115,"line":9082},[2113,10645,10646],{"class":2330},"                rdev",[2113,10648,4429],{"class":2119},[2113,10650,10497],{"class":2133},[2113,10652,4027],{"class":2119},[2113,10654,4030],{"class":2326},[2113,10656,10198],{"class":2330},[2113,10658,7142],{"class":2119},[2113,10660,10661,10664,10666,10668,10670,10672,10674],{"class":2115,"line":9105},[2113,10662,10663],{"class":2330},"                ino",[2113,10665,4429],{"class":2119},[2113,10667,10497],{"class":2133},[2113,10669,4027],{"class":2119},[2113,10671,4030],{"class":2326},[2113,10673,10198],{"class":2330},[2113,10675,7142],{"class":2119},[2113,10677,10678,10681,10683,10685,10687,10689,10691],{"class":2115,"line":9138},[2113,10679,10680],{"class":2330},"                nlink",[2113,10682,4429],{"class":2119},[2113,10684,10497],{"class":2133},[2113,10686,4027],{"class":2119},[2113,10688,4030],{"class":2326},[2113,10690,10198],{"class":2330},[2113,10692,7142],{"class":2119},[2113,10694,10695],{"class":2115,"line":9149},[2113,10696,10697],{"class":2119},"            },\n",[2113,10699,10700,10703,10705,10708],{"class":2115,"line":9157},[2113,10701,10702],{"class":2330},"            chunks",[2113,10704,4429],{"class":2119},[2113,10706,10707],{"class":2133},"vec!",[2113,10709,10710],{"class":2119},"[\n",[2113,10712,10713,10716,10718,10720,10722],{"class":2115,"line":9182},[2113,10714,10715],{"class":2133},"                generate_random_sha256",[2113,10717,4027],{"class":2119},[2113,10719,4030],{"class":2326},[2113,10721,10198],{"class":2330},[2113,10723,7142],{"class":2119},[2113,10725,10726,10728,10730,10732,10734],{"class":2115,"line":9188},[2113,10727,10715],{"class":2133},[2113,10729,4027],{"class":2119},[2113,10731,4030],{"class":2326},[2113,10733,10198],{"class":2330},[2113,10735,7142],{"class":2119},[2113,10737,10738,10740,10742,10744,10746],{"class":2115,"line":9193},[2113,10739,10715],{"class":2133},[2113,10741,4027],{"class":2119},[2113,10743,4030],{"class":2326},[2113,10745,10198],{"class":2330},[2113,10747,7142],{"class":2119},[2113,10749,10750],{"class":2115,"line":9198},[2113,10751,10752],{"class":2119},"            ],\n",[2113,10754,10755,10758,10760,10763,10765,10767,10769],{"class":2115,"line":9216},[2113,10756,10757],{"class":2330},"            sha256",[2113,10759,4429],{"class":2119},[2113,10761,10762],{"class":2133},"generate_random_sha256",[2113,10764,4027],{"class":2119},[2113,10766,4030],{"class":2326},[2113,10768,10198],{"class":2330},[2113,10770,7142],{"class":2119},[2113,10772,10773],{"class":2115,"line":9238},[2113,10774,10775],{"class":2119},"        });\n",[2113,10777,10778],{"class":2115,"line":9243},[2113,10779,4665],{"class":2119},[2113,10781,10783],{"class":2115,"line":10782},97,[2113,10784,2125],{"emptyLinePlaceholder":1767},[2113,10786,10788,10790,10793,10795,10797,10799,10801,10803,10805,10807,10809],{"class":2115,"line":10787},98,[2113,10789,3917],{"class":2326},[2113,10791,10792],{"class":2330}," now_after_creation",[2113,10794,2153],{"class":2334},[2113,10796,3885],{"class":2414},[2113,10798,2418],{"class":2119},[2113,10800,10315],{"class":2414},[2113,10802,2418],{"class":2119},[2113,10804,10320],{"class":2414},[2113,10806,2418],{"class":2119},[2113,10808,8616],{"class":2133},[2113,10810,6745],{"class":2119},[2113,10812,10814,10816,10818,10820,10822,10824,10826,10828,10830],{"class":2115,"line":10813},99,[2113,10815,10331],{"class":2119},[2113,10817,10334],{"class":2133},[2113,10819,2423],{"class":2119},[2113,10821,6833],{"class":2414},[2113,10823,2418],{"class":2119},[2113,10825,10315],{"class":2414},[2113,10827,2418],{"class":2119},[2113,10829,10347],{"class":2166},[2113,10831,4660],{"class":2119},[2113,10833,10835,10837,10839],{"class":2115,"line":10834},100,[2113,10836,10331],{"class":2119},[2113,10838,3941],{"class":2133},[2113,10840,6745],{"class":2119},[2113,10842,10844,10846,10848],{"class":2115,"line":10843},101,[2113,10845,10331],{"class":2119},[2113,10847,10364],{"class":2133},[2113,10849,3944],{"class":2119},[2113,10851,10853],{"class":2115,"line":10852},102,[2113,10854,10855],{"class":2396},"    \u002F\u002F Calculate the creation time\n",[2113,10857,10859,10861,10863,10866,10868,10871,10874,10876],{"class":2115,"line":10858},103,[2113,10860,4043],{"class":2133},[2113,10862,2423],{"class":2119},[2113,10864,10865],{"class":2149},"\"Creation time: {} ms\"",[2113,10867,932],{"class":2119},[2113,10869,10870],{"class":2330},"now_after_creation",[2113,10872,10873],{"class":2119}," - ",[2113,10875,8616],{"class":2330},[2113,10877,2553],{"class":2119},[2113,10879,10881],{"class":2115,"line":10880},104,[2113,10882,2125],{"emptyLinePlaceholder":1767},[2113,10884,10886],{"class":2115,"line":10885},105,[2113,10887,10271],{"class":2396},[2113,10889,10891,10893,10896,10898,10900,10902,10904,10906,10908],{"class":2115,"line":10890},106,[2113,10892,3917],{"class":2326},[2113,10894,10895],{"class":2330}," memory_after_creation",[2113,10897,2153],{"class":2334},[2113,10899,10283],{"class":2414},[2113,10901,2418],{"class":2119},[2113,10903,10288],{"class":2133},[2113,10905,5224],{"class":2119},[2113,10907,3941],{"class":2133},[2113,10909,3944],{"class":2119},[2113,10911,10913],{"class":2115,"line":10912},107,[2113,10914,10915],{"class":2396},"    \u002F\u002F Show memory consumption\n",[2113,10917,10919,10921],{"class":2115,"line":10918},108,[2113,10920,4043],{"class":2133},[2113,10922,3146],{"class":2119},[2113,10924,10926,10929],{"class":2115,"line":10925},109,[2113,10927,10928],{"class":2149},"        \"Memory consumption after creation: {}\"",[2113,10930,4706],{"class":2119},[2113,10932,10934,10937,10939,10942,10945,10948,10951,10954],{"class":2115,"line":10933},110,[2113,10935,10936],{"class":2133},"        formatter",[2113,10938,8879],{"class":2119},[2113,10940,10941],{"class":2330},"memory_after_creation",[2113,10943,10944],{"class":2119},".size - ",[2113,10946,10947],{"class":2330},"memory",[2113,10949,10950],{"class":2119},".size) * ",[2113,10952,10953],{"class":2166},"4096",[2113,10955,4660],{"class":2119},[2113,10957,10959],{"class":2115,"line":10958},111,[2113,10960,10961],{"class":2119},"    );\n",[2113,10963,10965],{"class":2115,"line":10964},112,[2113,10966,10967],{"class":2396},"    \u002F\u002F Show average memory consumption per object\n",[2113,10969,10971,10973],{"class":2115,"line":10970},113,[2113,10972,4043],{"class":2133},[2113,10974,3146],{"class":2119},[2113,10976,10978,10981],{"class":2115,"line":10977},114,[2113,10979,10980],{"class":2149},"        \"Average memory consumption per object: {}\"",[2113,10982,4706],{"class":2119},[2113,10984,10986,10988,10991,10993,10995,10997,10999,11001,11004,11006],{"class":2115,"line":10985},115,[2113,10987,10936],{"class":2133},[2113,10989,10990],{"class":2119},"(((",[2113,10992,10941],{"class":2330},[2113,10994,10944],{"class":2119},[2113,10996,10947],{"class":2330},[2113,10998,10950],{"class":2119},[2113,11000,10953],{"class":2166},[2113,11002,11003],{"class":2119},") \u002F ",[2113,11005,10421],{"class":2330},[2113,11007,4660],{"class":2119},[2113,11009,11011],{"class":2115,"line":11010},116,[2113,11012,10961],{"class":2119},[2113,11014,11016],{"class":2115,"line":11015},117,[2113,11017,2125],{"emptyLinePlaceholder":1767},[2113,11019,11021],{"class":2115,"line":11020},118,[2113,11022,11023],{"class":2396},"    \u002F\u002F Remove the file test.bin if it exists\n",[2113,11025,11027,11029,11031,11033,11035,11037,11040,11042,11045],{"class":2115,"line":11026},119,[2113,11028,6581],{"class":2326},[2113,11030,3885],{"class":2414},[2113,11032,2418],{"class":2119},[2113,11034,3890],{"class":2414},[2113,11036,2418],{"class":2119},[2113,11038,11039],{"class":2133},"remove_file",[2113,11041,2423],{"class":2119},[2113,11043,11044],{"class":2149},"\"test.bin\"",[2113,11046,2433],{"class":2119},[2113,11048,11050,11052,11054,11057],{"class":2115,"line":11049},120,[2113,11051,4653],{"class":2414},[2113,11053,2423],{"class":2119},[2113,11055,11056],{"class":2330},"_",[2113,11058,11059],{"class":2119},") => {}\n",[2113,11061,11063,11066,11068,11070],{"class":2115,"line":11062},121,[2113,11064,11065],{"class":2414},"        Err",[2113,11067,2423],{"class":2119},[2113,11069,11056],{"class":2330},[2113,11071,11059],{"class":2119},[2113,11073,11075],{"class":2115,"line":11074},122,[2113,11076,4665],{"class":2119},[2113,11078,11080],{"class":2115,"line":11079},123,[2113,11081,2125],{"emptyLinePlaceholder":1767},[2113,11083,11085],{"class":2115,"line":11084},124,[2113,11086,11087],{"class":2396},"    \u002F\u002F Serialize all the objects in a file\n",[2113,11089,11091,11093,11095,11097,11099,11101,11103,11105,11107,11109,11112,11114,11116,11118,11120],{"class":2115,"line":11090},125,[2113,11092,3917],{"class":2326},[2113,11094,7379],{"class":2330},[2113,11096,2153],{"class":2334},[2113,11098,3885],{"class":2414},[2113,11100,2418],{"class":2119},[2113,11102,3890],{"class":2414},[2113,11104,2418],{"class":2119},[2113,11106,3895],{"class":2414},[2113,11108,2418],{"class":2119},[2113,11110,11111],{"class":2133},"create",[2113,11113,2423],{"class":2119},[2113,11115,11044],{"class":2149},[2113,11117,3938],{"class":2119},[2113,11119,3941],{"class":2133},[2113,11121,3944],{"class":2119},[2113,11123,11125,11127,11129,11132,11134,11137,11139,11141,11143,11146],{"class":2115,"line":11124},126,[2113,11126,3917],{"class":2326},[2113,11128,3975],{"class":2326},[2113,11130,11131],{"class":2330}," buffile",[2113,11133,2153],{"class":2334},[2113,11135,11136],{"class":2414}," BufWriter",[2113,11138,2418],{"class":2119},[2113,11140,3961],{"class":2133},[2113,11142,2423],{"class":2119},[2113,11144,11145],{"class":2330},"file",[2113,11147,2553],{"class":2119},[2113,11149,11151,11153,11156,11158,11160,11162,11165],{"class":2115,"line":11150},127,[2113,11152,10061],{"class":2326},[2113,11154,11155],{"class":2330}," test_object",[2113,11157,10067],{"class":2326},[2113,11159,10397],{"class":2330},[2113,11161,179],{"class":2119},[2113,11163,11164],{"class":2133},"iter",[2113,11166,3912],{"class":2119},[2113,11168,11170,11173,11175,11178,11180,11182,11184,11186,11189,11191,11193],{"class":2115,"line":11169},128,[2113,11171,11172],{"class":2414},"        bincode",[2113,11174,2418],{"class":2119},[2113,11176,11177],{"class":2133},"serialize_into",[2113,11179,4027],{"class":2119},[2113,11181,4030],{"class":2326},[2113,11183,11131],{"class":2330},[2113,11185,932],{"class":2119},[2113,11187,11188],{"class":2330},"test_object",[2113,11190,3938],{"class":2119},[2113,11192,3941],{"class":2133},[2113,11194,3944],{"class":2119},[2113,11196,11198],{"class":2115,"line":11197},129,[2113,11199,4665],{"class":2119},[2113,11201,11203],{"class":2115,"line":11202},130,[2113,11204,2125],{"emptyLinePlaceholder":1767},[2113,11206,11208],{"class":2115,"line":11207},131,[2113,11209,11210],{"class":2396},"    \u002F\u002F Show time to write\n",[2113,11212,11214,11216,11219,11221,11223,11225,11227,11229,11231,11233,11235],{"class":2115,"line":11213},132,[2113,11215,3917],{"class":2326},[2113,11217,11218],{"class":2330}," now_after_serialization",[2113,11220,2153],{"class":2334},[2113,11222,3885],{"class":2414},[2113,11224,2418],{"class":2119},[2113,11226,10315],{"class":2414},[2113,11228,2418],{"class":2119},[2113,11230,10320],{"class":2414},[2113,11232,2418],{"class":2119},[2113,11234,8616],{"class":2133},[2113,11236,6745],{"class":2119},[2113,11238,11240,11242,11244,11246,11248,11250,11252,11254,11256],{"class":2115,"line":11239},133,[2113,11241,10331],{"class":2119},[2113,11243,10334],{"class":2133},[2113,11245,2423],{"class":2119},[2113,11247,6833],{"class":2414},[2113,11249,2418],{"class":2119},[2113,11251,10315],{"class":2414},[2113,11253,2418],{"class":2119},[2113,11255,10347],{"class":2166},[2113,11257,4660],{"class":2119},[2113,11259,11261,11263,11265],{"class":2115,"line":11260},134,[2113,11262,10331],{"class":2119},[2113,11264,3941],{"class":2133},[2113,11266,6745],{"class":2119},[2113,11268,11270,11272,11274],{"class":2115,"line":11269},135,[2113,11271,10331],{"class":2119},[2113,11273,10364],{"class":2133},[2113,11275,3944],{"class":2119},[2113,11277,11279],{"class":2115,"line":11278},136,[2113,11280,2125],{"emptyLinePlaceholder":1767},[2113,11282,11284,11286],{"class":2115,"line":11283},137,[2113,11285,4043],{"class":2133},[2113,11287,3146],{"class":2119},[2113,11289,11291,11294],{"class":2115,"line":11290},138,[2113,11292,11293],{"class":2149},"        \"Write to file time: {} ms\"",[2113,11295,4706],{"class":2119},[2113,11297,11299,11302,11304],{"class":2115,"line":11298},139,[2113,11300,11301],{"class":2330},"        now_after_serialization",[2113,11303,10873],{"class":2119},[2113,11305,11306],{"class":2330},"now_after_creation\n",[2113,11308,11310],{"class":2115,"line":11309},140,[2113,11311,10961],{"class":2119},[2113,11313,11315],{"class":2115,"line":11314},141,[2113,11316,2125],{"emptyLinePlaceholder":1767},[2113,11318,11320],{"class":2115,"line":11319},142,[2113,11321,11322],{"class":2396},"    \u002F\u002F Get the file size and the average object size in file\n",[2113,11324,11326,11328,11331,11333,11335,11337,11339,11341,11344,11346,11348,11350,11352],{"class":2115,"line":11325},143,[2113,11327,3917],{"class":2326},[2113,11329,11330],{"class":2330}," metadata",[2113,11332,2153],{"class":2334},[2113,11334,3885],{"class":2414},[2113,11336,2418],{"class":2119},[2113,11338,3890],{"class":2414},[2113,11340,2418],{"class":2119},[2113,11342,11343],{"class":2133},"metadata",[2113,11345,2423],{"class":2119},[2113,11347,11044],{"class":2149},[2113,11349,3938],{"class":2119},[2113,11351,3941],{"class":2133},[2113,11353,3944],{"class":2119},[2113,11355,11357,11359],{"class":2115,"line":11356},144,[2113,11358,4043],{"class":2133},[2113,11360,3146],{"class":2119},[2113,11362,11364,11367],{"class":2115,"line":11363},145,[2113,11365,11366],{"class":2149},"        \"Size of file on disk: {}\"",[2113,11368,4706],{"class":2119},[2113,11370,11372,11375,11377,11379,11381,11383,11385,11387],{"class":2115,"line":11371},146,[2113,11373,11374],{"class":2133},"        format_size",[2113,11376,2423],{"class":2119},[2113,11378,11343],{"class":2330},[2113,11380,179],{"class":2119},[2113,11382,4540],{"class":2133},[2113,11384,8110],{"class":2119},[2113,11386,9554],{"class":2166},[2113,11388,4660],{"class":2119},[2113,11390,11392],{"class":2115,"line":11391},147,[2113,11393,10961],{"class":2119},[2113,11395,11397,11399],{"class":2115,"line":11396},148,[2113,11398,4043],{"class":2133},[2113,11400,3146],{"class":2119},[2113,11402,11404,11407],{"class":2115,"line":11403},149,[2113,11405,11406],{"class":2149},"        \"Size of object in the file: {}\"",[2113,11408,4706],{"class":2119},[2113,11410,11412,11414,11416,11418,11420,11422,11425,11427,11430,11432,11434,11436],{"class":2115,"line":11411},150,[2113,11413,11374],{"class":2133},[2113,11415,2423],{"class":2119},[2113,11417,11343],{"class":2330},[2113,11419,179],{"class":2119},[2113,11421,4540],{"class":2133},[2113,11423,11424],{"class":2119},"() \u002F ",[2113,11426,10421],{"class":2330},[2113,11428,11429],{"class":2326}," as",[2113,11431,6245],{"class":2414},[2113,11433,932],{"class":2119},[2113,11435,9554],{"class":2166},[2113,11437,4660],{"class":2119},[2113,11439,11441],{"class":2115,"line":11440},151,[2113,11442,10961],{"class":2119},[2113,11444,11446],{"class":2115,"line":11445},152,[2113,11447,2599],{"class":2119},[12,11449,11450],{},"Ce qui donne le résultat suivant :",[22,11452,11453,11461],{},[25,11454,11455],{},[28,11456,11457,11459],{},[31,11458],{},[31,11460],{},[41,11462,11463,11469,11476,11483,11490,11497,11504],{},[28,11464,11465,11467],{},[46,11466,9275],{},[46,11468,9278],{},[28,11470,11471,11473],{},[46,11472,9283],{},[46,11474,11475],{},"2 secondes",[28,11477,11478,11480],{},[46,11479,9291],{},[46,11481,11482],{},"519,92 Mo",[28,11484,11485,11487],{},[46,11486,9299],{},[46,11488,11489],{},"399 octets",[28,11491,11492,11494],{},[46,11493,9307],{},[46,11495,11496],{},"1 seconde",[28,11498,11499,11501],{},[46,11500,9315],{},[46,11502,11503],{},"441,99 Mb",[28,11505,11506,11508],{},[46,11507,9323],{},[46,11509,11510],{},"339 octets",[12,11512,11513],{},"En comparant les résultats obtenus avec Node.js et Rust, on peut conclure que Rust est, sans contestation possible,\nbeaucoup plus performant que Node.js en termes de temps d'exécution et de consommation de mémoire pour la création de\n1,3 million d'objets. En effet, Rust a créé tous les objets en seulement 2 secondes et consommé 519,92 Mo de mémoire,\ntandis que Node.js a pris 15 secondes et consommé 2,8 Go de mémoire.",[12,11515,11516],{},"La consommation moyenne de mémoire par objet est du coup nettement plus faible avec Rust (399 octets) qu'avec\nNode.js (2,2 Ko).",[12,11518,11519],{},"En somme, cette première partie montre clairement que Rust est une alternative intéressante pour les applications\nnécessitant une manipulation intensive de données, en particulier lorsque la performance et la consommation de mémoire\nsont des critères importants.",[128,11521,11523],{"id":11522},"optimisation-de-notre-application","Optimisation de notre application",[12,11525,11526],{},"Pour optimiser notre application en TypeScript, nous allons développer un module natif en Node.js avec Rust. Pour cela,\nnous allons utiliser NAPI.RS.",[12,11528,11529],{},"N-API est une interface de programmation d'applications (API) qui permet aux modules natifs d'être facilement utilisés\ndans Node.js. Cela permet aux développeurs de créer des modules en C++ et de les utiliser dans des projets Node.js\nsans avoir à se soucier de la compatibilité entre les versions de Node.js.",[12,11531,11532],{},"N-API est une API stable et évolutive qui est maintenue par l'équipe Node.js. N-API fournit une interface de\nprogrammation d'applications indépendante du moteur JavaScript utilisé par Node.js. Cela signifie que les modules\ncompilés avec N-API fonctionnent de manière cohérente, quel que soit le moteur JavaScript utilisé par Node.js.",[12,11534,11535],{},"napi.rs est une bibliothèque Rust qui fournit une API pour écrire des modules Node.js en Rust. Elle simplifie la création\nde modules Node.js en fournissant des abstractions de niveau supérieur pour les fonctionnalités N-API. Avec napi.rs, les\ndéveloppeurs Rust peuvent facilement écrire des modules Node.js sans avoir à se soucier des détails techniques de N-API.",[12,11537,11538,11539,179],{},"Nous allons débuter le développement de la partie Rust de notre application. Je ne vais pas détailler la création d'un\nmodule Rust avec NAPI.RS. La documentation est assez bien faite pour cette partie : ",[49,11540,11541],{"href":11541,"rel":11542},"https:\u002F\u002Fnapi.rs\u002Fdocs\u002Fintroduction\u002Fsimple-package",[347],[12,11544,11545],{},"Le module que nous allons écrire doit conserver les objets qu'il crée en mémoire. En effet, si les objets créés côté Rust\nétaient ensuite transférés dans la partie Node.js, la consommation de mémoire serait la même que si nous avions créé\nles objets directement en Node.js.",[12,11547,11548],{},"L'exemple ci-dessous ne fait pas grand-chose, mais c'est un point à prendre en considération lorsque je vais développer\nle module pour mon application. Les objets transférés à la partie JavaScript ne doivent que transiter.",[12,11550,11551],{},"Voici la partie Rust :",[2105,11553,11555],{"className":3855,"code":11554,"language":1775,"meta":1646,"style":1646},"#![deny(clippy::all)]\nuse humansize::{format_size, DECIMAL};\nuse procinfo::pid;\nuse rand::{rngs::ThreadRng, Rng};\n\n#[macro_use]\nextern crate napi_derive;\n\nstruct Stats {\n  owner_id: u64,\n  group_id: u64,\n  size: u64,\n  compressed_size: u64,\n  last_read: u64,\n  last_modified: u64,\n  created: u64,\n  mode: u64,\n  dev: u64,\n  rdev: u64,\n  ino: u64,\n  nlink: u64,\n}\n\nstruct Sha256 {\n  data: [u8; 32],\n}\n\nstruct TestObject {\n  path: Vec\u003Cu8>,\n  stats: Stats,\n\n  chunks: Vec\u003CSha256>,\n  sha256: Sha256,\n}\n\nfn generate_random_number(rng: &mut ThreadRng) -> u64 {\n  rng.gen()\n}\n\nfn generate_random_vect(rng: &mut ThreadRng, size: usize) -> Vec\u003Cu8> {\n  let mut vect: Vec\u003Cu8> = Vec::with_capacity(size);\n  for _ in 0..size {\n    vect.push(rng.gen());\n  }\n  vect\n}\n\nfn generate_random_sha256(rng: &mut ThreadRng) -> Sha256 {\n  let mut sha256 = Sha256 { data: [0; 32] };\n  for i in 0..32 {\n    sha256.data[i] = rng.gen();\n  }\n  sha256\n}\n\npub struct TestObjectWrapper {\n  test_objects: Vec\u003CTestObject>,\n}\n\n#[napi(js_name = \"TestObjectWrapper\")]\npub struct JsTestObjectWrapper {\n  test_object_wrapper: TestObjectWrapper,\n}\n\n#[napi]\nimpl JsTestObjectWrapper {\n  #[napi(constructor)]\n  pub fn new() -> Self {\n    Self {\n      test_object_wrapper: TestObjectWrapper {\n        test_objects: Vec::new(),\n      },\n    }\n  }\n\n  #[napi]\n  pub fn fill(&mut self, nb_object: i32) {\n    let mut rng = rand::thread_rng();\n    \u002F\u002F Get the memory consumption of the current process\n    let memory = pid::statm_self().unwrap();\n    \u002F\u002F Get the current time in ms\n    let now = std::time::SystemTime::now()\n      .duration_since(std::time::UNIX_EPOCH)\n      .unwrap()\n      .as_millis();\n\n    self.test_object_wrapper.test_objects = Vec::with_capacity(nb_object.try_into().unwrap());\n\n    for _ in 0..nb_object {\n      self.test_object_wrapper.test_objects.push(TestObject {\n        path: generate_random_vect(&mut rng, 100),\n        stats: Stats {\n          owner_id: generate_random_number(&mut rng),\n          group_id: generate_random_number(&mut rng),\n          size: generate_random_number(&mut rng),\n          compressed_size: generate_random_number(&mut rng),\n          last_read: generate_random_number(&mut rng),\n          last_modified: generate_random_number(&mut rng),\n          created: generate_random_number(&mut rng),\n          mode: generate_random_number(&mut rng),\n          dev: generate_random_number(&mut rng),\n          rdev: generate_random_number(&mut rng),\n          ino: generate_random_number(&mut rng),\n          nlink: generate_random_number(&mut rng),\n        },\n        chunks: vec![\n          generate_random_sha256(&mut rng),\n          generate_random_sha256(&mut rng),\n          generate_random_sha256(&mut rng),\n        ],\n        sha256: generate_random_sha256(&mut rng),\n      });\n    }\n\n    let now_after_creation = std::time::SystemTime::now()\n      .duration_since(std::time::UNIX_EPOCH)\n      .unwrap()\n      .as_millis();\n    \u002F\u002F Calculate the creation time\n    println!(\"Creation time: {} ms\", now_after_creation - now);\n\n    \u002F\u002F Get the memory consumption of the current process\n    let memory_after_creation = pid::statm_self().unwrap();\n    \u002F\u002F Show memory consumption\n    println!(\n      \"Memory consumption after creation: {}\",\n      format_size((memory_after_creation.size - memory.size) * 4096, DECIMAL)\n    );\n    \u002F\u002F Show average memory consumption per object\n    println!(\n      \"Average memory consumption per object: {}\",\n      format_size(\n        ((memory_after_creation.size - memory.size) * 4096) \u002F nb_object as usize,\n        DECIMAL\n      )\n    );\n  }\n\n  #[napi(js_name = \"toString\")]\n  pub fn to_string(&self) -> String {\n    format!(\n      \"TestObjectWrapper {{ test_objects: {} }}\",\n      self.test_object_wrapper.test_objects.len()\n    )\n  }\n}\n",[184,11556,11557,11562,11575,11583,11603,11607,11612,11623,11627,11635,11646,11657,11668,11679,11690,11701,11712,11723,11734,11745,11756,11767,11771,11775,11783,11798,11802,11806,11814,11828,11838,11842,11856,11866,11870,11874,11896,11907,11911,11915,11949,11982,11998,12017,12021,12026,12030,12034,12056,12082,12098,12118,12122,12127,12131,12135,12146,12161,12165,12169,12181,12192,12204,12208,12212,12217,12225,12230,12246,12253,12264,12278,12283,12287,12291,12295,12300,12326,12344,12348,12368,12372,12396,12417,12425,12433,12437,12468,12472,12488,12504,12525,12536,12553,12570,12587,12604,12621,12638,12655,12672,12689,12706,12723,12740,12745,12756,12769,12781,12793,12798,12814,12819,12823,12827,12851,12871,12879,12887,12891,12909,12913,12917,12937,12941,12947,12954,12977,12981,12985,12991,12998,13004,13030,13035,13040,13044,13048,13052,13064,13084,13091,13098,13108,13113,13117],{"__ignoreMap":1646},[2113,11558,11559],{"class":2115,"line":2116},[2113,11560,11561],{"class":2119},"#![deny(clippy::all)]\n",[2113,11563,11564,11566,11568,11571,11573],{"class":2115,"line":1647},[2113,11565,3863],{"class":2326},[2113,11567,9548],{"class":2414},[2113,11569,11570],{"class":2119},"::{format_size, ",[2113,11572,9554],{"class":2414},[2113,11574,2787],{"class":2119},[2113,11576,11577,11579,11581],{"class":2115,"line":1652},[2113,11578,3863],{"class":2326},[2113,11580,9563],{"class":2414},[2113,11582,9566],{"class":2119},[2113,11584,11585,11587,11589,11591,11593,11595,11597,11599,11601],{"class":2115,"line":2185},[2113,11586,3863],{"class":2326},[2113,11588,9573],{"class":2414},[2113,11590,9576],{"class":2119},[2113,11592,9579],{"class":2414},[2113,11594,2418],{"class":2119},[2113,11596,9584],{"class":2414},[2113,11598,932],{"class":2119},[2113,11600,9589],{"class":2414},[2113,11602,2787],{"class":2119},[2113,11604,11605],{"class":2115,"line":2230},[2113,11606,2125],{"emptyLinePlaceholder":1767},[2113,11608,11609],{"class":2115,"line":2235},[2113,11610,11611],{"class":2119},"#[macro_use]\n",[2113,11613,11614,11617,11620],{"class":2115,"line":2241},[2113,11615,11616],{"class":2326},"extern",[2113,11618,11619],{"class":2326}," crate",[2113,11621,11622],{"class":2119}," napi_derive;\n",[2113,11624,11625],{"class":2115,"line":2246},[2113,11626,2125],{"emptyLinePlaceholder":1767},[2113,11628,11629,11631,11633],{"class":2115,"line":2464},[2113,11630,2641],{"class":2326},[2113,11632,9657],{"class":2414},[2113,11634,2647],{"class":2119},[2113,11636,11637,11640,11642,11644],{"class":2115,"line":2085},[2113,11638,11639],{"class":2330},"  owner_id",[2113,11641,4429],{"class":2119},[2113,11643,6101],{"class":2414},[2113,11645,4706],{"class":2119},[2113,11647,11648,11651,11653,11655],{"class":2115,"line":2514},[2113,11649,11650],{"class":2330},"  group_id",[2113,11652,4429],{"class":2119},[2113,11654,6101],{"class":2414},[2113,11656,4706],{"class":2119},[2113,11658,11659,11662,11664,11666],{"class":2115,"line":2533},[2113,11660,11661],{"class":2330},"  size",[2113,11663,4429],{"class":2119},[2113,11665,6101],{"class":2414},[2113,11667,4706],{"class":2119},[2113,11669,11670,11673,11675,11677],{"class":2115,"line":2556},[2113,11671,11672],{"class":2330},"  compressed_size",[2113,11674,4429],{"class":2119},[2113,11676,6101],{"class":2414},[2113,11678,4706],{"class":2119},[2113,11680,11681,11684,11686,11688],{"class":2115,"line":2569},[2113,11682,11683],{"class":2330},"  last_read",[2113,11685,4429],{"class":2119},[2113,11687,6101],{"class":2414},[2113,11689,4706],{"class":2119},[2113,11691,11692,11695,11697,11699],{"class":2115,"line":2575},[2113,11693,11694],{"class":2330},"  last_modified",[2113,11696,4429],{"class":2119},[2113,11698,6101],{"class":2414},[2113,11700,4706],{"class":2119},[2113,11702,11703,11706,11708,11710],{"class":2115,"line":2596},[2113,11704,11705],{"class":2330},"  created",[2113,11707,4429],{"class":2119},[2113,11709,6101],{"class":2414},[2113,11711,4706],{"class":2119},[2113,11713,11714,11717,11719,11721],{"class":2115,"line":3192},[2113,11715,11716],{"class":2330},"  mode",[2113,11718,4429],{"class":2119},[2113,11720,6101],{"class":2414},[2113,11722,4706],{"class":2119},[2113,11724,11725,11728,11730,11732],{"class":2115,"line":3213},[2113,11726,11727],{"class":2330},"  dev",[2113,11729,4429],{"class":2119},[2113,11731,6101],{"class":2414},[2113,11733,4706],{"class":2119},[2113,11735,11736,11739,11741,11743],{"class":2115,"line":3236},[2113,11737,11738],{"class":2330},"  rdev",[2113,11740,4429],{"class":2119},[2113,11742,6101],{"class":2414},[2113,11744,4706],{"class":2119},[2113,11746,11747,11750,11752,11754],{"class":2115,"line":3248},[2113,11748,11749],{"class":2330},"  ino",[2113,11751,4429],{"class":2119},[2113,11753,6101],{"class":2414},[2113,11755,4706],{"class":2119},[2113,11757,11758,11761,11763,11765],{"class":2115,"line":4899},[2113,11759,11760],{"class":2330},"  nlink",[2113,11762,4429],{"class":2119},[2113,11764,6101],{"class":2414},[2113,11766,4706],{"class":2119},[2113,11768,11769],{"class":2115,"line":1777},[2113,11770,2599],{"class":2119},[2113,11772,11773],{"class":2115,"line":4931},[2113,11774,2125],{"emptyLinePlaceholder":1767},[2113,11776,11777,11779,11781],{"class":2115,"line":4961},[2113,11778,2641],{"class":2326},[2113,11780,9815],{"class":2414},[2113,11782,2647],{"class":2119},[2113,11784,11785,11788,11790,11792,11794,11796],{"class":2115,"line":4976},[2113,11786,11787],{"class":2330},"  data",[2113,11789,6155],{"class":2119},[2113,11791,4480],{"class":2414},[2113,11793,6160],{"class":2119},[2113,11795,8504],{"class":2166},[2113,11797,7173],{"class":2119},[2113,11799,11800],{"class":2115,"line":4993},[2113,11801,2599],{"class":2119},[2113,11803,11804],{"class":2115,"line":5013},[2113,11805,2125],{"emptyLinePlaceholder":1767},[2113,11807,11808,11810,11812],{"class":2115,"line":5018},[2113,11809,2641],{"class":2326},[2113,11811,9863],{"class":2414},[2113,11813,2647],{"class":2119},[2113,11815,11816,11818,11820,11822,11824,11826],{"class":2115,"line":5042},[2113,11817,8071],{"class":2330},[2113,11819,4429],{"class":2119},[2113,11821,4733],{"class":2414},[2113,11823,3109],{"class":2119},[2113,11825,4480],{"class":2414},[2113,11827,9892],{"class":2119},[2113,11829,11830,11832,11834,11836],{"class":2115,"line":5057},[2113,11831,8088],{"class":2330},[2113,11833,4429],{"class":2119},[2113,11835,9902],{"class":2414},[2113,11837,4706],{"class":2119},[2113,11839,11840],{"class":2115,"line":5062},[2113,11841,2125],{"emptyLinePlaceholder":1767},[2113,11843,11844,11846,11848,11850,11852,11854],{"class":2115,"line":5098},[2113,11845,8495],{"class":2330},[2113,11847,4429],{"class":2119},[2113,11849,4733],{"class":2414},[2113,11851,3109],{"class":2119},[2113,11853,9536],{"class":2414},[2113,11855,9892],{"class":2119},[2113,11857,11858,11860,11862,11864],{"class":2115,"line":5117},[2113,11859,8529],{"class":2330},[2113,11861,4429],{"class":2119},[2113,11863,9536],{"class":2414},[2113,11865,4706],{"class":2119},[2113,11867,11868],{"class":2115,"line":5142},[2113,11869,2599],{"class":2119},[2113,11871,11872],{"class":2115,"line":5148},[2113,11873,2125],{"emptyLinePlaceholder":1767},[2113,11875,11876,11878,11880,11882,11884,11886,11888,11890,11892,11894],{"class":2115,"line":5163},[2113,11877,3906],{"class":2326},[2113,11879,9949],{"class":2133},[2113,11881,2423],{"class":2119},[2113,11883,9954],{"class":2330},[2113,11885,4472],{"class":2119},[2113,11887,4030],{"class":2326},[2113,11889,9961],{"class":2414},[2113,11891,4790],{"class":2119},[2113,11893,6101],{"class":2414},[2113,11895,2647],{"class":2119},[2113,11897,11898,11901,11903,11905],{"class":2115,"line":5169},[2113,11899,11900],{"class":2330},"  rng",[2113,11902,179],{"class":2119},[2113,11904,9977],{"class":2133},[2113,11906,6745],{"class":2119},[2113,11908,11909],{"class":2115,"line":5175},[2113,11910,2599],{"class":2119},[2113,11912,11913],{"class":2115,"line":5180},[2113,11914,2125],{"emptyLinePlaceholder":1767},[2113,11916,11917,11919,11921,11923,11925,11927,11929,11931,11933,11935,11937,11939,11941,11943,11945,11947],{"class":2115,"line":5199},[2113,11918,3906],{"class":2326},[2113,11920,9994],{"class":2133},[2113,11922,2423],{"class":2119},[2113,11924,9954],{"class":2330},[2113,11926,4472],{"class":2119},[2113,11928,4030],{"class":2326},[2113,11930,9961],{"class":2414},[2113,11932,932],{"class":2119},[2113,11934,7873],{"class":2330},[2113,11936,4429],{"class":2119},[2113,11938,4496],{"class":2414},[2113,11940,4790],{"class":2119},[2113,11942,4733],{"class":2414},[2113,11944,3109],{"class":2119},[2113,11946,4480],{"class":2414},[2113,11948,4449],{"class":2119},[2113,11950,11951,11954,11956,11958,11960,11962,11964,11966,11968,11970,11972,11974,11976,11978,11980],{"class":2115,"line":5204},[2113,11952,11953],{"class":2326},"  let",[2113,11955,3975],{"class":2326},[2113,11957,10031],{"class":2330},[2113,11959,4429],{"class":2119},[2113,11961,4733],{"class":2414},[2113,11963,3109],{"class":2119},[2113,11965,4480],{"class":2414},[2113,11967,4435],{"class":2119},[2113,11969,2335],{"class":2334},[2113,11971,4008],{"class":2414},[2113,11973,2418],{"class":2119},[2113,11975,10050],{"class":2133},[2113,11977,2423],{"class":2119},[2113,11979,7873],{"class":2330},[2113,11981,2553],{"class":2119},[2113,11983,11984,11986,11988,11990,11992,11994,11996],{"class":2115,"line":5209},[2113,11985,7902],{"class":2326},[2113,11987,10064],{"class":2330},[2113,11989,10067],{"class":2326},[2113,11991,3208],{"class":2166},[2113,11993,10072],{"class":2119},[2113,11995,7873],{"class":2330},[2113,11997,2647],{"class":2119},[2113,11999,12000,12003,12005,12007,12009,12011,12013,12015],{"class":2115,"line":5232},[2113,12001,12002],{"class":2330},"    vect",[2113,12004,179],{"class":2119},[2113,12006,10086],{"class":2133},[2113,12008,2423],{"class":2119},[2113,12010,9954],{"class":2330},[2113,12012,179],{"class":2119},[2113,12014,9977],{"class":2326},[2113,12016,2593],{"class":2119},[2113,12018,12019],{"class":2115,"line":5237},[2113,12020,2572],{"class":2119},[2113,12022,12023],{"class":2115,"line":5242},[2113,12024,12025],{"class":2330},"  vect\n",[2113,12027,12028],{"class":2115,"line":5267},[2113,12029,2599],{"class":2119},[2113,12031,12032],{"class":2115,"line":5282},[2113,12033,2125],{"emptyLinePlaceholder":1767},[2113,12035,12036,12038,12040,12042,12044,12046,12048,12050,12052,12054],{"class":2115,"line":5295},[2113,12037,3906],{"class":2326},[2113,12039,10120],{"class":2133},[2113,12041,2423],{"class":2119},[2113,12043,9954],{"class":2330},[2113,12045,4472],{"class":2119},[2113,12047,4030],{"class":2326},[2113,12049,9961],{"class":2414},[2113,12051,4790],{"class":2119},[2113,12053,9536],{"class":2414},[2113,12055,2647],{"class":2119},[2113,12057,12058,12060,12062,12064,12066,12068,12070,12072,12074,12076,12078,12080],{"class":2115,"line":5310},[2113,12059,11953],{"class":2326},[2113,12061,3975],{"class":2326},[2113,12063,10145],{"class":2330},[2113,12065,2153],{"class":2334},[2113,12067,9815],{"class":2414},[2113,12069,7616],{"class":2119},[2113,12071,10154],{"class":2330},[2113,12073,6155],{"class":2119},[2113,12075,3095],{"class":2166},[2113,12077,6160],{"class":2119},[2113,12079,8504],{"class":2166},[2113,12081,10165],{"class":2119},[2113,12083,12084,12086,12088,12090,12092,12094,12096],{"class":2115,"line":5315},[2113,12085,7902],{"class":2326},[2113,12087,7910],{"class":2330},[2113,12089,10067],{"class":2326},[2113,12091,3208],{"class":2166},[2113,12093,10072],{"class":2119},[2113,12095,8504],{"class":2166},[2113,12097,2647],{"class":2119},[2113,12099,12100,12102,12104,12106,12108,12110,12112,12114,12116],{"class":2115,"line":5320},[2113,12101,9928],{"class":2330},[2113,12103,10189],{"class":2119},[2113,12105,7919],{"class":2330},[2113,12107,4129],{"class":2119},[2113,12109,2335],{"class":2334},[2113,12111,10198],{"class":2330},[2113,12113,179],{"class":2119},[2113,12115,9977],{"class":2133},[2113,12117,3944],{"class":2119},[2113,12119,12120],{"class":2115,"line":8625},[2113,12121,2572],{"class":2119},[2113,12123,12124],{"class":2115,"line":8631},[2113,12125,12126],{"class":2330},"  sha256\n",[2113,12128,12129],{"class":2115,"line":8637},[2113,12130,2599],{"class":2119},[2113,12132,12133],{"class":2115,"line":8652},[2113,12134,2125],{"emptyLinePlaceholder":1767},[2113,12136,12137,12139,12141,12144],{"class":2115,"line":8674},[2113,12138,5374],{"class":2326},[2113,12140,5377],{"class":2326},[2113,12142,12143],{"class":2414}," TestObjectWrapper",[2113,12145,2647],{"class":2119},[2113,12147,12148,12151,12153,12155,12157,12159],{"class":2115,"line":8706},[2113,12149,12150],{"class":2330},"  test_objects",[2113,12152,4429],{"class":2119},[2113,12154,4733],{"class":2414},[2113,12156,3109],{"class":2119},[2113,12158,10406],{"class":2414},[2113,12160,9892],{"class":2119},[2113,12162,12163],{"class":2115,"line":8724},[2113,12164,2599],{"class":2119},[2113,12166,12167],{"class":2115,"line":8729},[2113,12168,2125],{"emptyLinePlaceholder":1767},[2113,12170,12171,12174,12176,12179],{"class":2115,"line":8734},[2113,12172,12173],{"class":2119},"#[napi(js_name ",[2113,12175,2335],{"class":2334},[2113,12177,12178],{"class":2149}," \"TestObjectWrapper\"",[2113,12180,9650],{"class":2119},[2113,12182,12183,12185,12187,12190],{"class":2115,"line":8740},[2113,12184,5374],{"class":2326},[2113,12186,5377],{"class":2326},[2113,12188,12189],{"class":2414}," JsTestObjectWrapper",[2113,12191,2647],{"class":2119},[2113,12193,12194,12197,12199,12202],{"class":2115,"line":8772},[2113,12195,12196],{"class":2330},"  test_object_wrapper",[2113,12198,4429],{"class":2119},[2113,12200,12201],{"class":2414},"TestObjectWrapper",[2113,12203,4706],{"class":2119},[2113,12205,12206],{"class":2115,"line":8778},[2113,12207,2599],{"class":2119},[2113,12209,12210],{"class":2115,"line":8789},[2113,12211,2125],{"emptyLinePlaceholder":1767},[2113,12213,12214],{"class":2115,"line":8795},[2113,12215,12216],{"class":2119},"#[napi]\n",[2113,12218,12219,12221,12223],{"class":2115,"line":8817},[2113,12220,4421],{"class":2326},[2113,12222,12189],{"class":2414},[2113,12224,2647],{"class":2119},[2113,12226,12227],{"class":2115,"line":8822},[2113,12228,12229],{"class":2119},"  #[napi(constructor)]\n",[2113,12231,12232,12235,12237,12239,12242,12244],{"class":2115,"line":8857},[2113,12233,12234],{"class":2326},"  pub",[2113,12236,6542],{"class":2326},[2113,12238,4778],{"class":2133},[2113,12240,12241],{"class":2119},"() -> ",[2113,12243,4793],{"class":2414},[2113,12245,2647],{"class":2119},[2113,12247,12248,12251],{"class":2115,"line":8896},[2113,12249,12250],{"class":2414},"    Self",[2113,12252,2647],{"class":2119},[2113,12254,12255,12258,12260,12262],{"class":2115,"line":8901},[2113,12256,12257],{"class":2330},"      test_object_wrapper",[2113,12259,4429],{"class":2119},[2113,12261,12201],{"class":2414},[2113,12263,2647],{"class":2119},[2113,12265,12266,12268,12270,12272,12274,12276],{"class":2115,"line":8907},[2113,12267,10444],{"class":2330},[2113,12269,4429],{"class":2119},[2113,12271,4733],{"class":2414},[2113,12273,2418],{"class":2119},[2113,12275,3961],{"class":2133},[2113,12277,8210],{"class":2119},[2113,12279,12280],{"class":2115,"line":8913},[2113,12281,12282],{"class":2119},"      },\n",[2113,12284,12285],{"class":2115,"line":8931},[2113,12286,4665],{"class":2119},[2113,12288,12289],{"class":2115,"line":8936},[2113,12290,2572],{"class":2119},[2113,12292,12293],{"class":2115,"line":8942},[2113,12294,2125],{"emptyLinePlaceholder":1767},[2113,12296,12297],{"class":2115,"line":8950},[2113,12298,12299],{"class":2119},"  #[napi]\n",[2113,12301,12302,12304,12306,12309,12311,12313,12315,12317,12319,12321,12324],{"class":2115,"line":8968},[2113,12303,12234],{"class":2326},[2113,12305,6542],{"class":2326},[2113,12307,12308],{"class":2133}," fill",[2113,12310,4027],{"class":2119},[2113,12312,4030],{"class":2326},[2113,12314,4464],{"class":2414},[2113,12316,932],{"class":2119},[2113,12318,10421],{"class":2330},[2113,12320,4429],{"class":2119},[2113,12322,12323],{"class":2414},"i32",[2113,12325,2433],{"class":2119},[2113,12327,12328,12330,12332,12334,12336,12338,12340,12342],{"class":2115,"line":8983},[2113,12329,3917],{"class":2326},[2113,12331,3975],{"class":2326},[2113,12333,10198],{"class":2330},[2113,12335,2153],{"class":2334},[2113,12337,9573],{"class":2414},[2113,12339,2418],{"class":2119},[2113,12341,10264],{"class":2133},[2113,12343,3944],{"class":2119},[2113,12345,12346],{"class":2115,"line":8988},[2113,12347,10271],{"class":2396},[2113,12349,12350,12352,12354,12356,12358,12360,12362,12364,12366],{"class":2115,"line":9011},[2113,12351,3917],{"class":2326},[2113,12353,10278],{"class":2330},[2113,12355,2153],{"class":2334},[2113,12357,10283],{"class":2414},[2113,12359,2418],{"class":2119},[2113,12361,10288],{"class":2133},[2113,12363,5224],{"class":2119},[2113,12365,3941],{"class":2133},[2113,12367,3944],{"class":2119},[2113,12369,12370],{"class":2115,"line":9016},[2113,12371,10299],{"class":2396},[2113,12373,12374,12376,12378,12380,12382,12384,12386,12388,12390,12392,12394],{"class":2115,"line":9022},[2113,12375,3917],{"class":2326},[2113,12377,10306],{"class":2330},[2113,12379,2153],{"class":2334},[2113,12381,3885],{"class":2414},[2113,12383,2418],{"class":2119},[2113,12385,10315],{"class":2414},[2113,12387,2418],{"class":2119},[2113,12389,10320],{"class":2414},[2113,12391,2418],{"class":2119},[2113,12393,8616],{"class":2133},[2113,12395,6745],{"class":2119},[2113,12397,12398,12401,12403,12405,12407,12409,12411,12413,12415],{"class":2115,"line":9045},[2113,12399,12400],{"class":2119},"      .",[2113,12402,10334],{"class":2133},[2113,12404,2423],{"class":2119},[2113,12406,6833],{"class":2414},[2113,12408,2418],{"class":2119},[2113,12410,10315],{"class":2414},[2113,12412,2418],{"class":2119},[2113,12414,10347],{"class":2166},[2113,12416,4660],{"class":2119},[2113,12418,12419,12421,12423],{"class":2115,"line":9076},[2113,12420,12400],{"class":2119},[2113,12422,3941],{"class":2133},[2113,12424,6745],{"class":2119},[2113,12426,12427,12429,12431],{"class":2115,"line":9082},[2113,12428,12400],{"class":2119},[2113,12430,10364],{"class":2133},[2113,12432,3944],{"class":2119},[2113,12434,12435],{"class":2115,"line":9105},[2113,12436,2125],{"emptyLinePlaceholder":1767},[2113,12438,12439,12442,12445,12447,12449,12451,12453,12455,12457,12459,12462,12464,12466],{"class":2115,"line":9138},[2113,12440,12441],{"class":2414},"    self",[2113,12443,12444],{"class":2119},".test_object_wrapper.test_objects ",[2113,12446,2335],{"class":2334},[2113,12448,4008],{"class":2414},[2113,12450,2418],{"class":2119},[2113,12452,10050],{"class":2133},[2113,12454,2423],{"class":2119},[2113,12456,10421],{"class":2330},[2113,12458,179],{"class":2119},[2113,12460,12461],{"class":2133},"try_into",[2113,12463,5224],{"class":2119},[2113,12465,3941],{"class":2133},[2113,12467,2593],{"class":2119},[2113,12469,12470],{"class":2115,"line":9149},[2113,12471,2125],{"emptyLinePlaceholder":1767},[2113,12473,12474,12476,12478,12480,12482,12484,12486],{"class":2115,"line":9157},[2113,12475,10061],{"class":2326},[2113,12477,10064],{"class":2330},[2113,12479,10067],{"class":2326},[2113,12481,3208],{"class":2166},[2113,12483,10072],{"class":2119},[2113,12485,10421],{"class":2330},[2113,12487,2647],{"class":2119},[2113,12489,12490,12493,12496,12498,12500,12502],{"class":2115,"line":9182},[2113,12491,12492],{"class":2414},"      self",[2113,12494,12495],{"class":2119},".test_object_wrapper.test_objects.",[2113,12497,10086],{"class":2133},[2113,12499,2423],{"class":2119},[2113,12501,10406],{"class":2414},[2113,12503,2647],{"class":2119},[2113,12505,12506,12509,12511,12513,12515,12517,12519,12521,12523],{"class":2115,"line":9188},[2113,12507,12508],{"class":2330},"        path",[2113,12510,4429],{"class":2119},[2113,12512,10464],{"class":2133},[2113,12514,4027],{"class":2119},[2113,12516,4030],{"class":2326},[2113,12518,10198],{"class":2330},[2113,12520,932],{"class":2119},[2113,12522,8081],{"class":2166},[2113,12524,7142],{"class":2119},[2113,12526,12527,12530,12532,12534],{"class":2115,"line":9193},[2113,12528,12529],{"class":2330},"        stats",[2113,12531,4429],{"class":2119},[2113,12533,9902],{"class":2414},[2113,12535,2647],{"class":2119},[2113,12537,12538,12541,12543,12545,12547,12549,12551],{"class":2115,"line":9198},[2113,12539,12540],{"class":2330},"          owner_id",[2113,12542,4429],{"class":2119},[2113,12544,10497],{"class":2133},[2113,12546,4027],{"class":2119},[2113,12548,4030],{"class":2326},[2113,12550,10198],{"class":2330},[2113,12552,7142],{"class":2119},[2113,12554,12555,12558,12560,12562,12564,12566,12568],{"class":2115,"line":9216},[2113,12556,12557],{"class":2330},"          group_id",[2113,12559,4429],{"class":2119},[2113,12561,10497],{"class":2133},[2113,12563,4027],{"class":2119},[2113,12565,4030],{"class":2326},[2113,12567,10198],{"class":2330},[2113,12569,7142],{"class":2119},[2113,12571,12572,12575,12577,12579,12581,12583,12585],{"class":2115,"line":9238},[2113,12573,12574],{"class":2330},"          size",[2113,12576,4429],{"class":2119},[2113,12578,10497],{"class":2133},[2113,12580,4027],{"class":2119},[2113,12582,4030],{"class":2326},[2113,12584,10198],{"class":2330},[2113,12586,7142],{"class":2119},[2113,12588,12589,12592,12594,12596,12598,12600,12602],{"class":2115,"line":9243},[2113,12590,12591],{"class":2330},"          compressed_size",[2113,12593,4429],{"class":2119},[2113,12595,10497],{"class":2133},[2113,12597,4027],{"class":2119},[2113,12599,4030],{"class":2326},[2113,12601,10198],{"class":2330},[2113,12603,7142],{"class":2119},[2113,12605,12606,12609,12611,12613,12615,12617,12619],{"class":2115,"line":10782},[2113,12607,12608],{"class":2330},"          last_read",[2113,12610,4429],{"class":2119},[2113,12612,10497],{"class":2133},[2113,12614,4027],{"class":2119},[2113,12616,4030],{"class":2326},[2113,12618,10198],{"class":2330},[2113,12620,7142],{"class":2119},[2113,12622,12623,12626,12628,12630,12632,12634,12636],{"class":2115,"line":10787},[2113,12624,12625],{"class":2330},"          last_modified",[2113,12627,4429],{"class":2119},[2113,12629,10497],{"class":2133},[2113,12631,4027],{"class":2119},[2113,12633,4030],{"class":2326},[2113,12635,10198],{"class":2330},[2113,12637,7142],{"class":2119},[2113,12639,12640,12643,12645,12647,12649,12651,12653],{"class":2115,"line":10813},[2113,12641,12642],{"class":2330},"          created",[2113,12644,4429],{"class":2119},[2113,12646,10497],{"class":2133},[2113,12648,4027],{"class":2119},[2113,12650,4030],{"class":2326},[2113,12652,10198],{"class":2330},[2113,12654,7142],{"class":2119},[2113,12656,12657,12660,12662,12664,12666,12668,12670],{"class":2115,"line":10834},[2113,12658,12659],{"class":2330},"          mode",[2113,12661,4429],{"class":2119},[2113,12663,10497],{"class":2133},[2113,12665,4027],{"class":2119},[2113,12667,4030],{"class":2326},[2113,12669,10198],{"class":2330},[2113,12671,7142],{"class":2119},[2113,12673,12674,12677,12679,12681,12683,12685,12687],{"class":2115,"line":10843},[2113,12675,12676],{"class":2330},"          dev",[2113,12678,4429],{"class":2119},[2113,12680,10497],{"class":2133},[2113,12682,4027],{"class":2119},[2113,12684,4030],{"class":2326},[2113,12686,10198],{"class":2330},[2113,12688,7142],{"class":2119},[2113,12690,12691,12694,12696,12698,12700,12702,12704],{"class":2115,"line":10852},[2113,12692,12693],{"class":2330},"          rdev",[2113,12695,4429],{"class":2119},[2113,12697,10497],{"class":2133},[2113,12699,4027],{"class":2119},[2113,12701,4030],{"class":2326},[2113,12703,10198],{"class":2330},[2113,12705,7142],{"class":2119},[2113,12707,12708,12711,12713,12715,12717,12719,12721],{"class":2115,"line":10858},[2113,12709,12710],{"class":2330},"          ino",[2113,12712,4429],{"class":2119},[2113,12714,10497],{"class":2133},[2113,12716,4027],{"class":2119},[2113,12718,4030],{"class":2326},[2113,12720,10198],{"class":2330},[2113,12722,7142],{"class":2119},[2113,12724,12725,12728,12730,12732,12734,12736,12738],{"class":2115,"line":10880},[2113,12726,12727],{"class":2330},"          nlink",[2113,12729,4429],{"class":2119},[2113,12731,10497],{"class":2133},[2113,12733,4027],{"class":2119},[2113,12735,4030],{"class":2326},[2113,12737,10198],{"class":2330},[2113,12739,7142],{"class":2119},[2113,12741,12742],{"class":2115,"line":10885},[2113,12743,12744],{"class":2119},"        },\n",[2113,12746,12747,12750,12752,12754],{"class":2115,"line":10890},[2113,12748,12749],{"class":2330},"        chunks",[2113,12751,4429],{"class":2119},[2113,12753,10707],{"class":2133},[2113,12755,10710],{"class":2119},[2113,12757,12758,12761,12763,12765,12767],{"class":2115,"line":10912},[2113,12759,12760],{"class":2133},"          generate_random_sha256",[2113,12762,4027],{"class":2119},[2113,12764,4030],{"class":2326},[2113,12766,10198],{"class":2330},[2113,12768,7142],{"class":2119},[2113,12770,12771,12773,12775,12777,12779],{"class":2115,"line":10918},[2113,12772,12760],{"class":2133},[2113,12774,4027],{"class":2119},[2113,12776,4030],{"class":2326},[2113,12778,10198],{"class":2330},[2113,12780,7142],{"class":2119},[2113,12782,12783,12785,12787,12789,12791],{"class":2115,"line":10925},[2113,12784,12760],{"class":2133},[2113,12786,4027],{"class":2119},[2113,12788,4030],{"class":2326},[2113,12790,10198],{"class":2330},[2113,12792,7142],{"class":2119},[2113,12794,12795],{"class":2115,"line":10933},[2113,12796,12797],{"class":2119},"        ],\n",[2113,12799,12800,12802,12804,12806,12808,12810,12812],{"class":2115,"line":10958},[2113,12801,10186],{"class":2330},[2113,12803,4429],{"class":2119},[2113,12805,10762],{"class":2133},[2113,12807,4027],{"class":2119},[2113,12809,4030],{"class":2326},[2113,12811,10198],{"class":2330},[2113,12813,7142],{"class":2119},[2113,12815,12816],{"class":2115,"line":10964},[2113,12817,12818],{"class":2119},"      });\n",[2113,12820,12821],{"class":2115,"line":10970},[2113,12822,4665],{"class":2119},[2113,12824,12825],{"class":2115,"line":10977},[2113,12826,2125],{"emptyLinePlaceholder":1767},[2113,12828,12829,12831,12833,12835,12837,12839,12841,12843,12845,12847,12849],{"class":2115,"line":10985},[2113,12830,3917],{"class":2326},[2113,12832,10792],{"class":2330},[2113,12834,2153],{"class":2334},[2113,12836,3885],{"class":2414},[2113,12838,2418],{"class":2119},[2113,12840,10315],{"class":2414},[2113,12842,2418],{"class":2119},[2113,12844,10320],{"class":2414},[2113,12846,2418],{"class":2119},[2113,12848,8616],{"class":2133},[2113,12850,6745],{"class":2119},[2113,12852,12853,12855,12857,12859,12861,12863,12865,12867,12869],{"class":2115,"line":11010},[2113,12854,12400],{"class":2119},[2113,12856,10334],{"class":2133},[2113,12858,2423],{"class":2119},[2113,12860,6833],{"class":2414},[2113,12862,2418],{"class":2119},[2113,12864,10315],{"class":2414},[2113,12866,2418],{"class":2119},[2113,12868,10347],{"class":2166},[2113,12870,4660],{"class":2119},[2113,12872,12873,12875,12877],{"class":2115,"line":11015},[2113,12874,12400],{"class":2119},[2113,12876,3941],{"class":2133},[2113,12878,6745],{"class":2119},[2113,12880,12881,12883,12885],{"class":2115,"line":11020},[2113,12882,12400],{"class":2119},[2113,12884,10364],{"class":2133},[2113,12886,3944],{"class":2119},[2113,12888,12889],{"class":2115,"line":11026},[2113,12890,10855],{"class":2396},[2113,12892,12893,12895,12897,12899,12901,12903,12905,12907],{"class":2115,"line":11049},[2113,12894,4043],{"class":2133},[2113,12896,2423],{"class":2119},[2113,12898,10865],{"class":2149},[2113,12900,932],{"class":2119},[2113,12902,10870],{"class":2330},[2113,12904,10873],{"class":2119},[2113,12906,8616],{"class":2330},[2113,12908,2553],{"class":2119},[2113,12910,12911],{"class":2115,"line":11062},[2113,12912,2125],{"emptyLinePlaceholder":1767},[2113,12914,12915],{"class":2115,"line":11074},[2113,12916,10271],{"class":2396},[2113,12918,12919,12921,12923,12925,12927,12929,12931,12933,12935],{"class":2115,"line":11079},[2113,12920,3917],{"class":2326},[2113,12922,10895],{"class":2330},[2113,12924,2153],{"class":2334},[2113,12926,10283],{"class":2414},[2113,12928,2418],{"class":2119},[2113,12930,10288],{"class":2133},[2113,12932,5224],{"class":2119},[2113,12934,3941],{"class":2133},[2113,12936,3944],{"class":2119},[2113,12938,12939],{"class":2115,"line":11084},[2113,12940,10915],{"class":2396},[2113,12942,12943,12945],{"class":2115,"line":11090},[2113,12944,4043],{"class":2133},[2113,12946,3146],{"class":2119},[2113,12948,12949,12952],{"class":2115,"line":11124},[2113,12950,12951],{"class":2149},"      \"Memory consumption after creation: {}\"",[2113,12953,4706],{"class":2119},[2113,12955,12956,12959,12961,12963,12965,12967,12969,12971,12973,12975],{"class":2115,"line":11150},[2113,12957,12958],{"class":2133},"      format_size",[2113,12960,8879],{"class":2119},[2113,12962,10941],{"class":2330},[2113,12964,10944],{"class":2119},[2113,12966,10947],{"class":2330},[2113,12968,10950],{"class":2119},[2113,12970,10953],{"class":2166},[2113,12972,932],{"class":2119},[2113,12974,9554],{"class":2166},[2113,12976,4660],{"class":2119},[2113,12978,12979],{"class":2115,"line":11169},[2113,12980,10961],{"class":2119},[2113,12982,12983],{"class":2115,"line":11197},[2113,12984,10967],{"class":2396},[2113,12986,12987,12989],{"class":2115,"line":11202},[2113,12988,4043],{"class":2133},[2113,12990,3146],{"class":2119},[2113,12992,12993,12996],{"class":2115,"line":11207},[2113,12994,12995],{"class":2149},"      \"Average memory consumption per object: {}\"",[2113,12997,4706],{"class":2119},[2113,12999,13000,13002],{"class":2115,"line":11213},[2113,13001,12958],{"class":2133},[2113,13003,3146],{"class":2119},[2113,13005,13006,13009,13011,13013,13015,13017,13019,13021,13023,13025,13028],{"class":2115,"line":11239},[2113,13007,13008],{"class":2119},"        ((",[2113,13010,10941],{"class":2330},[2113,13012,10944],{"class":2119},[2113,13014,10947],{"class":2330},[2113,13016,10950],{"class":2119},[2113,13018,10953],{"class":2166},[2113,13020,11003],{"class":2119},[2113,13022,10421],{"class":2330},[2113,13024,11429],{"class":2326},[2113,13026,13027],{"class":2414}," usize",[2113,13029,4706],{"class":2119},[2113,13031,13032],{"class":2115,"line":11260},[2113,13033,13034],{"class":2166},"        DECIMAL\n",[2113,13036,13037],{"class":2115,"line":11269},[2113,13038,13039],{"class":2119},"      )\n",[2113,13041,13042],{"class":2115,"line":11278},[2113,13043,10961],{"class":2119},[2113,13045,13046],{"class":2115,"line":11283},[2113,13047,2572],{"class":2119},[2113,13049,13050],{"class":2115,"line":11290},[2113,13051,2125],{"emptyLinePlaceholder":1767},[2113,13053,13054,13057,13059,13062],{"class":2115,"line":11298},[2113,13055,13056],{"class":2119},"  #[napi(js_name ",[2113,13058,2335],{"class":2334},[2113,13060,13061],{"class":2149}," \"toString\"",[2113,13063,9650],{"class":2119},[2113,13065,13066,13068,13070,13073,13075,13077,13079,13082],{"class":2115,"line":11309},[2113,13067,12234],{"class":2326},[2113,13069,6542],{"class":2326},[2113,13071,13072],{"class":2133}," to_string",[2113,13074,4027],{"class":2119},[2113,13076,5216],{"class":2414},[2113,13078,4790],{"class":2119},[2113,13080,13081],{"class":2414},"String",[2113,13083,2647],{"class":2119},[2113,13085,13086,13089],{"class":2115,"line":11314},[2113,13087,13088],{"class":2133},"    format!",[2113,13090,3146],{"class":2119},[2113,13092,13093,13096],{"class":2115,"line":11319},[2113,13094,13095],{"class":2149},"      \"TestObjectWrapper {{ test_objects: {} }}\"",[2113,13097,4706],{"class":2119},[2113,13099,13100,13102,13104,13106],{"class":2115,"line":11325},[2113,13101,12492],{"class":2414},[2113,13103,12495],{"class":2119},[2113,13105,4540],{"class":2133},[2113,13107,6745],{"class":2119},[2113,13109,13110],{"class":2115,"line":11356},[2113,13111,13112],{"class":2119},"    )\n",[2113,13114,13115],{"class":2115,"line":11363},[2113,13116,2572],{"class":2119},[2113,13118,13119],{"class":2115,"line":11371},[2113,13120,2599],{"class":2119},[12,13122,13123],{},"Pour la parte Node.JS :",[2105,13125,13127],{"className":7776,"code":13126,"language":7778,"meta":1646,"style":1646},"const test = require(\".\u002Findex.js\");\nconst filesize = require(\"filesize.js\");\nconst fs = require(\"fs\");\nconst { serialize, deserialize } = require(\"v8\");\n\u002F\u002F Start by consuming memory with big object\n\n\u002F\u002F Run GC\nglobal.gc();\n\u002F\u002F Get memory consumption before\nconst memoryBefore = process.memoryUsage().heapUsed;\n\nconst time = Date.now();\n\n\u002F\u002F Create objects\nconst nbObjects = 1_300_000;\nconst testArray = new test.TestObjectWrapper();\ntestArray.fill(nbObjects);\n\n\u002F\u002F Bench creation\nconsole.log(\"Creation time: \", Date.now() - time);\n\n\u002F\u002F Run GC\n\u002F\u002F Get memory consumption after\nconst memoryAfter = process.memoryUsage().heapUsed;\n\n\u002F\u002F Print memory consumption\nconsole.log(\n  \"Memory consumption in JS: \",\n  filesize.default(memoryAfter - memoryBefore)\n);\n\nconsole.log(testArray.toString());\n",[184,13128,13129,13147,13163,13179,13203,13208,13212,13217,13227,13232,13252,13256,13272,13276,13281,13293,13311,13327,13331,13336,13364,13368,13372,13377,13397,13401,13406,13416,13423,13442,13446,13450],{"__ignoreMap":1646},[2113,13130,13131,13133,13136,13138,13140,13142,13145],{"class":2115,"line":2116},[2113,13132,7785],{"class":2326},[2113,13134,13135],{"class":2414}," test",[2113,13137,2153],{"class":2334},[2113,13139,7793],{"class":2133},[2113,13141,2423],{"class":2119},[2113,13143,13144],{"class":2149},"\".\u002Findex.js\"",[2113,13146,2553],{"class":2119},[2113,13148,13149,13151,13153,13155,13157,13159,13161],{"class":2115,"line":1647},[2113,13150,7785],{"class":2326},[2113,13152,7788],{"class":2414},[2113,13154,2153],{"class":2334},[2113,13156,7793],{"class":2133},[2113,13158,2423],{"class":2119},[2113,13160,7798],{"class":2149},[2113,13162,2553],{"class":2119},[2113,13164,13165,13167,13169,13171,13173,13175,13177],{"class":2115,"line":1652},[2113,13166,7785],{"class":2326},[2113,13168,7807],{"class":2414},[2113,13170,2153],{"class":2334},[2113,13172,7793],{"class":2133},[2113,13174,2423],{"class":2119},[2113,13176,7816],{"class":2149},[2113,13178,2553],{"class":2119},[2113,13180,13181,13183,13185,13187,13189,13191,13193,13195,13197,13199,13201],{"class":2115,"line":2185},[2113,13182,7785],{"class":2326},[2113,13184,7616],{"class":2119},[2113,13186,7832],{"class":2414},[2113,13188,932],{"class":2119},[2113,13190,7837],{"class":2414},[2113,13192,7840],{"class":2119},[2113,13194,2335],{"class":2334},[2113,13196,7793],{"class":2133},[2113,13198,2423],{"class":2119},[2113,13200,7849],{"class":2149},[2113,13202,2553],{"class":2119},[2113,13204,13205],{"class":2115,"line":2230},[2113,13206,13207],{"class":2396},"\u002F\u002F Start by consuming memory with big object\n",[2113,13209,13210],{"class":2115,"line":2235},[2113,13211,2125],{"emptyLinePlaceholder":1767},[2113,13213,13214],{"class":2115,"line":2241},[2113,13215,13216],{"class":2396},"\u002F\u002F Run GC\n",[2113,13218,13219,13221,13223,13225],{"class":2115,"line":2246},[2113,13220,8558],{"class":2414},[2113,13222,179],{"class":2119},[2113,13224,8563],{"class":2133},[2113,13226,3944],{"class":2119},[2113,13228,13229],{"class":2115,"line":2464},[2113,13230,13231],{"class":2396},"\u002F\u002F Get memory consumption before\n",[2113,13233,13234,13236,13238,13240,13242,13244,13246,13248,13250],{"class":2115,"line":2085},[2113,13235,7785],{"class":2326},[2113,13237,8577],{"class":2414},[2113,13239,2153],{"class":2334},[2113,13241,8582],{"class":2414},[2113,13243,179],{"class":2119},[2113,13245,8587],{"class":2133},[2113,13247,5224],{"class":2119},[2113,13249,8592],{"class":2330},[2113,13251,2487],{"class":2119},[2113,13253,13254],{"class":2115,"line":2514},[2113,13255,2125],{"emptyLinePlaceholder":1767},[2113,13257,13258,13260,13262,13264,13266,13268,13270],{"class":2115,"line":2533},[2113,13259,7785],{"class":2326},[2113,13261,8606],{"class":2414},[2113,13263,2153],{"class":2334},[2113,13265,8611],{"class":2414},[2113,13267,179],{"class":2119},[2113,13269,8616],{"class":2133},[2113,13271,3944],{"class":2119},[2113,13273,13274],{"class":2115,"line":2556},[2113,13275,2125],{"emptyLinePlaceholder":1767},[2113,13277,13278],{"class":2115,"line":2569},[2113,13279,13280],{"class":2396},"\u002F\u002F Create objects\n",[2113,13282,13283,13285,13287,13289,13291],{"class":2115,"line":2575},[2113,13284,7785],{"class":2326},[2113,13286,8642],{"class":2414},[2113,13288,2153],{"class":2334},[2113,13290,8647],{"class":2166},[2113,13292,2487],{"class":2119},[2113,13294,13295,13297,13299,13301,13303,13305,13307,13309],{"class":2115,"line":2596},[2113,13296,7785],{"class":2326},[2113,13298,8657],{"class":2414},[2113,13300,2153],{"class":2334},[2113,13302,4778],{"class":2326},[2113,13304,13135],{"class":2414},[2113,13306,179],{"class":2119},[2113,13308,12201],{"class":2133},[2113,13310,3944],{"class":2119},[2113,13312,13313,13316,13318,13321,13323,13325],{"class":2115,"line":3192},[2113,13314,13315],{"class":2414},"testArray",[2113,13317,179],{"class":2119},[2113,13319,13320],{"class":2133},"fill",[2113,13322,2423],{"class":2119},[2113,13324,8669],{"class":2330},[2113,13326,2553],{"class":2119},[2113,13328,13329],{"class":2115,"line":3213},[2113,13330,2125],{"emptyLinePlaceholder":1767},[2113,13332,13333],{"class":2115,"line":3236},[2113,13334,13335],{"class":2396},"\u002F\u002F Bench creation\n",[2113,13337,13338,13340,13342,13344,13346,13348,13350,13352,13354,13356,13358,13360,13362],{"class":2115,"line":3248},[2113,13339,8743],{"class":2414},[2113,13341,179],{"class":2119},[2113,13343,8748],{"class":2133},[2113,13345,2423],{"class":2119},[2113,13347,8753],{"class":2149},[2113,13349,932],{"class":2119},[2113,13351,36],{"class":2414},[2113,13353,179],{"class":2119},[2113,13355,8616],{"class":2133},[2113,13357,4543],{"class":2119},[2113,13359,3242],{"class":2334},[2113,13361,8606],{"class":2330},[2113,13363,2553],{"class":2119},[2113,13365,13366],{"class":2115,"line":4899},[2113,13367,2125],{"emptyLinePlaceholder":1767},[2113,13369,13370],{"class":2115,"line":1777},[2113,13371,13216],{"class":2396},[2113,13373,13374],{"class":2115,"line":4931},[2113,13375,13376],{"class":2396},"\u002F\u002F Get memory consumption after\n",[2113,13378,13379,13381,13383,13385,13387,13389,13391,13393,13395],{"class":2115,"line":4961},[2113,13380,7785],{"class":2326},[2113,13382,8800],{"class":2414},[2113,13384,2153],{"class":2334},[2113,13386,8582],{"class":2414},[2113,13388,179],{"class":2119},[2113,13390,8587],{"class":2133},[2113,13392,5224],{"class":2119},[2113,13394,8592],{"class":2330},[2113,13396,2487],{"class":2119},[2113,13398,13399],{"class":2115,"line":4976},[2113,13400,2125],{"emptyLinePlaceholder":1767},[2113,13402,13403],{"class":2115,"line":4993},[2113,13404,13405],{"class":2396},"\u002F\u002F Print memory consumption\n",[2113,13407,13408,13410,13412,13414],{"class":2115,"line":5013},[2113,13409,8743],{"class":2414},[2113,13411,179],{"class":2119},[2113,13413,8748],{"class":2133},[2113,13415,3146],{"class":2119},[2113,13417,13418,13421],{"class":2115,"line":5018},[2113,13419,13420],{"class":2149},"  \"Memory consumption in JS: \"",[2113,13422,4706],{"class":2119},[2113,13424,13425,13428,13430,13432,13434,13436,13438,13440],{"class":2115,"line":5042},[2113,13426,13427],{"class":2414},"  filesize",[2113,13429,179],{"class":2119},[2113,13431,8843],{"class":2133},[2113,13433,2423],{"class":2119},[2113,13435,8848],{"class":2330},[2113,13437,2270],{"class":2334},[2113,13439,8577],{"class":2330},[2113,13441,4660],{"class":2119},[2113,13443,13444],{"class":2115,"line":5057},[2113,13445,2553],{"class":2119},[2113,13447,13448],{"class":2115,"line":5062},[2113,13449,2125],{"emptyLinePlaceholder":1767},[2113,13451,13452,13454,13456,13458,13460,13462,13464,13467],{"class":2115,"line":5098},[2113,13453,8743],{"class":2414},[2113,13455,179],{"class":2119},[2113,13457,8748],{"class":2133},[2113,13459,2423],{"class":2119},[2113,13461,13315],{"class":2414},[2113,13463,179],{"class":2119},[2113,13465,13466],{"class":2133},"toString",[2113,13468,2593],{"class":2119},[12,13470,13471],{},"Voici le résultat:",[22,13473,13474,13482],{},[25,13475,13476],{},[28,13477,13478,13480],{},[31,13479],{},[31,13481],{},[41,13483,13484,13490,13496,13503],{},[28,13485,13486,13488],{},[46,13487,9275],{},[46,13489,9278],{},[28,13491,13492,13494],{},[46,13493,9283],{},[46,13495,11496],{},[28,13497,13498,13500],{},[46,13499,9291],{},[46,13501,13502],{},"520,31 Mo",[28,13504,13505,13507],{},[46,13506,9299],{},[46,13508,13509],{},"400 octets",[12,13511,13512],{},"Parfait, le module écrit en Rust va nous permettre de réduire la consommation mémoire de notre application et améliorer\nses performances.",[128,13514,13516,13518],{"id":13515},"conclusion-wasm",[1892,13517,1621],{}," - Wasm",[12,13520,13521],{},"Une alternative possible à l'écriture d'un module natif en Rust est l'utilisation de WebAssembly. WebAssembly est un\nlangage de bas niveau qui permet d'écrire des modules qui seront exécutés dans un environnement sécurisé. Il est\npossible d'écrire des modules en Rust qui seront compilés en WebAssembly.",[12,13523,13524,13525,179],{},"Pour compiler notre module en WebAssembly, nous allons utiliser le compilateur ",[49,13526,13529],{"href":13527,"rel":13528},"https:\u002F\u002Frustwasm.github.io\u002Fwasm-pack\u002F",[347],"wasm-pack",[12,13531,13532],{},"La partie Rust du module n'est pas très différente de la version native. La seule différence est que nous devons\nutiliser le crate wasm-bindgen pour pouvoir utiliser notre module dans Node.JS.",[12,13534,13535,13536,13540],{},"Là encore, la documentation est très bien faite (",[49,13537,13538],{"href":13538,"rel":13539},"https:\u002F\u002Frustwasm.github.io\u002Fdocs\u002Fbook\u002F",[347],"). Je ne vais pas détailler ici\nla création de ce module.",[2105,13542,13544],{"className":3855,"code":13543,"language":1775,"meta":1646,"style":1646},"mod utils;\n\nuse rand::{rngs::ThreadRng, Rng};\n\nuse wasm_bindgen::prelude::*;\n\n\u002F\u002F When the `wee_alloc` feature is enabled, use `wee_alloc` as the global\n\u002F\u002F allocator.\n#[cfg(feature = \"wee_alloc\")]\n#[global_allocator]\nstatic ALLOC: wee_alloc::WeeAlloc = wee_alloc::WeeAlloc::INIT;\n\n#[wasm_bindgen]\nstruct Stats {\n    owner_id: u64,\n    group_id: u64,\n    size: u64,\n    compressed_size: u64,\n    last_read: u64,\n    last_modified: u64,\n    created: u64,\n    mode: u64,\n    dev: u64,\n    rdev: u64,\n    ino: u64,\n    nlink: u64,\n}\n\n#[wasm_bindgen]\nstruct Sha256 {\n    data: [u8; 32],\n}\n\n#[wasm_bindgen]\nstruct TestObject {\n    path: Vec\u003Cu8>,\n    stats: Stats,\n\n    chunks: Vec\u003CSha256>,\n    sha256: Sha256,\n}\n\nfn generate_random_number(rng: &mut ThreadRng) -> u64 {\n    rng.gen()\n}\n\nfn generate_random_vect(rng: &mut ThreadRng, size: usize) -> Vec\u003Cu8> {\n    let mut vect: Vec\u003Cu8> = Vec::with_capacity(size);\n    for _ in 0..size {\n        vect.push(rng.gen());\n    }\n    vect\n}\n\nfn generate_random_sha256(rng: &mut ThreadRng) -> Sha256 {\n    let mut sha256 = Sha256 { data: [0; 32] };\n    for i in 0..32 {\n        sha256.data[i] = rng.gen();\n    }\n    sha256\n}\n\n#[wasm_bindgen]\npub struct TestObjectWrapper {\n    test_objects: Vec\u003CTestObject>,\n}\n\n#[wasm_bindgen]\nimpl TestObjectWrapper {\n    #[wasm_bindgen(constructor)]\n    pub fn new() -> Self {\n        Self {\n            test_objects: Vec::new(),\n        }\n    }\n\n    #[wasm_bindgen]\n    pub fn fill(&mut self, nb_object: i32) {\n        let mut rng = rand::thread_rng();\n        \u002F\u002F Get the current time in ms\n        self.test_objects = Vec::with_capacity(nb_object as usize);\n\n        for _ in 0..nb_object {\n            self.test_objects.push(TestObject {\n                path: generate_random_vect(&mut rng, 100),\n                stats: Stats {\n                    owner_id: generate_random_number(&mut rng),\n                    group_id: generate_random_number(&mut rng),\n                    size: generate_random_number(&mut rng),\n                    compressed_size: generate_random_number(&mut rng),\n                    last_read: generate_random_number(&mut rng),\n                    last_modified: generate_random_number(&mut rng),\n                    created: generate_random_number(&mut rng),\n                    mode: generate_random_number(&mut rng),\n                    dev: generate_random_number(&mut rng),\n                    rdev: generate_random_number(&mut rng),\n                    ino: generate_random_number(&mut rng),\n                    nlink: generate_random_number(&mut rng),\n                },\n                chunks: vec![\n                    generate_random_sha256(&mut rng),\n                    generate_random_sha256(&mut rng),\n                    generate_random_sha256(&mut rng),\n                ],\n                sha256: generate_random_sha256(&mut rng),\n            });\n        }\n    }\n\n    #[wasm_bindgen(js_name = toString)]\n    pub fn to_string(&self) -> String {\n        format!(\n            \"TestObjectWrapper {{ test_objects: {} }}\",\n            self.test_objects.len()\n        )\n    }\n}\n",[184,13545,13546,13554,13558,13578,13582,13597,13601,13606,13611,13623,13628,13661,13665,13670,13678,13688,13698,13708,13718,13728,13738,13748,13758,13768,13778,13788,13798,13802,13806,13810,13818,13832,13836,13840,13844,13852,13866,13876,13880,13894,13904,13908,13912,13934,13944,13948,13952,13986,14018,14034,14052,14056,14060,14064,14068,14090,14116,14132,14152,14156,14160,14164,14168,14172,14182,14197,14201,14205,14209,14217,14222,14237,14243,14258,14262,14266,14270,14275,14299,14317,14322,14347,14351,14368,14383,14404,14415,14432,14449,14466,14483,14500,14517,14534,14551,14568,14585,14602,14619,14624,14635,14648,14660,14672,14677,14694,14699,14703,14707,14711,14721,14739,14746,14753,14763,14768,14772],{"__ignoreMap":1646},[2113,13547,13548,13551],{"class":2115,"line":2116},[2113,13549,13550],{"class":2326},"mod",[2113,13552,13553],{"class":2119}," utils;\n",[2113,13555,13556],{"class":2115,"line":1647},[2113,13557,2125],{"emptyLinePlaceholder":1767},[2113,13559,13560,13562,13564,13566,13568,13570,13572,13574,13576],{"class":2115,"line":1652},[2113,13561,3863],{"class":2326},[2113,13563,9573],{"class":2414},[2113,13565,9576],{"class":2119},[2113,13567,9579],{"class":2414},[2113,13569,2418],{"class":2119},[2113,13571,9584],{"class":2414},[2113,13573,932],{"class":2119},[2113,13575,9589],{"class":2414},[2113,13577,2787],{"class":2119},[2113,13579,13580],{"class":2115,"line":2185},[2113,13581,2125],{"emptyLinePlaceholder":1767},[2113,13583,13584,13586,13589,13591,13594],{"class":2115,"line":2230},[2113,13585,3863],{"class":2326},[2113,13587,13588],{"class":2414}," wasm_bindgen",[2113,13590,2418],{"class":2119},[2113,13592,13593],{"class":2414},"prelude",[2113,13595,13596],{"class":2119},"::*;\n",[2113,13598,13599],{"class":2115,"line":2235},[2113,13600,2125],{"emptyLinePlaceholder":1767},[2113,13602,13603],{"class":2115,"line":2241},[2113,13604,13605],{"class":2396},"\u002F\u002F When the `wee_alloc` feature is enabled, use `wee_alloc` as the global\n",[2113,13607,13608],{"class":2115,"line":2246},[2113,13609,13610],{"class":2396},"\u002F\u002F allocator.\n",[2113,13612,13613,13616,13618,13621],{"class":2115,"line":2464},[2113,13614,13615],{"class":2119},"#[cfg(feature ",[2113,13617,2335],{"class":2334},[2113,13619,13620],{"class":2149}," \"wee_alloc\"",[2113,13622,9650],{"class":2119},[2113,13624,13625],{"class":2115,"line":2085},[2113,13626,13627],{"class":2119},"#[global_allocator]\n",[2113,13629,13630,13632,13635,13637,13640,13642,13645,13647,13650,13652,13654,13656,13659],{"class":2115,"line":2514},[2113,13631,7668],{"class":2326},[2113,13633,13634],{"class":2166}," ALLOC",[2113,13636,4429],{"class":2119},[2113,13638,13639],{"class":2414},"wee_alloc",[2113,13641,2418],{"class":2119},[2113,13643,13644],{"class":2414},"WeeAlloc",[2113,13646,2153],{"class":2334},[2113,13648,13649],{"class":2414}," wee_alloc",[2113,13651,2418],{"class":2119},[2113,13653,13644],{"class":2414},[2113,13655,2418],{"class":2119},[2113,13657,13658],{"class":2166},"INIT",[2113,13660,2487],{"class":2119},[2113,13662,13663],{"class":2115,"line":2533},[2113,13664,2125],{"emptyLinePlaceholder":1767},[2113,13666,13667],{"class":2115,"line":2556},[2113,13668,13669],{"class":2119},"#[wasm_bindgen]\n",[2113,13671,13672,13674,13676],{"class":2115,"line":2569},[2113,13673,2641],{"class":2326},[2113,13675,9657],{"class":2414},[2113,13677,2647],{"class":2119},[2113,13679,13680,13682,13684,13686],{"class":2115,"line":2575},[2113,13681,9664],{"class":2330},[2113,13683,4429],{"class":2119},[2113,13685,6101],{"class":2414},[2113,13687,4706],{"class":2119},[2113,13689,13690,13692,13694,13696],{"class":2115,"line":2596},[2113,13691,9675],{"class":2330},[2113,13693,4429],{"class":2119},[2113,13695,6101],{"class":2414},[2113,13697,4706],{"class":2119},[2113,13699,13700,13702,13704,13706],{"class":2115,"line":3192},[2113,13701,8165],{"class":2330},[2113,13703,4429],{"class":2119},[2113,13705,6101],{"class":2414},[2113,13707,4706],{"class":2119},[2113,13709,13710,13712,13714,13716],{"class":2115,"line":3213},[2113,13711,9696],{"class":2330},[2113,13713,4429],{"class":2119},[2113,13715,6101],{"class":2414},[2113,13717,4706],{"class":2119},[2113,13719,13720,13722,13724,13726],{"class":2115,"line":3236},[2113,13721,9707],{"class":2330},[2113,13723,4429],{"class":2119},[2113,13725,6101],{"class":2414},[2113,13727,4706],{"class":2119},[2113,13729,13730,13732,13734,13736],{"class":2115,"line":3248},[2113,13731,9718],{"class":2330},[2113,13733,4429],{"class":2119},[2113,13735,6101],{"class":2414},[2113,13737,4706],{"class":2119},[2113,13739,13740,13742,13744,13746],{"class":2115,"line":4899},[2113,13741,8304],{"class":2330},[2113,13743,4429],{"class":2119},[2113,13745,6101],{"class":2414},[2113,13747,4706],{"class":2119},[2113,13749,13750,13752,13754,13756],{"class":2115,"line":1777},[2113,13751,8335],{"class":2330},[2113,13753,4429],{"class":2119},[2113,13755,6101],{"class":2414},[2113,13757,4706],{"class":2119},[2113,13759,13760,13762,13764,13766],{"class":2115,"line":4931},[2113,13761,8366],{"class":2330},[2113,13763,4429],{"class":2119},[2113,13765,6101],{"class":2414},[2113,13767,4706],{"class":2119},[2113,13769,13770,13772,13774,13776],{"class":2115,"line":4961},[2113,13771,8397],{"class":2330},[2113,13773,4429],{"class":2119},[2113,13775,6101],{"class":2414},[2113,13777,4706],{"class":2119},[2113,13779,13780,13782,13784,13786],{"class":2115,"line":4976},[2113,13781,8428],{"class":2330},[2113,13783,4429],{"class":2119},[2113,13785,6101],{"class":2414},[2113,13787,4706],{"class":2119},[2113,13789,13790,13792,13794,13796],{"class":2115,"line":4993},[2113,13791,8459],{"class":2330},[2113,13793,4429],{"class":2119},[2113,13795,6101],{"class":2414},[2113,13797,4706],{"class":2119},[2113,13799,13800],{"class":2115,"line":5013},[2113,13801,2599],{"class":2119},[2113,13803,13804],{"class":2115,"line":5018},[2113,13805,2125],{"emptyLinePlaceholder":1767},[2113,13807,13808],{"class":2115,"line":5042},[2113,13809,13669],{"class":2119},[2113,13811,13812,13814,13816],{"class":2115,"line":5057},[2113,13813,2641],{"class":2326},[2113,13815,9815],{"class":2414},[2113,13817,2647],{"class":2119},[2113,13819,13820,13822,13824,13826,13828,13830],{"class":2115,"line":5062},[2113,13821,9822],{"class":2330},[2113,13823,6155],{"class":2119},[2113,13825,4480],{"class":2414},[2113,13827,6160],{"class":2119},[2113,13829,8504],{"class":2166},[2113,13831,7173],{"class":2119},[2113,13833,13834],{"class":2115,"line":5098},[2113,13835,2599],{"class":2119},[2113,13837,13838],{"class":2115,"line":5117},[2113,13839,2125],{"emptyLinePlaceholder":1767},[2113,13841,13842],{"class":2115,"line":5142},[2113,13843,13669],{"class":2119},[2113,13845,13846,13848,13850],{"class":2115,"line":5148},[2113,13847,2641],{"class":2326},[2113,13849,9863],{"class":2414},[2113,13851,2647],{"class":2119},[2113,13853,13854,13856,13858,13860,13862,13864],{"class":2115,"line":5163},[2113,13855,7559],{"class":2330},[2113,13857,4429],{"class":2119},[2113,13859,4733],{"class":2414},[2113,13861,3109],{"class":2119},[2113,13863,4480],{"class":2414},[2113,13865,9892],{"class":2119},[2113,13867,13868,13870,13872,13874],{"class":2115,"line":5169},[2113,13869,9897],{"class":2330},[2113,13871,4429],{"class":2119},[2113,13873,9902],{"class":2414},[2113,13875,4706],{"class":2119},[2113,13877,13878],{"class":2115,"line":5175},[2113,13879,2125],{"emptyLinePlaceholder":1767},[2113,13881,13882,13884,13886,13888,13890,13892],{"class":2115,"line":5180},[2113,13883,9913],{"class":2330},[2113,13885,4429],{"class":2119},[2113,13887,4733],{"class":2414},[2113,13889,3109],{"class":2119},[2113,13891,9536],{"class":2414},[2113,13893,9892],{"class":2119},[2113,13895,13896,13898,13900,13902],{"class":2115,"line":5199},[2113,13897,9928],{"class":2330},[2113,13899,4429],{"class":2119},[2113,13901,9536],{"class":2414},[2113,13903,4706],{"class":2119},[2113,13905,13906],{"class":2115,"line":5204},[2113,13907,2599],{"class":2119},[2113,13909,13910],{"class":2115,"line":5209},[2113,13911,2125],{"emptyLinePlaceholder":1767},[2113,13913,13914,13916,13918,13920,13922,13924,13926,13928,13930,13932],{"class":2115,"line":5232},[2113,13915,3906],{"class":2326},[2113,13917,9949],{"class":2133},[2113,13919,2423],{"class":2119},[2113,13921,9954],{"class":2330},[2113,13923,4472],{"class":2119},[2113,13925,4030],{"class":2326},[2113,13927,9961],{"class":2414},[2113,13929,4790],{"class":2119},[2113,13931,6101],{"class":2414},[2113,13933,2647],{"class":2119},[2113,13935,13936,13938,13940,13942],{"class":2115,"line":5237},[2113,13937,9972],{"class":2330},[2113,13939,179],{"class":2119},[2113,13941,9977],{"class":2133},[2113,13943,6745],{"class":2119},[2113,13945,13946],{"class":2115,"line":5242},[2113,13947,2599],{"class":2119},[2113,13949,13950],{"class":2115,"line":5267},[2113,13951,2125],{"emptyLinePlaceholder":1767},[2113,13953,13954,13956,13958,13960,13962,13964,13966,13968,13970,13972,13974,13976,13978,13980,13982,13984],{"class":2115,"line":5282},[2113,13955,3906],{"class":2326},[2113,13957,9994],{"class":2133},[2113,13959,2423],{"class":2119},[2113,13961,9954],{"class":2330},[2113,13963,4472],{"class":2119},[2113,13965,4030],{"class":2326},[2113,13967,9961],{"class":2414},[2113,13969,932],{"class":2119},[2113,13971,7873],{"class":2330},[2113,13973,4429],{"class":2119},[2113,13975,4496],{"class":2414},[2113,13977,4790],{"class":2119},[2113,13979,4733],{"class":2414},[2113,13981,3109],{"class":2119},[2113,13983,4480],{"class":2414},[2113,13985,4449],{"class":2119},[2113,13987,13988,13990,13992,13994,13996,13998,14000,14002,14004,14006,14008,14010,14012,14014,14016],{"class":2115,"line":5295},[2113,13989,3917],{"class":2326},[2113,13991,3975],{"class":2326},[2113,13993,10031],{"class":2330},[2113,13995,4429],{"class":2119},[2113,13997,4733],{"class":2414},[2113,13999,3109],{"class":2119},[2113,14001,4480],{"class":2414},[2113,14003,4435],{"class":2119},[2113,14005,2335],{"class":2334},[2113,14007,4008],{"class":2414},[2113,14009,2418],{"class":2119},[2113,14011,10050],{"class":2133},[2113,14013,2423],{"class":2119},[2113,14015,7873],{"class":2330},[2113,14017,2553],{"class":2119},[2113,14019,14020,14022,14024,14026,14028,14030,14032],{"class":2115,"line":5310},[2113,14021,10061],{"class":2326},[2113,14023,10064],{"class":2330},[2113,14025,10067],{"class":2326},[2113,14027,3208],{"class":2166},[2113,14029,10072],{"class":2119},[2113,14031,7873],{"class":2330},[2113,14033,2647],{"class":2119},[2113,14035,14036,14038,14040,14042,14044,14046,14048,14050],{"class":2115,"line":5315},[2113,14037,10081],{"class":2330},[2113,14039,179],{"class":2119},[2113,14041,10086],{"class":2133},[2113,14043,2423],{"class":2119},[2113,14045,9954],{"class":2330},[2113,14047,179],{"class":2119},[2113,14049,9977],{"class":2326},[2113,14051,2593],{"class":2119},[2113,14053,14054],{"class":2115,"line":5320},[2113,14055,4665],{"class":2119},[2113,14057,14058],{"class":2115,"line":8625},[2113,14059,10105],{"class":2330},[2113,14061,14062],{"class":2115,"line":8631},[2113,14063,2599],{"class":2119},[2113,14065,14066],{"class":2115,"line":8637},[2113,14067,2125],{"emptyLinePlaceholder":1767},[2113,14069,14070,14072,14074,14076,14078,14080,14082,14084,14086,14088],{"class":2115,"line":8652},[2113,14071,3906],{"class":2326},[2113,14073,10120],{"class":2133},[2113,14075,2423],{"class":2119},[2113,14077,9954],{"class":2330},[2113,14079,4472],{"class":2119},[2113,14081,4030],{"class":2326},[2113,14083,9961],{"class":2414},[2113,14085,4790],{"class":2119},[2113,14087,9536],{"class":2414},[2113,14089,2647],{"class":2119},[2113,14091,14092,14094,14096,14098,14100,14102,14104,14106,14108,14110,14112,14114],{"class":2115,"line":8674},[2113,14093,3917],{"class":2326},[2113,14095,3975],{"class":2326},[2113,14097,10145],{"class":2330},[2113,14099,2153],{"class":2334},[2113,14101,9815],{"class":2414},[2113,14103,7616],{"class":2119},[2113,14105,10154],{"class":2330},[2113,14107,6155],{"class":2119},[2113,14109,3095],{"class":2166},[2113,14111,6160],{"class":2119},[2113,14113,8504],{"class":2166},[2113,14115,10165],{"class":2119},[2113,14117,14118,14120,14122,14124,14126,14128,14130],{"class":2115,"line":8706},[2113,14119,10061],{"class":2326},[2113,14121,7910],{"class":2330},[2113,14123,10067],{"class":2326},[2113,14125,3208],{"class":2166},[2113,14127,10072],{"class":2119},[2113,14129,8504],{"class":2166},[2113,14131,2647],{"class":2119},[2113,14133,14134,14136,14138,14140,14142,14144,14146,14148,14150],{"class":2115,"line":8724},[2113,14135,10186],{"class":2330},[2113,14137,10189],{"class":2119},[2113,14139,7919],{"class":2330},[2113,14141,4129],{"class":2119},[2113,14143,2335],{"class":2334},[2113,14145,10198],{"class":2330},[2113,14147,179],{"class":2119},[2113,14149,9977],{"class":2133},[2113,14151,3944],{"class":2119},[2113,14153,14154],{"class":2115,"line":8729},[2113,14155,4665],{"class":2119},[2113,14157,14158],{"class":2115,"line":8734},[2113,14159,10213],{"class":2330},[2113,14161,14162],{"class":2115,"line":8740},[2113,14163,2599],{"class":2119},[2113,14165,14166],{"class":2115,"line":8772},[2113,14167,2125],{"emptyLinePlaceholder":1767},[2113,14169,14170],{"class":2115,"line":8778},[2113,14171,13669],{"class":2119},[2113,14173,14174,14176,14178,14180],{"class":2115,"line":8789},[2113,14175,5374],{"class":2326},[2113,14177,5377],{"class":2326},[2113,14179,12143],{"class":2414},[2113,14181,2647],{"class":2119},[2113,14183,14184,14187,14189,14191,14193,14195],{"class":2115,"line":8795},[2113,14185,14186],{"class":2330},"    test_objects",[2113,14188,4429],{"class":2119},[2113,14190,4733],{"class":2414},[2113,14192,3109],{"class":2119},[2113,14194,10406],{"class":2414},[2113,14196,9892],{"class":2119},[2113,14198,14199],{"class":2115,"line":8817},[2113,14200,2599],{"class":2119},[2113,14202,14203],{"class":2115,"line":8822},[2113,14204,2125],{"emptyLinePlaceholder":1767},[2113,14206,14207],{"class":2115,"line":8857},[2113,14208,13669],{"class":2119},[2113,14210,14211,14213,14215],{"class":2115,"line":8896},[2113,14212,4421],{"class":2326},[2113,14214,12143],{"class":2414},[2113,14216,2647],{"class":2119},[2113,14218,14219],{"class":2115,"line":8901},[2113,14220,14221],{"class":2119},"    #[wasm_bindgen(constructor)]\n",[2113,14223,14224,14227,14229,14231,14233,14235],{"class":2115,"line":8907},[2113,14225,14226],{"class":2326},"    pub",[2113,14228,6542],{"class":2326},[2113,14230,4778],{"class":2133},[2113,14232,12241],{"class":2119},[2113,14234,4793],{"class":2414},[2113,14236,2647],{"class":2119},[2113,14238,14239,14241],{"class":2115,"line":8913},[2113,14240,4800],{"class":2414},[2113,14242,2647],{"class":2119},[2113,14244,14245,14248,14250,14252,14254,14256],{"class":2115,"line":8931},[2113,14246,14247],{"class":2330},"            test_objects",[2113,14249,4429],{"class":2119},[2113,14251,4733],{"class":2414},[2113,14253,2418],{"class":2119},[2113,14255,3961],{"class":2133},[2113,14257,8210],{"class":2119},[2113,14259,14260],{"class":2115,"line":8936},[2113,14261,4578],{"class":2119},[2113,14263,14264],{"class":2115,"line":8942},[2113,14265,4665],{"class":2119},[2113,14267,14268],{"class":2115,"line":8950},[2113,14269,2125],{"emptyLinePlaceholder":1767},[2113,14271,14272],{"class":2115,"line":8968},[2113,14273,14274],{"class":2119},"    #[wasm_bindgen]\n",[2113,14276,14277,14279,14281,14283,14285,14287,14289,14291,14293,14295,14297],{"class":2115,"line":8983},[2113,14278,14226],{"class":2326},[2113,14280,6542],{"class":2326},[2113,14282,12308],{"class":2133},[2113,14284,4027],{"class":2119},[2113,14286,4030],{"class":2326},[2113,14288,4464],{"class":2414},[2113,14290,932],{"class":2119},[2113,14292,10421],{"class":2330},[2113,14294,4429],{"class":2119},[2113,14296,12323],{"class":2414},[2113,14298,2433],{"class":2119},[2113,14300,14301,14303,14305,14307,14309,14311,14313,14315],{"class":2115,"line":8988},[2113,14302,4583],{"class":2326},[2113,14304,3975],{"class":2326},[2113,14306,10198],{"class":2330},[2113,14308,2153],{"class":2334},[2113,14310,9573],{"class":2414},[2113,14312,2418],{"class":2119},[2113,14314,10264],{"class":2133},[2113,14316,3944],{"class":2119},[2113,14318,14319],{"class":2115,"line":9011},[2113,14320,14321],{"class":2396},"        \u002F\u002F Get the current time in ms\n",[2113,14323,14324,14326,14329,14331,14333,14335,14337,14339,14341,14343,14345],{"class":2115,"line":9016},[2113,14325,4637],{"class":2414},[2113,14327,14328],{"class":2119},".test_objects ",[2113,14330,2335],{"class":2334},[2113,14332,4008],{"class":2414},[2113,14334,2418],{"class":2119},[2113,14336,10050],{"class":2133},[2113,14338,2423],{"class":2119},[2113,14340,10421],{"class":2330},[2113,14342,11429],{"class":2326},[2113,14344,13027],{"class":2414},[2113,14346,2553],{"class":2119},[2113,14348,14349],{"class":2115,"line":9022},[2113,14350,2125],{"emptyLinePlaceholder":1767},[2113,14352,14353,14356,14358,14360,14362,14364,14366],{"class":2115,"line":9045},[2113,14354,14355],{"class":2326},"        for",[2113,14357,10064],{"class":2330},[2113,14359,10067],{"class":2326},[2113,14361,3208],{"class":2166},[2113,14363,10072],{"class":2119},[2113,14365,10421],{"class":2330},[2113,14367,2647],{"class":2119},[2113,14369,14370,14372,14375,14377,14379,14381],{"class":2115,"line":9076},[2113,14371,5183],{"class":2414},[2113,14373,14374],{"class":2119},".test_objects.",[2113,14376,10086],{"class":2133},[2113,14378,2423],{"class":2119},[2113,14380,10406],{"class":2414},[2113,14382,2647],{"class":2119},[2113,14384,14385,14388,14390,14392,14394,14396,14398,14400,14402],{"class":2115,"line":9082},[2113,14386,14387],{"class":2330},"                path",[2113,14389,4429],{"class":2119},[2113,14391,10464],{"class":2133},[2113,14393,4027],{"class":2119},[2113,14395,4030],{"class":2326},[2113,14397,10198],{"class":2330},[2113,14399,932],{"class":2119},[2113,14401,8081],{"class":2166},[2113,14403,7142],{"class":2119},[2113,14405,14406,14409,14411,14413],{"class":2115,"line":9105},[2113,14407,14408],{"class":2330},"                stats",[2113,14410,4429],{"class":2119},[2113,14412,9902],{"class":2414},[2113,14414,2647],{"class":2119},[2113,14416,14417,14420,14422,14424,14426,14428,14430],{"class":2115,"line":9138},[2113,14418,14419],{"class":2330},"                    owner_id",[2113,14421,4429],{"class":2119},[2113,14423,10497],{"class":2133},[2113,14425,4027],{"class":2119},[2113,14427,4030],{"class":2326},[2113,14429,10198],{"class":2330},[2113,14431,7142],{"class":2119},[2113,14433,14434,14437,14439,14441,14443,14445,14447],{"class":2115,"line":9149},[2113,14435,14436],{"class":2330},"                    group_id",[2113,14438,4429],{"class":2119},[2113,14440,10497],{"class":2133},[2113,14442,4027],{"class":2119},[2113,14444,4030],{"class":2326},[2113,14446,10198],{"class":2330},[2113,14448,7142],{"class":2119},[2113,14450,14451,14454,14456,14458,14460,14462,14464],{"class":2115,"line":9157},[2113,14452,14453],{"class":2330},"                    size",[2113,14455,4429],{"class":2119},[2113,14457,10497],{"class":2133},[2113,14459,4027],{"class":2119},[2113,14461,4030],{"class":2326},[2113,14463,10198],{"class":2330},[2113,14465,7142],{"class":2119},[2113,14467,14468,14471,14473,14475,14477,14479,14481],{"class":2115,"line":9182},[2113,14469,14470],{"class":2330},"                    compressed_size",[2113,14472,4429],{"class":2119},[2113,14474,10497],{"class":2133},[2113,14476,4027],{"class":2119},[2113,14478,4030],{"class":2326},[2113,14480,10198],{"class":2330},[2113,14482,7142],{"class":2119},[2113,14484,14485,14488,14490,14492,14494,14496,14498],{"class":2115,"line":9188},[2113,14486,14487],{"class":2330},"                    last_read",[2113,14489,4429],{"class":2119},[2113,14491,10497],{"class":2133},[2113,14493,4027],{"class":2119},[2113,14495,4030],{"class":2326},[2113,14497,10198],{"class":2330},[2113,14499,7142],{"class":2119},[2113,14501,14502,14505,14507,14509,14511,14513,14515],{"class":2115,"line":9193},[2113,14503,14504],{"class":2330},"                    last_modified",[2113,14506,4429],{"class":2119},[2113,14508,10497],{"class":2133},[2113,14510,4027],{"class":2119},[2113,14512,4030],{"class":2326},[2113,14514,10198],{"class":2330},[2113,14516,7142],{"class":2119},[2113,14518,14519,14522,14524,14526,14528,14530,14532],{"class":2115,"line":9198},[2113,14520,14521],{"class":2330},"                    created",[2113,14523,4429],{"class":2119},[2113,14525,10497],{"class":2133},[2113,14527,4027],{"class":2119},[2113,14529,4030],{"class":2326},[2113,14531,10198],{"class":2330},[2113,14533,7142],{"class":2119},[2113,14535,14536,14539,14541,14543,14545,14547,14549],{"class":2115,"line":9216},[2113,14537,14538],{"class":2330},"                    mode",[2113,14540,4429],{"class":2119},[2113,14542,10497],{"class":2133},[2113,14544,4027],{"class":2119},[2113,14546,4030],{"class":2326},[2113,14548,10198],{"class":2330},[2113,14550,7142],{"class":2119},[2113,14552,14553,14556,14558,14560,14562,14564,14566],{"class":2115,"line":9238},[2113,14554,14555],{"class":2330},"                    dev",[2113,14557,4429],{"class":2119},[2113,14559,10497],{"class":2133},[2113,14561,4027],{"class":2119},[2113,14563,4030],{"class":2326},[2113,14565,10198],{"class":2330},[2113,14567,7142],{"class":2119},[2113,14569,14570,14573,14575,14577,14579,14581,14583],{"class":2115,"line":9243},[2113,14571,14572],{"class":2330},"                    rdev",[2113,14574,4429],{"class":2119},[2113,14576,10497],{"class":2133},[2113,14578,4027],{"class":2119},[2113,14580,4030],{"class":2326},[2113,14582,10198],{"class":2330},[2113,14584,7142],{"class":2119},[2113,14586,14587,14590,14592,14594,14596,14598,14600],{"class":2115,"line":10782},[2113,14588,14589],{"class":2330},"                    ino",[2113,14591,4429],{"class":2119},[2113,14593,10497],{"class":2133},[2113,14595,4027],{"class":2119},[2113,14597,4030],{"class":2326},[2113,14599,10198],{"class":2330},[2113,14601,7142],{"class":2119},[2113,14603,14604,14607,14609,14611,14613,14615,14617],{"class":2115,"line":10787},[2113,14605,14606],{"class":2330},"                    nlink",[2113,14608,4429],{"class":2119},[2113,14610,10497],{"class":2133},[2113,14612,4027],{"class":2119},[2113,14614,4030],{"class":2326},[2113,14616,10198],{"class":2330},[2113,14618,7142],{"class":2119},[2113,14620,14621],{"class":2115,"line":10813},[2113,14622,14623],{"class":2119},"                },\n",[2113,14625,14626,14629,14631,14633],{"class":2115,"line":10834},[2113,14627,14628],{"class":2330},"                chunks",[2113,14630,4429],{"class":2119},[2113,14632,10707],{"class":2133},[2113,14634,10710],{"class":2119},[2113,14636,14637,14640,14642,14644,14646],{"class":2115,"line":10843},[2113,14638,14639],{"class":2133},"                    generate_random_sha256",[2113,14641,4027],{"class":2119},[2113,14643,4030],{"class":2326},[2113,14645,10198],{"class":2330},[2113,14647,7142],{"class":2119},[2113,14649,14650,14652,14654,14656,14658],{"class":2115,"line":10852},[2113,14651,14639],{"class":2133},[2113,14653,4027],{"class":2119},[2113,14655,4030],{"class":2326},[2113,14657,10198],{"class":2330},[2113,14659,7142],{"class":2119},[2113,14661,14662,14664,14666,14668,14670],{"class":2115,"line":10858},[2113,14663,14639],{"class":2133},[2113,14665,4027],{"class":2119},[2113,14667,4030],{"class":2326},[2113,14669,10198],{"class":2330},[2113,14671,7142],{"class":2119},[2113,14673,14674],{"class":2115,"line":10880},[2113,14675,14676],{"class":2119},"                ],\n",[2113,14678,14679,14682,14684,14686,14688,14690,14692],{"class":2115,"line":10885},[2113,14680,14681],{"class":2330},"                sha256",[2113,14683,4429],{"class":2119},[2113,14685,10762],{"class":2133},[2113,14687,4027],{"class":2119},[2113,14689,4030],{"class":2326},[2113,14691,10198],{"class":2330},[2113,14693,7142],{"class":2119},[2113,14695,14696],{"class":2115,"line":10890},[2113,14697,14698],{"class":2119},"            });\n",[2113,14700,14701],{"class":2115,"line":10912},[2113,14702,4578],{"class":2119},[2113,14704,14705],{"class":2115,"line":10918},[2113,14706,4665],{"class":2119},[2113,14708,14709],{"class":2115,"line":10925},[2113,14710,2125],{"emptyLinePlaceholder":1767},[2113,14712,14713,14716,14718],{"class":2115,"line":10933},[2113,14714,14715],{"class":2119},"    #[wasm_bindgen(js_name ",[2113,14717,2335],{"class":2334},[2113,14719,14720],{"class":2119}," toString)]\n",[2113,14722,14723,14725,14727,14729,14731,14733,14735,14737],{"class":2115,"line":10958},[2113,14724,14226],{"class":2326},[2113,14726,6542],{"class":2326},[2113,14728,13072],{"class":2133},[2113,14730,4027],{"class":2119},[2113,14732,5216],{"class":2414},[2113,14734,4790],{"class":2119},[2113,14736,13081],{"class":2414},[2113,14738,2647],{"class":2119},[2113,14740,14741,14744],{"class":2115,"line":10964},[2113,14742,14743],{"class":2133},"        format!",[2113,14745,3146],{"class":2119},[2113,14747,14748,14751],{"class":2115,"line":10970},[2113,14749,14750],{"class":2149},"            \"TestObjectWrapper {{ test_objects: {} }}\"",[2113,14752,4706],{"class":2119},[2113,14754,14755,14757,14759,14761],{"class":2115,"line":10977},[2113,14756,5183],{"class":2414},[2113,14758,14374],{"class":2119},[2113,14760,4540],{"class":2133},[2113,14762,6745],{"class":2119},[2113,14764,14765],{"class":2115,"line":10985},[2113,14766,14767],{"class":2119},"        )\n",[2113,14769,14770],{"class":2115,"line":11010},[2113,14771,4665],{"class":2119},[2113,14773,14774],{"class":2115,"line":11015},[2113,14775,2599],{"class":2119},[12,14777,14778,14779,179],{},"Pour utiliser notre module dans Node.JS, nous devons importer le module ",[184,14780,14781],{},"wasm-bindgen",[2105,14783,14785],{"className":7776,"code":14784,"language":7778,"meta":1646,"style":1646},"const { webcrypto } = require('node:crypto')\nglobal.crypto = webcrypto\n\nconst wasm = require('..\u002Fpkg\u002Ftest_rust_was.js');\nconst filesize = require(\"filesize.js\");\n\nglobal.gc();\nconst memoryBefore = process.memoryUsage().rss;\n\nconst time = Date.now();\n\nconst nbObjects = 1300000;\nconst testArray = new wasm.TestObjectWrapper();\ntestArray.fill(nbObjects);\n\nconsole.log(\"Creation time: \", Date.now() - time);\n\nglobal.gc();\nconst memoryAfter = process.memoryUsage().rss;\n\nconsole.log(\"Memory consumption in JS: \", filesize.default(memoryAfter - memoryBefore));\nconsole.log(\"Memory consumption calculated by head: \", filesize.default(432));\nconsole.log(\"Memory consumption by objects: \", filesize.default((memoryAfter - memoryBefore) \u002F nbObjects));\n  \nconsole.log(testArray.toString());\n",[184,14786,14787,14809,14823,14827,14845,14861,14865,14875,14896,14900,14916,14920,14933,14951,14965,14969,14997,15001,15011,15031,15035,15066,15094,15130,15135],{"__ignoreMap":1646},[2113,14788,14789,14791,14793,14796,14798,14800,14802,14804,14807],{"class":2115,"line":2116},[2113,14790,7785],{"class":2326},[2113,14792,7616],{"class":2119},[2113,14794,14795],{"class":2414},"webcrypto",[2113,14797,7840],{"class":2119},[2113,14799,2335],{"class":2334},[2113,14801,7793],{"class":2133},[2113,14803,2423],{"class":2119},[2113,14805,14806],{"class":2149},"'node:crypto'",[2113,14808,4660],{"class":2119},[2113,14810,14811,14813,14815,14818,14820],{"class":2115,"line":1647},[2113,14812,8558],{"class":2414},[2113,14814,179],{"class":2119},[2113,14816,14817],{"class":2330},"crypto",[2113,14819,2153],{"class":2334},[2113,14821,14822],{"class":2330}," webcrypto\n",[2113,14824,14825],{"class":2115,"line":1652},[2113,14826,2125],{"emptyLinePlaceholder":1767},[2113,14828,14829,14831,14834,14836,14838,14840,14843],{"class":2115,"line":2185},[2113,14830,7785],{"class":2326},[2113,14832,14833],{"class":2414}," wasm",[2113,14835,2153],{"class":2334},[2113,14837,7793],{"class":2133},[2113,14839,2423],{"class":2119},[2113,14841,14842],{"class":2149},"'..\u002Fpkg\u002Ftest_rust_was.js'",[2113,14844,2553],{"class":2119},[2113,14846,14847,14849,14851,14853,14855,14857,14859],{"class":2115,"line":2230},[2113,14848,7785],{"class":2326},[2113,14850,7788],{"class":2414},[2113,14852,2153],{"class":2334},[2113,14854,7793],{"class":2133},[2113,14856,2423],{"class":2119},[2113,14858,7798],{"class":2149},[2113,14860,2553],{"class":2119},[2113,14862,14863],{"class":2115,"line":2235},[2113,14864,2125],{"emptyLinePlaceholder":1767},[2113,14866,14867,14869,14871,14873],{"class":2115,"line":2241},[2113,14868,8558],{"class":2414},[2113,14870,179],{"class":2119},[2113,14872,8563],{"class":2133},[2113,14874,3944],{"class":2119},[2113,14876,14877,14879,14881,14883,14885,14887,14889,14891,14894],{"class":2115,"line":2246},[2113,14878,7785],{"class":2326},[2113,14880,8577],{"class":2414},[2113,14882,2153],{"class":2334},[2113,14884,8582],{"class":2414},[2113,14886,179],{"class":2119},[2113,14888,8587],{"class":2133},[2113,14890,5224],{"class":2119},[2113,14892,14893],{"class":2330},"rss",[2113,14895,2487],{"class":2119},[2113,14897,14898],{"class":2115,"line":2464},[2113,14899,2125],{"emptyLinePlaceholder":1767},[2113,14901,14902,14904,14906,14908,14910,14912,14914],{"class":2115,"line":2085},[2113,14903,7785],{"class":2326},[2113,14905,8606],{"class":2414},[2113,14907,2153],{"class":2334},[2113,14909,8611],{"class":2414},[2113,14911,179],{"class":2119},[2113,14913,8616],{"class":2133},[2113,14915,3944],{"class":2119},[2113,14917,14918],{"class":2115,"line":2514},[2113,14919,2125],{"emptyLinePlaceholder":1767},[2113,14921,14922,14924,14926,14928,14931],{"class":2115,"line":2533},[2113,14923,7785],{"class":2326},[2113,14925,8642],{"class":2414},[2113,14927,2153],{"class":2334},[2113,14929,14930],{"class":2166}," 1300000",[2113,14932,2487],{"class":2119},[2113,14934,14935,14937,14939,14941,14943,14945,14947,14949],{"class":2115,"line":2556},[2113,14936,7785],{"class":2326},[2113,14938,8657],{"class":2414},[2113,14940,2153],{"class":2334},[2113,14942,4778],{"class":2326},[2113,14944,14833],{"class":2414},[2113,14946,179],{"class":2119},[2113,14948,12201],{"class":2133},[2113,14950,3944],{"class":2119},[2113,14952,14953,14955,14957,14959,14961,14963],{"class":2115,"line":2569},[2113,14954,13315],{"class":2414},[2113,14956,179],{"class":2119},[2113,14958,13320],{"class":2133},[2113,14960,2423],{"class":2119},[2113,14962,8669],{"class":2330},[2113,14964,2553],{"class":2119},[2113,14966,14967],{"class":2115,"line":2575},[2113,14968,2125],{"emptyLinePlaceholder":1767},[2113,14970,14971,14973,14975,14977,14979,14981,14983,14985,14987,14989,14991,14993,14995],{"class":2115,"line":2596},[2113,14972,8743],{"class":2414},[2113,14974,179],{"class":2119},[2113,14976,8748],{"class":2133},[2113,14978,2423],{"class":2119},[2113,14980,8753],{"class":2149},[2113,14982,932],{"class":2119},[2113,14984,36],{"class":2414},[2113,14986,179],{"class":2119},[2113,14988,8616],{"class":2133},[2113,14990,4543],{"class":2119},[2113,14992,3242],{"class":2334},[2113,14994,8606],{"class":2330},[2113,14996,2553],{"class":2119},[2113,14998,14999],{"class":2115,"line":3192},[2113,15000,2125],{"emptyLinePlaceholder":1767},[2113,15002,15003,15005,15007,15009],{"class":2115,"line":3213},[2113,15004,8558],{"class":2414},[2113,15006,179],{"class":2119},[2113,15008,8563],{"class":2133},[2113,15010,3944],{"class":2119},[2113,15012,15013,15015,15017,15019,15021,15023,15025,15027,15029],{"class":2115,"line":3236},[2113,15014,7785],{"class":2326},[2113,15016,8800],{"class":2414},[2113,15018,2153],{"class":2334},[2113,15020,8582],{"class":2414},[2113,15022,179],{"class":2119},[2113,15024,8587],{"class":2133},[2113,15026,5224],{"class":2119},[2113,15028,14893],{"class":2330},[2113,15030,2487],{"class":2119},[2113,15032,15033],{"class":2115,"line":3248},[2113,15034,2125],{"emptyLinePlaceholder":1767},[2113,15036,15037,15039,15041,15043,15045,15048,15050,15052,15054,15056,15058,15060,15062,15064],{"class":2115,"line":4899},[2113,15038,8743],{"class":2414},[2113,15040,179],{"class":2119},[2113,15042,8748],{"class":2133},[2113,15044,2423],{"class":2119},[2113,15046,15047],{"class":2149},"\"Memory consumption in JS: \"",[2113,15049,932],{"class":2119},[2113,15051,8838],{"class":2414},[2113,15053,179],{"class":2119},[2113,15055,8843],{"class":2133},[2113,15057,2423],{"class":2119},[2113,15059,8848],{"class":2330},[2113,15061,2270],{"class":2334},[2113,15063,8577],{"class":2330},[2113,15065,5818],{"class":2119},[2113,15067,15068,15070,15072,15074,15076,15079,15081,15083,15085,15087,15089,15092],{"class":2115,"line":1777},[2113,15069,8743],{"class":2414},[2113,15071,179],{"class":2119},[2113,15073,8748],{"class":2133},[2113,15075,2423],{"class":2119},[2113,15077,15078],{"class":2149},"\"Memory consumption calculated by head: \"",[2113,15080,932],{"class":2119},[2113,15082,8838],{"class":2414},[2113,15084,179],{"class":2119},[2113,15086,8843],{"class":2133},[2113,15088,2423],{"class":2119},[2113,15090,15091],{"class":2166},"432",[2113,15093,5818],{"class":2119},[2113,15095,15096,15098,15100,15102,15104,15106,15108,15110,15112,15114,15116,15118,15120,15122,15124,15126,15128],{"class":2115,"line":4931},[2113,15097,8743],{"class":2414},[2113,15099,179],{"class":2119},[2113,15101,8748],{"class":2133},[2113,15103,2423],{"class":2119},[2113,15105,8868],{"class":2149},[2113,15107,932],{"class":2119},[2113,15109,8838],{"class":2414},[2113,15111,179],{"class":2119},[2113,15113,8843],{"class":2133},[2113,15115,8879],{"class":2119},[2113,15117,8848],{"class":2330},[2113,15119,2270],{"class":2334},[2113,15121,8577],{"class":2330},[2113,15123,5709],{"class":2119},[2113,15125,2520],{"class":2334},[2113,15127,8642],{"class":2330},[2113,15129,5818],{"class":2119},[2113,15131,15132],{"class":2115,"line":4961},[2113,15133,15134],{"class":2119},"  \n",[2113,15136,15137,15139,15141,15143,15145,15147,15149,15151],{"class":2115,"line":4976},[2113,15138,8743],{"class":2414},[2113,15140,179],{"class":2119},[2113,15142,8748],{"class":2133},[2113,15144,2423],{"class":2119},[2113,15146,13315],{"class":2414},[2113,15148,179],{"class":2119},[2113,15150,13466],{"class":2133},[2113,15152,2593],{"class":2119},[12,15154,15155],{},"Nous n'oublions pas de compiler notre module en mode release et avec la target nodejs.",[12,15157,15158],{},"Le résultat est alors le suivant :",[22,15160,15161,15169],{},[25,15162,15163],{},[28,15164,15165,15167],{},[31,15166],{},[31,15168],{},[41,15170,15171,15177,15184,15191],{},[28,15172,15173,15175],{},[46,15174,9275],{},[46,15176,9278],{},[28,15178,15179,15181],{},[46,15180,9283],{},[46,15182,15183],{},"42 secondes",[28,15185,15186,15188],{},[46,15187,9291],{},[46,15189,15190],{},"450,6 Mo",[28,15192,15193,15195],{},[46,15194,9299],{},[46,15196,15197],{},"363.5 octets",[12,15199,15200],{},"Wasm permet d'améliorer la consommation mémoire. Sur mon exemple, le temps de création n'est pas très impressionnant\nmais est, je crois, lié à la génération des nombres aléatoires.",[12,15202,15203],{},"L'utilisation de WASM pourrait être une bonne alternative.",[12,15205,15206],{},"Quand utiliser WASM et quand faire un module node ? L'avantage de faire du Wasm, c'est que le module peut tourner\négalement côté navigateur. L'autre avantage est que le module n'a pas besoin d'être recompilé en cas de changement de\nplateforme (Linux, Windows, ... ; x86, ARM, ...).",[12,15208,15209],{},"D'un autre côté, un module node natif a l'avantage d'être plus simple (moins de bindings) et sera sûrement plus rapide\nà l'exécution.",[128,15211,1621],{"id":1620},[12,15213,15214],{},"Pour améliorer les performances de notre application, nous allons donc écrire un module natif en Rust avec N-API.",[12,15216,15217,15218,179],{},"Vous pouvez retrouver le code source de cet article sur mon dépôt ",[49,15219,15222],{"href":15220,"rel":15221},"https:\u002F\u002Fgogs.shadoware.org\u002FShadowareOrg\u002Fblog_compare_js_rust_wasm",[347],"Gitea",[3358,15224,15225],{},"html pre.shiki code .seHd6, html code.shiki .seHd6{--shiki-default:#C678DD}html pre.shiki code .sU0A5, html code.shiki .sU0A5{--shiki-default:#E5C07B}html pre.shiki code .sjrmR, html code.shiki .sjrmR{--shiki-default:#56B6C2}html pre.shiki code .sVbv2, html code.shiki .sVbv2{--shiki-default:#61AFEF}html pre.shiki code .sn6KH, html code.shiki .sn6KH{--shiki-default:#ABB2BF}html pre.shiki code .subq3, html code.shiki .subq3{--shiki-default:#98C379}html pre.shiki code .sV9Aq, html code.shiki .sV9Aq{--shiki-default:#7F848E;--shiki-default-font-style:italic}html pre.shiki code .s_ZVi, html code.shiki .s_ZVi{--shiki-default:#E06C75;--shiki-default-font-style:italic}html pre.shiki code .sVyAn, html code.shiki .sVyAn{--shiki-default:#E06C75}html pre.shiki code .sVC51, html code.shiki .sVC51{--shiki-default:#D19A66}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}",{"title":1646,"searchDepth":1647,"depth":1647,"links":15227},[15228,15229,15230,15231,15232,15233,15235],{"id":7759,"depth":1647,"text":7760},{"id":7769,"depth":1647,"text":7770},{"id":9335,"depth":1647,"text":9336},{"id":9522,"depth":1647,"text":9523},{"id":11522,"depth":1647,"text":11523},{"id":13515,"depth":1647,"text":15234},"Conclusion - Wasm",{"id":1620,"depth":1647,"text":1621},{"type":9,"value":15237},[15238,15240,15242],[128,15239,7760],{"id":7759},[12,15241,7763],{},[12,15243,7766],{},{"planet":1767},{"title":7754,"description":1646},"woodstock_rust","posts\u002FWoodstock\u002F2023-05-10_woodstock_rust",[1683,1773,1774,3510,3511],"RXnKX2dJmrksM5sZOeN70MXCyeSAMu1H3Jj5D3fie28",{"id":15251,"title":15252,"author":7,"body":15253,"category":2057,"categorySlug":2058,"date":17355,"description":1646,"excerpt":17356,"extension":1764,"location":1765,"meta":17375,"navigation":1767,"path":79,"published":1767,"seo":17376,"slug":17377,"stem":17378,"tags":17379,"timeToRead":4931,"__hash__":17380},"posts\u002Fposts\u002FWoodstock\u002F2021-04-18_woodstock_protocol_language_sauvegarde.md","Woodstock Backup - Protocol et Language de sauvegarde",{"type":9,"value":15254,"toc":17341},[15255,15262,15273,15280,15283,15286,15289,15292,15295,15298,15302,15305,15319,15322,15330,15333,15336,15352,15356,15359,15373,15379,15382,15385,15402,15405,15416,15422,15425,15428,15432,15438,15441,15465,15468,15471,15474,15477,15480,15484,15487,15492,15495,15506,15509,15513,15516,15519,15538,15541,15549,15552,15558,15582,15585,15589,15592,15612,15615,15618,15621,15624,15628,15631,15634,15637,15640,15644,15663,15689,15695,15709,15722,15737,15747,15965,15972,16967,16975,16984,16987,16998,17004,17008,17020,17029,17032,17049,17052,17055,17135,17138,17141,17144,17202,17214,17217,17247,17250,17253,17258,17262,17285,17289,17292,17295,17298,17301,17318,17321,17324,17327,17338],[12,15256,15257],{},[121,15258],{"alt":15252,"src":15259,"className":15260,"width":15261},"\u002FWoodstock\u002Fsplash_proto.png",[126],"400px,",[1886,15263,15264,15267,15270],{},[12,15265,15266],{},"Note de 2023 : Ce billet a été écrit en avril 2021, il y a deux ans, mais n'a jamais été publié. Le temps passe vite.",[12,15268,15269],{},"Depuis lors, j'ai travaillé sur d'autres projets, mais aussi sur ce logiciel de sauvegarde. En progressant dans le\ndéveloppement du projet, j'ai pu optimiser les performances et me faire une opinion sur le choix que j'ai finalement\nfait, que je partagerai à la fin de l'article.",[12,15271,15272],{},"Je mettrai à jour mes conclusions en fonction de mes avancées sur le sujet.",[12,15274,15275,15276,15279],{},"Dans notre ",[49,15277,15278],{"href":65},"précédent article",", nous avons vu comment dédupliquer les fichiers dans un pool sans\nutiliser btrfs, un système de fichier permettant la déduplication. Pour pouvoir copier les fichiers dans notre pool,\nle logiciel doit savoir comment écrire les fichiers de manière appropriée.",[12,15281,15282],{},"Si nous voulons continuer à utiliser rsync, nous pourrions envisager de créer un système de fichiers FUSE (Filesystem in\nUser Space) pour faire le pont entre rsync et notre pool de stockage. Cela nous permettrait de continuer à utiliser\nrsync pour les sauvegardes et les restaurations. Cependant, cela nécessiterait la mise en œuvre d'un système de fichiers\ncomplet, y compris la lecture, l'écriture, la déduplication, etc., pour un usage très spécifique de rsync. En fin de\ncompte, le système de fichiers ne serait utilisé que pour la sauvegarde (ajout de nouveaux éléments) et la restauration\n(lecture d'une sauvegarde). Il n'y aurait pas d'écriture aléatoire dans le système de fichiers, ni dans un fichier même.",[12,15284,15285],{},"Après avoir rapidement examiné cette possibilité, je l'ai écartée car elle me semble être une solution trop lourde pour\nmes besoins.",[12,15287,15288],{},"Par conséquent, nous devrons nous passer de rsync. Je n'ai pas trouvé de bibliothèque permettant d'implémenter\nfacilement une synchronisation sur le protocole rsync. Nous devrons donc écrire notre propre protocole de\nsynchronisation de fichiers.",[12,15290,15291],{},"Lorsque nous développerons ce protocole, nous devrons nous assurer de la sécurité. En effet, rsync est capable de\nsynchroniser des fichiers via un tunnel ssh. Notre protocole devra empêcher les attaquants d'écouter ce qui est\nsynchronisé et d'accéder aux fichiers sans autorisation.",[12,15293,15294],{},"Dans la suite de cet article, nous étudierons comment écrire notre protocole de synchronisation et nous comparerons les\nperformances de sauvegarde entre le langage JavaScript (actuellement utilisé par le serveur de sauvegarde) et le\nlangage C++.",[12,15296,15297],{},"Comme toujours, n'hésitez pas à me faire part de vos commentaires et de vos avis (système de commentaires en bas de la\npage).",[128,15299,15301],{"id":15300},"création-de-la-communication-périphériqueserveur","Création de la communication périphérique\u002Fserveur",[12,15303,15304],{},"Nous allons aborder la question de la communication entre le client et le serveur. Afin d'éviter toute confusion entre\nle client et le serveur dans le sens réseau et le client et le serveur dans le sens applicatif, nous allons désigner :",[506,15306,15307,15313],{},[370,15308,15309,15312],{},[184,15310,15311],{},"Périphérique"," pour le client à sauvegarder",[370,15314,15315,15318],{},[184,15316,15317],{},"Serveur de sauvegarde"," pour le serveur de sauvegarde.",[12,15320,15321],{},"En ce qui concerne le protocole de communication, je pense utiliser une HTTP2 plutôt que d'ouvrir un socket TCP et\ntravailler directement dessus. L'utilisation d'un protocole de haut niveau me permettra de m'appuyer sur des\nbibliothèques existantes et déjà largement utilisées. De plus, je n'aurai pas à gérer :",[506,15323,15324,15327],{},[370,15325,15326],{},"la compression",[370,15328,15329],{},"la prise en charge de TLS",[12,15331,15332],{},"En raison de la nature d'HTTP2, l'initiateur de la connexion est important : il faut déterminer qui est le serveur HTTP\net qui est le client HTTP.",[12,15334,15335],{},"Dans la suite de cet article, nous allons essayer de répondre aux deux questions suivantes :",[506,15337,15338,15341],{},[370,15339,15340],{},"Comment effectuer la communication entre le serveur de sauvegarde et le périphérique à sauvegarder ?",[370,15342,15343,15344],{},"Qui doit initier la connexion ?\n",[506,15345,15346,15349],{},[370,15347,15348],{},"Est-ce que le serveur de sauvegarde doit contacter le périphérique ?",[370,15350,15351],{},"Ou est-ce que le périphérique doit contacter le serveur de sauvegarde ?",[133,15353,15355],{"id":15354},"le-serveur-de-sauvegarde-initie-la-connection","Le serveur de sauvegarde initie la connection",[12,15357,15358],{},"Nous allons parler de deux éléments clés pour la sauvegarde: le manifeste et les chunks.",[506,15360,15361,15367],{},[370,15362,15363,15366],{},[184,15364,15365],{},"Manifeste",": la liste des fichiers à sauvegarder avec le nom du fichier, les attributs, les acl, ... un hash du\nfichier et un hash des différents morceaux qui le constituent.",[370,15368,15369,15372],{},[184,15370,15371],{},"Chunk",": un morceau de fichier (un fichier peut être découpé en plusieurs morceaux de taille donnée).",[239,15374,15375],{},[121,15376],{"alt":15377,"src":15378},"Le serveur initie la connection","https:\u002F\u002Fwww.plantuml.com\u002Fplantuml\u002Fpng\u002FZP1DJWCn38NtEOKvm5oWYweIOe740i49hEILM2JErFcvqvnZBeO6LAcqAcBZZTydVtw7sjXQpyazj8WCojnWmiwzmmQCfZszhel97BTvwjZHiqeJJbAvIL4AeCHKkGzyi0NyXRwmUcHekwNODndSSCMuLCfCS-b6FlAfWuxYey2g-nsaQThJp-KTFUaeGW6LCgiSKLibxbItTRTBmpFnMFBCnbAB4W_upIx0L62urBp_szkw-3wlYvrhgUHZryz_Ydvd7JGu5t2lZ0Cqz9o-0000",[12,15380,15381],{},"Lorsqu'une sauvegarde doit démarrer, le serveur contacte le périphérique et initie la sauvegarde. Le périphérique\nenvoie ensuite les différents fichiers au serveur qui peut alors demander au périphérique les morceaux de fichiers\nqui lui manquent.",[12,15383,15384],{},"Voici des exemples de logiciels de sauvegarde où le périphérique initie la connexion:",[506,15386,15387,15394],{},[370,15388,15389,15393],{},[49,15390,1013],{"href":15391,"rel":15392},"https:\u002F\u002Fbackuppc.github.io\u002Fbackuppc\u002F",[347]," - basé sur un rsync modifié pour gérer la partie pool",[370,15395,15396,15401],{},[49,15397,15400],{"href":15398,"rel":15399},"https:\u002F\u002Fwww.urbackup.org\u002F",[347],"UrBackup"," - basé sur son propre client de sauvegarde à installer sur le périphérique",[12,15403,15404],{},"Lorsque le serveur initie la connexion sur le périphérique:",[506,15406,15407,15410,15413],{},[370,15408,15409],{},"Le serveur décide quand faire la sauvegarde (en fonction de quand la dernière sauvegarde a été construite, des heures\nde travail, ...).",[370,15411,15412],{},"Le serveur a besoin de pouvoir accéder aux machines, mais les périphériques n'ont pas besoin d'avoir un accès direct\nau serveur de sauvegarde. Cela permet de ne pas compromettre le serveur de sauvegarde en cas de compromission d'un\npériphérique.",[370,15414,15415],{},"Le serveur doit détecter la présence du périphérique (si ce dernier n'est pas toujours sur le réseau) et donc faire\nun ping régulier, par exemple.",[12,15417,15418,15419,15421],{},"Il faudra prévoir un ",[184,15420,319],{}," pour vérifier la présence des machines sur le réseau. Le serveur doit rester éveillé\n(et donc être actif) et ne peut pas juste attendre l'arrivée d'un périphérique.",[12,15423,15424],{},"Il doit y avoir un serveur (le serveur ne peut pas être juste un espace de stockage, comme un S3). Le serveur décide à\npartir du nouveau manifeste du client quels sont les fichiers qui ont changé et demande au client de lui envoyer ceux\nqui lui manquent.",[12,15426,15427],{},"Le serveur décide d'organiser son pool de sauvegarde comme il le souhaite, ainsi que le nombre de sauvegardes en\nparallèle. Il peut bloquer les sauvegardes si le pool est plein.",[133,15429,15431],{"id":15430},"le-périphérique-initie-la-connection","Le périphérique initie la connection",[239,15433,15434],{},[121,15435],{"alt":15436,"src":15437},"Le périphérique initie la connexion","https:\u002F\u002Fwww.plantuml.com\u002Fplantuml\u002Fpng\u002FZP3DJGD138NlKuKfW0PoG94G1sv8q80ryvArD3yRZxrAIMmTsnWQ52c00ENYW-tt_3tlkx6QbnpkR4815JQeS0WlsweoJwEU77J_GA_G1RgZvoecdAM3CbDdNt1aJGY1eyd2XijgoAtDD3TNYXCFbuF4IQ3z5_VldHqzjjfDFPgqIicfS9K3klq3zhQjULlZn7f4GRKXIz0gqAlyfjDbPcNfhH8lY2Fcfy_shlWQ-6-KfxeqeOHEfMa4-vdIDVwWEJbNwxTPNDWxiLCVOzU0ca98-FdoFMhoaZy0",[12,15439,15440],{},"Voici quelques exemples de logiciels de sauvegarde qui peuvent être déclenchés depuis le périphérique:",[506,15442,15443,15451,15457],{},[370,15444,15445,15450],{},[49,15446,15449],{"href":15447,"rel":15448},"https:\u002F\u002Fborgbackup.readthedocs.io\u002Fen\u002Fstable\u002Findex.html",[347],"Borg"," (avec un lieu de stockage)",[370,15452,15453,15450],{},[49,15454,1007],{"href":15455,"rel":15456},"https:\u002F\u002Frestic.net\u002F",[347],[370,15458,15459,15464],{},[49,15460,15463],{"href":15461,"rel":15462},"https:\u002F\u002Fburp.grke.org\u002F",[347],"Burp"," (avec un serveur qui centralise les sauvegardes)",[12,15466,15467],{},"Le périphérique sait quand il est allumé et connecté au réseau. S'il y a une erreur de sauvegarde (par exemple, si le\nserveur de stockage n'est pas disponible), il peut réessayer plus tard. Il n'est donc pas nécessaire de planifier des\nsauvegardes sur le serveur, mais il est nécessaire d'avoir un plan de sauvegarde pour chaque périphérique.",[12,15469,15470],{},"Le périphérique doit être capable de se connecter au serveur, donc il est important d'ouvrir le flux de données du\npériphérique vers le serveur (par exemple, si le périphérique est un ordinateur hors réseau interne). Cela peut être\nrésolu en créant un VPN.",[12,15472,15473],{},"Lorsque la sauvegarde est initialisée, le serveur peut renvoyer des informations au périphérique pour indiquer qu'il\nrefuse la sauvegarde (pool plein, ...). Le périphérique devra alors réessayer plus tard.",[12,15475,15476],{},"Si un périphérique est compromis, toutes les sauvegardes de ce périphérique sont en danger (il existe des solutions\npour bloquer les sauvegardes en lecture\u002Fécriture). Si le pool de sauvegarde  est mutualisé entre plusieurs machines,\ncela peut compromettre l'ensemble du pool.",[12,15478,15479],{},"Le serveur reçoit un manifeste et une liste de chunks. Si nous souhaitons mutualiser le pool de sauvegarde, nous ne\npouvons pas laisser le client accéder à l'intégralité du pool. Nous devons donc disposer d'un serveur qui gère les\nchunks et vérifie que les fichiers reçus correspondent bien aux sauvegardes effectuées. Il est important de s'assurer\nque les clients n'envoient pas de chunks qui ne sont pas relatifs à une sauvegarde ou qui ont un sha incorrect pour\néviter que les fichiers soient mal rangés. Il faut aussi bloquer la modification des anciennes sauvegardes, et aussi\nconditionner la lecture de ces derniers.",[133,15481,15483],{"id":15482},"choix","Choix",[12,15485,15486],{},"Personnellement, j'ai une préférence pour l'initialisation de la sauvegarde par le serveur. Dans mon projet actuel,\nj'ai déjà prévu le code pour gérer la file d'attente, lancer rsync, ainsi que le planificateur. Il serait facile de\nremplacer ce code par celui de mon nouveau serveur.",[1886,15488,15489],{},[12,15490,15491],{},"Note de 2023: En avançant dans la réécriture, les dépendances ont été mises à jour et une grande partie du code a\nété modifié. Par conséquent, remplacer l'appel à rsync par un appel au nouveau système de sauvegarde n'a pas été\naussi simple que prévu.",[12,15493,15494],{},"Il y a plusieurs raisons pour lesquelles je penche vers le contrôle du déclenchement par le serveur :",[506,15496,15497,15500,15503],{},[370,15498,15499],{},"Le serveur sera le propriétaire du pool et des sauvegardes.",[370,15501,15502],{},"Le serveur décide du cycle de vie des sauvegardes (nombre de sauvegarde, durée de vie, ...)",[370,15504,15505],{},"Le serveur peut fusionner les sauvegardes de plusieurs périphériques dans le pool, donc il doit contrôler tout ce qui\ny entre.",[12,15507,15508],{},"Je tiens à souligner que la version finale sera terminée, tous les choix ici présent peuvent avoir été remis en\nquestion. En informatique, faire et défaire, c'est avancer.",[128,15510,15512],{"id":15511},"cinematique-dappel","Cinematique d'appel",[12,15514,15515],{},"Dans cet article, nous allons examiner la cinématique d'appel entre les requêtes et comment elle peut être utilisée\npour améliorer la sauvegarde de contenu en fonction de nos besoins.",[12,15517,15518],{},"Pour sauvegarder le contenu d'un périphérique, celui-ci doit suivre les étapes suivantes :",[506,15520,15521,15535],{},[370,15522,15523,15524],{},"Parcourir l'ensemble de ses fichiers et pour chaque fichier :\n",[506,15525,15526,15529,15532],{},[370,15527,15528],{},"Récupérer les attributs du fichier, tels que le nom, la date de création, de modification, les droits, etc.",[370,15530,15531],{},"Calculer un hash pour déterminer si le fichier est modifié et quelle partie du fichier a été modifiée.",[370,15533,15534],{},"Envoyer cette liste de fichiers (manifest) au serveur de sauvegarde.",[370,15536,15537],{},"Envoyer le contenu nouveau ou modifié des fichiers, chunk par chunk, vers le serveur.",[12,15539,15540],{},"Le processus de sauvegarde peut être optimisé de la manière suivante :",[506,15542,15543,15546],{},[370,15544,15545],{},"Le périphérique peut recevoir la dernière sauvegarde qu'il a effectuée et la comparer avec ce qu'il a de son côté\npour n'envoyer que le différentiel.",[370,15547,15548],{},"Il n'est pas nécessaire de calculer le hash du fichier entier s'il n'a jamais été sauvegardé. Ce hash peut être\ncalculé lors de la récupération des données, ce qui évite de lire deux fois le fichier.",[12,15550,15551],{},"Voici la séquence utilisée pour la sauvegade:",[239,15553,15554],{},[121,15555],{"alt":15556,"src":15557},"Sequence de sauvegarde","https:\u002F\u002Fwww.plantuml.com\u002Fplantuml\u002Fpng\u002FVT5DJiCm40NWlK_nsC6YGVnBNLH5Y1qB12SOsLCo4DkfxScPo3boCTIW_b6HJPJ5wEczpyjSA1NrZJahDNk6fy99o9XtJXqdp1Pu7VeaRRtvhfNdkAhmgANcK6Gbbeh4C75zNU4vT57W53Q6ma7X60t1SGeoV2T69ktuWv9ZdEUIcFp5L86R2Y-I2wFXZ9NOMZXbq6VKClJvqaSdAzdyPMtR97xeio5RfAF2VyACQM9iqKPDi3MjzI3H79zYDblWjzGSjxjLdE4fo8fpoI15tbnesW_Xu8nn_6-1_Svj-5s5f-XRIYGv1b37TkV5HrmdxKyu41LRa0dI-mflGs-r7VeqlkWupDOQllwtc_1vEFGPO_OW4nQZrPA3Kz_y0000",[367,15559,15560,15563,15570,15576],{},[370,15561,15562],{},"Le serveur doit commencer par s'authentifier auprès du client pour garantir que la communication se fait en toute\nsécurité. L'authentification peut se faire via un échange de clés publique\u002Fprivée, avec la connaissance d'un mot de\npasse commun associé au device. L'objectif est que le client puisse s'assurer qu'il communique avec le bon serveur.",[370,15564,15565,15566,15569],{},"Si une sauvegarde a déjà eu lieu par le passé, le serveur peut envoyer au client le contenu de la dernière\nsauvegarde. Cette liste comprend les fichiers avec leurs attributs et leur hash. Cette étape permet au client de\nsavoir quels fichiers ont été ajoutés, supprimés ou modifiés depuis la dernière sauvegarde.",[15567,15568],"br",{},"Par rapport à rsync, le serveur n'a pas besoin de recalculer cette liste.",[370,15571,15572,15573,15575],{},"Le client parcourt les différents dossiers demandés et génère une nouvelle liste. Il peut se contenter de n'envoyer\nque les fichiers qui ont changé depuis la dernière fois.",[15567,15574],{},"Le serveur stocke cela pour préparer la nouvelle sauvegarde. Il est possible d'utiliser un système de journal pour\nécrire les modifications retournées par le client. Ces modifications ne sont persistées dans le fichier final qu'une\nfois la sauvegarde terminée.",[370,15577,15578,15579,15581],{},"Le serveur utilise la liste reçue pour demander au client de lui renvoyer les fichiers modifiés. Si le client a\ncalculé le hash des chunks (morceaux de fichier), le serveur peut ne demander que les morceaux qui ont été modifiés\net pas l'intégralité du fichier.",[15567,15580],{},"Il est important de trouver une taille idéale pour les chunks. Si la taille est trop petite, cela risque de\nmultiplier la quantité de fichiers dans le pool sans aucun bénéfice. Si elle est trop grande, cela peut bloquer le\ntransfert intégral du fichier sur le serveur.",[12,15583,15584],{},"À l'avenir, il pourrait être envisageable d'avoir différents types de chunks pour déterminer ce qui doit être\nrenvoyé dans les transferts réseaux et ceux pour le stockage du pool. Cependant, cela complexifie le stockage.",[128,15586,15588],{"id":15587},"choix-du-protocol","Choix du protocol",[12,15590,15591],{},"Pour envoyer des informations en flux continu du périphérique au serveur et vice versa (un stream pour le manifeste,\nun autre pour les fichiers, un autre pour les logs), le choix de protocole est crucial. Voici quelques options que nous\nallons considérer pour notre shortlist :",[506,15593,15594,15600,15606],{},[370,15595,15596,15599],{},[184,15597,15598],{},"REST",": Protocole simple et facilement utilisable pour transférer des fichiers via upload ou download. Pour le\nstreaming, nous pouvons également envisager l'utilisation de Websocket.",[370,15601,15602,15605],{},[184,15603,15604],{},"gRPC",": Protocole permettant d'utiliser HTTP\u002F2 pour faire des requêtes avec du streaming bidirectionnel.",[370,15607,15608,15611],{},[184,15609,15610],{},"Protocole maison à base de socket",": Maîtrise complète du flux (comme rsync par exemple).",[12,15613,15614],{},"Comme vu précédement, les protocoles basés sur HTTP, comme REST et gRPC, offrent des avantages tels que l'utilisation\nd'un protocole de haut niveau permettant de bénéficier de TLS, de la compression, de la mutualisation de la connexion,\netc.",[12,15616,15617],{},"Le protocole maison, même s'il permettrait de bonnes performances, nécessite une plus grande complexité de\ndéveloppement, mais aussi un plus grand risque de bugs ou de failles de sécurité. Il est donc écarté de notre list\npour le moment, mais nous pourrions l'envisager dans une future refonte si les performances sont vraiment mauvaises.",[12,15619,15620],{},"Pour déterminer le protocole le plus performant pour nos besoins, nous allons comparer les protocoles REST s\u002F HTTP\u002F2 et\ngRPC.",[12,15622,15623],{},"Il est important de noter qu'il est crucial de choisir le langage ou le protocole le plus adapté à notre besoin et ne\npas partir sur des \"a priori\". Souvent, on choisit un langage ou un protocole parce qu'on le connaît bien ou parce\nqu'on l'estime meilleur dans certaines situations. Mais chaque besoin étant différent, l'utilisation dans un contexte\nne signifie pas qu'un langage ou un protocole est le meilleur dans tous les contextes.",[128,15625,15627],{"id":15626},"quel-langage-choisir","Quel langage choisir",[12,15629,15630],{},"Le client de sauvegarde doit être compatible avec Windows, Linux et MacOS.",[12,15632,15633],{},"Ma première approche pour développer un logiciel performant consiste à utiliser le language C++ pour le périphérique\net la partie synchronisation et gestion du pool pour serveur, tout en gardant Node.JS pour l'orchestration.",[12,15635,15636],{},"Comme une partie du serveur est développée en Node.JS, je vais également tester les performances dans ce langage. Si\nj'obtiens des performances similaires, le développement en Node.JS pourrait être plus simple (un seul language pour\ntout et moins de complexité à gérer pour le multithreading).",[12,15638,15639],{},"Si je décide de développer en C++, j'utiliserai Qt pour l'interface graphique, car je le connais bien.",[128,15641,15643],{"id":15642},"lécriture","L'écriture",[12,15645,15646,15648,15649,15652,15653,15656,15657,15659,15660,15662],{},[184,15647,15604],{}," est un framework moderne pour la création de services distants. Il est basé sur ",[184,15650,15651],{},"HTTP\u002F2",", qui offre des\naméliorations de performances significatives par rapport à ",[184,15654,15655],{},"HTTP\u002F1.1",". Pour le protocole ",[184,15658,15598],{},", il existe des\nlibrairies permettant de créer des serveurs et clients ",[184,15661,15651],{}," en C++, mais à mon grand désarroi c'est plus difficile\nque prévu.",[506,15664,15665,15673,15681],{},[370,15666,15667,15672],{},[49,15668,15671],{"href":15669,"rel":15670},"https:\u002F\u002Fgithub.com\u002Fmicrosoft\u002Fcpprestsdk",[347],"cpprestsdk",": Support HTTPS mais pas HTTP\u002F2 (en 2023, il n'évoluera plus),",[370,15674,15675,15680],{},[49,15676,15679],{"href":15677,"rel":15678},"https:\u002F\u002Fnghttp2.org\u002F",[347],"nghttp2",": Librairie très bas niveau (trop bas niveau),",[370,15682,15683,15688],{},[49,15684,15687],{"href":15685,"rel":15686},"https:\u002F\u002Fnghttp2.org\u002Fdocumentation\u002Flibnghttp2_asio.html",[347],"libnghttp2_asio",": Librairie plus haut niveau, possède une\ndépendance sur boost. J'ai également l'impression que cette librairie ne permet pas de faire tout ce qu'on veut en\nHTTP\u002F2 (par exemple le streaming d'un fichier n'est pas quelque chose de facile à écrire, contrairement à l'allocation\ncomplète en mémoire de la réponse à envoyer).",[12,15690,15691,15692,15694],{},"Pour le protocole ",[184,15693,15604],{},", nous avons:",[506,15696,15697],{},[370,15698,15699,4429,15704,15706,15707,179],{},[49,15700,15703],{"href":15701,"rel":15702},"https:\u002F\u002Fgrpc.io\u002Fdocs\u002Flanguages\u002Fcpp\u002Fquickstart\u002F",[347],"grpccpp",[184,15705,15604],{}," est de basé basé sur ",[184,15708,15651],{},[12,15710,15711,15712,15715,15716,15719,15720,179],{},"Coté ",[184,15713,15714],{},"Node.JS",", j'utilise ",[184,15717,15718],{},"Nest.JS"," pour la partie serveur, je vais donc m'appuyer sur les librairies suivante pour le\nprotocol ",[184,15721,15604],{},[506,15723,15724,15730],{},[370,15725,15726],{},[49,15727,1776],{"href":15728,"rel":15729},"https:\u002F\u002Fwww.npmjs.com\u002Fpackage\u002Fgrpc",[347],[370,15731,15732],{},[49,15733,15736],{"href":15734,"rel":15735},"https:\u002F\u002Fdocs.nestjs.com\u002Fmicroservices\u002Fgrpc",[347],"@grpc\u002Fproto-loader",[12,15738,15739,15740,15742,15743,15746],{},"L'utilisation d'",[184,15741,15651],{}," avec Axios (utilisé par Nest.JS) peut se faire en utilisant ",[184,15744,15745],{},"http2-wrapper",":",[2105,15748,15752],{"className":15749,"code":15750,"language":15751,"meta":1646,"style":1646},"language-ts shiki shiki-themes one-dark-pro","import * as http2 from \"http2-wrapper\";\nimport { AxiosRequestConfig } from \"axios\";\n\nconst data = ProtoGetChunkRequest.encode(request).finish();\nreturn this.httpService\n  .request\u003CReadable>({\n    method: \"post\",\n    url: `https:\u002F\u002F${this.hostToBackup}:3000\u002Fget-chunk`,\n    data,\n    transport: http2,\n    responseType: \"stream\",\n  } as AxiosRequestConfig)\n  .pipe(map((response) => response.data));\n","ts",[184,15753,15754,15774,15793,15797,15826,15838,15853,15865,15894,15900,15912,15924,15936],{"__ignoreMap":1646},[2113,15755,15756,15759,15761,15763,15766,15769,15772],{"class":2115,"line":2116},[2113,15757,15758],{"class":2326},"import",[2113,15760,2671],{"class":2166},[2113,15762,11429],{"class":2326},[2113,15764,15765],{"class":2330}," http2",[2113,15767,15768],{"class":2326}," from",[2113,15770,15771],{"class":2149}," \"http2-wrapper\"",[2113,15773,2487],{"class":2119},[2113,15775,15776,15778,15780,15783,15785,15788,15791],{"class":2115,"line":1647},[2113,15777,15758],{"class":2326},[2113,15779,7616],{"class":2119},[2113,15781,15782],{"class":2330},"AxiosRequestConfig",[2113,15784,7840],{"class":2119},[2113,15786,15787],{"class":2326},"from",[2113,15789,15790],{"class":2149}," \"axios\"",[2113,15792,2487],{"class":2119},[2113,15794,15795],{"class":2115,"line":1652},[2113,15796,2125],{"emptyLinePlaceholder":1767},[2113,15798,15799,15801,15804,15806,15809,15811,15814,15816,15819,15821,15824],{"class":2115,"line":2185},[2113,15800,7785],{"class":2326},[2113,15802,15803],{"class":2414}," data",[2113,15805,2153],{"class":2334},[2113,15807,15808],{"class":2414}," ProtoGetChunkRequest",[2113,15810,179],{"class":2119},[2113,15812,15813],{"class":2133},"encode",[2113,15815,2423],{"class":2119},[2113,15817,15818],{"class":2330},"request",[2113,15820,3938],{"class":2119},[2113,15822,15823],{"class":2133},"finish",[2113,15825,3944],{"class":2119},[2113,15827,15828,15830,15833,15835],{"class":2115,"line":2230},[2113,15829,3205],{"class":2326},[2113,15831,15832],{"class":2414}," this",[2113,15834,179],{"class":2119},[2113,15836,15837],{"class":2330},"httpService\n",[2113,15839,15840,15843,15845,15847,15850],{"class":2115,"line":2235},[2113,15841,15842],{"class":2119},"  .",[2113,15844,15818],{"class":2133},[2113,15846,3109],{"class":2119},[2113,15848,15849],{"class":2414},"Readable",[2113,15851,15852],{"class":2119},">({\n",[2113,15854,15855,15858,15860,15863],{"class":2115,"line":2241},[2113,15856,15857],{"class":2330},"    method",[2113,15859,4429],{"class":2119},[2113,15861,15862],{"class":2149},"\"post\"",[2113,15864,4706],{"class":2119},[2113,15866,15867,15870,15872,15875,15878,15881,15883,15886,15889,15892],{"class":2115,"line":2246},[2113,15868,15869],{"class":2330},"    url",[2113,15871,4429],{"class":2119},[2113,15873,15874],{"class":2149},"`https:\u002F\u002F",[2113,15876,15877],{"class":2326},"${",[2113,15879,15880],{"class":2414},"this",[2113,15882,179],{"class":2119},[2113,15884,15885],{"class":2330},"hostToBackup",[2113,15887,15888],{"class":2326},"}",[2113,15890,15891],{"class":2149},":3000\u002Fget-chunk`",[2113,15893,4706],{"class":2119},[2113,15895,15896,15898],{"class":2115,"line":2464},[2113,15897,9822],{"class":2330},[2113,15899,4706],{"class":2119},[2113,15901,15902,15905,15907,15910],{"class":2115,"line":2085},[2113,15903,15904],{"class":2330},"    transport",[2113,15906,4429],{"class":2119},[2113,15908,15909],{"class":2330},"http2",[2113,15911,4706],{"class":2119},[2113,15913,15914,15917,15919,15922],{"class":2115,"line":2514},[2113,15915,15916],{"class":2330},"    responseType",[2113,15918,4429],{"class":2119},[2113,15920,15921],{"class":2149},"\"stream\"",[2113,15923,4706],{"class":2119},[2113,15925,15926,15929,15931,15934],{"class":2115,"line":2533},[2113,15927,15928],{"class":2119},"  } ",[2113,15930,6242],{"class":2326},[2113,15932,15933],{"class":2414}," AxiosRequestConfig",[2113,15935,4660],{"class":2119},[2113,15937,15938,15940,15943,15945,15947,15949,15952,15954,15956,15959,15961,15963],{"class":2115,"line":2556},[2113,15939,15842],{"class":2119},[2113,15941,15942],{"class":2133},"pipe",[2113,15944,2423],{"class":2119},[2113,15946,6644],{"class":2133},[2113,15948,8879],{"class":2119},[2113,15950,15951],{"class":2429},"response",[2113,15953,5709],{"class":2119},[2113,15955,8063],{"class":2326},[2113,15957,15958],{"class":2414}," response",[2113,15960,179],{"class":2119},[2113,15962,10154],{"class":2330},[2113,15964,5818],{"class":2119},[12,15966,15967,15968,15971],{},"Nous allons écrire un fichier de description ",[184,15969,15970],{},"protobuf"," (qui pourra être le même entre la version C++ et Node.JS). Le\nfichier va décrire chaque élément du fichier manifest, ainsi que les appels RPC entre le périphérique et le serveur.",[2105,15973,15976],{"className":15974,"code":15975,"language":15970,"meta":1646,"style":1646},"language-protobuf shiki shiki-themes one-dark-pro","syntax = \"proto3\";\n\npackage woodstock;\n\nenum StatusCode {\n  Ok = 0;\n  Failed = 1;\n}\n\nmessage FileManifest {\n  message FileManifestStat {\n    int32 ownerId = 1;\n    int32 groupId = 2;\n    int64 size = 3;\n    int64 lastRead = 4;\n    int64 lastModified = 5;\n    int64 created = 6;\n    int32 mode = 7;\n  }\n\n  bytes path = 1;\n  FileManifestStat stats = 2;\n  repeated bytes chunks = 3;\n  bytes sha256 = 4;\n}\n\nmessage FileManifestJournalEntry {\n  enum EntryType {\n    ADD = 0;\n    MODIFY = 1;\n    REMOVE = 2;\n  }\n\n  oneof entry {\n    FileManifest manifest = 1;\n    bytes path = 2;\n  }\n  EntryType type = 3;\n}\n\nmessage BackupConfiguration {\n    message Share {\n        string name = 1;\n        repeated string includes = 2;\n        repeated string excludes = 3;\n        string pathPrefix = 4;\n    }\n\n    message Task {\n        string command = 1;\n        repeated Share shares = 2;\n        repeated string includes = 3;\n        repeated string excludes = 4;\n    }\n\n    message Operations {\n        repeated Task tasks = 1;\n        repeated Task finalizedTasks = 2;\n    }\n\n    Operations operations = 1;\n}\n\nmessage FileChunk {\n  bytes data = 1;\n}\n\nmessage PrepareBackupRequest {\n  BackupConfiguration configuration = 1;\n  uint32 lastBackupNumber = 2;\n  uint32 newBackupNumber = 3;\n}\n\nmessage PrepareBackupReply {\n  StatusCode code = 1;\n  bool needRefreshCache = 2;\n}\n\nmessage RefreshCacheReply {\n  StatusCode code = 1;\n}\n\nmessage LaunchBackupRequest {\n  uint32 backupNumber = 1;\n}\n\nmessage GetChunkRequest {\n  bytes filename = 1;\n  uint64 position = 2;\n  uint64 size = 3;\n  bytes sha256 = 4;\n}\n\nservice WoodstockpériphériqueService {\n  rpc PrepareBackup(PrepareBackupRequest) returns (PrepareBackupReply) {}\n\n  rpc RefreshCache(stream FileManifest) returns (RefreshCacheReply) {}\n\n  rpc LaunchBackup(LaunchBackupRequest) returns (stream FileManifestJournalEntry) {}\n\n  rpc GetChunk(GetChunkRequest) returns (stream FileChunk) {}\n}\n",[184,15977,15978,15990,15994,16004,16008,16018,16029,16040,16044,16048,16058,16068,16082,16095,16109,16122,16136,16150,16163,16167,16171,16184,16197,16214,16226,16230,16234,16243,16253,16264,16275,16286,16290,16294,16304,16318,16331,16335,16349,16353,16357,16366,16376,16390,16407,16422,16435,16439,16443,16452,16465,16479,16493,16507,16511,16515,16524,16539,16554,16558,16562,16576,16580,16584,16593,16605,16609,16613,16622,16636,16650,16663,16667,16671,16680,16694,16708,16712,16716,16725,16737,16741,16745,16754,16767,16771,16775,16784,16797,16811,16823,16835,16839,16843,16854,16879,16883,16907,16911,16935,16939,16963],{"__ignoreMap":1646},[2113,15979,15980,15983,15985,15988],{"class":2115,"line":2116},[2113,15981,15982],{"class":2326},"syntax",[2113,15984,2153],{"class":2334},[2113,15986,15987],{"class":2149}," \"proto3\"",[2113,15989,2487],{"class":2119},[2113,15991,15992],{"class":2115,"line":1647},[2113,15993,2125],{"emptyLinePlaceholder":1767},[2113,15995,15996,15999,16002],{"class":2115,"line":1652},[2113,15997,15998],{"class":2326},"package",[2113,16000,16001],{"class":2149}," woodstock",[2113,16003,2487],{"class":2119},[2113,16005,16006],{"class":2115,"line":2185},[2113,16007,2125],{"emptyLinePlaceholder":1767},[2113,16009,16010,16013,16016],{"class":2115,"line":2230},[2113,16011,16012],{"class":2326},"enum",[2113,16014,16015],{"class":2414}," StatusCode",[2113,16017,2647],{"class":2119},[2113,16019,16020,16023,16025,16027],{"class":2115,"line":2235},[2113,16021,16022],{"class":2330},"  Ok",[2113,16024,2153],{"class":2334},[2113,16026,3208],{"class":2166},[2113,16028,2487],{"class":2119},[2113,16030,16031,16034,16036,16038],{"class":2115,"line":2241},[2113,16032,16033],{"class":2330},"  Failed",[2113,16035,2153],{"class":2334},[2113,16037,2850],{"class":2166},[2113,16039,2487],{"class":2119},[2113,16041,16042],{"class":2115,"line":2246},[2113,16043,2599],{"class":2119},[2113,16045,16046],{"class":2115,"line":2464},[2113,16047,2125],{"emptyLinePlaceholder":1767},[2113,16049,16050,16053,16056],{"class":2115,"line":2085},[2113,16051,16052],{"class":2326},"message",[2113,16054,16055],{"class":2414}," FileManifest",[2113,16057,2647],{"class":2119},[2113,16059,16060,16063,16066],{"class":2115,"line":2514},[2113,16061,16062],{"class":2326},"  message",[2113,16064,16065],{"class":2414}," FileManifestStat",[2113,16067,2647],{"class":2119},[2113,16069,16070,16073,16076,16078,16080],{"class":2115,"line":2533},[2113,16071,16072],{"class":2326},"    int32",[2113,16074,16075],{"class":2330}," ownerId",[2113,16077,2153],{"class":2334},[2113,16079,2850],{"class":2166},[2113,16081,2487],{"class":2119},[2113,16083,16084,16086,16089,16091,16093],{"class":2115,"line":2556},[2113,16085,16072],{"class":2326},[2113,16087,16088],{"class":2330}," groupId",[2113,16090,2153],{"class":2334},[2113,16092,2484],{"class":2166},[2113,16094,2487],{"class":2119},[2113,16096,16097,16100,16102,16104,16107],{"class":2115,"line":2569},[2113,16098,16099],{"class":2326},"    int64",[2113,16101,7924],{"class":2330},[2113,16103,2153],{"class":2334},[2113,16105,16106],{"class":2166}," 3",[2113,16108,2487],{"class":2119},[2113,16110,16111,16113,16116,16118,16120],{"class":2115,"line":2575},[2113,16112,16099],{"class":2326},[2113,16114,16115],{"class":2330}," lastRead",[2113,16117,2153],{"class":2334},[2113,16119,2894],{"class":2166},[2113,16121,2487],{"class":2119},[2113,16123,16124,16126,16129,16131,16134],{"class":2115,"line":2596},[2113,16125,16099],{"class":2326},[2113,16127,16128],{"class":2330}," lastModified",[2113,16130,2153],{"class":2334},[2113,16132,16133],{"class":2166}," 5",[2113,16135,2487],{"class":2119},[2113,16137,16138,16140,16143,16145,16148],{"class":2115,"line":3192},[2113,16139,16099],{"class":2326},[2113,16141,16142],{"class":2330}," created",[2113,16144,2153],{"class":2334},[2113,16146,16147],{"class":2166}," 6",[2113,16149,2487],{"class":2119},[2113,16151,16152,16154,16157,16159,16161],{"class":2115,"line":3213},[2113,16153,16072],{"class":2326},[2113,16155,16156],{"class":2330}," mode",[2113,16158,2153],{"class":2334},[2113,16160,6408],{"class":2166},[2113,16162,2487],{"class":2119},[2113,16164,16165],{"class":2115,"line":3236},[2113,16166,2572],{"class":2119},[2113,16168,16169],{"class":2115,"line":3248},[2113,16170,2125],{"emptyLinePlaceholder":1767},[2113,16172,16173,16176,16178,16180,16182],{"class":2115,"line":4899},[2113,16174,16175],{"class":2326},"  bytes",[2113,16177,6584],{"class":2330},[2113,16179,2153],{"class":2334},[2113,16181,2850],{"class":2166},[2113,16183,2487],{"class":2119},[2113,16185,16186,16189,16191,16193,16195],{"class":2115,"line":1777},[2113,16187,16188],{"class":2326},"  FileManifestStat",[2113,16190,9087],{"class":2330},[2113,16192,2153],{"class":2334},[2113,16194,2484],{"class":2166},[2113,16196,2487],{"class":2119},[2113,16198,16199,16202,16205,16208,16210,16212],{"class":2115,"line":4931},[2113,16200,16201],{"class":2326},"  repeated",[2113,16203,16204],{"class":2326}," bytes",[2113,16206,16207],{"class":2330}," chunks",[2113,16209,2153],{"class":2334},[2113,16211,16106],{"class":2166},[2113,16213,2487],{"class":2119},[2113,16215,16216,16218,16220,16222,16224],{"class":2115,"line":4961},[2113,16217,16175],{"class":2326},[2113,16219,10145],{"class":2330},[2113,16221,2153],{"class":2334},[2113,16223,2894],{"class":2166},[2113,16225,2487],{"class":2119},[2113,16227,16228],{"class":2115,"line":4976},[2113,16229,2599],{"class":2119},[2113,16231,16232],{"class":2115,"line":4993},[2113,16233,2125],{"emptyLinePlaceholder":1767},[2113,16235,16236,16238,16241],{"class":2115,"line":5013},[2113,16237,16052],{"class":2326},[2113,16239,16240],{"class":2414}," FileManifestJournalEntry",[2113,16242,2647],{"class":2119},[2113,16244,16245,16248,16251],{"class":2115,"line":5018},[2113,16246,16247],{"class":2326},"  enum",[2113,16249,16250],{"class":2414}," EntryType",[2113,16252,2647],{"class":2119},[2113,16254,16255,16258,16260,16262],{"class":2115,"line":5042},[2113,16256,16257],{"class":2330},"    ADD",[2113,16259,2153],{"class":2334},[2113,16261,3208],{"class":2166},[2113,16263,2487],{"class":2119},[2113,16265,16266,16269,16271,16273],{"class":2115,"line":5057},[2113,16267,16268],{"class":2330},"    MODIFY",[2113,16270,2153],{"class":2334},[2113,16272,2850],{"class":2166},[2113,16274,2487],{"class":2119},[2113,16276,16277,16280,16282,16284],{"class":2115,"line":5062},[2113,16278,16279],{"class":2330},"    REMOVE",[2113,16281,2153],{"class":2334},[2113,16283,2484],{"class":2166},[2113,16285,2487],{"class":2119},[2113,16287,16288],{"class":2115,"line":5098},[2113,16289,2572],{"class":2119},[2113,16291,16292],{"class":2115,"line":5117},[2113,16293,2125],{"emptyLinePlaceholder":1767},[2113,16295,16296,16299,16302],{"class":2115,"line":5142},[2113,16297,16298],{"class":2326},"  oneof",[2113,16300,16301],{"class":2330}," entry",[2113,16303,2647],{"class":2119},[2113,16305,16306,16309,16312,16314,16316],{"class":2115,"line":5148},[2113,16307,16308],{"class":2326},"    FileManifest",[2113,16310,16311],{"class":2330}," manifest",[2113,16313,2153],{"class":2334},[2113,16315,2850],{"class":2166},[2113,16317,2487],{"class":2119},[2113,16319,16320,16323,16325,16327,16329],{"class":2115,"line":5163},[2113,16321,16322],{"class":2326},"    bytes",[2113,16324,6584],{"class":2330},[2113,16326,2153],{"class":2334},[2113,16328,2484],{"class":2166},[2113,16330,2487],{"class":2119},[2113,16332,16333],{"class":2115,"line":5169},[2113,16334,2572],{"class":2119},[2113,16336,16337,16340,16343,16345,16347],{"class":2115,"line":5175},[2113,16338,16339],{"class":2326},"  EntryType",[2113,16341,16342],{"class":2330}," type",[2113,16344,2153],{"class":2334},[2113,16346,16106],{"class":2166},[2113,16348,2487],{"class":2119},[2113,16350,16351],{"class":2115,"line":5180},[2113,16352,2599],{"class":2119},[2113,16354,16355],{"class":2115,"line":5199},[2113,16356,2125],{"emptyLinePlaceholder":1767},[2113,16358,16359,16361,16364],{"class":2115,"line":5204},[2113,16360,16052],{"class":2326},[2113,16362,16363],{"class":2414}," BackupConfiguration",[2113,16365,2647],{"class":2119},[2113,16367,16368,16371,16374],{"class":2115,"line":5209},[2113,16369,16370],{"class":2326},"    message",[2113,16372,16373],{"class":2414}," Share",[2113,16375,2647],{"class":2119},[2113,16377,16378,16381,16384,16386,16388],{"class":2115,"line":5232},[2113,16379,16380],{"class":2326},"        string",[2113,16382,16383],{"class":2330}," name",[2113,16385,2153],{"class":2334},[2113,16387,2850],{"class":2166},[2113,16389,2487],{"class":2119},[2113,16391,16392,16395,16398,16401,16403,16405],{"class":2115,"line":5237},[2113,16393,16394],{"class":2326},"        repeated",[2113,16396,16397],{"class":2326}," string",[2113,16399,16400],{"class":2330}," includes",[2113,16402,2153],{"class":2334},[2113,16404,2484],{"class":2166},[2113,16406,2487],{"class":2119},[2113,16408,16409,16411,16413,16416,16418,16420],{"class":2115,"line":5242},[2113,16410,16394],{"class":2326},[2113,16412,16397],{"class":2326},[2113,16414,16415],{"class":2330}," excludes",[2113,16417,2153],{"class":2334},[2113,16419,16106],{"class":2166},[2113,16421,2487],{"class":2119},[2113,16423,16424,16426,16429,16431,16433],{"class":2115,"line":5267},[2113,16425,16380],{"class":2326},[2113,16427,16428],{"class":2330}," pathPrefix",[2113,16430,2153],{"class":2334},[2113,16432,2894],{"class":2166},[2113,16434,2487],{"class":2119},[2113,16436,16437],{"class":2115,"line":5282},[2113,16438,4665],{"class":2119},[2113,16440,16441],{"class":2115,"line":5295},[2113,16442,2125],{"emptyLinePlaceholder":1767},[2113,16444,16445,16447,16450],{"class":2115,"line":5310},[2113,16446,16370],{"class":2326},[2113,16448,16449],{"class":2414}," Task",[2113,16451,2647],{"class":2119},[2113,16453,16454,16456,16459,16461,16463],{"class":2115,"line":5315},[2113,16455,16380],{"class":2326},[2113,16457,16458],{"class":2330}," command",[2113,16460,2153],{"class":2334},[2113,16462,2850],{"class":2166},[2113,16464,2487],{"class":2119},[2113,16466,16467,16469,16471,16473,16475,16477],{"class":2115,"line":5320},[2113,16468,16394],{"class":2326},[2113,16470,16373],{"class":2326},[2113,16472,7077],{"class":2330},[2113,16474,2153],{"class":2334},[2113,16476,2484],{"class":2166},[2113,16478,2487],{"class":2119},[2113,16480,16481,16483,16485,16487,16489,16491],{"class":2115,"line":8625},[2113,16482,16394],{"class":2326},[2113,16484,16397],{"class":2326},[2113,16486,16400],{"class":2330},[2113,16488,2153],{"class":2334},[2113,16490,16106],{"class":2166},[2113,16492,2487],{"class":2119},[2113,16494,16495,16497,16499,16501,16503,16505],{"class":2115,"line":8631},[2113,16496,16394],{"class":2326},[2113,16498,16397],{"class":2326},[2113,16500,16415],{"class":2330},[2113,16502,2153],{"class":2334},[2113,16504,2894],{"class":2166},[2113,16506,2487],{"class":2119},[2113,16508,16509],{"class":2115,"line":8637},[2113,16510,4665],{"class":2119},[2113,16512,16513],{"class":2115,"line":8652},[2113,16514,2125],{"emptyLinePlaceholder":1767},[2113,16516,16517,16519,16522],{"class":2115,"line":8674},[2113,16518,16370],{"class":2326},[2113,16520,16521],{"class":2414}," Operations",[2113,16523,2647],{"class":2119},[2113,16525,16526,16528,16530,16533,16535,16537],{"class":2115,"line":8706},[2113,16527,16394],{"class":2326},[2113,16529,16449],{"class":2326},[2113,16531,16532],{"class":2330}," tasks",[2113,16534,2153],{"class":2334},[2113,16536,2850],{"class":2166},[2113,16538,2487],{"class":2119},[2113,16540,16541,16543,16545,16548,16550,16552],{"class":2115,"line":8724},[2113,16542,16394],{"class":2326},[2113,16544,16449],{"class":2326},[2113,16546,16547],{"class":2330}," finalizedTasks",[2113,16549,2153],{"class":2334},[2113,16551,2484],{"class":2166},[2113,16553,2487],{"class":2119},[2113,16555,16556],{"class":2115,"line":8729},[2113,16557,4665],{"class":2119},[2113,16559,16560],{"class":2115,"line":8734},[2113,16561,2125],{"emptyLinePlaceholder":1767},[2113,16563,16564,16567,16570,16572,16574],{"class":2115,"line":8740},[2113,16565,16566],{"class":2326},"    Operations",[2113,16568,16569],{"class":2330}," operations",[2113,16571,2153],{"class":2334},[2113,16573,2850],{"class":2166},[2113,16575,2487],{"class":2119},[2113,16577,16578],{"class":2115,"line":8772},[2113,16579,2599],{"class":2119},[2113,16581,16582],{"class":2115,"line":8778},[2113,16583,2125],{"emptyLinePlaceholder":1767},[2113,16585,16586,16588,16591],{"class":2115,"line":8789},[2113,16587,16052],{"class":2326},[2113,16589,16590],{"class":2414}," FileChunk",[2113,16592,2647],{"class":2119},[2113,16594,16595,16597,16599,16601,16603],{"class":2115,"line":8795},[2113,16596,16175],{"class":2326},[2113,16598,15803],{"class":2330},[2113,16600,2153],{"class":2334},[2113,16602,2850],{"class":2166},[2113,16604,2487],{"class":2119},[2113,16606,16607],{"class":2115,"line":8817},[2113,16608,2599],{"class":2119},[2113,16610,16611],{"class":2115,"line":8822},[2113,16612,2125],{"emptyLinePlaceholder":1767},[2113,16614,16615,16617,16620],{"class":2115,"line":8857},[2113,16616,16052],{"class":2326},[2113,16618,16619],{"class":2414}," PrepareBackupRequest",[2113,16621,2647],{"class":2119},[2113,16623,16624,16627,16630,16632,16634],{"class":2115,"line":8896},[2113,16625,16626],{"class":2326},"  BackupConfiguration",[2113,16628,16629],{"class":2330}," configuration",[2113,16631,2153],{"class":2334},[2113,16633,2850],{"class":2166},[2113,16635,2487],{"class":2119},[2113,16637,16638,16641,16644,16646,16648],{"class":2115,"line":8901},[2113,16639,16640],{"class":2326},"  uint32",[2113,16642,16643],{"class":2330}," lastBackupNumber",[2113,16645,2153],{"class":2334},[2113,16647,2484],{"class":2166},[2113,16649,2487],{"class":2119},[2113,16651,16652,16654,16657,16659,16661],{"class":2115,"line":8907},[2113,16653,16640],{"class":2326},[2113,16655,16656],{"class":2330}," newBackupNumber",[2113,16658,2153],{"class":2334},[2113,16660,16106],{"class":2166},[2113,16662,2487],{"class":2119},[2113,16664,16665],{"class":2115,"line":8913},[2113,16666,2599],{"class":2119},[2113,16668,16669],{"class":2115,"line":8931},[2113,16670,2125],{"emptyLinePlaceholder":1767},[2113,16672,16673,16675,16678],{"class":2115,"line":8936},[2113,16674,16052],{"class":2326},[2113,16676,16677],{"class":2414}," PrepareBackupReply",[2113,16679,2647],{"class":2119},[2113,16681,16682,16685,16688,16690,16692],{"class":2115,"line":8942},[2113,16683,16684],{"class":2326},"  StatusCode",[2113,16686,16687],{"class":2330}," code",[2113,16689,2153],{"class":2334},[2113,16691,2850],{"class":2166},[2113,16693,2487],{"class":2119},[2113,16695,16696,16699,16702,16704,16706],{"class":2115,"line":8950},[2113,16697,16698],{"class":2326},"  bool",[2113,16700,16701],{"class":2330}," needRefreshCache",[2113,16703,2153],{"class":2334},[2113,16705,2484],{"class":2166},[2113,16707,2487],{"class":2119},[2113,16709,16710],{"class":2115,"line":8968},[2113,16711,2599],{"class":2119},[2113,16713,16714],{"class":2115,"line":8983},[2113,16715,2125],{"emptyLinePlaceholder":1767},[2113,16717,16718,16720,16723],{"class":2115,"line":8988},[2113,16719,16052],{"class":2326},[2113,16721,16722],{"class":2414}," RefreshCacheReply",[2113,16724,2647],{"class":2119},[2113,16726,16727,16729,16731,16733,16735],{"class":2115,"line":9011},[2113,16728,16684],{"class":2326},[2113,16730,16687],{"class":2330},[2113,16732,2153],{"class":2334},[2113,16734,2850],{"class":2166},[2113,16736,2487],{"class":2119},[2113,16738,16739],{"class":2115,"line":9016},[2113,16740,2599],{"class":2119},[2113,16742,16743],{"class":2115,"line":9022},[2113,16744,2125],{"emptyLinePlaceholder":1767},[2113,16746,16747,16749,16752],{"class":2115,"line":9045},[2113,16748,16052],{"class":2326},[2113,16750,16751],{"class":2414}," LaunchBackupRequest",[2113,16753,2647],{"class":2119},[2113,16755,16756,16758,16761,16763,16765],{"class":2115,"line":9076},[2113,16757,16640],{"class":2326},[2113,16759,16760],{"class":2330}," backupNumber",[2113,16762,2153],{"class":2334},[2113,16764,2850],{"class":2166},[2113,16766,2487],{"class":2119},[2113,16768,16769],{"class":2115,"line":9082},[2113,16770,2599],{"class":2119},[2113,16772,16773],{"class":2115,"line":9105},[2113,16774,2125],{"emptyLinePlaceholder":1767},[2113,16776,16777,16779,16782],{"class":2115,"line":9138},[2113,16778,16052],{"class":2326},[2113,16780,16781],{"class":2414}," GetChunkRequest",[2113,16783,2647],{"class":2119},[2113,16785,16786,16788,16791,16793,16795],{"class":2115,"line":9149},[2113,16787,16175],{"class":2326},[2113,16789,16790],{"class":2330}," filename",[2113,16792,2153],{"class":2334},[2113,16794,2850],{"class":2166},[2113,16796,2487],{"class":2119},[2113,16798,16799,16802,16805,16807,16809],{"class":2115,"line":9157},[2113,16800,16801],{"class":2326},"  uint64",[2113,16803,16804],{"class":2330}," position",[2113,16806,2153],{"class":2334},[2113,16808,2484],{"class":2166},[2113,16810,2487],{"class":2119},[2113,16812,16813,16815,16817,16819,16821],{"class":2115,"line":9182},[2113,16814,16801],{"class":2326},[2113,16816,7924],{"class":2330},[2113,16818,2153],{"class":2334},[2113,16820,16106],{"class":2166},[2113,16822,2487],{"class":2119},[2113,16824,16825,16827,16829,16831,16833],{"class":2115,"line":9188},[2113,16826,16175],{"class":2326},[2113,16828,10145],{"class":2330},[2113,16830,2153],{"class":2334},[2113,16832,2894],{"class":2166},[2113,16834,2487],{"class":2119},[2113,16836,16837],{"class":2115,"line":9193},[2113,16838,2599],{"class":2119},[2113,16840,16841],{"class":2115,"line":9198},[2113,16842,2125],{"emptyLinePlaceholder":1767},[2113,16844,16845,16848,16851],{"class":2115,"line":9216},[2113,16846,16847],{"class":2326},"service",[2113,16849,16850],{"class":2414}," Woodstockp",[2113,16852,16853],{"class":2119},"ériphériqueService {\n",[2113,16855,16856,16859,16862,16864,16867,16869,16872,16874,16877],{"class":2115,"line":9238},[2113,16857,16858],{"class":2326},"  rpc",[2113,16860,16861],{"class":2133}," PrepareBackup",[2113,16863,2423],{"class":2119},[2113,16865,16866],{"class":2414},"PrepareBackupRequest",[2113,16868,5709],{"class":2119},[2113,16870,16871],{"class":2326},"returns",[2113,16873,2495],{"class":2119},[2113,16875,16876],{"class":2414},"PrepareBackupReply",[2113,16878,8980],{"class":2119},[2113,16880,16881],{"class":2115,"line":9243},[2113,16882,2125],{"emptyLinePlaceholder":1767},[2113,16884,16885,16887,16890,16892,16894,16896,16898,16900,16902,16905],{"class":2115,"line":10782},[2113,16886,16858],{"class":2326},[2113,16888,16889],{"class":2133}," RefreshCache",[2113,16891,2423],{"class":2119},[2113,16893,9025],{"class":2326},[2113,16895,16055],{"class":2414},[2113,16897,5709],{"class":2119},[2113,16899,16871],{"class":2326},[2113,16901,2495],{"class":2119},[2113,16903,16904],{"class":2414},"RefreshCacheReply",[2113,16906,8980],{"class":2119},[2113,16908,16909],{"class":2115,"line":10787},[2113,16910,2125],{"emptyLinePlaceholder":1767},[2113,16912,16913,16915,16918,16920,16923,16925,16927,16929,16931,16933],{"class":2115,"line":10813},[2113,16914,16858],{"class":2326},[2113,16916,16917],{"class":2133}," LaunchBackup",[2113,16919,2423],{"class":2119},[2113,16921,16922],{"class":2414},"LaunchBackupRequest",[2113,16924,5709],{"class":2119},[2113,16926,16871],{"class":2326},[2113,16928,2495],{"class":2119},[2113,16930,9025],{"class":2326},[2113,16932,16240],{"class":2414},[2113,16934,8980],{"class":2119},[2113,16936,16937],{"class":2115,"line":10834},[2113,16938,2125],{"emptyLinePlaceholder":1767},[2113,16940,16941,16943,16946,16948,16951,16953,16955,16957,16959,16961],{"class":2115,"line":10843},[2113,16942,16858],{"class":2326},[2113,16944,16945],{"class":2133}," GetChunk",[2113,16947,2423],{"class":2119},[2113,16949,16950],{"class":2414},"GetChunkRequest",[2113,16952,5709],{"class":2119},[2113,16954,16871],{"class":2326},[2113,16956,2495],{"class":2119},[2113,16958,9025],{"class":2326},[2113,16960,16590],{"class":2414},[2113,16962,8980],{"class":2119},[2113,16964,16965],{"class":2115,"line":10852},[2113,16966,2599],{"class":2119},[12,16968,16969,16970,16974],{},"Vient ensuite l'écriture de la partie périphérique et de la partie serveur. Je me base sur le\n",[49,16971,16973],{"href":15701,"rel":16972},[347],"Quick Start"," pour la partie C++.",[12,16976,16977,16978,16983],{},"L'exemple se veut simple, et surtout utilise l'API synchrone. gRPC propose également une\n",[49,16979,16982],{"href":16980,"rel":16981},"https:\u002F\u002Fgrpc.io\u002Fdocs\u002Flanguages\u002Fcpp\u002Fasync\u002F",[347],"API asynchrone"," mais qui est plus complexe à mettre en place.",[12,16985,16986],{},"L'API asynchrone devrait permettre d'améliorer les performances dans le cadre d'application multi-threadé. Pour notre\ncas de test nous allons commencer par utiliser l'API synchrone.",[12,16988,16989,16990,16994,16995,179],{},"Dans le cas de l'API synchrone, le serveur reste multi-threadé (et peut donc aussi recevoir plusieurs requêtes en même\ntemps). Vous trouverez les sources de la partie périphérique et de la partie serveur dans le ",[49,16991,16993],{"href":16992},"\u002FWoodstock\u002Fwoodstock-backup-feature_storage-db.zip","ZIP suivant"," dans le dossier ",[184,16996,16997],{},"périphérique-sync",[12,16999,17000,17001,17003],{},"Le développement de la partie ",[184,17002,15651],{}," vient ensuite. La librairie n'est pas des plus faciles d'utilisation, je me\nconcentre sur le développement à des fins de tests de perfs et non de sécurité (ou de non bug).",[128,17005,17007],{"id":17006},"benchmark","Benchmark",[12,17009,17010,17011,17013,17014,17019],{},"Un bench d'envoi de fichier via ",[184,17012,15604],{}," a déjà été fait par d'autres personnes. L'article se trouve ici :\n",[49,17015,17018],{"href":17016,"rel":17017},"https:\u002F\u002Fops.tips\u002Fblog\u002Fsending-files-via-grpc\u002F",[347],"Sending files via gRPC",". Je vous laisse le lire.\nCet article a été fait en Go. Je suppose que le résultat devrait être très proche d'un résultat en C++.",[12,17021,17022,17023,17025,17026,17028],{},"Le résultat de cet article est que du pur ",[184,17024,15651],{}," en Go est tout de même plus performant que ",[184,17027,15604],{}," (probablement du\nà la sérialisation et à la déserialisation des messages qui prend un peu plus de temps).",[12,17030,17031],{},"Pour réaliser les tests, nous avons utilisé le scénario suivant :",[506,17033,17034,17037,17040,17043,17046],{},[370,17035,17036],{},"Sauvegarde d'environ 46 Go de données à partir d'un périphérique équipé d'un SSD ;",[370,17038,17039],{},"Le PC de destination est équipé d'un disque dur connecté en USB 3 ;",[370,17041,17042],{},"Les machines sont reliées par un tuyau de 1 Gb\u002Fs ;",[370,17044,17045],{},"Les chunks sont stockés au format du pool défini dans l'article\n[\u002Fpost\u002Fwoodstock_brtfs](Utilisation de Btrfs et son remplacement) ceci afin de representer au mieux notre cas\nd'utilisation.",[370,17047,17048],{},"Le hash est un SHA3_256, qui est plus performant qu'un SHA2_256.",[12,17050,17051],{},"Les tests ont été réalisés dans des conditions relativement réelles (calcul des hash, copie des fichiers dans un pool\nqui fait de la déduplication), ce qui prend en compte le transfert des fichiers mais aussi les calculs effectués autour.",[12,17053,17054],{},"Une fois le développement effectué, pour compiler la version compatible gRPC, il suffit de lancer la commande suivante :",[2105,17056,17058],{"className":2317,"code":17057,"language":2319,"meta":1646,"style":1646},"cd périphérique-sync\nmkdir build\ncd build\ncmake -DWITH_PROTOCOL_HTTP2=OFF -DWITH_PROTOCOL_GRPC=ON -DCMAKE_BUILD_TYPE=Release ..\u002F\nmake -j16\n\n# Lancement du périphérique\n.\u002Fsrc\u002Fpériphérique_daemon\u002Fpériphérique\n\n# Lancement du serveur\n.\u002Fsrc\u002Fserver\u002Fserver\n",[184,17059,17060,17068,17076,17082,17099,17107,17111,17116,17121,17125,17130],{"__ignoreMap":1646},[2113,17061,17062,17065],{"class":2115,"line":2116},[2113,17063,17064],{"class":2334},"cd",[2113,17066,17067],{"class":2149}," périphérique-sync\n",[2113,17069,17070,17073],{"class":2115,"line":1647},[2113,17071,17072],{"class":2133},"mkdir",[2113,17074,17075],{"class":2149}," build\n",[2113,17077,17078,17080],{"class":2115,"line":1652},[2113,17079,17064],{"class":2334},[2113,17081,17075],{"class":2149},[2113,17083,17084,17087,17090,17093,17096],{"class":2115,"line":2185},[2113,17085,17086],{"class":2133},"cmake",[2113,17088,17089],{"class":2166}," -DWITH_PROTOCOL_HTTP2=OFF",[2113,17091,17092],{"class":2166}," -DWITH_PROTOCOL_GRPC=ON",[2113,17094,17095],{"class":2166}," -DCMAKE_BUILD_TYPE=Release",[2113,17097,17098],{"class":2149}," ..\u002F\n",[2113,17100,17101,17104],{"class":2115,"line":2230},[2113,17102,17103],{"class":2133},"make",[2113,17105,17106],{"class":2166}," -j16\n",[2113,17108,17109],{"class":2115,"line":2235},[2113,17110,2125],{"emptyLinePlaceholder":1767},[2113,17112,17113],{"class":2115,"line":2241},[2113,17114,17115],{"class":2396},"# Lancement du périphérique\n",[2113,17117,17118],{"class":2115,"line":2246},[2113,17119,17120],{"class":2133},".\u002Fsrc\u002Fpériphérique_daemon\u002Fpériphérique\n",[2113,17122,17123],{"class":2115,"line":2464},[2113,17124,2125],{"emptyLinePlaceholder":1767},[2113,17126,17127],{"class":2115,"line":2085},[2113,17128,17129],{"class":2396},"# Lancement du serveur\n",[2113,17131,17132],{"class":2115,"line":2514},[2113,17133,17134],{"class":2133},".\u002Fsrc\u002Fserver\u002Fserver\n",[12,17136,17137],{},"Pour la partie HTTP\u002F2, il faudra inverser les valeurs ON et OFF. Il est important de noter que la configuration du\nserveur est actuellement en dur et liée à la sauvegarde de l'ordinateur. Par conséquent, si vous souhaiter lancer\nle bench chez vous, cette configuration doit être modifiée pour adapter le serveur à votre propre configuration.",[12,17139,17140],{},"Une fois les tests effectués, j'ai réécris uniquement la partie serveur en NodeJS. La réécriture en Node.JS a été plus\nrapide que le développement en C++, car je savais exactement où nous allions et que le Javascript est plus simple à\nécrire (quand on utilise des frameworks comme RxJs, ...).",[12,17142,17143],{},"Voici le résultat du benchmark:",[22,17145,17146,17161],{},[25,17147,17148],{},[28,17149,17150,17153,17157,17159],{},[31,17151],{"align":17152},"left",[31,17154,17156],{"align":17155},"right","rSync",[31,17158,15604],{"align":17155},[31,17160,15651],{"align":17155},[41,17162,17163,17177,17190],{},[28,17164,17165,17168,17171,17174],{},[46,17166,17167],{"align":17152},"Server C++ \u002F Périphérique C++",[46,17169,17170],{"align":17155},"47m",[46,17172,17173],{"align":17155},"3h01",[46,17175,17176],{"align":17155},"2h25",[28,17178,17179,17182,17184,17187],{},[46,17180,17181],{"align":17152},"Server Node.JS \u002F Périphérique C++ (concatMap)",[46,17183],{"align":17155},[46,17185,17186],{"align":17155},"4h57",[46,17188,17189],{"align":17155},"1h50",[28,17191,17192,17195,17197,17200],{},[46,17193,17194],{"align":17152},"Server Node.JS \u002F Périphérique C++ (mergeMap)",[46,17196],{"align":17155},[46,17198,17199],{"align":17155},"60m",[46,17201],{"align":17155},[12,17203,17204,17205,17207,17208,17210,17211,17213],{},"Je n'ai pas testé tous les cas de figure, j'ai principalement fait varier la partie serveur. Et je me suis concentré sur\n",[184,17206,15604],{}," car je voulais comprendre pourquoi ",[184,17209,15651],{}," était beaucoup plus rapide que ",[184,17212,15604],{}," en Node.JS.",[12,17215,17216],{},"Ce que le l'on peut remarquer:",[506,17218,17219,17233,17241,17244],{},[370,17220,17221,17222],{},"En C++ pure:\n",[506,17223,17224,17227,17230],{},[370,17225,17226],{},"rsync est le plus rapide",[370,17228,17229],{},"HTTP\u002F2 est légèrement plus rapide que gRPC",[370,17231,17232],{},"La lenteur est problablement due au fait qu'on soit monothreadé",[370,17234,17235,17236],{},"Sur une écriture monothreadé coté serveur:\n",[506,17237,17238],{},[370,17239,17240],{},"C++ est plus rapide que Node.JS pour gRPC mais pas pour HTTP\u002F2",[370,17242,17243],{},"La parallélisation avec NodeJS est beaucoup plus simple qu'en C++ : il est important de noter que l'amélioration\nvisible en NodeJS vient du fait qu'à partir du moment où l'on ne fait que des I\u002FO en asynchrone, le thread JavaScript\neffectue peu de traitement, ce qui permet d'avoir des traitements asynchrones multi-threadés.",[370,17245,17246],{},"Le transfert est nettement plus lent que rSync (même en HTTP\u002F2)",[12,17248,17249],{},"La grande question est donc : dans quel langage doit-on écrire le périphérique et le serveur au vu de ces résultats ?\nL'écriture en C++ sera plus performante avec l'utilisation de threads. Cependant, le code en C++ sera plus complexe\n(surtout avec le multi-threading) et plus long à écrire, avec des risques de fuites mémoires et de segmentation fault.",[12,17251,17252],{},"L'écriture en JavaScript sera moins performante, mais la parallélisation sera plus facile, et le risque de fuite sera\nmoins élevé. J'ai par contre peur que la consommation mémoire des objets JavaScript soit plus importante que celle des\nobjets en C++.",[1886,17254,17255],{},[12,17256,17257],{},"Note de 2023: Le constat de la consommation mémoire des objets en Node.JS par rapport à C++ est vérifier. Je ferai un\narticle dédié sur le sujet.",[128,17259,17261],{"id":17260},"amélioration-possible-du-bench","Amélioration possible du bench",[506,17263,17264,17274,17282],{},[370,17265,17266,17267,17270,17271,17273],{},"Coté C++: J'ai testé ",[184,17268,17269],{},"RxCpp"," pour ajouter plus d'asynchronisme et traiter les objets en mode flux. Malheureusement,\ncontrairement à Node.JS, la librairie ",[184,17272,17269],{}," est mono-threadé. Du coup cela ne change rien. L'utilisation de RxCpp\nfait aussi des noeuds au cerveau à cause de l'utilisation des templates.",[370,17275,17276,17277,17281],{},"Coté C++: utiliser l'",[49,17278,17280],{"href":16980,"rel":17279},[347],"API Asynchrone"," de gRPC pourrait améliorer les choses.",[370,17283,17284],{},"Coté Node.JS: Il est possible d'améliorer les performances avec des bindings sur des librairies C++.",[128,17286,17288],{"id":17287},"pourquoi-nodejs-pourquoi-pas-nodejs","Pourquoi NodeJS \u002F Pourquoi pas NodeJS",[12,17290,17291],{},"On m'a toujours dit, et je considère que c'est une bonne pratique, d'avancer vite pour avoir une première version et\nensuite d'optimiser ce qui doit l'être (et uniquement ce qui doit l'être).",[12,17293,17294],{},"Clairement écrire en Javascript sera plus rapide que l'écriture en C++. Je pense donc commencer par un MVP en utilisant\nNodeJS. Et ensuite, si le besoin s'en fait sentir, je viendrai réécrire des parties en C++ soit via des bindings, soit\nvia des binaires dédiés.",[12,17296,17297],{},"Je vois déjà les anti NodeJS qui vont me dire que faire un logiciel de sauvegarde avec NodeJS c'est pas terrible. Que\nNodeJS est un language pourris :) ou que c'est un language pour faire des sites Internet, ou que cela utilise un\ninterpreteur.",[12,17299,17300],{},"Mais quand on pense que:",[506,17302,17303,17311],{},[370,17304,17305,17307,17308,17310],{},[184,17306,1013],{}," est fait en ",[184,17309,1063],{}," (avec une partie faite en C depuis la version 4)",[370,17312,17313,17307,17315],{},[184,17314,15449],{},[184,17316,17317],{},"Python",[12,17319,17320],{},"Bref des logiciels de sauvegardes faits avec un language intreprété, il y en a.",[12,17322,17323],{},"Node.JS est basé sur le moteur V8 de Google dont les performances augmentent à chaque version. Donc la réponse pourrait\nsimplement être: pourquoi pas !\nLe plus important c'est de faire un logiciel fiable et qui fonctionne. Donc que l'on utiliser NodeJS, PHP ou autre pour\nfaire le développement, au final : OSEF.",[12,17325,17326],{},"Je terminerai donc sur cette note finale. Je vais finaliser quelques tests et me lancer dans le développement.",[1886,17328,17329,17332,17335],{},[12,17330,17331],{},"Note de 2023 : Finalement, j'ai donc fait le développement en NodeJS. Les performances ont été grandement améliorées\ndepuis le bench. Par contre la consomation en mémoire des objets NodeJS éclate les scores les plus pessimistes\nque j'avais :).",[12,17333,17334],{},"Une fois la première version sortie, je réécrirai certaines parties dans des modules natifs pour améliorer les\nperformances. Voir le client complètement.",[12,17336,17337],{},"Par contre au lieu de choisir C++, je pense partir sur Rust que j'ai commencé à utiliser très récemment. Je ferai un\narticle sur le bench que j'ai pu faire sur la consommation mémoire de nodejs vs rust sur le sujet.",[3358,17339,17340],{},"html pre.shiki code .seHd6, html code.shiki .seHd6{--shiki-default:#C678DD}html pre.shiki code .sVC51, html code.shiki .sVC51{--shiki-default:#D19A66}html pre.shiki code .sVyAn, html code.shiki .sVyAn{--shiki-default:#E06C75}html pre.shiki code .subq3, html code.shiki .subq3{--shiki-default:#98C379}html pre.shiki code .sn6KH, html code.shiki .sn6KH{--shiki-default:#ABB2BF}html pre.shiki code .sU0A5, html code.shiki .sU0A5{--shiki-default:#E5C07B}html pre.shiki code .sjrmR, html code.shiki .sjrmR{--shiki-default:#56B6C2}html pre.shiki code .sVbv2, html code.shiki .sVbv2{--shiki-default:#61AFEF}html pre.shiki code .s_ZVi, html code.shiki .s_ZVi{--shiki-default:#E06C75;--shiki-default-font-style:italic}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html pre.shiki code .sV9Aq, html code.shiki .sV9Aq{--shiki-default:#7F848E;--shiki-default-font-style:italic}",{"title":1646,"searchDepth":1647,"depth":1647,"links":17342},[17343,17348,17349,17350,17351,17352,17353,17354],{"id":15300,"depth":1647,"text":15301,"children":17344},[17345,17346,17347],{"id":15354,"depth":1652,"text":15355},{"id":15430,"depth":1652,"text":15431},{"id":15482,"depth":1652,"text":15483},{"id":15511,"depth":1647,"text":15512},{"id":15587,"depth":1647,"text":15588},{"id":15626,"depth":1647,"text":15627},{"id":15642,"depth":1647,"text":15643},{"id":17006,"depth":1647,"text":17007},{"id":17260,"depth":1647,"text":17261},{"id":17287,"depth":1647,"text":17288},"2023-04-07",{"type":9,"value":17357},[17358,17363,17371],[12,17359,17360],{},[121,17361],{"alt":15252,"src":15259,"className":17362,"width":15261},[126],[1886,17364,17365,17367,17369],{},[12,17366,15266],{},[12,17368,15269],{},[12,17370,15272],{},[12,17372,15275,17373,15279],{},[49,17374,15278],{"href":65},{},{"title":15252,"description":1646},"woodstock_protocol_language_sauvegarde","posts\u002FWoodstock\u002F2021-04-18_woodstock_protocol_language_sauvegarde",[1683,1773,1774,3510,3511],"Ysf2Tbm729RouGvBsMuyaZAuMY3uaQkdpCWyqg6fk7U",{"id":17382,"title":17383,"author":7,"body":17384,"category":2057,"categorySlug":2058,"date":23273,"description":17388,"excerpt":23274,"extension":1764,"location":1765,"meta":23299,"navigation":1767,"path":23300,"published":1767,"seo":23301,"slug":23302,"stem":23303,"tags":23304,"timeToRead":3236,"__hash__":23309},"posts\u002Fposts\u002FProgrammation\u002F2021-09-27_achat_velo.md","Du souhait d'achat d'un vélo ...",{"type":9,"value":17385,"toc":23257},[17386,17389,17396,17408,17411,17418,17421,17424,17428,17431,17442,17445,17452,17455,17463,17466,17477,17482,17489,17497,17505,17581,17584,17916,17919,17922,17929,17932,17959,17962,17965,17984,17990,18001,18004,18008,18015,18018,18025,18032,18035,18046,18049,18144,18147,18151,18154,18200,18203,18496,18499,18503,18506,18509,18518,18520,19149,19152,19158,19635,19638,19645,19649,19657,19665,20089,20092,20108,20111,20115,20118,20121,20124,20127,20130,20133,20136,20140,20143,20146,20149,20152,20155,20526,20529,20947,20950,20953,20971,21833,21836,21843,21847,21850,21861,22157,22160,22163,22690,22693,22697,22700,22703,22710,22767,22774,22781,23119,23122,23179,23183,23186,23189,23196,23200,23203,23206,23209,23212,23215,23218,23254],[12,17387,17388],{},"Que penseriez-vous si je vous racontais un peu mes vacances ? Attendez ... attendez ... ne partez pas ... l'histoire est intéressante,\net surtout nous allons parler informatique.",[12,17390,17391,17392,17395],{},"Début Août j'ai décidé de m'acheter un nouveau vélo (un VTC à assistance électrique). Le choix du vélo importe peu, mais du fait d'une pénurie\nde matière première et d'une forte demande en vélo depuis le début de la crise ",[1892,17393,17394],{},"de mes"," sanitaire, tous les vélos sont en rupture de stock.",[12,17397,17398,17399],{},"J'ai fait le choix personnel de me rendre dans une enseigne connue dont le nom est aussi une discipline de l'athlétisme pour acheter ce VTC.",[17400,17401,17402],"sup",{},[49,17403,3686],{"href":17404,"ariaDescribedBy":17405,"dataFootnoteRef":1646,"id":17407},"#user-content-fn-1",[17406],"footnote-label","user-content-fnref-1",[12,17409,17410],{},"Et là c'est le drame.",[12,17412,17413,17414,17417],{},"Si vous regardez les différents vélos de la marque (et en fonction de la taille du cadre qui vous correspond) vous tombez sur le message: ",[362,17415,17416],{},"En\nrupture de stock",". (Bon. À aujourd'hui, nous avons un peu plus de stock sur le site)",[12,17419,17420],{},"Arf. Moi qui quand j'ai décidé quelque chose, je deviens impatient ...",[12,17422,17423],{},"Alors il est vrai que j'aurais pu aller voir sur un autre site, aller dans une autre boutique pour choisir un autre modèle, mais ce n'est pas\nce que j'ai fait.",[128,17425,17427],{"id":17426},"lattente","L'attente",[12,17429,17430],{},"J'ai donc commencé par attendre, en allant régulièrement sur le site ou en rafraîchissant l'onglet d'un navigateur. C'est long, la page est\nrelativement lourde pour un site WEB: plus de 2.2M de fichier à transférer pour un total de 175 requêtes et ce malgrès la présence d'un adblock.",[12,17432,17433,17434,17437,17438,17441],{},"De plus je dois sélectionner manuellement la taille dans une select box pour voir : ",[362,17435,17436],{},"Rupture de stock sur cette taille",". Là je clique sur\nle bouton ",[362,17439,17440],{},"Vérifier le stock en magasin",", ... je rentre mon code postal, ... et je regarde sur l'ensemble des magasins de ma région...",[12,17443,17444],{},"Bref, je prépare mes valises, et je pars me mettre au vert. Je fais un peu de marche avec les enfants (C'est les vacances quoi). Et je ne\npeux donc pas m'amuser à rafraîchir en continu (même si je le fais depuis mon téléphone portable).",[12,17446,17447,17448,17451],{},"Bien sûr j'ai cliqué sur le bouton ",[362,17449,17450],{},"M'avertir lorsque le produit est à nouveau disponible"," mais je me demande si ce bouton fonctionne, je n'ai\nà aujourd'hui toujours pas recu de mail 😄.",[12,17453,17454],{},"Je décide donc de passer à l'étape suivante.",[128,17456,17458,17459,17462],{"id":17457},"la-1er-version","La 1",[17400,17460,17461],{},"er"," version",[12,17464,17465],{},"Je me pose donc et me demande comment je peux mettre mes compétences à contribution pour m'aider moi-même à m'acheter pour moi mon propre vélo. Je décide donc de mon propre chef de me développer une application sur mon compte AWS que je viens de me créer. Ce programme devra\nme permettre de faire plusieurs choses:",[367,17467,17468,17471,17474],{},[370,17469,17470],{},"En un coup d'oeil visualiser le stock pour le vélo sur Internet et dans les différents magasins",[370,17472,17473],{},"M'avertir si le stock change",[370,17475,17476],{},"Et le plus important, ne pas me prendre la tête et développer l'application dans le moins de temps possible",[12,17478,17458,17479,17481],{},[17400,17480,17461],{}," version que je développe a pour but de visualiser le stock.",[12,17483,17484],{},[121,17485],{"alt":17486,"className":17487,"src":17488},"Selecteur JS",[126],"\u002FProgrammation\u002Fachat_velo\u002Ffirst_version.png",[12,17490,17491,17492,17496],{},"Le but de l'application est de récupérer l'état des stocks du site Internet. Si je peux le faire avec un navigateur internet sur le site,\nalors un programme peut le faire aussi. Je prends les outils dont j'ai l'habitude pour faire le travail: Node.JS et le framework\n",[49,17493,15718],{"href":17494,"rel":17495},"https:\u002F\u002Fnestjs.com\u002F",[347]," - framework que j'apprécie pour sa simplicité d'utilisation.",[12,17498,17499,17500,15746],{},"Pour parser la page, j'utilise ",[49,17501,17504],{"href":17502,"rel":17503},"https:\u002F\u002Fgithub.com\u002Fjsdom\u002Fjsdom",[347],"JSDom",[2105,17506,17508],{"className":15749,"code":17507,"language":15751,"meta":1646,"style":1646},"const dom = await JSDOM.fromURL(VTC_540E_URL, options);\n\nconsole.log(dom.window.document.querySelector(\"#product-size-selection\"));\n",[184,17509,17510,17542,17546],{"__ignoreMap":1646},[2113,17511,17512,17514,17517,17519,17522,17525,17527,17530,17532,17535,17537,17540],{"class":2115,"line":2116},[2113,17513,7785],{"class":2326},[2113,17515,17516],{"class":2414}," dom",[2113,17518,2153],{"class":2334},[2113,17520,17521],{"class":2326}," await",[2113,17523,17524],{"class":2414}," JSDOM",[2113,17526,179],{"class":2119},[2113,17528,17529],{"class":2133},"fromURL",[2113,17531,2423],{"class":2119},[2113,17533,17534],{"class":2414},"VTC_540E_URL",[2113,17536,932],{"class":2119},[2113,17538,17539],{"class":2330},"options",[2113,17541,2553],{"class":2119},[2113,17543,17544],{"class":2115,"line":1647},[2113,17545,2125],{"emptyLinePlaceholder":1767},[2113,17547,17548,17550,17552,17554,17556,17559,17561,17564,17566,17569,17571,17574,17576,17579],{"class":2115,"line":1652},[2113,17549,8743],{"class":2414},[2113,17551,179],{"class":2119},[2113,17553,8748],{"class":2133},[2113,17555,2423],{"class":2119},[2113,17557,17558],{"class":2414},"dom",[2113,17560,179],{"class":2119},[2113,17562,17563],{"class":2414},"window",[2113,17565,179],{"class":2119},[2113,17567,17568],{"class":2414},"document",[2113,17570,179],{"class":2119},[2113,17572,17573],{"class":2133},"querySelector",[2113,17575,2423],{"class":2119},[2113,17577,17578],{"class":2149},"\"#product-size-selection\"",[2113,17580,5818],{"class":2119},[12,17582,17583],{},"Première erreur quand je lance le programme:",[2105,17585,17589],{"className":17586,"code":17587,"language":17588,"meta":1646,"style":1646},"language-sh shiki shiki-themes one-dark-pro","(node:15246) UnhandledPromiseRejectionWarning: Error: Parse Error: Header overflow\n    at TLSSocket.socketOnData (_http_client.js:476:22)\n    at TLSSocket.emit (events.js:311:20)\n    at addChunk (_stream_readable.js:294:12)\n    at readableAddChunk (_stream_readable.js:275:11)\n    at TLSSocket.Readable.push (_stream_readable.js:209:10)\n    at TLSWrap.onStreamRead (internal\u002Fstream_base_commons.js:186:23)\n(node:15246) UnhandledPromiseRejectionWarning: Unhandled promise rejection. This error originated either by throwing inside of an async function without a catch block, or by rejecting a promise which was not handled with .catch(). To terminate the node process on unhandled promise rejection, use the CLI flag `--unhandled-rejections=strict` (see https:\u002F\u002Fnodejs.org\u002Fapi\u002Fcli.html#cli_unhandled_rejections_mode). (rejection id: 1)\n(node:15246) [DEP0018] DeprecationWarning: Unhandled promise rejections are deprecated. In the future, promise rejections that are not handled will terminate the Node.js process with a non-zero exit code.\n","sh",[184,17590,17591,17617,17628,17638,17648,17658,17668,17678,17845],{"__ignoreMap":1646},[2113,17592,17593,17595,17598,17600,17603,17606,17609,17611,17614],{"class":2115,"line":2116},[2113,17594,2423],{"class":2119},[2113,17596,17597],{"class":2133},"node:15246",[2113,17599,5709],{"class":2119},[2113,17601,17602],{"class":2133},"UnhandledPromiseRejectionWarning:",[2113,17604,17605],{"class":2149}," Error:",[2113,17607,17608],{"class":2149}," Parse",[2113,17610,17605],{"class":2149},[2113,17612,17613],{"class":2149}," Header",[2113,17615,17616],{"class":2149}," overflow\n",[2113,17618,17619,17622,17625],{"class":2115,"line":1647},[2113,17620,17621],{"class":2133},"    at",[2113,17623,17624],{"class":2149}," TLSSocket.socketOnData",[2113,17626,17627],{"class":2119}," (_http_client.js:476:22)\n",[2113,17629,17630,17632,17635],{"class":2115,"line":1652},[2113,17631,17621],{"class":2133},[2113,17633,17634],{"class":2149}," TLSSocket.emit",[2113,17636,17637],{"class":2119}," (events.js:311:20)\n",[2113,17639,17640,17642,17645],{"class":2115,"line":2185},[2113,17641,17621],{"class":2133},[2113,17643,17644],{"class":2149}," addChunk",[2113,17646,17647],{"class":2119}," (_stream_readable.js:294:12)\n",[2113,17649,17650,17652,17655],{"class":2115,"line":2230},[2113,17651,17621],{"class":2133},[2113,17653,17654],{"class":2149}," readableAddChunk",[2113,17656,17657],{"class":2119}," (_stream_readable.js:275:11)\n",[2113,17659,17660,17662,17665],{"class":2115,"line":2235},[2113,17661,17621],{"class":2133},[2113,17663,17664],{"class":2149}," TLSSocket.Readable.push",[2113,17666,17667],{"class":2119}," (_stream_readable.js:209:10)\n",[2113,17669,17670,17672,17675],{"class":2115,"line":2241},[2113,17671,17621],{"class":2133},[2113,17673,17674],{"class":2149}," TLSWrap.onStreamRead",[2113,17676,17677],{"class":2119}," (internal\u002Fstream_base_commons.js:186:23)\n",[2113,17679,17680,17682,17684,17686,17688,17691,17694,17697,17700,17703,17706,17709,17712,17715,17718,17720,17723,17726,17729,17732,17735,17738,17741,17744,17746,17749,17751,17753,17756,17759,17761,17764,17767,17770,17773,17775,17778,17781,17784,17787,17789,17792,17795,17797,17800,17803,17805,17808,17811,17814,17817,17819,17822,17824,17827,17830,17833,17835,17838,17841,17843],{"class":2115,"line":2246},[2113,17681,2423],{"class":2119},[2113,17683,17597],{"class":2133},[2113,17685,5709],{"class":2119},[2113,17687,17602],{"class":2133},[2113,17689,17690],{"class":2149}," Unhandled",[2113,17692,17693],{"class":2149}," promise",[2113,17695,17696],{"class":2149}," rejection.",[2113,17698,17699],{"class":2149}," This",[2113,17701,17702],{"class":2149}," error",[2113,17704,17705],{"class":2149}," originated",[2113,17707,17708],{"class":2149}," either",[2113,17710,17711],{"class":2149}," by",[2113,17713,17714],{"class":2149}," throwing",[2113,17716,17717],{"class":2149}," inside",[2113,17719,2281],{"class":2149},[2113,17721,17722],{"class":2149}," an",[2113,17724,17725],{"class":2149}," async",[2113,17727,17728],{"class":2149}," function",[2113,17730,17731],{"class":2149}," without",[2113,17733,17734],{"class":2149}," a",[2113,17736,17737],{"class":2149}," catch",[2113,17739,17740],{"class":2149}," block,",[2113,17742,17743],{"class":2149}," or",[2113,17745,17711],{"class":2149},[2113,17747,17748],{"class":2149}," rejecting",[2113,17750,17734],{"class":2149},[2113,17752,17693],{"class":2149},[2113,17754,17755],{"class":2149}," which",[2113,17757,17758],{"class":2149}," was",[2113,17760,2179],{"class":2149},[2113,17762,17763],{"class":2149}," handled",[2113,17765,17766],{"class":2149}," with",[2113,17768,17769],{"class":2149}," .catch",[2113,17771,17772],{"class":2119},"()",[2113,17774,179],{"class":2149},[2113,17776,17777],{"class":2149}," To",[2113,17779,17780],{"class":2149}," terminate",[2113,17782,17783],{"class":2149}," the",[2113,17785,17786],{"class":2149}," node",[2113,17788,8582],{"class":2149},[2113,17790,17791],{"class":2149}," on",[2113,17793,17794],{"class":2149}," unhandled",[2113,17796,17693],{"class":2149},[2113,17798,17799],{"class":2149}," rejection,",[2113,17801,17802],{"class":2149}," use",[2113,17804,17783],{"class":2149},[2113,17806,17807],{"class":2149}," CLI",[2113,17809,17810],{"class":2149}," flag",[2113,17812,17813],{"class":2149}," `",[2113,17815,17816],{"class":2330},"--unhandled-rejections",[2113,17818,2335],{"class":2334},[2113,17820,17821],{"class":2149},"strict`",[2113,17823,2495],{"class":2119},[2113,17825,17826],{"class":2133},"see",[2113,17828,17829],{"class":2149}," https:\u002F\u002Fnodejs.org\u002Fapi\u002Fcli.html#cli_unhandled_rejections_mode",[2113,17831,17832],{"class":2119},")",[2113,17834,179],{"class":2334},[2113,17836,17837],{"class":2119}," (rejection ",[2113,17839,17840],{"class":2149},"id:",[2113,17842,2850],{"class":2166},[2113,17844,4660],{"class":2119},[2113,17846,17847,17849,17851,17854,17857,17859,17861,17864,17867,17870,17873,17875,17878,17880,17882,17885,17887,17889,17891,17894,17896,17898,17901,17903,17905,17907,17910,17913],{"class":2115,"line":2464},[2113,17848,2423],{"class":2119},[2113,17850,17597],{"class":2133},[2113,17852,17853],{"class":2119},") [DEP0018] ",[2113,17855,17856],{"class":2133},"DeprecationWarning:",[2113,17858,17690],{"class":2149},[2113,17860,17693],{"class":2149},[2113,17862,17863],{"class":2149}," rejections",[2113,17865,17866],{"class":2149}," are",[2113,17868,17869],{"class":2149}," deprecated.",[2113,17871,17872],{"class":2149}," In",[2113,17874,17783],{"class":2149},[2113,17876,17877],{"class":2149}," future,",[2113,17879,17693],{"class":2149},[2113,17881,17863],{"class":2149},[2113,17883,17884],{"class":2149}," that",[2113,17886,17866],{"class":2149},[2113,17888,2179],{"class":2149},[2113,17890,17763],{"class":2149},[2113,17892,17893],{"class":2149}," will",[2113,17895,17780],{"class":2149},[2113,17897,17783],{"class":2149},[2113,17899,17900],{"class":2149}," Node.js",[2113,17902,8582],{"class":2149},[2113,17904,17766],{"class":2149},[2113,17906,17734],{"class":2149},[2113,17908,17909],{"class":2149}," non-zero",[2113,17911,17912],{"class":2149}," exit",[2113,17914,17915],{"class":2149}," code.\n",[12,17917,17918],{},"Pas de chance, les headers http de la grande enseigne contiennent tellement de blabla que nodejs limite et bloque. C'est l'effet CSP (Content-Security-Policy). Les CSP sont\nutilisés pour bloquer les requêtes faites par le navigateur sur une page si elle ne sont pas autorisées.",[12,17920,17921],{},"Par exemple, si un utisateur arrive à profiter d'une faille et arrive dans un avis client à mettre une iframe, une image, ... vers un site dont il possède le contrôle. La CSP\nva dire au navigateur que ce site n'est pas autorisé. Et le hack de l'utilisateur ne fonctionnera pas totalement.",[12,17923,17924,17925,17928],{},"Mais quand un site commence à avoir beaucoup de ",[362,17926,17927],{},"\"partenaire\""," (google, vimeo, cloudfront, facebook, gstatic.com, et tous les trucs bloqués par\nadblock ...), la liste prend de la place.",[12,17930,17931],{},"Je relance node avec l'option adéquate:",[2105,17933,17935],{"className":17586,"code":17934,"language":17588,"meta":1646,"style":1646},"node --max-http-header-size=80000 app.js\n\nHTMLSelectElement {}\n",[184,17936,17937,17947,17951],{"__ignoreMap":1646},[2113,17938,17939,17941,17944],{"class":2115,"line":2116},[2113,17940,2303],{"class":2133},[2113,17942,17943],{"class":2166}," --max-http-header-size=80000",[2113,17945,17946],{"class":2149}," app.js\n",[2113,17948,17949],{"class":2115,"line":1647},[2113,17950,2125],{"emptyLinePlaceholder":1767},[2113,17952,17953,17956],{"class":2115,"line":1652},[2113,17954,17955],{"class":2133},"HTMLSelectElement",[2113,17957,17958],{"class":2149}," {}\n",[12,17960,17961],{},"Cela commence bien.",[12,17963,17964],{},"Maintenant nous allons devoir ouvrir le sélecteur de taille, choisir la taille L (qui me correspond) et cliquer dessus. Je pourrai alors récupérer l'info de stock.",[12,17966,17967,17968,17973,17980,17981,179],{},"Malheureusement beaucoup de choses sur le site sont en JavaScript, ",[49,17969,17972],{"href":17970,"rel":17971},"https:\u002F\u002Fgithub.com\u002Fjsdom\u002Fjsdom\u002Fissues\u002F2162",[347],"JSDom ne permet pas d'executer le JS de la page",[17400,17974,17975],{},[49,17976,2547],{"href":17977,"ariaDescribedBy":17978,"dataFootnoteRef":1646,"id":17979},"#user-content-fn-2",[17406],"user-content-fnref-2"," et donc je suis bloqué. Déjà, pour un truc que je voulais faire rapidement, mauvais choix de solution ;). Je commence à me dire que je vais devoir sortir la grosse\nartillerie : ",[362,17982,17983],{},"Puppeteer",[12,17985,17986],{},[121,17987],{"alt":17486,"className":17988,"src":17989},[126],"\u002FProgrammation\u002Fachat_velo\u002Fselecteur_js.png",[12,17991,17992,17996,17997,18000],{},[49,17993,17983],{"href":17994,"rel":17995},"https:\u002F\u002Fgithub.com\u002Fpuppeteer\u002Fpuppeteer",[347]," est une librairie javascript qui permet de contrôler le navigateur chrome en mode headless (sans fenêtre). Cela me\npermettrait donc de parser la page en ",[1892,17998,17999],{},"simulant"," avec un navigateur chrome.",[12,18002,18003],{},"Mais attendez, s'il y a beaucoup de javascript, il y a peut-être des requêtes XHR ?",[128,18005,18007],{"id":18006},"xhr","XHR",[12,18009,18010,18011,18014],{},"XHR, c'est pour ",[184,18012,18013],{},"XMLHttpRequest",", ce sont des requêtes executées par le Javascript pour récupérer quelques informations.",[12,18016,18017],{},"Avec Chrome, on peut récupérer la liste des requêtes qui sont effectuées lors de l'affichage de la page mais également lors des différentes interactions.",[12,18019,18020],{},[121,18021],{"alt":18022,"className":18023,"src":18024},"Vue network de Chrome",[126],"\u002FProgrammation\u002Fachat_velo\u002Fchrome_network.png",[12,18026,18027,18028,18031],{},"On active alors le filtre ",[362,18029,18030],{},"Fetch\u002FXHR"," qu nous permet de voir les requêtes qui ne sont pas du CSS, ni du HTML, ni du JS, mais uniquement des requêtes qui sont lancées\npar le code Javascript.",[12,18033,18034],{},"Bien sûr il faut éliminer les requêtes vers d'autres sites (comme abtasty, ...). On peut alors retrouver la requête qui permet de retourner le stock du site Internet:",[2105,18036,18040],{"className":18037,"code":18038,"language":18039,"meta":1646,"style":1646},"language-http shiki shiki-themes one-dark-pro","GET \u002Ffr\u002Fajax\u002Fnfs\u002Fstocks\u002Fonline?skuIds=4216661,4216663,4216662,4216664\n","http",[184,18041,18042],{"__ignoreMap":1646},[2113,18043,18044],{"class":2115,"line":2116},[2113,18045,18038],{},[12,18047,18048],{},"Et avec la réponse:",[2105,18050,18054],{"className":18051,"code":18052,"language":18053,"meta":1646,"style":1646},"language-json shiki shiki-themes one-dark-pro","{\n  \"4216661\": {\n    \"stockOnline\": 0\n  },\n  \"4216662\": {\n    \"stockOnline\": 0\n  },\n  \"4216663\": {\n    \"stockOnline\": 0\n  },\n  \"4216664\": {\n    \"stockOnline\": 18\n  }\n}\n","json",[184,18055,18056,18061,18068,18078,18082,18089,18097,18101,18108,18116,18120,18127,18136,18140],{"__ignoreMap":1646},[2113,18057,18058],{"class":2115,"line":2116},[2113,18059,18060],{"class":2119},"{\n",[2113,18062,18063,18066],{"class":2115,"line":1647},[2113,18064,18065],{"class":2330},"  \"4216661\"",[2113,18067,8091],{"class":2119},[2113,18069,18070,18073,18075],{"class":2115,"line":1652},[2113,18071,18072],{"class":2330},"    \"stockOnline\"",[2113,18074,4429],{"class":2119},[2113,18076,18077],{"class":2166},"0\n",[2113,18079,18080],{"class":2115,"line":2185},[2113,18081,8490],{"class":2119},[2113,18083,18084,18087],{"class":2115,"line":2230},[2113,18085,18086],{"class":2330},"  \"4216662\"",[2113,18088,8091],{"class":2119},[2113,18090,18091,18093,18095],{"class":2115,"line":2235},[2113,18092,18072],{"class":2330},[2113,18094,4429],{"class":2119},[2113,18096,18077],{"class":2166},[2113,18098,18099],{"class":2115,"line":2241},[2113,18100,8490],{"class":2119},[2113,18102,18103,18106],{"class":2115,"line":2246},[2113,18104,18105],{"class":2330},"  \"4216663\"",[2113,18107,8091],{"class":2119},[2113,18109,18110,18112,18114],{"class":2115,"line":2464},[2113,18111,18072],{"class":2330},[2113,18113,4429],{"class":2119},[2113,18115,18077],{"class":2166},[2113,18117,18118],{"class":2115,"line":2085},[2113,18119,8490],{"class":2119},[2113,18121,18122,18125],{"class":2115,"line":2514},[2113,18123,18124],{"class":2330},"  \"4216664\"",[2113,18126,8091],{"class":2119},[2113,18128,18129,18131,18133],{"class":2115,"line":2533},[2113,18130,18072],{"class":2330},[2113,18132,4429],{"class":2119},[2113,18134,18135],{"class":2166},"18\n",[2113,18137,18138],{"class":2115,"line":2556},[2113,18139,2572],{"class":2119},[2113,18141,18142],{"class":2115,"line":2569},[2113,18143,2599],{"class":2119},[12,18145,18146],{},"La requête nous retourne les différents stocks pour les différentes tailles de cadre (S, M, L, XL). C'est la taille L qui m'intéresse, mais comment savoir quel\ncode produit correspond à quelle taille ?",[128,18148,18150],{"id":18149},"code-source-de-la-page","Code source de la page",[12,18152,18153],{},"On peut afficher le code source de la page (clic droit sur la page HTML : Afficher la source). Dans le source HTML on retrouve le bout de code suivant :",[2105,18155,18159],{"className":18156,"code":18157,"language":18158,"meta":1646,"style":1646},"language-html shiki shiki-themes one-dark-pro","\u003Cscript id=\"__dkt\" type=\"application\u002Fjson\">\n  ...\n\u003C\u002Fscript>\n","html",[184,18160,18161,18186,18191],{"__ignoreMap":1646},[2113,18162,18163,18165,18168,18171,18173,18176,18178,18180,18183],{"class":2115,"line":2116},[2113,18164,3109],{"class":2119},[2113,18166,18167],{"class":2330},"script",[2113,18169,18170],{"class":2166}," id",[2113,18172,2335],{"class":2119},[2113,18174,18175],{"class":2149},"\"__dkt\"",[2113,18177,16342],{"class":2166},[2113,18179,2335],{"class":2119},[2113,18181,18182],{"class":2149},"\"application\u002Fjson\"",[2113,18184,18185],{"class":2119},">\n",[2113,18187,18188],{"class":2115,"line":1647},[2113,18189,18190],{"class":2119},"  ...\n",[2113,18192,18193,18196,18198],{"class":2115,"line":1652},[2113,18194,18195],{"class":2119},"\u003C\u002F",[2113,18197,18167],{"class":2330},[2113,18199,18185],{"class":2119},[12,18201,18202],{},"Le JSON contient le contexte qui sera utilisé par le code Javascript pour la génération de la page et le côté interactif. C'est l'équivalent d'un appel XHR intégré au démarrage\nde la page. On peut y retrouver ce que l'on cherche :",[2105,18204,18206],{"className":18051,"code":18205,"language":18053,"meta":1646,"style":1646},"{\n  \"skus\": [\n    {\n      \"skuId\": \"4216661\",\n      \"size\": \"S\",\n      \"grossWeight\": \"20.9\",\n      \"price\": 2199,\n      \"isNotAvailable\": true,\n      \"availableInStores\": []\n    },\n    {\n      \"skuId\": \"4216663\",\n      \"size\": \"M\",\n      \"grossWeight\": \"21.0\",\n      \"price\": 2199,\n      \"isNotAvailable\": true,\n      \"availableInStores\": []\n    },\n    {\n      \"skuId\": \"4216662\",\n      \"size\": \"L\",\n      \"grossWeight\": \"21.1\",\n      \"price\": 2199,\n      \"isNotAvailable\": true,\n      \"availableInStores\": []\n    },\n    {\n      \"skuId\": \"4216664\",\n      \"size\": \"XL\",\n      \"grossWeight\": \"21.2\",\n      \"price\": 2199,\n      \"availableInStores\": []\n    }\n  ]\n}\n",[184,18207,18208,18212,18220,18225,18237,18249,18261,18273,18284,18292,18296,18300,18311,18322,18333,18343,18353,18359,18363,18367,18378,18389,18400,18410,18420,18426,18430,18434,18445,18456,18467,18477,18483,18487,18492],{"__ignoreMap":1646},[2113,18209,18210],{"class":2115,"line":2116},[2113,18211,18060],{"class":2119},[2113,18213,18214,18217],{"class":2115,"line":1647},[2113,18215,18216],{"class":2330},"  \"skus\"",[2113,18218,18219],{"class":2119},": [\n",[2113,18221,18222],{"class":2115,"line":1652},[2113,18223,18224],{"class":2119},"    {\n",[2113,18226,18227,18230,18232,18235],{"class":2115,"line":2185},[2113,18228,18229],{"class":2330},"      \"skuId\"",[2113,18231,4429],{"class":2119},[2113,18233,18234],{"class":2149},"\"4216661\"",[2113,18236,4706],{"class":2119},[2113,18238,18239,18242,18244,18247],{"class":2115,"line":2230},[2113,18240,18241],{"class":2330},"      \"size\"",[2113,18243,4429],{"class":2119},[2113,18245,18246],{"class":2149},"\"S\"",[2113,18248,4706],{"class":2119},[2113,18250,18251,18254,18256,18259],{"class":2115,"line":2235},[2113,18252,18253],{"class":2330},"      \"grossWeight\"",[2113,18255,4429],{"class":2119},[2113,18257,18258],{"class":2149},"\"20.9\"",[2113,18260,4706],{"class":2119},[2113,18262,18263,18266,18268,18271],{"class":2115,"line":2241},[2113,18264,18265],{"class":2330},"      \"price\"",[2113,18267,4429],{"class":2119},[2113,18269,18270],{"class":2166},"2199",[2113,18272,4706],{"class":2119},[2113,18274,18275,18278,18280,18282],{"class":2115,"line":2246},[2113,18276,18277],{"class":2330},"      \"isNotAvailable\"",[2113,18279,4429],{"class":2119},[2113,18281,4819],{"class":2166},[2113,18283,4706],{"class":2119},[2113,18285,18286,18289],{"class":2115,"line":2464},[2113,18287,18288],{"class":2330},"      \"availableInStores\"",[2113,18290,18291],{"class":2119},": []\n",[2113,18293,18294],{"class":2115,"line":2085},[2113,18295,8237],{"class":2119},[2113,18297,18298],{"class":2115,"line":2514},[2113,18299,18224],{"class":2119},[2113,18301,18302,18304,18306,18309],{"class":2115,"line":2533},[2113,18303,18229],{"class":2330},[2113,18305,4429],{"class":2119},[2113,18307,18308],{"class":2149},"\"4216663\"",[2113,18310,4706],{"class":2119},[2113,18312,18313,18315,18317,18320],{"class":2115,"line":2556},[2113,18314,18241],{"class":2330},[2113,18316,4429],{"class":2119},[2113,18318,18319],{"class":2149},"\"M\"",[2113,18321,4706],{"class":2119},[2113,18323,18324,18326,18328,18331],{"class":2115,"line":2569},[2113,18325,18253],{"class":2330},[2113,18327,4429],{"class":2119},[2113,18329,18330],{"class":2149},"\"21.0\"",[2113,18332,4706],{"class":2119},[2113,18334,18335,18337,18339,18341],{"class":2115,"line":2575},[2113,18336,18265],{"class":2330},[2113,18338,4429],{"class":2119},[2113,18340,18270],{"class":2166},[2113,18342,4706],{"class":2119},[2113,18344,18345,18347,18349,18351],{"class":2115,"line":2596},[2113,18346,18277],{"class":2330},[2113,18348,4429],{"class":2119},[2113,18350,4819],{"class":2166},[2113,18352,4706],{"class":2119},[2113,18354,18355,18357],{"class":2115,"line":3192},[2113,18356,18288],{"class":2330},[2113,18358,18291],{"class":2119},[2113,18360,18361],{"class":2115,"line":3213},[2113,18362,8237],{"class":2119},[2113,18364,18365],{"class":2115,"line":3236},[2113,18366,18224],{"class":2119},[2113,18368,18369,18371,18373,18376],{"class":2115,"line":3248},[2113,18370,18229],{"class":2330},[2113,18372,4429],{"class":2119},[2113,18374,18375],{"class":2149},"\"4216662\"",[2113,18377,4706],{"class":2119},[2113,18379,18380,18382,18384,18387],{"class":2115,"line":4899},[2113,18381,18241],{"class":2330},[2113,18383,4429],{"class":2119},[2113,18385,18386],{"class":2149},"\"L\"",[2113,18388,4706],{"class":2119},[2113,18390,18391,18393,18395,18398],{"class":2115,"line":1777},[2113,18392,18253],{"class":2330},[2113,18394,4429],{"class":2119},[2113,18396,18397],{"class":2149},"\"21.1\"",[2113,18399,4706],{"class":2119},[2113,18401,18402,18404,18406,18408],{"class":2115,"line":4931},[2113,18403,18265],{"class":2330},[2113,18405,4429],{"class":2119},[2113,18407,18270],{"class":2166},[2113,18409,4706],{"class":2119},[2113,18411,18412,18414,18416,18418],{"class":2115,"line":4961},[2113,18413,18277],{"class":2330},[2113,18415,4429],{"class":2119},[2113,18417,4819],{"class":2166},[2113,18419,4706],{"class":2119},[2113,18421,18422,18424],{"class":2115,"line":4976},[2113,18423,18288],{"class":2330},[2113,18425,18291],{"class":2119},[2113,18427,18428],{"class":2115,"line":4993},[2113,18429,8237],{"class":2119},[2113,18431,18432],{"class":2115,"line":5013},[2113,18433,18224],{"class":2119},[2113,18435,18436,18438,18440,18443],{"class":2115,"line":5018},[2113,18437,18229],{"class":2330},[2113,18439,4429],{"class":2119},[2113,18441,18442],{"class":2149},"\"4216664\"",[2113,18444,4706],{"class":2119},[2113,18446,18447,18449,18451,18454],{"class":2115,"line":5042},[2113,18448,18241],{"class":2330},[2113,18450,4429],{"class":2119},[2113,18452,18453],{"class":2149},"\"XL\"",[2113,18455,4706],{"class":2119},[2113,18457,18458,18460,18462,18465],{"class":2115,"line":5057},[2113,18459,18253],{"class":2330},[2113,18461,4429],{"class":2119},[2113,18463,18464],{"class":2149},"\"21.2\"",[2113,18466,4706],{"class":2119},[2113,18468,18469,18471,18473,18475],{"class":2115,"line":5062},[2113,18470,18265],{"class":2330},[2113,18472,4429],{"class":2119},[2113,18474,18270],{"class":2166},[2113,18476,4706],{"class":2119},[2113,18478,18479,18481],{"class":2115,"line":5098},[2113,18480,18288],{"class":2330},[2113,18482,18291],{"class":2119},[2113,18484,18485],{"class":2115,"line":5117},[2113,18486,4665],{"class":2119},[2113,18488,18489],{"class":2115,"line":5142},[2113,18490,18491],{"class":2119},"  ]\n",[2113,18493,18494],{"class":2115,"line":5148},[2113,18495,2599],{"class":2119},[12,18497,18498],{},"La taille qui m'intéresse est donc le SKU 4216662.",[128,18500,18502],{"id":18501},"stock-des-magasins","Stock des magasins",[12,18504,18505],{},"Vient ensuite la partie sur le stock des magasins. Je clique sur le bouton permettant de vérifier le stock des magasins, rentre mon code postal, et je me\nretrouve avec la liste des magasins.",[12,18507,18508],{},"Je regarde les requêtes et trouve celle qui m'intéresse avec l'ensemble des magasins du nord de la france:",[2105,18510,18512],{"className":18037,"code":18511,"language":18039,"meta":1646,"style":1646},"GET \u002Ffr\u002Fajax\u002Frest\u002Fmodel\u002Fcom\u002Fdecathlon\u002Fcube\u002Fcommerce\u002Finventory\u002FInventoryActor\u002FgetStoreAvailability?storeIds=0070000200002%2C0070093300933%2C0070043700437%2C0070051800518%2C0070011800118%2C0070219902199%2C0070050400504%2C0070253902539%2C0070001500015%2C0070064800648&skuId=4216662&modelId=8614842&displayStoreDetails=false\n",[184,18513,18514],{"__ignoreMap":1646},[2113,18515,18516],{"class":2115,"line":2116},[2113,18517,18511],{},[12,18519,18048],{},[2105,18521,18523],{"className":18051,"code":18522,"language":18053,"meta":1646,"style":1646},"{\n  \"responseTO\": {\n    \"data\": [\n      {\n        \"aboveThreshold\": false,\n        \"address\": null,\n        \"availabilityInfo\": \"noStock\",\n        \"clickNcollect1h\": false,\n        \"favoriteStore\": false,\n        \"latitude\": 0,\n        \"longitude\": 0,\n        \"optionId\": null,\n        \"originId\": null,\n        \"phoneNumber\": null,\n        \"priceId\": null,\n        \"quantity\": 0,\n        \"securedStockLevel\": 0,\n        \"skuId\": \"4216662\",\n        \"storeId\": \"0070000200002\",\n        \"storeName\": \"Neuville en Ferrain - Roncq\",\n        \"storeSchedule\": null,\n        \"storeUrl\": null\n      },\n      {\n        \"aboveThreshold\": false,\n        \"address\": null,\n        \"availabilityInfo\": \"noStock\",\n        \"clickNcollect1h\": false,\n        \"favoriteStore\": false,\n        \"latitude\": 0,\n        \"longitude\": 0,\n        \"optionId\": null,\n        \"originId\": null,\n        \"phoneNumber\": null,\n        \"priceId\": null,\n        \"quantity\": 0,\n        \"securedStockLevel\": 1,\n        \"skuId\": \"4216662\",\n        \"storeId\": \"0070051800518\",\n        \"storeName\": \"Marcq-en-Baroeul DOMYOS\",\n        \"storeSchedule\": null,\n        \"storeUrl\": null\n      },\n      {\n        \"aboveThreshold\": false,\n        \"address\": null,\n        \"availabilityInfo\": \"noStock\",\n        \"clickNcollect1h\": false,\n        \"favoriteStore\": false,\n        \"latitude\": 0,\n        \"longitude\": 0,\n        \"optionId\": null,\n        \"originId\": null,\n        \"phoneNumber\": null,\n        \"priceId\": null,\n        \"quantity\": 0,\n        \"securedStockLevel\": 1,\n        \"skuId\": \"4216662\",\n        \"storeId\": \"0070219902199\",\n        \"storeName\": \"Villeneuve d'Ascq - DX\",\n        \"storeSchedule\": null,\n        \"storeUrl\": null\n      },\n      ...\n    ],\n  }\n}\n",[184,18524,18525,18529,18536,18543,18548,18560,18572,18584,18595,18606,18617,18628,18639,18650,18661,18672,18683,18694,18705,18717,18729,18740,18750,18754,18758,18768,18778,18788,18798,18808,18818,18828,18838,18848,18858,18868,18878,18888,18898,18909,18920,18930,18938,18942,18946,18956,18966,18976,18986,18996,19006,19016,19026,19036,19046,19056,19066,19076,19086,19097,19108,19118,19126,19130,19136,19141,19145],{"__ignoreMap":1646},[2113,18526,18527],{"class":2115,"line":2116},[2113,18528,18060],{"class":2119},[2113,18530,18531,18534],{"class":2115,"line":1647},[2113,18532,18533],{"class":2330},"  \"responseTO\"",[2113,18535,8091],{"class":2119},[2113,18537,18538,18541],{"class":2115,"line":1652},[2113,18539,18540],{"class":2330},"    \"data\"",[2113,18542,18219],{"class":2119},[2113,18544,18545],{"class":2115,"line":2185},[2113,18546,18547],{"class":2119},"      {\n",[2113,18549,18550,18553,18555,18558],{"class":2115,"line":2230},[2113,18551,18552],{"class":2330},"        \"aboveThreshold\"",[2113,18554,4429],{"class":2119},[2113,18556,18557],{"class":2166},"false",[2113,18559,4706],{"class":2119},[2113,18561,18562,18565,18567,18570],{"class":2115,"line":2235},[2113,18563,18564],{"class":2330},"        \"address\"",[2113,18566,4429],{"class":2119},[2113,18568,18569],{"class":2166},"null",[2113,18571,4706],{"class":2119},[2113,18573,18574,18577,18579,18582],{"class":2115,"line":2241},[2113,18575,18576],{"class":2330},"        \"availabilityInfo\"",[2113,18578,4429],{"class":2119},[2113,18580,18581],{"class":2149},"\"noStock\"",[2113,18583,4706],{"class":2119},[2113,18585,18586,18589,18591,18593],{"class":2115,"line":2246},[2113,18587,18588],{"class":2330},"        \"clickNcollect1h\"",[2113,18590,4429],{"class":2119},[2113,18592,18557],{"class":2166},[2113,18594,4706],{"class":2119},[2113,18596,18597,18600,18602,18604],{"class":2115,"line":2464},[2113,18598,18599],{"class":2330},"        \"favoriteStore\"",[2113,18601,4429],{"class":2119},[2113,18603,18557],{"class":2166},[2113,18605,4706],{"class":2119},[2113,18607,18608,18611,18613,18615],{"class":2115,"line":2085},[2113,18609,18610],{"class":2330},"        \"latitude\"",[2113,18612,4429],{"class":2119},[2113,18614,3095],{"class":2166},[2113,18616,4706],{"class":2119},[2113,18618,18619,18622,18624,18626],{"class":2115,"line":2514},[2113,18620,18621],{"class":2330},"        \"longitude\"",[2113,18623,4429],{"class":2119},[2113,18625,3095],{"class":2166},[2113,18627,4706],{"class":2119},[2113,18629,18630,18633,18635,18637],{"class":2115,"line":2533},[2113,18631,18632],{"class":2330},"        \"optionId\"",[2113,18634,4429],{"class":2119},[2113,18636,18569],{"class":2166},[2113,18638,4706],{"class":2119},[2113,18640,18641,18644,18646,18648],{"class":2115,"line":2556},[2113,18642,18643],{"class":2330},"        \"originId\"",[2113,18645,4429],{"class":2119},[2113,18647,18569],{"class":2166},[2113,18649,4706],{"class":2119},[2113,18651,18652,18655,18657,18659],{"class":2115,"line":2569},[2113,18653,18654],{"class":2330},"        \"phoneNumber\"",[2113,18656,4429],{"class":2119},[2113,18658,18569],{"class":2166},[2113,18660,4706],{"class":2119},[2113,18662,18663,18666,18668,18670],{"class":2115,"line":2575},[2113,18664,18665],{"class":2330},"        \"priceId\"",[2113,18667,4429],{"class":2119},[2113,18669,18569],{"class":2166},[2113,18671,4706],{"class":2119},[2113,18673,18674,18677,18679,18681],{"class":2115,"line":2596},[2113,18675,18676],{"class":2330},"        \"quantity\"",[2113,18678,4429],{"class":2119},[2113,18680,3095],{"class":2166},[2113,18682,4706],{"class":2119},[2113,18684,18685,18688,18690,18692],{"class":2115,"line":3192},[2113,18686,18687],{"class":2330},"        \"securedStockLevel\"",[2113,18689,4429],{"class":2119},[2113,18691,3095],{"class":2166},[2113,18693,4706],{"class":2119},[2113,18695,18696,18699,18701,18703],{"class":2115,"line":3213},[2113,18697,18698],{"class":2330},"        \"skuId\"",[2113,18700,4429],{"class":2119},[2113,18702,18375],{"class":2149},[2113,18704,4706],{"class":2119},[2113,18706,18707,18710,18712,18715],{"class":2115,"line":3236},[2113,18708,18709],{"class":2330},"        \"storeId\"",[2113,18711,4429],{"class":2119},[2113,18713,18714],{"class":2149},"\"0070000200002\"",[2113,18716,4706],{"class":2119},[2113,18718,18719,18722,18724,18727],{"class":2115,"line":3248},[2113,18720,18721],{"class":2330},"        \"storeName\"",[2113,18723,4429],{"class":2119},[2113,18725,18726],{"class":2149},"\"Neuville en Ferrain - Roncq\"",[2113,18728,4706],{"class":2119},[2113,18730,18731,18734,18736,18738],{"class":2115,"line":4899},[2113,18732,18733],{"class":2330},"        \"storeSchedule\"",[2113,18735,4429],{"class":2119},[2113,18737,18569],{"class":2166},[2113,18739,4706],{"class":2119},[2113,18741,18742,18745,18747],{"class":2115,"line":1777},[2113,18743,18744],{"class":2330},"        \"storeUrl\"",[2113,18746,4429],{"class":2119},[2113,18748,18749],{"class":2166},"null\n",[2113,18751,18752],{"class":2115,"line":4931},[2113,18753,12282],{"class":2119},[2113,18755,18756],{"class":2115,"line":4961},[2113,18757,18547],{"class":2119},[2113,18759,18760,18762,18764,18766],{"class":2115,"line":4976},[2113,18761,18552],{"class":2330},[2113,18763,4429],{"class":2119},[2113,18765,18557],{"class":2166},[2113,18767,4706],{"class":2119},[2113,18769,18770,18772,18774,18776],{"class":2115,"line":4993},[2113,18771,18564],{"class":2330},[2113,18773,4429],{"class":2119},[2113,18775,18569],{"class":2166},[2113,18777,4706],{"class":2119},[2113,18779,18780,18782,18784,18786],{"class":2115,"line":5013},[2113,18781,18576],{"class":2330},[2113,18783,4429],{"class":2119},[2113,18785,18581],{"class":2149},[2113,18787,4706],{"class":2119},[2113,18789,18790,18792,18794,18796],{"class":2115,"line":5018},[2113,18791,18588],{"class":2330},[2113,18793,4429],{"class":2119},[2113,18795,18557],{"class":2166},[2113,18797,4706],{"class":2119},[2113,18799,18800,18802,18804,18806],{"class":2115,"line":5042},[2113,18801,18599],{"class":2330},[2113,18803,4429],{"class":2119},[2113,18805,18557],{"class":2166},[2113,18807,4706],{"class":2119},[2113,18809,18810,18812,18814,18816],{"class":2115,"line":5057},[2113,18811,18610],{"class":2330},[2113,18813,4429],{"class":2119},[2113,18815,3095],{"class":2166},[2113,18817,4706],{"class":2119},[2113,18819,18820,18822,18824,18826],{"class":2115,"line":5062},[2113,18821,18621],{"class":2330},[2113,18823,4429],{"class":2119},[2113,18825,3095],{"class":2166},[2113,18827,4706],{"class":2119},[2113,18829,18830,18832,18834,18836],{"class":2115,"line":5098},[2113,18831,18632],{"class":2330},[2113,18833,4429],{"class":2119},[2113,18835,18569],{"class":2166},[2113,18837,4706],{"class":2119},[2113,18839,18840,18842,18844,18846],{"class":2115,"line":5117},[2113,18841,18643],{"class":2330},[2113,18843,4429],{"class":2119},[2113,18845,18569],{"class":2166},[2113,18847,4706],{"class":2119},[2113,18849,18850,18852,18854,18856],{"class":2115,"line":5142},[2113,18851,18654],{"class":2330},[2113,18853,4429],{"class":2119},[2113,18855,18569],{"class":2166},[2113,18857,4706],{"class":2119},[2113,18859,18860,18862,18864,18866],{"class":2115,"line":5148},[2113,18861,18665],{"class":2330},[2113,18863,4429],{"class":2119},[2113,18865,18569],{"class":2166},[2113,18867,4706],{"class":2119},[2113,18869,18870,18872,18874,18876],{"class":2115,"line":5163},[2113,18871,18676],{"class":2330},[2113,18873,4429],{"class":2119},[2113,18875,3095],{"class":2166},[2113,18877,4706],{"class":2119},[2113,18879,18880,18882,18884,18886],{"class":2115,"line":5169},[2113,18881,18687],{"class":2330},[2113,18883,4429],{"class":2119},[2113,18885,3686],{"class":2166},[2113,18887,4706],{"class":2119},[2113,18889,18890,18892,18894,18896],{"class":2115,"line":5175},[2113,18891,18698],{"class":2330},[2113,18893,4429],{"class":2119},[2113,18895,18375],{"class":2149},[2113,18897,4706],{"class":2119},[2113,18899,18900,18902,18904,18907],{"class":2115,"line":5180},[2113,18901,18709],{"class":2330},[2113,18903,4429],{"class":2119},[2113,18905,18906],{"class":2149},"\"0070051800518\"",[2113,18908,4706],{"class":2119},[2113,18910,18911,18913,18915,18918],{"class":2115,"line":5199},[2113,18912,18721],{"class":2330},[2113,18914,4429],{"class":2119},[2113,18916,18917],{"class":2149},"\"Marcq-en-Baroeul DOMYOS\"",[2113,18919,4706],{"class":2119},[2113,18921,18922,18924,18926,18928],{"class":2115,"line":5204},[2113,18923,18733],{"class":2330},[2113,18925,4429],{"class":2119},[2113,18927,18569],{"class":2166},[2113,18929,4706],{"class":2119},[2113,18931,18932,18934,18936],{"class":2115,"line":5209},[2113,18933,18744],{"class":2330},[2113,18935,4429],{"class":2119},[2113,18937,18749],{"class":2166},[2113,18939,18940],{"class":2115,"line":5232},[2113,18941,12282],{"class":2119},[2113,18943,18944],{"class":2115,"line":5237},[2113,18945,18547],{"class":2119},[2113,18947,18948,18950,18952,18954],{"class":2115,"line":5242},[2113,18949,18552],{"class":2330},[2113,18951,4429],{"class":2119},[2113,18953,18557],{"class":2166},[2113,18955,4706],{"class":2119},[2113,18957,18958,18960,18962,18964],{"class":2115,"line":5267},[2113,18959,18564],{"class":2330},[2113,18961,4429],{"class":2119},[2113,18963,18569],{"class":2166},[2113,18965,4706],{"class":2119},[2113,18967,18968,18970,18972,18974],{"class":2115,"line":5282},[2113,18969,18576],{"class":2330},[2113,18971,4429],{"class":2119},[2113,18973,18581],{"class":2149},[2113,18975,4706],{"class":2119},[2113,18977,18978,18980,18982,18984],{"class":2115,"line":5295},[2113,18979,18588],{"class":2330},[2113,18981,4429],{"class":2119},[2113,18983,18557],{"class":2166},[2113,18985,4706],{"class":2119},[2113,18987,18988,18990,18992,18994],{"class":2115,"line":5310},[2113,18989,18599],{"class":2330},[2113,18991,4429],{"class":2119},[2113,18993,18557],{"class":2166},[2113,18995,4706],{"class":2119},[2113,18997,18998,19000,19002,19004],{"class":2115,"line":5315},[2113,18999,18610],{"class":2330},[2113,19001,4429],{"class":2119},[2113,19003,3095],{"class":2166},[2113,19005,4706],{"class":2119},[2113,19007,19008,19010,19012,19014],{"class":2115,"line":5320},[2113,19009,18621],{"class":2330},[2113,19011,4429],{"class":2119},[2113,19013,3095],{"class":2166},[2113,19015,4706],{"class":2119},[2113,19017,19018,19020,19022,19024],{"class":2115,"line":8625},[2113,19019,18632],{"class":2330},[2113,19021,4429],{"class":2119},[2113,19023,18569],{"class":2166},[2113,19025,4706],{"class":2119},[2113,19027,19028,19030,19032,19034],{"class":2115,"line":8631},[2113,19029,18643],{"class":2330},[2113,19031,4429],{"class":2119},[2113,19033,18569],{"class":2166},[2113,19035,4706],{"class":2119},[2113,19037,19038,19040,19042,19044],{"class":2115,"line":8637},[2113,19039,18654],{"class":2330},[2113,19041,4429],{"class":2119},[2113,19043,18569],{"class":2166},[2113,19045,4706],{"class":2119},[2113,19047,19048,19050,19052,19054],{"class":2115,"line":8652},[2113,19049,18665],{"class":2330},[2113,19051,4429],{"class":2119},[2113,19053,18569],{"class":2166},[2113,19055,4706],{"class":2119},[2113,19057,19058,19060,19062,19064],{"class":2115,"line":8674},[2113,19059,18676],{"class":2330},[2113,19061,4429],{"class":2119},[2113,19063,3095],{"class":2166},[2113,19065,4706],{"class":2119},[2113,19067,19068,19070,19072,19074],{"class":2115,"line":8706},[2113,19069,18687],{"class":2330},[2113,19071,4429],{"class":2119},[2113,19073,3686],{"class":2166},[2113,19075,4706],{"class":2119},[2113,19077,19078,19080,19082,19084],{"class":2115,"line":8724},[2113,19079,18698],{"class":2330},[2113,19081,4429],{"class":2119},[2113,19083,18375],{"class":2149},[2113,19085,4706],{"class":2119},[2113,19087,19088,19090,19092,19095],{"class":2115,"line":8729},[2113,19089,18709],{"class":2330},[2113,19091,4429],{"class":2119},[2113,19093,19094],{"class":2149},"\"0070219902199\"",[2113,19096,4706],{"class":2119},[2113,19098,19099,19101,19103,19106],{"class":2115,"line":8734},[2113,19100,18721],{"class":2330},[2113,19102,4429],{"class":2119},[2113,19104,19105],{"class":2149},"\"Villeneuve d'Ascq - DX\"",[2113,19107,4706],{"class":2119},[2113,19109,19110,19112,19114,19116],{"class":2115,"line":8740},[2113,19111,18733],{"class":2330},[2113,19113,4429],{"class":2119},[2113,19115,18569],{"class":2166},[2113,19117,4706],{"class":2119},[2113,19119,19120,19122,19124],{"class":2115,"line":8772},[2113,19121,18744],{"class":2330},[2113,19123,4429],{"class":2119},[2113,19125,18749],{"class":2166},[2113,19127,19128],{"class":2115,"line":8778},[2113,19129,12282],{"class":2119},[2113,19131,19132],{"class":2115,"line":8789},[2113,19133,19135],{"class":19134},"sLaUg","      ...\n",[2113,19137,19138],{"class":2115,"line":8795},[2113,19139,19140],{"class":2119},"    ],\n",[2113,19142,19143],{"class":2115,"line":8817},[2113,19144,2572],{"class":2119},[2113,19146,19147],{"class":2115,"line":8822},[2113,19148,2599],{"class":2119},[12,19150,19151],{},"Parfait, j'ai l'ensemble des requêtes et le résultat permettant de recupérer les informations dont j'ai besoin. Il ne me reste plus qu'à coder. Je crée donc\nun premier service dont le but est pour un code donné de récupérer le stock internet et les stocks en magasin.",[12,19153,19154,19155,19157],{},"Il est probable que j'aurais pu mieux écrire mon code (et d'ailleurs mieux utiliser RxJS), mais je vous rappelle, ceci est un 1",[17400,19156,17461],{}," jet. Le but étant de déployer\ncela le plus rapidement possible.",[2105,19159,19161],{"className":15749,"code":19160,"language":15751,"meta":1646,"style":1646},"@Injectable()\nexport class AppService {\n  constructor(private httpService: HttpService) {}\n\n  async getVtcElectectricL(code: string): Promise\u003CStockInformation[]> {\n    const availability = [];\n    const sku = await firstValueFrom(\n      this.httpService.get\u003CStocksOnline>(STOCKS_ONLINE([code])),\n    );\n    const onlineStocks = sku.data[code]?.stockOnline;\n\n    availability.push({\n      type: 'Internet',\n      stocks: onlineStocks,\n    });\n\n    const stores = await firstValueFrom(\n      this.httpService.get\u003CStocksStores>(STOCKS_STORE(code)),\n    );\n\n    availability.push(\n      ...stores.data.responseTO.data\n        .map((store) => ({\n          type: store.storeName,\n          stocks: store.quantity,\n        }))\n        .sort((a, b) => b.stocks - a.stocks),\n    );\n\n    return availability.map((store) => ({\n      ...store,\n      availability: store.stocks > 0 ? 'available' : 'not_available',\n    }));\n  }\n}\n",[184,19162,19163,19173,19185,19205,19209,19240,19253,19269,19303,19307,19334,19338,19350,19362,19374,19379,19383,19398,19427,19431,19435,19445,19467,19484,19500,19516,19521,19557,19561,19565,19585,19593,19622,19627,19631],{"__ignoreMap":1646},[2113,19164,19165,19168,19171],{"class":2115,"line":2116},[2113,19166,19167],{"class":2119},"@",[2113,19169,19170],{"class":2133},"Injectable",[2113,19172,6745],{"class":2119},[2113,19174,19175,19177,19180,19183],{"class":2115,"line":1647},[2113,19176,2327],{"class":2326},[2113,19178,19179],{"class":2326}," class",[2113,19181,19182],{"class":2414}," AppService",[2113,19184,2647],{"class":2119},[2113,19186,19187,19190,19192,19195,19198,19200,19203],{"class":2115,"line":1652},[2113,19188,19189],{"class":2326},"  constructor",[2113,19191,2423],{"class":2119},[2113,19193,19194],{"class":2326},"private",[2113,19196,19197],{"class":2429}," httpService",[2113,19199,4429],{"class":2119},[2113,19201,19202],{"class":2414},"HttpService",[2113,19204,8980],{"class":2119},[2113,19206,19207],{"class":2115,"line":2185},[2113,19208,2125],{"emptyLinePlaceholder":1767},[2113,19210,19211,19214,19217,19219,19221,19223,19226,19229,19232,19234,19237],{"class":2115,"line":2230},[2113,19212,19213],{"class":2326},"  async",[2113,19215,19216],{"class":2133}," getVtcElectectricL",[2113,19218,2423],{"class":2119},[2113,19220,184],{"class":2429},[2113,19222,4429],{"class":2119},[2113,19224,19225],{"class":2414},"string",[2113,19227,19228],{"class":2119},"): ",[2113,19230,19231],{"class":2414},"Promise",[2113,19233,3109],{"class":2119},[2113,19235,19236],{"class":2414},"StockInformation",[2113,19238,19239],{"class":2119},"[]> {\n",[2113,19241,19242,19245,19248,19250],{"class":2115,"line":2235},[2113,19243,19244],{"class":2326},"    const",[2113,19246,19247],{"class":2414}," availability",[2113,19249,2153],{"class":2334},[2113,19251,19252],{"class":2119}," [];\n",[2113,19254,19255,19257,19260,19262,19264,19267],{"class":2115,"line":2241},[2113,19256,19244],{"class":2326},[2113,19258,19259],{"class":2414}," sku",[2113,19261,2153],{"class":2334},[2113,19263,17521],{"class":2326},[2113,19265,19266],{"class":2133}," firstValueFrom",[2113,19268,3146],{"class":2119},[2113,19270,19271,19274,19276,19279,19281,19284,19286,19289,19292,19295,19298,19300],{"class":2115,"line":2246},[2113,19272,19273],{"class":2414},"      this",[2113,19275,179],{"class":2119},[2113,19277,19278],{"class":2414},"httpService",[2113,19280,179],{"class":2119},[2113,19282,19283],{"class":2133},"get",[2113,19285,3109],{"class":2119},[2113,19287,19288],{"class":2414},"StocksOnline",[2113,19290,19291],{"class":2119},">(",[2113,19293,19294],{"class":2133},"STOCKS_ONLINE",[2113,19296,19297],{"class":2119},"([",[2113,19299,184],{"class":2330},[2113,19301,19302],{"class":2119},"])),\n",[2113,19304,19305],{"class":2115,"line":2464},[2113,19306,10961],{"class":2119},[2113,19308,19309,19311,19314,19316,19318,19320,19322,19324,19326,19329,19332],{"class":2115,"line":2085},[2113,19310,19244],{"class":2326},[2113,19312,19313],{"class":2414}," onlineStocks",[2113,19315,2153],{"class":2334},[2113,19317,19259],{"class":2414},[2113,19319,179],{"class":2119},[2113,19321,10154],{"class":2330},[2113,19323,4124],{"class":2119},[2113,19325,184],{"class":2330},[2113,19327,19328],{"class":2119},"]?.",[2113,19330,19331],{"class":2330},"stockOnline",[2113,19333,2487],{"class":2119},[2113,19335,19336],{"class":2115,"line":2514},[2113,19337,2125],{"emptyLinePlaceholder":1767},[2113,19339,19340,19343,19345,19347],{"class":2115,"line":2533},[2113,19341,19342],{"class":2414},"    availability",[2113,19344,179],{"class":2119},[2113,19346,10086],{"class":2133},[2113,19348,19349],{"class":2119},"({\n",[2113,19351,19352,19355,19357,19360],{"class":2115,"line":2556},[2113,19353,19354],{"class":2330},"      type",[2113,19356,4429],{"class":2119},[2113,19358,19359],{"class":2149},"'Internet'",[2113,19361,4706],{"class":2119},[2113,19363,19364,19367,19369,19372],{"class":2115,"line":2569},[2113,19365,19366],{"class":2330},"      stocks",[2113,19368,4429],{"class":2119},[2113,19370,19371],{"class":2330},"onlineStocks",[2113,19373,4706],{"class":2119},[2113,19375,19376],{"class":2115,"line":2575},[2113,19377,19378],{"class":2119},"    });\n",[2113,19380,19381],{"class":2115,"line":2596},[2113,19382,2125],{"emptyLinePlaceholder":1767},[2113,19384,19385,19387,19390,19392,19394,19396],{"class":2115,"line":3192},[2113,19386,19244],{"class":2326},[2113,19388,19389],{"class":2414}," stores",[2113,19391,2153],{"class":2334},[2113,19393,17521],{"class":2326},[2113,19395,19266],{"class":2133},[2113,19397,3146],{"class":2119},[2113,19399,19400,19402,19404,19406,19408,19410,19412,19415,19417,19420,19422,19424],{"class":2115,"line":3213},[2113,19401,19273],{"class":2414},[2113,19403,179],{"class":2119},[2113,19405,19278],{"class":2414},[2113,19407,179],{"class":2119},[2113,19409,19283],{"class":2133},[2113,19411,3109],{"class":2119},[2113,19413,19414],{"class":2414},"StocksStores",[2113,19416,19291],{"class":2119},[2113,19418,19419],{"class":2133},"STOCKS_STORE",[2113,19421,2423],{"class":2119},[2113,19423,184],{"class":2330},[2113,19425,19426],{"class":2119},")),\n",[2113,19428,19429],{"class":2115,"line":3236},[2113,19430,10961],{"class":2119},[2113,19432,19433],{"class":2115,"line":3248},[2113,19434,2125],{"emptyLinePlaceholder":1767},[2113,19436,19437,19439,19441,19443],{"class":2115,"line":4899},[2113,19438,19342],{"class":2414},[2113,19440,179],{"class":2119},[2113,19442,10086],{"class":2133},[2113,19444,3146],{"class":2119},[2113,19446,19447,19450,19453,19455,19457,19459,19462,19464],{"class":2115,"line":1777},[2113,19448,19449],{"class":2119},"      ...",[2113,19451,19452],{"class":2414},"stores",[2113,19454,179],{"class":2119},[2113,19456,10154],{"class":2414},[2113,19458,179],{"class":2119},[2113,19460,19461],{"class":2414},"responseTO",[2113,19463,179],{"class":2119},[2113,19465,19466],{"class":2330},"data\n",[2113,19468,19469,19471,19473,19475,19478,19480,19482],{"class":2115,"line":4931},[2113,19470,10331],{"class":2119},[2113,19472,6644],{"class":2133},[2113,19474,8879],{"class":2119},[2113,19476,19477],{"class":2429},"store",[2113,19479,5709],{"class":2119},[2113,19481,8063],{"class":2326},[2113,19483,8066],{"class":2119},[2113,19485,19486,19489,19491,19493,19495,19498],{"class":2115,"line":4961},[2113,19487,19488],{"class":2330},"          type",[2113,19490,4429],{"class":2119},[2113,19492,19477],{"class":2414},[2113,19494,179],{"class":2119},[2113,19496,19497],{"class":2330},"storeName",[2113,19499,4706],{"class":2119},[2113,19501,19502,19505,19507,19509,19511,19514],{"class":2115,"line":4976},[2113,19503,19504],{"class":2330},"          stocks",[2113,19506,4429],{"class":2119},[2113,19508,19477],{"class":2414},[2113,19510,179],{"class":2119},[2113,19512,19513],{"class":2330},"quantity",[2113,19515,4706],{"class":2119},[2113,19517,19518],{"class":2115,"line":4993},[2113,19519,19520],{"class":2119},"        }))\n",[2113,19522,19523,19525,19528,19530,19532,19534,19536,19538,19540,19542,19544,19547,19549,19551,19553,19555],{"class":2115,"line":5013},[2113,19524,10331],{"class":2119},[2113,19526,19527],{"class":2133},"sort",[2113,19529,8879],{"class":2119},[2113,19531,49],{"class":2429},[2113,19533,932],{"class":2119},[2113,19535,3992],{"class":2429},[2113,19537,5709],{"class":2119},[2113,19539,8063],{"class":2326},[2113,19541,3951],{"class":2414},[2113,19543,179],{"class":2119},[2113,19545,19546],{"class":2330},"stocks",[2113,19548,2270],{"class":2334},[2113,19550,17734],{"class":2414},[2113,19552,179],{"class":2119},[2113,19554,19546],{"class":2330},[2113,19556,7142],{"class":2119},[2113,19558,19559],{"class":2115,"line":5018},[2113,19560,10961],{"class":2119},[2113,19562,19563],{"class":2115,"line":5042},[2113,19564,2125],{"emptyLinePlaceholder":1767},[2113,19566,19567,19569,19571,19573,19575,19577,19579,19581,19583],{"class":2115,"line":5057},[2113,19568,3137],{"class":2326},[2113,19570,19247],{"class":2414},[2113,19572,179],{"class":2119},[2113,19574,6644],{"class":2133},[2113,19576,8879],{"class":2119},[2113,19578,19477],{"class":2429},[2113,19580,5709],{"class":2119},[2113,19582,8063],{"class":2326},[2113,19584,8066],{"class":2119},[2113,19586,19587,19589,19591],{"class":2115,"line":5062},[2113,19588,19449],{"class":2119},[2113,19590,19477],{"class":2330},[2113,19592,4706],{"class":2119},[2113,19594,19595,19598,19600,19602,19604,19606,19608,19610,19612,19615,19617,19620],{"class":2115,"line":5098},[2113,19596,19597],{"class":2330},"      availability",[2113,19599,4429],{"class":2119},[2113,19601,19477],{"class":2414},[2113,19603,179],{"class":2119},[2113,19605,19546],{"class":2330},[2113,19607,5275],{"class":2334},[2113,19609,3208],{"class":2166},[2113,19611,9343],{"class":2326},[2113,19613,19614],{"class":2149}," 'available'",[2113,19616,2384],{"class":2326},[2113,19618,19619],{"class":2149}," 'not_available'",[2113,19621,4706],{"class":2119},[2113,19623,19624],{"class":2115,"line":5117},[2113,19625,19626],{"class":2119},"    }));\n",[2113,19628,19629],{"class":2115,"line":5142},[2113,19630,2572],{"class":2119},[2113,19632,19633],{"class":2115,"line":5148},[2113,19634,2599],{"class":2119},[12,19636,19637],{},"Une petite page en Mustashe pour afficher en HTML le contenu de la page et c'est parti !",[12,19639,19640],{},[121,19641],{"alt":19642,"className":19643,"src":19644},"La page WEB",[126],"\u002FProgrammation\u002Fachat_velo\u002Fpage_web.png",[128,19646,19648],{"id":19647},"le-déploiement","Le déploiement",[12,19650,19651,19652,179],{},"Afin d'améliorer mes compétences professionnelles je décide de déployer cette application sur AWS, et afin que cette application me coûte le moins cher possible, je\nvais déployer cette application dans une Lambda à l'aide du framework ",[49,19653,19656],{"href":19654,"rel":19655},"https:\u002F\u002Fwww.serverless.com\u002F",[347],"Serverless",[12,19658,19659,19660,179],{},"Viens alors l'adaptation du l'application Nest.JS pour utiliser le framework Serverless. Je laisse la documentation de Nest.JS expliquer comment faire:\n",[49,19661,19664],{"href":19662,"rel":19663},"https:\u002F\u002Fdocs.nestjs.com\u002Ffaq\u002Fserverless",[347],"Nest.JS - Serverless",[2105,19666,19670],{"className":19667,"code":19668,"language":19669,"meta":1646,"style":1646},"language-yaml shiki shiki-themes one-dark-pro","service: triathlon\nframeworkVersion: \"2\"\n\nplugins:\n  - serverless-plugin-log-retention\n  - serverless-offline\n  - serverless-domain-manager\n\ncustom:\n  region: eu-central-1\n  logRetentionInDays: 7\n  serverless-offline:\n    noPrependStageInUrl: true\n    allowCache: false\n  customDomain:\n    domainName: \"decat-${self:provider.stage}.aws.shadoware.org\"\n    basePath: \"\"\n    certificateName: \"aws.shadoware.org\"\n    createRoute53Record: true\n    endpointType: \"regional\"\n    securityPolicy: tls_1_2\n    apiType: rest\n    autoDomain: false\n\nprovider:\n  name: aws\n  region: ${self:custom.region}\n  runtime: nodejs12.x\n  lambdaHashingVersion: \"20201221\"\n  stage: ${opt:stage, 'dev'}\n  environment:\n    NODE_OPTIONS: \"--max-http-header-size=80000\"\n\npackage:\n  patterns:\n    - \"!.\u002F**\"\n    - dist\u002F**\n    - views\u002F**\n\nfunctions:\n  api:\n    handler: dist\u002Fmain.handler\n    environment:\n      region: ${self:custom.region}\n    events:\n      - http:\n          path: \u002F\n          method: ANY\n      - http:\n          path: \u002F{proxy+}\n          method: ANY\n","yaml",[184,19671,19672,19681,19691,19695,19703,19711,19718,19725,19729,19736,19746,19756,19763,19773,19783,19790,19800,19810,19820,19829,19839,19849,19859,19868,19872,19879,19889,19898,19908,19918,19928,19935,19945,19949,19955,19962,19970,19977,19984,19988,19995,20002,20012,20019,20028,20035,20044,20054,20064,20072,20081],{"__ignoreMap":1646},[2113,19673,19674,19676,19678],{"class":2115,"line":2116},[2113,19675,16847],{"class":2330},[2113,19677,4429],{"class":2119},[2113,19679,19680],{"class":2149},"triathlon\n",[2113,19682,19683,19686,19688],{"class":2115,"line":1647},[2113,19684,19685],{"class":2330},"frameworkVersion",[2113,19687,4429],{"class":2119},[2113,19689,19690],{"class":2149},"\"2\"\n",[2113,19692,19693],{"class":2115,"line":1652},[2113,19694,2125],{"emptyLinePlaceholder":1767},[2113,19696,19697,19700],{"class":2115,"line":2185},[2113,19698,19699],{"class":2330},"plugins",[2113,19701,19702],{"class":2119},":\n",[2113,19704,19705,19708],{"class":2115,"line":2230},[2113,19706,19707],{"class":2119},"  - ",[2113,19709,19710],{"class":2149},"serverless-plugin-log-retention\n",[2113,19712,19713,19715],{"class":2115,"line":2235},[2113,19714,19707],{"class":2119},[2113,19716,19717],{"class":2149},"serverless-offline\n",[2113,19719,19720,19722],{"class":2115,"line":2241},[2113,19721,19707],{"class":2119},[2113,19723,19724],{"class":2149},"serverless-domain-manager\n",[2113,19726,19727],{"class":2115,"line":2246},[2113,19728,2125],{"emptyLinePlaceholder":1767},[2113,19730,19731,19734],{"class":2115,"line":2464},[2113,19732,19733],{"class":2330},"custom",[2113,19735,19702],{"class":2119},[2113,19737,19738,19741,19743],{"class":2115,"line":2085},[2113,19739,19740],{"class":2330},"  region",[2113,19742,4429],{"class":2119},[2113,19744,19745],{"class":2149},"eu-central-1\n",[2113,19747,19748,19751,19753],{"class":2115,"line":2514},[2113,19749,19750],{"class":2330},"  logRetentionInDays",[2113,19752,4429],{"class":2119},[2113,19754,19755],{"class":2166},"7\n",[2113,19757,19758,19761],{"class":2115,"line":2533},[2113,19759,19760],{"class":2330},"  serverless-offline",[2113,19762,19702],{"class":2119},[2113,19764,19765,19768,19770],{"class":2115,"line":2556},[2113,19766,19767],{"class":2330},"    noPrependStageInUrl",[2113,19769,4429],{"class":2119},[2113,19771,19772],{"class":2166},"true\n",[2113,19774,19775,19778,19780],{"class":2115,"line":2569},[2113,19776,19777],{"class":2330},"    allowCache",[2113,19779,4429],{"class":2119},[2113,19781,19782],{"class":2166},"false\n",[2113,19784,19785,19788],{"class":2115,"line":2575},[2113,19786,19787],{"class":2330},"  customDomain",[2113,19789,19702],{"class":2119},[2113,19791,19792,19795,19797],{"class":2115,"line":2596},[2113,19793,19794],{"class":2330},"    domainName",[2113,19796,4429],{"class":2119},[2113,19798,19799],{"class":2149},"\"decat-${self:provider.stage}.aws.shadoware.org\"\n",[2113,19801,19802,19805,19807],{"class":2115,"line":3192},[2113,19803,19804],{"class":2330},"    basePath",[2113,19806,4429],{"class":2119},[2113,19808,19809],{"class":2149},"\"\"\n",[2113,19811,19812,19815,19817],{"class":2115,"line":3213},[2113,19813,19814],{"class":2330},"    certificateName",[2113,19816,4429],{"class":2119},[2113,19818,19819],{"class":2149},"\"aws.shadoware.org\"\n",[2113,19821,19822,19825,19827],{"class":2115,"line":3236},[2113,19823,19824],{"class":2330},"    createRoute53Record",[2113,19826,4429],{"class":2119},[2113,19828,19772],{"class":2166},[2113,19830,19831,19834,19836],{"class":2115,"line":3248},[2113,19832,19833],{"class":2330},"    endpointType",[2113,19835,4429],{"class":2119},[2113,19837,19838],{"class":2149},"\"regional\"\n",[2113,19840,19841,19844,19846],{"class":2115,"line":4899},[2113,19842,19843],{"class":2330},"    securityPolicy",[2113,19845,4429],{"class":2119},[2113,19847,19848],{"class":2149},"tls_1_2\n",[2113,19850,19851,19854,19856],{"class":2115,"line":1777},[2113,19852,19853],{"class":2330},"    apiType",[2113,19855,4429],{"class":2119},[2113,19857,19858],{"class":2149},"rest\n",[2113,19860,19861,19864,19866],{"class":2115,"line":4931},[2113,19862,19863],{"class":2330},"    autoDomain",[2113,19865,4429],{"class":2119},[2113,19867,19782],{"class":2166},[2113,19869,19870],{"class":2115,"line":4961},[2113,19871,2125],{"emptyLinePlaceholder":1767},[2113,19873,19874,19877],{"class":2115,"line":4976},[2113,19875,19876],{"class":2330},"provider",[2113,19878,19702],{"class":2119},[2113,19880,19881,19884,19886],{"class":2115,"line":4993},[2113,19882,19883],{"class":2330},"  name",[2113,19885,4429],{"class":2119},[2113,19887,19888],{"class":2149},"aws\n",[2113,19890,19891,19893,19895],{"class":2115,"line":5013},[2113,19892,19740],{"class":2330},[2113,19894,4429],{"class":2119},[2113,19896,19897],{"class":2149},"${self:custom.region}\n",[2113,19899,19900,19903,19905],{"class":2115,"line":5018},[2113,19901,19902],{"class":2330},"  runtime",[2113,19904,4429],{"class":2119},[2113,19906,19907],{"class":2149},"nodejs12.x\n",[2113,19909,19910,19913,19915],{"class":2115,"line":5042},[2113,19911,19912],{"class":2330},"  lambdaHashingVersion",[2113,19914,4429],{"class":2119},[2113,19916,19917],{"class":2149},"\"20201221\"\n",[2113,19919,19920,19923,19925],{"class":2115,"line":5057},[2113,19921,19922],{"class":2330},"  stage",[2113,19924,4429],{"class":2119},[2113,19926,19927],{"class":2149},"${opt:stage, 'dev'}\n",[2113,19929,19930,19933],{"class":2115,"line":5062},[2113,19931,19932],{"class":2330},"  environment",[2113,19934,19702],{"class":2119},[2113,19936,19937,19940,19942],{"class":2115,"line":5098},[2113,19938,19939],{"class":2330},"    NODE_OPTIONS",[2113,19941,4429],{"class":2119},[2113,19943,19944],{"class":2149},"\"--max-http-header-size=80000\"\n",[2113,19946,19947],{"class":2115,"line":5117},[2113,19948,2125],{"emptyLinePlaceholder":1767},[2113,19950,19951,19953],{"class":2115,"line":5142},[2113,19952,15998],{"class":2330},[2113,19954,19702],{"class":2119},[2113,19956,19957,19960],{"class":2115,"line":5148},[2113,19958,19959],{"class":2330},"  patterns",[2113,19961,19702],{"class":2119},[2113,19963,19964,19967],{"class":2115,"line":5163},[2113,19965,19966],{"class":2119},"    - ",[2113,19968,19969],{"class":2149},"\"!.\u002F**\"\n",[2113,19971,19972,19974],{"class":2115,"line":5169},[2113,19973,19966],{"class":2119},[2113,19975,19976],{"class":2149},"dist\u002F**\n",[2113,19978,19979,19981],{"class":2115,"line":5175},[2113,19980,19966],{"class":2119},[2113,19982,19983],{"class":2149},"views\u002F**\n",[2113,19985,19986],{"class":2115,"line":5180},[2113,19987,2125],{"emptyLinePlaceholder":1767},[2113,19989,19990,19993],{"class":2115,"line":5199},[2113,19991,19992],{"class":2330},"functions",[2113,19994,19702],{"class":2119},[2113,19996,19997,20000],{"class":2115,"line":5204},[2113,19998,19999],{"class":2330},"  api",[2113,20001,19702],{"class":2119},[2113,20003,20004,20007,20009],{"class":2115,"line":5209},[2113,20005,20006],{"class":2330},"    handler",[2113,20008,4429],{"class":2119},[2113,20010,20011],{"class":2149},"dist\u002Fmain.handler\n",[2113,20013,20014,20017],{"class":2115,"line":5232},[2113,20015,20016],{"class":2330},"    environment",[2113,20018,19702],{"class":2119},[2113,20020,20021,20024,20026],{"class":2115,"line":5237},[2113,20022,20023],{"class":2330},"      region",[2113,20025,4429],{"class":2119},[2113,20027,19897],{"class":2149},[2113,20029,20030,20033],{"class":2115,"line":5242},[2113,20031,20032],{"class":2330},"    events",[2113,20034,19702],{"class":2119},[2113,20036,20037,20040,20042],{"class":2115,"line":5267},[2113,20038,20039],{"class":2119},"      - ",[2113,20041,18039],{"class":2330},[2113,20043,19702],{"class":2119},[2113,20045,20046,20049,20051],{"class":2115,"line":5282},[2113,20047,20048],{"class":2330},"          path",[2113,20050,4429],{"class":2119},[2113,20052,20053],{"class":2149},"\u002F\n",[2113,20055,20056,20059,20061],{"class":2115,"line":5295},[2113,20057,20058],{"class":2330},"          method",[2113,20060,4429],{"class":2119},[2113,20062,20063],{"class":2149},"ANY\n",[2113,20065,20066,20068,20070],{"class":2115,"line":5310},[2113,20067,20039],{"class":2119},[2113,20069,18039],{"class":2330},[2113,20071,19702],{"class":2119},[2113,20073,20074,20076,20078],{"class":2115,"line":5315},[2113,20075,20048],{"class":2330},[2113,20077,4429],{"class":2119},[2113,20079,20080],{"class":2149},"\u002F{proxy+}\n",[2113,20082,20083,20085,20087],{"class":2115,"line":5320},[2113,20084,20058],{"class":2330},[2113,20086,4429],{"class":2119},[2113,20088,20063],{"class":2149},[12,20090,20091],{},"Il ne reste plus qu'à lancer le déploiement:",[2105,20093,20095],{"className":17586,"code":20094,"language":17588,"meta":1646,"style":1646},"npx serverless deploy\n",[184,20096,20097],{"__ignoreMap":1646},[2113,20098,20099,20102,20105],{"class":2115,"line":2116},[2113,20100,20101],{"class":2133},"npx",[2113,20103,20104],{"class":2149}," serverless",[2113,20106,20107],{"class":2149}," deploy\n",[12,20109,20110],{},"Nous avons alors notre belle page de déployée.",[128,20112,20114],{"id":20113},"la-suite-de-laventure","La suite de l'aventure",[12,20116,20117],{},"Me voilà avec un petit programme qui me permet de facilement visualiser les stocks du vélo que je souhaite. J'en profite également pour ajouter le même\nproduit en taille XL (où il y a du stock) et un VTT.",[12,20119,20120],{},"Le problème c'est que je dois continuer à rafraîchir la page, même si c'est plus rapide.",[12,20122,20123],{},"Pour l'anecdocte, après une matinée de glandouille (c'est les vacances), je décide de rafraîchir la page vers midi. Je vois \"Stock: 1\". Heureux, je me précipite pour\nacheter le seul exemplaire. Mais lors de la commande je tombe sur le fameux message: \"Internal Server Error\" dans un bandeau rouge en haut de l'écran.",[12,20125,20126],{},"J'appelle le service client qui m'informe que cela provient du fait qu'il n'y a déjà plus de stock mais que le stock du site n'est mis à jours que tout les 24h.\nJe m'en doutais mais le message d'erreur laisse à désirer un peu.",[12,20128,20129],{},"Malgrès l'apparition soudaine d'un stock, je n'ai recu aucun mail pour me prévenir (relatif à la fonctionnalité \"M'avertir lorsque le produit est à nouveau disponible\"). Il\nme faut un moyen d'être averti dans les premiers dès qu'il y a du stock.",[12,20131,20132],{},"La situation s'est reproduite sans que je sois averti par mail.",[12,20134,20135],{},"Bref, il me faut une solution !",[128,20137,20139],{"id":20138},"la-notification","La notification",[12,20141,20142],{},"Je profite donc des services AWS pour développer une nouvelle fonctionnalité. Cette dernière est simple: je vais faire une requête à mon\nservice toutes les 30 minutes, pour vérifier le stock, et si ce dernier bouge, j'envoie un SMS.",[12,20144,20145],{},"Le coût actuel d'envoi d'un SMS vers la France sur AWS est de 0.06933$. Donc si je recois 1 SMS par heure en journée, je vais aller vers 1$ par jour.\nC'est à prévoir pour mon budget. Maintenant je veux recevoir le minimum de SMS. Je n'ai pas envie de recevoir de SMS quand le stock ne change pas. Il faut donc\nque je puisse comparer le stock entre l'état précédent et l'etat actuel.",[12,20147,20148],{},"Quand on utilise les lambda (fonction) d'AWS, il n'y a pas de serveur de démarré. Donc je ne peux pas stocker les valeurs précédentes temporairement en\nmémoire pour contrôler les changements (ce qui ne serait pas une bonne pratique, mais un truc rapide à faire pour un dev maison).",[12,20150,20151],{},"Je stocke donc le dernier accès au site dans une base de données de type Document de chez AWS: DynamoDB.",[12,20153,20154],{},"Le service d'envoi de SMS est simple (et prend un numéro de téléphone et un message):",[2105,20156,20158],{"className":15749,"code":20157,"language":15751,"meta":1646,"style":1646},"  \u002F**\n   * Sends a SMS message to the specificed phone number.\n   * Returns the message ID from SNS\n   * @param   {String} phoneNumber\n   * @param   {String} message\n   * @returns {Promise|String}\n   *\u002F\n  async sendMessage(phoneNumber, message) {\n    await this.SNS.setSMSAttributes({\n      attributes: { DefaultSMSType: 'Promotional' },\n    }).promise();\n\n    const smsData = {\n      Message: message,\n      PhoneNumber: phoneNumber,\n      MessageAttributes: {\n        'AWS.SNS.SMS.SenderID': {\n          DataType: 'String',\n          StringValue: 'DecatStock',\n        },\n      },\n    };\n\n    this.logger.log(\n      `Send message to ${phoneNumber} (${message.length}):\\n${message}`,\n    );\n    const response = await this.SNS.publish(smsData).promise();\n\n    this.logger.log(`Response of SMS is ${JSON.stringify(response)}`);\n    return response.MessageId;\n  }\n",[184,20159,20160,20165,20170,20175,20191,20202,20212,20217,20235,20254,20271,20281,20285,20296,20307,20318,20325,20332,20344,20356,20360,20364,20369,20373,20389,20430,20434,20466,20470,20509,20522],{"__ignoreMap":1646},[2113,20161,20162],{"class":2115,"line":2116},[2113,20163,20164],{"class":2396},"  \u002F**\n",[2113,20166,20167],{"class":2115,"line":1647},[2113,20168,20169],{"class":2396},"   * Sends a SMS message to the specificed phone number.\n",[2113,20171,20172],{"class":2115,"line":1652},[2113,20173,20174],{"class":2396},"   * Returns the message ID from SNS\n",[2113,20176,20177,20180,20184,20188],{"class":2115,"line":2185},[2113,20178,20179],{"class":2396},"   * ",[2113,20181,20183],{"class":20182},"shdRp","@param",[2113,20185,20187],{"class":20186},"sKU4T","   {String}",[2113,20189,20190],{"class":2429}," phoneNumber\n",[2113,20192,20193,20195,20197,20199],{"class":2115,"line":2230},[2113,20194,20179],{"class":2396},[2113,20196,20183],{"class":20182},[2113,20198,20187],{"class":20186},[2113,20200,20201],{"class":2429}," message\n",[2113,20203,20204,20206,20209],{"class":2115,"line":2235},[2113,20205,20179],{"class":2396},[2113,20207,20208],{"class":20182},"@returns",[2113,20210,20211],{"class":20186}," {Promise|String}\n",[2113,20213,20214],{"class":2115,"line":2241},[2113,20215,20216],{"class":2396},"   *\u002F\n",[2113,20218,20219,20221,20224,20226,20229,20231,20233],{"class":2115,"line":2246},[2113,20220,19213],{"class":2330},[2113,20222,20223],{"class":2133}," sendMessage",[2113,20225,2423],{"class":2119},[2113,20227,20228],{"class":2330},"phoneNumber",[2113,20230,932],{"class":2119},[2113,20232,16052],{"class":2330},[2113,20234,2433],{"class":2119},[2113,20236,20237,20240,20242,20244,20247,20249,20252],{"class":2115,"line":2464},[2113,20238,20239],{"class":2326},"    await",[2113,20241,15832],{"class":2414},[2113,20243,179],{"class":2119},[2113,20245,20246],{"class":2414},"SNS",[2113,20248,179],{"class":2119},[2113,20250,20251],{"class":2133},"setSMSAttributes",[2113,20253,19349],{"class":2119},[2113,20255,20256,20259,20261,20264,20266,20269],{"class":2115,"line":2085},[2113,20257,20258],{"class":2330},"      attributes",[2113,20260,8099],{"class":2119},[2113,20262,20263],{"class":2330},"DefaultSMSType",[2113,20265,4429],{"class":2119},[2113,20267,20268],{"class":2149},"'Promotional'",[2113,20270,8129],{"class":2119},[2113,20272,20273,20276,20279],{"class":2115,"line":2514},[2113,20274,20275],{"class":2119},"    }).",[2113,20277,20278],{"class":2133},"promise",[2113,20280,3944],{"class":2119},[2113,20282,20283],{"class":2115,"line":2533},[2113,20284,2125],{"emptyLinePlaceholder":1767},[2113,20286,20287,20289,20292,20294],{"class":2115,"line":2556},[2113,20288,19244],{"class":2326},[2113,20290,20291],{"class":2414}," smsData",[2113,20293,2153],{"class":2334},[2113,20295,2647],{"class":2119},[2113,20297,20298,20301,20303,20305],{"class":2115,"line":2569},[2113,20299,20300],{"class":2330},"      Message",[2113,20302,4429],{"class":2119},[2113,20304,16052],{"class":2330},[2113,20306,4706],{"class":2119},[2113,20308,20309,20312,20314,20316],{"class":2115,"line":2575},[2113,20310,20311],{"class":2330},"      PhoneNumber",[2113,20313,4429],{"class":2119},[2113,20315,20228],{"class":2330},[2113,20317,4706],{"class":2119},[2113,20319,20320,20323],{"class":2115,"line":2596},[2113,20321,20322],{"class":2330},"      MessageAttributes",[2113,20324,8091],{"class":2119},[2113,20326,20327,20330],{"class":2115,"line":3192},[2113,20328,20329],{"class":2149},"        'AWS.SNS.SMS.SenderID'",[2113,20331,8091],{"class":2119},[2113,20333,20334,20337,20339,20342],{"class":2115,"line":3213},[2113,20335,20336],{"class":2330},"          DataType",[2113,20338,4429],{"class":2119},[2113,20340,20341],{"class":2149},"'String'",[2113,20343,4706],{"class":2119},[2113,20345,20346,20349,20351,20354],{"class":2115,"line":3236},[2113,20347,20348],{"class":2330},"          StringValue",[2113,20350,4429],{"class":2119},[2113,20352,20353],{"class":2149},"'DecatStock'",[2113,20355,4706],{"class":2119},[2113,20357,20358],{"class":2115,"line":3248},[2113,20359,12744],{"class":2119},[2113,20361,20362],{"class":2115,"line":4899},[2113,20363,12282],{"class":2119},[2113,20365,20366],{"class":2115,"line":1777},[2113,20367,20368],{"class":2119},"    };\n",[2113,20370,20371],{"class":2115,"line":4931},[2113,20372,2125],{"emptyLinePlaceholder":1767},[2113,20374,20375,20378,20380,20383,20385,20387],{"class":2115,"line":4961},[2113,20376,20377],{"class":2414},"    this",[2113,20379,179],{"class":2119},[2113,20381,20382],{"class":2414},"logger",[2113,20384,179],{"class":2119},[2113,20386,8748],{"class":2133},[2113,20388,3146],{"class":2119},[2113,20390,20391,20394,20396,20398,20400,20402,20404,20406,20408,20411,20413,20416,20419,20421,20423,20425,20428],{"class":2115,"line":4976},[2113,20392,20393],{"class":2149},"      `Send message to ",[2113,20395,15877],{"class":2326},[2113,20397,20228],{"class":2330},[2113,20399,15888],{"class":2326},[2113,20401,2495],{"class":2149},[2113,20403,15877],{"class":2326},[2113,20405,16052],{"class":2414},[2113,20407,179],{"class":2119},[2113,20409,20410],{"class":2330},"length",[2113,20412,15888],{"class":2326},[2113,20414,20415],{"class":2149},"):",[2113,20417,20418],{"class":2334},"\\n",[2113,20420,15877],{"class":2326},[2113,20422,16052],{"class":2330},[2113,20424,15888],{"class":2326},[2113,20426,20427],{"class":2149},"`",[2113,20429,4706],{"class":2119},[2113,20431,20432],{"class":2115,"line":4993},[2113,20433,10961],{"class":2119},[2113,20435,20436,20438,20440,20442,20444,20446,20448,20450,20452,20455,20457,20460,20462,20464],{"class":2115,"line":5013},[2113,20437,19244],{"class":2326},[2113,20439,15958],{"class":2414},[2113,20441,2153],{"class":2334},[2113,20443,17521],{"class":2326},[2113,20445,15832],{"class":2414},[2113,20447,179],{"class":2119},[2113,20449,20246],{"class":2414},[2113,20451,179],{"class":2119},[2113,20453,20454],{"class":2133},"publish",[2113,20456,2423],{"class":2119},[2113,20458,20459],{"class":2330},"smsData",[2113,20461,3938],{"class":2119},[2113,20463,20278],{"class":2133},[2113,20465,3944],{"class":2119},[2113,20467,20468],{"class":2115,"line":5018},[2113,20469,2125],{"emptyLinePlaceholder":1767},[2113,20471,20472,20474,20476,20478,20480,20482,20484,20487,20489,20492,20494,20497,20499,20501,20503,20505,20507],{"class":2115,"line":5042},[2113,20473,20377],{"class":2414},[2113,20475,179],{"class":2119},[2113,20477,20382],{"class":2414},[2113,20479,179],{"class":2119},[2113,20481,8748],{"class":2133},[2113,20483,2423],{"class":2119},[2113,20485,20486],{"class":2149},"`Response of SMS is ",[2113,20488,15877],{"class":2326},[2113,20490,20491],{"class":2414},"JSON",[2113,20493,179],{"class":2119},[2113,20495,20496],{"class":2133},"stringify",[2113,20498,2423],{"class":2119},[2113,20500,15951],{"class":2330},[2113,20502,17832],{"class":2119},[2113,20504,15888],{"class":2326},[2113,20506,20427],{"class":2149},[2113,20508,2553],{"class":2119},[2113,20510,20511,20513,20515,20517,20520],{"class":2115,"line":5057},[2113,20512,3137],{"class":2326},[2113,20514,15958],{"class":2414},[2113,20516,179],{"class":2119},[2113,20518,20519],{"class":2330},"MessageId",[2113,20521,2487],{"class":2119},[2113,20523,20524],{"class":2115,"line":5062},[2113,20525,2572],{"class":2119},[12,20527,20528],{},"Le service permettant de lire et d'écrire dans la base DynamoDB est lui aussi très simple et ne possède que deux méthodes:",[2105,20530,20532],{"className":15749,"code":20531,"language":15751,"meta":1646,"style":1646},"class {\n  async findOneById(sku: string): Promise\u003CDecathlonStatus> {\n    try {\n      const result = await dynamoDB\n        .get({\n          TableName: process.env.DECATHLON_TABLE_NAME,\n          Key: { id: sku },\n        })\n        .promise();\n      return result.Item as DecathlonStatus;\n    } catch (error) {\n      this.logger.warn(`Can't read the table ${sku}: ${error.message}`);\n      return null;\n    }\n  }\n\n  async update(\n    sku: string,\n    items: StockInformations\n  ): Promise\u003CDecathlonStatus> {\n    const status = new DecathlonStatus();\n    status.id = sku;\n    status.informations = items;\n\n    try {\n      await dynamoDB\n        .put({\n          TableName: process.env.DECATHLON_TABLE_NAME,\n          Item: status,\n        })\n        .promise();\n      return status;\n    } catch (error) {\n      throw new InternalServerErrorException(error);\n    }\n  }\n}\n",[184,20533,20534,20541,20568,20575,20589,20597,20619,20635,20640,20648,20667,20681,20721,20730,20734,20738,20742,20751,20762,20772,20785,20800,20815,20831,20835,20841,20848,20857,20875,20887,20891,20899,20907,20919,20935,20939,20943],{"__ignoreMap":1646},[2113,20535,20536,20539],{"class":2115,"line":2116},[2113,20537,20538],{"class":2326},"class",[2113,20540,2647],{"class":2119},[2113,20542,20543,20545,20548,20550,20553,20555,20557,20559,20561,20563,20566],{"class":2115,"line":1647},[2113,20544,19213],{"class":2326},[2113,20546,20547],{"class":2133}," findOneById",[2113,20549,2423],{"class":2119},[2113,20551,20552],{"class":2429},"sku",[2113,20554,4429],{"class":2119},[2113,20556,19225],{"class":2414},[2113,20558,19228],{"class":2119},[2113,20560,19231],{"class":2414},[2113,20562,3109],{"class":2119},[2113,20564,20565],{"class":2414},"DecathlonStatus",[2113,20567,4449],{"class":2119},[2113,20569,20570,20573],{"class":2115,"line":1652},[2113,20571,20572],{"class":2326},"    try",[2113,20574,2647],{"class":2119},[2113,20576,20577,20580,20582,20584,20586],{"class":2115,"line":2185},[2113,20578,20579],{"class":2326},"      const",[2113,20581,6112],{"class":2414},[2113,20583,2153],{"class":2334},[2113,20585,17521],{"class":2326},[2113,20587,20588],{"class":2330}," dynamoDB\n",[2113,20590,20591,20593,20595],{"class":2115,"line":2230},[2113,20592,10331],{"class":2119},[2113,20594,19283],{"class":2133},[2113,20596,19349],{"class":2119},[2113,20598,20599,20602,20604,20607,20609,20612,20614,20617],{"class":2115,"line":2235},[2113,20600,20601],{"class":2330},"          TableName",[2113,20603,4429],{"class":2119},[2113,20605,20606],{"class":2414},"process",[2113,20608,179],{"class":2119},[2113,20610,20611],{"class":2414},"env",[2113,20613,179],{"class":2119},[2113,20615,20616],{"class":2330},"DECATHLON_TABLE_NAME",[2113,20618,4706],{"class":2119},[2113,20620,20621,20624,20626,20629,20631,20633],{"class":2115,"line":2241},[2113,20622,20623],{"class":2330},"          Key",[2113,20625,8099],{"class":2119},[2113,20627,20628],{"class":2330},"id",[2113,20630,4429],{"class":2119},[2113,20632,20552],{"class":2330},[2113,20634,8129],{"class":2119},[2113,20636,20637],{"class":2115,"line":2246},[2113,20638,20639],{"class":2119},"        })\n",[2113,20641,20642,20644,20646],{"class":2115,"line":2464},[2113,20643,10331],{"class":2119},[2113,20645,20278],{"class":2133},[2113,20647,3944],{"class":2119},[2113,20649,20650,20653,20655,20657,20660,20662,20665],{"class":2115,"line":2085},[2113,20651,20652],{"class":2326},"      return",[2113,20654,6112],{"class":2414},[2113,20656,179],{"class":2119},[2113,20658,20659],{"class":2330},"Item",[2113,20661,11429],{"class":2326},[2113,20663,20664],{"class":2414}," DecathlonStatus",[2113,20666,2487],{"class":2119},[2113,20668,20669,20672,20674,20676,20679],{"class":2115,"line":2514},[2113,20670,20671],{"class":2119},"    } ",[2113,20673,8973],{"class":2326},[2113,20675,2495],{"class":2119},[2113,20677,20678],{"class":2330},"error",[2113,20680,2433],{"class":2119},[2113,20682,20683,20685,20687,20689,20691,20694,20696,20699,20701,20703,20705,20707,20709,20711,20713,20715,20717,20719],{"class":2115,"line":2533},[2113,20684,19273],{"class":2414},[2113,20686,179],{"class":2119},[2113,20688,20382],{"class":2414},[2113,20690,179],{"class":2119},[2113,20692,20693],{"class":2133},"warn",[2113,20695,2423],{"class":2119},[2113,20697,20698],{"class":2149},"`Can't read the table ",[2113,20700,15877],{"class":2326},[2113,20702,20552],{"class":2330},[2113,20704,15888],{"class":2326},[2113,20706,4429],{"class":2149},[2113,20708,15877],{"class":2326},[2113,20710,20678],{"class":2414},[2113,20712,179],{"class":2119},[2113,20714,16052],{"class":2330},[2113,20716,15888],{"class":2326},[2113,20718,20427],{"class":2149},[2113,20720,2553],{"class":2119},[2113,20722,20723,20725,20728],{"class":2115,"line":2556},[2113,20724,20652],{"class":2326},[2113,20726,20727],{"class":2166}," null",[2113,20729,2487],{"class":2119},[2113,20731,20732],{"class":2115,"line":2569},[2113,20733,4665],{"class":2119},[2113,20735,20736],{"class":2115,"line":2575},[2113,20737,2572],{"class":2119},[2113,20739,20740],{"class":2115,"line":2596},[2113,20741,2125],{"emptyLinePlaceholder":1767},[2113,20743,20744,20746,20749],{"class":2115,"line":3192},[2113,20745,19213],{"class":2326},[2113,20747,20748],{"class":2133}," update",[2113,20750,3146],{"class":2119},[2113,20752,20753,20756,20758,20760],{"class":2115,"line":3213},[2113,20754,20755],{"class":2429},"    sku",[2113,20757,4429],{"class":2119},[2113,20759,19225],{"class":2414},[2113,20761,4706],{"class":2119},[2113,20763,20764,20767,20769],{"class":2115,"line":3236},[2113,20765,20766],{"class":2429},"    items",[2113,20768,4429],{"class":2119},[2113,20770,20771],{"class":2414},"StockInformations\n",[2113,20773,20774,20777,20779,20781,20783],{"class":2115,"line":3248},[2113,20775,20776],{"class":2119},"  ): ",[2113,20778,19231],{"class":2414},[2113,20780,3109],{"class":2119},[2113,20782,20565],{"class":2414},[2113,20784,4449],{"class":2119},[2113,20786,20787,20789,20792,20794,20796,20798],{"class":2115,"line":4899},[2113,20788,19244],{"class":2326},[2113,20790,20791],{"class":2414}," status",[2113,20793,2153],{"class":2334},[2113,20795,4778],{"class":2326},[2113,20797,20664],{"class":2133},[2113,20799,3944],{"class":2119},[2113,20801,20802,20805,20807,20809,20811,20813],{"class":2115,"line":1777},[2113,20803,20804],{"class":2414},"    status",[2113,20806,179],{"class":2119},[2113,20808,20628],{"class":2330},[2113,20810,2153],{"class":2334},[2113,20812,19259],{"class":2330},[2113,20814,2487],{"class":2119},[2113,20816,20817,20819,20821,20824,20826,20829],{"class":2115,"line":4931},[2113,20818,20804],{"class":2414},[2113,20820,179],{"class":2119},[2113,20822,20823],{"class":2330},"informations",[2113,20825,2153],{"class":2334},[2113,20827,20828],{"class":2330}," items",[2113,20830,2487],{"class":2119},[2113,20832,20833],{"class":2115,"line":4961},[2113,20834,2125],{"emptyLinePlaceholder":1767},[2113,20836,20837,20839],{"class":2115,"line":4976},[2113,20838,20572],{"class":2326},[2113,20840,2647],{"class":2119},[2113,20842,20843,20846],{"class":2115,"line":4993},[2113,20844,20845],{"class":2326},"      await",[2113,20847,20588],{"class":2330},[2113,20849,20850,20852,20855],{"class":2115,"line":5013},[2113,20851,10331],{"class":2119},[2113,20853,20854],{"class":2133},"put",[2113,20856,19349],{"class":2119},[2113,20858,20859,20861,20863,20865,20867,20869,20871,20873],{"class":2115,"line":5018},[2113,20860,20601],{"class":2330},[2113,20862,4429],{"class":2119},[2113,20864,20606],{"class":2414},[2113,20866,179],{"class":2119},[2113,20868,20611],{"class":2414},[2113,20870,179],{"class":2119},[2113,20872,20616],{"class":2330},[2113,20874,4706],{"class":2119},[2113,20876,20877,20880,20882,20885],{"class":2115,"line":5042},[2113,20878,20879],{"class":2330},"          Item",[2113,20881,4429],{"class":2119},[2113,20883,20884],{"class":2330},"status",[2113,20886,4706],{"class":2119},[2113,20888,20889],{"class":2115,"line":5057},[2113,20890,20639],{"class":2119},[2113,20892,20893,20895,20897],{"class":2115,"line":5062},[2113,20894,10331],{"class":2119},[2113,20896,20278],{"class":2133},[2113,20898,3944],{"class":2119},[2113,20900,20901,20903,20905],{"class":2115,"line":5098},[2113,20902,20652],{"class":2326},[2113,20904,20791],{"class":2330},[2113,20906,2487],{"class":2119},[2113,20908,20909,20911,20913,20915,20917],{"class":2115,"line":5117},[2113,20910,20671],{"class":2119},[2113,20912,8973],{"class":2326},[2113,20914,2495],{"class":2119},[2113,20916,20678],{"class":2330},[2113,20918,2433],{"class":2119},[2113,20920,20921,20924,20926,20929,20931,20933],{"class":2115,"line":5142},[2113,20922,20923],{"class":2326},"      throw",[2113,20925,4778],{"class":2326},[2113,20927,20928],{"class":2133}," InternalServerErrorException",[2113,20930,2423],{"class":2119},[2113,20932,20678],{"class":2330},[2113,20934,2553],{"class":2119},[2113,20936,20937],{"class":2115,"line":5148},[2113,20938,4665],{"class":2119},[2113,20940,20941],{"class":2115,"line":5163},[2113,20942,2572],{"class":2119},[2113,20944,20945],{"class":2115,"line":5169},[2113,20946,2599],{"class":2119},[12,20948,20949],{},"Le but est bien de lire le status et d'écrire le status pour un produit.",[12,20951,20952],{},"Le service qui sera appellé de façon régulière est relativement simple. Il va:",[367,20954,20955,20961,20968],{},[370,20956,20957,20958,20960],{},"appeler le service d'API ",[184,20959,15598],{}," pour vérifier le stock en live,",[370,20962,20963,20964,20967],{},"appeler la service ",[184,20965,20966],{},"DynamoDB"," pour récupérer le dernier stock en base de données,",[370,20969,20970],{},"et faire la différences entre les deux, et s'il y en a une: envoyer un SMS.",[2105,20972,20974],{"className":15749,"code":20973,"language":15751,"meta":1646,"style":1646},"export class AvailibilityCheckerService {\n  private logger = new Logger(AvailibilityCheckerService.name);\n\n  constructor(\n    private service: AppService,\n    private decathlonService: DecathlonsService,\n    private smsService: SMSService,\n  ) {}\n\n  async checkAvailibility() {\n    const message = (\n      await Promise.all(\n        PRODUCTS.map(\n          async (product) =>\n            await this.checkAvailabilityOfSku(product.code, product.name),\n        ),\n      )\n    )\n      .filter((message) => !!message)\n      .join('\\n\\n');\n\n    if (message.length) {\n      await this.smsService.sendMessage(process.env.PHONE_NUMBER, message);\n    }\n  }\n\n  async checkAvailabilityOfSku(code: string, name: string) {\n    const stocks = await this.service.getVtcElectectricL(code);\n    const oldStocks = await this.decathlonService.findOneById(code);\n    const diffStocks = this.diffStocks(oldStocks?.informations, stocks);\n    this.logger.debug(`The are ${diffStocks.length} stocks that change`);\n\n    let message;\n    if (diffStocks.length) {\n      message = diffStocks\n        .map((value) => `  ${value.type} - ${value.stocks}`)\n        .join('\\n');\n    }\n\n    await this.decathlonService.update(code, stocks);\n    return message && `${name}:\\n${message}`;\n  }\n\n  diffStocks(oldInfos: StockInformations, newInfos: StockInformations) {\n    oldInfos || (oldInfos = []);\n    newInfos || (newInfos = []);\n\n    const oldMap = oldInfos.reduce((acc, i) => {\n      acc[i.type] = i;\n      return acc;\n    }, {} as Record\u003Cstring, StockInformation>);\n\n    return newInfos.filter((info) => {\n      const oldStocks = oldMap[info.type]?.stocks || 0;\n      return oldStocks !== (info?.stocks || 0);\n    });\n  }\n}\n",[184,20975,20976,20987,21014,21018,21024,21039,21053,21067,21072,21076,21085,21097,21111,21122,21137,21167,21172,21176,21180,21202,21220,21224,21238,21273,21277,21281,21285,21310,21338,21367,21399,21432,21436,21444,21458,21468,21513,21529,21533,21537,21562,21592,21596,21600,21626,21642,21657,21661,21693,21714,21723,21744,21748,21770,21798,21821,21825,21829],{"__ignoreMap":1646},[2113,20977,20978,20980,20982,20985],{"class":2115,"line":2116},[2113,20979,2327],{"class":2326},[2113,20981,19179],{"class":2326},[2113,20983,20984],{"class":2414}," AvailibilityCheckerService",[2113,20986,2647],{"class":2119},[2113,20988,20989,20992,20995,20997,20999,21002,21004,21007,21009,21012],{"class":2115,"line":1647},[2113,20990,20991],{"class":2326},"  private",[2113,20993,20994],{"class":2330}," logger",[2113,20996,2153],{"class":2334},[2113,20998,4778],{"class":2326},[2113,21000,21001],{"class":2133}," Logger",[2113,21003,2423],{"class":2119},[2113,21005,21006],{"class":2414},"AvailibilityCheckerService",[2113,21008,179],{"class":2119},[2113,21010,21011],{"class":2330},"name",[2113,21013,2553],{"class":2119},[2113,21015,21016],{"class":2115,"line":1652},[2113,21017,2125],{"emptyLinePlaceholder":1767},[2113,21019,21020,21022],{"class":2115,"line":2185},[2113,21021,19189],{"class":2326},[2113,21023,3146],{"class":2119},[2113,21025,21026,21029,21032,21034,21037],{"class":2115,"line":2230},[2113,21027,21028],{"class":2326},"    private",[2113,21030,21031],{"class":2429}," service",[2113,21033,4429],{"class":2119},[2113,21035,21036],{"class":2414},"AppService",[2113,21038,4706],{"class":2119},[2113,21040,21041,21043,21046,21048,21051],{"class":2115,"line":2235},[2113,21042,21028],{"class":2326},[2113,21044,21045],{"class":2429}," decathlonService",[2113,21047,4429],{"class":2119},[2113,21049,21050],{"class":2414},"DecathlonsService",[2113,21052,4706],{"class":2119},[2113,21054,21055,21057,21060,21062,21065],{"class":2115,"line":2241},[2113,21056,21028],{"class":2326},[2113,21058,21059],{"class":2429}," smsService",[2113,21061,4429],{"class":2119},[2113,21063,21064],{"class":2414},"SMSService",[2113,21066,4706],{"class":2119},[2113,21068,21069],{"class":2115,"line":2246},[2113,21070,21071],{"class":2119},"  ) {}\n",[2113,21073,21074],{"class":2115,"line":2464},[2113,21075,2125],{"emptyLinePlaceholder":1767},[2113,21077,21078,21080,21083],{"class":2115,"line":2085},[2113,21079,19213],{"class":2326},[2113,21081,21082],{"class":2133}," checkAvailibility",[2113,21084,3912],{"class":2119},[2113,21086,21087,21089,21092,21094],{"class":2115,"line":2514},[2113,21088,19244],{"class":2326},[2113,21090,21091],{"class":2133}," message",[2113,21093,2153],{"class":2334},[2113,21095,21096],{"class":2119}," (\n",[2113,21098,21099,21101,21104,21106,21109],{"class":2115,"line":2533},[2113,21100,20845],{"class":2326},[2113,21102,21103],{"class":2414}," Promise",[2113,21105,179],{"class":2119},[2113,21107,21108],{"class":2133},"all",[2113,21110,3146],{"class":2119},[2113,21112,21113,21116,21118,21120],{"class":2115,"line":2556},[2113,21114,21115],{"class":2414},"        PRODUCTS",[2113,21117,179],{"class":2119},[2113,21119,6644],{"class":2133},[2113,21121,3146],{"class":2119},[2113,21123,21124,21127,21129,21132,21134],{"class":2115,"line":2569},[2113,21125,21126],{"class":2326},"          async",[2113,21128,2495],{"class":2119},[2113,21130,21131],{"class":2429},"product",[2113,21133,5709],{"class":2119},[2113,21135,21136],{"class":2326},"=>\n",[2113,21138,21139,21142,21144,21146,21149,21151,21153,21155,21157,21159,21161,21163,21165],{"class":2115,"line":2575},[2113,21140,21141],{"class":2326},"            await",[2113,21143,15832],{"class":2414},[2113,21145,179],{"class":2119},[2113,21147,21148],{"class":2133},"checkAvailabilityOfSku",[2113,21150,2423],{"class":2119},[2113,21152,21131],{"class":2414},[2113,21154,179],{"class":2119},[2113,21156,184],{"class":2330},[2113,21158,932],{"class":2119},[2113,21160,21131],{"class":2414},[2113,21162,179],{"class":2119},[2113,21164,21011],{"class":2330},[2113,21166,7142],{"class":2119},[2113,21168,21169],{"class":2115,"line":2596},[2113,21170,21171],{"class":2119},"        ),\n",[2113,21173,21174],{"class":2115,"line":3192},[2113,21175,13039],{"class":2119},[2113,21177,21178],{"class":2115,"line":3213},[2113,21179,13112],{"class":2119},[2113,21181,21182,21184,21187,21189,21191,21193,21195,21198,21200],{"class":2115,"line":3236},[2113,21183,12400],{"class":2119},[2113,21185,21186],{"class":2133},"filter",[2113,21188,8879],{"class":2119},[2113,21190,16052],{"class":2429},[2113,21192,5709],{"class":2119},[2113,21194,8063],{"class":2326},[2113,21196,21197],{"class":2334}," !!",[2113,21199,16052],{"class":2330},[2113,21201,4660],{"class":2119},[2113,21203,21204,21206,21208,21210,21213,21216,21218],{"class":2115,"line":3248},[2113,21205,12400],{"class":2119},[2113,21207,7230],{"class":2133},[2113,21209,2423],{"class":2119},[2113,21211,21212],{"class":2149},"'",[2113,21214,21215],{"class":2334},"\\n\\n",[2113,21217,21212],{"class":2149},[2113,21219,2553],{"class":2119},[2113,21221,21222],{"class":2115,"line":4899},[2113,21223,2125],{"emptyLinePlaceholder":1767},[2113,21225,21226,21228,21230,21232,21234,21236],{"class":2115,"line":1777},[2113,21227,7394],{"class":2326},[2113,21229,2495],{"class":2119},[2113,21231,16052],{"class":2414},[2113,21233,179],{"class":2119},[2113,21235,20410],{"class":2330},[2113,21237,2433],{"class":2119},[2113,21239,21240,21242,21244,21246,21249,21251,21254,21256,21258,21260,21262,21264,21267,21269,21271],{"class":2115,"line":4931},[2113,21241,20845],{"class":2326},[2113,21243,15832],{"class":2414},[2113,21245,179],{"class":2119},[2113,21247,21248],{"class":2414},"smsService",[2113,21250,179],{"class":2119},[2113,21252,21253],{"class":2133},"sendMessage",[2113,21255,2423],{"class":2119},[2113,21257,20606],{"class":2414},[2113,21259,179],{"class":2119},[2113,21261,20611],{"class":2414},[2113,21263,179],{"class":2119},[2113,21265,21266],{"class":2330},"PHONE_NUMBER",[2113,21268,932],{"class":2119},[2113,21270,16052],{"class":2330},[2113,21272,2553],{"class":2119},[2113,21274,21275],{"class":2115,"line":4961},[2113,21276,4665],{"class":2119},[2113,21278,21279],{"class":2115,"line":4976},[2113,21280,2572],{"class":2119},[2113,21282,21283],{"class":2115,"line":4993},[2113,21284,2125],{"emptyLinePlaceholder":1767},[2113,21286,21287,21289,21292,21294,21296,21298,21300,21302,21304,21306,21308],{"class":2115,"line":5013},[2113,21288,19213],{"class":2326},[2113,21290,21291],{"class":2133}," checkAvailabilityOfSku",[2113,21293,2423],{"class":2119},[2113,21295,184],{"class":2429},[2113,21297,4429],{"class":2119},[2113,21299,19225],{"class":2414},[2113,21301,932],{"class":2119},[2113,21303,21011],{"class":2429},[2113,21305,4429],{"class":2119},[2113,21307,19225],{"class":2414},[2113,21309,2433],{"class":2119},[2113,21311,21312,21314,21317,21319,21321,21323,21325,21327,21329,21332,21334,21336],{"class":2115,"line":5018},[2113,21313,19244],{"class":2326},[2113,21315,21316],{"class":2414}," stocks",[2113,21318,2153],{"class":2334},[2113,21320,17521],{"class":2326},[2113,21322,15832],{"class":2414},[2113,21324,179],{"class":2119},[2113,21326,16847],{"class":2414},[2113,21328,179],{"class":2119},[2113,21330,21331],{"class":2133},"getVtcElectectricL",[2113,21333,2423],{"class":2119},[2113,21335,184],{"class":2330},[2113,21337,2553],{"class":2119},[2113,21339,21340,21342,21345,21347,21349,21351,21353,21356,21358,21361,21363,21365],{"class":2115,"line":5042},[2113,21341,19244],{"class":2326},[2113,21343,21344],{"class":2414}," oldStocks",[2113,21346,2153],{"class":2334},[2113,21348,17521],{"class":2326},[2113,21350,15832],{"class":2414},[2113,21352,179],{"class":2119},[2113,21354,21355],{"class":2414},"decathlonService",[2113,21357,179],{"class":2119},[2113,21359,21360],{"class":2133},"findOneById",[2113,21362,2423],{"class":2119},[2113,21364,184],{"class":2330},[2113,21366,2553],{"class":2119},[2113,21368,21369,21371,21374,21376,21378,21380,21383,21385,21388,21391,21393,21395,21397],{"class":2115,"line":5057},[2113,21370,19244],{"class":2326},[2113,21372,21373],{"class":2414}," diffStocks",[2113,21375,2153],{"class":2334},[2113,21377,15832],{"class":2414},[2113,21379,179],{"class":2119},[2113,21381,21382],{"class":2133},"diffStocks",[2113,21384,2423],{"class":2119},[2113,21386,21387],{"class":2414},"oldStocks",[2113,21389,21390],{"class":2119},"?.",[2113,21392,20823],{"class":2330},[2113,21394,932],{"class":2119},[2113,21396,19546],{"class":2330},[2113,21398,2553],{"class":2119},[2113,21400,21401,21403,21405,21407,21409,21412,21414,21417,21419,21421,21423,21425,21427,21430],{"class":2115,"line":5062},[2113,21402,20377],{"class":2414},[2113,21404,179],{"class":2119},[2113,21406,20382],{"class":2414},[2113,21408,179],{"class":2119},[2113,21410,21411],{"class":2133},"debug",[2113,21413,2423],{"class":2119},[2113,21415,21416],{"class":2149},"`The are ",[2113,21418,15877],{"class":2326},[2113,21420,21382],{"class":2414},[2113,21422,179],{"class":2119},[2113,21424,20410],{"class":2330},[2113,21426,15888],{"class":2326},[2113,21428,21429],{"class":2149}," stocks that change`",[2113,21431,2553],{"class":2119},[2113,21433,21434],{"class":2115,"line":5098},[2113,21435,2125],{"emptyLinePlaceholder":1767},[2113,21437,21438,21440,21442],{"class":2115,"line":5117},[2113,21439,3917],{"class":2326},[2113,21441,21091],{"class":2330},[2113,21443,2487],{"class":2119},[2113,21445,21446,21448,21450,21452,21454,21456],{"class":2115,"line":5142},[2113,21447,7394],{"class":2326},[2113,21449,2495],{"class":2119},[2113,21451,21382],{"class":2414},[2113,21453,179],{"class":2119},[2113,21455,20410],{"class":2330},[2113,21457,2433],{"class":2119},[2113,21459,21460,21463,21465],{"class":2115,"line":5148},[2113,21461,21462],{"class":2330},"      message",[2113,21464,2153],{"class":2334},[2113,21466,21467],{"class":2330}," diffStocks\n",[2113,21469,21470,21472,21474,21476,21479,21481,21483,21486,21488,21490,21492,21495,21497,21499,21501,21503,21505,21507,21509,21511],{"class":2115,"line":5163},[2113,21471,10331],{"class":2119},[2113,21473,6644],{"class":2133},[2113,21475,8879],{"class":2119},[2113,21477,21478],{"class":2429},"value",[2113,21480,5709],{"class":2119},[2113,21482,8063],{"class":2326},[2113,21484,21485],{"class":2149}," `  ",[2113,21487,15877],{"class":2326},[2113,21489,21478],{"class":2414},[2113,21491,179],{"class":2119},[2113,21493,21494],{"class":2330},"type",[2113,21496,15888],{"class":2326},[2113,21498,10873],{"class":2149},[2113,21500,15877],{"class":2326},[2113,21502,21478],{"class":2414},[2113,21504,179],{"class":2119},[2113,21506,19546],{"class":2330},[2113,21508,15888],{"class":2326},[2113,21510,20427],{"class":2149},[2113,21512,4660],{"class":2119},[2113,21514,21515,21517,21519,21521,21523,21525,21527],{"class":2115,"line":5169},[2113,21516,10331],{"class":2119},[2113,21518,7230],{"class":2133},[2113,21520,2423],{"class":2119},[2113,21522,21212],{"class":2149},[2113,21524,20418],{"class":2334},[2113,21526,21212],{"class":2149},[2113,21528,2553],{"class":2119},[2113,21530,21531],{"class":2115,"line":5175},[2113,21532,4665],{"class":2119},[2113,21534,21535],{"class":2115,"line":5180},[2113,21536,2125],{"emptyLinePlaceholder":1767},[2113,21538,21539,21541,21543,21545,21547,21549,21552,21554,21556,21558,21560],{"class":2115,"line":5199},[2113,21540,20239],{"class":2326},[2113,21542,15832],{"class":2414},[2113,21544,179],{"class":2119},[2113,21546,21355],{"class":2414},[2113,21548,179],{"class":2119},[2113,21550,21551],{"class":2133},"update",[2113,21553,2423],{"class":2119},[2113,21555,184],{"class":2330},[2113,21557,932],{"class":2119},[2113,21559,19546],{"class":2330},[2113,21561,2553],{"class":2119},[2113,21563,21564,21566,21568,21570,21572,21574,21576,21578,21580,21582,21584,21586,21588,21590],{"class":2115,"line":5204},[2113,21565,3137],{"class":2326},[2113,21567,21091],{"class":2330},[2113,21569,2506],{"class":2334},[2113,21571,17813],{"class":2149},[2113,21573,15877],{"class":2326},[2113,21575,21011],{"class":2330},[2113,21577,15888],{"class":2326},[2113,21579,15746],{"class":2149},[2113,21581,20418],{"class":2334},[2113,21583,15877],{"class":2326},[2113,21585,16052],{"class":2330},[2113,21587,15888],{"class":2326},[2113,21589,20427],{"class":2149},[2113,21591,2487],{"class":2119},[2113,21593,21594],{"class":2115,"line":5209},[2113,21595,2572],{"class":2119},[2113,21597,21598],{"class":2115,"line":5232},[2113,21599,2125],{"emptyLinePlaceholder":1767},[2113,21601,21602,21605,21607,21610,21612,21615,21617,21620,21622,21624],{"class":2115,"line":5237},[2113,21603,21604],{"class":2133},"  diffStocks",[2113,21606,2423],{"class":2119},[2113,21608,21609],{"class":2429},"oldInfos",[2113,21611,4429],{"class":2119},[2113,21613,21614],{"class":2414},"StockInformations",[2113,21616,932],{"class":2119},[2113,21618,21619],{"class":2429},"newInfos",[2113,21621,4429],{"class":2119},[2113,21623,21614],{"class":2414},[2113,21625,2433],{"class":2119},[2113,21627,21628,21631,21633,21635,21637,21639],{"class":2115,"line":5242},[2113,21629,21630],{"class":2330},"    oldInfos",[2113,21632,4140],{"class":2334},[2113,21634,2495],{"class":2119},[2113,21636,21609],{"class":2330},[2113,21638,2153],{"class":2334},[2113,21640,21641],{"class":2119}," []);\n",[2113,21643,21644,21647,21649,21651,21653,21655],{"class":2115,"line":5267},[2113,21645,21646],{"class":2330},"    newInfos",[2113,21648,4140],{"class":2334},[2113,21650,2495],{"class":2119},[2113,21652,21619],{"class":2330},[2113,21654,2153],{"class":2334},[2113,21656,21641],{"class":2119},[2113,21658,21659],{"class":2115,"line":5282},[2113,21660,2125],{"emptyLinePlaceholder":1767},[2113,21662,21663,21665,21668,21670,21673,21675,21678,21680,21683,21685,21687,21689,21691],{"class":2115,"line":5295},[2113,21664,19244],{"class":2326},[2113,21666,21667],{"class":2414}," oldMap",[2113,21669,2153],{"class":2334},[2113,21671,21672],{"class":2414}," oldInfos",[2113,21674,179],{"class":2119},[2113,21676,21677],{"class":2133},"reduce",[2113,21679,8879],{"class":2119},[2113,21681,21682],{"class":2429},"acc",[2113,21684,932],{"class":2119},[2113,21686,7919],{"class":2429},[2113,21688,5709],{"class":2119},[2113,21690,8063],{"class":2326},[2113,21692,2647],{"class":2119},[2113,21694,21695,21698,21700,21702,21704,21706,21708,21710,21712],{"class":2115,"line":5310},[2113,21696,21697],{"class":2330},"      acc",[2113,21699,4124],{"class":2119},[2113,21701,7919],{"class":2414},[2113,21703,179],{"class":2119},[2113,21705,21494],{"class":2330},[2113,21707,4129],{"class":2119},[2113,21709,2335],{"class":2334},[2113,21711,7910],{"class":2330},[2113,21713,2487],{"class":2119},[2113,21715,21716,21718,21721],{"class":2115,"line":5315},[2113,21717,20652],{"class":2326},[2113,21719,21720],{"class":2330}," acc",[2113,21722,2487],{"class":2119},[2113,21724,21725,21728,21730,21733,21735,21737,21739,21741],{"class":2115,"line":5320},[2113,21726,21727],{"class":2119},"    }, {} ",[2113,21729,6242],{"class":2326},[2113,21731,21732],{"class":2414}," Record",[2113,21734,3109],{"class":2119},[2113,21736,19225],{"class":2414},[2113,21738,932],{"class":2119},[2113,21740,19236],{"class":2414},[2113,21742,21743],{"class":2119},">);\n",[2113,21745,21746],{"class":2115,"line":8625},[2113,21747,2125],{"emptyLinePlaceholder":1767},[2113,21749,21750,21752,21755,21757,21759,21761,21764,21766,21768],{"class":2115,"line":8631},[2113,21751,3137],{"class":2326},[2113,21753,21754],{"class":2414}," newInfos",[2113,21756,179],{"class":2119},[2113,21758,21186],{"class":2133},[2113,21760,8879],{"class":2119},[2113,21762,21763],{"class":2429},"info",[2113,21765,5709],{"class":2119},[2113,21767,8063],{"class":2326},[2113,21769,2647],{"class":2119},[2113,21771,21772,21774,21776,21778,21780,21782,21784,21786,21788,21790,21792,21794,21796],{"class":2115,"line":8637},[2113,21773,20579],{"class":2326},[2113,21775,21344],{"class":2414},[2113,21777,2153],{"class":2334},[2113,21779,21667],{"class":2330},[2113,21781,4124],{"class":2119},[2113,21783,21763],{"class":2414},[2113,21785,179],{"class":2119},[2113,21787,21494],{"class":2330},[2113,21789,19328],{"class":2119},[2113,21791,19546],{"class":2330},[2113,21793,4140],{"class":2334},[2113,21795,3208],{"class":2166},[2113,21797,2487],{"class":2119},[2113,21799,21800,21802,21804,21807,21809,21811,21813,21815,21817,21819],{"class":2115,"line":8652},[2113,21801,20652],{"class":2326},[2113,21803,21344],{"class":2330},[2113,21805,21806],{"class":2334}," !==",[2113,21808,2495],{"class":2119},[2113,21810,21763],{"class":2414},[2113,21812,21390],{"class":2119},[2113,21814,19546],{"class":2330},[2113,21816,4140],{"class":2334},[2113,21818,3208],{"class":2166},[2113,21820,2553],{"class":2119},[2113,21822,21823],{"class":2115,"line":8674},[2113,21824,19378],{"class":2119},[2113,21826,21827],{"class":2115,"line":8706},[2113,21828,2572],{"class":2119},[2113,21830,21831],{"class":2115,"line":8724},[2113,21832,2599],{"class":2119},[12,21834,21835],{},"Cela me permet de recevoir un SMS de la forme :",[12,21837,21838],{},[121,21839],{"alt":21840,"className":21841,"src":21842},"Les SMS",[126],"\u002FProgrammation\u002Fachat_velo\u002FSMS.png",[128,21844,21846],{"id":21845},"déploiement-de-la-notification","Déploiement de la notification",[12,21848,21849],{},"Pour déployer la notification, il me faut deux endpoints (un pour la partie web, et l'autre pour la notification).",[12,21851,21852,21853,21856,21857,21860],{},"Je garde donc dans le dossier ",[184,21854,21855],{},"src"," le fichier ",[184,21858,21859],{},"main.ts"," pour la partie web et je créé un point d'entrée spécial pour la lambda de notification:",[2105,21862,21864],{"className":15749,"code":21863,"language":15751,"meta":1646,"style":1646},"\nimport { HttpStatus } from '@nestjs\u002Fcommon';\nimport { NestFactory } from '@nestjs\u002Fcore';\nimport { Handler } from 'aws-lambda';\n\nimport { CheckAvailabilityModule } from '.\u002Fcheck-availability.module';\nimport { AvailibilityCheckerService } from '.\u002Favailability\u002Favailibility-checker.service';\n\nlet service: AvailibilityCheckerService;\n\nasync function bootstrap(): Promise\u003CAvailibilityCheckerService> {\n  const appContext = await NestFactory.createApplicationContext(\n    CheckAvailabilityModule,\n  );\n  return appContext.get(AvailibilityCheckerService);\n}\n\nexport const handler: Handler = async () => {\n  service = service ?? (await bootstrap());\n\n  await service.checkAvailibility();\n\n  return {\n    statusCode: HttpStatus.OK,\n  };\n};\n",[184,21865,21866,21870,21888,21906,21924,21928,21946,21963,21967,21979,21983,22004,22025,22032,22036,22052,22056,22060,22083,22104,22108,22122,22126,22132,22148,22153],{"__ignoreMap":1646},[2113,21867,21868],{"class":2115,"line":2116},[2113,21869,2125],{"emptyLinePlaceholder":1767},[2113,21871,21872,21874,21876,21879,21881,21883,21886],{"class":2115,"line":1647},[2113,21873,15758],{"class":2326},[2113,21875,7616],{"class":2119},[2113,21877,21878],{"class":2330},"HttpStatus",[2113,21880,7840],{"class":2119},[2113,21882,15787],{"class":2326},[2113,21884,21885],{"class":2149}," '@nestjs\u002Fcommon'",[2113,21887,2487],{"class":2119},[2113,21889,21890,21892,21894,21897,21899,21901,21904],{"class":2115,"line":1652},[2113,21891,15758],{"class":2326},[2113,21893,7616],{"class":2119},[2113,21895,21896],{"class":2330},"NestFactory",[2113,21898,7840],{"class":2119},[2113,21900,15787],{"class":2326},[2113,21902,21903],{"class":2149}," '@nestjs\u002Fcore'",[2113,21905,2487],{"class":2119},[2113,21907,21908,21910,21912,21915,21917,21919,21922],{"class":2115,"line":2185},[2113,21909,15758],{"class":2326},[2113,21911,7616],{"class":2119},[2113,21913,21914],{"class":2330},"Handler",[2113,21916,7840],{"class":2119},[2113,21918,15787],{"class":2326},[2113,21920,21921],{"class":2149}," 'aws-lambda'",[2113,21923,2487],{"class":2119},[2113,21925,21926],{"class":2115,"line":2230},[2113,21927,2125],{"emptyLinePlaceholder":1767},[2113,21929,21930,21932,21934,21937,21939,21941,21944],{"class":2115,"line":2235},[2113,21931,15758],{"class":2326},[2113,21933,7616],{"class":2119},[2113,21935,21936],{"class":2330},"CheckAvailabilityModule",[2113,21938,7840],{"class":2119},[2113,21940,15787],{"class":2326},[2113,21942,21943],{"class":2149}," '.\u002Fcheck-availability.module'",[2113,21945,2487],{"class":2119},[2113,21947,21948,21950,21952,21954,21956,21958,21961],{"class":2115,"line":2241},[2113,21949,15758],{"class":2326},[2113,21951,7616],{"class":2119},[2113,21953,21006],{"class":2330},[2113,21955,7840],{"class":2119},[2113,21957,15787],{"class":2326},[2113,21959,21960],{"class":2149}," '.\u002Favailability\u002Favailibility-checker.service'",[2113,21962,2487],{"class":2119},[2113,21964,21965],{"class":2115,"line":2246},[2113,21966,2125],{"emptyLinePlaceholder":1767},[2113,21968,21969,21971,21973,21975,21977],{"class":2115,"line":2464},[2113,21970,7907],{"class":2326},[2113,21972,21031],{"class":2330},[2113,21974,4429],{"class":2119},[2113,21976,21006],{"class":2414},[2113,21978,2487],{"class":2119},[2113,21980,21981],{"class":2115,"line":2085},[2113,21982,2125],{"emptyLinePlaceholder":1767},[2113,21984,21985,21988,21990,21993,21996,21998,22000,22002],{"class":2115,"line":2514},[2113,21986,21987],{"class":2326},"async",[2113,21989,17728],{"class":2326},[2113,21991,21992],{"class":2133}," bootstrap",[2113,21994,21995],{"class":2119},"(): ",[2113,21997,19231],{"class":2414},[2113,21999,3109],{"class":2119},[2113,22001,21006],{"class":2414},[2113,22003,4449],{"class":2119},[2113,22005,22006,22008,22011,22013,22015,22018,22020,22023],{"class":2115,"line":2533},[2113,22007,2995],{"class":2326},[2113,22009,22010],{"class":2414}," appContext",[2113,22012,2153],{"class":2334},[2113,22014,17521],{"class":2326},[2113,22016,22017],{"class":2414}," NestFactory",[2113,22019,179],{"class":2119},[2113,22021,22022],{"class":2133},"createApplicationContext",[2113,22024,3146],{"class":2119},[2113,22026,22027,22030],{"class":2115,"line":2556},[2113,22028,22029],{"class":2330},"    CheckAvailabilityModule",[2113,22031,4706],{"class":2119},[2113,22033,22034],{"class":2115,"line":2569},[2113,22035,9185],{"class":2119},[2113,22037,22038,22040,22042,22044,22046,22048,22050],{"class":2115,"line":2575},[2113,22039,2578],{"class":2326},[2113,22041,22010],{"class":2414},[2113,22043,179],{"class":2119},[2113,22045,19283],{"class":2133},[2113,22047,2423],{"class":2119},[2113,22049,21006],{"class":2330},[2113,22051,2553],{"class":2119},[2113,22053,22054],{"class":2115,"line":2596},[2113,22055,2599],{"class":2119},[2113,22057,22058],{"class":2115,"line":3192},[2113,22059,2125],{"emptyLinePlaceholder":1767},[2113,22061,22062,22064,22066,22069,22071,22073,22075,22077,22079,22081],{"class":2115,"line":3213},[2113,22063,2327],{"class":2326},[2113,22065,2839],{"class":2326},[2113,22067,22068],{"class":2133}," handler",[2113,22070,4429],{"class":2119},[2113,22072,21914],{"class":2414},[2113,22074,2153],{"class":2334},[2113,22076,17725],{"class":2326},[2113,22078,8060],{"class":2119},[2113,22080,8063],{"class":2326},[2113,22082,2647],{"class":2119},[2113,22084,22085,22088,22090,22092,22095,22097,22100,22102],{"class":2115,"line":3236},[2113,22086,22087],{"class":2330},"  service",[2113,22089,2153],{"class":2334},[2113,22091,21031],{"class":2330},[2113,22093,22094],{"class":2334}," ??",[2113,22096,2495],{"class":2119},[2113,22098,22099],{"class":2326},"await",[2113,22101,21992],{"class":2133},[2113,22103,2593],{"class":2119},[2113,22105,22106],{"class":2115,"line":3248},[2113,22107,2125],{"emptyLinePlaceholder":1767},[2113,22109,22110,22113,22115,22117,22120],{"class":2115,"line":4899},[2113,22111,22112],{"class":2326},"  await",[2113,22114,21031],{"class":2414},[2113,22116,179],{"class":2119},[2113,22118,22119],{"class":2133},"checkAvailibility",[2113,22121,3944],{"class":2119},[2113,22123,22124],{"class":2115,"line":1777},[2113,22125,2125],{"emptyLinePlaceholder":1767},[2113,22127,22128,22130],{"class":2115,"line":4931},[2113,22129,2578],{"class":2326},[2113,22131,2647],{"class":2119},[2113,22133,22134,22137,22139,22141,22143,22146],{"class":2115,"line":4961},[2113,22135,22136],{"class":2330},"    statusCode",[2113,22138,4429],{"class":2119},[2113,22140,21878],{"class":2414},[2113,22142,179],{"class":2119},[2113,22144,22145],{"class":2330},"OK",[2113,22147,4706],{"class":2119},[2113,22149,22150],{"class":2115,"line":4976},[2113,22151,22152],{"class":2119},"  };\n",[2113,22154,22155],{"class":2115,"line":4993},[2113,22156,2787],{"class":2119},[12,22158,22159],{},"Contrairement à la partie WEB ce service ne démarre pas de serveur Web (ni la partie express).",[12,22161,22162],{},"Le paramétrage de la lambda se fait donc avec le fichier serverless.yml dont voici les principales modifications:",[2105,22164,22166],{"className":19667,"code":22165,"language":19669,"meta":1646,"style":1646},"service: decathlon\nframeworkVersion: \"2\"\n\nplugins:\n  - serverless-dynamodb-local\n  - serverless-plugin-log-retention\n  - serverless-offline\n  - serverless-domain-manager\n\ncustom:\n  region: eu-central-1\n  logRetentionInDays: 7\n  serverless-offline:\n    noPrependStageInUrl: true\n    allowCache: false\n  decathlonTable:\n    name: ${self:provider.stage}-decathlon\n    arn: !GetAtt decathlonTable.Arn\n  dynamodb:\n    stages:\n      - ${self:provider.stage}\n    start:\n      migrate: true\n  endpoints:\n    dynamodbURL: \"http:\u002F\u002Flocalhost:8000\"\n  customDomain: ...\n\nprovider:\n  name: aws\n  region: ${self:custom.region}\n  runtime: nodejs12.x\n  lambdaHashingVersion: \"20201221\"\n  stage: ${opt:stage, 'dev'}\n  environment:\n    DYNAMODB_ENDPOINT: ${self:custom.endpoints.dynamodbURL}\n    DECATHLON_TABLE_NAME: ${self:custom.decathlonTable.name}\n    NODE_OPTIONS: \"--max-http-header-size=80000\"\n  iamRoleStatements: ${file(delivery\u002Froles.yml)}\n\npackage:\n  individually: true\n  patterns:\n    - \"!.\u002F**\"\n\nfunctions:\n  api:\n    handler: dist\u002Fmain.handler\n    environment:\n      region: ${self:custom.region}\n    events:\n      - http:\n          path: \u002F\n          method: ANY\n      - http:\n          path: \u002F{proxy+}\n          method: ANY\n    package:\n      patterns:\n        - dist\u002Fmain.js\n        - views\u002F**\n\n  check-availability:\n    handler: dist\u002Fcheck-availability.handler\n    events:\n      - schedule: rate(30 minutes)\n    package:\n      patterns:\n        - dist\u002Fcheck-availability.js\n\nresources:\n  Resources:\n    decathlonTable: ${file(delivery\u002FdecathlonTable.yml):decathlonTable}\n",[184,22167,22168,22177,22185,22189,22195,22202,22208,22214,22220,22224,22230,22238,22246,22252,22260,22268,22275,22285,22298,22305,22312,22319,22326,22335,22342,22352,22360,22364,22370,22378,22386,22394,22402,22410,22416,22426,22436,22444,22454,22458,22464,22473,22479,22485,22489,22495,22501,22509,22515,22523,22529,22537,22545,22553,22561,22569,22577,22584,22591,22599,22605,22609,22616,22625,22631,22643,22649,22655,22662,22666,22673,22680],{"__ignoreMap":1646},[2113,22169,22170,22172,22174],{"class":2115,"line":2116},[2113,22171,16847],{"class":2330},[2113,22173,4429],{"class":2119},[2113,22175,22176],{"class":2149},"decathlon\n",[2113,22178,22179,22181,22183],{"class":2115,"line":1647},[2113,22180,19685],{"class":2330},[2113,22182,4429],{"class":2119},[2113,22184,19690],{"class":2149},[2113,22186,22187],{"class":2115,"line":1652},[2113,22188,2125],{"emptyLinePlaceholder":1767},[2113,22190,22191,22193],{"class":2115,"line":2185},[2113,22192,19699],{"class":2330},[2113,22194,19702],{"class":2119},[2113,22196,22197,22199],{"class":2115,"line":2230},[2113,22198,19707],{"class":2119},[2113,22200,22201],{"class":2149},"serverless-dynamodb-local\n",[2113,22203,22204,22206],{"class":2115,"line":2235},[2113,22205,19707],{"class":2119},[2113,22207,19710],{"class":2149},[2113,22209,22210,22212],{"class":2115,"line":2241},[2113,22211,19707],{"class":2119},[2113,22213,19717],{"class":2149},[2113,22215,22216,22218],{"class":2115,"line":2246},[2113,22217,19707],{"class":2119},[2113,22219,19724],{"class":2149},[2113,22221,22222],{"class":2115,"line":2464},[2113,22223,2125],{"emptyLinePlaceholder":1767},[2113,22225,22226,22228],{"class":2115,"line":2085},[2113,22227,19733],{"class":2330},[2113,22229,19702],{"class":2119},[2113,22231,22232,22234,22236],{"class":2115,"line":2514},[2113,22233,19740],{"class":2330},[2113,22235,4429],{"class":2119},[2113,22237,19745],{"class":2149},[2113,22239,22240,22242,22244],{"class":2115,"line":2533},[2113,22241,19750],{"class":2330},[2113,22243,4429],{"class":2119},[2113,22245,19755],{"class":2166},[2113,22247,22248,22250],{"class":2115,"line":2556},[2113,22249,19760],{"class":2330},[2113,22251,19702],{"class":2119},[2113,22253,22254,22256,22258],{"class":2115,"line":2569},[2113,22255,19767],{"class":2330},[2113,22257,4429],{"class":2119},[2113,22259,19772],{"class":2166},[2113,22261,22262,22264,22266],{"class":2115,"line":2575},[2113,22263,19777],{"class":2330},[2113,22265,4429],{"class":2119},[2113,22267,19782],{"class":2166},[2113,22269,22270,22273],{"class":2115,"line":2596},[2113,22271,22272],{"class":2330},"  decathlonTable",[2113,22274,19702],{"class":2119},[2113,22276,22277,22280,22282],{"class":2115,"line":3192},[2113,22278,22279],{"class":2330},"    name",[2113,22281,4429],{"class":2119},[2113,22283,22284],{"class":2149},"${self:provider.stage}-decathlon\n",[2113,22286,22287,22290,22292,22295],{"class":2115,"line":3213},[2113,22288,22289],{"class":2330},"    arn",[2113,22291,4429],{"class":2119},[2113,22293,22294],{"class":2326},"!GetAtt",[2113,22296,22297],{"class":2149}," decathlonTable.Arn\n",[2113,22299,22300,22303],{"class":2115,"line":3236},[2113,22301,22302],{"class":2330},"  dynamodb",[2113,22304,19702],{"class":2119},[2113,22306,22307,22310],{"class":2115,"line":3248},[2113,22308,22309],{"class":2330},"    stages",[2113,22311,19702],{"class":2119},[2113,22313,22314,22316],{"class":2115,"line":4899},[2113,22315,20039],{"class":2119},[2113,22317,22318],{"class":2149},"${self:provider.stage}\n",[2113,22320,22321,22324],{"class":2115,"line":1777},[2113,22322,22323],{"class":2330},"    start",[2113,22325,19702],{"class":2119},[2113,22327,22328,22331,22333],{"class":2115,"line":4931},[2113,22329,22330],{"class":2330},"      migrate",[2113,22332,4429],{"class":2119},[2113,22334,19772],{"class":2166},[2113,22336,22337,22340],{"class":2115,"line":4961},[2113,22338,22339],{"class":2330},"  endpoints",[2113,22341,19702],{"class":2119},[2113,22343,22344,22347,22349],{"class":2115,"line":4976},[2113,22345,22346],{"class":2330},"    dynamodbURL",[2113,22348,4429],{"class":2119},[2113,22350,22351],{"class":2149},"\"http:\u002F\u002Flocalhost:8000\"\n",[2113,22353,22354,22356,22358],{"class":2115,"line":4993},[2113,22355,19787],{"class":2330},[2113,22357,4429],{"class":2119},[2113,22359,4902],{"class":2166},[2113,22361,22362],{"class":2115,"line":5013},[2113,22363,2125],{"emptyLinePlaceholder":1767},[2113,22365,22366,22368],{"class":2115,"line":5018},[2113,22367,19876],{"class":2330},[2113,22369,19702],{"class":2119},[2113,22371,22372,22374,22376],{"class":2115,"line":5042},[2113,22373,19883],{"class":2330},[2113,22375,4429],{"class":2119},[2113,22377,19888],{"class":2149},[2113,22379,22380,22382,22384],{"class":2115,"line":5057},[2113,22381,19740],{"class":2330},[2113,22383,4429],{"class":2119},[2113,22385,19897],{"class":2149},[2113,22387,22388,22390,22392],{"class":2115,"line":5062},[2113,22389,19902],{"class":2330},[2113,22391,4429],{"class":2119},[2113,22393,19907],{"class":2149},[2113,22395,22396,22398,22400],{"class":2115,"line":5098},[2113,22397,19912],{"class":2330},[2113,22399,4429],{"class":2119},[2113,22401,19917],{"class":2149},[2113,22403,22404,22406,22408],{"class":2115,"line":5117},[2113,22405,19922],{"class":2330},[2113,22407,4429],{"class":2119},[2113,22409,19927],{"class":2149},[2113,22411,22412,22414],{"class":2115,"line":5142},[2113,22413,19932],{"class":2330},[2113,22415,19702],{"class":2119},[2113,22417,22418,22421,22423],{"class":2115,"line":5148},[2113,22419,22420],{"class":2330},"    DYNAMODB_ENDPOINT",[2113,22422,4429],{"class":2119},[2113,22424,22425],{"class":2149},"${self:custom.endpoints.dynamodbURL}\n",[2113,22427,22428,22431,22433],{"class":2115,"line":5163},[2113,22429,22430],{"class":2330},"    DECATHLON_TABLE_NAME",[2113,22432,4429],{"class":2119},[2113,22434,22435],{"class":2149},"${self:custom.decathlonTable.name}\n",[2113,22437,22438,22440,22442],{"class":2115,"line":5169},[2113,22439,19939],{"class":2330},[2113,22441,4429],{"class":2119},[2113,22443,19944],{"class":2149},[2113,22445,22446,22449,22451],{"class":2115,"line":5175},[2113,22447,22448],{"class":2330},"  iamRoleStatements",[2113,22450,4429],{"class":2119},[2113,22452,22453],{"class":2149},"${file(delivery\u002Froles.yml)}\n",[2113,22455,22456],{"class":2115,"line":5180},[2113,22457,2125],{"emptyLinePlaceholder":1767},[2113,22459,22460,22462],{"class":2115,"line":5199},[2113,22461,15998],{"class":2330},[2113,22463,19702],{"class":2119},[2113,22465,22466,22469,22471],{"class":2115,"line":5204},[2113,22467,22468],{"class":2330},"  individually",[2113,22470,4429],{"class":2119},[2113,22472,19772],{"class":2166},[2113,22474,22475,22477],{"class":2115,"line":5209},[2113,22476,19959],{"class":2330},[2113,22478,19702],{"class":2119},[2113,22480,22481,22483],{"class":2115,"line":5232},[2113,22482,19966],{"class":2119},[2113,22484,19969],{"class":2149},[2113,22486,22487],{"class":2115,"line":5237},[2113,22488,2125],{"emptyLinePlaceholder":1767},[2113,22490,22491,22493],{"class":2115,"line":5242},[2113,22492,19992],{"class":2330},[2113,22494,19702],{"class":2119},[2113,22496,22497,22499],{"class":2115,"line":5267},[2113,22498,19999],{"class":2330},[2113,22500,19702],{"class":2119},[2113,22502,22503,22505,22507],{"class":2115,"line":5282},[2113,22504,20006],{"class":2330},[2113,22506,4429],{"class":2119},[2113,22508,20011],{"class":2149},[2113,22510,22511,22513],{"class":2115,"line":5295},[2113,22512,20016],{"class":2330},[2113,22514,19702],{"class":2119},[2113,22516,22517,22519,22521],{"class":2115,"line":5310},[2113,22518,20023],{"class":2330},[2113,22520,4429],{"class":2119},[2113,22522,19897],{"class":2149},[2113,22524,22525,22527],{"class":2115,"line":5315},[2113,22526,20032],{"class":2330},[2113,22528,19702],{"class":2119},[2113,22530,22531,22533,22535],{"class":2115,"line":5320},[2113,22532,20039],{"class":2119},[2113,22534,18039],{"class":2330},[2113,22536,19702],{"class":2119},[2113,22538,22539,22541,22543],{"class":2115,"line":8625},[2113,22540,20048],{"class":2330},[2113,22542,4429],{"class":2119},[2113,22544,20053],{"class":2149},[2113,22546,22547,22549,22551],{"class":2115,"line":8631},[2113,22548,20058],{"class":2330},[2113,22550,4429],{"class":2119},[2113,22552,20063],{"class":2149},[2113,22554,22555,22557,22559],{"class":2115,"line":8637},[2113,22556,20039],{"class":2119},[2113,22558,18039],{"class":2330},[2113,22560,19702],{"class":2119},[2113,22562,22563,22565,22567],{"class":2115,"line":8652},[2113,22564,20048],{"class":2330},[2113,22566,4429],{"class":2119},[2113,22568,20080],{"class":2149},[2113,22570,22571,22573,22575],{"class":2115,"line":8674},[2113,22572,20058],{"class":2330},[2113,22574,4429],{"class":2119},[2113,22576,20063],{"class":2149},[2113,22578,22579,22582],{"class":2115,"line":8706},[2113,22580,22581],{"class":2330},"    package",[2113,22583,19702],{"class":2119},[2113,22585,22586,22589],{"class":2115,"line":8724},[2113,22587,22588],{"class":2330},"      patterns",[2113,22590,19702],{"class":2119},[2113,22592,22593,22596],{"class":2115,"line":8729},[2113,22594,22595],{"class":2119},"        - ",[2113,22597,22598],{"class":2149},"dist\u002Fmain.js\n",[2113,22600,22601,22603],{"class":2115,"line":8734},[2113,22602,22595],{"class":2119},[2113,22604,19983],{"class":2149},[2113,22606,22607],{"class":2115,"line":8740},[2113,22608,2125],{"emptyLinePlaceholder":1767},[2113,22610,22611,22614],{"class":2115,"line":8772},[2113,22612,22613],{"class":2330},"  check-availability",[2113,22615,19702],{"class":2119},[2113,22617,22618,22620,22622],{"class":2115,"line":8778},[2113,22619,20006],{"class":2330},[2113,22621,4429],{"class":2119},[2113,22623,22624],{"class":2149},"dist\u002Fcheck-availability.handler\n",[2113,22626,22627,22629],{"class":2115,"line":8789},[2113,22628,20032],{"class":2330},[2113,22630,19702],{"class":2119},[2113,22632,22633,22635,22638,22640],{"class":2115,"line":8795},[2113,22634,20039],{"class":2119},[2113,22636,22637],{"class":2330},"schedule",[2113,22639,4429],{"class":2119},[2113,22641,22642],{"class":2149},"rate(30 minutes)\n",[2113,22644,22645,22647],{"class":2115,"line":8817},[2113,22646,22581],{"class":2330},[2113,22648,19702],{"class":2119},[2113,22650,22651,22653],{"class":2115,"line":8822},[2113,22652,22588],{"class":2330},[2113,22654,19702],{"class":2119},[2113,22656,22657,22659],{"class":2115,"line":8857},[2113,22658,22595],{"class":2119},[2113,22660,22661],{"class":2149},"dist\u002Fcheck-availability.js\n",[2113,22663,22664],{"class":2115,"line":8896},[2113,22665,2125],{"emptyLinePlaceholder":1767},[2113,22667,22668,22671],{"class":2115,"line":8901},[2113,22669,22670],{"class":2330},"resources",[2113,22672,19702],{"class":2119},[2113,22674,22675,22678],{"class":2115,"line":8907},[2113,22676,22677],{"class":2330},"  Resources",[2113,22679,19702],{"class":2119},[2113,22681,22682,22685,22687],{"class":2115,"line":8913},[2113,22683,22684],{"class":2330},"    decathlonTable",[2113,22686,4429],{"class":2119},[2113,22688,22689],{"class":2149},"${file(delivery\u002FdecathlonTable.yml):decathlonTable}\n",[12,22691,22692],{},"Il ne reste plus qu'à déployer et profiter de l'envoi de SMS.",[128,22694,22696],{"id":22695},"optimisation","Optimisation",[12,22698,22699],{},"Sur AWS, le coût d'une lambda est lié au nombre d'exécutions mais aussi au temps d'exécution. Il est important que le temps d'exécution soit le plus\nrapide possible.",[12,22701,22702],{},"Ici dans notre cas le temps le temps de traitement sera lié au temps de réponse de la requête HTTP extérieur à notre site. Afin de parfaire notre petite\napplication de bonne pratique, nous allons utiliser webpack (côté serveur) afin d'optimiser le temps de démarrage de notre application.",[12,22704,22705,22706,22709],{},"Grâce au framework Nest.JS, cela est très facile à faire, dans le fichier ",[184,22707,22708],{},"nest-cli.json",", nous allons demander à Nest.JS de générer un bundle webpack:",[2105,22711,22713],{"className":18051,"code":22712,"language":18053,"meta":1646,"style":1646},"{\n  \"collection\": \"@nestjs\u002Fschematics\",\n  \"sourceRoot\": \"src\",\n  \"compilerOptions\": {\n    \"webpack\": true\n  }\n}\n",[184,22714,22715,22719,22731,22743,22750,22759,22763],{"__ignoreMap":1646},[2113,22716,22717],{"class":2115,"line":2116},[2113,22718,18060],{"class":2119},[2113,22720,22721,22724,22726,22729],{"class":2115,"line":1647},[2113,22722,22723],{"class":2330},"  \"collection\"",[2113,22725,4429],{"class":2119},[2113,22727,22728],{"class":2149},"\"@nestjs\u002Fschematics\"",[2113,22730,4706],{"class":2119},[2113,22732,22733,22736,22738,22741],{"class":2115,"line":1652},[2113,22734,22735],{"class":2330},"  \"sourceRoot\"",[2113,22737,4429],{"class":2119},[2113,22739,22740],{"class":2149},"\"src\"",[2113,22742,4706],{"class":2119},[2113,22744,22745,22748],{"class":2115,"line":2185},[2113,22746,22747],{"class":2330},"  \"compilerOptions\"",[2113,22749,8091],{"class":2119},[2113,22751,22752,22755,22757],{"class":2115,"line":2230},[2113,22753,22754],{"class":2330},"    \"webpack\"",[2113,22756,4429],{"class":2119},[2113,22758,19772],{"class":2166},[2113,22760,22761],{"class":2115,"line":2235},[2113,22762,2572],{"class":2119},[2113,22764,22765],{"class":2115,"line":2241},[2113,22766,2599],{"class":2119},[12,22768,22769,22770,3938],{},"Par défaut, webpack ne crée un bundle qu'avec le source de notre application (et donc sans les modules node). Ce qui nous intéresse c'est de faire un bundle\ncomprenant notre application ainsi que les modules node (on peut voir un bench sur la ",[49,22771,22773],{"href":19662,"rel":22772},[347],"FAQ Nest.JS",[12,22775,22776,22777,22780],{},"Pour cela nous allons créer un fichier ",[184,22778,22779],{},"webpack.config.js"," qui sera pris automatiquement (inspiré de la documentation mais modifié pour posséder plusieurs points\nd'entrées, on exclut aussi le framework AWS qui est déjà inclus dans AWS):",[2105,22782,22784],{"className":15749,"code":22783,"language":15751,"meta":1646,"style":1646},"module.exports = (options, webpack) => {\n  const lazyImports = [\n    \"@nestjs\u002Fmicroservices\u002Fmicroservices-module\",\n    \"@nestjs\u002Fwebsockets\u002Fsocket-module\",\n  ];\n\n  return {\n    ...options,\n    entry: {\n      main: options.entry,\n      \"check-availability\": options.entry.replace(\"main\", \"check-availability\"),\n    },\n    externals: [\"aws-sdk\"],\n    output: {\n      filename: \"[name].js\",\n      libraryTarget: \"commonjs2\",\n    },\n    plugins: [\n      ...options.plugins,\n      new webpack.IgnorePlugin({\n        checkResource(resource) {\n          if (lazyImports.includes(resource)) {\n            try {\n              require.resolve(resource);\n            } catch (err) {\n              return true;\n            }\n          }\n          return false;\n        },\n      }),\n    ],\n  };\n};\n",[184,22785,22786,22813,22825,22832,22839,22844,22848,22854,22863,22870,22886,22916,22920,22932,22939,22951,22963,22967,22974,22986,23001,23013,23035,23042,23058,23071,23080,23084,23089,23098,23102,23107,23111,23115],{"__ignoreMap":1646},[2113,22787,22788,22791,22793,22796,22798,22800,22802,22804,22807,22809,22811],{"class":2115,"line":2116},[2113,22789,22790],{"class":2414},"module",[2113,22792,179],{"class":2119},[2113,22794,22795],{"class":2414},"exports",[2113,22797,2153],{"class":2334},[2113,22799,2495],{"class":2119},[2113,22801,17539],{"class":2429},[2113,22803,932],{"class":2119},[2113,22805,22806],{"class":2429},"webpack",[2113,22808,5709],{"class":2119},[2113,22810,8063],{"class":2326},[2113,22812,2647],{"class":2119},[2113,22814,22815,22817,22820,22822],{"class":2115,"line":1647},[2113,22816,2995],{"class":2326},[2113,22818,22819],{"class":2414}," lazyImports",[2113,22821,2153],{"class":2334},[2113,22823,22824],{"class":2119}," [\n",[2113,22826,22827,22830],{"class":2115,"line":1652},[2113,22828,22829],{"class":2149},"    \"@nestjs\u002Fmicroservices\u002Fmicroservices-module\"",[2113,22831,4706],{"class":2119},[2113,22833,22834,22837],{"class":2115,"line":2185},[2113,22835,22836],{"class":2149},"    \"@nestjs\u002Fwebsockets\u002Fsocket-module\"",[2113,22838,4706],{"class":2119},[2113,22840,22841],{"class":2115,"line":2230},[2113,22842,22843],{"class":2119},"  ];\n",[2113,22845,22846],{"class":2115,"line":2235},[2113,22847,2125],{"emptyLinePlaceholder":1767},[2113,22849,22850,22852],{"class":2115,"line":2241},[2113,22851,2578],{"class":2326},[2113,22853,2647],{"class":2119},[2113,22855,22856,22859,22861],{"class":2115,"line":2246},[2113,22857,22858],{"class":2119},"    ...",[2113,22860,17539],{"class":2330},[2113,22862,4706],{"class":2119},[2113,22864,22865,22868],{"class":2115,"line":2464},[2113,22866,22867],{"class":2330},"    entry",[2113,22869,8091],{"class":2119},[2113,22871,22872,22875,22877,22879,22881,22884],{"class":2115,"line":2085},[2113,22873,22874],{"class":2330},"      main",[2113,22876,4429],{"class":2119},[2113,22878,17539],{"class":2414},[2113,22880,179],{"class":2119},[2113,22882,22883],{"class":2330},"entry",[2113,22885,4706],{"class":2119},[2113,22887,22888,22891,22893,22895,22897,22899,22901,22904,22906,22909,22911,22914],{"class":2115,"line":2514},[2113,22889,22890],{"class":2149},"      \"check-availability\"",[2113,22892,4429],{"class":2119},[2113,22894,17539],{"class":2414},[2113,22896,179],{"class":2119},[2113,22898,22883],{"class":2414},[2113,22900,179],{"class":2119},[2113,22902,22903],{"class":2133},"replace",[2113,22905,2423],{"class":2119},[2113,22907,22908],{"class":2149},"\"main\"",[2113,22910,932],{"class":2119},[2113,22912,22913],{"class":2149},"\"check-availability\"",[2113,22915,7142],{"class":2119},[2113,22917,22918],{"class":2115,"line":2533},[2113,22919,8237],{"class":2119},[2113,22921,22922,22925,22927,22930],{"class":2115,"line":2556},[2113,22923,22924],{"class":2330},"    externals",[2113,22926,6155],{"class":2119},[2113,22928,22929],{"class":2149},"\"aws-sdk\"",[2113,22931,7173],{"class":2119},[2113,22933,22934,22937],{"class":2115,"line":2569},[2113,22935,22936],{"class":2330},"    output",[2113,22938,8091],{"class":2119},[2113,22940,22941,22944,22946,22949],{"class":2115,"line":2575},[2113,22942,22943],{"class":2330},"      filename",[2113,22945,4429],{"class":2119},[2113,22947,22948],{"class":2149},"\"[name].js\"",[2113,22950,4706],{"class":2119},[2113,22952,22953,22956,22958,22961],{"class":2115,"line":2596},[2113,22954,22955],{"class":2330},"      libraryTarget",[2113,22957,4429],{"class":2119},[2113,22959,22960],{"class":2149},"\"commonjs2\"",[2113,22962,4706],{"class":2119},[2113,22964,22965],{"class":2115,"line":3192},[2113,22966,8237],{"class":2119},[2113,22968,22969,22972],{"class":2115,"line":3213},[2113,22970,22971],{"class":2330},"    plugins",[2113,22973,18219],{"class":2119},[2113,22975,22976,22978,22980,22982,22984],{"class":2115,"line":3236},[2113,22977,19449],{"class":2119},[2113,22979,17539],{"class":2414},[2113,22981,179],{"class":2119},[2113,22983,19699],{"class":2330},[2113,22985,4706],{"class":2119},[2113,22987,22988,22991,22994,22996,22999],{"class":2115,"line":3248},[2113,22989,22990],{"class":2326},"      new",[2113,22992,22993],{"class":2414}," webpack",[2113,22995,179],{"class":2119},[2113,22997,22998],{"class":2133},"IgnorePlugin",[2113,23000,19349],{"class":2119},[2113,23002,23003,23006,23008,23011],{"class":2115,"line":4899},[2113,23004,23005],{"class":2133},"        checkResource",[2113,23007,2423],{"class":2119},[2113,23009,23010],{"class":2429},"resource",[2113,23012,2433],{"class":2119},[2113,23014,23015,23018,23020,23023,23025,23028,23030,23032],{"class":2115,"line":1777},[2113,23016,23017],{"class":2326},"          if",[2113,23019,2495],{"class":2119},[2113,23021,23022],{"class":2414},"lazyImports",[2113,23024,179],{"class":2119},[2113,23026,23027],{"class":2133},"includes",[2113,23029,2423],{"class":2119},[2113,23031,23010],{"class":2330},[2113,23033,23034],{"class":2119},")) {\n",[2113,23036,23037,23040],{"class":2115,"line":4931},[2113,23038,23039],{"class":2326},"            try",[2113,23041,2647],{"class":2119},[2113,23043,23044,23047,23049,23052,23054,23056],{"class":2115,"line":4961},[2113,23045,23046],{"class":2414},"              require",[2113,23048,179],{"class":2119},[2113,23050,23051],{"class":2133},"resolve",[2113,23053,2423],{"class":2119},[2113,23055,23010],{"class":2330},[2113,23057,2553],{"class":2119},[2113,23059,23060,23063,23065,23067,23069],{"class":2115,"line":4976},[2113,23061,23062],{"class":2119},"            } ",[2113,23064,8973],{"class":2326},[2113,23066,2495],{"class":2119},[2113,23068,6794],{"class":2330},[2113,23070,2433],{"class":2119},[2113,23072,23073,23076,23078],{"class":2115,"line":4993},[2113,23074,23075],{"class":2326},"              return",[2113,23077,4872],{"class":2166},[2113,23079,2487],{"class":2119},[2113,23081,23082],{"class":2115,"line":5013},[2113,23083,5172],{"class":2119},[2113,23085,23086],{"class":2115,"line":5018},[2113,23087,23088],{"class":2119},"          }\n",[2113,23090,23091,23094,23096],{"class":2115,"line":5042},[2113,23092,23093],{"class":2326},"          return",[2113,23095,5052],{"class":2166},[2113,23097,2487],{"class":2119},[2113,23099,23100],{"class":2115,"line":5057},[2113,23101,12744],{"class":2119},[2113,23103,23104],{"class":2115,"line":5062},[2113,23105,23106],{"class":2119},"      }),\n",[2113,23108,23109],{"class":2115,"line":5098},[2113,23110,19140],{"class":2119},[2113,23112,23113],{"class":2115,"line":5117},[2113,23114,22152],{"class":2119},[2113,23116,23117],{"class":2115,"line":5142},[2113,23118,2787],{"class":2119},[12,23120,23121],{},"A l'heure actuelle (mais principalement à cause du temps d'exécution de la requête), la requête prend environ 1s:",[2105,23123,23126],{"className":23124,"code":23125,"language":8748,"meta":1646,"style":1646},"language-log shiki shiki-themes one-dark-pro","REPORT RequestId: eaa2b5a3-a754-4b1f-adcc-22ca3c6045aa Duration: 984.41 ms Billed Duration: 985 ms Memory Size: 1024 MB Max Memory Used: 109 MB Init Duration: 540.30 ms\n",[184,23127,23128],{"__ignoreMap":1646},[2113,23129,23130,23133,23136,23139,23142,23144,23147,23150,23153,23156,23159,23162,23165,23168,23171,23173,23176],{"class":2115,"line":2116},[2113,23131,23132],{"class":2119},"REPORT RequestId: ",[2113,23134,23135],{"class":2166},"eaa2b5a3-a754-4b1f-adcc-22ca3c6045aa",[2113,23137,23138],{"class":2119}," Duration: ",[2113,23140,23141],{"class":2166},"984",[2113,23143,179],{"class":2119},[2113,23145,23146],{"class":2166},"41",[2113,23148,23149],{"class":2119}," ms Billed Duration: ",[2113,23151,23152],{"class":2166},"985",[2113,23154,23155],{"class":2119}," ms Memory Size: ",[2113,23157,23158],{"class":2166},"1024",[2113,23160,23161],{"class":2119}," MB Max Memory Used: ",[2113,23163,23164],{"class":2166},"109",[2113,23166,23167],{"class":2119}," MB Init Duration: ",[2113,23169,23170],{"class":2166},"540",[2113,23172,179],{"class":2119},[2113,23174,23175],{"class":2166},"30",[2113,23177,23178],{"class":2119}," ms\n",[128,23180,23182],{"id":23181},"le-coût-du-projet","Le coût du projet",[12,23184,23185],{},"Grâce au Free-Tiers d'AWS, ce qui me coûte réellement de l'argent dans ce projet est SNS (qui sert à l'envoi de SMS). L'autre coût est relatif à Route 53 car j'ai déployé le site\ndans un sous-domaine.",[12,23187,23188],{},"Au 29 Août 2021, je vais avoir dépensé 7,34$ pour me faire plaisir (j'aurais pu le déployer sur mes propres serveurs dédiés) et pour pouvoir m'acheter un vélo sans\nattendre que le site m'envoie un mail ou qu'il y ait suffisamment de stock pendant suffisamment longtemps pour que je puisse voir le vélo, ou l'acheter en magasin.",[12,23190,23191],{},[121,23192],{"alt":23193,"className":23194,"src":23195},"Coût du projet",[126],"\u002FProgrammation\u002Fachat_velo\u002Fcout.png",[128,23197,23199],{"id":23198},"bilan","Bilan",[12,23201,23202],{},"On est fin septembre, j'ai reçu mon vélo et j'ai pu rouler plusieurs semaines avec (Je l'ai recu début septembre).",[12,23204,23205],{},"Cela a commencé par une notification à 11h59 indiquant que 4 exemplaires avaient été ajoutés au site. J'ai pu le commander dans la foulée. A 14h, tous les exemplaires avaient été vendus.\nJe devais partir manger quand j'ai reçu la notification.",[12,23207,23208],{},"Si je n'avais pas développé ce petit programme, je n'aurais jamais su que des vélos étaient en stock, car je n'ai jamais reçu le fameux mail. De plus en travaillant\n(car je suis revenu de vacances depuis) je ne peux pas rafraîchir la page en continu (1 fois le matin, 1 fois le soir).",[12,23210,23211],{},"Bref, est-ce que le mail que doit nous envoyer le site est de la poudre aux yeux ? Se sert-il d'une file d'attente (et dans ce cas je m'excuse auprès de celui que\nj'ai doublé) ? Ou n'est-il envoyé que quand il y a plus de 10 exemplaires qui arrivent sur le site ?",[12,23213,23214],{},"Depuis j'ai désactivé le programme. Avant j'avais pu voir le stock bouger rapidement sur les VTT en XL et sur le VTC en XL et en S. Ce que je peux dire c'est qu'une\ntrentaine de vélo ajoutés sur le VTT le soir à 21h partent comme des petits pains. Il n'en restait plus le lendemin à 12h...",[12,23216,23217],{},"Au delà de l'achat du vélo, j'ai été content de pouvoir mettre un site en production rapidement et quasi-gratuitement (si on ne compte pas les SMS). Je me demande ce\nque m'aurais coûté le même site au delà de la période des 1 an (après la fin du free-tiers).",[23219,23220,23223,23228],"section",{"className":23221,"dataFootnotes":1646},[23222],"footnotes",[128,23224,23227],{"className":23225,"id":17406},[23226],"sr-only","Footnotes",[367,23229,23230,23241],{},[370,23231,23233,23234],{"id":23232},"user-content-fn-1","Certains me conseillent des petits magasins de vélo, d'autres des VTCAE à plus de 5 000 €. Bref, nous ne sommes pas là pour parler de mon choix d'enseigne. Mais si vous voulez tout savoir, c'est une question pratique. Il est à coté de chez moi. ",[49,23235,23240],{"href":23236,"ariaLabel":23237,"className":23238,"dataFootnoteBackref":1646},"#user-content-fnref-1","Back to reference 1",[23239],"data-footnote-backref","↩",[370,23242,23244,23249],{"id":23243},"user-content-fn-2",[1886,23245,23246],{},[12,23247,23248],{},"Hello friend jsdom only handles static html, to make the click event you will need a headless browser for scraping the web. There are several one of them is the Puppeter that uses the chorme in the background.",[49,23250,23240],{"href":23251,"ariaLabel":23252,"className":23253,"dataFootnoteBackref":1646},"#user-content-fnref-2","Back to reference 2",[23239],[3358,23255,23256],{},"html pre.shiki code .seHd6, html code.shiki .seHd6{--shiki-default:#C678DD}html pre.shiki code .sU0A5, html code.shiki .sU0A5{--shiki-default:#E5C07B}html pre.shiki code .sjrmR, html code.shiki .sjrmR{--shiki-default:#56B6C2}html pre.shiki code .sn6KH, html code.shiki .sn6KH{--shiki-default:#ABB2BF}html pre.shiki code .sVbv2, html code.shiki .sVbv2{--shiki-default:#61AFEF}html pre.shiki code .sVyAn, html code.shiki .sVyAn{--shiki-default:#E06C75}html pre.shiki code .subq3, html code.shiki .subq3{--shiki-default:#98C379}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html pre.shiki code .sVC51, html code.shiki .sVC51{--shiki-default:#D19A66}html pre.shiki code .sLaUg, html code.shiki .sLaUg{--shiki-default:#FFFFFF}html pre.shiki code .s_ZVi, html code.shiki .s_ZVi{--shiki-default:#E06C75;--shiki-default-font-style:italic}html pre.shiki code .sV9Aq, html code.shiki .sV9Aq{--shiki-default:#7F848E;--shiki-default-font-style:italic}html pre.shiki code .shdRp, html code.shiki .shdRp{--shiki-default:#C678DD;--shiki-default-font-style:italic}html pre.shiki code .sKU4T, html code.shiki .sKU4T{--shiki-default:#E5C07B;--shiki-default-font-style:italic}",{"title":1646,"searchDepth":1647,"depth":1647,"links":23258},[23259,23260,23262,23263,23264,23265,23266,23267,23268,23269,23270,23271,23272],{"id":17426,"depth":1647,"text":17427},{"id":17457,"depth":1647,"text":23261},"La 1er version",{"id":18006,"depth":1647,"text":18007},{"id":18149,"depth":1647,"text":18150},{"id":18501,"depth":1647,"text":18502},{"id":19647,"depth":1647,"text":19648},{"id":20113,"depth":1647,"text":20114},{"id":20138,"depth":1647,"text":20139},{"id":21845,"depth":1647,"text":21846},{"id":22695,"depth":1647,"text":22696},{"id":23181,"depth":1647,"text":23182},{"id":23198,"depth":1647,"text":23199},{"id":17406,"depth":1647,"text":23227},"2021-09-27",{"type":9,"value":23275},[23276,23278,23282,23289,23291,23295,23297],[12,23277,17388],{},[12,23279,17391,23280,17395],{},[1892,23281,17394],{},[12,23283,17398,23284],{},[17400,23285,23286],{},[49,23287,3686],{"href":17404,"ariaDescribedBy":23288,"dataFootnoteRef":1646,"id":17407},[17406],[12,23290,17410],{},[12,23292,17413,23293,17417],{},[362,23294,17416],{},[12,23296,17420],{},[12,23298,17423],{},{"planet":1767},"\u002Fpost\u002Fachat_velo",{"title":17383,"description":17388},"achat_velo","posts\u002FProgrammation\u002F2021-09-27_achat_velo",[3510,3511,23305,23306,23307,23308],"aws","web","velo","sms","p5JgMwprkltEBjhdZdpkmC59vL1CqDYiHhoE51Qhs0U",{"id":23311,"title":23312,"author":7,"body":23313,"category":24866,"categorySlug":24867,"date":24868,"description":24869,"excerpt":24870,"extension":1764,"location":1765,"meta":24891,"navigation":1767,"path":24892,"published":1767,"seo":24893,"slug":24894,"stem":24895,"tags":24896,"timeToRead":2514,"__hash__":24898},"posts\u002Fposts\u002FLogiciels\u002F2021-03-02-gridsome-mise-a-jour-du-blog.md","Mise à jour du blog vers gridsome",{"type":9,"value":23314,"toc":24857},[23315,23329,23343,23346,23349,23363,23366,23370,23373,23376,23404,23407,23411,23414,23442,23445,23448,23476,23479,23504,23507,23565,23568,23637,23641,23648,23656,23659,23753,23771,23839,23842,23882,23889,23962,23965,24023,24032,24035,24102,24105,24187,24190,24194,24197,24200,24203,24206,24215,24218,24225,24475,24484,24488,24491,24494,24504,24507,24537,24540,24543,24546,24554,24563,24572,24580,24583,24593,24766,24769,24821,24824,24826,24829,24840,24854],[12,23316,23317,23318,23322,23323,23328],{},"J'avais migré en Janvier 2013 mon ",[49,23319,23321],{"href":23320},"\u002Fpages\u002Fmigration-to-pelican","blog vers Pelican"," en venant de\n",[49,23324,23327],{"href":23325,"rel":23326},"https:\u002F\u002Ffr.dotclear.org\u002F",[347],"Dotclear",". Pelican est un générateur de site statique en Python. J'avais\nalors dû faire l'impasse sur les commentaires mais au bénéfice d'un site performant et avec une\nsurface d'attaque plus faible.",[12,23330,23331,23332,23337,23338,179],{},"Plus tard, en décembre 2016, j'avais changé le thème pour ",[49,23333,23336],{"href":23334,"rel":23335},"https:\u002F\u002Fgithub.com\u002Falexandrevicenzi\u002FFlex",[347],"alexandrevicenzi\u002FFlex",".\nAujoud'hui je vais vous montrer la migration de mon blog vers ",[49,23339,23342],{"href":23340,"rel":23341},"https:\u002F\u002Fgridsome.org\u002F",[347],"Gridsome",[12,23344,23345],{},"Je reste sur un générateur de site statique car, j'aime l'idée d'un site à la fois rapide et\nimmuable tant que je ne décide pas de modifier le contenu mon blog. Alors pourquoi changer de Pelican\nvers Gridsome ?",[12,23347,23348],{},"Gridsome est un générateur de site statique écrit en Javascript. Il a la particularité de pouvoir générer\ndes pages depuis des fichiers au format Markdown mais aussi depuis des CMS Headless, ou de n'importe\nquelles autres API et ce grace à une API commune en GraphQL.",[12,23350,23351,23352,23357,23358,179],{},"En plus de générer le site statique au format HTML, Gridsome fonctionne comme une SPA",[17400,23353,23354],{},[49,23355,3686],{"href":17404,"ariaDescribedBy":23356,"dataFootnoteRef":1646,"id":17407},[17406],". Dans une SPA\nles pages sont générées côté client et sont mises à jour lors d'appel API pour récupérer les données. Ainsi\nseul le contenu modifié est rafraichit. Le framework Gridsome se base sur ",[49,23359,23362],{"href":23360,"rel":23361},"https:\u002F\u002Ffr.vuejs.org\u002F",[347],"Vue.Js",[12,23364,23365],{},"Ce qui m'a plus dans Gridsome, c'est l'utilisation de Vue.JS pour générer les pages statiques, mais aussi\nl'utilisation de GraphQL pour requêter le contenu des pages d'une même et unique façon.",[128,23367,23369],{"id":23368},"alternatives","Alternatives",[12,23371,23372],{},"Lors de la migration de mon blog, je voulais une technologie basée sur Vue.Js, car c'est un framework que\nj'apprécie. Mais le plus important, c'est que j'avais besoin de changement (pour le plaisir quoi ;)).",[12,23374,23375],{},"Les générateurs de site statique en Vue.Js sont :",[506,23377,23378,23385,23392,23399],{},[370,23379,23380],{},[49,23381,23384],{"href":23382,"rel":23383},"https:\u002F\u002Fvuepress.vuejs.org\u002F",[347],"VuePress",[370,23386,23387],{},[49,23388,23391],{"href":23389,"rel":23390},"https:\u002F\u002Ffr.nuxtjs.org\u002F",[347],"Nuxt.js",[370,23393,23394],{},[49,23395,23398],{"href":23396,"rel":23397},"https:\u002F\u002Fsaber.land\u002F",[347],"Saber",[370,23400,23401],{},[49,23402,23342],{"href":23340,"rel":23403},[347],[12,23405,23406],{},"Je vous laisse regarder et vous faire votre avis sur les différents framework. J'ai surtout testé VuePress mais\nj'ai changé pour Gridsome quand j'ai vu la simplicité d'implémentation.",[128,23408,23410],{"id":23409},"début-de-limplémentation","Début de l'implémentation",[12,23412,23413],{},"Je voulais changer de technologie, mais j'aimais bien le thème Pelican que j'utilisais. Je n'avais donc pas\nenvie de le changer. Je me suis imposé également les contraintes suivantes:",[506,23415,23416,23419,23430,23433,23436,23439],{},[370,23417,23418],{},"Je ne voulais pas changer les différents chemins d'accès au site (tant que faire se peut).",[370,23420,23421,23422],{},"Je voulais garder les fonctionnalités suivantes:\n",[506,23423,23424,23427],{},[370,23425,23426],{},"Sitemap",[370,23428,23429],{},"Atom",[370,23431,23432],{},"J'avais des billets écrits en Markdown mais aussi 3 pages que je ne voulais pas réécrire en HTML",[370,23434,23435],{},"Garder le même thème",[370,23437,23438],{},"Avoir une page par catégorie et une page avec toutes les catégories",[370,23440,23441],{},"Avoir une page par tag et une page avec tous les tags",[12,23443,23444],{},"Je commence donc à initialiser un projet de zéro avec mes différents billets de blog, les pages (contact,\nanciens-projets, et contact) et les fichiers statiques (les images, les zips, ...).",[12,23446,23447],{},"J'ai du retravailler les différents billets de blog au format Markdown:",[506,23449,23450,23453,23469],{},[370,23451,23452],{},"J'ai dû déplacer les différents attributs du fichier pour les placer dans un frontmatter au format YAML.",[370,23454,23455,23456,23459,23460,23462,23463,179,23466,23468],{},"Les fichiers ont un nom qui porte de l'information. Ainsi, le dossier dans lequel se trouve le fichier\nconstitue le nom de la catégorie, et le nom du fichier porte la date de publication et le ",[184,23457,23458],{},"slug"," (utilisé\npour le chemin dans l'URL).",[15567,23461],{},"Par exemple, avec le fichier: ",[184,23464,23465],{},"Logiciels\u002F2009-02-15_debian-lenny-est-sortie.md",[15567,23467],{},"Avec le changement de paradigme de Gridsome, j'ai dû intégrer ces différentes informations dans le frontmatter.",[370,23470,23471,23472,23475],{},"J'ai également du retravailler les emplacements de fichiers qui étaient préfixés dans Pelican\nde ",[184,23473,23474],{},"|static|\u002Fpublic\u002F...",". Dans Gridsome, il n'y pas de syntaxe particulière, juste le nom du fichier absolu.",[12,23477,23478],{},"Avant j'avais:",[2105,23480,23483],{"className":23481,"code":23482,"language":1764,"meta":1646,"style":1646},"language-md shiki shiki-themes one-dark-pro","Title: Debian Lenny est sortie\nTags: debian, kde\n\n![Logo](|static|\u002Fpublic\u002FLogiciels\u002Fdebian-lenny-est-sortie\u002Fdebian-logo.png)\n",[184,23484,23485,23490,23495,23499],{"__ignoreMap":1646},[2113,23486,23487],{"class":2115,"line":2116},[2113,23488,23489],{},"Title: Debian Lenny est sortie\n",[2113,23491,23492],{"class":2115,"line":1647},[2113,23493,23494],{},"Tags: debian, kde\n",[2113,23496,23497],{"class":2115,"line":1652},[2113,23498,2125],{"emptyLinePlaceholder":1767},[2113,23500,23501],{"class":2115,"line":2185},[2113,23502,23503],{},"![Logo](|static|\u002Fpublic\u002FLogiciels\u002Fdebian-lenny-est-sortie\u002Fdebian-logo.png)\n",[12,23505,23506],{},"et j'ai corrigé cela en",[2105,23508,23510],{"className":23481,"code":23509,"language":1764,"meta":1646,"style":1646},"---\ntitle: Debian Lenny est sortie\ndate: 2009-02-15\ntags:\n  - debian\n  - kde\npublished: True\ncategory: Logiciels\n---\n\n![Logo](\u002FLogiciels\u002Fdebian-lenny-est-sortie\u002Fdebian-logo.png)\n",[184,23511,23512,23517,23522,23527,23532,23537,23542,23547,23552,23556,23560],{"__ignoreMap":1646},[2113,23513,23514],{"class":2115,"line":2116},[2113,23515,23516],{},"---\n",[2113,23518,23519],{"class":2115,"line":1647},[2113,23520,23521],{},"title: Debian Lenny est sortie\n",[2113,23523,23524],{"class":2115,"line":1652},[2113,23525,23526],{},"date: 2009-02-15\n",[2113,23528,23529],{"class":2115,"line":2185},[2113,23530,23531],{},"tags:\n",[2113,23533,23534],{"class":2115,"line":2230},[2113,23535,23536],{},"  - debian\n",[2113,23538,23539],{"class":2115,"line":2235},[2113,23540,23541],{},"  - kde\n",[2113,23543,23544],{"class":2115,"line":2241},[2113,23545,23546],{},"published: True\n",[2113,23548,23549],{"class":2115,"line":2246},[2113,23550,23551],{},"category: Logiciels\n",[2113,23553,23554],{"class":2115,"line":2464},[2113,23555,23516],{},[2113,23557,23558],{"class":2115,"line":2085},[2113,23559,2125],{"emptyLinePlaceholder":1767},[2113,23561,23562],{"class":2115,"line":2514},[2113,23563,23564],{},"![Logo](\u002FLogiciels\u002Fdebian-lenny-est-sortie\u002Fdebian-logo.png)\n",[12,23566,23567],{},"Afin de pouvoir reprendre mes fichiers au format markdown sans trop de modification, j'ai également installé les\nplugins remark suivants (ce sur quoi Gridsome se base pour transformer le Markdown en HTML) :",[506,23569,23570,23576,23610,23625,23631],{},[370,23571,23572,23575],{},[184,23573,23574],{},"@gridsome\u002Fremark-prismjs",": pour effectuer une coloration syntaxique.",[370,23577,23578,23581,23582],{},[184,23579,23580],{},"remark-inline-links",": pour pouvoir gérer les liens inclus (qui sont ensuite reportés en bas du fichier)",[2105,23583,23585],{"className":23481,"code":23584,"language":1764,"meta":1646,"style":1646},"[foo], [foo][], [bar][foo].\n\n![foo], ![foo][], ![bar][foo].\n\n[foo]: http:\u002F\u002Fexample.com \"Example Domain\"\n",[184,23586,23587,23592,23596,23601,23605],{"__ignoreMap":1646},[2113,23588,23589],{"class":2115,"line":2116},[2113,23590,23591],{},"[foo], [foo][], [bar][foo].\n",[2113,23593,23594],{"class":2115,"line":1647},[2113,23595,2125],{"emptyLinePlaceholder":1767},[2113,23597,23598],{"class":2115,"line":1652},[2113,23599,23600],{},"![foo], ![foo][], ![bar][foo].\n",[2113,23602,23603],{"class":2115,"line":2185},[2113,23604,2125],{"emptyLinePlaceholder":1767},[2113,23606,23607],{"class":2115,"line":2230},[2113,23608,23609],{},"[foo]: http:\u002F\u002Fexample.com \"Example Domain\"\n",[370,23611,23612,23615,23616],{},[184,23613,23614],{},"remark-attr",": pour pouvoir ajouter des attributs à une image ou à un objet.",[2105,23617,23619],{"className":23481,"code":23618,"language":1764,"meta":1646,"style":1646},"![alt](img){attrs} \u002F ![alt](img){ height=50 }\n",[184,23620,23621],{"__ignoreMap":1646},[2113,23622,23623],{"class":2115,"line":2116},[2113,23624,23618],{},[370,23626,23627,23630],{},[184,23628,23629],{},"remark-toc",": pour pouvoir ajouter un sommaire à certain billet.",[370,23632,23633,23636],{},[184,23634,23635],{},"remark-footnotes",": pour pouvoir ajouter les footnotes.",[128,23638,23640],{"id":23639},"création-du-thème","Création du thème",[12,23642,23643,23644,23647],{},"Le thème Pelican de mon blog est basé sur ",[49,23645,23336],{"href":23334,"rel":23646},[347],".\nJ'ai donc créé un thème gridsome en reprenant le style du thème précédent mais en convertissant les templates\nen Vue.Js.",[12,23649,23650,23651,179],{},"J'ai donc créé ce thème que j'ai mis à disposition ici : ",[49,23652,23655],{"href":23653,"rel":23654},"https:\u002F\u002Fgithub.com\u002Fphoenix741\u002Fgridsome-flex-markdown-starter",[347],"phoenix741\u002Fgridsome-flex-markdown-starter",[12,23657,23658],{},"Voici la configuration de gridsome que j'ai implémenté pour reconstruire le thème du blog:",[2105,23660,23662],{"className":7776,"code":23661,"language":7778,"meta":1646,"style":1646},"  plugins: [\n    {\n      use: \"@gridsome\u002Fsource-filesystem\",\n      options: {\n        baseDir: \"content\u002Fposts\",\n        path: \"**\u002F*.md\",\n        typeName: \"Post\",\n        refs: {\n          tags: {\n            typeName: \"Tag\",\n            create: true,\n          },\n          category: {\n            typeName: \"Category\",\n            create: true,\n          },\n        },\n      },\n    },\n",[184,23663,23664,23669,23673,23678,23683,23688,23693,23698,23703,23708,23713,23718,23723,23728,23733,23737,23741,23745,23749],{"__ignoreMap":1646},[2113,23665,23666],{"class":2115,"line":2116},[2113,23667,23668],{},"  plugins: [\n",[2113,23670,23671],{"class":2115,"line":1647},[2113,23672,18224],{},[2113,23674,23675],{"class":2115,"line":1652},[2113,23676,23677],{},"      use: \"@gridsome\u002Fsource-filesystem\",\n",[2113,23679,23680],{"class":2115,"line":2185},[2113,23681,23682],{},"      options: {\n",[2113,23684,23685],{"class":2115,"line":2230},[2113,23686,23687],{},"        baseDir: \"content\u002Fposts\",\n",[2113,23689,23690],{"class":2115,"line":2235},[2113,23691,23692],{},"        path: \"**\u002F*.md\",\n",[2113,23694,23695],{"class":2115,"line":2241},[2113,23696,23697],{},"        typeName: \"Post\",\n",[2113,23699,23700],{"class":2115,"line":2246},[2113,23701,23702],{},"        refs: {\n",[2113,23704,23705],{"class":2115,"line":2464},[2113,23706,23707],{},"          tags: {\n",[2113,23709,23710],{"class":2115,"line":2085},[2113,23711,23712],{},"            typeName: \"Tag\",\n",[2113,23714,23715],{"class":2115,"line":2514},[2113,23716,23717],{},"            create: true,\n",[2113,23719,23720],{"class":2115,"line":2533},[2113,23721,23722],{},"          },\n",[2113,23724,23725],{"class":2115,"line":2556},[2113,23726,23727],{},"          category: {\n",[2113,23729,23730],{"class":2115,"line":2569},[2113,23731,23732],{},"            typeName: \"Category\",\n",[2113,23734,23735],{"class":2115,"line":2575},[2113,23736,23717],{},[2113,23738,23739],{"class":2115,"line":2596},[2113,23740,23722],{},[2113,23742,23743],{"class":2115,"line":3192},[2113,23744,12744],{},[2113,23746,23747],{"class":2115,"line":3213},[2113,23748,12282],{},[2113,23750,23751],{"class":2115,"line":3236},[2113,23752,8237],{},[12,23754,23755,23756,23759,23760,23763,23764,23767,23768,179],{},"Pour commencer, on utilise le module ",[184,23757,23758],{},"@gridsome\u002Fsource-filesystem"," pour lire le contenu du dossier\n",[184,23761,23762],{},"content\u002Fposts\u002F**\u002F*.md",". On définit au plugin comment on créé les ",[184,23765,23766],{},"Tag"," et les ",[184,23769,23770],{},"Category",[2105,23772,23774],{"className":7776,"code":23773,"language":7778,"meta":1646,"style":1646},"    {\n      use: \"@microflash\u002Fgridsome-plugin-feed\",\n      options: {\n        contentTypes: [\"Post\"],\n        rss: {\n          enabled: true,\n          output: \"\u002Ffeed.xml\",\n        },\n        atom: {\n          enabled: true,\n          output: \"\u002Ffeed.atom\",\n        },\n      },\n    },\n",[184,23775,23776,23780,23785,23789,23794,23799,23804,23809,23813,23818,23822,23827,23831,23835],{"__ignoreMap":1646},[2113,23777,23778],{"class":2115,"line":2116},[2113,23779,18224],{},[2113,23781,23782],{"class":2115,"line":1647},[2113,23783,23784],{},"      use: \"@microflash\u002Fgridsome-plugin-feed\",\n",[2113,23786,23787],{"class":2115,"line":1652},[2113,23788,23682],{},[2113,23790,23791],{"class":2115,"line":2185},[2113,23792,23793],{},"        contentTypes: [\"Post\"],\n",[2113,23795,23796],{"class":2115,"line":2230},[2113,23797,23798],{},"        rss: {\n",[2113,23800,23801],{"class":2115,"line":2235},[2113,23802,23803],{},"          enabled: true,\n",[2113,23805,23806],{"class":2115,"line":2241},[2113,23807,23808],{},"          output: \"\u002Ffeed.xml\",\n",[2113,23810,23811],{"class":2115,"line":2246},[2113,23812,12744],{},[2113,23814,23815],{"class":2115,"line":2464},[2113,23816,23817],{},"        atom: {\n",[2113,23819,23820],{"class":2115,"line":2085},[2113,23821,23803],{},[2113,23823,23824],{"class":2115,"line":2514},[2113,23825,23826],{},"          output: \"\u002Ffeed.atom\",\n",[2113,23828,23829],{"class":2115,"line":2533},[2113,23830,12744],{},[2113,23832,23833],{"class":2115,"line":2556},[2113,23834,12282],{},[2113,23836,23837],{"class":2115,"line":2569},[2113,23838,8237],{},[12,23840,23841],{},"Ensuite on crée un flux RSS et un flux ATOM avec ces posts, afin que les lecteurs qui utilisent\nencore un lecteur de flux, ou les programmes qui s'abonnent directement à mon site fonctionnent encore.",[2105,23843,23845],{"className":7776,"code":23844,"language":7778,"meta":1646,"style":1646},"    {\n      use: \"@gridsome\u002Fsource-filesystem\",\n      options: {\n        baseDir: \"content\u002Fpages\",\n        path: \"*.md\",\n        typeName: \"BlogPage\",\n      },\n    },\n",[184,23846,23847,23851,23855,23859,23864,23869,23874,23878],{"__ignoreMap":1646},[2113,23848,23849],{"class":2115,"line":2116},[2113,23850,18224],{},[2113,23852,23853],{"class":2115,"line":1647},[2113,23854,23677],{},[2113,23856,23857],{"class":2115,"line":1652},[2113,23858,23682],{},[2113,23860,23861],{"class":2115,"line":2185},[2113,23862,23863],{},"        baseDir: \"content\u002Fpages\",\n",[2113,23865,23866],{"class":2115,"line":2230},[2113,23867,23868],{},"        path: \"*.md\",\n",[2113,23870,23871],{"class":2115,"line":2235},[2113,23872,23873],{},"        typeName: \"BlogPage\",\n",[2113,23875,23876],{"class":2115,"line":2241},[2113,23877,12282],{},[2113,23879,23880],{"class":2115,"line":2246},[2113,23881,8237],{},[12,23883,23884,23885,23888],{},"On recommence avec les pages du site, que l'on type comme ",[184,23886,23887],{},"BlogPage",". Ces pages ne seront\npas accessibles via des catégories ou des pages. Il faudra faire des liens directement vers les\npages depuis un menu.",[2105,23890,23892],{"className":7776,"code":23891,"language":7778,"meta":1646,"style":1646},"    {\n      use: \"@gridsome\u002Fplugin-sitemap\",\n      options: {\n        config: {\n          \"\u002Fpost\u002F*\": {\n            changefreq: \"weekly\",\n            priority: 0.5,\n          },\n          \"\u002Fpage\u002F*\": {\n            changefreq: \"monthly\",\n            priority: 0.7,\n          },\n        },\n      },\n    },\n",[184,23893,23894,23898,23903,23907,23912,23917,23922,23927,23931,23936,23941,23946,23950,23954,23958],{"__ignoreMap":1646},[2113,23895,23896],{"class":2115,"line":2116},[2113,23897,18224],{},[2113,23899,23900],{"class":2115,"line":1647},[2113,23901,23902],{},"      use: \"@gridsome\u002Fplugin-sitemap\",\n",[2113,23904,23905],{"class":2115,"line":1652},[2113,23906,23682],{},[2113,23908,23909],{"class":2115,"line":2185},[2113,23910,23911],{},"        config: {\n",[2113,23913,23914],{"class":2115,"line":2230},[2113,23915,23916],{},"          \"\u002Fpost\u002F*\": {\n",[2113,23918,23919],{"class":2115,"line":2235},[2113,23920,23921],{},"            changefreq: \"weekly\",\n",[2113,23923,23924],{"class":2115,"line":2241},[2113,23925,23926],{},"            priority: 0.5,\n",[2113,23928,23929],{"class":2115,"line":2246},[2113,23930,23722],{},[2113,23932,23933],{"class":2115,"line":2464},[2113,23934,23935],{},"          \"\u002Fpage\u002F*\": {\n",[2113,23937,23938],{"class":2115,"line":2085},[2113,23939,23940],{},"            changefreq: \"monthly\",\n",[2113,23942,23943],{"class":2115,"line":2514},[2113,23944,23945],{},"            priority: 0.7,\n",[2113,23947,23948],{"class":2115,"line":2533},[2113,23949,23722],{},[2113,23951,23952],{"class":2115,"line":2556},[2113,23953,12744],{},[2113,23955,23956],{"class":2115,"line":2569},[2113,23957,12282],{},[2113,23959,23960],{"class":2115,"line":2575},[2113,23961,8237],{},[12,23963,23964],{},"Après, on définit un sitemap qui pourra être utilisé par les moteurs de recherches.",[2105,23966,23968],{"className":7776,"code":23967,"language":7778,"meta":1646,"style":1646},"  ],\n  templates: {\n    Post: (obj) => {\n      return (\n        \"\u002Fpost\u002F\" + obj.fileInfo.name.replace(\u002F^\\d{4}-\\d{2}-\\d{2}[_\\-]\u002F, \"\")\n      );\n    },\n    BlogPage: \"\u002Fpages\u002F:fileInfo__name\",\n    Tag: \"\u002Ftag\u002F:id\",\n    Category: \"\u002Fcategory\u002F:title\",\n  },\n",[184,23969,23970,23975,23980,23985,23990,23995,24000,24004,24009,24014,24019],{"__ignoreMap":1646},[2113,23971,23972],{"class":2115,"line":2116},[2113,23973,23974],{},"  ],\n",[2113,23976,23977],{"class":2115,"line":1647},[2113,23978,23979],{},"  templates: {\n",[2113,23981,23982],{"class":2115,"line":1652},[2113,23983,23984],{},"    Post: (obj) => {\n",[2113,23986,23987],{"class":2115,"line":2185},[2113,23988,23989],{},"      return (\n",[2113,23991,23992],{"class":2115,"line":2230},[2113,23993,23994],{},"        \"\u002Fpost\u002F\" + obj.fileInfo.name.replace(\u002F^\\d{4}-\\d{2}-\\d{2}[_\\-]\u002F, \"\")\n",[2113,23996,23997],{"class":2115,"line":2235},[2113,23998,23999],{},"      );\n",[2113,24001,24002],{"class":2115,"line":2241},[2113,24003,8237],{},[2113,24005,24006],{"class":2115,"line":2246},[2113,24007,24008],{},"    BlogPage: \"\u002Fpages\u002F:fileInfo__name\",\n",[2113,24010,24011],{"class":2115,"line":2464},[2113,24012,24013],{},"    Tag: \"\u002Ftag\u002F:id\",\n",[2113,24015,24016],{"class":2115,"line":2085},[2113,24017,24018],{},"    Category: \"\u002Fcategory\u002F:title\",\n",[2113,24020,24021],{"class":2115,"line":2514},[2113,24022,8490],{},[12,24024,24025,24026,24028,24029,24031],{},"On définit les différentes chemins d'accès pour nos billets, nos pages, nos ",[184,24027,23766],{},",\net nos ",[184,24030,23770],{},". Comme je ne souhaite pas changer les adresses de mes billets sur mon site,\nj'ai défini les adresses sur les mêmes chemins que mon blog sous Pelican.",[12,24033,24034],{},"Comme les noms des fichiers des billets portent la date et le nom du billet, pour retrouver\nle nom du chemin d'origine, je supprime la date pour la génération du chemin.",[2105,24036,24038],{"className":7776,"code":24037,"language":7778,"meta":1646,"style":1646},"  transformers: {\n    remark: {\n      plugins: [\n        [\"@gridsome\u002Fremark-prismjs\", { showLineNumbers: true }],\n        \"remark-inline-links\",\n        [\"remark-toc\", { heading: \"sommaire\" }],\n        \"remark-attr\",\n      ],\n      config: {\n        footnotes: true,\n      },\n    },\n  },\n",[184,24039,24040,24045,24050,24055,24060,24065,24070,24075,24080,24085,24090,24094,24098],{"__ignoreMap":1646},[2113,24041,24042],{"class":2115,"line":2116},[2113,24043,24044],{},"  transformers: {\n",[2113,24046,24047],{"class":2115,"line":1647},[2113,24048,24049],{},"    remark: {\n",[2113,24051,24052],{"class":2115,"line":1652},[2113,24053,24054],{},"      plugins: [\n",[2113,24056,24057],{"class":2115,"line":2185},[2113,24058,24059],{},"        [\"@gridsome\u002Fremark-prismjs\", { showLineNumbers: true }],\n",[2113,24061,24062],{"class":2115,"line":2230},[2113,24063,24064],{},"        \"remark-inline-links\",\n",[2113,24066,24067],{"class":2115,"line":2235},[2113,24068,24069],{},"        [\"remark-toc\", { heading: \"sommaire\" }],\n",[2113,24071,24072],{"class":2115,"line":2241},[2113,24073,24074],{},"        \"remark-attr\",\n",[2113,24076,24077],{"class":2115,"line":2246},[2113,24078,24079],{},"      ],\n",[2113,24081,24082],{"class":2115,"line":2464},[2113,24083,24084],{},"      config: {\n",[2113,24086,24087],{"class":2115,"line":2085},[2113,24088,24089],{},"        footnotes: true,\n",[2113,24091,24092],{"class":2115,"line":2514},[2113,24093,12282],{},[2113,24095,24096],{"class":2115,"line":2533},[2113,24097,8237],{},[2113,24099,24100],{"class":2115,"line":2556},[2113,24101,8490],{},[12,24103,24104],{},"Enfin, je définis les différents plugins dont j'ai besoin pour que mes différents billets\nfonctionnent correctement.",[2105,24106,24108],{"className":7776,"code":24107,"language":7778,"meta":1646,"style":1646},"  permalinks: {\n    trailingSlash: \"false\",\n    slugify: {\n      use: \"@sindresorhus\u002Fslugify\",\n      options: {\n        decamelize: false,\n      },\n    },\n  },\n  css: {\n    loaderOptions: {\n      less: {\n        \u002F\u002F options here will be passed to less-loader\n      },\n    },\n  },\n};\n",[184,24109,24110,24115,24120,24125,24130,24134,24139,24143,24147,24151,24156,24161,24166,24171,24175,24179,24183],{"__ignoreMap":1646},[2113,24111,24112],{"class":2115,"line":2116},[2113,24113,24114],{},"  permalinks: {\n",[2113,24116,24117],{"class":2115,"line":1647},[2113,24118,24119],{},"    trailingSlash: \"false\",\n",[2113,24121,24122],{"class":2115,"line":1652},[2113,24123,24124],{},"    slugify: {\n",[2113,24126,24127],{"class":2115,"line":2185},[2113,24128,24129],{},"      use: \"@sindresorhus\u002Fslugify\",\n",[2113,24131,24132],{"class":2115,"line":2230},[2113,24133,23682],{},[2113,24135,24136],{"class":2115,"line":2235},[2113,24137,24138],{},"        decamelize: false,\n",[2113,24140,24141],{"class":2115,"line":2241},[2113,24142,12282],{},[2113,24144,24145],{"class":2115,"line":2246},[2113,24146,8237],{},[2113,24148,24149],{"class":2115,"line":2464},[2113,24150,8490],{},[2113,24152,24153],{"class":2115,"line":2085},[2113,24154,24155],{},"  css: {\n",[2113,24157,24158],{"class":2115,"line":2514},[2113,24159,24160],{},"    loaderOptions: {\n",[2113,24162,24163],{"class":2115,"line":2533},[2113,24164,24165],{},"      less: {\n",[2113,24167,24168],{"class":2115,"line":2556},[2113,24169,24170],{},"        \u002F\u002F options here will be passed to less-loader\n",[2113,24172,24173],{"class":2115,"line":2569},[2113,24174,12282],{},[2113,24176,24177],{"class":2115,"line":2575},[2113,24178,8237],{},[2113,24180,24181],{"class":2115,"line":2596},[2113,24182,8490],{},[2113,24184,24185],{"class":2115,"line":3192},[2113,24186,2787],{},[12,24188,24189],{},"Je termine par modifier la manière dont Gridsome transforme les adresses, toujours pour\nque les chemins de mon blog ne changent pas.",[128,24191,24193],{"id":24192},"développement-des-pages","Développement des pages",[12,24195,24196],{},"Viens ensuite l'écriture des différentes pages, composants et layout pour définir notre thème.\nLe plus compliqué, c'est que le résumé (excerpt) fourni par Gridsome supprime le rendu HTML et\nles retours chariots.",[12,24198,24199],{},"Je voulais que sur la page principale de mon application, je puisse générer un résumé de chaque\nbillet avec le texte et le rendu original du billet.",[12,24201,24202],{},"Pour cela malheureusement Gridsome ne fournit pas une manière simple de générer un résumé. Je vais\nvous présenter mon implémentation afin d'avoir un résumé au format Markdown.",[12,24204,24205],{},"Ma première version a été de créer à partir de contenu HTML le résumé dans un composant vue dédié.\nLors de la génération des pages statiques, le résultat était comme attendu. Par contre lors du\nchangement des pages (en mode SPA), le billet intégral du blog est chargé, pour être tronqué dynamiquement. Ce\nqui est long et pas performant quand cela arrive sur 10 billets à la suite. J'ai donc modifié le système pour\ngénérer les pages côté serveur.",[12,24207,24208,24209,24212,24213,3938],{},"Pour cela j'ai donc modifié le fichier ",[184,24210,24211],{},"gridsome.server.js",". Le problème, c'est que Gridsome ne permet\npas d'appeler un transformer (remark) directement, et ne permet pas de convertir un text en markdown (autrement\nqu'en passant par le plugin ",[184,24214,23758],{},[12,24216,24217],{},"J'ai donc dû implémenter l'appel à remark (et les différents plugins) directement dans la partie\nserveur manuellement.",[12,24219,24220,24221,24224],{},"Une fois le fichier HTML obtenu, j'appelle une librairie dédiée ",[184,24222,24223],{},"truncatise"," afin de réduire le contenu\nà quelques paragraphes (4).",[2105,24226,24228],{"className":7776,"code":24227,"language":7778,"meta":1646,"style":1646},"const truncatise = require(\"truncatise\");\n\nconst remark = require(\"remark\");\nconst html = require(\"remark-html\");\n\nconst remarkPrism = require(\"@gridsome\u002Fremark-prismjs\");\nconst remarkInlineLinks = require(\"remark-inline-links\");\nconst remarkToc = require(\"remark-toc\");\nconst remarkAttr = require(\"remark-attr\");\nconst remarkFN = require(\"remark-footnotes\");\n\nmodule.exports = function(api) {\n  api.loadSource(({ addSchemaResolvers }) => {\n    addSchemaResolvers({\n      Post: {\n        excerpt: {\n          type: \"String\",\n          async resolve(obj) {\n            return new Promise((resolve, reject) => {\n              remark()\n                \u002F\u002F Appel des différents plugins utilisé coté config.\n                .use(remarkPrism)\n                .use(remarkInlineLinks)\n                .use(remarkToc, { heading: \"sommaire\" })\n                .use(remarkAttr)\n                .use(remarkFN)\n                \u002F\u002F Génération HTML\n                .use(html)\n                .process(obj.content, function(err, file) {\n                  if (err) {\n                    return reject(err);\n                  }\n\n                  \u002F\u002F On tronque en ne gardant que les 4 premiers paragraphes\n                  resolve(\n                    truncatise(String(file), {\n                      TruncateLength: 4,\n                      TruncateBy: \"paragraphs\",\n                      Strict: false,\n                      StripHTML: false,\n                      Suffix: \" ...\",\n                    })\n                  );\n                });\n            });\n          },\n        },\n      },\n    });\n  });\n};\n",[184,24229,24230,24235,24239,24244,24249,24253,24258,24263,24268,24273,24278,24282,24287,24292,24297,24302,24307,24312,24317,24322,24327,24332,24337,24342,24347,24352,24357,24362,24367,24372,24377,24382,24387,24391,24396,24401,24406,24411,24416,24421,24426,24431,24436,24441,24446,24450,24454,24458,24462,24466,24471],{"__ignoreMap":1646},[2113,24231,24232],{"class":2115,"line":2116},[2113,24233,24234],{},"const truncatise = require(\"truncatise\");\n",[2113,24236,24237],{"class":2115,"line":1647},[2113,24238,2125],{"emptyLinePlaceholder":1767},[2113,24240,24241],{"class":2115,"line":1652},[2113,24242,24243],{},"const remark = require(\"remark\");\n",[2113,24245,24246],{"class":2115,"line":2185},[2113,24247,24248],{},"const html = require(\"remark-html\");\n",[2113,24250,24251],{"class":2115,"line":2230},[2113,24252,2125],{"emptyLinePlaceholder":1767},[2113,24254,24255],{"class":2115,"line":2235},[2113,24256,24257],{},"const remarkPrism = require(\"@gridsome\u002Fremark-prismjs\");\n",[2113,24259,24260],{"class":2115,"line":2241},[2113,24261,24262],{},"const remarkInlineLinks = require(\"remark-inline-links\");\n",[2113,24264,24265],{"class":2115,"line":2246},[2113,24266,24267],{},"const remarkToc = require(\"remark-toc\");\n",[2113,24269,24270],{"class":2115,"line":2464},[2113,24271,24272],{},"const remarkAttr = require(\"remark-attr\");\n",[2113,24274,24275],{"class":2115,"line":2085},[2113,24276,24277],{},"const remarkFN = require(\"remark-footnotes\");\n",[2113,24279,24280],{"class":2115,"line":2514},[2113,24281,2125],{"emptyLinePlaceholder":1767},[2113,24283,24284],{"class":2115,"line":2533},[2113,24285,24286],{},"module.exports = function(api) {\n",[2113,24288,24289],{"class":2115,"line":2556},[2113,24290,24291],{},"  api.loadSource(({ addSchemaResolvers }) => {\n",[2113,24293,24294],{"class":2115,"line":2569},[2113,24295,24296],{},"    addSchemaResolvers({\n",[2113,24298,24299],{"class":2115,"line":2575},[2113,24300,24301],{},"      Post: {\n",[2113,24303,24304],{"class":2115,"line":2596},[2113,24305,24306],{},"        excerpt: {\n",[2113,24308,24309],{"class":2115,"line":3192},[2113,24310,24311],{},"          type: \"String\",\n",[2113,24313,24314],{"class":2115,"line":3213},[2113,24315,24316],{},"          async resolve(obj) {\n",[2113,24318,24319],{"class":2115,"line":3236},[2113,24320,24321],{},"            return new Promise((resolve, reject) => {\n",[2113,24323,24324],{"class":2115,"line":3248},[2113,24325,24326],{},"              remark()\n",[2113,24328,24329],{"class":2115,"line":4899},[2113,24330,24331],{},"                \u002F\u002F Appel des différents plugins utilisé coté config.\n",[2113,24333,24334],{"class":2115,"line":1777},[2113,24335,24336],{},"                .use(remarkPrism)\n",[2113,24338,24339],{"class":2115,"line":4931},[2113,24340,24341],{},"                .use(remarkInlineLinks)\n",[2113,24343,24344],{"class":2115,"line":4961},[2113,24345,24346],{},"                .use(remarkToc, { heading: \"sommaire\" })\n",[2113,24348,24349],{"class":2115,"line":4976},[2113,24350,24351],{},"                .use(remarkAttr)\n",[2113,24353,24354],{"class":2115,"line":4993},[2113,24355,24356],{},"                .use(remarkFN)\n",[2113,24358,24359],{"class":2115,"line":5013},[2113,24360,24361],{},"                \u002F\u002F Génération HTML\n",[2113,24363,24364],{"class":2115,"line":5018},[2113,24365,24366],{},"                .use(html)\n",[2113,24368,24369],{"class":2115,"line":5042},[2113,24370,24371],{},"                .process(obj.content, function(err, file) {\n",[2113,24373,24374],{"class":2115,"line":5057},[2113,24375,24376],{},"                  if (err) {\n",[2113,24378,24379],{"class":2115,"line":5062},[2113,24380,24381],{},"                    return reject(err);\n",[2113,24383,24384],{"class":2115,"line":5098},[2113,24385,24386],{},"                  }\n",[2113,24388,24389],{"class":2115,"line":5117},[2113,24390,2125],{"emptyLinePlaceholder":1767},[2113,24392,24393],{"class":2115,"line":5142},[2113,24394,24395],{},"                  \u002F\u002F On tronque en ne gardant que les 4 premiers paragraphes\n",[2113,24397,24398],{"class":2115,"line":5148},[2113,24399,24400],{},"                  resolve(\n",[2113,24402,24403],{"class":2115,"line":5163},[2113,24404,24405],{},"                    truncatise(String(file), {\n",[2113,24407,24408],{"class":2115,"line":5169},[2113,24409,24410],{},"                      TruncateLength: 4,\n",[2113,24412,24413],{"class":2115,"line":5175},[2113,24414,24415],{},"                      TruncateBy: \"paragraphs\",\n",[2113,24417,24418],{"class":2115,"line":5180},[2113,24419,24420],{},"                      Strict: false,\n",[2113,24422,24423],{"class":2115,"line":5199},[2113,24424,24425],{},"                      StripHTML: false,\n",[2113,24427,24428],{"class":2115,"line":5204},[2113,24429,24430],{},"                      Suffix: \" ...\",\n",[2113,24432,24433],{"class":2115,"line":5209},[2113,24434,24435],{},"                    })\n",[2113,24437,24438],{"class":2115,"line":5232},[2113,24439,24440],{},"                  );\n",[2113,24442,24443],{"class":2115,"line":5237},[2113,24444,24445],{},"                });\n",[2113,24447,24448],{"class":2115,"line":5242},[2113,24449,14698],{},[2113,24451,24452],{"class":2115,"line":5267},[2113,24453,23722],{},[2113,24455,24456],{"class":2115,"line":5282},[2113,24457,12744],{},[2113,24459,24460],{"class":2115,"line":5295},[2113,24461,12282],{},[2113,24463,24464],{"class":2115,"line":5310},[2113,24465,19378],{},[2113,24467,24468],{"class":2115,"line":5315},[2113,24469,24470],{},"  });\n",[2113,24472,24473],{"class":2115,"line":5320},[2113,24474,2787],{},[12,24476,24477,24478,24480,24481,3938],{},"C'est ainsi que j'ai converti mon blog vers le framework ",[184,24479,23342],{},". Je vous passe tous les\ndétails (vu que le thème est disponible sur Github sur ",[49,24482,23655],{"href":23653,"rel":24483},[347],[128,24485,24487],{"id":24486},"ajout-dun-système-de-commentaire","Ajout d'un système de commentaire",[12,24489,24490],{},"Ce qu'il manquait sur mon blog précédent c'était un système de commentaires. J'ai donc ajouté\nce système de commentaires à mon blog.",[12,24492,24493],{},"Je ne souhaitais pas ajouter n'importe quel système de commentaires. J'ai donc regardé\nce que je pouvais ajouter sur un site statique. On va retrouver les systèmes de commentaires\nnon libres, et faciles à implémenter (et pas forcément très compatible RGPD).",[506,24495,24496,24499,24502],{},[370,24497,24498],{},"Disqus",[370,24500,24501],{},"Facebook comments",[370,24503,3699],{},[12,24505,24506],{},"De mon coté je me suis dirigé vers les systèmes de commentaires libres:",[506,24508,24509,24516,24523,24530],{},[370,24510,24511],{},[49,24512,24515],{"href":24513,"rel":24514},"https:\u002F\u002Fvssue.js.org\u002F",[347],"Vssue",[370,24517,24518],{},[49,24519,24522],{"href":24520,"rel":24521},"https:\u002F\u002Fgithub.com\u002Fimsun\u002Fgitment",[347],"Gitment",[370,24524,24525],{},[49,24526,24529],{"href":24527,"rel":24528},"https:\u002F\u002Futteranc.es\u002F",[347],"Utterances",[370,24531,24532],{},[49,24533,24536],{"href":24534,"rel":24535},"https:\u002F\u002Fgithub.com\u002Fumputun\u002Fremark42",[347],"Remark42",[12,24538,24539],{},"Remark42 nécessite l'installation d'un programme sur mon serveur. Je l'ai testé mais j'ai été à quelques\nproblème lors de l'intégration avec Gridsome.",[12,24541,24542],{},"Vssue, Gitment et Utterances sont intéressants car ils permettent d'ajouter un système de commentaires\nse basant sur le système de ticketing de Github. Github n'est en lui même pas OpenSource mais les frameworks\nci-dessus le sont.",[12,24544,24545],{},"Je considère que la plupart des personnes lisant mon blog ont un compte Github. Mais n'hésitez pas à commenter\nsi ce n'est pas le cas ;).",[12,24547,24548,24549,179],{},"Vssue était mon système préféré, beau, simple compatible avec Gitea (que j'utilise pour mes sources), Gitlab,\net avec Github également. Mais dû aux limitations de Github et Gitea, il m'était nécessaire de fournir mon\n",[49,24550,24553],{"href":24551,"rel":24552},"https:\u002F\u002Fvssue.js.org\u002Fguide\u002Fgithub.html",[347],"ClientSecret OAuth2",[12,24555,24556,24557,24562],{},"Je n'ai pas apprécié cette idée mais j'ai tout de même voulu tester. Là je me suis rendu compte qu'il\ny avait des problèmes CORS. Le composant passe par ",[49,24558,24561],{"href":24559,"rel":24560},"https:\u002F\u002Fgithub.com\u002FRob--W\u002Fcors-anywhere",[347],"CORS Anywhere"," mais\nce dernier ne peut être utilisé que pour le développement. Ce qui signifie que je dois installer un proxy équivalent\nsur mon serveur. J'ai donc regardé pour autre chose.",[12,24564,24565,24566,24571],{},"J'ai alors testé Utterances. Le problème est que cette librairie n'est pas compatible avec les\napplications de type SPA. On peut voir d'ailleurs quelques commentaires sur le ticket ",[49,24567,24570],{"href":24568,"rel":24569},"https:\u002F\u002Fgithub.com\u002Futterance\u002Futterances\u002Fissues\u002F231",[347],"Github #231","\nde personnes ayant tenté de faire une version du client pour Vue.Js.",[12,24573,24574,24575,3938],{},"Je me suis donc inspiré d'un composant Vue.JS écrit par quelqu'un d'autre (dans le 1er commentaire) pour inclure\nce système sur mon site.\nCe composant a dû être légèrement adapté pour fonctionner (je vous invite à regarder\n",[49,24576,24579],{"href":24577,"rel":24578},"https:\u002F\u002Fgithub.com\u002Fphoenix741\u002Fgridsome-flex-markdown-starter\u002Fblob\u002Fmain\u002Fsrc\u002Fcomponents\u002FGithubComponent.vue",[347],"GithubComponent.vue",[12,24581,24582],{},"Comme le plugin utilise directement l'objet window, il ne peut fonctionner avec le mode SSR (Server Side Rendering) de\nVue.Js. J'ai dû donc désactiver le mode commentaires pour le côté SSR (la génération). Ce n'est pas déconnant car\nde toutes façon les commentaires sont gérés sur Github.",[12,24584,24585,24586,24589,24590,179],{},"Voici comment on fait avec Gridsome. On commence par encadrer le bloc qui ne doit pas faire partie de la génération\ncôté serveur (et qui est donc ",[184,24587,24588],{},"Client Only","). Ce bloc contient notre composant ",[184,24591,24592],{},"GithubComponent",[2105,24594,24596],{"className":18156,"code":24595,"language":18158,"meta":1646,"style":1646},"\u003Carticle>\n  ...\n  \u003CClientOnly>\n    \u003Cdiv class=\"commentswrap\">\n      \u003Cdiv id=\"comments\">\n        \u003CGithubComponent\n          :title=\"$page.post.title\"\n          :repo=\"$page.metadata.utterances.repo\"\n          :pathname=\"this.$route.path\"\n          :url=\"url\"\n          :issueTerm=\"$page.metadata.utterances.issueTerm\"\n          :label=\"$page.metadata.utterances.label\"\n        >\u003C\u002FGithubComponent>\n      \u003C\u002Fdiv>\n    \u003C\u002Fdiv>\n  \u003C\u002FClientOnly>\n\u003C\u002Farticle>\n",[184,24597,24598,24607,24611,24621,24638,24654,24662,24672,24682,24692,24702,24712,24722,24731,24740,24749,24758],{"__ignoreMap":1646},[2113,24599,24600,24602,24605],{"class":2115,"line":2116},[2113,24601,3109],{"class":2119},[2113,24603,24604],{"class":2330},"article",[2113,24606,18185],{"class":2119},[2113,24608,24609],{"class":2115,"line":1647},[2113,24610,18190],{"class":2119},[2113,24612,24613,24616,24619],{"class":2115,"line":1652},[2113,24614,24615],{"class":2119},"  \u003C",[2113,24617,24618],{"class":2330},"ClientOnly",[2113,24620,18185],{"class":2119},[2113,24622,24623,24626,24629,24631,24633,24636],{"class":2115,"line":2185},[2113,24624,24625],{"class":2119},"    \u003C",[2113,24627,24628],{"class":2330},"div",[2113,24630,19179],{"class":2166},[2113,24632,2335],{"class":2119},[2113,24634,24635],{"class":2149},"\"commentswrap\"",[2113,24637,18185],{"class":2119},[2113,24639,24640,24643,24645,24647,24649,24652],{"class":2115,"line":2230},[2113,24641,24642],{"class":2119},"      \u003C",[2113,24644,24628],{"class":2330},[2113,24646,18170],{"class":2166},[2113,24648,2335],{"class":2119},[2113,24650,24651],{"class":2149},"\"comments\"",[2113,24653,18185],{"class":2119},[2113,24655,24656,24659],{"class":2115,"line":2235},[2113,24657,24658],{"class":2119},"        \u003C",[2113,24660,24661],{"class":2330},"GithubComponent\n",[2113,24663,24664,24667,24669],{"class":2115,"line":2241},[2113,24665,24666],{"class":2166},"          :title",[2113,24668,2335],{"class":2119},[2113,24670,24671],{"class":2149},"\"$page.post.title\"\n",[2113,24673,24674,24677,24679],{"class":2115,"line":2246},[2113,24675,24676],{"class":2166},"          :repo",[2113,24678,2335],{"class":2119},[2113,24680,24681],{"class":2149},"\"$page.metadata.utterances.repo\"\n",[2113,24683,24684,24687,24689],{"class":2115,"line":2464},[2113,24685,24686],{"class":2166},"          :pathname",[2113,24688,2335],{"class":2119},[2113,24690,24691],{"class":2149},"\"this.$route.path\"\n",[2113,24693,24694,24697,24699],{"class":2115,"line":2085},[2113,24695,24696],{"class":2166},"          :url",[2113,24698,2335],{"class":2119},[2113,24700,24701],{"class":2149},"\"url\"\n",[2113,24703,24704,24707,24709],{"class":2115,"line":2514},[2113,24705,24706],{"class":2166},"          :issueTerm",[2113,24708,2335],{"class":2119},[2113,24710,24711],{"class":2149},"\"$page.metadata.utterances.issueTerm\"\n",[2113,24713,24714,24717,24719],{"class":2115,"line":2533},[2113,24715,24716],{"class":2166},"          :label",[2113,24718,2335],{"class":2119},[2113,24720,24721],{"class":2149},"\"$page.metadata.utterances.label\"\n",[2113,24723,24724,24727,24729],{"class":2115,"line":2556},[2113,24725,24726],{"class":2119},"        >\u003C\u002F",[2113,24728,24592],{"class":2330},[2113,24730,18185],{"class":2119},[2113,24732,24733,24736,24738],{"class":2115,"line":2569},[2113,24734,24735],{"class":2119},"      \u003C\u002F",[2113,24737,24628],{"class":2330},[2113,24739,18185],{"class":2119},[2113,24741,24742,24745,24747],{"class":2115,"line":2575},[2113,24743,24744],{"class":2119},"    \u003C\u002F",[2113,24746,24628],{"class":2330},[2113,24748,18185],{"class":2119},[2113,24750,24751,24754,24756],{"class":2115,"line":2596},[2113,24752,24753],{"class":2119},"  \u003C\u002F",[2113,24755,24618],{"class":2330},[2113,24757,18185],{"class":2119},[2113,24759,24760,24762,24764],{"class":2115,"line":3192},[2113,24761,18195],{"class":2119},[2113,24763,24604],{"class":2330},[2113,24765,18185],{"class":2119},[12,24767,24768],{},"Enfin on charge le composant dynamiquement dans la partie Javascript.",[2105,24770,24772],{"className":7776,"code":24771,"language":7778,"meta":1646,"style":1646},"export default {\n  components: {\n    PostHeaderTitle,\n    GithubComponent: () =>\n      import(\"..\u002Fcomponents\u002FGithubComponent\")\n        .then((m) => m.default)\n        .catch(),\n  },\n  ...\n}\n",[184,24773,24774,24779,24784,24789,24794,24799,24804,24809,24813,24817],{"__ignoreMap":1646},[2113,24775,24776],{"class":2115,"line":2116},[2113,24777,24778],{},"export default {\n",[2113,24780,24781],{"class":2115,"line":1647},[2113,24782,24783],{},"  components: {\n",[2113,24785,24786],{"class":2115,"line":1652},[2113,24787,24788],{},"    PostHeaderTitle,\n",[2113,24790,24791],{"class":2115,"line":2185},[2113,24792,24793],{},"    GithubComponent: () =>\n",[2113,24795,24796],{"class":2115,"line":2230},[2113,24797,24798],{},"      import(\"..\u002Fcomponents\u002FGithubComponent\")\n",[2113,24800,24801],{"class":2115,"line":2235},[2113,24802,24803],{},"        .then((m) => m.default)\n",[2113,24805,24806],{"class":2115,"line":2241},[2113,24807,24808],{},"        .catch(),\n",[2113,24810,24811],{"class":2115,"line":2246},[2113,24812,8490],{},[2113,24814,24815],{"class":2115,"line":2464},[2113,24816,18190],{},[2113,24818,24819],{"class":2115,"line":2085},[2113,24820,2599],{},[12,24822,24823],{},"Ces deux opérations permettent d'exclure cette partie lors de la génération des pages statique, mais de\nl'ajouter dynamiquement après le chargement de la page coté client.",[128,24825,1621],{"id":1620},[12,24827,24828],{},"Voici donc comment j'ai mis en place la version Vue.JS de mon blog. N'hésitez pas à m'envoyer un mail\nou lâcher un commentaire.",[12,24830,24831,24832,24835,24836,179],{},"Vous pouvez retrouver le kit de démarrage que j'ai fait pour mon blog sur github: ",[49,24833,23655],{"href":23653,"rel":24834},[347],". Vous pouvez également consulter l'intégration d'utterances sur un site\nen vue ici: ",[49,24837,24839],{"href":24577,"rel":24838},[347],"phoenix741\u002Fgridsome-flex-markdown-starter:GithubComponent.vue",[23219,24841,24843,24846],{"className":24842,"dataFootnotes":1646},[23222],[128,24844,23227],{"className":24845,"id":17406},[23226],[367,24847,24848],{},[370,24849,24850,24851],{"id":23232},"Single Page Application ",[49,24852,23240],{"href":23236,"ariaLabel":23237,"className":24853,"dataFootnoteBackref":1646},[23239],[3358,24855,24856],{},"html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html pre.shiki code .sn6KH, html code.shiki .sn6KH{--shiki-default:#ABB2BF}html pre.shiki code .sVyAn, html code.shiki .sVyAn{--shiki-default:#E06C75}html pre.shiki code .sVC51, html code.shiki .sVC51{--shiki-default:#D19A66}html pre.shiki code .subq3, html code.shiki .subq3{--shiki-default:#98C379}",{"title":1646,"searchDepth":1647,"depth":1647,"links":24858},[24859,24860,24861,24862,24863,24864,24865],{"id":23368,"depth":1647,"text":23369},{"id":23409,"depth":1647,"text":23410},{"id":23639,"depth":1647,"text":23640},{"id":24192,"depth":1647,"text":24193},{"id":24486,"depth":1647,"text":24487},{"id":1620,"depth":1647,"text":1621},{"id":17406,"depth":1647,"text":23227},"Logiciels","logiciels","2021-03-02","J'avais migré en Janvier 2013 mon blog vers Pelican en venant de\nDotclear. Pelican est un générateur de site statique en Python. J'avais\nalors dû faire l'impasse sur les commentaires mais au bénéfice d'un site performant et avec une\nsurface d'attaque plus faible.",{"type":9,"value":24871},[24872,24879,24887,24889],[12,24873,23317,24874,23322,24876,23328],{},[49,24875,23321],{"href":23320},[49,24877,23327],{"href":23325,"rel":24878},[347],[12,24880,23331,24881,23337,24884,179],{},[49,24882,23336],{"href":23334,"rel":24883},[347],[49,24885,23342],{"href":23340,"rel":24886},[347],[12,24888,23345],{},[12,24890,23348],{},{"planet":1767},"\u002Fpost\u002Fgridsome-mise-a-jour-du-blog",{"title":23312,"description":24869},"gridsome-mise-a-jour-du-blog","posts\u002FLogiciels\u002F2021-03-02-gridsome-mise-a-jour-du-blog",[24897,3510],"dedie","92imksbvFvyNB9g-BgsuOp-f1qDd_PCUcic_SG3Gf2o",{"id":24900,"title":24901,"author":7,"body":24902,"category":1682,"categorySlug":1683,"date":69,"description":14,"excerpt":26057,"extension":1764,"location":1765,"meta":26083,"navigation":1767,"path":65,"published":1767,"seo":26084,"slug":26085,"stem":26086,"tags":26087,"timeToRead":2556,"__hash__":26089},"posts\u002Fposts\u002FWoodstock\u002F2021-01-12_woodstock_brtfs.md","Woodstock Backup - Utilisation de Btrfs et son remplacement",{"type":9,"value":24903,"toc":26043},[24904,24906,24909,24912,24938,24946,24949,24952,24955,24958,24962,24966,24969,24972,24980,24983,25054,25057,25305,25308,25311,25315,25318,25321,25324,25335,25338,25341,25344,25347,25358,25361,25364,25367,25370,25373,25382,25396,25399,25402,25411,25420,25423,25426,25429,25432,25435,25438,25487,25490,25494,25497,25501,25504,25507,25542,25545,25693,25696,25699,25706,25709,25713,25716,25719,25722,25725,25734,26011,26018,26026,26029,26032,26034,26040],[12,24905,14],{},[12,24907,24908],{},"La version 1 de mon programme de sauvegarde Woodstock Backup utlise Btrfs et Rsync pour effectuer une sauvegarde. Je\nl'utilise depuis quelques mois pour sauvegarder mes differentes machines (7 machines).",[12,24910,24911],{},"Voici un premier compte-rendu de l'utilisation de la première version de cet outil dont je suis l'auteur:",[506,24913,24914,24917,24926],{},[370,24915,24916],{},"Lors de mon utilisation la sauvegarde fonctionne très bien, et cela c'est cool :). Je suis aux alentours de 200 snapshots.",[370,24918,24919,24920,24922,24923,24925],{},"J'ai eu un problème d'espace disque. Lors du déplacement de plusieurs énormes fichiers sur un serveur. La taille de\nl'espace de stockage à augmenté énormément.",[15567,24921],{},"En effet rsync ne permet pas de détecter les déplacements de fichiers et btrfs ne permet pas de dédupliquer à la volée\nles données.",[15567,24924],{},"Les fichiers ont donc été considérés comme étant nouveau.",[370,24927,24928,24929,24931,24932,24934,24935,24937],{},"L'espace disque étant tombé à zéro, j'ai voulu supprimer la dernière snapshot pour tester un déplacement de fichiers\ndans btrfs (à la main).",[15567,24930],{},"La suppression de la snapshot a commencé à prendre énormément de temps, puis la machine est devenue inaccessible.",[15567,24933],{},"En me connectant en direct sur la machine (KVM), j'ai découvert que la suppression du dernier volume Btrfs remplissait\nla mémore. Les 8Go octets de mémoire ont été remplis. Et le noyaux linux a utilisé OOM Killer pour détruire tous les\nprocessus.",[15567,24936],{},"Bref la machine n'était plus dans un état lui permettant de faire les sauvegardes.",[12,24939,24940,24941,24945],{},"On parle déjà de problème avec btrfs et la suppression de snapshot (",[49,24942,24943],{"href":24943,"rel":24944},"https:\u002F\u002Fwww.spinics.net\u002Flists\u002Flinux-btrfs\u002Fmsg52088.html",[347],").\nOn trouve aussi pas mal d'article si les problèmes de btrfs quand il n'y a plus d'espace.",[12,24947,24948],{},"Cela me fait penser que btrfs ne me permettrait pas de gérer mes sauvegardes sur le long terme (tant sur le nombre de snapshots\nque cela allait créér que sur la déduplication).",[12,24950,24951],{},"Cela m'amène à la conclusion suivante, il faut que je repense la gestion du pool et de la déduplication. C'est d'ailleurs\nce que font beaucoup de logiciel de sauvegarde (UrBackup proposer Btrfs et une version interne ; BackupPC gère son pool interne,\nBorg gère son pool interne ...)",[12,24953,24954],{},"Je vais donc repenser la manière dont je gère la déduplication dans l'application de sauvegarde. Si je réécris la manière\ndont est géré le stockage, je ne pourrais plus me baser sur rsync pour la sauvegarde. Il faudra donc que je développe mon\npropre outil pour générer la sauvegarde.",[12,24956,24957],{},"Par contre cela à l'avantage de ne plus être dépendant d'un système de fichier.",[128,24959,24961],{"id":24960},"le-pool","Le pool",[133,24963,24965],{"id":24964},"diviser-pour-réigner","Diviser pour réigner",[12,24967,24968],{},"Nous allons donc commencer par construire ce qui doit être le pool des données. Permettant de faire la déduplication.",[12,24970,24971],{},"Afin de faire de la déduplication, nous allons découper les fichiers en plusieurs morceaux. Cela permettra de faire des\nsauvegardes de gros fichiers dont seule une partie change:",[506,24973,24974,24977],{},[370,24975,24976],{},"Comme les images de machine virtuelle par exemple",[370,24978,24979],{},"Comme les fichiers de logs par exemple",[12,24981,24982],{},"Quelle est la bonne taille pour un morceau de fichier. Si on découpe le fichier de taille trop petite alors le découpage ne\nsert à rien. J'ai donc pris un échantillon de tous les fichiers que j'ai sur mes différentes machines pour déterminer la taille\nde cet échantillon.",[2105,24984,24986],{"className":2317,"code":24985,"language":2319,"meta":1646,"style":1646},"find . -type f -print0 | \\\nxargs -0 ls -l | \\\nawk '{ n=int(log($5)\u002Flog(2)); if (n\u003C10) { n=10; } size[n]++ } END { for (i in size) printf(\"%d %d\\n\", 2^i, size[i]) }' | \\\nsort -n | \\\nawk 'function human(x) { x[1]\u002F=1024; if (x[1]>=1024) { x[2]++; human(x) } } { a[1]=$1; a[2]=0; human(a); printf(\"%3d%s: %6d\\n\", a[1],substr(\"kMGTEPYZ\",a[2]+1,1),$2) }'\n",[184,24987,24988,25006,25024,25036,25047],{"__ignoreMap":1646},[2113,24989,24990,24992,24995,24997,24999,25001,25003],{"class":2115,"line":2116},[2113,24991,7343],{"class":2133},[2113,24993,24994],{"class":2149}," .",[2113,24996,7349],{"class":2166},[2113,24998,3920],{"class":2149},[2113,25000,7354],{"class":2166},[2113,25002,7357],{"class":2119},[2113,25004,25005],{"class":2334},"\\\n",[2113,25007,25008,25011,25014,25017,25020,25022],{"class":2115,"line":1647},[2113,25009,25010],{"class":2133},"xargs",[2113,25012,25013],{"class":2166}," -0",[2113,25015,25016],{"class":2149}," ls",[2113,25018,25019],{"class":2166}," -l",[2113,25021,7357],{"class":2119},[2113,25023,25005],{"class":2334},[2113,25025,25026,25029,25032,25034],{"class":2115,"line":1652},[2113,25027,25028],{"class":2133},"awk",[2113,25030,25031],{"class":2149}," '{ n=int(log($5)\u002Flog(2)); if (n\u003C10) { n=10; } size[n]++ } END { for (i in size) printf(\"%d %d\\n\", 2^i, size[i]) }'",[2113,25033,7357],{"class":2119},[2113,25035,25005],{"class":2334},[2113,25037,25038,25040,25043,25045],{"class":2115,"line":2185},[2113,25039,19527],{"class":2133},[2113,25041,25042],{"class":2166}," -n",[2113,25044,7357],{"class":2119},[2113,25046,25005],{"class":2334},[2113,25048,25049,25051],{"class":2115,"line":2230},[2113,25050,25028],{"class":2133},[2113,25052,25053],{"class":2149}," 'function human(x) { x[1]\u002F=1024; if (x[1]>=1024) { x[2]++; human(x) } } { a[1]=$1; a[2]=0; human(a); printf(\"%3d%s: %6d\\n\", a[1],substr(\"kMGTEPYZ\",a[2]+1,1),$2) }'\n",[12,25055,25056],{},"Voici le resultat de ce test d'échantillon.",[22,25058,25059,25072],{},[25,25060,25061],{},[28,25062,25063,25066,25069],{},[31,25064,25065],{},"File size",[31,25067,25068],{},"Number",[31,25070,25071],{},"Repartition",[41,25073,25074,25085,25096,25107,25118,25129,25140,25151,25162,25173,25184,25195,25206,25217,25228,25239,25250,25261,25272,25283,25294],{},[28,25075,25076,25079,25082],{},[46,25077,25078],{},"1k",[46,25080,25081],{},"29126558",[46,25083,25084],{},"37,36 %",[28,25086,25087,25090,25093],{},[46,25088,25089],{},"2k",[46,25091,25092],{},"8649088",[46,25094,25095],{},"48,45 %",[28,25097,25098,25101,25104],{},[46,25099,25100],{},"4k",[46,25102,25103],{},"7915884",[46,25105,25106],{},"58,60 %",[28,25108,25109,25112,25115],{},[46,25110,25111],{},"8k",[46,25113,25114],{},"6394302",[46,25116,25117],{},"66,81 %",[28,25119,25120,25123,25126],{},[46,25121,25122],{},"16k",[46,25124,25125],{},"4839627",[46,25127,25128],{},"73,01 %",[28,25130,25131,25134,25137],{},[46,25132,25133],{},"32k",[46,25135,25136],{},"3606949",[46,25138,25139],{},"77,64 %",[28,25141,25142,25145,25148],{},[46,25143,25144],{},"64k",[46,25146,25147],{},"3477900",[46,25149,25150],{},"82,10 %",[28,25152,25153,25156,25159],{},[46,25154,25155],{},"128k",[46,25157,25158],{},"5158625",[46,25160,25161],{},"88,72 %",[28,25163,25164,25167,25170],{},[46,25165,25166],{},"256k",[46,25168,25169],{},"3601985",[46,25171,25172],{},"93,34 %",[28,25174,25175,25178,25181],{},[46,25176,25177],{},"512k",[46,25179,25180],{},"971108",[46,25182,25183],{},"94,58 %",[28,25185,25186,25189,25192],{},[46,25187,25188],{},"1M",[46,25190,25191],{},"875574",[46,25193,25194],{},"95,71 %",[28,25196,25197,25200,25203],{},[46,25198,25199],{},"2M",[46,25201,25202],{},"1698194",[46,25204,25205],{},"97,88 %",[28,25207,25208,25211,25214],{},[46,25209,25210],{},"4M",[46,25212,25213],{},"1046430",[46,25215,25216],{},"99,23 %",[28,25218,25219,25222,25225],{},[46,25220,25221],{},"8M",[46,25223,25224],{},"309027",[46,25226,25227],{},"99,62 %",[28,25229,25230,25233,25236],{},[46,25231,25232],{},"16M",[46,25234,25235],{},"105271",[46,25237,25238],{},"99,76 %",[28,25240,25241,25244,25247],{},[46,25242,25243],{},"32M",[46,25245,25246],{},"65211",[46,25248,25249],{},"99,84 %",[28,25251,25252,25255,25258],{},[46,25253,25254],{},"64M",[46,25256,25257],{},"50832",[46,25259,25260],{},"99,91 %",[28,25262,25263,25266,25269],{},[46,25264,25265],{},"128M",[46,25267,25268],{},"33947",[46,25270,25271],{},"99,95 %",[28,25273,25274,25277,25280],{},[46,25275,25276],{},"256M",[46,25278,25279],{},"21338",[46,25281,25282],{},"99,98 %",[28,25284,25285,25288,25291],{},[46,25286,25287],{},"512M",[46,25289,25290],{},"8066",[46,25292,25293],{},"99,99 %",[28,25295,25296,25299,25302],{},[46,25297,25298],{},"> 1G",[46,25300,25301],{},"10068",[46,25303,25304],{},"100,00 %",[12,25306,25307],{},"La conclusion que l'on peut tirer de cela est que si on prend un chunk de 4Mo, alors 99% des fichiers ne seront pas découpés. Dans\nle lot rentrerons la plupart des fichiers textes, des photos, ... . Tous des fichiers qui s'ils ne sont pas des copies, sont normalement\nunique.",[12,25309,25310],{},"Dans une première version, je pense que cette taille ne sera pas paramétrable, mais il faudra la rendre dynamiquement paramétrable dans le\nfutur (si le type des fichiers d'autres personnes a pour conséquence une répartition différente).",[128,25312,25314],{"id":25313},"la-table-de-hashage","La table de hashage",[12,25316,25317],{},"Ces différents morceaux de fichiers (chunk) doivent ensuite pouvoir être identifiés facilement sans pour autant relire le contenu du fichier à\nchaque fois.",[12,25319,25320],{},"L'idée est d'utiliser une table de hashage où avec une clé on peut retrouver le contenu d'un morceau de fichier.",[12,25322,25323],{},"Il faut pouvoir, à partir de la clé:",[506,25325,25326,25329,25332],{},[370,25327,25328],{},"savoir si le morceau de fichier existe,",[370,25330,25331],{},"lire le contenu du morceau de fichier,",[370,25333,25334],{},"écrire le contenu du morceau de fichier.",[12,25336,25337],{},"Quelle clé peut-on utiliser ?",[12,25339,25340],{},"Après quelque recherches je vais partir sur un SHA-256. Quels sont les avantages et les inconvénients d'une telle clé ?",[12,25342,25343],{},"L'avantage est qu'il est possible, quand on connait le hash de faire le lien direct avec le morceau de fichier associé. La clé est\nfacile à générer et le lien entre la clé et le chunk est facile à connaître.",[12,25345,25346],{},"Le plus gros inconvénient est le risque de collision. Le but d'une fonction de hash est de calculer un nombre \"court\" qui permet\nde representer un texte beaucoup plus long. Ce type de fonction de hashage est généralement utilisé pour:",[506,25348,25349,25352,25355],{},[370,25350,25351],{},"quand elle est coupler à une clé public\u002Fprivé, signer un document,",[370,25353,25354],{},"verifier l'intégrité d'un document (en cas d'erreur transfert réseau),",[370,25356,25357],{},"envoyer séparement permet de vérifier que le fichier n'a pas été corrompu.",[12,25359,25360],{},"Ce qui est le propre d'une fonction de hash est sa non réversibilité. En effet s'il était possible de retrouver facilement à partir d'un\nhash le texte contenu associé, les algo de compression n'utiliseraient que ca ;).\nMais la plus grosse conséquence de tout cela est que deux textes (de contenus et de longueurs différentes) peuvent arriver au même hash.",[12,25362,25363],{},"Pour un logiciel de sauvegarde, cela pose alors un gros problème. Si parcequ'il possède le même hash qu'un autre fichier, on ne sauvegarde\npas un document, la restauration ne sera pas faite à l'identique et cela engendrera de la perte de fichier.",[12,25365,25366],{},"Je me suis donc renseigné sur ce qui se faisait ailleurs.",[133,25368,15449],{"id":25369},"borg",[12,25371,25372],{},"Je trouve borg intéressant dans son fonctionnement, ses performances, ainsi que son stockage interne. Ce qui est dommage, c'est que dans\nmon cas, je n'ai pas une vue centralisée des machines dont j'organise la sauvegarde.",[12,25374,25375,25376,25381],{},"Dans la partie ",[49,25377,25380],{"href":25378,"rel":25379},"https:\u002F\u002Fborgbackup.readthedocs.io\u002Fen\u002Fstable\u002Finternals.html",[347],"Internals"," on retrouve la structure interne du pool de Borg.",[12,25383,25384,25385,25390,25391,179],{},"Borg utilise ",[49,25386,25389],{"href":25387,"rel":25388},"https:\u002F\u002Fborgbackup.readthedocs.io\u002Fen\u002Fstable\u002Finternals\u002Fdata-structures.html#hashindex",[347],"HashIndex",", qui si j'ai bien compris\ncalcule un hash sur un contenu dont la taille peut varier. Des utilisateurs se sont déjà posés la question du ",[49,25392,25395],{"href":25393,"rel":25394},"https:\u002F\u002Fgithub.com\u002Fborgbackup\u002Fborg\u002Fissues\u002F170",[347],"risque de collision",[12,25397,25398],{},"La réponse faite par l'équipe de Borg a été de rejeter la demande. L'explication qui est faite est que la probabilité qu'une telle\ncollision arrive est faible sur un hash de 256. Très faible.",[133,25400,15400],{"id":25401},"urbackup",[12,25403,25404,25405,25410],{},"Même chose pour UrBackup. Dans le ",[49,25406,25409],{"href":25407,"rel":25408},"https:\u002F\u002Fwww.urbackup.org\u002Fadministration_manual.html",[347],"manuel d'administration, partie 6.3",",\non retrouve l'information suivante:",[1886,25412,25413],{},[12,25414,25415,25416,25419],{},"UrBackup uses SHA512 to hash the files before file deduplication. In comparison ZFS uses SHA256 for block deduplication.\nThe choice of SHA512 is safer. The Wikipedia page for “Birthday attack” has a probability table for SHA512. According to\nit one needs 1.6 ",[362,25417,25418],{},"1068 different files (of same size) to reach a probability of 10-18 of a collision. It also states\nthat 10-18 is the best case uncorrectable bit error rate of a typical hard disk. To have 1.6"," 1068 different files of\n1KB you need 1.4551915 * 1056 EB of hard disk space. So it is ridiculously more likely that the hard disk returns bad\ndata or the data gets corrupted in RAM, rather than UrBackup linking the wrong files to each other.",[12,25421,25422],{},"Bref, on y explique sur la probabilité de collision pour un SHA512 est plus faible que celle utilisé pour la déduplication\ndans ZFS. On y explique également qu'elle est ridicule par rapport au risque de panne d'un disque dur.",[133,25424,1013],{"id":25425},"backuppc",[12,25427,25428],{},"BackupPC utilise un MD5 pour gérer son les clés de son pool. En cas de collision (ce qui est beaucoup plus probable avec un MD5 qu'avec\nun SHA-256) une extension est ajoutée à la fin du fichier.",[12,25430,25431],{},"Cela necessite alors de ré-envoyer le fichier sur le réseau pour comparer, mais permet de s'assurer qu'en cas de collision on ait une\nsolution de repli.",[12,25433,25434],{},"Un MD5 fait 4 octets contrairement à un SHA-256 qui en fait 32.",[12,25436,25437],{},"Du coup, est-ce qu'avec un md5, j'ai beaucoup de collision sur mon instance BackupPC ? Pour un pool qui contient 3 464 397 fichiers, j'ai\n0 collision.",[2105,25439,25441],{"className":2317,"code":25440,"language":2319,"meta":1646,"style":1646},"> find . -type f | wc -l\n3464397\n> find . -type f | awk 'length($0) > 40' | wc -l\n0\n",[184,25442,25443,25459,25464,25483],{"__ignoreMap":1646},[2113,25444,25445,25448,25450,25453,25456],{"class":2115,"line":2116},[2113,25446,25447],{"class":2119},"> find ",[2113,25449,179],{"class":2334},[2113,25451,25452],{"class":2119}," -type f | ",[2113,25454,25455],{"class":2133},"wc",[2113,25457,25458],{"class":2166}," -l\n",[2113,25460,25461],{"class":2115,"line":1647},[2113,25462,25463],{"class":2133},"3464397\n",[2113,25465,25466,25468,25470,25472,25474,25477,25479,25481],{"class":2115,"line":1652},[2113,25467,25447],{"class":2119},[2113,25469,179],{"class":2334},[2113,25471,25452],{"class":2119},[2113,25473,25028],{"class":2133},[2113,25475,25476],{"class":2149}," 'length($0) > 40'",[2113,25478,7357],{"class":2119},[2113,25480,25455],{"class":2133},[2113,25482,25458],{"class":2166},[2113,25484,25485],{"class":2115,"line":2185},[2113,25486,18077],{"class":2133},[12,25488,25489],{},"Ce qui est plutôt rassurant sur le risque de collision sur un SHA-256",[133,25491,25493],{"id":25492},"woodstock-backup","Woodstock Backup",[12,25495,25496],{},"Pour mon propre logiciel, je vais partir sur un SHA-256. Je vais partir du principe qu'il n'y a pas de collision (et je ferais un test\npour vérifier cela).\nDans une version future il pourrait être intéressant d'ajouter une extension au fichier dans le cas où il y a une collision, et laisser\nla possibilité à l'utilisateur de demander à retransférer le fichier pour être sûr qu'il n'y a pas de collision.",[128,25498,25500],{"id":25499},"structure-du-pool","Structure du pool",[12,25502,25503],{},"Maintenant que l'on connaît la taille des différents morceau de fichier ainsi que la clé qui nous servira à les classer. Nous allons\nmaintenant voir comment les organiser dans notre système de fichier.",[12,25505,25506],{},"Derrière le stockage j'ai les idées suivantes:",[506,25508,25509,25516,25519,25536,25539],{},[370,25510,25511,25512,25515],{},"Si on stocke tout les chunks (morceau de fichier) dans un seul dossier, nous pourrions au plus avoir ",[184,25513,25514],{},"1.3 x 10^154"," fichiers. Mais\ncela dépendra plus du nombre de fichier (chunk) que du nombre du nombre de possibilités.",[370,25517,25518],{},"Un système de fichier est en lui-même déjà une table de correspondance où la clé est le nom du fichier.",[370,25520,25521,25522],{},"Il y a une limite sur le nombre de fichiers par dossier:\n",[506,25523,25524,25527,25530,25533],{},[370,25525,25526],{},"FAT32: 65 536 (donc non utilisable)",[370,25528,25529],{},"NTFS: à priori pas de limit.",[370,25531,25532],{},"EXT2: ~1.3 x 10^20 (mais problème de perf au delà de 10 000) - (donc je déconseille son utilisation).",[370,25534,25535],{},"EXT4: pas de limit de nombre de fichier par dossier",[370,25537,25538],{},"J'aimerai à terme (un jour peut-être) pouvoir proposer l'utilisation d'un stockage objet (exemple S3, minio, ...) afin de pouvoir\nutiliser ce stockage depuis plusieurs serveurs. Il n'y a pas de limite du nombre de fichier dans un bucket S3.",[370,25540,25541],{},"Sur une structure à deux niveaux (et 255 possibilité par niveau), on peut espérer que les 3,5 millions de fichiers seront répartis\nce qui ferait un peu plus d'une 50 de fichier par dossier. (le nombre de fichier par dossier dépend du nombre de fichier et de leurs\ntaille).",[12,25543,25544],{},"Voici la structure que j'imagine utiliser pour le stockage des chunks.",[2105,25546,25548],{"className":2317,"code":25547,"language":2319,"meta":1646,"style":1646}," pool\n   ├── aa\n   │    ├── aa\n   │    │    ├── aa\n   │    │    │    ├── REFCNT\n   │    │    │    │     ├── sha256 cnt\n   │    │    │    │     ├── sha256 cnt\n   │    │    │    │     └── sha256 cnt\n   │    │    │    ├── LOCK\n   │    │    │    │     └── host backupNumber\n   │    │    │    └── aaaaaacdefghih-sha256.zlib\n",[184,25549,25550,25555,25563,25573,25584,25597,25615,25631,25648,25661,25679],{"__ignoreMap":1646},[2113,25551,25552],{"class":2115,"line":2116},[2113,25553,25554],{"class":2133}," pool\n",[2113,25556,25557,25560],{"class":2115,"line":1647},[2113,25558,25559],{"class":2133},"   ├──",[2113,25561,25562],{"class":2149}," aa\n",[2113,25564,25565,25568,25571],{"class":2115,"line":1652},[2113,25566,25567],{"class":2133},"   │",[2113,25569,25570],{"class":2149},"    ├──",[2113,25572,25562],{"class":2149},[2113,25574,25575,25577,25580,25582],{"class":2115,"line":2185},[2113,25576,25567],{"class":2133},[2113,25578,25579],{"class":2149},"    │",[2113,25581,25570],{"class":2149},[2113,25583,25562],{"class":2149},[2113,25585,25586,25588,25590,25592,25594],{"class":2115,"line":2230},[2113,25587,25567],{"class":2133},[2113,25589,25579],{"class":2149},[2113,25591,25579],{"class":2149},[2113,25593,25570],{"class":2149},[2113,25595,25596],{"class":2149}," REFCNT\n",[2113,25598,25599,25601,25603,25605,25607,25610,25612],{"class":2115,"line":2235},[2113,25600,25567],{"class":2133},[2113,25602,25579],{"class":2149},[2113,25604,25579],{"class":2149},[2113,25606,25579],{"class":2149},[2113,25608,25609],{"class":2149},"     ├──",[2113,25611,10145],{"class":2149},[2113,25613,25614],{"class":2149}," cnt\n",[2113,25616,25617,25619,25621,25623,25625,25627,25629],{"class":2115,"line":2241},[2113,25618,25567],{"class":2133},[2113,25620,25579],{"class":2149},[2113,25622,25579],{"class":2149},[2113,25624,25579],{"class":2149},[2113,25626,25609],{"class":2149},[2113,25628,10145],{"class":2149},[2113,25630,25614],{"class":2149},[2113,25632,25633,25635,25637,25639,25641,25644,25646],{"class":2115,"line":2246},[2113,25634,25567],{"class":2133},[2113,25636,25579],{"class":2149},[2113,25638,25579],{"class":2149},[2113,25640,25579],{"class":2149},[2113,25642,25643],{"class":2149},"     └──",[2113,25645,10145],{"class":2149},[2113,25647,25614],{"class":2149},[2113,25649,25650,25652,25654,25656,25658],{"class":2115,"line":2464},[2113,25651,25567],{"class":2133},[2113,25653,25579],{"class":2149},[2113,25655,25579],{"class":2149},[2113,25657,25570],{"class":2149},[2113,25659,25660],{"class":2149}," LOCK\n",[2113,25662,25663,25665,25667,25669,25671,25673,25676],{"class":2115,"line":2085},[2113,25664,25567],{"class":2133},[2113,25666,25579],{"class":2149},[2113,25668,25579],{"class":2149},[2113,25670,25579],{"class":2149},[2113,25672,25643],{"class":2149},[2113,25674,25675],{"class":2149}," host",[2113,25677,25678],{"class":2149}," backupNumber\n",[2113,25680,25681,25683,25685,25687,25690],{"class":2115,"line":2514},[2113,25682,25567],{"class":2133},[2113,25684,25579],{"class":2149},[2113,25686,25579],{"class":2149},[2113,25688,25689],{"class":2149},"    └──",[2113,25691,25692],{"class":2149}," aaaaaacdefghih-sha256.zlib\n",[12,25694,25695],{},"Pour les 3 premiers niveaux, nous allons avoir une structure de dossier qui est constituée des 3 premiers octets du SHA-256. Ce qui permet\nde réduire le nombre de fichiers par dossier, mais aussi de limiter le nombre de LOCK lors de la création du pool.",[12,25697,25698],{},"Ensuite dans chaque dossier, nous aurons un fichier de LOCK qui sera créé lors de l'ajout d'un nouveau morceau de fichier. Il sera également\nutilisé lors de la lecture.",[12,25700,25701,25702,25705],{},"Un fichier ",[184,25703,25704],{},"REFCNT"," sera utilisé pour compter le nombre de référence. Cela permettra de supprimer les morceaux quand ces derniers ne seront\nplus utilisés.",[12,25707,25708],{},"Enfin les morceaux de fichiers sont stockés dans des fichiers dont le nom contient le hash. Ces fichiers peuvent être compressés pour réduire\nl'espace utilisé.",[128,25710,25712],{"id":25711},"structure-des-fichiers-de-sauvegardes","Structure des fichiers de sauvegardes",[12,25714,25715],{},"Une fois les morceaux de fichiers déposés dans le pool, il nous faut un fichier permettant de référencer l'ensemble des fichiers qui composent\nla sauvegarde.",[12,25717,25718],{},"Il y aura un fichier par sauvegarde.",[12,25720,25721],{},"Ces fichiers de sauvegarde seront stockés dans les dossiers des différents serveurs sauvegardés. Ce fichier contient l'ensemble des fichiers\nd'une sauvegarde et pour chaque fichier, le hash de l'enssemble des morceaux de fichiers.",[12,25723,25724],{},"Ce fichier doit avoir un format lisible facilement et rapidement par un programme. Ce fichier sera dans un format binaire (pour que le fichier\nsoit lisible rapidement et qu'il ne prenne pas trop de place).",[12,25726,25727,25728,25733],{},"Afin de simplifier la mise en place de ce fichier, nous allons utiliser ",[49,25729,25732],{"href":25730,"rel":25731},"https:\u002F\u002Fdevelopers.google.com\u002Fprotocol-buffers",[347],"protocol-buffers",". Ce\ndernier permet d'avoir un format de fichier compatible quel que soit le language de l'application. Voici un premier jet de format de fichier qui\nsera utilisé pour stocker chaque fichier d'une sauvegarde.",[2105,25735,25737],{"className":15974,"code":25736,"language":15970,"meta":1646,"style":1646},"message FileManifest {\n  message FileManifestStat {\n    int32 ownerId = 1;\n    int32 groupId = 2;\n    int64 size = 3;\n    int64 lastRead = 4;\n    int64 lastModified = 5;\n    int64 created = 6;\n    int32 mode = 7;\n  }\n\n  message FileManifestAcl {\n    string user = 1;\n    string group = 2;\n    int32 mask = 3;\n    int32 other = 4;\n  }\n\n  bytes path = 1;\n  FileManifestStat stats = 2;\n  map\u003Cstring, bytes> xattr = 5;\n  repeated FileManifestAcl acl = 6;\n  repeated bytes chunks = 3;\n  bytes sha256 = 4;\n}\n",[184,25738,25739,25747,25755,25767,25779,25791,25803,25815,25827,25839,25843,25847,25856,25870,25883,25896,25909,25913,25917,25929,25941,25966,25981,25995,26007],{"__ignoreMap":1646},[2113,25740,25741,25743,25745],{"class":2115,"line":2116},[2113,25742,16052],{"class":2326},[2113,25744,16055],{"class":2414},[2113,25746,2647],{"class":2119},[2113,25748,25749,25751,25753],{"class":2115,"line":1647},[2113,25750,16062],{"class":2326},[2113,25752,16065],{"class":2414},[2113,25754,2647],{"class":2119},[2113,25756,25757,25759,25761,25763,25765],{"class":2115,"line":1652},[2113,25758,16072],{"class":2326},[2113,25760,16075],{"class":2330},[2113,25762,2153],{"class":2334},[2113,25764,2850],{"class":2166},[2113,25766,2487],{"class":2119},[2113,25768,25769,25771,25773,25775,25777],{"class":2115,"line":2185},[2113,25770,16072],{"class":2326},[2113,25772,16088],{"class":2330},[2113,25774,2153],{"class":2334},[2113,25776,2484],{"class":2166},[2113,25778,2487],{"class":2119},[2113,25780,25781,25783,25785,25787,25789],{"class":2115,"line":2230},[2113,25782,16099],{"class":2326},[2113,25784,7924],{"class":2330},[2113,25786,2153],{"class":2334},[2113,25788,16106],{"class":2166},[2113,25790,2487],{"class":2119},[2113,25792,25793,25795,25797,25799,25801],{"class":2115,"line":2235},[2113,25794,16099],{"class":2326},[2113,25796,16115],{"class":2330},[2113,25798,2153],{"class":2334},[2113,25800,2894],{"class":2166},[2113,25802,2487],{"class":2119},[2113,25804,25805,25807,25809,25811,25813],{"class":2115,"line":2241},[2113,25806,16099],{"class":2326},[2113,25808,16128],{"class":2330},[2113,25810,2153],{"class":2334},[2113,25812,16133],{"class":2166},[2113,25814,2487],{"class":2119},[2113,25816,25817,25819,25821,25823,25825],{"class":2115,"line":2246},[2113,25818,16099],{"class":2326},[2113,25820,16142],{"class":2330},[2113,25822,2153],{"class":2334},[2113,25824,16147],{"class":2166},[2113,25826,2487],{"class":2119},[2113,25828,25829,25831,25833,25835,25837],{"class":2115,"line":2464},[2113,25830,16072],{"class":2326},[2113,25832,16156],{"class":2330},[2113,25834,2153],{"class":2334},[2113,25836,6408],{"class":2166},[2113,25838,2487],{"class":2119},[2113,25840,25841],{"class":2115,"line":2085},[2113,25842,2572],{"class":2119},[2113,25844,25845],{"class":2115,"line":2514},[2113,25846,2125],{"emptyLinePlaceholder":1767},[2113,25848,25849,25851,25854],{"class":2115,"line":2533},[2113,25850,16062],{"class":2326},[2113,25852,25853],{"class":2414}," FileManifestAcl",[2113,25855,2647],{"class":2119},[2113,25857,25858,25861,25864,25866,25868],{"class":2115,"line":2556},[2113,25859,25860],{"class":2326},"    string",[2113,25862,25863],{"class":2330}," user",[2113,25865,2153],{"class":2334},[2113,25867,2850],{"class":2166},[2113,25869,2487],{"class":2119},[2113,25871,25872,25874,25877,25879,25881],{"class":2115,"line":2569},[2113,25873,25860],{"class":2326},[2113,25875,25876],{"class":2330}," group",[2113,25878,2153],{"class":2334},[2113,25880,2484],{"class":2166},[2113,25882,2487],{"class":2119},[2113,25884,25885,25887,25890,25892,25894],{"class":2115,"line":2575},[2113,25886,16072],{"class":2326},[2113,25888,25889],{"class":2330}," mask",[2113,25891,2153],{"class":2334},[2113,25893,16106],{"class":2166},[2113,25895,2487],{"class":2119},[2113,25897,25898,25900,25903,25905,25907],{"class":2115,"line":2596},[2113,25899,16072],{"class":2326},[2113,25901,25902],{"class":2330}," other",[2113,25904,2153],{"class":2334},[2113,25906,2894],{"class":2166},[2113,25908,2487],{"class":2119},[2113,25910,25911],{"class":2115,"line":3192},[2113,25912,2572],{"class":2119},[2113,25914,25915],{"class":2115,"line":3213},[2113,25916,2125],{"emptyLinePlaceholder":1767},[2113,25918,25919,25921,25923,25925,25927],{"class":2115,"line":3236},[2113,25920,16175],{"class":2326},[2113,25922,6584],{"class":2330},[2113,25924,2153],{"class":2334},[2113,25926,2850],{"class":2166},[2113,25928,2487],{"class":2119},[2113,25930,25931,25933,25935,25937,25939],{"class":2115,"line":3248},[2113,25932,16188],{"class":2326},[2113,25934,9087],{"class":2330},[2113,25936,2153],{"class":2334},[2113,25938,2484],{"class":2166},[2113,25940,2487],{"class":2119},[2113,25942,25943,25946,25948,25950,25952,25955,25957,25960,25962,25964],{"class":2115,"line":4899},[2113,25944,25945],{"class":2326},"  map",[2113,25947,3109],{"class":2119},[2113,25949,19225],{"class":2326},[2113,25951,932],{"class":2119},[2113,25953,25954],{"class":2326},"bytes",[2113,25956,4435],{"class":2119},[2113,25958,25959],{"class":2330},"xattr",[2113,25961,2153],{"class":2334},[2113,25963,16133],{"class":2166},[2113,25965,2487],{"class":2119},[2113,25967,25968,25970,25972,25975,25977,25979],{"class":2115,"line":1777},[2113,25969,16201],{"class":2326},[2113,25971,25853],{"class":2326},[2113,25973,25974],{"class":2330}," acl",[2113,25976,2153],{"class":2334},[2113,25978,16147],{"class":2166},[2113,25980,2487],{"class":2119},[2113,25982,25983,25985,25987,25989,25991,25993],{"class":2115,"line":4931},[2113,25984,16201],{"class":2326},[2113,25986,16204],{"class":2326},[2113,25988,16207],{"class":2330},[2113,25990,2153],{"class":2334},[2113,25992,16106],{"class":2166},[2113,25994,2487],{"class":2119},[2113,25996,25997,25999,26001,26003,26005],{"class":2115,"line":4961},[2113,25998,16175],{"class":2326},[2113,26000,10145],{"class":2330},[2113,26002,2153],{"class":2334},[2113,26004,2894],{"class":2166},[2113,26006,2487],{"class":2119},[2113,26008,26009],{"class":2115,"line":4976},[2113,26010,2599],{"class":2119},[12,26012,26013,26014,26017],{},"Le fichier sera constitué d'une liste de ",[184,26015,26016],{},"FileManifest",". Ce fichier est de la forme :",[2105,26019,26024],{"className":26020,"code":26022,"language":26023},[26021],"language-text","int32 FileManifest int32 FileManifest int32 FileManifest int32 FileManifest int32 FileManifest int32 FileManifest int32 FileManifest\nint32 FileManifest int32 FileManifest int32 FileManifest int32 FileManifest int32 FileManifest int32 FileManifest int32 FileManifest\n(vous avez compris le principe)\n","text",[184,26025,26022],{"__ignoreMap":1646},[12,26027,26028],{},"où chaque int32 est la taille de chaque FileManifest.",[12,26030,26031],{},"Lors de la sauvegarde, le serveur repartira de la sauvegarde précédente et écrira toute modification dans un journal. Le journal\nindiquera, les fichiers ajoutés, supprimés, et modifiés. Une fois la sauvegarde terminée, le journal sera fusionné dans le fichier de\nsauvegarde (et le comptage de référence sera mis à jour).",[128,26033,1621],{"id":1620},[12,26035,26036,26037,179],{},"Maintenant que j'ai une idée de la forme du nouveau de format de stockage, il ne me reste plus qu'à développer le pool de stockage.\nSi vous avez des retours à me faire sur le format de stockage alors n'hésitez pas à me contacter. Vous pouvez le faire par exemple sur\n",[49,26038,26039],{"href":25492},"https:\u002F\u002Fgithub.com\u002Fphoenix741\u002Fwoodstock-backup",[3358,26041,26042],{},"html pre.shiki code .sVbv2, html code.shiki .sVbv2{--shiki-default:#61AFEF}html pre.shiki code .subq3, html code.shiki .subq3{--shiki-default:#98C379}html pre.shiki code .sVC51, html code.shiki .sVC51{--shiki-default:#D19A66}html pre.shiki code .sn6KH, html code.shiki .sn6KH{--shiki-default:#ABB2BF}html pre.shiki code .sjrmR, html code.shiki .sjrmR{--shiki-default:#56B6C2}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html pre.shiki code .seHd6, html code.shiki .seHd6{--shiki-default:#C678DD}html pre.shiki code .sU0A5, html code.shiki .sU0A5{--shiki-default:#E5C07B}html pre.shiki code .sVyAn, html code.shiki .sVyAn{--shiki-default:#E06C75}",{"title":1646,"searchDepth":1647,"depth":1647,"links":26044},[26045,26048,26054,26055,26056],{"id":24960,"depth":1647,"text":24961,"children":26046},[26047],{"id":24964,"depth":1652,"text":24965},{"id":25313,"depth":1647,"text":25314,"children":26049},[26050,26051,26052,26053],{"id":25369,"depth":1652,"text":15449},{"id":25401,"depth":1652,"text":15400},{"id":25425,"depth":1652,"text":1013},{"id":25492,"depth":1652,"text":25493},{"id":25499,"depth":1647,"text":25500},{"id":25711,"depth":1647,"text":25712},{"id":1620,"depth":1647,"text":1621},{"type":9,"value":26058},[26059,26061,26063,26065],[12,26060,14],{},[12,26062,24908],{},[12,26064,24911],{},[506,26066,26067,26069,26075],{},[370,26068,24916],{},[370,26070,24919,26071,24922,26073,24925],{},[15567,26072],{},[15567,26074],{},[370,26076,24928,26077,24931,26079,24934,26081,24937],{},[15567,26078],{},[15567,26080],{},[15567,26082],{},{"planet":1767},{"title":24901,"description":14},"woodstock_brtfs","posts\u002FWoodstock\u002F2021-01-12_woodstock_brtfs",[1773,1774,26088,983,3510,3511,1683],"btrfs","f12bC0YlcreB0kEuvkJiJsi0nYRb0O3yavG9pPjrXUM",{"id":26091,"title":26092,"author":7,"body":26093,"category":2057,"categorySlug":2058,"date":29132,"description":26097,"excerpt":29133,"extension":1764,"location":1765,"meta":29155,"navigation":1767,"path":29156,"published":1767,"seo":29157,"slug":29158,"stem":29159,"tags":29160,"timeToRead":1777,"__hash__":29163},"posts\u002Fposts\u002FProgrammation\u002F2020-11-29_creation-api-3.md","Comment créer une bonne API Web - Partie 3",{"type":9,"value":26094,"toc":29111},[26095,26098,26101,26121,26124,26128,26131,26139,26142,26148,26151,26154,26157,26288,26294,26300,26303,26469,26472,26475,26483,26487,26490,26493,26496,26499,26502,26510,26513,26517,26520,26523,26633,26636,26639,26646,26649,26657,26661,26668,26676,26680,26683,26946,26949,26966,26975,26978,27010,27013,27030,27033,27036,27112,27115,27126,27138,27182,27185,27334,27337,27341,27344,27347,27350,27353,27356,27360,27425,27428,27431,27437,27441,27444,27519,27532,27537,27540,27669,27676,27680,27683,27691,27704,27713,27716,27720,27723,27726,27729,27740,27746,27773,27889,27897,27901,27904,27907,27913,27924,28092,28101,28105,28114,28117,28182,28185,28194,28252,28255,28351,28357,28361,28366,28369,28380,28383,28391,28395,28398,28416,28420,28426,28429,28525,28528,28531,28534,28573,28576,28756,28762,28765,28773,28776,28785,28788,28796,28800,28807,28810,28813,28816,28859,28862,28870,29044,29051,29053,29056,29059,29063,29108],[12,26096,26097],{},"Bonjour,",[12,26099,26100],{},"Cet article fait partie d'un ensemble:",[506,26102,26103,26109,26115],{},[370,26104,26105],{},[49,26106,26108],{"href":26107},"\u002Fpost\u002Fcreation-api-1\u002F","Généralités sur l'écriture d'une bonne API",[370,26110,26111],{},[49,26112,26114],{"href":26113},"\u002Fpost\u002Fcreation-api-2\u002F","Qu'est ce qu'une API REST",[370,26116,26117],{},[49,26118,26120],{"href":26119},"\u002Fpost\u002Fcreation-api-3\u002F","Qu'est ce qu'une API GraphQL",[12,26122,26123],{},"Il y a quelques années de cela, j'ai souhaité résoudre un problème que j'ai depuis longtemps avec les API REST: comment bien\nnormaliser les tris, les projections, et les filtres. En effectuant mes recherches je suis tombé sur deux frameworks qui permettent\nde résoudre le problème des projections.",[128,26125,26127],{"id":26126},"quest-quune-api-falcor","Qu'est qu'une API Falcor",[12,26129,26130],{},"Je ne vais parler que succinctement de Falcor. C'est un framework que je n'ai pas utilisé mais j'ai tout de même été très intéressé\npar ce dernier et je vais écrire quelques lignes sur ce Framework.",[12,26132,26133,26134,179],{},"Pour plus d'informations, vous pourrez vous référer à la ",[49,26135,26138],{"href":26136,"rel":26137},"https:\u002F\u002Fnetflix.github.io\u002Ffalcor\u002Fstarter\u002Fwhat-is-falcor.html",[347],"documentation",[12,26140,26141],{},"Pour reprendre les explications de la documentation, Falcor est un middleware de votre application qui permet d'interroger des ressources\nau format JSON sur le serveur, comme votre application le ferait sur des données en mémoire.",[12,26143,26144],{},[121,26145],{"alt":26146,"src":26147},"Falcor","https:\u002F\u002Fnetflix.github.io\u002Ffalcor\u002Fdocumentation\u002Fnetwork-diagram.png",[12,26149,26150],{},"La requête envoyée alors au serveur ne contient que les champs demandés par le client, ce qui permet au serveur de sélectionner les champs\net de ne retourner que ces champs. De plus le serveur peut n'exposer qu'un seul modèle contenant toutes les ressources (que ce soit pour\nretourner des listes ou des items particuliers).",[12,26152,26153],{},"En une seule requête, le client peut alors demander l'ensemble des données dont il a besoin, et l'afficher.",[12,26155,26156],{},"Nous n'avons plus alors le dilemne \"faut-il créér une sous-resource, ou l'intégrer dans la ressource actuelle ?\". Du point de vue du client:\nplus besoin d'effectuer plusieurs requêtes complexes pour récupérer plusieurs ressources, un seul appel suffit.",[2105,26158,26160],{"className":7776,"code":26159,"language":7778,"meta":1646,"style":1646},"\u002Fmodel.json?paths=[\"user.name\", \"user.surname\", \"user.address\"]\n\nGET \u002Fmodel.json?paths=[\"user.name\", \"user.surname\", \"user.address\"]\n{\n  user: {\n    name: \"Frank\",\n    surname: \"Underwood\",\n    address: \"1600 Pennsylvania Avenue, Washington, DC\"\n  }\n}\n",[184,26161,26162,26199,26203,26236,26240,26247,26258,26270,26280,26284],{"__ignoreMap":1646},[2113,26163,26164,26166,26169,26171,26173,26176,26179,26181,26183,26186,26188,26191,26193,26196],{"class":2115,"line":2116},[2113,26165,2520],{"class":2334},[2113,26167,26168],{"class":2414},"model",[2113,26170,179],{"class":2119},[2113,26172,18053],{"class":2330},[2113,26174,26175],{"class":2326},"?",[2113,26177,26178],{"class":2330},"paths",[2113,26180,2335],{"class":2334},[2113,26182,4124],{"class":2119},[2113,26184,26185],{"class":2149},"\"user.name\"",[2113,26187,932],{"class":2119},[2113,26189,26190],{"class":2149},"\"user.surname\"",[2113,26192,932],{"class":2119},[2113,26194,26195],{"class":2149},"\"user.address\"",[2113,26197,26198],{"class":2119},"]\n",[2113,26200,26201],{"class":2115,"line":1647},[2113,26202,2125],{"emptyLinePlaceholder":1767},[2113,26204,26205,26208,26210,26212,26214,26216,26218,26220,26222,26224,26226,26228,26230,26232,26234],{"class":2115,"line":1652},[2113,26206,26207],{"class":2414},"GET",[2113,26209,9175],{"class":2334},[2113,26211,26168],{"class":2414},[2113,26213,179],{"class":2119},[2113,26215,18053],{"class":2330},[2113,26217,26175],{"class":2326},[2113,26219,26178],{"class":2330},[2113,26221,2335],{"class":2334},[2113,26223,4124],{"class":2119},[2113,26225,26185],{"class":2149},[2113,26227,932],{"class":2119},[2113,26229,26190],{"class":2149},[2113,26231,932],{"class":2119},[2113,26233,26195],{"class":2149},[2113,26235,26198],{"class":2119},[2113,26237,26238],{"class":2115,"line":2185},[2113,26239,18060],{"class":2119},[2113,26241,26242,26245],{"class":2115,"line":2230},[2113,26243,26244],{"class":2330},"  user",[2113,26246,8091],{"class":2119},[2113,26248,26249,26251,26253,26256],{"class":2115,"line":2235},[2113,26250,22279],{"class":2330},[2113,26252,4429],{"class":2119},[2113,26254,26255],{"class":2149},"\"Frank\"",[2113,26257,4706],{"class":2119},[2113,26259,26260,26263,26265,26268],{"class":2115,"line":2241},[2113,26261,26262],{"class":2330},"    surname",[2113,26264,4429],{"class":2119},[2113,26266,26267],{"class":2149},"\"Underwood\"",[2113,26269,4706],{"class":2119},[2113,26271,26272,26275,26277],{"class":2115,"line":2246},[2113,26273,26274],{"class":2330},"    address",[2113,26276,4429],{"class":2119},[2113,26278,26279],{"class":2149},"\"1600 Pennsylvania Avenue, Washington, DC\"\n",[2113,26281,26282],{"class":2115,"line":2464},[2113,26283,2572],{"class":2119},[2113,26285,26286],{"class":2115,"line":2085},[2113,26287,2599],{"class":2119},[12,26289,1583,26290,26293],{},[184,26291,26292],{},"Router"," falcor côté serveur, s'occupe alors de dispatcher les différentes parties demandées par le client à différents backends, qui peuvent\nalors répondre indépendament les uns des autres.\nFalcor va s'occuper alors d'aggréger le résultat.",[12,26295,26296,179],{},[121,26297],{"alt":26298,"src":26299},"Diagramme de service","https:\u002F\u002Fnetflix.github.io\u002Ffalcor\u002Fimages\u002Fservices-diagram.png",[12,26301,26302],{},"Pour récupérer des données depuis le front:",[2105,26304,26306],{"className":7776,"code":26305,"language":7778,"meta":1646,"style":1646},"\u002F\u002F ask for name and age of user with id = 5\nmodel.get(\"users[5]['name','age']\");\u002F\u002F which will eventually return something like\n{\n  \"users\": {\n    \"5\": {\n      \"name\": \"John Doe\",\n      \"age\": 33\n    }\n  }\n}\u002F\u002F you can also ask for ranges\nmodel.get(\"users[5..7].name);\u002F\u002F which eventually returns the following\n{\n  \"users\": {\n    \"5\": { \"name\": \"John Doe\" },\n    \"6\": { \"name\": \"Jane Doe\" },\n    \"7\": { \"name\": \"Mary Poppins\" }\n  }\n}\n",[184,26307,26308,26313,26332,26336,26343,26350,26362,26372,26376,26380,26387,26403,26407,26413,26428,26444,26461,26465],{"__ignoreMap":1646},[2113,26309,26310],{"class":2115,"line":2116},[2113,26311,26312],{"class":2396},"\u002F\u002F ask for name and age of user with id = 5\n",[2113,26314,26315,26317,26319,26321,26323,26326,26329],{"class":2115,"line":1647},[2113,26316,26168],{"class":2414},[2113,26318,179],{"class":2119},[2113,26320,19283],{"class":2133},[2113,26322,2423],{"class":2119},[2113,26324,26325],{"class":2149},"\"users[5]['name','age']\"",[2113,26327,26328],{"class":2119},");",[2113,26330,26331],{"class":2396},"\u002F\u002F which will eventually return something like\n",[2113,26333,26334],{"class":2115,"line":1652},[2113,26335,18060],{"class":2119},[2113,26337,26338,26341],{"class":2115,"line":2185},[2113,26339,26340],{"class":2149},"  \"users\"",[2113,26342,8091],{"class":2119},[2113,26344,26345,26348],{"class":2115,"line":2230},[2113,26346,26347],{"class":2149},"    \"5\"",[2113,26349,8091],{"class":2119},[2113,26351,26352,26355,26357,26360],{"class":2115,"line":2235},[2113,26353,26354],{"class":2149},"      \"name\"",[2113,26356,4429],{"class":2119},[2113,26358,26359],{"class":2149},"\"John Doe\"",[2113,26361,4706],{"class":2119},[2113,26363,26364,26367,26369],{"class":2115,"line":2241},[2113,26365,26366],{"class":2149},"      \"age\"",[2113,26368,4429],{"class":2119},[2113,26370,26371],{"class":2166},"33\n",[2113,26373,26374],{"class":2115,"line":2246},[2113,26375,4665],{"class":2119},[2113,26377,26378],{"class":2115,"line":2464},[2113,26379,2572],{"class":2119},[2113,26381,26382,26384],{"class":2115,"line":2085},[2113,26383,15888],{"class":2119},[2113,26385,26386],{"class":2396},"\u002F\u002F you can also ask for ranges\n",[2113,26388,26389,26391,26393,26395,26397,26400],{"class":2115,"line":2514},[2113,26390,26168],{"class":2414},[2113,26392,179],{"class":2119},[2113,26394,19283],{"class":2133},[2113,26396,2423],{"class":2119},[2113,26398,26399],{"class":2149},"\"users[5..7].name);\u002F\u002F which eventually returns the followin",[2113,26401,26402],{"class":19134},"g\n",[2113,26404,26405],{"class":2115,"line":2533},[2113,26406,18060],{"class":2119},[2113,26408,26409,26411],{"class":2115,"line":2556},[2113,26410,26340],{"class":2149},[2113,26412,8091],{"class":2119},[2113,26414,26415,26417,26419,26422,26424,26426],{"class":2115,"line":2569},[2113,26416,26347],{"class":2149},[2113,26418,8099],{"class":2119},[2113,26420,26421],{"class":2149},"\"name\"",[2113,26423,4429],{"class":2119},[2113,26425,26359],{"class":2149},[2113,26427,8129],{"class":2119},[2113,26429,26430,26433,26435,26437,26439,26442],{"class":2115,"line":2575},[2113,26431,26432],{"class":2149},"    \"6\"",[2113,26434,8099],{"class":2119},[2113,26436,26421],{"class":2149},[2113,26438,4429],{"class":2119},[2113,26440,26441],{"class":2149},"\"Jane Doe\"",[2113,26443,8129],{"class":2119},[2113,26445,26446,26449,26451,26453,26455,26458],{"class":2115,"line":2596},[2113,26447,26448],{"class":2149},"    \"7\"",[2113,26450,8099],{"class":2119},[2113,26452,26421],{"class":2149},[2113,26454,4429],{"class":2119},[2113,26456,26457],{"class":2149},"\"Mary Poppins\"",[2113,26459,26460],{"class":2119}," }\n",[2113,26462,26463],{"class":2115,"line":3192},[2113,26464,2572],{"class":2119},[2113,26466,26467],{"class":2115,"line":3213},[2113,26468,2599],{"class":2119},[12,26470,26471],{},"L'avantage de ce framework, est qu'il permet de simplifier l'écriture des projections et de la partie lecture d'une API.",[12,26473,26474],{},"Ce pourquoi je n'ai pas choisi ce framework vient en deux choses:",[506,26476,26477,26480],{},[370,26478,26479],{},"Il est disponible principalement pour Javascript (NodeJS + Front) ; Il existe une version Java même qui n'a pas été mise à jour\ndepuis 2018 et donc je ne retrouve pas les sources.",[370,26481,26482],{},"J'ai trouvé mieux en GraphQL.",[128,26484,26486],{"id":26485},"quest-quune-api-graphql","Qu'est qu'une API GraphQL",[12,26488,26489],{},"Venant du constat que Falcor répondait à mon besoin de pouvoir normaliser la projection mais avec quelques limites, j'ai continué mes\nrecherches et je suis tombé sur GraphQL.",[12,26491,26492],{},"GraphQL répond à la même problèmatique: pouvoir laisser au client choisir la projection qu'il souhaite des données. Comme Falcor,\nGraphQL permet de ramener plusieurs ressources en une requête. Et comme Falcor, GraphQL permet d'aggréger le résultat côté serveur\nde façon asynchrone.",[12,26494,26495],{},"L'avantage de GraphQL sur Falcor est que GraphQL est une norme écrite par Facebook alors que Falcor est une librairie Javascript.\nDe la norme GraphQL, découle une implémentation officielle en Javascript mais aussi dans plein d'autres languages.",[12,26497,26498],{},"Par contre GraphQL n'est qu'un language de requête. Il ne décrit pas comment doit être transportée la requête sur le réseau, ni\ncomment doit être transférée la réponse. Seul le contenu est normalisé. Les frameworks sont tout de même compatibles entre eux.",[12,26500,26501],{},"Par contre cela permet d'utiliser GraphQL pour autre chose que des requêtes réseaux. On pourrait envisager de faire un service qui\nne répond qu'à des requêtes GraphQL. Ce service est alors rattaché au contrôleur pour une exposition mais aussi directement\nappelable en interne par d'autres services. Cela pourrait permettre de faire une couche d'abstraction interne.",[12,26503,26504,26505,179],{},"Il existe plusieurs frameworks ajoutant la couche de transport à GraphQL. Dans la suite je parlerai d'une des implémentations qui se\nnomme ",[49,26506,26509],{"href":26507,"rel":26508},"https:\u002F\u002Fwww.apollographql.com\u002F",[347],"Apollo",[12,26511,26512],{},"Maintenant passons au vif du sujet.",[133,26514,26516],{"id":26515},"query-introduction"," Query - Introduction",[12,26518,26519],{},"Le système de requête de graphql permet au client de décrire ce qu'il souhaite récupérer. C'est au client de décider des éléments\nqu'il souhaite et de construire sa requête.",[12,26521,26522],{},"Voici un exemple de requête de requête:",[2105,26524,26528],{"className":26525,"code":26526,"language":26527,"meta":1646,"style":1646},"language-graphql shiki shiki-themes one-dark-pro","query Dashboard {\n  queueStats {\n    waiting\n    active\n    failed\n    lastExecution\n    nextWakeup\n  }\n  diskUsageStats {\n    currentRepartition {\n      host\n      total\n    }\n    currentSpace {\n      size\n      used\n    }\n  }\n}\n","graphql",[184,26529,26530,26540,26547,26552,26557,26562,26567,26572,26576,26583,26590,26595,26600,26604,26611,26616,26621,26625,26629],{"__ignoreMap":1646},[2113,26531,26532,26535,26538],{"class":2115,"line":2116},[2113,26533,26534],{"class":2326},"query",[2113,26536,26537],{"class":2133}," Dashboard",[2113,26539,2647],{"class":2119},[2113,26541,26542,26545],{"class":2115,"line":1647},[2113,26543,26544],{"class":2330},"  queueStats",[2113,26546,2647],{"class":2119},[2113,26548,26549],{"class":2115,"line":1652},[2113,26550,26551],{"class":2330},"    waiting\n",[2113,26553,26554],{"class":2115,"line":2185},[2113,26555,26556],{"class":2330},"    active\n",[2113,26558,26559],{"class":2115,"line":2230},[2113,26560,26561],{"class":2330},"    failed\n",[2113,26563,26564],{"class":2115,"line":2235},[2113,26565,26566],{"class":2330},"    lastExecution\n",[2113,26568,26569],{"class":2115,"line":2241},[2113,26570,26571],{"class":2330},"    nextWakeup\n",[2113,26573,26574],{"class":2115,"line":2246},[2113,26575,2572],{"class":2119},[2113,26577,26578,26581],{"class":2115,"line":2464},[2113,26579,26580],{"class":2330},"  diskUsageStats",[2113,26582,2647],{"class":2119},[2113,26584,26585,26588],{"class":2115,"line":2085},[2113,26586,26587],{"class":2330},"    currentRepartition",[2113,26589,2647],{"class":2119},[2113,26591,26592],{"class":2115,"line":2514},[2113,26593,26594],{"class":2330},"      host\n",[2113,26596,26597],{"class":2115,"line":2533},[2113,26598,26599],{"class":2330},"      total\n",[2113,26601,26602],{"class":2115,"line":2556},[2113,26603,4665],{"class":2119},[2113,26605,26606,26609],{"class":2115,"line":2569},[2113,26607,26608],{"class":2330},"    currentSpace",[2113,26610,2647],{"class":2119},[2113,26612,26613],{"class":2115,"line":2575},[2113,26614,26615],{"class":2330},"      size\n",[2113,26617,26618],{"class":2115,"line":2596},[2113,26619,26620],{"class":2330},"      used\n",[2113,26622,26623],{"class":2115,"line":3192},[2113,26624,4665],{"class":2119},[2113,26626,26627],{"class":2115,"line":3213},[2113,26628,2572],{"class":2119},[2113,26630,26631],{"class":2115,"line":3236},[2113,26632,2599],{"class":2119},[12,26634,26635],{},"Derrière chaqu'un des membres ci-dessus, on peut retrouver un resolver. Un resolver c'est l'équivalent du contrôleur pour une API REST.\nC'est le resolver qui va récupérer les informations demandées par le client (arguments, projection, ...) et les transférer au service.",[12,26637,26638],{},"Il nous faudra alors faire un lien entre le schéma et ces resolvers. Pour le client, peu importe qu'il faille, pour une requête, exécuter\nen tâche de fond 1 resolver ou 10 resolver. Le client n'a pas besoin de le savoir.",[12,26640,26641,26642,26645],{},"Imaginons que dans l'exemple ci-dessous le champs ",[184,26643,26644],{},"nextWakeup"," nécessite un calcul complexe. Il suffit de créer un resolver qui\neffectue ce calcul.\nSi jamais le client n'a pas besoin de l'afficher et du coup, ne le demande pas, alors ce calcul complexe ne sera pas fait et c'est du temps\nde traitement gagné sur le serveur.",[12,26647,26648],{},"En REST, un champ qui nécessite un calcul complexe et soit",[506,26650,26651,26654],{},[370,26652,26653],{},"inclus dans la resource et donc toujours effectué quelque soit le besoin du client",[370,26655,26656],{},"séparé dans une ressource séparée, au risque de complexifier l'API et de demander au client de faire plusieurs requêtes si nécessaire (surtout si\non a plus d'un champ calculé).",[133,26658,26660],{"id":26659},"nommer-ces-requêtes","Nommer ces requêtes",[12,26662,26663,26664,26667],{},"Dans l'exemple ci-dessus, on peut remarquer que la requête possède un nom. C'est une bonne pratique de toujours nommer l'opération\n(ici ",[184,26665,26666],{},"Dashboard","). Nommer les opérations permet de :",[506,26669,26670,26673],{},[370,26671,26672],{},"mettre plusieurs opérations nommées dans une seule requête.",[370,26674,26675],{},"mais aussi de mieux tracer et débugger les requêtes côté serveur dans les logs.",[133,26677,26679],{"id":26678},"query-coté-serveur","Query - Coté serveur",[12,26681,26682],{},"Côté serveur nous allons commencer par décrire le schéma que pourra alors utiliser le client pour effectuer ces requêtes. Par exemple :",[2105,26684,26686],{"className":26525,"code":26685,"language":26527,"meta":1646,"style":1646},"scalar Date\n\n\"\"\"\nDefine the state of the queue\n\"\"\"\ntype QueueStats {\n  \"\"\"\n  Number of task waiting in queue\n  \"\"\"\n  waiting: Int!\n  \"\"\"\n  Number of task active in queue\n  \"\"\"\n  active: Int!\n  \"\"\"\n  Number of task that have failed\n  \"\"\"\n  failed: Int!\n  \"\"\"\n  Date of the last execution\n  \"\"\"\n  lastExecution: Date!\n  \"\"\"\n  Date of the next wakeup\n  \"\"\"\n  nextWakeup: Date!\n}\n\ntype DiskCurrentRepartition {\n  host: Int!\n  total: Int!\n}\n\ntype DiskCurrentSpace {\n  size: Int!\n  used: Int!\n}\n\ntype DiskUsageStats {\n  currentRepartition: [DiskCurrentRepartition!]!\n  currentSpace: DiskCurrentSpace!\n}\n\ntype Query {\n  queueStats: QueueStats!\n  diskUsageState: DiskUsageStats!\n}\n",[184,26687,26688,26696,26700,26705,26710,26714,26721,26726,26731,26735,26743,26747,26752,26756,26763,26767,26772,26776,26783,26787,26792,26796,26804,26808,26813,26817,26824,26828,26832,26839,26846,26853,26857,26861,26868,26874,26881,26885,26889,26896,26904,26912,26916,26920,26927,26934,26942],{"__ignoreMap":1646},[2113,26689,26690,26693],{"class":2115,"line":2116},[2113,26691,26692],{"class":2326},"scalar",[2113,26694,26695],{"class":2119}," Date\n",[2113,26697,26698],{"class":2115,"line":1647},[2113,26699,2125],{"emptyLinePlaceholder":1767},[2113,26701,26702],{"class":2115,"line":1652},[2113,26703,26704],{"class":2396},"\"\"\"\n",[2113,26706,26707],{"class":2115,"line":2185},[2113,26708,26709],{"class":2396},"Define the state of the queue\n",[2113,26711,26712],{"class":2115,"line":2230},[2113,26713,26704],{"class":2396},[2113,26715,26716,26718],{"class":2115,"line":2235},[2113,26717,21494],{"class":2326},[2113,26719,26720],{"class":2119}," QueueStats {\n",[2113,26722,26723],{"class":2115,"line":2241},[2113,26724,26725],{"class":2396},"  \"\"\"\n",[2113,26727,26728],{"class":2115,"line":2246},[2113,26729,26730],{"class":2396},"  Number of task waiting in queue\n",[2113,26732,26733],{"class":2115,"line":2464},[2113,26734,26725],{"class":2396},[2113,26736,26737,26740],{"class":2115,"line":2085},[2113,26738,26739],{"class":2330},"  waiting",[2113,26741,26742],{"class":2119},": Int!\n",[2113,26744,26745],{"class":2115,"line":2514},[2113,26746,26725],{"class":2396},[2113,26748,26749],{"class":2115,"line":2533},[2113,26750,26751],{"class":2396},"  Number of task active in queue\n",[2113,26753,26754],{"class":2115,"line":2556},[2113,26755,26725],{"class":2396},[2113,26757,26758,26761],{"class":2115,"line":2569},[2113,26759,26760],{"class":2330},"  active",[2113,26762,26742],{"class":2119},[2113,26764,26765],{"class":2115,"line":2575},[2113,26766,26725],{"class":2396},[2113,26768,26769],{"class":2115,"line":2596},[2113,26770,26771],{"class":2396},"  Number of task that have failed\n",[2113,26773,26774],{"class":2115,"line":3192},[2113,26775,26725],{"class":2396},[2113,26777,26778,26781],{"class":2115,"line":3213},[2113,26779,26780],{"class":2330},"  failed",[2113,26782,26742],{"class":2119},[2113,26784,26785],{"class":2115,"line":3236},[2113,26786,26725],{"class":2396},[2113,26788,26789],{"class":2115,"line":3248},[2113,26790,26791],{"class":2396},"  Date of the last execution\n",[2113,26793,26794],{"class":2115,"line":4899},[2113,26795,26725],{"class":2396},[2113,26797,26798,26801],{"class":2115,"line":1777},[2113,26799,26800],{"class":2330},"  lastExecution",[2113,26802,26803],{"class":2119},": Date!\n",[2113,26805,26806],{"class":2115,"line":4931},[2113,26807,26725],{"class":2396},[2113,26809,26810],{"class":2115,"line":4961},[2113,26811,26812],{"class":2396},"  Date of the next wakeup\n",[2113,26814,26815],{"class":2115,"line":4976},[2113,26816,26725],{"class":2396},[2113,26818,26819,26822],{"class":2115,"line":4993},[2113,26820,26821],{"class":2330},"  nextWakeup",[2113,26823,26803],{"class":2119},[2113,26825,26826],{"class":2115,"line":5013},[2113,26827,2599],{"class":2119},[2113,26829,26830],{"class":2115,"line":5018},[2113,26831,2125],{"emptyLinePlaceholder":1767},[2113,26833,26834,26836],{"class":2115,"line":5042},[2113,26835,21494],{"class":2326},[2113,26837,26838],{"class":2119}," DiskCurrentRepartition {\n",[2113,26840,26841,26844],{"class":2115,"line":5057},[2113,26842,26843],{"class":2330},"  host",[2113,26845,26742],{"class":2119},[2113,26847,26848,26851],{"class":2115,"line":5062},[2113,26849,26850],{"class":2330},"  total",[2113,26852,26742],{"class":2119},[2113,26854,26855],{"class":2115,"line":5098},[2113,26856,2599],{"class":2119},[2113,26858,26859],{"class":2115,"line":5117},[2113,26860,2125],{"emptyLinePlaceholder":1767},[2113,26862,26863,26865],{"class":2115,"line":5142},[2113,26864,21494],{"class":2326},[2113,26866,26867],{"class":2119}," DiskCurrentSpace {\n",[2113,26869,26870,26872],{"class":2115,"line":5148},[2113,26871,11661],{"class":2330},[2113,26873,26742],{"class":2119},[2113,26875,26876,26879],{"class":2115,"line":5163},[2113,26877,26878],{"class":2330},"  used",[2113,26880,26742],{"class":2119},[2113,26882,26883],{"class":2115,"line":5169},[2113,26884,2599],{"class":2119},[2113,26886,26887],{"class":2115,"line":5175},[2113,26888,2125],{"emptyLinePlaceholder":1767},[2113,26890,26891,26893],{"class":2115,"line":5180},[2113,26892,21494],{"class":2326},[2113,26894,26895],{"class":2119}," DiskUsageStats {\n",[2113,26897,26898,26901],{"class":2115,"line":5199},[2113,26899,26900],{"class":2330},"  currentRepartition",[2113,26902,26903],{"class":2119},": [DiskCurrentRepartition!]!\n",[2113,26905,26906,26909],{"class":2115,"line":5204},[2113,26907,26908],{"class":2330},"  currentSpace",[2113,26910,26911],{"class":2119},": DiskCurrentSpace!\n",[2113,26913,26914],{"class":2115,"line":5209},[2113,26915,2599],{"class":2119},[2113,26917,26918],{"class":2115,"line":5232},[2113,26919,2125],{"emptyLinePlaceholder":1767},[2113,26921,26922,26924],{"class":2115,"line":5237},[2113,26923,21494],{"class":2326},[2113,26925,26926],{"class":2119}," Query {\n",[2113,26928,26929,26931],{"class":2115,"line":5242},[2113,26930,26544],{"class":2330},[2113,26932,26933],{"class":2119},": QueueStats!\n",[2113,26935,26936,26939],{"class":2115,"line":5267},[2113,26937,26938],{"class":2330},"  diskUsageState",[2113,26940,26941],{"class":2119},": DiskUsageStats!\n",[2113,26943,26944],{"class":2115,"line":5282},[2113,26945,2599],{"class":2119},[12,26947,26948],{},"On peut remarquer plusieurs choses:",[506,26950,26951,26954,26957,26960,26963],{},[370,26952,26953],{},"Tous les champs ont un type (String, Int, Float, Array, Date)",[370,26955,26956],{},"Il est possible de créer de nouveaux types simples, scalaires: par exemple champ de type Date.",[370,26958,26959],{},"Il est possible de créer de nouveaux types complexes, ce qui en fait nos structures.",[370,26961,26962],{},"Le point d'exclamation permet d'indiquer que le champ retourné ne sera jamais null (et donc le client peut compter dessus s'il le souhaite).",[370,26964,26965],{},"Il est possible de mettre de la documentation au niveau de chaque champ mais aussi au niveau des types.",[12,26967,26968,26969,26974],{},"Ce schéma (qui peut aussi être généré à l'aide d'annotations en Typescript, ",[49,26970,26973],{"href":26971,"rel":26972},"https:\u002F\u002Fdocs.nestjs.com\u002Fgraphql\u002Fresolvers#code-first-resolver",[347],"par exemple",")\nsert également de documentation (un peu comme swagger).",[12,26976,26977],{},"Il est alors possible d'utiliser des outils comme",[506,26979,26980,26988,26995,27003],{},[370,26981,26982,26987],{},[49,26983,26986],{"href":26984,"rel":26985},"https:\u002F\u002F2fd.github.io\u002Fgraphdoc\u002F",[347],"Graph Doc",",",[370,26989,26990,26987],{},[49,26991,26994],{"href":26992,"rel":26993},"https:\u002F\u002Fdocql.io\u002F",[347],"DocQL",[370,26996,26997,27002],{},[49,26998,27001],{"href":26999,"rel":27000},"https:\u002F\u002Fgithub.com\u002FAPIs-guru\u002Fgraphql-voyager",[347],"GraphQL Voyager",", et",[370,27004,27005],{},[49,27006,27009],{"href":27007,"rel":27008},"https:\u002F\u002Fgithub.com\u002Fwayfair\u002Fdociql",[347],"Doc iQL",[12,27011,27012],{},"pour générer de la documentation mais aussi des outils comme",[506,27014,27015,27022],{},[370,27016,27017],{},[49,27018,27021],{"href":27019,"rel":27020},"https:\u002F\u002Fgithub.com\u002Fgraphql\u002Fgraphql-playground",[347],"GraphQL Playground",[370,27023,27024,27029],{},[49,27025,27028],{"href":27026,"rel":27027},"https:\u002F\u002Fgithub.com\u002Fgraphql\u002Fgraphiql",[347],"GraphiQL"," qui sont des IDE permettant de faire des requêtes (avec documentation et auto-completion).",[12,27031,27032],{},"Une fois le schéma écrit, il peut être communiqué aux équipes front (si les équipes sont séparées). Pendant que l'on développe alors le front, côté\nserveur on peut alors implémenter le schéma.",[12,27034,27035],{},"Pour implémenter le schéma ci-dessus, nous allons écrire un resolver (en Javascript pour l'exemple).",[2105,27037,27039],{"className":7776,"code":27038,"language":7778,"meta":1646,"style":1646},"const resolvers = {\n  Query: {\n    queueStats() {\n      return getQueueStats();\n    },\n    async diskUsageState() {\n      return await getDiskUsageState();\n    },\n  },\n};\n",[184,27040,27041,27052,27059,27066,27075,27079,27089,27100,27104,27108],{"__ignoreMap":1646},[2113,27042,27043,27045,27048,27050],{"class":2115,"line":2116},[2113,27044,7785],{"class":2326},[2113,27046,27047],{"class":2414}," resolvers",[2113,27049,2153],{"class":2334},[2113,27051,2647],{"class":2119},[2113,27053,27054,27057],{"class":2115,"line":1647},[2113,27055,27056],{"class":2330},"  Query",[2113,27058,8091],{"class":2119},[2113,27060,27061,27064],{"class":2115,"line":1652},[2113,27062,27063],{"class":2133},"    queueStats",[2113,27065,3912],{"class":2119},[2113,27067,27068,27070,27073],{"class":2115,"line":2185},[2113,27069,20652],{"class":2326},[2113,27071,27072],{"class":2133}," getQueueStats",[2113,27074,3944],{"class":2119},[2113,27076,27077],{"class":2115,"line":2230},[2113,27078,8237],{"class":2119},[2113,27080,27081,27084,27087],{"class":2115,"line":2235},[2113,27082,27083],{"class":2326},"    async",[2113,27085,27086],{"class":2133}," diskUsageState",[2113,27088,3912],{"class":2119},[2113,27090,27091,27093,27095,27098],{"class":2115,"line":2241},[2113,27092,20652],{"class":2326},[2113,27094,17521],{"class":2326},[2113,27096,27097],{"class":2133}," getDiskUsageState",[2113,27099,3944],{"class":2119},[2113,27101,27102],{"class":2115,"line":2246},[2113,27103,8237],{"class":2119},[2113,27105,27106],{"class":2115,"line":2464},[2113,27107,8490],{"class":2119},[2113,27109,27110],{"class":2115,"line":2085},[2113,27111,2787],{"class":2119},[12,27113,27114],{},"Un resolver peut pour un champ:",[506,27116,27117,27120,27123],{},[370,27118,27119],{},"retourner une valeur",[370,27121,27122],{},"retourner une fonction qui retourne la valeur",[370,27124,27125],{},"retourner une fonction qui retourne une promesse avec la valeur",[12,27127,27128,27129,27132,27133,792,27135,2384],{},"Ainsi imaginons que la méthode ",[184,27130,27131],{},"getQueueStats()"," retourne l'objet suivant, dans lequel il manque ",[184,27134,26644],{},[184,27136,27137],{},"lastExecution",[2105,27139,27141],{"className":18051,"code":27140,"language":18053,"meta":1646,"style":1646},"{\n  \"waiting\": 0,\n  \"active\": 2,\n  \"failed\": 0\n}\n",[184,27142,27143,27147,27158,27169,27178],{"__ignoreMap":1646},[2113,27144,27145],{"class":2115,"line":2116},[2113,27146,18060],{"class":2119},[2113,27148,27149,27152,27154,27156],{"class":2115,"line":1647},[2113,27150,27151],{"class":2330},"  \"waiting\"",[2113,27153,4429],{"class":2119},[2113,27155,3095],{"class":2166},[2113,27157,4706],{"class":2119},[2113,27159,27160,27163,27165,27167],{"class":2115,"line":1652},[2113,27161,27162],{"class":2330},"  \"active\"",[2113,27164,4429],{"class":2119},[2113,27166,2547],{"class":2166},[2113,27168,4706],{"class":2119},[2113,27170,27171,27174,27176],{"class":2115,"line":2185},[2113,27172,27173],{"class":2330},"  \"failed\"",[2113,27175,4429],{"class":2119},[2113,27177,18077],{"class":2166},[2113,27179,27180],{"class":2115,"line":2230},[2113,27181,2599],{"class":2119},[12,27183,27184],{},"Il est possible d'écrire un resolver :",[2105,27186,27188],{"className":7776,"code":27187,"language":7778,"meta":1646,"style":1646},"const resolvers = {\n  Query: {\n    queueStats() {\n      return getQueueStats();\n    },\n    async diskUsageState() {\n      return await getDiskUsageState();\n    },\n  },\n  QueueStats: {\n    lastExecution(parent \u002F*: QueueStats *\u002F) {\n      const { active } = parent; \u002F\u002F Ici pour l'exemple on peut récuperer un attribut de parent.\n      return getLastExecution();\n    },\n    nextWakeup() {\n      return getNextWakup();\n    },\n  },\n};\n",[184,27189,27190,27200,27206,27212,27220,27224,27232,27242,27246,27250,27257,27272,27293,27302,27306,27313,27322,27326,27330],{"__ignoreMap":1646},[2113,27191,27192,27194,27196,27198],{"class":2115,"line":2116},[2113,27193,7785],{"class":2326},[2113,27195,27047],{"class":2414},[2113,27197,2153],{"class":2334},[2113,27199,2647],{"class":2119},[2113,27201,27202,27204],{"class":2115,"line":1647},[2113,27203,27056],{"class":2330},[2113,27205,8091],{"class":2119},[2113,27207,27208,27210],{"class":2115,"line":1652},[2113,27209,27063],{"class":2133},[2113,27211,3912],{"class":2119},[2113,27213,27214,27216,27218],{"class":2115,"line":2185},[2113,27215,20652],{"class":2326},[2113,27217,27072],{"class":2133},[2113,27219,3944],{"class":2119},[2113,27221,27222],{"class":2115,"line":2230},[2113,27223,8237],{"class":2119},[2113,27225,27226,27228,27230],{"class":2115,"line":2235},[2113,27227,27083],{"class":2326},[2113,27229,27086],{"class":2133},[2113,27231,3912],{"class":2119},[2113,27233,27234,27236,27238,27240],{"class":2115,"line":2241},[2113,27235,20652],{"class":2326},[2113,27237,17521],{"class":2326},[2113,27239,27097],{"class":2133},[2113,27241,3944],{"class":2119},[2113,27243,27244],{"class":2115,"line":2246},[2113,27245,8237],{"class":2119},[2113,27247,27248],{"class":2115,"line":2464},[2113,27249,8490],{"class":2119},[2113,27251,27252,27255],{"class":2115,"line":2085},[2113,27253,27254],{"class":2330},"  QueueStats",[2113,27256,8091],{"class":2119},[2113,27258,27259,27262,27264,27267,27270],{"class":2115,"line":2514},[2113,27260,27261],{"class":2133},"    lastExecution",[2113,27263,2423],{"class":2119},[2113,27265,27266],{"class":2429},"parent",[2113,27268,27269],{"class":2396}," \u002F*: QueueStats *\u002F",[2113,27271,2433],{"class":2119},[2113,27273,27274,27276,27278,27281,27283,27285,27288,27290],{"class":2115,"line":2533},[2113,27275,20579],{"class":2326},[2113,27277,7616],{"class":2119},[2113,27279,27280],{"class":2414},"active",[2113,27282,7840],{"class":2119},[2113,27284,2335],{"class":2334},[2113,27286,27287],{"class":2330}," parent",[2113,27289,6160],{"class":2119},[2113,27291,27292],{"class":2396},"\u002F\u002F Ici pour l'exemple on peut récuperer un attribut de parent.\n",[2113,27294,27295,27297,27300],{"class":2115,"line":2556},[2113,27296,20652],{"class":2326},[2113,27298,27299],{"class":2133}," getLastExecution",[2113,27301,3944],{"class":2119},[2113,27303,27304],{"class":2115,"line":2569},[2113,27305,8237],{"class":2119},[2113,27307,27308,27311],{"class":2115,"line":2575},[2113,27309,27310],{"class":2133},"    nextWakeup",[2113,27312,3912],{"class":2119},[2113,27314,27315,27317,27320],{"class":2115,"line":2596},[2113,27316,20652],{"class":2326},[2113,27318,27319],{"class":2133}," getNextWakup",[2113,27321,3944],{"class":2119},[2113,27323,27324],{"class":2115,"line":3192},[2113,27325,8237],{"class":2119},[2113,27327,27328],{"class":2115,"line":3213},[2113,27329,8490],{"class":2119},[2113,27331,27332],{"class":2115,"line":3236},[2113,27333,2787],{"class":2119},[12,27335,27336],{},"Il est alors possible de créer son schéma en pensant à comment le client va intéroger ce dernier, et lors de l'implémentation ajouter des structures\ncomplexes qui sont issues du calcul synchrone ou asynchrone des données.",[133,27338,27340],{"id":27339},"query-nullable","Query - Nullable",[12,27342,27343],{},"On a vu précédement qu'on pouvait utiliser le point d'exclamation pour indiquer qu'un champ ne sera jamais NULL. Cela peut avoir des avantages pour le client\nmais cela a aussi de grandes implications côté serveur.",[12,27345,27346],{},"Par défaut pour GraphQL, tous les champs sont nullable par défaut. En effet, si un resolver n'arrive pas à récupérer la donnée (erreur côté serveur, back HS, problème\nde base de données, problème réseau, droits d'accès différents selon les champs ...), le serveur pourra retourner NULL à la place de la valeur (et une erreur en\nparallèle du json). Cela permet au client une vue partielle même si certains services ne sont pas disponibles.",[12,27348,27349],{},"Si le champ ne peut pas être null, alors le json ne pourra pas du tout être envoyé et c'est la requête complète qui est en ereur.",[12,27351,27352],{},"C'est pour cela qu'en GraphQL chaque champ peut, par défaut, obtenir la valeur null.",[12,27354,27355],{},"Lors de la conception d'un schéma GraphQL, il faut utiliser la possibilité de rendre le champ non nullable avec réflexion et uniquement pour les champs dont on\nsouhaite garantir la non nullité.",[133,27357,27359],{"id":27358},"query-ajout-darguments","Query - Ajout d'arguments",[2105,27361,27363],{"className":26525,"code":27362,"language":26527,"meta":1646,"style":1646},"query HeroNameAndFriends($episode: Episode) {\n  hero(episode: $episode) {\n    name\n    friends {\n      name\n    }\n  }\n}\n",[184,27364,27365,27380,27396,27401,27408,27413,27417,27421],{"__ignoreMap":1646},[2113,27366,27367,27369,27372,27374,27377],{"class":2115,"line":2116},[2113,27368,26534],{"class":2326},[2113,27370,27371],{"class":2133}," HeroNameAndFriends",[2113,27373,2423],{"class":2119},[2113,27375,27376],{"class":2429},"$episode",[2113,27378,27379],{"class":2119},": Episode) {\n",[2113,27381,27382,27385,27387,27390,27392,27394],{"class":2115,"line":1647},[2113,27383,27384],{"class":2330},"  hero",[2113,27386,2423],{"class":2119},[2113,27388,27389],{"class":2429},"episode",[2113,27391,4429],{"class":2119},[2113,27393,27376],{"class":2330},[2113,27395,2433],{"class":2119},[2113,27397,27398],{"class":2115,"line":1652},[2113,27399,27400],{"class":2330},"    name\n",[2113,27402,27403,27406],{"class":2115,"line":2185},[2113,27404,27405],{"class":2330},"    friends",[2113,27407,2647],{"class":2119},[2113,27409,27410],{"class":2115,"line":2230},[2113,27411,27412],{"class":2330},"      name\n",[2113,27414,27415],{"class":2115,"line":2235},[2113,27416,4665],{"class":2119},[2113,27418,27419],{"class":2115,"line":2241},[2113,27420,2572],{"class":2119},[2113,27422,27423],{"class":2115,"line":2246},[2113,27424,2599],{"class":2119},[12,27426,27427],{},"Il est possible en GraphQL de définir certain champs comme ayant des paramètres. Ils seront alors passés au resolver. Il est également possible d'avoir\ndes paramètres à différents niveaux du schéma (pas seulement au niveau le plus haut).",[12,27429,27430],{},"Le passage de paramètres permet d'écrire son opération une fois et ensuite de l'appeler avec des paramètres. C'est important de définir les saisie utilisateurs\ncomme des paramètres pour éviter les injections GraphQL (comme en SQL, ou autre).",[12,27432,27433,27436],{},[155,27434,27435],{},"Règle n°1",": Ne jamais faire confiance à l'utilisateur.",[133,27438,27440],{"id":27439},"query-création-dalias","Query - Création d'alias",[12,27442,27443],{},"Si on souhaite récupérer plusieurs valeurs d'un attribut en fonction de ses paramètres, il est possible de le demander plusieurs fois et de lui associer\nun alias.",[2105,27445,27447],{"className":26525,"code":27446,"language":26527,"meta":1646,"style":1646},"query aliasQuery {\n  empireHero: hero(episode: EMPIRE) {\n    name\n  }\n  jediHero: hero(episode: JEDI) {\n    name\n  }\n}\n",[184,27448,27449,27458,27479,27483,27487,27507,27511,27515],{"__ignoreMap":1646},[2113,27450,27451,27453,27456],{"class":2115,"line":2116},[2113,27452,26534],{"class":2326},[2113,27454,27455],{"class":2133}," aliasQuery",[2113,27457,2647],{"class":2119},[2113,27459,27460,27463,27465,27468,27470,27472,27474,27477],{"class":2115,"line":1647},[2113,27461,27462],{"class":2149},"  empireHero",[2113,27464,4429],{"class":2119},[2113,27466,27467],{"class":2330},"hero",[2113,27469,2423],{"class":2119},[2113,27471,27389],{"class":2429},[2113,27473,15746],{"class":2119},[2113,27475,27476],{"class":2166}," EMPIRE",[2113,27478,2433],{"class":2119},[2113,27480,27481],{"class":2115,"line":1652},[2113,27482,27400],{"class":2330},[2113,27484,27485],{"class":2115,"line":2185},[2113,27486,2572],{"class":2119},[2113,27488,27489,27492,27494,27496,27498,27500,27502,27505],{"class":2115,"line":2230},[2113,27490,27491],{"class":2149},"  jediHero",[2113,27493,4429],{"class":2119},[2113,27495,27467],{"class":2330},[2113,27497,2423],{"class":2119},[2113,27499,27389],{"class":2429},[2113,27501,15746],{"class":2119},[2113,27503,27504],{"class":2166}," JEDI",[2113,27506,2433],{"class":2119},[2113,27508,27509],{"class":2115,"line":2235},[2113,27510,27400],{"class":2330},[2113,27512,27513],{"class":2115,"line":2241},[2113,27514,2572],{"class":2119},[2113,27516,27517],{"class":2115,"line":2246},[2113,27518,2599],{"class":2119},[12,27520,27521,27522,792,27525,27528,27529,27531],{},"Dans le JSON résultant, on retrouve alors les deux attributs ",[184,27523,27524],{},"empireHero",[184,27526,27527],{},"jediHero"," qui sont tous les deux issus du membre ",[184,27530,27467],{}," avec un paramètre\ndifférent. Cela peut aussi être utilisé pour simplement renommer un champ.",[27533,27534,27536],"h4",{"id":27535},"query-fragment","Query - Fragment",[12,27538,27539],{},"Les fragments permettent de factoriser et de créer des morceaux de requêtes réutilisables.",[2105,27541,27543],{"className":26525,"code":27542,"language":26527,"meta":1646,"style":1646},"# Dans le fichier FragmentJob.graphql\n\nfragment FragmentJob on Job {\n  id\n  state\n  failedReason\n  data {\n    host\n  }\n}\n\n# Dans le fichier QueueTasks.graphql\n\n#import \".\u002FFragmentJob.graphql\"\n\nquery QueueTasks($state: [String!]) {\n  queue(state: $state) {\n    ...FragmentJob\n  }\n}\n",[184,27544,27545,27550,27554,27567,27572,27577,27582,27588,27593,27597,27601,27605,27610,27614,27619,27623,27638,27654,27661,27665],{"__ignoreMap":1646},[2113,27546,27547],{"class":2115,"line":2116},[2113,27548,27549],{"class":2396},"# Dans le fichier FragmentJob.graphql\n",[2113,27551,27552],{"class":2115,"line":1647},[2113,27553,2125],{"emptyLinePlaceholder":1767},[2113,27555,27556,27559,27562,27564],{"class":2115,"line":1652},[2113,27557,27558],{"class":2326},"fragment",[2113,27560,27561],{"class":2119}," FragmentJob ",[2113,27563,9030],{"class":2326},[2113,27565,27566],{"class":2119}," Job {\n",[2113,27568,27569],{"class":2115,"line":2185},[2113,27570,27571],{"class":2330},"  id\n",[2113,27573,27574],{"class":2115,"line":2230},[2113,27575,27576],{"class":2330},"  state\n",[2113,27578,27579],{"class":2115,"line":2235},[2113,27580,27581],{"class":2330},"  failedReason\n",[2113,27583,27584,27586],{"class":2115,"line":2241},[2113,27585,11787],{"class":2330},[2113,27587,2647],{"class":2119},[2113,27589,27590],{"class":2115,"line":2246},[2113,27591,27592],{"class":2330},"    host\n",[2113,27594,27595],{"class":2115,"line":2464},[2113,27596,2572],{"class":2119},[2113,27598,27599],{"class":2115,"line":2085},[2113,27600,2599],{"class":2119},[2113,27602,27603],{"class":2115,"line":2514},[2113,27604,2125],{"emptyLinePlaceholder":1767},[2113,27606,27607],{"class":2115,"line":2533},[2113,27608,27609],{"class":2396},"# Dans le fichier QueueTasks.graphql\n",[2113,27611,27612],{"class":2115,"line":2556},[2113,27613,2125],{"emptyLinePlaceholder":1767},[2113,27615,27616],{"class":2115,"line":2569},[2113,27617,27618],{"class":2396},"#import \".\u002FFragmentJob.graphql\"\n",[2113,27620,27621],{"class":2115,"line":2575},[2113,27622,2125],{"emptyLinePlaceholder":1767},[2113,27624,27625,27627,27630,27632,27635],{"class":2115,"line":2596},[2113,27626,26534],{"class":2326},[2113,27628,27629],{"class":2133}," QueueTasks",[2113,27631,2423],{"class":2119},[2113,27633,27634],{"class":2429},"$state",[2113,27636,27637],{"class":2119},": [String!]) {\n",[2113,27639,27640,27643,27645,27648,27650,27652],{"class":2115,"line":3192},[2113,27641,27642],{"class":2330},"  queue",[2113,27644,2423],{"class":2119},[2113,27646,27647],{"class":2429},"state",[2113,27649,4429],{"class":2119},[2113,27651,27634],{"class":2330},[2113,27653,2433],{"class":2119},[2113,27655,27656,27658],{"class":2115,"line":3213},[2113,27657,22858],{"class":2119},[2113,27659,27660],{"class":2330},"FragmentJob\n",[2113,27662,27663],{"class":2115,"line":3236},[2113,27664,2572],{"class":2119},[2113,27666,27667],{"class":2115,"line":3248},[2113,27668,2599],{"class":2119},[12,27670,27671,27672,27675],{},"Le Fragment ",[184,27673,27674],{},"FragmentJob"," peut alors être réutilisé dans différentes requêtes, voir même plusieurs fois dans la même requête.",[133,27677,27679],{"id":27678},"query-gestion-de-version","Query - Gestion de version",[12,27681,27682],{},"J'en parlais dans les articles précédents, il existe plusieurs manières de versionner une API. GraphQL n'y échappe pas. On peut:",[506,27684,27685,27688],{},[370,27686,27687],{},"ajouter un numéro de version dans le path de l'url, dans le nom de l'hôte, dans un header http.",[370,27689,27690],{},"ou décider de gérer une seule API rétro-compatible où on décommissionne au fur et à mesure les champs.",[12,27692,27693,27694,27699,27700,27703],{},"Les créateurs de GraphQL partagent ",[49,27695,27698],{"href":27696,"rel":27697},"https:\u002F\u002Fgraphql.org\u002Flearn\u002Fbest-practices\u002F#versioning",[347],"leur opinions",". Comme c'est le client qui décide\ndes champs qu'il souhaite rappatrier, toute évolution du schéma ajoutant de nouveaux attributs ne pose aucun problème et ne surchargera pas\nplus le client.\nLes suppressions doivent passer par une phase d'obsolescence ou les attributs sont marqués avec l'annotation ",[184,27701,27702],{},"@deprecated"," et leur décommissionnement\nautomatique.",[12,27705,27706,27707,27712],{},"Afin de pouvoir facilement décommissionner des attributs, le mieux est de savoir quels attributs sont utilisés ou non. La librairie Apollo permet de\nse connecter au ",[49,27708,27711],{"href":27709,"rel":27710},"https:\u002F\u002Fstudio.apollographql.com\u002F",[347],"studio d'appolo"," et de visualiser l'utilisation des attributs. Savoir qu'un attribut n'est pas\nutilisé permet de le décommissionner ou de le modifier. Bien sûr et malheureusement cette partie n'est pas open source. Je n'ai pas encore trouvé de\ndashboard OpenSource permettant d'analyser l'utilisation d'un champ.",[12,27714,27715],{},"Ce qui est important c'est d'avoir une politique de versionning.",[133,27717,27719],{"id":27718},"query-pagination","Query - Pagination",[12,27721,27722],{},"GraphQL n'a pas défini de règle concernant la pagination car il y a plein de manières de la gérer. Pour des listes contenant peu d'éléments, il n'y a\npar exemple pas lieu de gérer la pagination.",[12,27724,27725],{},"Quand on envisage la pagination, on peut en faire une par page avec un nombre d'éléments à passer (skip), ou une basée sur l'id du premier élément\nà afficher.",[12,27727,27728],{},"Il existe également des patterns que l'on peut utiliser pour ne pas réinventer une nouvelle facon de faire.",[12,27730,27731,27732,27735,27736,27739],{},"Par exemple l'un de ses patterns s'appele ",[184,27733,27734],{},"Connections"," et des librairies comme ",[184,27737,27738],{},"Relay"," savent comment gérer automatiquement ce pattern.",[12,27741,27742,27743,27745],{},"Dans le pattern ",[184,27744,27734],{},", nous retrouvons les notions:",[506,27747,27748,27759],{},[370,27749,27750,27751],{},"En entrée",[506,27752,27753,27756],{},[370,27754,27755],{},"nombre d'éléments à récupérer",[370,27757,27758],{},"le curseur à partir duquel retourner les données",[370,27760,27761,27762],{},"En sortie",[506,27763,27764,27767,27770],{},[370,27765,27766],{},"Le nombre d'éléments total",[370,27768,27769],{},"Les informations sur la page comme le prochain curseur et si on a une page suivante",[370,27771,27772],{},"Les différents éléments de la page (avec le curseur associé)",[2105,27774,27776],{"className":26525,"code":27775,"language":26527,"meta":1646,"style":1646},"{\n  hero {\n    name\n    friendsConnection(first: 2, after: \"Y3Vyc29yMQ==\") {\n      totalCount\n      edges {\n        node {\n          name\n        }\n        cursor\n      }\n      pageInfo {\n        endCursor\n        hasNextPage\n      }\n    }\n  }\n}\n",[184,27777,27778,27782,27788,27792,27818,27823,27830,27837,27842,27846,27851,27856,27863,27868,27873,27877,27881,27885],{"__ignoreMap":1646},[2113,27779,27780],{"class":2115,"line":2116},[2113,27781,18060],{"class":2119},[2113,27783,27784,27786],{"class":2115,"line":1647},[2113,27785,27384],{"class":2330},[2113,27787,2647],{"class":2119},[2113,27789,27790],{"class":2115,"line":1652},[2113,27791,27400],{"class":2330},[2113,27793,27794,27797,27799,27802,27804,27806,27808,27811,27813,27816],{"class":2115,"line":2185},[2113,27795,27796],{"class":2330},"    friendsConnection",[2113,27798,2423],{"class":2119},[2113,27800,27801],{"class":2429},"first",[2113,27803,4429],{"class":2119},[2113,27805,2547],{"class":2166},[2113,27807,932],{"class":2119},[2113,27809,27810],{"class":2429},"after",[2113,27812,4429],{"class":2119},[2113,27814,27815],{"class":2149},"\"Y3Vyc29yMQ==\"",[2113,27817,2433],{"class":2119},[2113,27819,27820],{"class":2115,"line":2230},[2113,27821,27822],{"class":2330},"      totalCount\n",[2113,27824,27825,27828],{"class":2115,"line":2235},[2113,27826,27827],{"class":2330},"      edges",[2113,27829,2647],{"class":2119},[2113,27831,27832,27835],{"class":2115,"line":2241},[2113,27833,27834],{"class":2330},"        node",[2113,27836,2647],{"class":2119},[2113,27838,27839],{"class":2115,"line":2246},[2113,27840,27841],{"class":2330},"          name\n",[2113,27843,27844],{"class":2115,"line":2464},[2113,27845,4578],{"class":2119},[2113,27847,27848],{"class":2115,"line":2085},[2113,27849,27850],{"class":2330},"        cursor\n",[2113,27852,27853],{"class":2115,"line":2514},[2113,27854,27855],{"class":2119},"      }\n",[2113,27857,27858,27861],{"class":2115,"line":2533},[2113,27859,27860],{"class":2330},"      pageInfo",[2113,27862,2647],{"class":2119},[2113,27864,27865],{"class":2115,"line":2556},[2113,27866,27867],{"class":2330},"        endCursor\n",[2113,27869,27870],{"class":2115,"line":2569},[2113,27871,27872],{"class":2330},"        hasNextPage\n",[2113,27874,27875],{"class":2115,"line":2575},[2113,27876,27855],{"class":2119},[2113,27878,27879],{"class":2115,"line":2596},[2113,27880,4665],{"class":2119},[2113,27882,27883],{"class":2115,"line":3192},[2113,27884,2572],{"class":2119},[2113,27886,27887],{"class":2115,"line":3213},[2113,27888,2599],{"class":2119},[12,27890,27891,27892],{},"On peut retrouver la spécification de ce modèle de pagination sur le site de ",[49,27893,27896],{"href":27894,"rel":27895},"https:\u002F\u002Frelay.dev\u002Fgraphql\u002Fconnections.htm",[347],"relay.dev",[133,27898,27900],{"id":27899},"query-cache-control","Query - Cache - Control",[12,27902,27903],{},"Contrairement au REST où on peut peut utiliser les header HTTP, pour contrôler le cache, en GraphQL une requête peut avoir des limites d'âge différentes sur les différentes resources\nappelées. Il faut donc trouver d'autres moyens de gérer le cache.",[12,27905,27906],{},"C'est pour moi un des points les plus gênants de GraphQL.",[12,27908,27909,27910,179],{},"Sur le serveur, il y a plusieurs manières complémentaires (à différentes fins) de gérer le cache. Nous allons nous concentrer sur la librairie ",[184,27911,27912],{},"apollo-server",[12,27914,27915,27916,27919,27920,27923],{},"La 1ère manière s'apparentant au cache-control des requêtes HTTP peut être utilisée avec la directive ",[184,27917,27918],{},"@cacheControl"," ou au niveau du resolver. Elle ne permet pas de cacher directement la donnée\nmais de donner une indication au client sur combien de temps cette donnée peut-être cachée. Si la librairie utilisée côté client est également ",[184,27921,27922],{},"apollo"," elle utilisera alors ces indications pour\ngérer le cache de la donnée (et donc ne pas ré-effectuer la requête.)",[2105,27925,27927],{"className":26525,"code":27926,"language":26527,"meta":1646,"style":1646},"scalar Date\n\n\"\"\"\nDefine the state of the queue\n\"\"\"\ntype QueueStats @cacheControl(maxAge: 60) {\n  \"\"\"\n  Number of task waiting in queue\n  \"\"\"\n  waiting: Int!\n  \"\"\"\n  Number of task active in queue\n  \"\"\"\n  active: Int!\n  \"\"\"\n  Number of task that have failed\n  \"\"\"\n  failed: Int!\n  \"\"\"\n  Date of the last execution\n  \"\"\"\n  lastExecution: Date! @cacheControl(maxAge: 3600)\n  \"\"\"\n  Date of the next wakeup\n  \"\"\"\n  nextWakeup: Date! @cacheControl(maxAge: 3600)\n}\n",[184,27928,27929,27935,27939,27943,27947,27951,27972,27976,27980,27984,27990,27994,27998,28002,28008,28012,28016,28020,28026,28030,28034,28038,28058,28062,28066,28070,28088],{"__ignoreMap":1646},[2113,27930,27931,27933],{"class":2115,"line":2116},[2113,27932,26692],{"class":2326},[2113,27934,26695],{"class":2119},[2113,27936,27937],{"class":2115,"line":1647},[2113,27938,2125],{"emptyLinePlaceholder":1767},[2113,27940,27941],{"class":2115,"line":1652},[2113,27942,26704],{"class":2396},[2113,27944,27945],{"class":2115,"line":2185},[2113,27946,26709],{"class":2396},[2113,27948,27949],{"class":2115,"line":2230},[2113,27950,26704],{"class":2396},[2113,27952,27953,27955,27958,27960,27962,27965,27967,27970],{"class":2115,"line":2235},[2113,27954,21494],{"class":2326},[2113,27956,27957],{"class":2119}," QueueStats ",[2113,27959,27918],{"class":2133},[2113,27961,2423],{"class":2119},[2113,27963,27964],{"class":2429},"maxAge",[2113,27966,4429],{"class":2119},[2113,27968,27969],{"class":2166},"60",[2113,27971,2433],{"class":2119},[2113,27973,27974],{"class":2115,"line":2241},[2113,27975,26725],{"class":2396},[2113,27977,27978],{"class":2115,"line":2246},[2113,27979,26730],{"class":2396},[2113,27981,27982],{"class":2115,"line":2464},[2113,27983,26725],{"class":2396},[2113,27985,27986,27988],{"class":2115,"line":2085},[2113,27987,26739],{"class":2330},[2113,27989,26742],{"class":2119},[2113,27991,27992],{"class":2115,"line":2514},[2113,27993,26725],{"class":2396},[2113,27995,27996],{"class":2115,"line":2533},[2113,27997,26751],{"class":2396},[2113,27999,28000],{"class":2115,"line":2556},[2113,28001,26725],{"class":2396},[2113,28003,28004,28006],{"class":2115,"line":2569},[2113,28005,26760],{"class":2330},[2113,28007,26742],{"class":2119},[2113,28009,28010],{"class":2115,"line":2575},[2113,28011,26725],{"class":2396},[2113,28013,28014],{"class":2115,"line":2596},[2113,28015,26771],{"class":2396},[2113,28017,28018],{"class":2115,"line":3192},[2113,28019,26725],{"class":2396},[2113,28021,28022,28024],{"class":2115,"line":3213},[2113,28023,26780],{"class":2330},[2113,28025,26742],{"class":2119},[2113,28027,28028],{"class":2115,"line":3236},[2113,28029,26725],{"class":2396},[2113,28031,28032],{"class":2115,"line":3248},[2113,28033,26791],{"class":2396},[2113,28035,28036],{"class":2115,"line":4899},[2113,28037,26725],{"class":2396},[2113,28039,28040,28042,28045,28047,28049,28051,28053,28056],{"class":2115,"line":1777},[2113,28041,26800],{"class":2330},[2113,28043,28044],{"class":2119},": Date! ",[2113,28046,27918],{"class":2133},[2113,28048,2423],{"class":2119},[2113,28050,27964],{"class":2429},[2113,28052,4429],{"class":2119},[2113,28054,28055],{"class":2166},"3600",[2113,28057,4660],{"class":2119},[2113,28059,28060],{"class":2115,"line":4931},[2113,28061,26725],{"class":2396},[2113,28063,28064],{"class":2115,"line":4961},[2113,28065,26812],{"class":2396},[2113,28067,28068],{"class":2115,"line":4976},[2113,28069,26725],{"class":2396},[2113,28071,28072,28074,28076,28078,28080,28082,28084,28086],{"class":2115,"line":4993},[2113,28073,26821],{"class":2330},[2113,28075,28044],{"class":2119},[2113,28077,27918],{"class":2133},[2113,28079,2423],{"class":2119},[2113,28081,27964],{"class":2429},[2113,28083,4429],{"class":2119},[2113,28085,28055],{"class":2166},[2113,28087,4660],{"class":2119},[2113,28089,28090],{"class":2115,"line":5013},[2113,28091,2599],{"class":2119},[12,28093,28094,28095,28100],{},"Je vous invite à lire la ",[49,28096,28099],{"href":28097,"rel":28098},"https:\u002F\u002Fwww.apollographql.com\u002Fdocs\u002Fapollo-server\u002Fperformance\u002Fcaching\u002F",[347],"documentation d'apollo"," qui contient énormément d'informations. Cette méthode permet d'ajouter\ndes header http pour cacher les requêtes dans un CDN intérmédiaire, ou dans une base redis attachée au serveur, ou dans le cache navigateur, voir également au niveau même du client.",[133,28102,28104],{"id":28103},"query-cache-dataloader","Query - Cache - DataLoader",[12,28106,28107,28108,28113],{},"La 2nd méthode de cache consiste à l'utilisation de ",[49,28109,28112],{"href":28110,"rel":28111},"https:\u002F\u002Fgithub.com\u002Fgraphql\u002Fdataloader",[347],"DataLoader",". L'ajout d'un dataloader a pour but de cacher le temps d'une requête les différents éléments pour éviter de multiplier les appels.\nLe but n'étant pas de réellement faire du cache mais plutôt de pouvoir traiter une requête batch le plus rapidement possible.",[12,28115,28116],{},"Imaginons par exemple la requête suivante :",[2105,28118,28120],{"className":26525,"code":28119,"language":26527,"meta":1646,"style":1646},"query TestDataLoader {\n  backups {\n    id\n    startDate\n    endDate\n    user {\n      firstName\n      lastName\n    }\n  }\n}\n",[184,28121,28122,28131,28138,28143,28148,28153,28160,28165,28170,28174,28178],{"__ignoreMap":1646},[2113,28123,28124,28126,28129],{"class":2115,"line":2116},[2113,28125,26534],{"class":2326},[2113,28127,28128],{"class":2133}," TestDataLoader",[2113,28130,2647],{"class":2119},[2113,28132,28133,28136],{"class":2115,"line":1647},[2113,28134,28135],{"class":2330},"  backups",[2113,28137,2647],{"class":2119},[2113,28139,28140],{"class":2115,"line":1652},[2113,28141,28142],{"class":2330},"    id\n",[2113,28144,28145],{"class":2115,"line":2185},[2113,28146,28147],{"class":2330},"    startDate\n",[2113,28149,28150],{"class":2115,"line":2230},[2113,28151,28152],{"class":2330},"    endDate\n",[2113,28154,28155,28158],{"class":2115,"line":2235},[2113,28156,28157],{"class":2330},"    user",[2113,28159,2647],{"class":2119},[2113,28161,28162],{"class":2115,"line":2241},[2113,28163,28164],{"class":2330},"      firstName\n",[2113,28166,28167],{"class":2115,"line":2246},[2113,28168,28169],{"class":2330},"      lastName\n",[2113,28171,28172],{"class":2115,"line":2464},[2113,28173,4665],{"class":2119},[2113,28175,28176],{"class":2115,"line":2085},[2113,28177,2572],{"class":2119},[2113,28179,28180],{"class":2115,"line":2514},[2113,28181,2599],{"class":2119},[12,28183,28184],{},"Dans le cas présent, on souhaite récupérer les backups, mais également pour chaque backup récupérer l'utilisateur. Si l'utilisateur est le même pour chaque backup, on se retouve alors à charger\nl'utilisateur plusieurs fois. Le fait d'utiliser un dataloader permettra dans le cas présent de ne les charger qu'une fois.",[12,28186,28187,28188,28193],{},"Pour utiliser le système de data loader, on peut utiliser la bibliothèque du même nom: [",[49,28189,28192],{"href":28190,"rel":28191},"https:\u002F\u002Fgithub.com\u002Fgraphql\u002Fdataloader%5D(Github",[347],"https:\u002F\u002Fgithub.com\u002Fgraphql\u002Fdataloader](Github",": graphql\u002Fdataloader). Il nous faut alors créer un dataloader pour\nun type d'objet.",[2105,28195,28197],{"className":7776,"code":28196,"language":7778,"meta":1646,"style":1646},"import DataLoader from \"dataloader\";\n\nconst userLoader = new DataLoader((keys) => service.getUsers(keys));\n",[184,28198,28199,28213,28217],{"__ignoreMap":1646},[2113,28200,28201,28203,28206,28208,28211],{"class":2115,"line":2116},[2113,28202,15758],{"class":2326},[2113,28204,28205],{"class":2330}," DataLoader",[2113,28207,15768],{"class":2326},[2113,28209,28210],{"class":2149}," \"dataloader\"",[2113,28212,2487],{"class":2119},[2113,28214,28215],{"class":2115,"line":1647},[2113,28216,2125],{"emptyLinePlaceholder":1767},[2113,28218,28219,28221,28224,28226,28228,28230,28232,28235,28237,28239,28241,28243,28246,28248,28250],{"class":2115,"line":1652},[2113,28220,7785],{"class":2326},[2113,28222,28223],{"class":2414}," userLoader",[2113,28225,2153],{"class":2334},[2113,28227,4778],{"class":2326},[2113,28229,28205],{"class":2133},[2113,28231,8879],{"class":2119},[2113,28233,28234],{"class":2429},"keys",[2113,28236,5709],{"class":2119},[2113,28238,8063],{"class":2326},[2113,28240,21031],{"class":2414},[2113,28242,179],{"class":2119},[2113,28244,28245],{"class":2133},"getUsers",[2113,28247,2423],{"class":2119},[2113,28249,28234],{"class":2330},[2113,28251,5818],{"class":2119},[12,28253,28254],{},"Ensuite dans les resolvers, au lieu de récupérer les utilisateurs directement à partir du service, on utilise le data loader pour récupérer la valeur :",[2105,28256,28258],{"className":7776,"code":28257,"language":7778,"meta":1646,"style":1646},"const resolvers = {\n  async backups() {\n    return service.find();\n  },\n  Backup: {\n    async user(ctx, { id }) {\n      return await userLoader.load(id);\n    },\n  },\n};\n",[184,28259,28260,28270,28278,28290,28294,28301,28320,28339,28343,28347],{"__ignoreMap":1646},[2113,28261,28262,28264,28266,28268],{"class":2115,"line":2116},[2113,28263,7785],{"class":2326},[2113,28265,27047],{"class":2414},[2113,28267,2153],{"class":2334},[2113,28269,2647],{"class":2119},[2113,28271,28272,28274,28276],{"class":2115,"line":1647},[2113,28273,19213],{"class":2326},[2113,28275,6686],{"class":2133},[2113,28277,3912],{"class":2119},[2113,28279,28280,28282,28284,28286,28288],{"class":2115,"line":1652},[2113,28281,3137],{"class":2326},[2113,28283,21031],{"class":2414},[2113,28285,179],{"class":2119},[2113,28287,7343],{"class":2133},[2113,28289,3944],{"class":2119},[2113,28291,28292],{"class":2115,"line":2185},[2113,28293,8490],{"class":2119},[2113,28295,28296,28299],{"class":2115,"line":2230},[2113,28297,28298],{"class":2330},"  Backup",[2113,28300,8091],{"class":2119},[2113,28302,28303,28305,28307,28309,28312,28315,28317],{"class":2115,"line":2235},[2113,28304,27083],{"class":2326},[2113,28306,25863],{"class":2133},[2113,28308,2423],{"class":2119},[2113,28310,28311],{"class":2429},"ctx",[2113,28313,28314],{"class":2119},", { ",[2113,28316,20628],{"class":2429},[2113,28318,28319],{"class":2119}," }) {\n",[2113,28321,28322,28324,28326,28328,28330,28333,28335,28337],{"class":2115,"line":2241},[2113,28323,20652],{"class":2326},[2113,28325,17521],{"class":2326},[2113,28327,28223],{"class":2414},[2113,28329,179],{"class":2119},[2113,28331,28332],{"class":2133},"load",[2113,28334,2423],{"class":2119},[2113,28336,20628],{"class":2330},[2113,28338,2553],{"class":2119},[2113,28340,28341],{"class":2115,"line":2246},[2113,28342,8237],{"class":2119},[2113,28344,28345],{"class":2115,"line":2464},[2113,28346,8490],{"class":2119},[2113,28348,28349],{"class":2115,"line":2085},[2113,28350,2787],{"class":2119},[12,28352,28353,28356],{},[184,28354,28355],{},"Dataloader"," n'est par contre pas fait pour être utilisé en tant que cache applicatif et ne remplace donc pas un memcached ou un redis.",[133,28358,28360],{"id":28359},"query-batch","Query - Batch",[12,28362,28363,28365],{},[184,28364,26509],{}," côté client propose de pouvoir faire du batching de requêtes. Cela consiste à attendre un léger laps de temps pour regrouper en une seule requête plusieurs requêtes.",[12,28367,28368],{},"Attention néanmoins, mettre en place le batching de requêtes implique:",[506,28370,28371,28374,28377],{},[370,28372,28373],{},"qu'on attend (pas très longtemps) pour envoyer les requêtes",[370,28375,28376],{},"qu'on attend l'aggrégat des réponses avant de les recevoir",[370,28378,28379],{},"qu'on ne bénéficie pas du multiplexing des requêtes de HTTP\u002F2.",[12,28381,28382],{},"Il est alors conseillé d'abord de faire d'autres types d'optimisations (comme utiliser les requêtes persistentes, un cache dans un CDN des réponses, utiliser HTTP\u002F2 jusqu'au NodeJS)\navant de mettre en place le système de batch.",[12,28384,28385,28386,179],{},"Si vous voulez le mettre tout de même en place, vous pouvez regarder la ",[49,28387,28390],{"href":28388,"rel":28389},"https:\u002F\u002Fwww.apollographql.com\u002Fdocs\u002Flink\u002Flinks\u002Fbatch-http\u002F",[347],"documentation d'Apollo sur ce sujet",[133,28392,28394],{"id":28393},"query-transfert-réseau","Query - Transfert réseau",[12,28396,28397],{},"Pour une API Web, les requêtes et les réponses sont transférées en utilisant le protocole HTTP. Pour améliorer les performances il est\npossible d'utiliser plusieurs méthodes:",[506,28399,28400,28403,28410,28413],{},[370,28401,28402],{},"Comme en REST, activer la compression GZIP coté serveur, permet de réduire les flux réseaux",[370,28404,28405,28406],{},"Utiliser les requêtes persistentes (stocké coté serveur) : ",[49,28407,28408],{"href":28408,"rel":28409},"https:\u002F\u002Fwww.apollographql.com\u002Fdocs\u002Fapollo-server\u002Fperformance\u002Fapq\u002F",[347],[370,28411,28412],{},"Utiliser les fragments pour réutiliser certaines parties des requêtes qui peuvent se répéter.",[370,28414,28415],{},"Utiliser HTTP\u002F2 et le multiplexing des requêtes. Faire une seule grosse requête peut être contre-productif avec HTTP\u002F2. En effet il faut attendre la fin de la résolution de l'ensemble des resolvers\ncôté serveur avant d'avoir la réponse. Faire plusieurs requêtes peut permettre d'avoir certaines parties de la requête plus vite. De plus HTTP\u002F2 permet de mieux paralléliser les requêtes. Il faut donc\ntrouver le bon nombre de requêtes à executer pour avoir les meilleurs performances.",[133,28417,28419],{"id":28418},"mutation","Mutation",[12,28421,28422,28423,179],{},"Nous avons énormément parlé de toute la partie requête de GraphQL. GraphQL permet également de faire des modifications via des ",[184,28424,28425],{},"mutations",[12,28427,28428],{},"Une mutation est similaire à une requête :",[2105,28430,28432],{"className":26525,"code":28431,"language":26527,"meta":1646,"style":1646},"mutation CreateBackup($backup: CreateBackupInput!) {\n  createBackup(backup: $backup) {\n    statusCode\n    message\n    error\n    errorCode\n    backup {\n      id\n      state\n      user {\n        id\n      }\n    }\n  }\n",[184,28433,28434,28449,28464,28469,28474,28479,28484,28491,28496,28501,28508,28513,28517,28521],{"__ignoreMap":1646},[2113,28435,28436,28438,28441,28443,28446],{"class":2115,"line":2116},[2113,28437,28418],{"class":2326},[2113,28439,28440],{"class":2133}," CreateBackup",[2113,28442,2423],{"class":2119},[2113,28444,28445],{"class":2429},"$backup",[2113,28447,28448],{"class":2119},": CreateBackupInput!) {\n",[2113,28450,28451,28454,28456,28458,28460,28462],{"class":2115,"line":1647},[2113,28452,28453],{"class":2330},"  createBackup",[2113,28455,2423],{"class":2119},[2113,28457,1773],{"class":2429},[2113,28459,4429],{"class":2119},[2113,28461,28445],{"class":2330},[2113,28463,2433],{"class":2119},[2113,28465,28466],{"class":2115,"line":1652},[2113,28467,28468],{"class":2330},"    statusCode\n",[2113,28470,28471],{"class":2115,"line":2185},[2113,28472,28473],{"class":2330},"    message\n",[2113,28475,28476],{"class":2115,"line":2230},[2113,28477,28478],{"class":2330},"    error\n",[2113,28480,28481],{"class":2115,"line":2235},[2113,28482,28483],{"class":2330},"    errorCode\n",[2113,28485,28486,28489],{"class":2115,"line":2241},[2113,28487,28488],{"class":2330},"    backup",[2113,28490,2647],{"class":2119},[2113,28492,28493],{"class":2115,"line":2246},[2113,28494,28495],{"class":2330},"      id\n",[2113,28497,28498],{"class":2115,"line":2464},[2113,28499,28500],{"class":2330},"      state\n",[2113,28502,28503,28506],{"class":2115,"line":2085},[2113,28504,28505],{"class":2330},"      user",[2113,28507,2647],{"class":2119},[2113,28509,28510],{"class":2115,"line":2514},[2113,28511,28512],{"class":2330},"        id\n",[2113,28514,28515],{"class":2115,"line":2533},[2113,28516,27855],{"class":2119},[2113,28518,28519],{"class":2115,"line":2556},[2113,28520,4665],{"class":2119},[2113,28522,28523],{"class":2115,"line":2569},[2113,28524,2572],{"class":2119},[12,28526,28527],{},"Nous retrouvons la partie input, et la partie query (sur la réponse). Alors que généralement sur query on va retrouver des types primitifs en paramètres, on va plutôt retrouver\nen paramètre d'une mutation un objet de type Input. Sur le résultat le fonctionnement est lui le même que sur une Query.",[12,28529,28530],{},"Nous pouvons d'ailleurs utiliser les mêmes resolvers sur les réponses que dans les queries (et ainsi récupérer l'utilisateur de la backup de la même manière).",[12,28532,28533],{},"En bonne pratique, ce que nous pouvons retrouver sur les mutations sont:",[506,28535,28536,28539,28550,28555,28567],{},[370,28537,28538],{},"Ne pas renvoyer l'objet créé\u002Fmodifié directement. Renvoyer un objet permettant à l'utilisateur de récupérer l'erreur fonctionnelle ou l'objet selon le cas. Cela permettra de faire\névoluer plus facilement le résultat si nécessaire.",[370,28540,28541,28542,28545,28546,28549],{},"De la même manière, ",[155,28543,28544],{},"même"," si la mutation a besoin de très peu de paramètres en entrée, il vaut mieux créer un objet de type ",[184,28547,28548],{},"input"," afin de passer les paramètres. Ceci également\nafin de pouvoir évoluer facilement avec l'API au fur et à mesure des évolutions.",[370,28551,28552,28553,179],{},"Eviter pour le paramètre principal ou la sortie principale de réutiliser un objet. Il vaut mieux en créer un propre à la ",[184,28554,28418],{},[370,28556,28557,28558,28560,28561,28563,28564,28566],{},"Contrairement à REST, les ",[184,28559,28425],{}," GraphQL ressemblent plus à du RPC. Il vaut mieux alors éviter de penser ressources mais plutôt actions. Quels sont les actions que l'on souhaite\npouvoir faire depuis l'IHM.",[15567,28562],{},"Il ne faut pas hésiter à découper ces actions en petites actions que l'utilisateur peut choisir d'appeler ou non (il est possible d'appeler plusieurs ",[184,28565,28425],{}," en un seul appel).",[370,28568,28569,28570,28572],{},"Penser les ",[184,28571,28425],{}," en fonction des appels de vos clients.",[12,28574,28575],{},"Si on reprend le schéma suivant:",[2105,28577,28579],{"className":26525,"code":28578,"language":26527,"meta":1646,"style":1646},"input InputUser {\n  name: String!\n}\n\ninput CreateBackupInput {\n  host: String!\n  user: InputUser!\n}\n\ntype Backup {\n  host: String!\n  id: Int!\n  startDate: Date\n  endDate: Date\n  user: User\n}\n\ntype User {\n  name: String\n  backups: [Backup!]\n}\n\ntype CreateBackupResponse {\n  statusCode: Int!\n  message: String\n  error: String\n  errorCode: String\n  backup: Backup\n}\n",[184,28580,28581,28588,28595,28599,28603,28610,28616,28623,28627,28631,28638,28644,28651,28659,28666,28673,28677,28681,28688,28695,28702,28706,28710,28717,28724,28730,28737,28744,28752],{"__ignoreMap":1646},[2113,28582,28583,28585],{"class":2115,"line":2116},[2113,28584,28548],{"class":2326},[2113,28586,28587],{"class":2119}," InputUser {\n",[2113,28589,28590,28592],{"class":2115,"line":1647},[2113,28591,19883],{"class":2330},[2113,28593,28594],{"class":2119},": String!\n",[2113,28596,28597],{"class":2115,"line":1652},[2113,28598,2599],{"class":2119},[2113,28600,28601],{"class":2115,"line":2185},[2113,28602,2125],{"emptyLinePlaceholder":1767},[2113,28604,28605,28607],{"class":2115,"line":2230},[2113,28606,28548],{"class":2326},[2113,28608,28609],{"class":2119}," CreateBackupInput {\n",[2113,28611,28612,28614],{"class":2115,"line":2235},[2113,28613,26843],{"class":2330},[2113,28615,28594],{"class":2119},[2113,28617,28618,28620],{"class":2115,"line":2241},[2113,28619,26244],{"class":2330},[2113,28621,28622],{"class":2119},": InputUser!\n",[2113,28624,28625],{"class":2115,"line":2246},[2113,28626,2599],{"class":2119},[2113,28628,28629],{"class":2115,"line":2464},[2113,28630,2125],{"emptyLinePlaceholder":1767},[2113,28632,28633,28635],{"class":2115,"line":2085},[2113,28634,21494],{"class":2326},[2113,28636,28637],{"class":2119}," Backup {\n",[2113,28639,28640,28642],{"class":2115,"line":2514},[2113,28641,26843],{"class":2330},[2113,28643,28594],{"class":2119},[2113,28645,28646,28649],{"class":2115,"line":2533},[2113,28647,28648],{"class":2330},"  id",[2113,28650,26742],{"class":2119},[2113,28652,28653,28656],{"class":2115,"line":2556},[2113,28654,28655],{"class":2330},"  startDate",[2113,28657,28658],{"class":2119},": Date\n",[2113,28660,28661,28664],{"class":2115,"line":2569},[2113,28662,28663],{"class":2330},"  endDate",[2113,28665,28658],{"class":2119},[2113,28667,28668,28670],{"class":2115,"line":2575},[2113,28669,26244],{"class":2330},[2113,28671,28672],{"class":2119},": User\n",[2113,28674,28675],{"class":2115,"line":2596},[2113,28676,2599],{"class":2119},[2113,28678,28679],{"class":2115,"line":3192},[2113,28680,2125],{"emptyLinePlaceholder":1767},[2113,28682,28683,28685],{"class":2115,"line":3213},[2113,28684,21494],{"class":2326},[2113,28686,28687],{"class":2119}," User {\n",[2113,28689,28690,28692],{"class":2115,"line":3236},[2113,28691,19883],{"class":2330},[2113,28693,28694],{"class":2119},": String\n",[2113,28696,28697,28699],{"class":2115,"line":3248},[2113,28698,28135],{"class":2330},[2113,28700,28701],{"class":2119},": [Backup!]\n",[2113,28703,28704],{"class":2115,"line":4899},[2113,28705,2599],{"class":2119},[2113,28707,28708],{"class":2115,"line":1777},[2113,28709,2125],{"emptyLinePlaceholder":1767},[2113,28711,28712,28714],{"class":2115,"line":4931},[2113,28713,21494],{"class":2326},[2113,28715,28716],{"class":2119}," CreateBackupResponse {\n",[2113,28718,28719,28722],{"class":2115,"line":4961},[2113,28720,28721],{"class":2330},"  statusCode",[2113,28723,26742],{"class":2119},[2113,28725,28726,28728],{"class":2115,"line":4976},[2113,28727,16062],{"class":2330},[2113,28729,28694],{"class":2119},[2113,28731,28732,28735],{"class":2115,"line":4993},[2113,28733,28734],{"class":2330},"  error",[2113,28736,28694],{"class":2119},[2113,28738,28739,28742],{"class":2115,"line":5013},[2113,28740,28741],{"class":2330},"  errorCode",[2113,28743,28694],{"class":2119},[2113,28745,28746,28749],{"class":2115,"line":5018},[2113,28747,28748],{"class":2330},"  backup",[2113,28750,28751],{"class":2119},": Backup\n",[2113,28753,28754],{"class":2115,"line":5042},[2113,28755,2599],{"class":2119},[12,28757,28758,28759,28761],{},"Contrairement aux paramètres de sortie, il n'est pas possible sur les ",[184,28760,28548],{}," de faire des dépendances circulaires. Un input doit donc être lié à l'action faite\npar la mutation.\nCela veux dire aussi qu'il n'est pas possible de réutiliser un type de sortie en input pour l'entrée. Le point d'exclamation n'est plus un indicateur de non nullité\nmais un indicateur de paramètre obligatoire.",[12,28763,28764],{},"Lors de la conception des ces inputs il faut d'ailleurs penser à plusieurs choses:",[506,28766,28767,28770],{},[370,28768,28769],{},"Certain champs en sortie dans un type sont là pour representer un calcul ou un état qui fait sens mais que l'utilisateur ne peut pas modifier. Il ne faut donc pas\nle mettre en entrée.",[370,28771,28772],{},"Un paramètre qui peut être non nul en sortie n'est pas forcément obligatoire en entrée (et inversement).",[12,28774,28775],{},"Pour une application possédant beaucoup de formulaires et d'écrans de modifications ou d'actions, il peut être donc compliqué d'écrire les requêtes et les mutations associées.",[12,28777,28778,28779,28784],{},"Il peut peut-être être intéressant d'utiliser un transpiler comme ",[49,28780,28783],{"href":28781,"rel":28782},"https:\u002F\u002Fwww.npmjs.com\u002Fpackage\u002Fgraphql-s2s",[347],"graphql-s2s"," qui permet de simplifier certaines choses\ndans l'écriture du schéma.\nLors de son utilisation sur un projet possédant un gros schéma, il a permis la reprise d'une API Rest existante plus facilement.",[12,28786,28787],{},"Il permet entre autres:",[506,28789,28790,28793],{},[370,28791,28792],{},"de ne pas répéter les champs de l'interface lors de l'héritage d'un type.",[370,28794,28795],{},"de définir des types génériques (qui à la compilation créeront des vrais types).",[133,28797,28799],{"id":28798},"subscription","Subscription",[12,28801,28802,28803,28806],{},"Un des points que j'adore avec GraphQL c'est la facilité d'implémentation qu'apporte Apollo avec les ",[184,28804,28805],{},"Subscritpions",". Quand on souhaite remonter des informations\ndu serveur au client, on peut mettre en place des WebSockets ou des Server-Side-Events. Il faut alors définir un potocole entre le client et le serveur.",[12,28808,28809],{},"GraphQL utilise de façon transparente une WebSocket dans le cadre des souscriptions. L'avantage c'est que le protocole est le même que pour les query. On effectue une\ndemande et au lieu d'avoir une réponse, une fois, on reçois les mise à jours sur la souscription.",[12,28811,28812],{},"Cela permet de mettre en place facilement des pages dynamiques qui évoluent sans actions utilisateurs (progression, chat, ...) sans se casser la tête.",[12,28814,28815],{},"Pour définir une souscription on peut utiliser une requête comme celle-ci:",[2105,28817,28819],{"className":26525,"code":28818,"language":26527,"meta":1646,"style":1646},"#import \".\u002FFragmentJob.graphql\"\n\nsubscription QueueTasksJobUpdated {\n  jobUpdated {\n    ...FragmentJob\n  }\n}\n",[184,28820,28821,28825,28829,28838,28845,28851,28855],{"__ignoreMap":1646},[2113,28822,28823],{"class":2115,"line":2116},[2113,28824,27618],{"class":2396},[2113,28826,28827],{"class":2115,"line":1647},[2113,28828,2125],{"emptyLinePlaceholder":1767},[2113,28830,28831,28833,28836],{"class":2115,"line":1652},[2113,28832,28798],{"class":2133},[2113,28834,28835],{"class":2133}," QueueTasksJobUpdated",[2113,28837,2647],{"class":2119},[2113,28839,28840,28843],{"class":2115,"line":2185},[2113,28841,28842],{"class":2330},"  jobUpdated",[2113,28844,2647],{"class":2119},[2113,28846,28847,28849],{"class":2115,"line":2230},[2113,28848,22858],{"class":2119},[2113,28850,27660],{"class":2330},[2113,28852,28853],{"class":2115,"line":2235},[2113,28854,2572],{"class":2119},[2113,28856,28857],{"class":2115,"line":2241},[2113,28858,2599],{"class":2119},[12,28860,28861],{},"Dans l'exemple ci-dessous je réutilise le fragment d'un job défini dans une query. La souscription dans apollo va automatiquement mettre à jour le résultat et\nrafraichir l'IHM.",[12,28863,28864,28865,20415],{},"Par exemple avec vue cela donne (cf la doc de ",[49,28866,28869],{"href":28867,"rel":28868},"https:\u002F\u002Fapollo.vuejs.org\u002Fguide\u002Fapollo\u002Fsubscriptions.html#subscribe-to-more",[347],"vue-apollo",[2105,28871,28873],{"className":7776,"code":28872,"language":7778,"meta":1646,"style":1646},"apollo: {\n  tags: {\n    query: TAGS_QUERY,\n    subscribeToMore: {\n      document: gql`subscription name($param: String!) {\n        itemAdded(param: $param) {\n          id\n          label\n        }\n      }`,\n      \u002F\u002F Variables passed to the subscription. Since we're using a function,\n      \u002F\u002F they are reactive\n      variables () {\n        return {\n          param: this.param,\n        }\n      },\n      \u002F\u002F Mutate the previous result\n      updateQuery: (previousResult, { subscriptionData }) => {\n        \u002F\u002F Here, return the new result from the previous with the new data\n      },\n    }\n  }\n}\n",[184,28874,28875,28881,28888,28900,28907,28920,28925,28930,28935,28939,28946,28951,28956,28964,28971,28987,28991,28995,29000,29023,29028,29032,29036,29040],{"__ignoreMap":1646},[2113,28876,28877,28879],{"class":2115,"line":2116},[2113,28878,27922],{"class":2330},[2113,28880,8091],{"class":2119},[2113,28882,28883,28886],{"class":2115,"line":1647},[2113,28884,28885],{"class":2330},"  tags",[2113,28887,8091],{"class":2119},[2113,28889,28890,28893,28895,28898],{"class":2115,"line":1652},[2113,28891,28892],{"class":2330},"    query",[2113,28894,4429],{"class":2119},[2113,28896,28897],{"class":2414},"TAGS_QUERY",[2113,28899,4706],{"class":2119},[2113,28901,28902,28905],{"class":2115,"line":2185},[2113,28903,28904],{"class":2330},"    subscribeToMore",[2113,28906,8091],{"class":2119},[2113,28908,28909,28912,28914,28917],{"class":2115,"line":2230},[2113,28910,28911],{"class":2330},"      document",[2113,28913,4429],{"class":2119},[2113,28915,28916],{"class":2133},"gql",[2113,28918,28919],{"class":2149},"`subscription name($param: String!) {\n",[2113,28921,28922],{"class":2115,"line":2235},[2113,28923,28924],{"class":2149},"        itemAdded(param: $param) {\n",[2113,28926,28927],{"class":2115,"line":2241},[2113,28928,28929],{"class":2149},"          id\n",[2113,28931,28932],{"class":2115,"line":2246},[2113,28933,28934],{"class":2149},"          label\n",[2113,28936,28937],{"class":2115,"line":2464},[2113,28938,4578],{"class":2149},[2113,28940,28941,28944],{"class":2115,"line":2085},[2113,28942,28943],{"class":2149},"      }`",[2113,28945,4706],{"class":2119},[2113,28947,28948],{"class":2115,"line":2514},[2113,28949,28950],{"class":2396},"      \u002F\u002F Variables passed to the subscription. Since we're using a function,\n",[2113,28952,28953],{"class":2115,"line":2533},[2113,28954,28955],{"class":2396},"      \u002F\u002F they are reactive\n",[2113,28957,28958,28961],{"class":2115,"line":2556},[2113,28959,28960],{"class":2133},"      variables",[2113,28962,28963],{"class":2119}," () {\n",[2113,28965,28966,28969],{"class":2115,"line":2569},[2113,28967,28968],{"class":2326},"        return",[2113,28970,2647],{"class":2119},[2113,28972,28973,28976,28978,28980,28982,28985],{"class":2115,"line":2575},[2113,28974,28975],{"class":2330},"          param",[2113,28977,4429],{"class":2119},[2113,28979,15880],{"class":2414},[2113,28981,179],{"class":2119},[2113,28983,28984],{"class":2330},"param",[2113,28986,4706],{"class":2119},[2113,28988,28989],{"class":2115,"line":2596},[2113,28990,4578],{"class":2119},[2113,28992,28993],{"class":2115,"line":3192},[2113,28994,12282],{"class":2119},[2113,28996,28997],{"class":2115,"line":3213},[2113,28998,28999],{"class":2396},"      \u002F\u002F Mutate the previous result\n",[2113,29001,29002,29005,29008,29011,29013,29016,29019,29021],{"class":2115,"line":3236},[2113,29003,29004],{"class":2330},"      updateQuery",[2113,29006,29007],{"class":2119},": (",[2113,29009,29010],{"class":2429},"previousResult",[2113,29012,28314],{"class":2119},[2113,29014,29015],{"class":2429},"subscriptionData",[2113,29017,29018],{"class":2119}," }) ",[2113,29020,8063],{"class":2326},[2113,29022,2647],{"class":2119},[2113,29024,29025],{"class":2115,"line":3248},[2113,29026,29027],{"class":2396},"        \u002F\u002F Here, return the new result from the previous with the new data\n",[2113,29029,29030],{"class":2115,"line":4899},[2113,29031,12282],{"class":2119},[2113,29033,29034],{"class":2115,"line":1777},[2113,29035,4665],{"class":2119},[2113,29037,29038],{"class":2115,"line":4931},[2113,29039,2572],{"class":2119},[2113,29041,29042],{"class":2115,"line":4961},[2113,29043,2599],{"class":2119},[12,29045,29046,29047,29050],{},"La requête initiale est faite grâce à ",[184,29048,29049],{},"TAG_QUERY",", puis toutes les mises à jours se font via la souscription. L'IHM est alors automatiquement mise à jour.",[133,29052,1621],{"id":1620},[12,29054,29055],{},"GraphQL est super puissant et facilite l'écriture d'API et son utilisation par des clients. Par contre la montée en compétence pour faire du GraphQL est un\npeu plus élevée que pour faire du REST (Bien qu'il n'est pas facile de faire du bon REST).",[12,29057,29058],{},"Il peut toujours être utile de proposer des API Rest en plus des API GraphQL. Pour par exemple permettre au client de choisir ce qu'il souhaite utiliser ou\nmême par exemple pour des cas d'usage particuliers. (Par exemple le téléchargement\u002Fl'upload d'un document binaire est plus facile en REST que en GraphQL).",[133,29060,29062],{"id":29061},"références","Références",[506,29064,29065,29072,29078,29085,29091,29098,29103],{},[370,29066,29067],{},[49,29068,29071],{"href":29069,"rel":29070},"https:\u002F\u002Fgraphql.org\u002Flearn\u002Fqueries\u002F",[347],"GraphQL",[370,29073,29074,179],{},[49,29075,26509],{"href":29076,"rel":29077},"https:\u002F\u002Fwww.apollographql.com\u002Fdocs\u002Fapollo-server\u002Fdata\u002Fresolvers\u002F",[347],[370,29079,29080],{},[49,29081,29084],{"href":29082,"rel":29083},"https:\u002F\u002Fwww.moesif.com\u002Fblog\u002Ftechnical\u002Fapi-design\u002FBest-Practices-for-Versioning-REST-and-GraphQL-APIs\u002F",[347],"Best Practices for Versioning REST and GraphQL APIs",[370,29086,29087],{},[49,29088,29090],{"href":27696,"rel":29089},[347],"GraphQL Versioning",[370,29092,29093],{},[49,29094,29097],{"href":29095,"rel":29096},"https:\u002F\u002Ftowardsdatascience.com\u002Fgraphql-best-practices-3fda586538c4",[347],"GraphQL Best Practices",[370,29099,29100],{},[49,29101,29082],{"href":29082,"rel":29102},[347],[370,29104,29105],{},[49,29106,27696],{"href":27696,"rel":29107},[347],[3358,29109,29110],{},"html pre.shiki code .sjrmR, html code.shiki .sjrmR{--shiki-default:#56B6C2}html pre.shiki code .sU0A5, html code.shiki .sU0A5{--shiki-default:#E5C07B}html pre.shiki code .sn6KH, html code.shiki .sn6KH{--shiki-default:#ABB2BF}html pre.shiki code .sVyAn, html code.shiki .sVyAn{--shiki-default:#E06C75}html pre.shiki code .seHd6, html code.shiki .seHd6{--shiki-default:#C678DD}html pre.shiki code .subq3, html code.shiki .subq3{--shiki-default:#98C379}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html pre.shiki code .sV9Aq, html code.shiki .sV9Aq{--shiki-default:#7F848E;--shiki-default-font-style:italic}html pre.shiki code .sVbv2, html code.shiki .sVbv2{--shiki-default:#61AFEF}html pre.shiki code .sVC51, html code.shiki .sVC51{--shiki-default:#D19A66}html pre.shiki code .sLaUg, html code.shiki .sLaUg{--shiki-default:#FFFFFF}html pre.shiki code .s_ZVi, html code.shiki .s_ZVi{--shiki-default:#E06C75;--shiki-default-font-style:italic}",{"title":1646,"searchDepth":1647,"depth":1647,"links":29112},[29113,29114],{"id":26126,"depth":1647,"text":26127},{"id":26485,"depth":1647,"text":26486,"children":29115},[29116,29117,29118,29119,29120,29121,29122,29123,29124,29125,29126,29127,29128,29129,29130,29131],{"id":26515,"depth":1652,"text":26516},{"id":26659,"depth":1652,"text":26660},{"id":26678,"depth":1652,"text":26679},{"id":27339,"depth":1652,"text":27340},{"id":27358,"depth":1652,"text":27359},{"id":27439,"depth":1652,"text":27440},{"id":27678,"depth":1652,"text":27679},{"id":27718,"depth":1652,"text":27719},{"id":27899,"depth":1652,"text":27900},{"id":28103,"depth":1652,"text":28104},{"id":28359,"depth":1652,"text":28360},{"id":28393,"depth":1652,"text":28394},{"id":28418,"depth":1652,"text":28419},{"id":28798,"depth":1652,"text":28799},{"id":1620,"depth":1652,"text":1621},{"id":29061,"depth":1652,"text":29062},"2020-11-29",{"type":9,"value":29134},[29135,29137,29139,29153],[12,29136,26097],{},[12,29138,26100],{},[506,29140,29141,29145,29149],{},[370,29142,29143],{},[49,29144,26108],{"href":26107},[370,29146,29147],{},[49,29148,26114],{"href":26113},[370,29150,29151],{},[49,29152,26120],{"href":26119},[12,29154,26123],{},{"planet":1767},"\u002Fpost\u002Fcreation-api-3",{"title":26092,"description":26097},"creation-api-3","posts\u002FProgrammation\u002F2020-11-29_creation-api-3",[29161,26527,29162,3510,3511],"api","rest","HYw0wo6aBhxcGM0oSLGWN8-QiSEHsmV51HJU8dZLB8E",1777582398887]