Begin
Mark beginning of transaction.
Short Name
TRANBEG()
Type
Low-Level data file function
Declaration
LONG Begin(COUNT trnmod)
Description
Begin() marks the beginning of a transaction. All files opened by this user, that have been created with a file mode of either ctPREIMG or ctTRNLOG, will have file updates held until they are committed with a matching call to Commit(). For a full description of transaction processing, see the chapter on Data Integrity.
trnmod values presently supported are summarized in the following table:
trmmod value |
Operation |
---|---|
ctTRNLOG |
Full transaction processing functionality, including auto-recovery. Mutually exclusive to ctPREIMG. |
ctPREIMG |
Transaction atomicity only, auto-recovery is not available. Mutually exclusive to ctTRNLOG. |
ctENABLE |
Automatically implements LockISAM(ctENABLE) when applicable. Mutually exclusive with ctREADREC. |
ctREADREC |
Automatically implements LockISAM(ctREADREC) when applicable. Mutually exclusive with ctENABLE. |
ctLK_BLOCK |
Automatically implements blocking locks when OR-ed with ctREADREC or ctENABLE. |
ctLK_RECR |
Enables recursive locking. |
ctSAVENV |
Saves current ISAM position after each successful ISAM file update operation (i.e., ReWriteRecord(), DeleteRecord(), etc. ). |
ctAUTOSAVE |
Automatically invokes savepoints after each successful ISAM or resource update. Disables ctSAVENV. |
ctTWOFASE |
Enables two-phase transaction support. |
ctDEFERBEG |
Enables a deferred transaction begin state. The TRANBEG log entry is delayed until an update is made within the transaction. This can benefit performance when no updates are made as the transaction flush can ultimately be skipped. |
Either ctPREIMG or ctTRNLOG must be used to specify the transaction logging mode.
Note: Using ctPREIMG with a file created with ctTRNLOG results in a TTYP_ERR (99).
Either ctENABLE or ctREADREC may also be OR-ed in to automatically invoke LockISAM(). If either is used, ctLK_BLOCK can be OR-ed in to convert the locks to blocking locks. This sleeps the process until the lock is available instead of returning DLOK_ERR (42).
If ctSAVENV is used, or if the user profile in InitCTreeXtd() or InitISAMXtd() includes USERPRF_SAVENV, then the current ISAM position of the files updated during the transaction is saved. This allows a subsequent Abort() or RestoreSavePoint() to reset the current ISAM position of the files.
Automatic Savepoints
ctAUTOSAVE automatically follows each successful ISAM update or resource update with a special savepoint. The savepoint permits an update error to be rolled-back so that a compound transaction can continue its updates and subsequently Commit() the transaction. This automatic savepoint does NOT support the ctSAVENV profile setting. ctSAVENV is disabled if ctAUTOSAVE is on.
This approach is faster than explicit savepoints after each update since no network traffic is required between updates, and the auto savepoint is more efficient than an explicit savepoint.
If an error occurs in the middle of a set of updates within a single transaction, and if ctAUTOSAVE was in trnmod, the application simply calls RestoreSavePoint(-1) to roll-back the error, and continues update processing.
Explicit calls to SetSavePoint() should not be made when ctAUTOSAVE is effective. Error ASAV_ERR (532) will be returned by SetSavePoint() if called after updates have occurred. A typical usage pattern in pseudo-code would be:
Begin(ctTRNLOG | ctAUTOSAVE | ctENABLE)
loop: update operation
if error
RestoreSavePoint(-1)
if not done
goto loop
Commit(...)
Deferred Begin
It is not uncommon for a higher-level API to start transactions without knowledge of whether or not any updates will occur. To reduce the overhead of unnecessary log flushes, a deferred mode is available. ctDEFERBEG results in the actual TRANBEG entry in the log to be delayed until an attempt is made to update a transaction controlled file, and if a TRANEND() or TRANABT() is called without any updates, then the transaction begin and end log entries are not flushed to disk. ctDEFERBEG has no affect for ctPREIMG transactions.
Note: When using a transaction mode of deferred begin, Begin(ctTRNLOG | ctDEFERBEG) returns a value of 1 as the actual transaction number assignment is delayed until the log write of the first file update takes place.
Two-Phase Transactions
Two-Phase transaction support allows, for example, a transaction to span multiple servers. This is useful for updating information from a master database to remote databases in an all-or-nothing approach.
To start a transaction that supports a two-phase commit, you would include the ctTWOFASE attribute in the transaction mode passed to the Begin() function. Call the TRANRDY() function to execute the first commit phase, and finally Commit() to execute the second commit phase.
Note: You may need additional caution with regard to locking and unlocking of records as your transactions become more complex in a multi-server environment to avoid performance problems.
Example
(Note that this example could also use individual threads of operation for the different c-tree Server connections, avoiding the c-tree instance calls.)
COUNT rc1,rc2,filno1,filno2,rcc1,rcc2;
TEXT databuf1[128],databuf2[128];
/* Create the connections and c-tree instances */
...
if (!RegisterCtree("server_1")) {
SwitchCtree("server_1");
InitISAMXtd(10, 10, 64, 10, 0, "ADMIN", "ADMIN", "FAIRCOMS1");
filno1 = OPNRFIL(0, "mydata.dat", ctSHARED);
FirstRecord(filno1, databuf1);
memcpy (databuf1, "new data", 8);
/* Prepare transaction on c-tree server 1 */
Begin(ctTRNLOG | ctTWOFASE | ctENABLE);
ReWriteRecord(filno1, databuf2);
rc1 = TRANRDY();
}
if (!RegisterCtree("server_2")) {
SwitchCtree("server_2");
InitISAMXtd(10, 10, 64, 10, 0, "ADMIN", "ADMIN", "FAIRCOMS2");
filno2 = OPNRFIL(0, "mydata.dat", ctSHARED);
FirstRecord(filno2, databuf2);
memcpy (databuf2, "new data", 8);
/* Prepare transaction on c-tree server 2 */
Begin(ctTRNLOG | ctTWOFASE | ctENABLE);
ReWriteRecord(filno2, databuf2);
rc2 = TRANRDY();
}
/* Commit the transactions */
if (!rc1 && !rc2) {
SwitchCtree("server_1");
rcc1 = Commit(ctFREE);
SwitchCtree("server_2");
rcc2 = Commit(ctFREE);
if (!rcc1 && !rcc2) {
printf("Transaction successfully committed across both servers.\n");
} else {
printf("One or more units of the second commit phase of the transaction failed: rcc1=%d rcc2=%d\n", rcc1, rcc2);
}
} else {
printf("One or more of the transactions failed to be prepared: rc1=%d rc2=%d\n", rc1, rc2);
printf("Pending transactions will be aborted.\n");
SwitchCtree("server_1");
Abort();
SwitchCtree("server_2");
Abort();
}
/* Done */
SwitchCtree("server_1");
CloseISAM();
SwitchCtree("server_2");
CloseISAM();
Note: Two-Phase transactions can become extremely difficult to debug should there be communications problems between servers at any time during the second commit phase. This can result in out of sync data between the servers as one server may have committed while another server failed. It is always appropriate to check the return codes of the individual Commit() functions to ensure a complete successful transaction commit across multiple servers.
Return
Begin() returns a transaction number (low word for six-byte transaction numbers) if successful, or zero on error. With six-byte transaction number support, call ctGETHGH() for the high word of the transaction number.
It an error occurs Begin() returns 0 and uerr_cod will return the appropriate c-tree error code.
Value |
Symbolic Constant |
Explanation |
---|---|---|
70 |
TEXS_ERR |
Transaction already pending - you cannot start one until the prior one is committed or aborted. |
99 |
TTYP_ERR |
Transaction type / trnmod conflict. |
150 |
SHUT_ERR |
Server is shutting down. |
See FairCom DB Error Codes for a complete listing of valid FairCom DB error values.
Example
COUNT savepoint;
void domaster() {
Begin(ctENABLE|ctTRNLOG); /* start transaction with locks */
while(another()); { /* get next record to add */
savepoint = SetSavePoint(); /* get save point at
beginning of each master record */
if (add_master() < 0)
Abort(); /* Begin if can't add master rec */
dodetail(); /* process detail records */
}
if (Commit(ctFREE) != 0)
printf("\nError %d in transaction",uerr_cod);
return;
}
void dodetail() {
while(moredetail()); { /*get next detail record to add */
if (add_detail()<0) { /* add details, if possible */
RestoreSavePoint(savepoint) /* with error, return
to savept */
return;
}
}
}
See also
Abort, AbortXtd, Commit, ClearSavePoint, ctGETHGH, ctSETHGH, DeleteRecord, InitCTreeXtd, InitISAMXtd, LockISAM, RestoreSavePoint, ReWriteRecord, SetSavePoint, SetOperationState, TRANRDY