Kategori arşivi: Python

Bu bölüm Python ile IoT uygulamalarını kapsamaktadır.

Python ve SQLite ile Hatalı Kaydı Atlayıp İşleme Devam Etmek

Bir dosyadan ürün listesi aktardığınızı düşünün. Yüz kayıt içindeki tek bir yinelenen ürün kodu yüzünden bütün aktarımı iptal etmek istemiyorsunuz. Ancak her başarılı kaydı ayrı ayrı onaylamak da istediğiniz davranış değil: aktarımın sonunda beklenmeyen bir hata çıkarsa o ana kadar yapılan değişiklikleri birlikte geri alabilmelisiniz. Bu dersin hedefi, bu iki ihtiyacı aynı örnekte karşılamak.

SQLite’ın savepoint mekanizması, işlem içinde geri dönebileceğiniz adlandırılmış noktalar oluşturur. Böylece belirli bir kayıt için yapılan değişiklikleri geri alırken önceki kayıtları işlem içinde tutabilirsiniz. İç noktayı kaldırmak, dış işlem sürerken değişiklikleri bağımsız olarak kalıcılaştırmaz. Bu ayrım SQLite savepoint belgesinde açıklanır.

1. Önce hata politikasını belirleyin

Örneğimizde her ürünün benzersiz bir kodu ve bir adı olacak. Aynı kod ikinci kez geldiğinde o satırı reddedeceğiz. Bunun dışındaki beklenmeyen hataları ise aktarımın tamamını durduracak sorunlar olarak ele alacağız. Burada asıl karar, hangi hatanın beklenen bir veri sorunu, hangisinin işleme devam etmeyi tehlikeli kılan bir durum olduğudur.

Bu politika her projeye uygun değildir. Örneğin aynı siparişin başlık ve kalemlerini aktarıyorsanız tek kalemin atlanması eksik bir sipariş oluşturabilir. Geri alma sınırını dosyadaki satır sayısına göre değil, uygulamanın anlamlı iş birimine göre seçin. Bu derste her ürün kaydını bağımsız kabul ediyoruz.

2. İşlem yönetimini açıkça seçin

Örnek, autocommit=False parametresi nedeniyle Python 3.12 veya üstünü gerektirir. Bu ayarda bağlantının bağlam yöneticisi normal çıkışta işlemi onaylar, dışarı taşan istisnada geri alır. Bağlantıyı kapatmak ise ayrıca gerekir. Parametre ve bağlam yöneticisinin davranışı Python sqlite3 belgelerinde tanımlanır.

Ders boyunca dış işlemin sahibi with con: bloğu olacak. Döngü içindeki kod yalnızca kendi savepoint sınırını yönetecek. Böylece kodu okurken iki ayrı soruyu takip edebiliriz: Bu satır kabul edildi mi? Aktarımın bütünü onaylandı mı? Bunlar aynı karar değildir.

3. Çalıştırılabilir aktarımı yazın

Aşağıdaki kodu aktarim.py adıyla kaydedip python aktarim.py komutuyla çalıştırabilirsiniz. Örnek bellek içi veritabanı kullanır; amacı kalıcı dosya üretmek değil, geri alma sınırlarını görünür kılmaktır. Kullanılan bağlantı ve parametre bağlama API’leri resmî Python başvurusunda bulunur.

import sqlite3


def aktar(con, kayitlar):
    kabul = 0
    reddedilen = []

    with con:  # Dış işlem: tamamını onayla veya geri al.
        for sira, (kod, ad) in enumerate(kayitlar, start=1):
            con.execute("SAVEPOINT satir")
            try:
                con.execute(
                    "INSERT INTO urun (kod, ad) VALUES (?, ?)",
                    (kod, ad),
                )
            except sqlite3.IntegrityError:
                con.execute("ROLLBACK TO satir")
                reddedilen.append(sira)
            else:
                kabul += 1
            finally:
                con.execute("RELEASE satir")

    return kabul, reddedilen


con = sqlite3.connect(":memory:", autocommit=False)
try:
    con.execute("""
        CREATE TABLE urun (
            kod TEXT PRIMARY KEY NOT NULL,
            ad TEXT NOT NULL
        )
    """)
    con.commit()  # Tablo kurulumunu aktarım işleminden ayır.

    kayitlar = [
        ("K-01", "Klavye"),
        ("F-01", "Fare"),
        ("K-01", "Yinelenen klavye"),
        ("E-01", "Ekran"),
    ]

    kabul, reddedilen = aktar(con, kayitlar)
    print("Kabul:", kabul)
    print("Reddedilen satırlar:", reddedilen)
    print(con.execute(
        "SELECT kod, ad FROM urun ORDER BY kod"
    ).fetchall())
finally:
    con.close()

Kod akışından beklenen sonuç üç kabul ve üçüncü satırın reddedilmesidir. Son sorguda E-01, F-01 ve K-01 kodlu ürünler görünmelidir; K-01 ürününün adı ilk kayıttaki gibi kalmalıdır. Bu, çalıştırılarak elde edilmiş bir ölçüm değil, editörün yerel ortamda doğrulayabileceği beklenen davranıştır.

4. Geri alma ile noktayı kaldırmayı ayırın

Döngünün başında oluşturulan satir noktası, o kaydın sınırını belirler. Ekleme başarılıysa nokta kaldırılır. Benzersizlik ihlalinde önce ROLLBACK TO satir çalışır, ardından nokta kaldırılır. Bunun nedeni, ROLLBACK TO komutunun hedef savepoint’i kendiliğinden silmemesidir. SQLite’ın tanımladığı kurallar bu iki komutu ayrı görevler olarak ele alır.

Üçüncü kayıt reddedildiğinde önceki iki kayıt henüz dış işlemin parçasıdır. Dördüncü kayıt da eklendikten sonra blok normal tamamlanırsa aktarım onaylanır. Başka bir istisna blok dışına çıkarsa bu örneğin hedeflediği davranış, başarılı eklemeleri de birlikte geri almaktır. Böylece “satır reddedildi” ile “aktarım başarısız oldu” durumlarını ayrı raporlayabiliriz.

Örnekte yalnızca IntegrityError beklenen kayıt hatası kabul edilir. Tabloya yeni kısıtlar eklerseniz bu hata sınıfının artık farklı ret nedenlerini kapsayabileceğini hesaba katın. Üretim sürümünde satır numarasının yanında anlaşılır ret nedeni de tutmayı planlayın; bütün sorunları “yinelenen kod” diye etiketlemeyin.

5. Küçük değişikliklerle davranışı sınayın

  • Yinelenen kodu benzersiz yapın; dört kaydın kabul edilmesini bekleyin.
  • İlk ve son kaydı aynı kodla deneyin; yalnızca sonuncunun reddedildiğini kontrol edin.
  • Döngü içinde ikinci kayıttan sonra geçici bir RuntimeError üretin. İstisnayı çağıran tarafta yakalayıp tablonun boş kaldığını doğrulayın.
  • Boş liste gönderin; kabul sayısının sıfır, ret listesinin boş olmasını bekleyin.

Testleri birbirinden bağımsız, yeni veritabanlarıyla kurun. Aksi hâlde önceki denemenin kayıtları sonraki denemede benzersizlik hatası oluşturabilir ve yanlış sonuca varabilirsiniz. Özellikle hata senaryosunda yalnızca istisnanın oluştuğunu değil, geride hangi kayıtların kaldığını da denetleyin.

6. Karmaşıklık ve kullanım sınırı

Bu örneğin Python katmanı, n kayıt için döngüyü n kez yürütür; dolayısıyla veritabanı işlemleri hariç kontrol akışı O(n) zaman alır. Reddedilen r satırın numarasını tuttuğumuz için ek Python belleği O(r), en kötü durumda O(n) olur. Bu değerlendirme SQL çalıştırma, indeks bakımı ve işlem günlüklerinin maliyetini kapsamaz; bütün aktarımın süresini yalnızca O(n) diye sunmak yanıltıcı olur.

Savepoint kullanmak eşzamanlı yazma kapasitesini artıran bir yöntem değildir. SQLite aynı anda tek yazma işlemine izin verir; büyük bir aktarım diğer yazarları bekletebilir. Bu sınır SQLite işlem belgesinde belirtilir. Aktarımı parçalara bölmeyi düşünürseniz bunun tüm dosyayı tek seferde geri alma hedefini değiştireceğini de değerlendirin.

Son olarak bu ders, veritabanı değişikliklerini yönetir; e-posta gönderme veya dış serviste işlem oluşturma gibi adımlar örneğin kapsamı dışındadır. İlk uygulamanızda akışı yalnızca kayıt ekleme ve sonuç raporlama ile sınırlayın. Sonra iş kurallarınızı genişletirken hangi adımın hangi hata durumunda geri alınacağını açıkça yazın.

Kaynaklar

Loading

Facebooktwitterredditpinterestlinkedinmail