PostgreSQL creëert geen geneste transactie wanneer u een tweede BEGIN stuurt. Het waarschuwt en behoudt de huidige transactie; de eerste succesvolle COMMIT beëindigt deze. Gebruik savepoints wanneer een helper een gedeeltelijke rollback nodig heeft. PostgreSQL: BEGIN
Deze gids helpt een developer die in Mac Terminal werkt om een helper te diagnosticeren die een bewerking voor het bewerken van een artikel te vroeg commit. De voorbeelden gebruiken synthetische artikelrijen en tonen hoe u één transactie-eigenaar kunt behouden terwijl een helper zijn eigen batch ongedaan kan maken. Ze volgen de huidige PostgreSQL- en node-postgres-documentatie.
Wat gebeurt er als BEGIN en COMMIT binnen een andere transactie verschijnen?
SQL-transactiecommando's werken op de huidige transactie van de verbinding. PostgreSQL telt herhaalde BEGIN-oproepen niet en wacht niet op bijpassende COMMIT-oproepen. Een tweede BEGIN laat de transactie ongewijzigd; een succesvolle gewone COMMIT committed die huidige transactie. Het daarna uitgeven van nog een gewone COMMIT produceert een waarschuwing omdat geen transactie open is. PostgreSQL: BEGIN, PostgreSQL: COMMIT
Voor een gezonde transactie is de grensverloop als volgt:
| Verzonden commando op één verbinding | Resulterende grens |
|---|---|
Eerste BEGIN |
Opent de transactie |
| Eerste artikel-invoeging | Behoort tot die transactie |
Tweede BEGIN |
Waarschuwt; creëert geen binnengrens |
| Tweede artikel-invoeging | Behoort tot dezelfde transactie |
Eerste COMMIT |
Commit beide invoegingen en beëindigt de transactie |
| Een ander apart verzonden statement | Draait in een eigen transactie onder normale autocommit-gedrag |
Later ROLLBACK |
Kan de al gecommitteerde invoegingen niet ongedaan maken |
Dit verklaart een veelvoorkomende toepassingsfout: de aanroeper opent een transactie, roept vervolgens een helper aan die zijn eigen BEGIN en COMMIT stuurt. De COMMIT van de helper beëindigt ook de transactie van de aanroeper. Een later optredende uitzondering in de aanroeper kan de oorspronkelijke all-or-nothing-bewerking niet herstellen. Dat gevolg trekt PostgreSQL's gedocumenteerde transactiegrenzen. PostgreSQL: Transactions
Vroegtijdige commit reproduceren in Mac Terminal
U hebt een draaiende PostgreSQL-database en een geïnstalleerd psql-commando met werkende verbindingsinstellingen nodig. Open Terminal en start een verse sessie met psql -X. De optie -X slaat opstartbestanden over, wat deze demonstratie minder afhankelijk maakt van lokale aanpassingen. Voer het volgende script uit in die sessie; het schakelt autocommit expliciet in. PostgreSQL: psql
De tabel is tijdelijk en bevat alleen synthetische artikeltitels. ON COMMIT PRESERVE ROWS houdt de rijen beschikbaar voor inspectie over transactiegrenzen heen; PostgreSQL dropt de tijdelijke tabel wanneer de sessie eindigt. Dit demonstreert rollback-gedrag binnen de sessie, in plaats van persistente opslag na het verbreken. 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;
Het verwachte resultaat bevat alle drie rijen. De tweede BEGIN waarschuwt dat er al een transactie bezig is. De COMMIT committed rijen 1 en 2; rij 3 committed vervolgens via autocommit. De laatste ROLLBACK heeft geen open transactie om ongedaan te maken. Dit zijn verwachte resultaten afgeleid van het gedocumenteerde gedrag, niet een gerapporteerde database-uitvoering. PostgreSQL: BEGIN, PostgreSQL: Transactions
Houd deze reproductie in interactieve psql, zoals getoond. Client-inzending is van belang: meerdere SQL-statements die samen via één psql -c-string worden doorgegeven, kunnen een impliciete transactie delen, tenzij expliciete transactiecommando's ze scheiden. De documentatie adviseert afzonderlijke commando's of standaardinvoer wanneer afzonderlijke uitvoering beoogd is. PostgreSQL: psql
Hoe behouden savepoints de buitenste transactie?
Een savepoint markeert een positie binnen een bestaande transactie. ROLLBACK TO SAVEPOINT verwijdert wijzigingen na die positie terwijl eerdere werk behouden blijft. RELEASE SAVEPOINT verwijdert de markering en behoudt de overgebleven wijzigingen binnen de omvattende transactie. Het committed die wijzigingen niet onafhankelijk. PostgreSQL: SAVEPOINT, PostgreSQL: RELEASE SAVEPOINT
Voer dit complete voorbeeld uit in dezelfde sessie of in een andere verse psql-sessie. Het creëert zijn eigen tijdelijke tabel:
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;
De verwachte rijen zijn 1 en 3. Rij 2 wordt verworpen op het savepoint, terwijl de openings- en sluitingsrijen onderdeel blijven van één transactie tot de laatste COMMIT. Het vervangen van die laatste COMMIT door ROLLBACK zou beide overgebleven invoegingen verwerpen. PostgreSQL: SAVEPOINT, PostgreSQL: Transactions
De levensduur van een savepoint is ook van belang bij het samenstellen van helpers. Terugrollen naar een savepoint laat die benoemde savepoint beschikbaar, maar vernietigt latere savepoints. Het vrijgeven van een savepoint verwijdert het en eventuele actieve savepoints die erna zijn gemaakt. Het voorbeeld geeft zijn markering expliciet vrij na herstel. PostgreSQL: ROLLBACK TO SAVEPOINT, PostgreSQL: RELEASE SAVEPOINT
Hoe moeten node-postgres helpers de transactie delen?
node-postgres vereist dat elke statement in een transactie dezelfde client-instantie gebruikt, en biedt geen automatische abstractie voor transactie-nesting. Geef de client van de aanroeper door aan helpers. Als de applicatie een pool gebruikt, verkrijg een client met pool.connect(), gebruik die client doorheen en geef hem daarna vrij; pool.query() kan opeenvolgende queries naar verschillende clients sturen. node-postgres: Transactions, node-postgres: Pool
Het volgende complete programma gebruikt één directe Client. De aanroeper bezit BEGIN, COMMIT en volledige ROLLBACK. De helper bezit een savepoint rond een optionele tweerijen-batch. De tweede invoeging herhaalt bewust artikel-ID 1, wat een uniekheidsfout veroorzaakt; herstel verwijdert de gehele optionele batch, inclusief de eerdere succesvolle invoeging.
Het complete voorbeeld opzetten en uitvoeren
Gebruik een huidige ondersteunde Node.js-versie met npm geïnstalleerd. In een werkmap voer npm install pg uit, sla vervolgens het onderstaande programma op als article-transactions.mjs. Het gebruikt uw bestaande PostgreSQL-verbindingsomgeving, inclusief PGHOST, PGPORT, PGDATABASE en PGUSER waar geconfigureerd. Die instellingen moeten een werkende database identificeren voordat het voorbeeld wordt uitgevoerd. 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
})
Voer node article-transactions.mjs uit. De verwachte output rapporteert Optional batch kept: false en toont artikel-ID's 1 en 3. Waarden gebruiken de gedocumenteerde query(text, values)-parameterinterface. De JavaScript is syntactisch gecontroleerd met Node.js; het databasescenario is geïnspecteerd aan de hand van de documentatie maar niet hier uitgevoerd. node-postgres: Queries
Alleen herstellen van een expliciet geaccepteerde fout
PostgreSQL wijst 23505 toe aan unique_violation en adviseert foutcodes te controleren in plaats van fouttekst. Hier is het accepteren van die code een illustratieregel voor de optionele synthetische batch. Andere fouten propageren naar de aanroeper, die probeert de gehele transactie terug te rollen. De helper retourneert een expliciet resultaat zodat verworpen optioneel werk zichtbaar is voor de aanroeper. PostgreSQL: PostgreSQL Error Codes
Alleen een JavaScript-uitzondering vangen herstelt een mislukte PostgreSQL-transactie niet. Herstel vereist rollback naar een eerder ingesteld savepoint of een volledige rollback. In het bijzonder kan een geannuleerde transactie niet herstellen door slechts de savepoint vrij te geven. PostgreSQL: Transactions, PostgreSQL: RELEASE SAVEPOINT
De helper veronderstelt een actieve transactie en sequentieel gebruik van deze client. Het is geen algemene recursieve transactie-wrapper: een herbruikbare nesting-abstraktie zou ook expliciete eigendoms- en savepoint-naambeheer nodig hebben.
Welke isolatielimieten blijven na het herstellen van de grenzen?
Savepoints bieden gedeeltelijke rollback binnen de huidige transactie; ze stellen geen apart isolatiebeleid in. Kies het isolatieniveau aan de buitenste grens. PostgreSQL verbiedt het wijzigen ervan na de eerste query of data-wijzigingsstatement van de transactie, zodat een helper een al gebruikte transactie niet kan upgraden door nog een BEGIN te sturen. PostgreSQL: SAVEPOINT, PostgreSQL: SET TRANSACTION
Voor gewone queries behoud deze onderscheiden:
| Isolatieniveau | Relevante limiet |
|---|---|
READ COMMITTED |
Opvolgende queries kunnen nieuw gecommitteerde wijzigingen van andere transacties zien. Eén transactie impliceert niet één vaste snapshot. |
REPEATABLE READ |
Queries delen de snapshot die gevestigd is door de eerste non-transactie-controlestatement, terwijl ze hun eigen eerdere wijzigingen zien. Serialisatiefouten blijven mogelijk. |
SERIALIZABLE |
Succesvol gecommitteerde transacties hebben een effect consistent met een seriële volgorde. Applicaties moeten serialisatiefouten nog steeds afhandelen. |
READ UNCOMMITTED |
PostgreSQL behandelt het als READ COMMITTED. |
PostgreSQL: Transaction Isolation, PostgreSQL: SET TRANSACTION
Een serialisatiefout vereist het opnieuw uitvoeren van de complete transactie, inclusief de beslissingen die de SQL produceerden. Alleen het herproberen van het savepoint-werk van de helper vernieuwt de snapshot van de buitenste transactie niet. PostgreSQL identificeert serialisatiefouten met SQLSTATE 40001. Het demonstratieprogramma propageert deze fouten; het implementeert geen automatische nieuwe pogingen. PostgreSQL: Transaction Isolation
Hoe kiest u de juiste helpergrens?
Gebruik deze beslissingsreeks bij het beoordelen van een aanroeper en zijn artikel-helpers:
- Alles moet samen slagen: laat de aanroeper de transactie bezitten en geef zijn client door aan helpers die alleen queries uitvoeren.
- Eén batch mag worden verworpen terwijl de aanroeper doorgaat: stel een savepoint in vóór die batch, herstel alleen van geaccepteerde fouten, en geef het resultaat expliciet terug.
- De batch moet onafhankelijk committen: gebruik een aparte client en transactie. De commit van die batch overleeft dan een buitenste rollback, dus kan het de oorspronkelijke all-or-nothing-taak niet vervullen.
- Een gelijktijdige uitvoering produceert een serialisatiefout: start de complete transactie opnieuw in plaats van de helper als een onafhankelijke retry-grens te behandelen.
Deze keuzes volgen de savepoint-semantiek van PostgreSQL en de vereiste van node-postgres dat een transactie op één client blijft. PostgreSQL: SAVEPOINT, node-postgres: Transactions, PostgreSQL: Transaction Isolation
Voor de hier getoonde artikel-bewerkingstaak houdt u één transactie-eigenaar, geeft u één client door via de aanroepketen, en geeft u optioneel werk een savepoint. Controleer de transactiecommando's van elke helper: een onverwachte COMMIT verandert de grens voor de hele verbinding, terwijl een vrijgegeven savepoint de aanroeper nog steeds verantwoordelijk laat voor het uiteindelijke resultaat.
Bronnen
BEGIN — PostgreSQL Global Development Group
COMMIT — PostgreSQL Global Development Group
3.4. Transacties — 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. Transactie-isolatie — PostgreSQL Global Development Group
Bijlage A. PostgreSQL-foutcodes — PostgreSQL Global Development Group
Transacties — node-postgres
Pool — node-postgres
Welkom — node-postgres
Verbinden — node-postgres
Query's — node-postgres