CodeGym /Kurslar /SQL SELF /PostgreSQL-də iç-içə transaction-larla işləyərkən tipik s...

PostgreSQL-də iç-içə transaction-larla işləyərkən tipik səhvlərin analizi

SQL SELF
Səviyyə , Dərs
Mövcuddur

Postgres-də proqramlaşdırma — əsl macəradır: bəzən bu, “Səhvini tap” adlı bir questə çevrilir. Bu blokda iç-içə transaction-larla işləyərkən rast gələ biləcəyin tipik səhvlər və tələlərdən danışacağıq. Başlayaq!

Funksiya və prosedurların içində transaction komandalarının səhv istifadəsi

Səhv: COMMIT, ROLLBACK və ya SAVEPOINT komandalarını FUNCTION daxilində istifadə etmək cəhdi.

Niyə: PostgreSQL-də funksiyalar (CREATE FUNCTION ... LANGUAGE plpgsql) həmişə bir xarici transaction daxilində icra olunur və funksiyanın içində hər hansı transaction komandası qadağandır. Onlardan istifadə etməyə cəhd etsən, syntax error alacaqsan.

Səhv nümunəsi:

CREATE OR REPLACE FUNCTION f_bad() RETURNS void AS $$
BEGIN
    SAVEPOINT sp1;  -- Səhv: transaction komandaları qadağandır
END;
$$ LANGUAGE plpgsql;

Necə düzgün:

“Hamısı və ya heç nə” tipli atomik əməliyyatlar üçün transaction komandalarını açıq şəkildə yazmadan funksiyalardan istifadə et. Əgər dəyişiklikləri mərhələli şəkildə saxlamaq lazımdırsa — prosedurlardan istifadə et.

Səhv: PL/pgSQL-də ROLLBACK TO SAVEPOINT istifadə etmək cəhdi.

Niyə: PostgreSQL 17-də yalnız COMMIT, ROLLBACK, SAVEPOINT, RELEASE SAVEPOINT komandalarına prosedurların (CREATE PROCEDURE ... LANGUAGE plpgsql) içində icazə verilir. Amma ROLLBACK TO SAVEPOINT PL/pgSQL-də istifadə oluna bilməz! Belə cəhd syntax error ilə nəticələnəcək.

Səhv nümunəsi:

CREATE PROCEDURE p_bad()
LANGUAGE plpgsql
AS $$
BEGIN
    SAVEPOINT sp1;
    -- ...
    ROLLBACK TO SAVEPOINT sp1; -- Səhv! İstifadə etmək olmaz
END;
$$;

Necə düzgün:

“Qismən rollback” üçün BEGIN ... EXCEPTION ... END bloklarından istifadə et — onlar avtomatik savepoint yaradır; blokun içində error olsa, bütün dəyişikliklər blokun əvvəlindəki vəziyyətə qaytarılır.

CREATE PROCEDURE p_good()
LANGUAGE plpgsql
AS $$
BEGIN
    BEGIN
        -- error verə biləcək əməliyyatlar
        ...
    EXCEPTION
        WHEN OTHERS THEN
            RAISE NOTICE 'BEGIN ... EXCEPTION ... END blokunda rollback oldu';
    END;
END;
$$;

İç-içə prosedur çağırışları: məhdudiyyətlər və tipik səhvlər

Səhv: artıq açıq olan client transaction-da içində COMMIT/ROLLBACK olan prosedurun çağırılması.

Niyə: transaction control-lu prosedurlar yalnız autocommit rejimində (bir prosedur — bir transaction) düzgün işləyir, əks halda prosedurun içində COMMIT və ya ROLLBACK istifadə etməyə cəhd etsən, error alacaqsan: transaction artıq client səviyyəsində açıqdır.

Nümunə:

# Python-da psycopg2 ilə default olaraq autocommit=False
cur.execute("BEGIN;")
cur.execute("CALL my_proc();")   -- my_proc içində COMMIT cəhdində səhv

Necə düzgün:

  • Prosedurları çağırmazdan əvvəl connection-u autocommit rejiminə keçir.
  • Prosedurları funksiyalar və ya SELECT ilə çağırma.

Səhv: transaction control-lu prosedurların (COMMIT, ROLLBACK) CALL komandası ilə deyil (məsələn, SELECT ilə) çağırılması işləmir.

Niyə: Yalnız CALL (və ya anonim DO-blokunda) çağırış transaction-ları idarə etməyə imkan verir. Funksiyadan çağırmaq olmaz.

Lock-lar və deadlock-larla bağlı problemlər

Lock-lar — istəmədiyin qonaqlar kimidir: əvvəlcə mane olurlar, sonra xaos yaradırlar. Deadlock isə o vaxt yaranır ki, transaction-lar bir-birini sonsuz gözləyir. Tipik nümunə:

  1. Transaction A orders cədvəlində bir sətri bloklayır və products cədvəlində sətri yeniləməyə çalışır.
  2. Transaction B products cədvəlində bir sətri bloklayır və orders cədvəlində sətri yeniləməyə çalışır.

Nəticədə heç bir transaction davam edə bilmir. Bu, iki maşının dar döngədə eyni anda dönməyə çalışmasına bənzəyir — nəticə: tıxac.

Nümunə:

-- Transaction A
BEGIN;
UPDATE orders SET status = 'Processing' WHERE id = 1;

-- Transaction B
BEGIN;
UPDATE products SET stock = stock - 1 WHERE id = 10;

-- İndi transaction A `products`-da eyni sətri yeniləməyə çalışır,
-- transaction B isə `orders`-da sətri dəyişməyə çalışır.
-- Deadlock!

Necə qaçmaq olar?

  1. Həmişə məlumatları eyni ardıcıllıqla yenilə. Məsələn, əvvəl orders, sonra products.
  2. Çox uzun transaction-lardan qaç.
  3. LOCK-dan ağılla istifadə et, minimal lock səviyyəsini seç.

Dinamik SQL-in (EXECUTE) səhv istifadəsi

Dinamik SQL-i ehtiyatsız istifadə etsən, başağrısı olacaq. Ən yayılmış səhv — SQL injection. Məsələn:

EXECUTE 'SELECT * FROM orders WHERE id = ' || user_input;

Əgər user_input belə bir şeydirsə: 1; DROP TABLE orders;, o zaman orders cədvəli ilə sağollaş.

Necə qaçmaq olar? Hazır (prepared) query-lərdən istifadə et:

EXECUTE 'SELECT * FROM orders WHERE id = $1' USING user_input;

Bu yanaşma tətbiqini SQL injection-dan qoruyacaq.

Error handling düzgün olmadıqda transaction rollback

Əgər error-lar düzgün idarə olunmazsa, transaction qeyri-valid vəziyyətdə qala bilər. Məsələn:

BEGIN;

INSERT INTO orders (order_id, status) VALUES (1, 'Pending');

BEGIN;
-- Hansısa əməliyyat, error verir
INSERT INTO non_existing_table VALUES (1);
-- Error, amma transaction bitmədi

COMMIT; -- Səhv: cari transaction pozulub

Error baş verdiyinə görə bütün kod ilişib qalır.

Necə qaçmaq olar? Düzgün rollback üçün EXCEPTION bloklarından istifadə et:

BEGIN
    INSERT INTO orders (order_id, status) VALUES (1, 'Pending');
EXCEPTION
    WHEN OTHERS THEN
        ROLLBACK;
        RAISE NOTICE 'Səhv baş verdi, transaction rollback olacaq.';
END;

Səhvlərdən qaçmaq üçün məsləhətlər və tövsiyələr

  • Çətin prosedur yazanda həmişə əvvəlcə pseudokoddan başla. Bütün addımları və mümkün error nöqtələrini yaz.
  • Transaction-ları izolyasiya etmək üçün SAVEPOINT-dan istifadə et. Amma istifadə etdikdən sonra onları azad etməyi unutma.
  • Çox uzun transaction-lardan qaç — transaction nə qədər uzun olsa, lock ehtimalı bir o qədər çoxdur.
  • İç-içə prosedur çağırışlarında xarici və daxili transaction kontekstlərinin düzgün sinxronizasiya olunduğuna əmin ol.
  • Prosedurlarının performansını həmişə EXPLAIN ANALYZE ilə yoxla.
  • Error-ları cədvələ və ya text fayllara log et — bu, debugging-i asanlaşdıracaq.

Səhv nümunələri və onların düzəldilməsi

Nümunə 1: İç-içə prosedur çağırışında səhv

Səhv kod:

BEGIN;

CALL process_order(5);

-- process_order içində ROLLBACK oldu
-- Bütün transaction qeyri-valid olur
COMMIT; -- Səhv

Düzgün kod:

BEGIN;

SAVEPOINT sp_outer;

CALL process_order(5);

-- Səhv olsa, yalnız rollback
ROLLBACK TO SAVEPOINT sp_outer;

COMMIT;

Nümunə 2: Deadlock problemi

Səhv kod:

-- Transaction A
BEGIN;
UPDATE orders SET status = 'Processing' WHERE id = 1;
-- `products` gözləyir

-- Transaction B
BEGIN;
UPDATE products SET stock = stock - 1 WHERE id = 10;
-- `orders` gözləyir

Düzəliş:

-- Hər iki query eyni ardıcıllıqla icra olunur:
-- Əvvəlcə `products`, sonra `orders`.
BEGIN;
UPDATE products SET stock = stock - 1 WHERE id = 10;
UPDATE orders SET status = 'Processing' WHERE id = 1;
COMMIT;

Bu səhvlər göstərir ki, transaction-larla işləmək diqqət və təcrübə tələb edir. Amma bilirsən də, nə qədər çox praktika olsa, real həyatda (və karyerada) ROLLBACK alma ehtimalın bir o qədər az olar.

1
Sorğu/viktorina
, səviyyə, dərs
Əlçatan deyil
Daxili prosedurlar
Daxili prosedurlar
Şərhlər
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION