4 Replies Latest reply on Sep 30, 2009 9:58 AM by timfox

    storage of heuristically completed transactions

    jmesnil

      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,
      jeff



        • 1. Re: storage of heuristically completed transactions
          clebert.suconic

          Just to complete the thread for future reference.


          We talked about this over IRC,


          And we came to the conclusion that we are not going to change the journal API. That would have consequences on compacting as the information would not outlive the transaction after compacting.

          As we talked, you can just use an addRecord.

          • 2. Re: storage of heuristically completed transactions
            jmesnil

            I have rewritten all this stuff by adding a HEURISTIC_COMPLETION record at the StorageManager level.

            I have one last doubt on the record ID. I need to identify the HEURISTIC_COMPLETION record to be able to delete it later when the TM will call forget() for the XID.
            When I store the HEURISTIC_COMPLETION, I use the long txID associated to the XID as the record's ID. Will it interfer somehow with all the records added *transactionally* to the transaction?
            Otherwise, I'll have to generate a long ID from a XID but if I could use directly the txID, it'd be simpler.

            wdyt?

            • 3. Re: storage of heuristically completed transactions
              timfox

               

              "jmesnil" wrote:
              I have rewritten all this stuff by adding a HEURISTIC_COMPLETION record at the StorageManager level.

              I have one last doubt on the record ID. I need to identify the HEURISTIC_COMPLETION record to be able to delete it later when the TM will call forget() for the XID.
              When I store the HEURISTIC_COMPLETION, I use the long txID associated to the XID as the record's ID. Will it interfer somehow with all the records added *transactionally* to the transaction?


              I'm not sure I really understand the question, but the record id is a unique identifier for the record and you need to use that to delete it later

              • 4. Re: storage of heuristically completed transactions
                timfox

                Ah maybe I understand the question now.

                The unique id you use must be generated from the method StorageManager::generateUniqueID to ensure it is unique.

                you can just "make up" an id and use that.