From: Jim Starkey Date: March 16 2009 6:18pm Subject: Re: New Transaction State object (Was: Problems with record visibility and how it is computed) List-Archive: http://lists.mysql.com/falcon/617 Message-Id: <49BE9806.6070207@nimbusdb.com> MIME-Version: 1.0 Content-Type: text/plain; charset=ISO-8859-1; format=flowed Content-Transfer-Encoding: 7bit I think TransactionState will also need: * Transaction id (for validation and sanity control) * Transaction state * syncPending (so it can be waited on) This makes TransactionState the primary operational object and Transaction mostly for bookkeeping. I think this also eliminates the need for TransactionManager::activeTransactions. Olav Sandstaa wrote: > Subj: New TransactionState object > > Hi, > > In our Athens meeting I went through the alternatives for how to solve > the issues with the "RecordVersion to Transaction object pointer". We > agreed on that introducing a "transaction state object" (proposal 9 > quoted below) seemed to be the best and least intrusive alternative. > > I have not looked into the actual existing code yet but here is an > initial overview of how my current plan for the "transaction state > object" is (it might changes as I look more at the existing code and > use of it): > > A. New data structure: > ====================== > > A new class called TransactionState will be introduced. This will as > a minimum store the following members: > > class TransactionState > { > TransId commitId; > volatile INTERLOCKED useCount; } > > It is also likely that we will move the transaction id to this > class (could save us from storing this in each RecordVersion > object). The transaction state of the transaction might also be > moved to this object. > > Changes to existing classes: > > 1. The Transaction object will have a pointer to a TransactionState > object. When committing the transaction the commitId will be > stored in the TransactionState object, not in the transaction > object. > > 2. The RecordVersion object will have a pointer to a > TransactionState object (replacing the existing pointer to the > Transaction object) (if there is need for accessing the > Transaction object from the RecordVersion object we will either > keep the transaction pointer or have a transaction pointer from > the TransactionState object back to its Transaction object) > > Goal with this new TransactionState object is to let it "live" as > long as there is RecordVersion objects refering to it (and let the > "real" transaction object be "purged" at "any time"). > > B. Controlling the "life span" of TransactionState objects > ========================================================== > > We will use a reference counter in the Transaction state object for > controlling when a Transaction state object can be deleted. > Operations on this reference counter will be done by using > interlocked operations (if would probably be possible to implement > this without interlocked instruction but that would make the > implementation a bit more complex and harder to maintain and the > discussion in Athens concluded that the cost of using interlocked > operations for this case should not be a performance problem). > > The reference counter will be incremented the following places: > > 1. In the Transcation's constructor when the TransactionState > object is created (we can also delay this until the first > Record is attached to a Transaction (Transaction::addRecord()) > but it is simpler to just create it when we create the > Transaction object) > 2. For every RecordVersion having a pointer to it (most likely in > the RecordVersion's constructor - have not looked at the code > yet) > > The reference counter will be decremented the following places: > > 1. In the Transaction's destructor > 2. When the RecordVersion is deleted (most likely in the > destructor) > > There should be no objects using the TransactionState object > without having incremented the reference counter (or accessing it > indirectly via the a Transaction object or a RecordVersion object). > > C. Usage of the TransactionState object: > ======================================== > > The initial usage of the TransactionState objects will be as > follows: > > 1. Each time when creating a transaction object, we create a > transaction state object and a pointer to the transaction state > object from the transaction object. > > 2. When we commit: the commitId is written to the transaction > state object (not the transaction object). So the commit is the > update to the transaction state object (not that it is possible > that we also will store the state of the Transaction in the > TransactionState object) > > 3. Each time we create a record version object it has a pointer to > the transaction state object - and the reference count is > incremented (if needed also a pointer to the > transaction object (hopefully not, alternatively have a pointer > from the TransactionState object back to the transaction) > > 4. When we need to compute visibility of records we only need to > use the transaction state object. This will have at least the > same life span as the record version objects pointing to it > (thus the pointer is guaranteed to be valid) > > 5. When the scavenger removes a recordversion it decrement the > reference counter in the transaction state object > > 6. When we purge old Transaction objects we decrement the referece > counter in the Transaction State object > > 7. When the reference counter reaches 0 we delete the transaction > state object (by "self destruction") > > > That is the current plan - and as most plans it might change :-) > > Please let me know if you have any feedback or suggestions. > > Olav > > Olav Sandstaa wrote: >> In addition to these I also have two new possible solutions: >> >> 9. (This is partly based on something Ann wrote: Make the >> transasction pointer indirect combined with my original proposal >> number 5: let the transaction objects live as long as there are >> record version pointing to them): Introduce a new "Transaction state >> object" that contains as a minimum the "commitId" but most likely >> also the "transactionId" and "state": >> >> 1. Each time when creating a transaction object, we create a >> transaction state object and a pointer to the transaction state >> object from the transaction object. >> 2. When we commit: the commitId is written to the transaction >> state object (not the transaction object). So the commit is the >> update to the transaction state object. >> 3. Each time we create a record version object it has a pointer >> to the transaction state object (and if needed also to the >> transaction object) >> 4. Each time we add a record to a transaction (ie. update a >> record) we increment a useCount in the transaction state object: This >> should not need any extra locking (I think) >> 5. When we need to compute visibility of records we only need to >> use the transaction state object. This will have at least the same >> life span as the record version objects pointing to it (thus the >> pointer is guaranteed to be valid) >> 6. When the scavenger removes a recordversion it decrement the >> useCount in the transaction state object (I think this can be done >> safely without locking given that we have one scavenger) >> 7. When the useCount reaches 0 we delete the transaction state >> object. >> >> Issue/question: is there any other places in the code that will >> access this transaction state object than by accessing it from the >> record version object? >> >> I assume when normal transactions accesses record version objects >> they are "locked" and this will implicitely also lock the transaction >> state object when normal transactions access it? > > -- Jim Starkey President, NimbusDB, Inc. 978 526-1376