Correction Phase 2

Phase 2 : Cohérence et intégrité des données

Énigme 4

Objectif 1 : Gestion des états incohérents [Code réponse 1]

Le but de cet exercice est que la requête de changement de quantité ne marche pas. Si la requête fonctionne c’est que la version de votre base de données n’est pas la bonne. Le mieux est de suivre le protocole pour faire un resetdb. Une alternative est de changer manuellement la quantité :

UPDATE merchant_products mp
SET available_quantity = 1
FROM transaction_details td
WHERE mp.merchant_id = td.merchant_id 
AND mp.product_id = td.product_id
AND td.transaction_id = 733;

Lors de l’exécution des requêtes de mise à jour, nous obtenons l’erreur suivante :

RuntimeError: (psycopg2.errors.CheckViolation) new row for relation "merchant_products" 
violates check constraint "merchant_products_available_quantity_check"
DETAIL: Failing row contains (1, 2, 1234.99, -1, 2024-11-06 13:49:24.125974).

Analyse du Problème :

  1. Cause de l’Erreur :

    • La requête tente de mettre à jour le stock à une valeur négative (-1)
    • Une contrainte CHECK dans la table merchant_products empêche les quantités négatives
    • Cette contrainte est une protection essentielle pour maintenir l’intégrité des données
  2. Séquence des Événements :

    • La première requête a modifié le statut de la transaction à COMPLETED
    • La deuxième requête a échoué en essayant de décrémenter le stock
    • Nous nous retrouvons dans un état incohérent : transaction validée mais stock non ajusté
  3. Implications :

    • Les deux opérations sont interdépendantes mais non atomiques
    • Un échec de la deuxième opération laisse la base dans un état invalide
    • Le système ne vérifie pas la disponibilité du stock avant de valider la transaction

Objectif 2 [Code réponse CANCELED,1]

import psycopg2

conn = psycopg2.connect(
    host="localhost",
    database="ue_info_reseaux_db",
    user="user_tp",
    password="ue_info_reseaux_db",
    options="-c search_path=game"
)

cur = conn.cursor()

try:
    cur.execute("""
        BEGIN;
        
        UPDATE transactions 
        SET status = 'COMPLETED'
        WHERE transaction_id = 733;
        
        UPDATE merchant_products mp
        SET available_quantity = mp.available_quantity - td.quantity
        FROM transaction_details td
        WHERE mp.merchant_id = td.merchant_id 
        AND mp.product_id = td.product_id
        AND td.transaction_id = 733;
        
        COMMIT;
    """)
except Exception as e:
    print("Erreur:", e)
    conn.rollback()
finally:
    cur.close()
    conn.close()

Retourne :

Erreur: new row for relation "merchant_products" violates check constraint
"merchant_products_available_quantity_check"
DETAIL:  Failing row contains (1, 2, 1234.99, -1, 2025-03-05 09:26:13.972379).

Cette solution :

  1. Regroupe les deux opérations dans une seule transaction atomique
  2. Si le stock est insuffisant, la contrainte CHECK déclenchera une erreur
  3. Le ROLLBACK annulera automatiquement le changement de statut
  4. La base reste dans un état cohérent

Avantages de cette approche :

  • Pas besoin de vérifications explicites du stock
  • Utilisation des contraintes natives de la base de données
  • Gestion automatique des erreurs par PostgreSQL
  • Garantie de l’atomicité des opérations

Énigme 5

Objectif 1 [Code réponse COMPLETED]

Objectif 2 [Code réponse 1]

Déroulement chronologique

  1. T+0s : Démarrage Client A

    • Le Client A démarre sa transaction
    • Il acquiert un verrou exclusif sur la ligne de stock en exécutant l’UPDATE
    • Il entre dans sa phase d’attente de 30 secondes
  2. T+5s : Tentative Client B

    • Le Client B démarre sa transaction
    • Il tente d’acquérir un verrou sur la même ligne de stock
    • Il est mis en attente car le verrou est déjà détenu par A
  3. T+30s : Fin Client A

    • Le Client A termine son attente
    • Il valide sa transaction (COMMIT)
    • Les verrous sont libérés
  4. T+30s : Échec Client B

    • Le Client B tente de reprendre son exécution
    • L’UPDATE échoue car le stock est maintenant insuffisant
    • La transaction est annulée (ROLLBACK)
    • Message d’erreur :
new row for relation "merchant_products" violates check constraint
"merchant_products_available_quantity_check"

Explication des mécanismes

Le comportement observé illustre plusieurs concepts clés de la gestion de concurrence :

  1. Verrouillage (Locking)

    • PostgreSQL utilise un système de verrous pour garantir l’isolation des transactions
    • L’UPDATE acquiert automatiquement un verrou exclusif
    • Les autres transactions doivent attendre la libération du verrou
  2. Files d’attente

    • Les transactions en attente sont mises en queue
    • Elles sont réactivées dans l’ordre d’arrivée
    • La première transaction conserve sa priorité
  3. Contraintes d’intégrité

    • La contrainte CHECK sur available_quantity reste active
    • Elle empêche les stocks négatifs même en cas de concurrence
    • C’est elle qui provoque l’échec final de la transaction du Client B

Protection contre la survente

Ce mécanisme protège effectivement contre la survente de plusieurs façons :

  1. Isolation

    • Une seule transaction peut modifier le stock à la fois
    • Les modifications sont atomiques
    • Pas de risque de lectures/écritures simultanées
  2. Contraintes

    • La contrainte CHECK forme une seconde ligne de défense
    • Même en cas de bug, le stock ne peut pas devenir négatif
  3. Ordre de traitement

    • Les transactions sont traitées séquentiellement
    • Premier arrivé, premier servi

Valeurs finales

À la fin de l’expérience :

  • Stock final : 0 (toutes les unités ont été réservées par le Client A)
  • Statut transaction A : COMPLETED
  • Statut transaction B : ROLLBACK

Cette solution de gestion de la concurrence est robuste mais présente aussi des limitations :

  • Temps d’attente potentiellement long
  • Risque de deadlock dans des scénarios plus complexes
  • Impact sur les performances en cas de forte concurrence

Dans un système réel, on pourrait envisager des améliorations comme :

  • Des timeouts sur les verrous
  • Un système de réservation temporaire
  • Une file d’attente applicative plutôt que base de données