Untuk memastikan senarai artikel dan bilangannya konsisten, jalankan kedua-dua bacaan dalam satu transaksi REPEATABLE READ READ ONLY pada klien node-postgres yang sama. Tutorial ini menunjukkan coraknya dan pemeriksaan dua sesi yang boleh anda jalankan pada Mac.

Mengapa senarai artikel mungkin tidak sepadan dengan bilangannya?

Katakan skrip desktop mengambil artikel yang diterbitkan dalam topik craft, kemudian mengira artikel yang sepadan. Antara kedua-dua bacaan itu, sesi lain menukar topik sesuatu artikel. Senarai yang dipulangkan mungkin mengandungi dua artikel sedangkan bilangannya menunjukkan satu.

Tahap pengasingan lalai PostgreSQL, READ COMMITTED, memberikan setiap SELECT biasa syot kilat pada permulaan pernyataan tersebut. Oleh itu, bacaan berturutan boleh melihat data komited yang berbeza walaupun dalam satu transaksi. PostgreSQL: Pengasingan Transaksi.

Untuk tugas ini, hasil yang dikehendaki ialah satu paparan koheren bagi baris artikel. Suntingan kemudian boleh muncul pada pelaksanaan seterusnya. REPEATABLE READ PostgreSQL mengekalkan bacaan berturutan pada syot kilat yang ditetapkan oleh pernyataan pertama transaksi yang bukan kawalan transaksi; sekadar melaksanakan BEGIN tidak menetapkan syot kilat itu. PostgreSQL: Pengasingan Transaksi.

Pelaksanaan ini mempunyai dua keperluan: pilih tahap pengasingan sebelum membaca, dan jalankan setiap pertanyaan berkaitan pada klien yang sama. node-postgres secara jelas mensyaratkan satu klien untuk transaksi dan memberi amaran agar tidak menggunakan pool.query() untuk pernyataannya. node-postgres: Transaksi.

Sediakan set contoh artikel kecil pada Mac anda

Gunakan pelayan PostgreSQL tempatan yang sedang berjalan, perintah psql, dan pemasangan Node.js yang disokong bersama npm. Perintah di bawah mengandaikan sambungan tempatan sedia ada anda berfungsi dengan pangkalan data postgres. Jika pangkalan data kerja anda menggunakan nama lain, gantikannya secara konsisten. Pemasangan PostgreSQL dan persediaan sambungan ialah prasyarat.

Buka Terminal dan cipta folder projek. Pakej node-postgres yang didokumenkan ialah pg. node-postgres: Pengenalan.

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

Pada gesaan psql, jalankan persediaan ini sekali. Ia mencipta jadual khusus yang mengandungi baris artikel sintetik sahaja. Pengecam eksplisit juga memudahkan anda mengikuti latihan seterusnya.

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);

Jika jadual itu sudah wujud, berhenti dan periksa set contoh terdahulu dan bukannya memadam atau menggantikannya secara automatik. Contoh ini mengandaikan tepat tiga baris permulaan tersebut. Pastikan psql dengan \q sebelum menjalankan skrip Node.js.

Baca senarai dan bilangan melalui satu klien yang telah dikeluarkan daripada pool

Simpan program lengkap berikut sebagai read-articles.mjs dalam folder projek. new Pool() menggunakan pemboleh ubah persekitaran sambungan PostgreSQL yang didokumenkan, termasuk nilai PGDATABASE yang dieksport di atas. node-postgres: Menyambung.

Program ini mendapatkan satu klien, memulakan transaksi dengan ciri-cirinya telah ditetapkan, menjalankan kedua-dua bacaan, dan membuat komit sebelum memulangkan hasil yang dihimpunkan. PostgreSQL menyokong ciri-ciri ini secara langsung dalam BEGIN; transaksi berakhir dengan COMMIT atau ROLLBACK. 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();
}

Jalankan program itu daripada sesi Terminal yang sama:

node read-articles.mjs

Dengan set contoh yang tidak berubah, hasil yang dijangka mengandungi artikel 1 dan 2, dengan total bersamaan rentetan \"2\". Bilangan itu dipulangkan secara eksplisit sebagai teks, jadi contoh ini tidak memerlukan penukaran kepada nombor JavaScript. Kedua-dua pertanyaan menggunakan penapis topik dan penerbitan yang sama; pengasingan tidak dapat membetulkan predikat yang tidak sepadan.

Pembersihan ialah sebahagian daripada corak yang boleh digunakan. Bacaan yang gagal mencetuskan rollback, dan rollback yang gagal menyebabkan klien dibuang. node-postgres mendokumenkan client.release(true) untuk memusnahkan klien yang telah dikeluarkan daripada pool, dan pool.end() untuk menutup pool skrip. node-postgres: API Pool.

Ini skrip kendiri, jadi ia menamatkan pool selepas satu pelaksanaan. Dalam aplikasi sedia ada, kekalkan kitar hayat pool aplikasi dan letakkan fungsi transaksi di dalamnya. Setiap pembantu yang terlibat dalam bacaan ini mesti menerima client yang telah dikeluarkan daripada pool; pembantu yang diam-diam memanggil pool.query() memutuskan sempadan itu. node-postgres: Transaksi.

Sahkan syot kilat dengan dua sesi Terminal

Skrip pantas mungkin selesai sebelum sesi lain sempat mengubah apa-apa. Gunakan latihan yang disusun dengan sengaja berikut untuk menjelaskan masa yang berkaitan. Ini ialah langkah SQL khusus sesi yang menggunakan jadual yang telah dicipta, bukannya program persediaan berasingan. Hasil pangkalan data yang dijangka mengikut tingkah laku pengasingan yang didokumenkan; hasil ini tidak dikemukakan sebagai ujian PostgreSQL yang telah dijalankan.

Buka dua tetingkap Terminal dan jalankan psql -d postgres dalam setiap satu. Sebelum memulakan pembaca, pulihkan topik set contoh sintetik dalam sesi B. Tunggu sehingga pernyataan ini selesai:

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

Dalam sesi A, mulakan transaksi pembaca dan ambil senarai. Jeda selepas pertanyaan selesai:

-- 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;

Senarai itu sepatutnya mengandungi Paper Shapes dan Color Studies. Sekarang beralih ke sesi B dan komit perubahan topik menggunakan pernyataan kendiri ini. Di luar blok transaksi eksplisit, PostgreSQL membuat komit secara automatik bagi pernyataan yang berjaya. PostgreSQL: BEGIN.

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

Kembali ke sesi A dan selesaikan bacaan berkaitan:

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

COMMIT;

Bilangan yang dijangka ialah 2, sepadan dengan senarai sesi A yang diambil lebih awal. Selepas itu, pelaksanaan baharu skrip Node.js sepatutnya memulangkan hanya Paper Shapes dan jumlah \"1\". Hasil lama dan hasil baharu masing-masing mewakili paparan yang koheren.

Untuk perbandingan terkawal, ulangi seluruh latihan itu, termasuk pemulihan awal oleh sesi B, tetapi mulakan sesi A dengan BEGIN ISOLATION LEVEL READ COMMITTED READ ONLY;. Kekalkan jeda dan susunan kemas kini yang sama. Senarai pertama yang dijangka mengandungi dua artikel, manakala bilangan kemudian ialah 1. Perbandingan ini mengasingkan tetapan transaksi sebagai keadaan yang berubah.

Pilih sempadan yang sesuai untuk bacaan berkaitan

Gunakan panduan membuat keputusan ini semasa menyesuaikan contoh. Mula-mula tentukan output yang mesti sepadan, kemudian pilih operasi pangkalan data terkecil yang membekalkannya.

Tugas pembaca Pendekatan yang sesuai Syarat yang perlu dikekalkan
Pulangkan satu senarai artikel daripada satu pertanyaan biasa Satu pertanyaan boleh menggunakan READ COMMITTED Tiada pertanyaan kemudian perlu berkongsi syot kilatnya.
Pulangkan senarai dan bilangan yang ditanya secara berasingan Satu transaksi REPEATABLE READ READ ONLY Gunakan satu klien dan penapis yang sepadan.
Ambil berbilang kelompok untuk satu hasil yang koheren Kekalkan bacaan dalam transaksi yang sama Transaksi baharu menetapkan paparan baharu.
Selaraskan bacaan merentas sesi pangkalan data berasingan Eksport dan import syot kilat secara eksplisit Transaksi bebas tidak berkongsi syot kilat secara automatik.

Untuk berbilang sesi, PostgreSQL menyediakan pg_export_snapshot() dan SET TRANSACTION SNAPSHOT. Pengeksport mesti kekal terbuka sehingga transaksi lain mengimport syot kilatnya. Transaksi pengimport mesti menggunakan REPEATABLE READ atau SERIALIZABLE dan mengimport sebelum pertanyaan pertama atau pernyataan pengubahsuaian datanya. Import serializable mempunyai sekatan tambahan yang didokumenkan dalam rujukan perintah. PostgreSQL: Fungsi Penyegerakan Syot Kilat, PostgreSQL: SET TRANSACTION.

Untuk tugas senarai dan bilangan desktop ini, mengekalkan satu klien mengelakkan penyelarasan tambahan itu. Selesaikan bacaan pangkalan data dan tutup transaksi sebelum menunggu seseorang memilih tindakan seterusnya.

Apakah yang masih belum diselesaikan oleh tahap pengasingan ini?

REPEATABLE READ PostgreSQL mencegah bacaan hantu, tetapi anomali serialisasi masih boleh berlaku. Oleh itu, syot kilat yang stabil tidak membuktikan bahawa aliran kerja serentak berkelakuan seolah-olah dilaksanakan satu demi satu. Ia juga tidak membekukan nilai jujukan; jujukan mempunyai tingkah laku transaksi yang khusus. PostgreSQL: Pengasingan Transaksi.

READ ONLY melarang perubahan pada jadual biasa, dengan pengecualian yang didokumenkan seperti operasi jadual sementara tertentu. Ia tidak dengan sendirinya memilih syot kilat yang stabil. Tetapkan kedua-dua ciri sebelum membaca: PostgreSQL tidak membenarkan anda menukar tahap pengasingan selepas pertanyaan pertama atau pernyataan pengubahsuaian data pertama transaksi. PostgreSQL: SET TRANSACTION.

Apabila laporan memerlukan semantik serializable yang lebih kukuh, PostgreSQL menyokong SERIALIZABLE READ ONLY DEFERRABLE. Ia mungkin menunggu semasa mendapatkan syot kilatnya, kemudian berjalan tanpa pembatalan akibat kegagalan serialisasi. Pertukaran itu berbeza daripada keperluan bacaan artikel biasa yang ditunjukkan di sini. PostgreSQL: SET TRANSACTION.

Untuk senarai dan bilangan artikel, pastikan sempadan pelaksanaan jelas: dapatkan satu klien, mulakan dengan ciri yang diperlukan, baca semua hasil berkaitan, komit, dan lepaskan klien. Gunakan semula penapis yang sama dan terbitkan hasil yang dihimpunkan hanya selepas transaksi selesai. Ini memberikan tindakan desktop seterusnya satu set data artikel yang koheren untuk digunakan.

Sumber