Bir makale listesini ve sayısını tutarlı tutmak için her iki okumayı da aynı node-postgres istemcisinde, tek bir REPEATABLE READ READ ONLY işlemi içinde çalıştırın. Bu eğitim, kalıbı ve Mac üzerinden çalıştırabileceğiniz iki oturumlu bir denetimi gösteriyor.

Makale listesi ile sayısı neden uyuşmayabilir?

Bir masaüstü betiğinin craft konusundaki yayımlanmış makaleleri getirdiğini, ardından eşleşen makaleleri saydığını varsayalım. Bu okumalar arasında başka bir oturum bir makalenin konusunu değiştirir. Döndürülen listede iki makale bulunurken sayı bir olabilir.

PostgreSQL’in varsayılan yalıtım düzeyi olan READ COMMITTED, her sıradan SELECT için o ifadenin başladığı anda bir anlık görüntü sağlar. Bu nedenle art arda yapılan okumalar, tek bir işlem içinde bile farklı commit edilmiş verileri görebilir. PostgreSQL: İşlem Yalıtımı.

Bu görevde istenen sonuç, makale satırlarının tutarlı tek bir görünümüdür. Sonraki bir düzenleme bir sonraki çalıştırmada görünebilir. PostgreSQL’in REPEATABLE READ düzeyi, art arda yapılan okumaları işlemin işlem denetimi dışındaki ilk ifadesiyle belirlenen anlık görüntüde tutar; yalnızca BEGIN çalıştırmak bu anlık görüntüyü belirlemez. PostgreSQL: İşlem Yalıtımı.

Uygulamanın iki gereksinimi vardır: okumadan önce yalıtım düzeyini seçmek ve ilgili tüm sorguları aynı istemcide tutmak. node-postgres, bir işlem için tek istemci kullanılmasını açıkça şart koşar ve ifadelerinde pool.query() kullanılmaması konusunda uyarır. node-postgres: İşlemler.

Mac’inizde küçük bir makale örnek veri kümesi hazırlayın

Çalışan bir yerel PostgreSQL sunucusu, psql komutu ve npm içeren, desteklenen bir Node.js kurulumu kullanın. Aşağıdaki komutlar, mevcut yerel bağlantınızın postgres veritabanıyla çalıştığını varsayar. Çalışma veritabanınızın adı farklıysa bunu tüm yerlerde tutarlı biçimde değiştirin. PostgreSQL kurulumu ve bağlantı yapılandırması ön koşullardır.

Terminal uygulamasını açın ve bir proje klasörü oluşturun. Belgelenmiş node-postgres paketi pg şeklindedir. node-postgres: Hoş Geldiniz.

mkdir article-snapshot-demo
cd article-snapshot-demo
npm init -y
npm install pg
export PGDATABASE=postgres
psql -d postgres

psql isteminde bu kurulumu bir kez çalıştırın. Yalnızca sentetik makale satırlarını içeren özel bir tablo oluşturur. Açık tanımlayıcılar, sonraki alıştırmayı da takip etmeyi kolaylaştırır.

CREATE TABLE public.article_snapshot_demo (
    id integer PRIMARY KEY,
    title text NOT NULL,
    topic text NOT NULL,
    published boolean NOT NULL
);

INSERT INTO public.article_snapshot_demo
    (id, title, topic, published)
VALUES
    (1, 'Paper Shapes', 'craft', true),
    (2, 'Color Studies', 'craft', true),
    (3, 'Desk Layouts', 'design', true);

Tablo zaten varsa, otomatik olarak silip değiştirmek yerine durun ve önceki örnek veri kümesini inceleyin. Örnekler tam olarak bu üç başlangıç satırının bulunduğunu varsayar. Node.js betiğini çalıştırmadan önce psql ifadesini \q ile bırakın.

Listeyi ve sayıyı aynı havuzdan alınmış istemci üzerinden okuyun

Aşağıdaki programın tamamını proje klasöründeki read-articles.mjs dosyasına kaydedin. new Pool(), yukarıda dışa aktarılan PGDATABASE değeri de dahil olmak üzere belgelenmiş PostgreSQL bağlantı ortam değişkenlerini kullanır. node-postgres: Bağlanma.

Program bir istemci alır, işlemi özellikleri baştan belirtilmiş biçimde başlatır, her iki okumayı da yapar ve birleştirilmiş sonucu döndürmeden önce işlemi commit eder. PostgreSQL bu özellikleri doğrudan BEGIN içinde destekler; işlem COMMIT veya ROLLBACK ile sona erer. PostgreSQL: BEGIN.

import pg from 'pg';
const { Pool } = pg;

const pool = new Pool({ connectionTimeoutMillis: 5000 });
pool.on('error', (error) => {
  console.error('Idle database client error:', error.message);
  process.exitCode = 1;
});

async function readArticles(topic) {
  const client = await pool.connect();
  let began = false;
  let discardClient = false;

  try {
    await client.query(
      'BEGIN ISOLATION LEVEL REPEATABLE READ READ ONLY'
    );
    began = true;

    const articles = await client.query(
      `SELECT id, title, topic
       FROM public.article_snapshot_demo
       WHERE topic = $1 AND published = true
       ORDER BY id`,
      [topic]
    );

    const count = await client.query(
      `SELECT count(*)::text AS total
       FROM public.article_snapshot_demo
       WHERE topic = $1 AND published = true`,
      [topic]
    );

    await client.query('COMMIT');
    began = false;

    return {
      topic,
      articles: articles.rows,
      total: count.rows[0].total
    };
  } catch (error) {
    if (began) {
      try {
        await client.query('ROLLBACK');
      } catch (rollbackError) {
        discardClient = true;
        throw new AggregateError(
          [error, rollbackError],
          'Article read failed; rollback also failed'
        );
      }
    } else {
      discardClient = true;
    }
    throw error;
  } finally {
    client.release(discardClient);
  }
}

try {
  const result = await readArticles('craft');
  console.log(JSON.stringify(result, null, 2));
} catch (error) {
  console.error(error);
  process.exitCode = 1;
} finally {
  await pool.end();
}

Programı aynı Terminal oturumundan çalıştırın:

node read-articles.mjs

Örnek veri kümesi değiştirilmediyse, beklenen sonuçta 1 ve 2 makaleleri bulunur; total değeri "2" dizgesine eşittir. Sayı açıkça metin olarak döndürülür; bu nedenle örnekte JavaScript sayısına dönüştürme gerekmez. Her iki sorguda da aynı konu ve yayımlanma filtresi kullanılır; yalıtım düzeyi, eşleşmeyen koşulları düzeltemez.

Temizleme adımları, kullanılabilir kalıbın bir parçasıdır. Bir okuma başarısız olursa geri alma işlemi tetiklenir; geri alma da başarısız olursa istemci kullanım dışı bırakılır. node-postgres, havuzdan alınmış bir istemciyi yok etmek için client.release(true), betiğin havuzunu kapatmak için pool.end() yöntemini belgeler. node-postgres: Havuz API’si.

Bu bağımsız bir betiktir; dolayısıyla tek çalıştırmadan sonra havuzunu kapatır. Mevcut bir uygulamada uygulamanın havuz yaşam döngüsünü koruyun ve işlem işlevini bu yaşam döngüsünün içine yerleştirin. Bu okumalara katılan her yardımcı işlev, havuzdan alınmış client istemcisini almalıdır; sessizce pool.query() çağıran bir yardımcı bu sınırı bozar. node-postgres: İşlemler.

Anlık görüntüyü iki Terminal oturumuyla doğrulayın

Hızlı bir betik, başka bir oturum herhangi bir şeyi değiştirmeden tamamlanabilir. İlgili zamanlamayı açıkça görmek için aşağıdaki adımları belirtilen sırayla uygulayın. Bunlar bağımsız kurulum programları değil, önceden oluşturulmuş tabloyu kullanan oturuma özgü SQL adımlarıdır. Beklenen veritabanı sonuçları belgelenmiş yalıtım davranışına dayanır; çalıştırılmış bir PostgreSQL testi olarak sunulmamaktadır.

İki Terminal penceresi açın ve her birinde psql -d postgres çalıştırın. Okuyucuyu başlatmadan önce B oturumunda sentetik örnek veri kümesinin konusunu eski hâline getirin. Bu ifadenin tamamlanmasını bekleyin:

-- Session B: restore the fixture before beginning the exercise.
UPDATE public.article_snapshot_demo
SET topic = 'craft'
WHERE id = 2;

A oturumunda okuyucu işlemini başlatıp listeyi getirin. Sorgu tamamlandıktan sonra bekleyin:

-- Session A: run these statements, then pause.
BEGIN ISOLATION LEVEL REPEATABLE READ READ ONLY;

SELECT id, title
FROM public.article_snapshot_demo
WHERE topic = 'craft' AND published = true
ORDER BY id;

Listede Paper Shapes ve Color Studies bulunmalıdır. Şimdi B oturumuna geçin ve bu bağımsız ifadeyle konu değişikliğini commit edin. Açık bir işlem bloğunun dışında PostgreSQL, başarılı bir ifadeyi otomatik olarak commit eder. PostgreSQL: BEGIN.

-- Session B: run while session A remains open.
UPDATE public.article_snapshot_demo
SET topic = 'design'
WHERE id = 2;

A oturumuna dönün ve ilgili okumayı tamamlayın:

-- Session A: finish the original reader transaction.
SELECT count(*)::text AS total
FROM public.article_snapshot_demo
WHERE topic = 'craft' AND published = true;

COMMIT;

Beklenen sayı 2 olur ve A oturumunun önceki listesiyle eşleşir. Ardından Node.js betiğini yeniden çalıştırdığınızda yalnızca Paper Shapes ve toplamda "1" döndürmelidir. Eski sonuç ve yeni sonuç, ayrı ayrı tutarlı bir görünümü temsil eder.

Kontrollü bir karşılaştırma için B oturumundaki ilk geri yükleme de dahil olmak üzere alıştırmanın tamamını tekrarlayın; ancak A oturumunu BEGIN ISOLATION LEVEL READ COMMITTED READ ONLY; ile başlatın. Aynı bekleme ve güncelleme sırasını koruyun. Beklenen ilk listede iki makale bulunurken sonraki sayı 1 olur. Bu karşılaştırmada değişen koşul yalnızca işlem ayarıdır.

İlgili okumalar için doğru sınırı seçin

Örneği uyarlarken bu karar yardımını kullanın. Önce hangi çıktıların birbiriyle tutarlı olması gerektiğine karar verin, sonra bunları sağlayan en küçük veritabanı işlemini seçin.

Okuyucunun görevi Uygun yaklaşım Korunması gereken koşul
Tek bir sıradan sorgudan bir makale listesi döndürmek Tek sorguda READ COMMITTED kullanılabilir Daha sonraki bir sorgunun aynı anlık görüntüyü paylaşması gerekmemelidir.
Listeyi ve ayrı sorguyla alınan sayıyı döndürmek Tek bir REPEATABLE READ READ ONLY işlemi Tek istemci ve eşleşen filtreler kullanın.
Tutarlı bir sonuç için birden fazla grup getirmek Okumaları aynı işlem içinde tutun Yeni bir işlem yeni bir görünüm belirler.
Ayrı veritabanı oturumlarındaki okumaları eşgüdümlemek Anlık görüntüyü açıkça dışa ve içe aktarmak Bağımsız işlemler anlık görüntüyü otomatik olarak paylaşmaz.

Birden fazla oturum için PostgreSQL, pg_export_snapshot() ve SET TRANSACTION SNAPSHOT sağlar. Dışa aktaran işlem, diğer işlemler anlık görüntüsünü içe aktarıncaya kadar açık kalmalıdır. Anlık görüntüyü içe aktaran işlem REPEATABLE READ veya SERIALIZABLE kullanmalı ve ilk sorgusundan ya da veri değiştirme ifadesinden önce içe aktarmalıdır. Serializable içe aktarım için komut başvurusunda belgelenen ek kısıtlamalar vardır. PostgreSQL: Anlık Görüntü Eşitleme İşlevleri, PostgreSQL: SET TRANSACTION.

Bu masaüstü liste ve sayı görevi için tek bir istemciyi kullanmak, bu ek eşgüdüm ihtiyacını ortadan kaldırır. Başka bir eylem seçmesi için bir kişiyi beklemeden önce veritabanı okumalarını tamamlayın ve işlemi kapatın.

Bu yalıtım düzeyi hangi sorunları çözmez?

PostgreSQL’in REPEATABLE READ düzeyi hayalet okumaları önler, ancak serileştirme anormallikleri yine de mümkündür. Bu nedenle kararlı bir anlık görüntü, eşzamanlı iş akışlarının tek tek yürütülmüş gibi davrandığını göstermez. Ayrıca özel işlem davranışları olan dizi değerlerini sabitlemez. PostgreSQL: İşlem Yalıtımı.

READ ONLY, belirli geçici tablo işlemleri gibi belgelenmiş istisnalar dışında sıradan tablolarda değişiklik yapılmasını engeller. Ancak kendi başına kararlı bir anlık görüntü seçmez. Okumadan önce her iki özelliği de ayarlayın: PostgreSQL, işlemin ilk sorgusundan veya veri değiştirme ifadesinden sonra yalıtım düzeyini değiştirmenize izin vermez. PostgreSQL: SET TRANSACTION.

Bir rapor daha güçlü serializable semantiği gerektirdiğinde PostgreSQL, SERIALIZABLE READ ONLY DEFERRABLE seçeneğini destekler. Bu seçenek, anlık görüntüyü alırken bekleyebilir; ardından serileştirme hatası nedeniyle iptal edilmeden çalışır. Bu ödünleşim, burada gösterilen sıradan makale okuma gereksiniminden farklıdır. PostgreSQL: SET TRANSACTION.

Makale listesi ve sayısı için uygulama sınırını açık tutun: tek bir istemci alın, gerekli özelliklerle işlemi başlatın, ilgili tüm sonuçları okuyun, commit edin ve istemciyi serbest bırakın. Aynı filtreleri yeniden kullanın ve birleştirilmiş sonucu ancak işlem tamamlandıktan sonra yayımlayın. Böylece masaüstündeki bir sonraki eylem, üzerinde çalışabileceği tutarlı bir makale veri kümesi alır.

Kaynaklar