PostgreSQL Source Code interpretation (123)-MVCC#8 (commit transaction-actual commit process)
SYNCHRONOUS_COMMIT_OFF) | | forceSyncCommit | | nrels > 0) {XLogFlush (XactLastRecEnd); / * * Now we may update the CLOG, if we wrote a COMMIT record above * now let's update CLOG if we have written COMMIT WAL Record on it. * / if (markXidCommitted) TransactionIdCommitTree (xid, nchildren, children);} else {/ / Asynchronous submission / * * Asynchronous commit case: * Asynchronous submission: * * This enables possible committed transaction loss in the case of a * postmaster crash because WAL buffers are left unwritten. Ideally we * could issue the WAL write without the fsync, but some * wal_sync_methods do not allow separate write/fsync. * this may result in the loss of committed transactions in the event of a postmaster crash, * because WAL buffer is not persisted. * ideally, we can issue WAL write without fsync, * but some wal_sync_methods do not allow separate write/fsync. * Report the latest async commit LSN, so that the WAL writer knows to * flush this commit. * report the final asynchronous submission LSN and notify the WAL writer to refresh the commit * / XLogSetAsyncXactLSN (XactLastRecEnd); / * We must not immediately update the CLOG, since we didn't flush the * XLOG. Instead, we store the LSN up to which the XLOG must be * flushed before the CLOG may be updated. * We can't update CLOG right away because we haven't refreshed XLOG yet. * on the contrary, we store LSN until the XLOG must be refreshed before the CLOG may have been updated. * / if (markXidCommitted) TransactionIdAsyncCommitTree (xid, nchildren, children, XactLastRecEnd);} / * If we entered a commit critical section, leave it now, and let * checkpoints proceed. * if you have entered the critical area of commit and have finished your work, you can leave and let checkpoints perform the relevant operations. * / if (markXidCommitted) {MyPgXact- > delayChkpt = false; END_CRIT_SECTION ();} / * Compute latestXid while we have the child XIDs handy * / / if you have a child XIDs, calculate the final latestXid latestXid = TransactionIdLatest (xid, nchildren, children); / * Wait for synchronous replication, if required. Similar to the decision * above about using committing asynchronously we only want to wait if * this backend assigned an xid and wrote WAL. No need to wait if an xid * was assigned due to temporary/unlogged tables or due to HOT pruning. * wait for synchronous replication if necessary. * similar to the above decision to use asynchronous commit, we only want to wait if the process has allocated and written WAL. * temporary / unlog table or HOT pruning, there is no need to wait for whether the transaction ID is allocated. * Note that at this stage we have marked clog, but still show as running * in the procarray and continue to hold locks. * Note that in this scenario, we must mark clog, but it still appears as running in procarray and holds the lock all the time. * / if (wrote_xlog & & markXidCommitted) SyncRepWaitForLSN (XactLastRecEnd, true); / * remember end of last commit record * / / record the location of the last commit record XactLastCommitEnd = XactLastRecEnd; / * Reset XactLastRecEnd until the next transaction writes something * / / reset XactLastRecEnd until the next transaction writes data. XactLastRecEnd = 0 cleanup: / * Clean up local data * / / clear local data if (rels) pfree (rels); / / return XID return latestXid;} 3. Track and analyze
Insert data, execute commit
10:57:56 (xdb@ [local]: 5432) testdb=# begin;BEGIN10:57:59 (xdb@ [local]: 5432) testdb=#* insert into t_session1 values (1); INSERT 0 110 local 58 01 (xdb@ [local]: 5432) testdb=#* commit
Start gdb and set breakpoint
(gdb) b RecordTransactionCommitBreakpoint 2 at 0x547528: file xact.c, line 1141. (gdb) cContinuing.Breakpoint 2, RecordTransactionCommit () at xact.c:11411141 TransactionId xid = GetTopTransactionIdIfAny (); (gdb)
View the call stack
(gdb) bt#0 RecordTransactionCommit () at xact.c:1141#1 0x00000000005483f2 in CommitTransaction () at xact.c:2070#2 0x0000000000549078 in CommitTransactionCommand () at xact.c:2831#3 0x00000000008c8ea9 in finish_xact_command () at postgres.c:2523#4 0x00000000008c6b5d in exec_simple_query (query_string=0x2c97ec8 "commit ") at postgres.c:1170#5 0x00000000008cae70 in PostgresMain (argc=1, argv=0x2cc3dc8, dbname=0x2cc3c30" testdb ", username=0x2c94ba8" xdb ") at postgres.c:4182#6 0x000000000082642b in BackendRun (port=0x2cb9c00) at postmaster.c:4361#7 0x0000000000825b8f in BackendStartup (port=0x2cb9c00) at postmaster.c:4033#8 0x0000000000821f1c in ServerLoop () at postmaster.c:1706#9 0x00000000008217b4 in PostmasterMain (argc=1, argv=0x2c92b60) at postmaster.c:1379#10 0x00000000007488ef in main (argc=1, argv=0x2c92b60) at main.c:228 (gdb)
Get transaction ID
(gdb) p xid$3 = 2411 (gdb)
Set other variables, markXidCommitted-> True
(gdb) n1143 TransactionId latestXid = InvalidTransactionId; (gdb) 1148 int nmsgs = 0; (gdb) 1149 SharedInvalidationMessage * invalMessages = NULL; (gdb) 1150 bool RelcacheInitFileInval = false; (gdb) 1154 nrels = smgrGetPendingDeletes (true, & rels); (gdb) 1155 nchildren = xactGetCommittedChildren (& children); (gdb) 1156 if (XLogStandbyInfoActive ()) (gdb) 1159 wrote_xlog = (XactLastRecEnd! = 0) (gdb) 1165 if (! markXidCommitted) (gdb) p latestXid$4 = 0 (gdb) p markXidCommitted$5 = true (gdb) p nrels$6 = 0 (gdb) p nchildren$7 = 0 (gdb) p wrote_xlog$8 = true (gdb)
MarkXidCommitted is T, enter the corresponding processing logic.
Begin to enter the key part of the submission and insert the commit XLOG record.
(gdb) n1214 replorigin = (replorigin_session_origin! = InvalidRepOriginId & & (gdb) 1221 BufmgrCommit (); (gdb) p replorigin$9 = false (gdb)
Enter the commit section and set the current transaction timestamp
(gdb) n1240 START_CRIT_SECTION (); (gdb) 1241 MyPgXact- > delayChkpt = true; (gdb) 1243 SetCurrentTransactionStopTimestamp (); (gdb) p * MyPgXact$10 = {xid = 2411, xmin = 0, vacuumFlags = 0'\ 000mm, overflowed = false, delayChkpt = true, nxids = 0'\ 000'} (gdb)
Insert XLOG
(gdb) n1245 XactLogCommitRecord (xactStopTimestamp, (gdb) 1252 if (replorigin) (gdb))
Set up commit transaction data
(gdb) 1267 if (! replorigin | | replorigin_session_origin_timestamp = = 0) (gdb) 1268 replorigin_session_origin_timestamp = xactStopTimest (gdb) 1270 TransactionTreeSetCommitTsData (xid, nchildren, children, (gdb) 1300 if ((wrote_xlog & markXidCommitted & & (gdb))
Synchronously refresh XLOG
(gdb) 1301 synchronous_commit > SYNCHRONOUS_COMMIT_OFF) | | (gdb) 1300 if ((wrote_xlog & markXidCommitted & & (gdb) 1304 XLogFlush (XactLastRecEnd); (gdb) 1309 if (markXidCommitted) (gdb))
Update CLOG if we have written COMMIT WAL Record.
(gdb) 1310 TransactionIdCommitTree (xid, nchildren, children); (gdb) 1309 if (markXidCommitted) (gdb)
Exit the critical area of submission
(gdb) 1340 if (markXidCommitted) (gdb) 1342 MyPgXact- > delayChkpt = false; (gdb) 1343 END_CRIT_SECTION (); (gdb)
Calculate the final latestXid
(gdb) 1347 latestXid = TransactionIdLatest (xid, nchildren, children); (gdb) n1358 if (wrote_xlog & & markXidCommitted) (gdb) p latestXid$11 = 2411 (gdb)
Record the location of the last commit record
(gdb) n1359 SyncRepWaitForLSN (XactLastRecEnd, true); (gdb) 1362 XactLastCommitEnd = XactLastRecEnd; (gdb) 1365 XactLastRecEnd = 0; (gdb) 1368 if (rels) (gdb) 1371 return latestXid; (gdb) p XactLastCommitEnd$12 = 5522364896 (gdb)
Return, complete the call
(gdb) n1372} (gdb) CommitTransaction () at xact.c:20872087 TRACE_POSTGRESQL_TRANSACTION_COMMIT (MyProc- > lxid); (gdb)
DONE!
IV. Reference materials
How Postgres Makes Transactions Atomic
PG Source Code