https://jira.jboss.org/jira/browse/HORNETQ-33 deals with storing heuristically completed transactions in our journal so that, after restart, we can inform the TM when it recover about these transactions.
I made several changes to the journal to support this and I'd like to check with our journal guru (yes, that's you cleber! :) if the changes are correct.
First, we only need to store the XIDs for heuristically completed tx. We do not have to remember TX which were completed upon the TM requests.
When I modified the Journal, in doubt, I mimicked the behavior provided by prepare records.
I modified the Journal API:
void appendCommitRecord(long txID, boolean sync) throws Exception; void appendCommitRecord(long txID, EncodingSupport transactionData, boolean sync) throws Exception; void appendRollbackRecord(long txID, boolean sync) throws Exception; void appendRollbackRecord(long txID, EncodingSupport transactionData, boolean sync) throws Exception;
When the tx is heuristically completed, I call the methods with the EncodingSupport parameter to encode the XID.
When the tx is completed by the TM, I do not encode the XID (passing a NullEncoding instead)
When we read the journal, I check for this transactionData for COMMIT and ROLLBACK records and add them to the JournalReaderCallback interface
void onReadCommitRecord(long transactionID, byte extraData, int numberOfRecords) throws Exception; void onReadRollbackRecord(long transactionID, byte extraData) throws Exception;
when I read commit or rollback record, if the extraData array is not empty, I instantiate a HeuristicCompletedTransactionHolder and add it to a List
Then, for each HeuristicCompletedTransactionHolder in the list, I add them to the LoaderCallback:
void addHeuristicCommittedTransaction(HeuristicCompletedTransactionInfo info);
In turn, the LoaderCallback adds each HeuristicCompletedTransactionInfo to a List that I added to the Journal API:
long load(List<RecordInfo> committedRecords, List<PreparedTransactionInfo> preparedTransactions, List<HeuristicCompletedTransactionInfo> heuristicCommittedTransactions, TransactionFailureCallback transactionFailure) throws Exception;
Finally, when the StorageManager calls this method Journal.load(), it adds all the HeuristicCompletedTransactionInfo to the ResourceManager interface:
void putHeuristicCompletion(Xid xid, boolean commit);
All is fine, the ResourceManager now knows the heuristically completed transactions and can reply correctly to the TM's recover().
In order to distinguish between a "regular" completion and a "heuristic" completion (which can only occur through our management API), I added the methods to our Transaction interface:
void heuristicCommit() throws Exception; void heuristicRollback() throws Exception;
There are still use cases that are not properly handled.
1. the admin heuristically completes a tx => the XID is stored in the journal
2. the TM calls recover() => we return the XID (the ResourceManager keeps a list of them)
3. the TM calls forget() for this XID => we must not keep the XID from the journal
Else, the XID will be read when the server is restarted but it will never be forgotten again.
Clebert, do you see how I can handle #3? I need to modify the record (to "erase" the XID). I don't know what is the best way to do so
Finally, a code style question, I added data structures to pass information between the various API (HeuristicCompletedTransactionHolder, HeuristicCompletedTransactionInfo).
I made them similar to the prepared data structure using public fields. Is there a reason we use public fields instead of getter/setter methods for these data structures?
Thanks for the help,