DéveloppeurWeb.Com
    DéveloppeurWeb.Com
    • Agile Zone
    • AI Zone
    • Cloud Zone
    • Database Zone
    • DevOps Zone
    • Integration Zone
    • Web Dev Zone
    DéveloppeurWeb.Com
    Home»Uncategorized»ID automatique dans PostgreSQL : Node.js et Sequelize
    Uncategorized

    ID automatique dans PostgreSQL : Node.js et Sequelize

    janvier 25, 2023
    ID automatique dans PostgreSQL : Node.js et Sequelize
    Share
    Facebook Twitter Pinterest Reddit WhatsApp Email

    Il existe de nombreuses façons de gérer la génération d’ID dans PostgreSQL, mais j’ai choisi d’étudier ces quatre approches :

    1. Incrémentation automatique (SERIAL Type de données)
    2. Mise en cache des séquences
    3. Incrémentation de séquence avec gestion des ID côté client
    4. Génération UUID

    En fonction de votre application et de vos tables de base de données sous-jacentes, vous pouvez choisir d’utiliser une ou plusieurs de ces options. Ci-dessous, j’expliquerai comment chacun peut être réalisé dans Node.js en utilisant l’ORM Sequelize.

    1. Incrémentation automatique

    La plupart des développeurs choisissent l’option la plus simple avant d’explorer les optimisations potentielles, et je ne suis pas différent. Voici comment vous pouvez créer un champ ID auto-incrémenté dans vos définitions de modèle Sequelize :

    // Sequelize
    const { DataTypes } = require('sequelize');
    const Product = sequelize.define(
       "product",
       {
           id: {
               type: DataTypes.INTEGER,
               autoIncrement: true,
               primaryKey: true,
           },
           title: {
               type: DataTypes.STRING,
           }
       }
    );

    Si vous connaissez Sequelize, vous ne serez pas étranger à cette syntaxe, mais d’autres pourraient se demander ce qui se passe réellement sous le capot.

    Les autoIncrement flag indique à PostgreSQL de créer un id colonne avec un SERIAL Type de données. Ce type de données crée implicitement un SEQUENCEqui appartient à la products les tables id colonne.

    // PostgreSQL equivalent
    CREATE SEQUENCE products_id_seq;
    CREATE TABLE products
    (
       id INT NOT NULL DEFAULT NEXTVAL('products_id_seq'),
       title VARCHAR(255)
    );

    Lors de l’insertion d’un produit dans notre table, nous n’avons pas besoin de fournir une valeur pour idcar il est automatiquement généré à partir de la séquence sous-jacente.

    Nous pouvons simplement exécuter ce qui suit pour insérer un produit :

    // Sequelize
    await Product.create({title: "iPad Pro"});

    //PostgreSQL equivalent
    INSERT INTO products (title) VALUES ('iPad Pro');

    La suppression de notre table supprimera également la séquence créée automatiquement, products_id_seq:

    // Sequelize
    await Product.drop();

    // PostgreSQL equivalent
    DROP TABLE products CASCADE;

    Bien que cette approche soit extrêmement facile à mettre en œuvre, notre serveur PostgreSQL doit accéder à la séquence pour obtenir sa prochaine valeur à chaque écriture, ce qui a un coût de latence. Ceci est particulièrement mauvais dans les déploiements distribués.

    Maintenant que nous avons les bases, essayons d’accélérer les choses. Comme nous le savons tous, « le cache est roi ».

    2. Mise en cache des séquences

    Bien que le autoIncrement flag dans la définition du modèle Sequelize élimine totalement le besoin d’interagir directement avec les séquences, il existe des scénarios où vous pourriez envisager de le faire. Par exemple, que se passe-t-il si vous souhaitez accélérer les écritures en mettant en cache les valeurs de séquence ? N’ayez crainte, avec un petit effort supplémentaire, nous pouvons y arriver.

    Sequelize n’a pas de support API pour que cela se produise, comme indiqué sur Github, mais il existe une solution de contournement simple. En utilisant le literal fonction, nous pouvons accéder à une séquence prédéfinie dans notre modèle :

    const { literal, DataTypes } = require('sequelize');
    const Product = sequelize.define("product", {
     id: {
       type: DataTypes.INTEGER,
       primaryKey: true,
       defaultValue: literal("nextval('custom_sequence')"),
     },
    });
     
    sequelize.beforeSync(() => {
     await sequelize.query('CREATE SEQUENCE IF NOT EXISTS custom_sequence CACHE 50');
    });
     
    await sequelize.sync();

    Ce n’est pas si mal. Alors voilà ce qui a changé :

    • Nous avons créé notre propre séquence, nommée custom_sequencequi est utilisé pour définir la valeur par défaut de notre ID de produit.
    • Cette séquence est créée dans le beforeSync crochet, il sera donc créé avant la table des produits et son CACHE valeur a été fixée à 50.
    • Les defaultValue est défini sur la valeur suivante dans notre séquence personnalisée.

    Eh bien, qu’en est-il du cache ? Les séquences dans PostgreSQL peuvent éventuellement être fournies CACHE valeur lors de la création, qui alloue un certain nombre de valeurs à stocker en mémoire par session. Avec notre cache défini sur 50, voici comment cela fonctionne :

    //Database Session A
    > SELECT nextval('custom_sequence');
    1
    > SELECT nextval('custom_sequence');
    2
     
    //Database Session B
    > SELECT nextval('custom_sequence');
    51
    >
    52

    Pour une application avec plusieurs connexions de base de données, comme une exécutant des microservices ou plusieurs serveurs derrière un équilibreur de charge, chaque connexion recevra un ensemble de valeurs mises en cache. Aucune session ne contiendra de valeurs en double dans son cache, garantissant qu’il n’y a pas de collisions lors de l’insertion d’enregistrements. En fait, selon la configuration de votre base de données, vous pourriez trouver des lacunes dans votre séquencement id colonne si une connexion à la base de données échoue et est redémarrée sans utiliser toutes les valeurs allouées dans son cache. Cependant, ce n’est généralement pas un problème, car nous ne nous préoccupons que de l’unicité.

    Alors, quel est le point? Vitesse. La vitesse est le point!

    En mettant en cache les valeurs sur notre backend PostgreSQL et en les stockant en mémoire, nous sommes en mesure de récupérer très rapidement la valeur suivante. Cela permet à la base de données de s’adapter, sans avoir besoin d’obtenir à plusieurs reprises la valeur de séquence suivante à partir du nœud maître lors des écritures. Bien sûr, la mise en cache présente l’inconvénient d’une contrainte de mémoire accrue sur le serveur PostgreSQL.

    Selon votre infrastructure, cela pourrait être une optimisation valable.

    3. Séquençage côté client

    La mise en cache des séquences améliore les performances en mettant en cache les valeurs sur notre backend PostgreSQL. Comment pourrions-nous utiliser une séquence pour mettre en cache des valeurs sur notre client à la place ?

    Les séquences dans PostgreSQL ont un paramètre supplémentaire appelé INCREMENT BY qui peut être utilisé pour y parvenir :

    // DB Initialization
    const { literal, DataTypes } = require('sequelize');
    const Product = sequelize.define("product", {
     id: {
       type: DataTypes.INTEGER,
       primaryKey: true
     },
    });
     
    sequelize.beforeSync(() => {
     await sequelize.query('CREATE SEQUENCE IF NOT EXISTS custom_sequence INCREMENT BY 50');
    });
     
    await sequelize.sync();
     
    // Caller
    let startVal = await sequelize.query("SELECT nextval('custom_sequence')");
    let limit = startVal + 50;
     
    if (startVal >= limit) {
       startVal = await sequelize.query("SELECT nextval('custom_sequence')");
       limit = startVal + 50;
    }
     
    await Product.create({id: startVal, title: "iPad Pro"})
    startVal += 1;

    Ici, nous utilisons notre séquence personnalisée d’une manière légèrement différente. Aucune valeur par défaut n’est fournie à notre définition de modèle. Au lieu de cela, nous utilisons cette séquence pour définir des valeurs uniques côté client, en parcourant les valeurs de la plage d’incréments. Lorsque nous avons épuisé toutes les valeurs de cette plage, nous effectuons un autre appel à notre base de données pour obtenir la valeur suivante de notre séquence afin de « rafraîchir » notre plage.

    Voici un exemple :

    // Database Session A
     
    > SELECT nextval('custom_sequence');
    1
     
    *
     inserts 50 records
     // id 1
     // id 2
     ...
     // id 50
    *
     
    > SELECT nextval('custom_sequence');
    151
     
    // Database Session B
     
    > SELECT nextval('custom_sequence');
    51
     
    * inserts 50 records before Session A has used all numbers in its range *
     
    > SELECT nextval('custom_sequence');
    101

    Session de base de données A se connecte et reçoit la première valeur de la séquence. Base de données Session B se connecte et reçoit la valeur de 51 car nous avons défini notre INCREMENT BY valeur à 50. Comme nos solutions d’auto-incrémentation, nous pouvons nous assurer qu’il n’y a pas de collisions d’ID en référençant notre séquence PostgreSQL pour déterminer la valeur de départ de notre plage.

    Quels problèmes pourraient survenir de cette solution ?

    Eh bien, il est possible qu’un administrateur de base de données choisisse d’augmenter ou de diminuer le INCREMENT BY valeur pour une séquence particulière, sans que les développeurs d’applications soient informés de ce changement. Cela briserait la logique de l’application.

    Comment pouvons-nous bénéficier du séquençage côté client ?

    Si vous disposez d’une grande quantité de mémoire disponible sur vos nœuds de serveur d’applications, cela peut constituer un avantage potentiel en termes de performances par rapport à la mise en cache des séquences sur les nœuds de base de données.

    En fait, vous vous demandez peut-être s’il est possible d’utiliser un cache sur le client et le serveur dans la même implémentation. La réponse courte est oui. En créant une séquence avec CACHE et INCREMENT BY valeurs, nous bénéficions d’un cache côté serveur de nos valeurs de séquence et d’un cache côté client pour la prochaine valeur de notre plage. Cette optimisation des performances offre le meilleur des deux mondes si les contraintes de mémoire ne sont pas la principale préoccupation.

    Assez avec les séquences déjà. Passons aux identifiants uniques.

    4. Génération d’UUID

    Jusqu’à présent, nous avons couvert trois façons de générer des identifiants séquentiels basés sur des nombres entiers. Un autre type de données, l’identifiant universel unique (UUID), supprime entièrement le besoin de séquences.

    Un UUID est un identifiant de 128 bits, qui est livré avec la garantie d’unicité en raison de la probabilité incroyablement faible que le même identifiant soit généré deux fois.

    PostgreSQL est livré avec une extension appelée pgcrypto, qui peut être installée pour générer des UUID avec le gen_random_uuid une fonction. Cette fonction génère une valeur UUID pour une colonne de base de données, à peu près la même que nextval est utilisé avec des séquences.

    De plus, Node.js possède plusieurs packages qui génèrent des UUID, tels que, vous l’avez deviné, uuid.

    // Sequelize
    const { literal, DataTypes } = require('sequelize');
    const Product = sequelize.define(
       "product",
       {
           id: {
               type: DataTypes.UUID,
               defaultValue: literal('gen_random_uuid()')
               primaryKey: true,
           },
           title: {
               type: DataTypes.STRING,
           }
       }
    );
     
    sequelize.beforeSync(() => {
     await sequelize.query('CREATE EXTENSION IF NOT EXISTS "pgcrypto"');
    });

    // PostreSQL Equivalent
    CREATE TABLE products
    (
       id UUID NOT NULL DEFAULT gen_random_uuid(),
       title VARCHAR(255)
    );

    Cela nous permet de générer un UUID côté client, avec un côté serveur par défaut, si nécessaire.

    Une approche basée sur l’UUID apporte des avantages uniques, la nature aléatoire du type de données étant utile pour certaines migrations de données. Ceci est également utile pour la sécurité de l’API, car l’identifiant unique est…

    Share. Facebook Twitter Pinterest LinkedIn WhatsApp Reddit Email
    Add A Comment

    Leave A Reply Cancel Reply

    Catégories

    • Politique de cookies
    • Politique de confidentialité
    • CONTACT
    • Politique du DMCA
    • CONDITIONS D’UTILISATION
    • Avertissement
    © 2023 DéveloppeurWeb.Com.

    Type above and press Enter to search. Press Esc to cancel.