Maîtriser PostgreSQL
Thème :
Junior
2 vues ·5/9 leçons

LEÇON 5 : Transactions et Gestion d’Erreurs

5.1 Transactions : ACID et Intégrité

Problématique Entreprise

Un système de paiement doit garantir qu’un débit et un crédit sont toujours exécutés ensemble, sans état intermédiaire visible.

Transaction Basique

-- Transfert d'argent entre comptes
BEGIN;

-- Débit compte source
UPDATE accounts
SET balance = balance - 500
WHERE account_id = 1001;

-- Crédit compte destination
UPDATE accounts
SET balance = balance + 500
WHERE account_id = 2002;

-- Log de la transaction
INSERT INTO transactions (from_account, to_account, amount, transaction_date)
VALUES (1001, 2002, 500, NOW());

COMMIT;  -- Valide tout ou ROLLBACK; -- Annule tout

Points de Sauvegarde (SAVEPOINT)

-- Traitement de commande complexe avec étapes
BEGIN;

-- Étape 1 : Créer la commande
INSERT INTO orders (customer_id, order_date, total_amount)
VALUES (123, NOW(), 1500.00)
RETURNING order_id INTO v_order_id;

SAVEPOINT after_order_creation;

-- Étape 2 : Ajouter les lignes
INSERT INTO order_lines (order_id, product_id, quantity, unit_price)
VALUES
    (v_order_id, 5001, 2, 500.00),
    (v_order_id, 5002, 1, 500.00);

SAVEPOINT after_lines_insertion;

-- Étape 3 : Déduire le stock
UPDATE products
SET stock_quantity = stock_quantity - 2
WHERE product_id = 5001;

UPDATE products
SET stock_quantity = stock_quantity - 1
WHERE product_id = 5002;

-- Si erreur de stock, annuler seulement les mises à jour de stock
-- ROLLBACK TO after_lines_insertion;

-- Tout OK, valider
COMMIT;

Exemple Réel : Système de Réservation

-- Réservation de place de concert (avec gestion de concurrence)
CREATE OR REPLACE FUNCTION book_ticket(
    p_event_id BIGINT,
    p_user_id BIGINT,
    p_seat_number VARCHAR
) RETURNS BIGINT AS $
DECLARE
    v_booking_id BIGINT;
    v_available_seats INT;
BEGIN
    -- Démarrer une transaction implicite

    -- 1. Vérifier la disponibilité avec verrou
    SELECT available_seats INTO v_available_seats
    FROM events
    WHERE event_id = p_event_id
    FOR UPDATE;  -- Verrou exclusif

    IF v_available_seats <= 0 THEN
        RAISE EXCEPTION 'Plus de places disponibles';
    END IF;

    -- 2. Vérifier que le siège n'est pas déjà réservé
    IF EXISTS (
        SELECT 1 FROM bookings
        WHERE event_id = p_event_id
          AND seat_number = p_seat_number
          AND status != 'cancelled'
    ) THEN
        RAISE EXCEPTION 'Siège déjà réservé : %', p_seat_number;
    END IF;

    -- 3. Créer la réservation
    INSERT INTO bookings (event_id, user_id, seat_number, status, booked_at)
    VALUES (p_event_id, p_user_id, p_seat_number, 'confirmed', NOW())
    RETURNING booking_id INTO v_booking_id;

    -- 4. Décrémenter les places disponibles
    UPDATE events
    SET available_seats = available_seats - 1
    WHERE event_id = p_event_id;

    -- 5. Retourner l'ID de réservation
    RETURN v_booking_id;

EXCEPTION
    WHEN OTHERS THEN
        -- En cas d'erreur, rollback automatique
        RAISE NOTICE 'Erreur réservation : %', SQLERRM;
        RETURN NULL;
END;
$ LANGUAGE plpgsql;

-- Utilisation
SELECT book_ticket(100, 456, 'A12');

5.2 Niveaux d’Isolation

Comprendre les Problèmes de Concurrence

Niveau Dirty Read Non-Repeatable Read Phantom Read Performance
Read Uncommitted ❌ Oui ❌ Oui ❌ Oui ⚡ Très rapide
Read Committed (défaut PG) ✅ Non ❌ Oui ❌ Oui ⚡ Rapide
Repeatable Read ✅ Non ✅ Non ❌ Oui (PostgreSQL: ✅ Non) 🐢 Moyen
Serializable ✅ Non ✅ Non ✅ Non 🐌 Lent

Configuration des Niveaux

-- Read Committed (défaut PostgreSQL, recommandé pour 95% des cas)
BEGIN TRANSACTION ISOLATION LEVEL READ COMMITTED;
-- Voit les commits des autres transactions

-- Repeatable Read (pour rapports cohérents)
BEGIN TRANSACTION ISOLATION LEVEL REPEATABLE READ;
-- Snapshot au début de la transaction

-- Serializable (pour logique métier critique)
BEGIN TRANSACTION ISOLATION LEVEL SERIALIZABLE;
-- Comme si les transactions s'exécutaient en série

Exemple Réel : Report Financier Cohérent

-- Générer un rapport comptable avec snapshot cohérent
BEGIN TRANSACTION ISOLATION LEVEL REPEATABLE READ;

-- Toutes ces requêtes verront le même état de la base
SELECT SUM(balance) FROM accounts WHERE account_type = 'checking';
SELECT SUM(balance) FROM

Prêt à passer à la suite ?

Vous avez terminé cette leçon sur LEÇON 5 : Transactions et Gestion d'Erreurs. Continuez votre apprentissage avec la prochaine leçon ou explorez d'autres formations.