İkinci bir BEGIN gönderdiğinizde PostgreSQL iç içe bir işlem oluşturmaz. Uyarı verir ve geçerli işlemi sürdürür; başarılı ilk COMMIT işlemi bitirir. Bir yardımcı işlevin kısmi geri alma yapması gerektiğinde kaydetme noktalarını kullanın. PostgreSQL: BEGIN
Bu kılavuz, Mac Terminal'de çalışan bir geliştiricinin makale düzenleme işlemini çok erken işleyen bir yardımcı işlevi teşhis etmesine yardımcı olur. Örneklerde sentetik makale satırları kullanılır ve bir yardımcı işlevin kendi toplu işlemini geri almasına izin verirken tek bir işlem sahibinin nasıl korunacağı gösterilir. Güncel PostgreSQL ve node-postgres belgelerini izler.
BEGIN ve COMMIT başka bir işlemin içinde kullanıldığında ne olur?
SQL işlem komutları, bağlantının geçerli işlemi üzerinde çalışır. PostgreSQL tekrarlanan BEGIN çağrılarını sayıp eşleşen COMMIT çağrılarını beklemez. İkinci bir BEGIN işlemi değiştirmez; başarılı bir yalın COMMIT geçerli işlemi işler. Ardından başka bir yalın COMMIT çalıştırmak, açık bir işlem olmadığından uyarı üretir. PostgreSQL: BEGIN, PostgreSQL: COMMIT
Sağlıklı bir işlemde sınırların izlediği sıra şöyledir:
| Tek bir bağlantıda gönderilen komut | Oluşan sınır |
|---|---|
İlk BEGIN |
İşlemi başlatır |
| İlk makale ekleme | Bu işleme aittir |
İkinci BEGIN |
Uyarı verir; iç içe bir sınır oluşturmaz |
| İkinci makale ekleme | Aynı işleme aittir |
İlk COMMIT |
Her iki eklemeyi işler ve işlemi bitirir |
| Ayrı olarak gönderilen başka bir ifade | Normal otomatik işleme davranışında kendi işleminde çalışır |
Daha sonraki ROLLBACK |
Önceden işlenmiş eklemeleri geri alamaz |
Bu, sık görülen bir uygulama hatasını açıklar: çağıran bir işlem başlatır, ardından kendi BEGIN ve COMMIT komutlarını gönderen bir yardımcı işlevi çağırır. Yardımcı işlevin COMMIT komutu çağıranın işlemini de bitirir. Çağıranda daha sonra oluşan bir istisna, başlangıçtaki ya hep ya hiç işlemini geri yükleyemez. Bu sonuç, PostgreSQL'in belgelenmiş işlem sınırlarından çıkar. PostgreSQL: Transactions
Mac Terminal'de erken işlemi yeniden oluşturma
Çalışan bir PostgreSQL veritabanına ve bağlantı ayarları düzgün çalışan, kurulu bir psql komutuna ihtiyacınız var. Terminal'i açın ve psql -X ile yeni bir oturum başlatın. -X seçeneği başlangıç dosyalarını atlayarak bu gösterimin yerel özelleştirmelere daha az bağımlı olmasını sağlar. Aşağıdaki betiği bu oturumda çalıştırın; otomatik işlemeyi açıkça etkinleştirir. PostgreSQL: psql
Tablo geçicidir ve yalnızca sentetik makale başlıkları içerir. ON COMMIT PRESERVE ROWS, işlem sınırları boyunca satırları incelemeye açık tutar; oturum sona erdiğinde PostgreSQL geçici tabloyu siler. Bu örnek, bağlantı kesildikten sonra kalıcı depolamayı değil, oturum içindeki geri alma davranışını gösterir. PostgreSQL: CREATE TABLE
\set AUTOCOMMIT on
CREATE TEMP TABLE article_boundary_demo (
id integer PRIMARY KEY,
title text NOT NULL
) ON COMMIT PRESERVE ROWS;
BEGIN;
INSERT INTO article_boundary_demo VALUES (1, 'Opening draft');
-- Deliberately incorrect attempt to open an inner transaction.
BEGIN;
INSERT INTO article_boundary_demo VALUES (2, 'Middle draft');
COMMIT;
-- The previous COMMIT has already ended the transaction.
INSERT INTO article_boundary_demo VALUES (3, 'Closing draft');
ROLLBACK;
SELECT id, title FROM article_boundary_demo ORDER BY id;
Beklenen sonuçta üç satırın tümü bulunur. İkinci BEGIN, bir işlemin zaten devam ettiği konusunda uyarı verir. COMMIT, 1 ve 2 satırlarını işler; ardından 3 satırı otomatik işleme yoluyla işlenir. Son ROLLBACK komutunun geri alabileceği açık bir işlem yoktur. Bunlar, veritabanında çalıştırıldığı bildirilmiş sonuçlar değil, belgelenmiş davranıştan çıkarılan beklenen sonuçlardır. PostgreSQL: BEGIN, PostgreSQL: Transactions
Bu yeniden oluşturmayı gösterildiği gibi etkileşimli psql içinde tutun. İstemcinin sorguları gönderme biçimi önemlidir: Tek bir psql -c dizgesiyle birlikte iletilen birkaç SQL ifadesi, açık işlem komutları bunları ayırmadıkça örtük bir işlemi paylaşabilir. Belgeler, ayrı yürütme amaçlandığında ayrı komutlar veya standart girdi kullanılmasını önerir. PostgreSQL: psql
Kaydetme noktaları dış işlemi nasıl korur?
Kaydetme noktası, mevcut bir işlem içinde bir konumu işaretler. ROLLBACK TO SAVEPOINT, bu konumdan sonraki değişiklikleri atarken önceki çalışmaları korur. RELEASE SAVEPOINT, işaretçiyi kaldırır ve kalan değişiklikleri kapsayan işlem içinde tutar. Bu değişiklikleri kendi başına işlemez. PostgreSQL: SAVEPOINT, PostgreSQL: RELEASE SAVEPOINT
Bu eksiksiz örneği aynı oturumda veya başka bir yeni psql oturumunda çalıştırın. Örnek kendi geçici tablosunu oluşturur:
CREATE TEMP TABLE article_savepoint_demo (
id integer PRIMARY KEY,
title text NOT NULL
) ON COMMIT PRESERVE ROWS;
BEGIN;
INSERT INTO article_savepoint_demo VALUES (1, 'Opening draft');
SAVEPOINT optional_article;
INSERT INTO article_savepoint_demo VALUES (2, 'Optional draft');
ROLLBACK TO SAVEPOINT optional_article;
RELEASE SAVEPOINT optional_article;
INSERT INTO article_savepoint_demo VALUES (3, 'Closing draft');
COMMIT;
SELECT id, title FROM article_savepoint_demo ORDER BY id;
Beklenen satırlar 1 ve 3 olur. 2 satırı kaydetme noktasında atılır; açılış ve kapanış satırları son COMMIT komutuna kadar tek bir işlemin parçası olarak kalır. Son COMMIT komutunu ROLLBACK ile değiştirmek, hayatta kalan iki eklemeyi de atar. PostgreSQL: SAVEPOINT, PostgreSQL: Transactions
Yardımcı işlevleri birleştirirken kaydetme noktalarının ömrü de önemlidir. Bir kaydetme noktasına geri dönmek, adı belirtilen kaydetme noktasını kullanılabilir durumda bırakır ancak daha sonraki kaydetme noktalarını yok eder. Bir kaydetme noktasını serbest bırakmak, onu ve ondan sonra oluşturulmuş tüm etkin kaydetme noktalarını kaldırır. Örnek, kurtarma sonrasında işaretçiyi açıkça serbest bırakır. PostgreSQL: ROLLBACK TO SAVEPOINT, PostgreSQL: RELEASE SAVEPOINT
node-postgres yardımcı işlevleri işlemi nasıl paylaşmalı?
node-postgres, bir işlemdeki her ifadenin aynı istemci örneğini kullanmasını gerektirir ve iç içe işlem için otomatik bir soyutlama sunmaz. Çağıranın istemcisini yardımcı işlevlere iletin. Uygulama bir havuz kullanıyorsa pool.connect() ile istemci alın, işlem boyunca bu istemciyi kullanın ve ardından serbest bırakın; pool.query() art arda gelen sorguları farklı istemcilere yönlendirebilir. node-postgres: Transactions, node-postgres: Pool
Aşağıdaki eksiksiz program tek bir doğrudan Client kullanır. Çağıran, BEGIN, COMMIT ve tam ROLLBACK işleminin sahibidir. Yardımcı işlev, isteğe bağlı iki satırlık bir toplu işlem için kaydetme noktası oluşturur. İkinci ekleme, 1 makale kimliğini bilerek tekrar ederek benzersizlik hatasına neden olur; kurtarma, daha önce başarılı olan ekleme de dahil olmak üzere isteğe bağlı toplu işlemin tamamını atar.
Eksiksiz örneği kurup çalıştırma
npm yüklü, desteklenen güncel bir Node.js sürümü kullanın. Çalışma klasöründe npm install pg komutunu çalıştırın ve aşağıdaki programı article-transactions.mjs adıyla kaydedin. Program, ayarlanmışlarsa PGHOST, PGPORT, PGDATABASE ve PGUSER dahil, mevcut PostgreSQL bağlantı ortamınızı kullanır. Örneği çalıştırmadan önce bu ayarlar çalışan bir veritabanını göstermelidir. node-postgres: Welcome, node-postgres: Connecting
import pg from 'pg'
const { Client } = pg
async function addOptionalArticles(client) {
await client.query('SAVEPOINT optional_articles')
try {
await client.query(
'INSERT INTO article_demo (id, title) VALUES ($1, $2)',
[2, 'Optional draft']
)
await client.query(
'INSERT INTO article_demo (id, title) VALUES ($1, $2)',
[1, 'Duplicate draft']
)
} catch (error) {
if (error.code !== '23505') throw error
await client.query('ROLLBACK TO SAVEPOINT optional_articles')
await client.query('RELEASE SAVEPOINT optional_articles')
return false
}
await client.query('RELEASE SAVEPOINT optional_articles')
return true
}
async function main() {
const client = new Client({ connectionTimeoutMillis: 5000 })
await client.connect()
try {
await client.query(`
CREATE TEMP TABLE article_demo (
id integer PRIMARY KEY,
title text NOT NULL
) ON COMMIT PRESERVE ROWS
`)
await client.query('BEGIN ISOLATION LEVEL READ COMMITTED')
try {
await client.query(
'INSERT INTO article_demo (id, title) VALUES ($1, $2)',
[1, 'Opening draft']
)
const optionalKept = await addOptionalArticles(client)
console.log('Optional batch kept:', optionalKept)
await client.query(
'INSERT INTO article_demo (id, title) VALUES ($1, $2)',
[3, 'Closing draft']
)
await client.query('COMMIT')
} catch (error) {
try {
await client.query('ROLLBACK')
} catch (rollbackError) {
throw new AggregateError(
[error, rollbackError], 'Transaction and rollback failed'
)
}
throw error
}
const result = await client.query(
'SELECT id, title FROM article_demo ORDER BY id'
)
console.table(result.rows)
} finally {
await client.end()
}
}
main().catch(error => {
console.error(error)
process.exitCode = 1
})
node article-transactions.mjs komutunu çalıştırın. Beklenen çıktıda Optional batch kept: false mesajı görünür ve 1 ile 3 makale kimlikleri görüntülenir. Değerlerde belgelenen query(text, values) parametre arayüzü kullanılır. JavaScript, Node.js ile sözdizimi açısından denetlenmiştir; veritabanı senaryosu belgelere göre incelenmiş ancak burada çalıştırılmamıştır. node-postgres: Queries
Yalnızca açıkça kabul edilen bir hatayı kurtarın
PostgreSQL, 23505 için unique_violation kodunu atar ve ileti metni yerine hata kodlarının denetlenmesini önerir. Burada bu kodu kabul etmek, isteğe bağlı sentetik toplu işlem için örnek bir kuraldır. Diğer hatalar çağırana iletilir; çağıran tüm işlemi geri almayı dener. Yardımcı işlev açık bir sonuç döndürerek atılan isteğe bağlı çalışmayı çağıranın görmesini sağlar. PostgreSQL: PostgreSQL Error Codes
Yalnızca JavaScript istisnasını yakalamak, başarısız bir PostgreSQL işlemini geri yüklemez. Kurtarma için önceden belirlenmiş bir kaydetme noktasına geri dönmek veya işlemin tamamını geri almak gerekir. Özellikle, iptal edilmiş bir işlem yalnızca kaydetme noktası serbest bırakılarak kurtarılamaz. PostgreSQL: Transactions, PostgreSQL: RELEASE SAVEPOINT
Yardımcı işlev, etkin bir işlem ve bu istemcinin sırayla kullanıldığını varsayar. Genel amaçlı, özyinelemeli bir işlem sarmalayıcısı değildir: yeniden kullanılabilir bir iç içe geçme soyutlaması, sahiplik ve kaydetme noktası adlarının yönetimi için de açık kurallar gerektirir.
Sınırlar düzeltildikten sonra yalıtım açısından hangi kısıtlar kalır?
Kaydetme noktaları geçerli işlem içinde kısmi geri alma sağlar; ayrı bir yalıtım politikası oluşturmaz. Yalıtım düzeyini dış sınırda seçin. PostgreSQL, işlemin ilk sorgusu veya veri değiştirme ifadesinden sonra yalıtım düzeyinin değiştirilmesine izin vermez; bu nedenle bir yardımcı işlev, başka bir BEGIN göndererek daha önce kullanılmış bir işlemin yalıtım düzeyini yükseltemez. PostgreSQL: SAVEPOINT, PostgreSQL: SET TRANSACTION
Olağan sorgular için şu ayrımları koruyun:
| Yalıtım düzeyi | İlgili kısıt |
|---|---|
READ COMMITTED |
Ardışık sorgular, diğer işlemlerde yeni işlenmiş değişiklikleri görebilir. Tek bir işlem, sabit bir anlık görüntü anlamına gelmez. |
REPEATABLE READ |
Sorgular, işlem denetimi dışındaki ilk ifadeyle oluşturulan anlık görüntüyü paylaşırken kendi önceki değişikliklerini görür. Serileştirme hataları yine de oluşabilir. |
SERIALIZABLE |
Başarıyla işlenmiş işlemlerin etkisi, bazı seri sıralamalarından biriyle tutarlıdır. Uygulamaların yine de serileştirme hatalarını ele alması gerekir. |
READ UNCOMMITTED |
PostgreSQL bunu READ COMMITTED olarak ele alır. |
PostgreSQL: Transaction Isolation, PostgreSQL: SET TRANSACTION
Serileştirme hatası, SQL'i üreten kararlar da dahil olmak üzere işlemin tamamının yeniden denenmesini gerektirir. Yalnızca yardımcı işlevin kaydetme noktası kapsamındaki çalışmasını yeniden denemek, dış işlemin anlık görüntüsünü yenilemez. PostgreSQL, serileştirme hatalarını SQLSTATE 40001 ile tanımlar. Gösterim programı bu hataları çağırana iletir; otomatik yeniden deneme uygulamaz. PostgreSQL: Transaction Isolation
Doğru yardımcı işlev sınırını nasıl seçersiniz?
Bir çağıranı ve onun makale yardımcı işlevlerini incelerken şu karar sırasını kullanın:
- Her şey birlikte başarılı olmalı: işlemin sahibi çağıran olsun ve yalnızca sorgu yürüten yardımcı işlevlere istemcisini iletsin.
- Çağıran devam ederken bir toplu işlem atılabilmeli: bu işlemden önce bir kaydetme noktası oluşturun, yalnızca kabul edilen hatalardan kurtulun ve sonucu açıkça döndürün.
- Toplu işlem bağımsız olarak işlenmeli: ayrı bir istemci ve işlem kullanın. Bu durumda işlem, dıştaki geri almadan etkilenmeden kalır ve başlangıçtaki ya hep ya hiç görevini karşılayamaz.
- Eşzamanlı bir yürütme serileştirme hatası üretirse: yardımcı işlevi bağımsız bir yeniden deneme sınırı olarak görmek yerine işlemin tamamını yeniden başlatın.
Bu seçimler, PostgreSQL'in kaydetme noktası anlambilimini ve node-postgres'in işlemin tek bir istemcide kalması gerekliliğini izler. PostgreSQL: SAVEPOINT, node-postgres: Transactions, PostgreSQL: Transaction Isolation
Burada gösterilen makale düzenleme görevi için tek bir işlem sahibi belirleyin, çağrı zinciri boyunca tek bir istemci iletin ve isteğe bağlı işler için kaydetme noktası kullanın. Her yardımcı işlevin işlem komutlarını gözden geçirin: beklenmeyen bir COMMIT tüm bağlantının sınırını değiştirir; serbest bırakılan bir kaydetme noktası ise nihai sonuçtan çağıranı sorumlu bırakır.
Kaynaklar
BEGIN — PostgreSQL Global Development Group
COMMIT — PostgreSQL Global Development Group
3.4. Transactions — PostgreSQL Global Development Group
psql — PostgreSQL Global Development Group
CREATE TABLE — PostgreSQL Global Development Group
SAVEPOINT — PostgreSQL Global Development Group
RELEASE SAVEPOINT — PostgreSQL Global Development Group
ROLLBACK TO SAVEPOINT — PostgreSQL Global Development Group
SET TRANSACTION — PostgreSQL Global Development Group
13.2. Transaction Isolation — PostgreSQL Global Development Group
Appendix A. PostgreSQL Error Codes — PostgreSQL Global Development Group
Transactions — node-postgres
Pool — node-postgres
Welcome — node-postgres
Connecting — node-postgres
Queries — node-postgres