Cheetah spot by spot: LAST COMMITTED READ
This is the first of a series of articles about the announced features of IDS v11.10, code named Cheetah and currently available as Beta release. I'll try to show the features in random order. Being the first doesn't mean it's more or less important than other features.
LAST COMMITTED READ is a new isolation level that is available in Cheetah. As you probably know, the isolation levels define how "isolated" one session's transactions are from other sessions. If you're not familiar with the subject I suggest you search online. Wikipedia has a nice article on this.
If you ever did any programming in Informix databases you surely understand the subject and you know that the two most used isolation levels are COMMITTED READ (the default for logging databases) and DIRTY READ. In short terms their definitions are:
- COMMITTED READ
In this isolation level, your session will "honour" other session's locks. The session's behaviour when it finds a locked resource (table row, index key) depends on the LOCK MODE you selected. If using the default (LOCK MODE NOT WAIT), you'll get an error immediately. If using a time wait (LOCK MODE TO WAIT [x seconds]), it will wait the maximum number of seconds for the lock release. If the lock remains after that timeout you'll get the same error as with lock mode NOT WAIT.
This is equivalent to the ANSI COMMITTED READ isolation level - DIRTY READ
In this mode, your session won't "see" any locks. It will not give you errors or wait. It will read whatever value is available at the moment. It is called "dirty" because this means you may get values that aren't and possibly will never be committed in the database.
This is equivalent to the ANSI UNCOMMITTED READ
Note also that this lock waiting and/or lock ignoring happens when we're SELECTing data. Obviously, if you want to change the data, even in DIRTY READ you'll have to wait for any lock holder to finish his transaction and unlock the resource.
So, what does Cheetah bring into this issue? A new isolation level called "COMMITTED READ LAST COMMITTED".
This isolation level is similar to COMMITTED READ in the sense that it only returns committed data, but with the great advantage that your reads will never block. If your session hits a locked resource, the IDS engine will retrieve the value that was there before the current lock holder session made any change. That's where the name "LAST COMMITTED" came...
This information is always available because if the lock holding transaction tries to ROLLBACK WORK, the original value will have to be restored.
Some of you with knowledge from other RDBMS like Oracle or Postgres may be wondering what's new about this... Well, historically there have been two types of RDBMS: The versioning (each transaction gets a "version" number that marks all the data it changes) and the non-versioning. The versioning RDBMSs have always this sort of isolation levels because each transaction sees the "current image" of the Database. But the non-versioning RDBMS usually didn't implement this. They have more light-weight reading primitives that read the data directly (not the before images marked with earlier versions stamps) and this causes the locking conflicts.
Also, some versioning RDBMS don't support the ANSI UNCOMMITTED READ, because the way they read doesn't allow it.
So, it's has been a kind of trade-off. But currently, most of the non-versioning databases implement this kind of COMMITTED isolation without blocking the readers.
Ok... But how do we use it?!
Well, the obvious answer is by issuing the "SET ISOLATION TO COMMITTED READ LAST COMMITTED". After this statement your session will be working in this new mode and it won't get blocked by any row or index locks while reading.
But this would force you to change your existing application code... and in some cases you possibly don't have access to that. So what can be done? Ah!... The nice people of R&D did think about this and you have three solutions:
- There is a new ONCONFIG parameter called USELASTCOMMITTED
You can set it to several values and depending of these values, the database server will automatically "promote" the specified isolation level to the new mode. The values are: - 'NONE'
This is the same as clearing it. No isolation level will be promoted. This is also the default value - 'COMMITTED READ'
The transactions with traditional COMMITTED READ will work in the new isolation level - 'DIRTY READ'
The transactions with traditional DIRTY READ will work in the new isolation level - 'ALL'
The transactions with traditional DIRTY READ and the transactions with traditional COMMITTED READ will work in the new isolation level - You can also use USELASTCOMMITTED as a client side environment variable. The syntax is the same
- If you want this promotion to happen only to specific users, you can use the new feature that allows you to force the execution of a stored procedure (sysdbopen) when a user connects to a database. Inside this procedure you could use the SET ISOLATION.... statement
- This feature requires that the tables use the ROW level locking. Tables with PAGE level locking will inhibit the feature in that transaction.
- When you established this new isolation level for a session, if it makes a distributed query to a remote database server that don't support this feature (i.e.: any pre v11.10) it will fallback to COMMITTED READ
- If the table is a RAW (non logged) table the feature will not work
- If the table has some user-defined columns and these don't support the new mode, the feature will not work
Now, let's see an example. To test this you'll need two sessions. We'll use the customer table from stores_demo database. Please make sure it's defined to use row level locking.
Session one:
- connect to the stores_demo
- show the customer data (fname, lname) for customer number 101
- upate this data to "John Doe" inside a transaction and maintain the transaction open
cheetah@PacMan.domus.online.pt:informix-> dbaccess stores_demo -
Database selected.
> select fname, lname from customer where customer_num = 101;
fname lname
Ludwig Pauli
1 row(s) retrieved.
> begin work; update customer set fname='John', lname='Doe' where customer_num=101;
Started transaction.
1 row(s) updated.
>
Now, in session 2:
- Set the isolation to committed read (default) and select the data: An error is returned
- Set the isolation to dirty read: the "current" data, as changed by session 1
- Set the isolation to the new mode: we get the original (pre session 1 update) data
cheetah@PacMan.domus.online.pt:informix-> dbaccess stores_demo -
Database selected.
> set isolation to committed read; select fname, lname from customer where customer_num = 101;
Isolation level set.
fname lname
244: Could not do a physical-order read to fetch next row.
107: ISAM error: record is locked.
Error in line 1
Near character position 90
> set isolation to dirty read; select fname, lname from customer where customer_num = 101;
Isolation level set.
fname lname
John Doe
1 row(s) retrieved.
> set isolation to committed read last committed; select fname, lname from customer where customer_num = 101;
Isolation level set.
fname lname
Ludwig Pauli
1 row(s) retrieved.
>
One final note: Recent versions of IDS have an ONCONFIG parameter (DEF_TABLE_LOCKMODE) where you can configure the default lock mode for tables created without the "LOCK MODE" clause. The current Cheetah Beta drop apparently doesn't support this. So, if you're counting on this to make your tests it won't work.
You can see aditional info about this subject at: