Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                
Skip to content

Commit 410b1df

Browse files
committed
Update the in-code documentation about the transaction system. Move it
into a README file instead of being in xact.c's header comment. Alvaro Herrera.
1 parent d6f8a76 commit 410b1df

File tree

2 files changed

+236
-127
lines changed

2 files changed

+236
-127
lines changed

src/backend/access/transam/README

Lines changed: 233 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,233 @@
1+
$PostgreSQL: pgsql/src/backend/access/transam/README,v 1.1 2004/08/01 20:57:59 tgl Exp $
2+
3+
The Transaction System
4+
----------------------
5+
6+
PostgreSQL's transaction system is a three-layer system. The bottom layer
7+
implements low-level transactions and subtransactions, on top of which rests
8+
the mainloop's control code, which in turn implements user-visible
9+
transactions and savepoints.
10+
11+
The middle layer of code is called by postgres.c before and after the
12+
processing of each query:
13+
14+
StartTransactionCommand
15+
CommitTransactionCommand
16+
AbortCurrentTransaction
17+
18+
Meanwhile, the user can alter the system's state by issuing the SQL commands
19+
BEGIN, COMMIT, ROLLBACK, SAVEPOINT, ROLLBACK TO or RELEASE. The traffic cop
20+
redirects these calls to the toplevel routines
21+
22+
BeginTransactionBlock
23+
EndTransactionBlock
24+
UserAbortTransactionBlock
25+
DefineSavepoint
26+
RollbackToSavepoint
27+
ReleaseSavepoint
28+
29+
respectively. Depending on the current state of the system, these functions
30+
call low level functions to activate the real transaction system:
31+
32+
StartTransaction
33+
CommitTransaction
34+
AbortTransaction
35+
CleanupTransaction
36+
StartSubTransaction
37+
CommitSubTransaction
38+
AbortSubTransaction
39+
CleanupSubTransaction
40+
41+
Additionally, within a transaction, CommandCounterIncrement is called to
42+
increment the command counter, which allows future commands to "see" the
43+
effects of previous commands within the same transaction. Note that this is
44+
done automatically by CommitTransactionCommand after each query inside a
45+
transaction block, but some utility functions also do it internally to allow
46+
some operations (usually in the system catalogs) to be seen by future
47+
operations in the same utility command (for example, in DefineRelation it is
48+
done after creating the heap so the pg_class row is visible, to be able to
49+
lock it).
50+
51+
52+
For example, consider the following sequence of user commands:
53+
54+
1) BEGIN
55+
2) SELECT * FROM foo
56+
3) INSERT INTO foo VALUES (...)
57+
4) COMMIT
58+
59+
In the main processing loop, this results in the following function call
60+
sequence:
61+
62+
/ StartTransactionCommand;
63+
/ ProcessUtility; << BEGIN
64+
1) < BeginTransactionBlock;
65+
\ CommitTransactionCommand;
66+
\ StartTransaction;
67+
68+
/ StartTransactionCommand;
69+
2) / ProcessQuery; << SELECT * FROM foo
70+
\ CommitTransactionCommand;
71+
\ CommandCounterIncrement;
72+
73+
/ StartTransactionCommand;
74+
3) / ProcessQuery; << INSERT INTO foo VALUES (...)
75+
\ CommitTransactionCommand;
76+
\ CommandCounterIncrement;
77+
78+
/ StartTransactionCommand;
79+
/ ProcessUtility; << COMMIT
80+
4) < EndTransactionBlock;
81+
\ CommitTransaction;
82+
\ CommitTransactionCommand;
83+
84+
The point of this example is to demonstrate the need for
85+
StartTransactionCommand and CommitTransactionCommand to be state smart -- they
86+
should call CommandCounterIncrement between the calls to BeginTransactionBlock
87+
and EndTransactionBlock and outside these calls they need to do normal start,
88+
commit or abort processing.
89+
90+
Furthermore, suppose the "SELECT * FROM foo" caused an abort condition. In
91+
this case AbortCurrentTransaction is called, and the transaction is put in
92+
aborted state. In this state, any user input is ignored except for
93+
transaction-termination statements, or ROLLBACK TO <savepoint> commands.
94+
95+
Transaction aborts can occur in two ways:
96+
97+
1) system dies from some internal cause (syntax error, etc)
98+
2) user types ROLLBACK
99+
100+
The reason we have to distinguish them is illustrated by the following two
101+
situations:
102+
103+
case 1 case 2
104+
------ ------
105+
1) user types BEGIN 1) user types BEGIN
106+
2) user does something 2) user does something
107+
3) user does not like what 3) system aborts for some reason
108+
she sees and types ABORT (syntax error, etc)
109+
110+
In case 1, we want to abort the transaction and return to the default state.
111+
In case 2, there may be more commands coming our way which are part of the
112+
same transaction block; we have to ignore these commands until we see a COMMIT
113+
or ROLLBACK.
114+
115+
Internal aborts are handled by AbortCurrentTransaction, while user aborts are
116+
handled by UserAbortTransactionBlock. Both of them rely on AbortTransaction
117+
to do all the real work. The only difference is what state we enter after
118+
AbortTransaction does its work:
119+
120+
* AbortCurrentTransaction leaves us in TBLOCK_ABORT,
121+
* UserAbortTransactionBlock leaves us in TBLOCK_ENDABORT
122+
123+
Low-level transaction abort handling is divided in two phases:
124+
* AbortTransaction executes as soon as we realize the transaction has
125+
failed. It should release all shared resources (locks etc) so that we do
126+
not delay other backends unnecessarily.
127+
* CleanupTransaction executes when we finally see a user COMMIT
128+
or ROLLBACK command; it cleans things up and gets us out of the transaction
129+
internally. In particular, we mustn't destroy TopTransactionContext until
130+
this point.
131+
132+
Also, note that when a transaction is committed, we don't close it right away.
133+
Rather it's put in TBLOCK_END state, which means that when
134+
CommitTransactionCommand is called after the query has finished processing,
135+
the transaction has to be closed. The distinction is subtle but important,
136+
because it means that control will leave the xact.c code with the transaction
137+
open, and the main loop will be able to keep processing inside the same
138+
transaction. So, in a sense, transaction commit is also handled in two
139+
phases, the first at EndTransactionBlock and the second at
140+
CommitTransactionCommand (which is where CommitTransaction is actually
141+
called).
142+
143+
The rest of the code in xact.c are routines to support the creation and
144+
finishing of transactions and subtransactions. For example, AtStart_Memory
145+
takes care of initializing the memory subsystem at main transaction start.
146+
147+
148+
Subtransaction handling
149+
-----------------------
150+
151+
Subtransactions are implemented using a stack of TransactionState structures,
152+
each of which has a pointer to its parent transaction's struct. When a new
153+
subtransaction is to be opened, PushTransaction is called, which creates a new
154+
TransactionState, with its parent link pointing to the current transaction.
155+
StartSubTransaction is in charge of initializing the new TransactionState to
156+
sane values, and properly initializing other subsystems (AtSubStart routines).
157+
158+
When closing a subtransaction, either CommitSubTransaction has to be called
159+
(if the subtransaction is committing), or AbortSubTransaction and
160+
CleanupSubTransaction (if it's aborting). In either case, PopTransaction is
161+
called so the system returns to the parent transaction.
162+
163+
One important point regarding subtransaction handling is that several may need
164+
to be closed in response to a single user command. That's because savepoints
165+
have names, and we allow to commit or rollback a savepoint by name, which is
166+
not necessarily the one that was last opened. In the case of subtransaction
167+
commit this is not a problem, and we close all the involved subtransactions
168+
right away by calling CommitTransactionToLevel, which in turn calls
169+
CommitSubTransaction and PopTransaction as many times as needed.
170+
171+
In the case of subtransaction abort (when the user issues ROLLBACK TO
172+
<savepoint>), things are not so easy. We have to keep the subtransactions
173+
open and return control to the main loop. So what RollbackToSavepoint does is
174+
abort the innermost subtransaction and put it in TBLOCK_SUBENDABORT state, and
175+
put the rest in TBLOCK_SUBABORT_PENDING state. Then we return control to the
176+
main loop, which will in turn return control to us by calling
177+
CommitTransactionCommand. At this point we can close all subtransactions that
178+
are marked with the "abort pending" state. When that's done, the outermost
179+
subtransaction is created again, to conform to SQL's definition of ROLLBACK TO.
180+
181+
Other subsystems are allowed to start "internal" subtransactions, which are
182+
handled by BeginInternalSubtransaction. This is to allow implementing
183+
exception handling, e.g. in PL/pgSQL. ReleaseCurrentSubTransaction and
184+
RollbackAndReleaseCurrentSubTransaction allows the subsystem to close said
185+
subtransactions. The main difference between this and the savepoint/release
186+
path is that BeginInternalSubtransaction is allowed when no explicit
187+
transaction block has been established, while DefineSavepoint is not.
188+
189+
190+
pg_clog and pg_subtrans
191+
-----------------------
192+
193+
pg_clog and pg_subtrans are permanent (on-disk) storage of transaction related
194+
information. There is a limited number of pages of each kept in memory, so
195+
in many cases there is no need to actually read from disk. However, if
196+
there's a long running transaction or a backend sitting idle with an open
197+
transaction, it may be necessary to be able to read and write this information
198+
from disk. They also allow information to be permanent across server restarts.
199+
200+
pg_clog records the commit status for each transaction. A transaction can be
201+
in progress, committed, aborted, or "sub-committed". This last state means
202+
that it's a subtransaction that's no longer running, but its parent has not
203+
updated its state yet (either it is still running, or the backend crashed
204+
without updating its status). A sub-committed transaction's status will be
205+
updated again to the final value as soon as the parent commits or aborts, or
206+
when the parent is detected to be aborted.
207+
208+
Savepoints are implemented using subtransactions. A subtransaction is a
209+
transaction inside a transaction; it gets its own TransactionId, but its
210+
commit or abort status is not only dependent on whether it committed itself,
211+
but also whether its parent transaction committed. To implement multiple
212+
savepoints in a transaction we allow unlimited transaction nesting depth, so
213+
any particular subtransaction's commit state is dependent on the commit status
214+
of each and every ancestor transaction.
215+
216+
The "subtransaction parent" (pg_subtrans) mechanism records, for each
217+
transaction, the TransactionId of its parent transaction. This information is
218+
stored as soon as the subtransaction is created. Top-level transactions do
219+
not have a parent, so they leave their pg_subtrans entries set to the default
220+
value of zero (InvalidTransactionId).
221+
222+
pg_subtrans is used to check whether the transaction in question is still
223+
running --- the main Xid of a transaction is recorded in the PGPROC struct,
224+
but since we allow arbitrary nesting of subtransactions, we can't fit all Xids
225+
in shared memory, so we have to store them on disk. Note, however, that for
226+
each transaction we keep a "cache" of Xids that are known to be part of the
227+
transaction tree, so we can skip looking at pg_subtrans unless we know the
228+
cache has been overflowed. See storage/ipc/sinval.c for the gory details.
229+
230+
slru.c is the supporting mechanism for both pg_clog and pg_subtrans. It
231+
implements the LRU policy for in-memory buffer pages. The high-level routines
232+
for pg_clog are implemented in transam.c, while the low-level functions are in
233+
clog.c. pg_subtrans is contained completely in subtrans.c.

src/backend/access/transam/xact.c

Lines changed: 3 additions & 127 deletions
Original file line numberDiff line numberDiff line change
@@ -3,138 +3,14 @@
33
* xact.c
44
* top level transaction system support routines
55
*
6+
* See src/backend/access/transam/README for more information.
7+
*
68
* Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group
79
* Portions Copyright (c) 1994, Regents of the University of California
810
*
911
*
1012
* IDENTIFICATION
11-
* $PostgreSQL: pgsql/src/backend/access/transam/xact.c,v 1.175 2004/08/01 17:32:13 tgl Exp $
12-
*
13-
* NOTES
14-
* Transaction aborts can now occur two ways:
15-
*
16-
* 1) system dies from some internal cause (syntax error, etc..)
17-
* 2) user types ABORT
18-
*
19-
* These two cases used to be treated identically, but now
20-
* we need to distinguish them. Why? consider the following
21-
* two situations:
22-
*
23-
* case 1 case 2
24-
* ------ ------
25-
* 1) user types BEGIN 1) user types BEGIN
26-
* 2) user does something 2) user does something
27-
* 3) user does not like what 3) system aborts for some reason
28-
* she sees and types ABORT
29-
*
30-
* In case 1, we want to abort the transaction and return to the
31-
* default state. In case 2, there may be more commands coming
32-
* our way which are part of the same transaction block and we have
33-
* to ignore these commands until we see a COMMIT transaction or
34-
* ROLLBACK.
35-
*
36-
* Internal aborts are now handled by AbortTransactionBlock(), just as
37-
* they always have been, and user aborts are now handled by
38-
* UserAbortTransactionBlock(). Both of them rely on AbortTransaction()
39-
* to do all the real work. The only difference is what state we
40-
* enter after AbortTransaction() does its work:
41-
*
42-
* * AbortTransactionBlock() leaves us in TBLOCK_ABORT and
43-
* * UserAbortTransactionBlock() leaves us in TBLOCK_ENDABORT
44-
*
45-
* Low-level transaction abort handling is divided into two phases:
46-
* * AbortTransaction() executes as soon as we realize the transaction
47-
* has failed. It should release all shared resources (locks etc)
48-
* so that we do not delay other backends unnecessarily.
49-
* * CleanupTransaction() executes when we finally see a user COMMIT
50-
* or ROLLBACK command; it cleans things up and gets us out of
51-
* the transaction internally. In particular, we mustn't destroy
52-
* TopTransactionContext until this point.
53-
*
54-
* NOTES
55-
* The essential aspects of the transaction system are:
56-
*
57-
* o transaction id generation
58-
* o transaction log updating
59-
* o memory cleanup
60-
* o cache invalidation
61-
* o lock cleanup
62-
*
63-
* Hence, the functional division of the transaction code is
64-
* based on which of the above things need to be done during
65-
* a start/commit/abort transaction. For instance, the
66-
* routine AtCommit_Memory() takes care of all the memory
67-
* cleanup stuff done at commit time.
68-
*
69-
* The code is layered as follows:
70-
*
71-
* StartTransaction
72-
* CommitTransaction
73-
* AbortTransaction
74-
* CleanupTransaction
75-
*
76-
* are provided to do the lower level work like recording
77-
* the transaction status in the log and doing memory cleanup.
78-
* above these routines are another set of functions:
79-
*
80-
* StartTransactionCommand
81-
* CommitTransactionCommand
82-
* AbortCurrentTransaction
83-
*
84-
* These are the routines used in the postgres main processing
85-
* loop. They are sensitive to the current transaction block state
86-
* and make calls to the lower level routines appropriately.
87-
*
88-
* Support for transaction blocks is provided via the functions:
89-
*
90-
* BeginTransactionBlock
91-
* CommitTransactionBlock
92-
* AbortTransactionBlock
93-
*
94-
* These are invoked only in response to a user "BEGIN WORK", "COMMIT",
95-
* or "ROLLBACK" command. The tricky part about these functions
96-
* is that they are called within the postgres main loop, in between
97-
* the StartTransactionCommand() and CommitTransactionCommand().
98-
*
99-
* For example, consider the following sequence of user commands:
100-
*
101-
* 1) begin
102-
* 2) select * from foo
103-
* 3) insert into foo (bar = baz)
104-
* 4) commit
105-
*
106-
* in the main processing loop, this results in the following
107-
* transaction sequence:
108-
*
109-
* / StartTransactionCommand();
110-
* 1) / ProcessUtility(); << begin
111-
* \ BeginTransactionBlock();
112-
* \ CommitTransactionCommand();
113-
*
114-
* / StartTransactionCommand();
115-
* 2) < ProcessQuery(); << select * from foo
116-
* \ CommitTransactionCommand();
117-
*
118-
* / StartTransactionCommand();
119-
* 3) < ProcessQuery(); << insert into foo (bar = baz)
120-
* \ CommitTransactionCommand();
121-
*
122-
* / StartTransactionCommand();
123-
* 4) / ProcessUtility(); << commit
124-
* \ CommitTransactionBlock();
125-
* \ CommitTransactionCommand();
126-
*
127-
* The point of this example is to demonstrate the need for
128-
* StartTransactionCommand() and CommitTransactionCommand() to
129-
* be state smart -- they should do nothing in between the calls
130-
* to BeginTransactionBlock() and EndTransactionBlock() and
131-
* outside these calls they need to do normal start/commit
132-
* processing.
133-
*
134-
* Furthermore, suppose the "select * from foo" caused an abort
135-
* condition. We would then want to abort the transaction and
136-
* ignore all subsequent commands up to the "commit".
137-
* -cim 3/23/90
13+
* $PostgreSQL: pgsql/src/backend/access/transam/xact.c,v 1.176 2004/08/01 20:57:59 tgl Exp $
13814
*
13915
*-------------------------------------------------------------------------
14016
*/

0 commit comments

Comments
 (0)