Transact Engine Tutorial

© 1997 Benoît GILON
v0.2.1, June 9th 1997
Logo de HTMLEdit v3
How to make an effective use of the Transact material provided here for the Acorn range of computers (recommended reading before programming over the Transact Engine).

Table of Content


Warning

This tutorial is written for the 0.2.1 release of the Transact engine as part of an effort from the author to help other people understand the main abilities of the Transact module. You should not take for granted the continued developing effort of this tutorial as part of the Transact engine itself. However, I'll be happy to communicate with those of you who would like to comment this document or simply request for further explanation on a specific topic related to the document.
My email is:
bgilon@free.fr
My native language is not English so be indulgent;

Prerequisites

All you should have is a RISC OS based computer running Risc OS 3.10 or later. I did'nt make any testing with a RO2 machine (too lazy to retrofit my A410/1 from RO3.1 to RO2): In case you are sticked to RO2 or RO3.0, I think the only prerequisite is the Shared C library version and I strongly recommend you upgrade to RO3.1.

Material overview

Inside the TransactM directory of the archive, you'll find a sub-directory named client which hold everything you need to make the "exercices";
Here is the role of some of the files laying there.
File name Purpose
DataFile Main data file: currently only 4 characters "abcd" but you can
change its contents either from inside the Transact programs or
from a stand alone editor (such as !Edit).
FileInsert File used by the menu program to insert characters at the end of
the main datafile: currently only 4 characters "Véro" but you can
change its contents by using an external editor (such as !Edit).
FileUpdate File used by the menu program to replace some of the original
characters with new ones: currently only 2 characters "cb" but
you can change its contents by using an external editor (such as
!Edit).
!Run1st Run the C client menu program but first recycle the Transact
Module by rmkilling it if previously alive.
!Run Same as above but does not attempt to recycle the Transact
module at first.
!Run1stB Same as !Run1st but starts the BASIC client instead.
!RunB Same as !Run but starts the BASIC client instead.
!RunImage C client executable file run from the !Run/!Run1st obey files.
TransactM Transact RM file (copied from the parent directory).

Some advanced features are only available thru the C client application and its use is preferred over the BASIC client application.

Concurrent access: basic concepts

The "liberal way"

As your computer is up and running, launch the !Edit application twice in the !Apps folder so that two icons reside on the icon bar as active tasks.
  1. Drag the Datafile to each of the icons. Two separate windows should popup.
  2. Modify the text content in the first window and then save it (use the F3 Return shortcut)
  3. Save the content of the second window to the same file (again use the F3 Return shortcut) (with no or different changes than previously applied);
  4. Close the two document windows;
Now shift-double click over the file icon in the File directory viewer; you may have noticed that the changes applied in the first document window has been lost since the second save operation;
Now imagine that the second window was opened by someone having access to your workfiles via a network.
This scheme offers no protection in term of lock management: when a data of a file needs to be updated some way then: Result: concurrent updates wich occured in the meantime are overwritten.

The "conservative" way

Well, in this example, we are using the C client program as a dumb file locker (every action and term used in this exercise will be explained further in relevant sections).
  1. If no TaskWindow server (such as !Edit) exists on the icon bar then install one of them (eg !Edit) on the icon bar now.
  2. Start the C Client menu program by double clicking on the !Run icon: A task window should appear;
  3. Press 1 to create a session;
  4. Press 1 to select the transaction mode (READ COMMITED): this sould lead to the main MENU;
  5. Press 8 to initiate a SELECT request to the Transact engine over the <Obey$Dir>.DataFile file.
  6. Use 0 as the start offset and 4 the end offset;
  7. Select a Forward Only cursor mode (value 0);
    You should see the lines below displayed:
    zdebut 0 zfin 4 type 0
    <abcd>
    Terminate the SELECT operation? (Y/N)
    

    Type Y and press Return;
  8. Now that the C client program has opened the file for update, shift doucle-click on the icon of the Datafile and see what happens then;
  9. A warning dialog box stating that "This file is already open" pops up.
Result: while a file is being opened for update mode by an application; no other application can have access to this file;
To allow other apps to make their own changes to the same file, the transact engine has been enhanced in its 0.2.1 release and hence the client menu program via its E option.

The "transactional" way

You guess it: that is using the Transact engine!
You will be able to retrieve the entire file' content in memory for editing while locking the whole against concurrent writes (better than the "liberal" way);
You will be able to do online changes w/o forbidding other applications from accessing disjointed parts of the same file for writing and the whole part of the file for reading!
Let's illustrate the hidden potential by emulating two client applications accessing the same file.
  1. Start the first app (app #1) by double-clicking on the !Run1st icon;
  2. Start the second app (app #2) by double-clicking on the !Run icon;
  3. In each of the open task windows, create a new session (READ COMMITED)
  4. Initiate an UPDATE operation in the first task window (app #1) (choose option 7) offset 1; that's mean that the letters "bc" in the original datafile are at least temporarily and for the sole session's view replaced to "cb";
  5. Check the result in the app #1 task window by initiating a SELECT operation begin offset 0, end offset 4, cursor mode Forward Only; The lines below should be displayed:
    zdebut 0 zfin 1 type 0	!read from the main data file
    <a>
    zdebut 1 zfin 3 type 2	!read from the log file (pending writes waiting to be validated|rejected)
    <cb>
    zdebut 3 zfin 4 type 0	!read from the main data file
    <d>
    Terminate the SELECT operation? (Y/N)

    Type Y to terminate the SELECT operation and hence close the cursor;
  6. In the app #2 window, initiate a SELECT operation (option #8) begin offset 0, end offset 4, cursor mode Forward Only; The lines below should be displayed:
    zdebut 0 zfin 4 type 0	!read from the main data file
    <abcd>
    Terminate the SELECT operation? (Y/N)

    Type Y to terminate the SELECT as usual;
    Partial conclusion: we have seen that a write operation does not lock concurrent read operations on the same data. That's a benefit from the multi-versioning system (please refer to the document "Transactional access to flat files" which is part of the same package as the document you're reading for more info). Also you may have noticed that you have selected READ COMMITED for the transaction mode of the session opened in the app #2, that's why the active transaction in the app #2 does not "see" pending writes of concurrent transactions before they are validated.
  7. In the app #2 window, initiate a SELECT FOR UPDATE operation (option #9) and specify the same parameters as above; this time, the operation should be less successful and the message below should be displayed:
    Erreur #2010007
    Conflict with another session

    The SELECT FOR UPDATE is like a normal SELECT except that the client app specifies its intent to update the data involved in a subsequent operation and hence has the same effect as an UPDATE request regarding the lock management. Therefore the message returned is to be considered normal given the fact that another program (app #1) held an update lock on bytes #1 and #2 of the main data file (starting from byte #0);
  8. To make things "work", reduce the "range" of our SELECT FOR UPDATE operation, reitereate the previous step but this time specify an end offset of 1 (only retrieve the first byte of the data file).
    zdebut 0 zfin 1 type 0
    <a>
    Terminate the SELECT operation? (Y/N)

    Type Y to terminate the SELECT as usual;
    Byte #0 ("a") is not involved in any pending write operation in the name of a concurrent transaction hence the absence of a conflict in this case.
  9. Before proceeding on the next chapter, we'll clean things up a bit! type 3 to drop the current session in the app #2 task window but keep the app #1 unchanged as the resulting configuration will serve soon.
The DELETE and INSERT operations are very simple to use; for the DELETE operator, the user has to enter the offset of the first byte relative to the beginning of the file chunk to remove.

Transaction modes: basic concepts

Briefly stated, the transaction mode (choosen as the session is created) acts upon the way the transactions view the main data files regarding to concurrent transactions.
In the previous chapter, we have seen that a READ COMMITED transaction mode session does not see "concurrent pending changes" until the concurrent transaction is validated.
In this chapter, we will show how DIRTY READ and REPEATABLE READ mode sessions behave.

DIRTY READ mode

  1. From the main menu in the second task window (app #2), create a new session and selects DIRTY READ mode as its transaction mode;
  2. Initiate a SELECT operation (option #8) an specify a begin offset 0, end offset 4 and Forward Only: The lines displayed should look like these:
    zdebut 0 zfin 1 type 0
    <a>
    zdebut 1 zfin 3 type 2
    <cb>
    zdebut 3 zfin 4 type 0
    <d>
    Terminate the SELECT operation? (Y/N)

    Type Y to terminate the SELECT as usual;
  3. Rollback the transaction originated from app #1 (currently with a pending UPDATE request) by selecting option D;
  4. Reiterate the SELECT operation in app #2 task window (same entry parms as above); You should then see the following lines being displayed.
    zdebut 0 zfin 4 type 0
    <abcd>
    Terminate the SELECT operation? (Y/N)

    Type Y to terminate the SELECT as usual;
    Conclusion: well, here we show that a DIRTY MODE transaction can see transient states in the data file life that is, it can read not yet validated changes which source is a concurrent transaction (from app #1 in this case).
  5. To clean things up, drop the active session (option 3) in the app#2 task window.

REPEATABLE READ mode

  1. In the app #2 task window, create a session and select REPEATABLE READ as its transaction mode;
  2. Initiate a SELECT operation in app #2 task window, params are: begin offset 0, end offset 4 and cursor mode: Forward Only. You should see the lines below:
    zdebut 0 zfin 4 type 0
    <abcd>
    Terminate the SELECT operation? (Y/N)

    Type Y to terminate the SELECT as usual;
  3. Initiate an UPDATE operation in app #1 task window, params are: offset 1;
  4. Initiate a COMMIT operation in app #1 task window; The datafile content now is "acbd";
  5. Initiate a SELECT operation in app #2 task window with the same parms as in step 2; You should see the lines below:
    zdebut 0 zfin 4 type 0
    <abcd>
    Terminate the SELECT operation? (Y/N)
    Type Y to terminate the SELECT as usual;
Conclusion: despite the fact that the data file content has been changed in between, the transaction's view of the data will never change apart from its own write request once it as been read. Of course at the beginning of the next transaction the change will be taken into account.

Miscellaneous topics

Result sets and cursors

Some facts to keep in mind: To illustrate this:
  1. Start an app (app #1) by double-clicking on the !Run1st icon;
  2. Create a new session (READ COMMITED)
  3. Initiate a SELECT operation begin offset 0, end offset 4, cursor mode Forward Only; the lines below should be displayed:
    zdebut 0 zfin 4 type 0	!read from the main data file
    <abcd>
    Terminate the SELECT operation? (Y/N)
    The lines above are displayed as part of a call to a debugging SWI and not to the normal FETCH SWI call, so type N this time;
  4. Initiate a Fetch operation on the newly created cursor (option A of the main menu). Start offset 0, End offset 4. The lines below should be displayed.
    p_fetch return status 4
    <abcd>
    4 characters have been fetched succesfully.
  5. Reiterate the same Fetch operation with the same parameters; This time an error should occur:
    Error #33619974
    The selected cursor mode doesn't allow such operation
  6. However, rereading the same data would work for a "Allow backwards" cursor mode result set; try it if you like by selecting option B to end dealing with the current result set and then repeat from step 3 but this time select an "Allow backwards" cursor mode when creating the result set.

Compound requests

Compound requests (introduced since the v0.2.1 release of the Transact engine) allow a client application to submit multiple write requests at the same time (that is in a single call).
We should focus on the fact that using compound requests currently is the only way to set intermediary rollback points as we'll see in the following example:
  1. Start an app (app #1) by double-clicking on the !Run1st icon;
  2. Create a new session (READ COMMITED);
  3. Initiate an INSERT operation in this task window;
  4. Launch a second instance of the client application (app #2) by double clicking on the !Run icon and create a new session the usual way;
  5. Initiate an UPDATE operation on the behalf of the newly started client app; parameters are begin offset 1;
  6. Initiate a compound request in the app #2 task window (option G); a specific menu should appear for specifying all the components of our compound request:
     Compound MENU
     -------------
    
    1 - INSERT operation
    2 - UPDATE operation
    3 - DELETE operation
    F - FINISH the data entry and call the Transact Engine
    Q - Cancel the compound request and return to the MAIN MENU
    Your choice ? : 
  7. Enters an UPDATE operation (option #2) from offset 0 at first;
  8. Then enters an INSERT operation (option #1);
  9. Choose option F to submit the two request in a row; The message below sould be displayed:
    Error #33619975
    Conflict with another session
    This message can be explained: we tried to insert bytes in a datafile as a pending insert exists as a part of an active concurrent transaction.
  10. From the main menu in the app #2 task window, initiate a commit operation (remember that we initiated an UPDATE operation in step 5 as the first operation of the current transaction).
  11. From the main menu in the first task window, initiate a SELECT operation; the result should be something like:
    zdebut 0 zfin 4 type 0
    <acbd>
    zdebut 4 zfin 8 type 4
    <Véro>
    Terminate the SELECT operation? (Y/N)
Conclusion: Althrough the compound request failed, the result was not a rollback to the begin state of the current transaction but only to the state of the transaction as the compound request was submitted. QED.

Troubleshooting guide

Unknown object error while running the client menu program The menu program always refer to the Obey$Dir environment variable; however, as applications are launched in your desktop: this variable is subject to multiple changes over time; this may imply that its value points to a directory unrelated to the Tramsact module at the time a request is submitted; If so an error is returned by the Transact engine saying "Unknown object"; To make things work again, all you have to do is to double-click on the ObeyFile inside the TransactM.client directory.