Database Management for Smalltalk

Archive for the 'FAQ' Category

Thu 20th Dec 2007   09:12 PM
posted by John Clapperton

Q:  “What’s the best way to implement interactive transactions?”

A:  The choice is either:

1)  Get all user input, then validate and execute in a block transaction forked off in a background process,  or

2)  Use open-ended transactions, which allow the user to browse and twiddle, retaining all locks until rollback or commit. 

1) has the advantage that any number of windows can do this concurrently and also the user interface process is free to do other things whilst the background process is running.  However, it does mean that user input cannot be validated field by field as it is input by the user.  A separate read-only background block transaction could validate each user input, but then there is the possibility that another user may change objects after the validation transaction has released its locks and before the read-write transaction performs the update.

However, in some applications this may not matter, for example the user may enter a part number, immediately see a description retrieved by a read-only background block transaction, and continue so forth for the transaction as a whole; the specified part description could have been changed, or the part removed from the dictionary, in the meantime by another user, causing a rare error in the update transaction, but something which changes frequently, such as stock-on-hand, should be validated and updated all in the same transaction whilst locks are retained.

2)  (Use open-ended transactions, which allow the user to browse and twiddle, retaining all locks until rollback or commit.) Open-ended transactions are normally used only in the user interface process, so if running VOSS in non-interactive mode take care to set up the transaction attributes to handle lock time-outs as intended, (see VOTransaction>>rollbackOnTimeOut: aBoolean).  In interactive mode VOSS gives the user a dialog of choices on lock time-out.

Any process can have a tree of nested and sibling sub-transactions. Whenever a transaction is committed or rolled back its whole sub-tree goes the same way. It is therefore possible to create sibling sub-transactions (of ‘Top’, typically) for separate windows which may be separately committed or rolled back under the control of their respective window buttons.

Whenever a process sends a message to a virtual object it is locked on behalf of that process into that process’s current transaction, so each window is responsible for making its transaction current each time that window gets focus.  When a transaction commits or rolls back it removes all its objects from its collection of locked objects, but the actual lock on an object, held by the voSession on behalf of its process, is not released until the last remaining transaction which has a lock on it has committed or rolled back. Since all these transaction belong to the same process they share objects, i.e. they don’t wait for locks on objects locked by each other as they would for locks held by transactions belonging to other processes, so care is required if two windows (sharing the same User Interface Process) are sharing access to any objects, they can overwrite each others changes.

A transaction tree can be created in the Control Panel (* indicates the current transaction), or with test code like this:

  Smalltalk “set up some global variables”
    at: #TxnA put: nil;
    at: #TxnB put: nil;
    at: #QAZ put: nil.

Then evaluate the following one line at a time updating the Control Panel each time to see the effect

  TxnA := VOTransaction newNamed: ‘txnA’.
  TxnA superTransaction makeCurrent.
  TxnB := VOTransaction newNamed: ‘txnB’.
  TxnA makeCurrent.
  QAZ := (Array newVirtual: 1) atAllPut: $x.
  TxnB makeCurrent.
  QAZ “display in workspace (and gets the object into TxnB too)”
  TxnB commit.
  TxnA makeCurrent.
  QAZ atAllPut: $y.
  TxnA commit.
  TxnA := TxnB := QAZ := nil.

Thu 20th Dec 2007   09:12 PM
posted by John Clapperton

Q:  “What are the pros and cons of open-ended vs block transactions?”

A:  An open-ended transaction is delimited by the execution of paired statements:

  VOTransaction newNamed: ‘Update customer’.     “starts a sub-transaction of the current transaction of the current process and makes it current”
  VOTransaction commit. “commits the current transaction of the current process”


  | aTxn |
  aTxn := VOTransaction new.
  aTxn name: ‘Update customer’.
  aTxn commit.

The paired statements do not have to be in the same method, but it is the programmer’s responsibility to ensure proper nesting.  The system ensures that there is always a default open-ended transaction called ‘Top’ for each process which has ever sent a message to a virtual object.  ‘Top’ is not normally used in applications, if objects are ever found in ‘Top’ it is usually a programming error.

With VOSS-on-client, open-ended transactions simplify the implementation of interactive transactions since the user interface process can access virtual objects directly and also return to input-watching during the transaction, with simple button-clicks for transaction start/commit/rollback. Multiple windows can have concurrently open sibling open-ended transactions in the user-interface process.

Block transactions are of the form:

  [:thisTxn |
    thisTxn name: ‘Update customer’. 
    “or name constructed via message to virtual object(s), must be done in transaction”
    (some rollback condition) ifTrue: [^anObject]. “Any explicit return forces rollback”
  ] atomicIfSucceed: [] ifFail: [].  “starts, executes and commits a sub-transaction of the current transaction of the current process”

These have the advantages of enforcing proper nesting, and by default will automatically rollback and retry on lock request time-out (lock requests are automatic).  Block transactions are also convenient to run in background processes and are therefore normal when VOSS is configured as a server.

Thu 20th Dec 2007   09:12 PM
posted by John Clapperton

Q:  “What are the pros and cons of configuring VOSS-on-server or VOSS-on-client?”

A:  VOSS-on-client is conceptually simpler since the user interface and virtual objects can both be accessed directly by the application running on the client, but in this configuration all objects touched in a transaction travel over the network to the client. This configuration can be multi-user since any number of images on separate machines on a LAN can connect to the same virtual space(s) on file-server(s). However, it’s more vulnerable to crashes, as a desktop machine could be switched off in the middle of committing a transaction, which will block all access to the virtual spaces touched by that transaction until after a Database Administration person has performed a restart and backup (if the transaction was writing to the transaction log at that instant), or a rollforward recovery (if it was writing to (one of) the work virtual space(s) at that instant).

When a VOSS-enabled image is running on a server, the application serves remote clients by concurrent background processes in the image.  This keeps the entire transaction on the server, but means the application must communicate with its clients via byte string data transfer.  Virtual objects may be serialized for transmission and re-instantiation between client and server if the client is also running Smalltalk, but synchronization is the responsibility of the application.

Thu 20th Dec 2007   09:12 PM
posted by John Clapperton

Q:  “If an object has an OrderedCollection as an attribute which contains a list of other objects in the virtual space, do I need to send the #madeVirtual message to the actual OrderedCollection object?”

A:  You don’t need to, but you can if you want to.  It’s entirely up to you how to organise the granularity; the only rule is that anything which is to be referenced by more than one virtual object must be a separate virtual object itself i.e. have been sent #madeVirtualIn: before assignment or #becomeVirtualIn: after assignment (but that’s slow).

If the OrderedCollection is small and frequently accessed then make it part of its parent virtual object i.e. don’t send it #madeVirtualIn:, but if it’s large or infrequently used you may prefer to make it a separate virtual object, so that it is not read from disk being part of its owner, only if sent a message; the elements of the OrderedCollection should themselves be separate virtual objects if they are referenced by any other virtual object, but if they are, for example short Strings that would be unnecessary and inefficient.  If in doubt, use a VirtualCollection and consider optimising to an OrderedCollection later, when the application is better understood.

VirtualCollection and VirtualDictionary etc are Btrees in which each Btree node is a separate virtual object, which is much more efficient for large collections, and not much slower for small ones.