Author: adrianc
Date: Sat Sep 20 17:02:43 2014 New Revision: 1626480 URL: http://svn.apache.org/r1626480 Log: Backport recent SequenceUtil.java refactoring to the R13 branch. Modified: ofbiz/branches/release13.07/ (props changed) ofbiz/branches/release13.07/framework/common/config/general.properties ofbiz/branches/release13.07/framework/entity/src/org/ofbiz/entity/GenericDelegator.java ofbiz/branches/release13.07/framework/entity/src/org/ofbiz/entity/test/EntityTestSuite.java ofbiz/branches/release13.07/framework/entity/src/org/ofbiz/entity/util/SequenceUtil.java Propchange: ofbiz/branches/release13.07/ ------------------------------------------------------------------------------ Merged /ofbiz/trunk:r1621335,1621438,1621442,1621599,1622170 Modified: ofbiz/branches/release13.07/framework/common/config/general.properties URL: http://svn.apache.org/viewvc/ofbiz/branches/release13.07/framework/common/config/general.properties?rev=1626480&r1=1626479&r2=1626480&view=diff ============================================================================== --- ofbiz/branches/release13.07/framework/common/config/general.properties (original) +++ ofbiz/branches/release13.07/framework/common/config/general.properties Sat Sep 20 17:02:43 2014 @@ -130,7 +130,3 @@ mail.spam.value=YES # -- Y if you want to display the multi-tenant textbox in the login page and install specify components which related to each tenant multitenant=N - -# -- Y if you use a cluster. Most of the time this should not be needed. Setting distributed-cache-clear-enabled="true" is enough -# -- to guarantee no sequenceIds duplicates. See OFBIZ-2353 and look for "DCC" in wiki for details -clustered=N Modified: ofbiz/branches/release13.07/framework/entity/src/org/ofbiz/entity/GenericDelegator.java URL: http://svn.apache.org/viewvc/ofbiz/branches/release13.07/framework/entity/src/org/ofbiz/entity/GenericDelegator.java?rev=1626480&r1=1626479&r2=1626480&view=diff ============================================================================== --- ofbiz/branches/release13.07/framework/entity/src/org/ofbiz/entity/GenericDelegator.java (original) +++ ofbiz/branches/release13.07/framework/entity/src/org/ofbiz/entity/GenericDelegator.java Sat Sep 20 17:02:43 2014 @@ -44,6 +44,7 @@ import org.ofbiz.base.util.UtilFormatOut import org.ofbiz.base.util.UtilGenerics; import org.ofbiz.base.util.UtilMisc; import org.ofbiz.base.util.UtilObject; +import org.ofbiz.base.util.UtilProperties; import org.ofbiz.base.util.UtilValidate; import org.ofbiz.base.util.UtilXml; import org.ofbiz.entity.cache.Cache; @@ -68,7 +69,6 @@ import org.ofbiz.entity.model.ModelRelat import org.ofbiz.entity.model.ModelViewEntity; import org.ofbiz.entity.serialize.SerializeException; import org.ofbiz.entity.serialize.XmlSerializer; -import org.ofbiz.entity.transaction.GenericTransactionException; import org.ofbiz.entity.transaction.TransactionUtil; import org.ofbiz.entity.util.DistributedCacheClear; import org.ofbiz.entity.util.EntityCrypto; @@ -2337,18 +2337,13 @@ public class GenericDelegator implements * @see org.ofbiz.entity.Delegator#getNextSeqIdLong(java.lang.String, long) */ public Long getNextSeqIdLong(String seqName, long staggerMax) { - boolean beganTransaction = false; try { - if (alwaysUseTransaction) { - beganTransaction = TransactionUtil.begin(); - } - // FIXME: Replace DCL code with AtomicReference if (sequencer == null) { synchronized (this) { if (sequencer == null) { ModelEntity seqEntity = this.getModelEntity("SequenceValueItem"); - sequencer = new SequenceUtil(this, this.getEntityHelperInfo("SequenceValueItem"), seqEntity, "seqName", "seqId"); + sequencer = new SequenceUtil(this.getEntityHelperInfo("SequenceValueItem"), seqEntity, "seqName", "seqId"); } } } @@ -2357,16 +2352,10 @@ public class GenericDelegator implements ModelEntity seqModelEntity = this.getModelEntity(seqName); Long newSeqId = sequencer == null ? null : sequencer.getNextSeqId(seqName, staggerMax, seqModelEntity); - TransactionUtil.commit(beganTransaction); return newSeqId; } catch (Exception e) { String errMsg = "Failure in getNextSeqIdLong operation for seqName [" + seqName + "]: " + e.toString() + ". Rolling back transaction."; Debug.logError(e, errMsg, module); - try { - TransactionUtil.rollback(beganTransaction, errMsg, e); - } catch (GenericTransactionException e1) { - Debug.logError(e1, "Exception thrown while rolling back transaction: ", module); - } throw new GeneralRuntimeException(errMsg, e); } } Modified: ofbiz/branches/release13.07/framework/entity/src/org/ofbiz/entity/test/EntityTestSuite.java URL: http://svn.apache.org/viewvc/ofbiz/branches/release13.07/framework/entity/src/org/ofbiz/entity/test/EntityTestSuite.java?rev=1626480&r1=1626479&r2=1626480&view=diff ============================================================================== --- ofbiz/branches/release13.07/framework/entity/src/org/ofbiz/entity/test/EntityTestSuite.java (original) +++ ofbiz/branches/release13.07/framework/entity/src/org/ofbiz/entity/test/EntityTestSuite.java Sat Sep 20 17:02:43 2014 @@ -27,7 +27,14 @@ import java.util.ArrayList; import java.util.LinkedList; import java.util.List; import java.util.Map; +import java.util.UUID; +import java.util.concurrent.Callable; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentMap; +import java.util.concurrent.Future; +import java.util.concurrent.atomic.AtomicBoolean; +import org.ofbiz.base.concurrent.ExecutionPool; import org.ofbiz.base.util.Debug; import org.ofbiz.base.util.Observable; import org.ofbiz.base.util.Observer; @@ -44,8 +51,8 @@ import org.ofbiz.entity.condition.Entity import org.ofbiz.entity.condition.EntityConditionList; import org.ofbiz.entity.condition.EntityExpr; import org.ofbiz.entity.condition.EntityOperator; -import org.ofbiz.entity.config.model.Datasource; import org.ofbiz.entity.config.EntityConfigUtil; +import org.ofbiz.entity.config.model.Datasource; import org.ofbiz.entity.model.ModelEntity; import org.ofbiz.entity.model.ModelField; import org.ofbiz.entity.testtools.EntityTestCase; @@ -1085,6 +1092,62 @@ public class EntityTestSuite extends Ent assertNull("Delete TestingType 2", testType); } + public void testSequenceValueItem() { + SequenceUtil sequencer = new SequenceUtil(delegator.getGroupHelperInfo(delegator.getEntityGroupName("SequenceValueItem")), + delegator.getModelEntity("SequenceValueItem"), + "seqName", "seqId"); + UUID id = UUID.randomUUID(); + String sequenceName = "BogusSequence" + id.toString(); + for (int i = 10000; i <= 10015; i++) { + Long seqId = sequencer.getNextSeqId(sequenceName, 1, null); + assertEquals(seqId.longValue(), i); + } + sequencer.forceBankRefresh(sequenceName, 1); + Long seqId = sequencer.getNextSeqId(sequenceName, 1, null); + assertEquals(seqId.longValue(), 10020); + } + + public void testSequenceValueItemWithConcurrentThreads() { + final SequenceUtil sequencer = new SequenceUtil(delegator.getGroupHelperInfo(delegator.getEntityGroupName("SequenceValueItem")), + delegator.getModelEntity("SequenceValueItem"), + "seqName", "seqId"); + UUID id = UUID.randomUUID(); + final String sequenceName = "BogusSequence" + id.toString(); + final ConcurrentMap<Long, Long> seqIds = new ConcurrentHashMap<Long, Long>(); + final AtomicBoolean duplicateFound = new AtomicBoolean(false); + final AtomicBoolean nullSeqIdReturned = new AtomicBoolean(false); + + List<Future<Void>> futures = new ArrayList<Future<Void>>(); + Callable getSeqIdTask = new Callable() { + public Callable<Void> call() throws Exception { + Long seqId = sequencer.getNextSeqId(sequenceName, 1, null); + if (seqId == null) { + nullSeqIdReturned.set(true); + return null; + } + Long existingValue = seqIds.putIfAbsent(seqId, seqId); + if (existingValue != null) { + duplicateFound.set(true); + } + return null; + } + }; + Callable refreshTask = new Callable() { + public Callable<Void> call() throws Exception { + sequencer.forceBankRefresh(sequenceName, 1); + return null; + } + }; + double probabilityOfRefresh = 0.1; + for (int i = 1; i <= 1000; i++) { + Callable randomTask = Math.random() < probabilityOfRefresh ? refreshTask : getSeqIdTask; + futures.add(ExecutionPool.GLOBAL_EXECUTOR.submit(randomTask)); + } + ExecutionPool.getAllFutures(futures); + assertFalse("Null sequence id returned", nullSeqIdReturned.get()); + assertFalse("Duplicate sequence id returned", duplicateFound.get()); + } + private final class TestObserver implements Observer { private Observable observable; private Object arg; Modified: ofbiz/branches/release13.07/framework/entity/src/org/ofbiz/entity/util/SequenceUtil.java URL: http://svn.apache.org/viewvc/ofbiz/branches/release13.07/framework/entity/src/org/ofbiz/entity/util/SequenceUtil.java?rev=1626480&r1=1626479&r2=1626480&view=diff ============================================================================== --- ofbiz/branches/release13.07/framework/entity/src/org/ofbiz/entity/util/SequenceUtil.java (original) +++ ofbiz/branches/release13.07/framework/entity/src/org/ofbiz/entity/util/SequenceUtil.java Sat Sep 20 17:02:43 2014 @@ -22,14 +22,12 @@ import java.sql.Connection; import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Statement; -import java.util.Hashtable; -import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentMap; import javax.transaction.Transaction; import org.ofbiz.base.util.Debug; -import org.ofbiz.base.util.UtilProperties; -import org.ofbiz.entity.Delegator; import org.ofbiz.entity.GenericEntityException; import org.ofbiz.entity.datasource.GenericHelperInfo; import org.ofbiz.entity.jdbc.ConnectionFactory; @@ -40,20 +38,18 @@ import org.ofbiz.entity.transaction.Tran /** * Sequence Utility to get unique sequences from named sequence banks - * Uses a collision detection approach to safely get unique sequenced ids in banks from the database */ public class SequenceUtil { public static final String module = SequenceUtil.class.getName(); - private final Map<String, SequenceBank> sequences = new Hashtable<String, SequenceBank>(); + private final ConcurrentMap<String, SequenceBank> sequences = new ConcurrentHashMap<String, SequenceBank>(); private final GenericHelperInfo helperInfo; private final String tableName; private final String nameColName; private final String idColName; - private final boolean clustered; - public SequenceUtil(Delegator delegator, GenericHelperInfo helperInfo, ModelEntity seqEntity, String nameFieldName, String idFieldName) { + public SequenceUtil(GenericHelperInfo helperInfo, ModelEntity seqEntity, String nameFieldName, String idFieldName) { this.helperInfo = helperInfo; if (seqEntity == null) { throw new IllegalArgumentException("The sequence model entity was null but is required."); @@ -73,7 +69,6 @@ public class SequenceUtil { throw new IllegalArgumentException("Could not find the field definition for the sequence id field " + idFieldName); } this.idColName = idField.getColName(); - clustered = delegator.useDistributedCacheClear() || "Y".equals(UtilProperties.getPropertyValue("general.properties", "clustered")); } public Long getNextSeqId(String seqName, long staggerMax, ModelEntity seqModelEntity) { @@ -95,18 +90,14 @@ public class SequenceUtil { SequenceBank bank = sequences.get(seqName); if (bank == null) { - synchronized(this) { - bank = sequences.get(seqName); - if (bank == null) { - long bankSize = SequenceBank.defaultBankSize; - if (seqModelEntity != null && seqModelEntity.getSequenceBankSize() != null) { - bankSize = seqModelEntity.getSequenceBankSize().longValue(); - if (bankSize > SequenceBank.maxBankSize) bankSize = SequenceBank.maxBankSize; - } - bank = new SequenceBank(seqName, bankSize); - sequences.put(seqName, bank); - } + long bankSize = SequenceBank.defaultBankSize; + if (seqModelEntity != null && seqModelEntity.getSequenceBankSize() != null) { + bankSize = seqModelEntity.getSequenceBankSize().longValue(); + if (bankSize > SequenceBank.maxBankSize) bankSize = SequenceBank.maxBankSize; } + bank = new SequenceBank(seqName, bankSize); + SequenceBank bankFromCache = sequences.putIfAbsent(seqName, bank); + bank = bankFromCache != null ? bankFromCache : bank; } return bank; @@ -116,57 +107,67 @@ public class SequenceUtil { public static final long defaultBankSize = 10; public static final long maxBankSize = 5000; public static final long startSeqId = 10000; - public static final long minWaitMillis = 5; - public static final long maxWaitMillis = 50; - public static final int maxTries = 5; - private long curSeqId; - private long maxSeqId; private final String seqName; private final long bankSize; + private final String updateForLockStatement; + private final String selectSequenceStatement; + + private long curSeqId; + private long maxSeqId; private SequenceBank(String seqName, long bankSize) { this.seqName = seqName; curSeqId = 0; maxSeqId = 0; this.bankSize = bankSize; - fillBank(1); + updateForLockStatement = "UPDATE " + SequenceUtil.this.tableName + " SET " + SequenceUtil.this.idColName + "=" + SequenceUtil.this.idColName + " WHERE " + SequenceUtil.this.nameColName + "='" + this.seqName + "'"; + selectSequenceStatement = "SELECT " + SequenceUtil.this.idColName + " FROM " + SequenceUtil.this.tableName + " WHERE " + SequenceUtil.this.nameColName + "='" + this.seqName + "'"; } - private synchronized Long getNextSeqId(long staggerMax) { + private Long getNextSeqId(long staggerMax) { long stagger = 1; if (staggerMax > 1) { - stagger = Math.round(Math.random() * staggerMax); + stagger = (long)Math.ceil(Math.random() * staggerMax); if (stagger == 0) stagger = 1; } - - if ((curSeqId + stagger) <= maxSeqId) { - Long retSeqId = Long.valueOf(curSeqId); - curSeqId += stagger; - return retSeqId; - } else { - fillBank(stagger); + synchronized (this) { if ((curSeqId + stagger) <= maxSeqId) { - Long retSeqId = Long.valueOf(curSeqId); + long retSeqId = curSeqId; curSeqId += stagger; return retSeqId; } else { - Debug.logError("[SequenceUtil.SequenceBank.getNextSeqId] Fill bank failed, returning null", module); - return null; + fillBank(stagger); + if ((curSeqId + stagger) <= maxSeqId) { + long retSeqId = curSeqId; + curSeqId += stagger; + return retSeqId; + } else { + Debug.logError("Fill bank failed, returning null", module); + return null; + } } } } - private void refresh(long staggerMax) { + private synchronized void refresh(long staggerMax) { this.curSeqId = this.maxSeqId; this.fillBank(staggerMax); } - private synchronized void fillBank(long stagger) { - //Debug.logWarning("[SequenceUtil.SequenceBank.fillBank] Starting fillBank Thread Name is: " + Thread.currentThread().getName() + ":" + Thread.currentThread().toString(), module); - + /* + The algorithm to get the new sequence id in a thread safe way is the following: + 1 - run an update with no changes to get a lock on the record + 1bis - if no record is found, try to create and update it to get the lock + 2 - select the record (now locked) to get the curSeqId + 3 - increment the sequence + The three steps are executed in one dedicated database transaction. + */ + private void fillBank(long stagger) { // no need to get a new bank, SeqIds available - if ((curSeqId + stagger) <= maxSeqId) return; + if ((curSeqId + stagger) <= maxSeqId) { + return; + } long bankSize = this.bankSize; if (stagger > 1) { @@ -174,180 +175,122 @@ public class SequenceUtil { bankSize = stagger * defaultBankSize; } - if (bankSize > maxBankSize) bankSize = maxBankSize; - - long val1 = 0; - long val2 = 0; + if (bankSize > maxBankSize) { + bankSize = maxBankSize; + } - // NOTE: the fancy ethernet type stuff is for the case where transactions not available, or something funny happens with really sensitive timing (between first select and update, for example) - int numTries = 0; + Transaction suspendedTransaction = null; + try { + suspendedTransaction = TransactionUtil.suspend(); + + boolean beganTransaction = false; + try { + beganTransaction = TransactionUtil.begin(); + + Connection connection = null; + Statement stmt = null; + ResultSet rs = null; - while (val1 + bankSize != val2) { - if (Debug.verboseOn()) Debug.logVerbose("[SequenceUtil.SequenceBank.fillBank] Trying to get a bank of sequenced ids for " + - this.seqName + "; start of loop val1=" + val1 + ", val2=" + val2 + ", bankSize=" + bankSize, module); - - // not sure if this synchronized block is totally necessary, the method is synchronized but it does do a wait/sleep - // outside of this block, and this is the really sensitive block, so making sure it is isolated; there is some overhead - // to this, but very bad things can happen if we try to do too many of these at once for a single sequencer - synchronized (this) { - Transaction suspendedTransaction = null; try { - //if we can suspend the transaction, we'll try to do this in a local manual transaction - suspendedTransaction = TransactionUtil.suspend(); - - boolean beganTransaction = false; - try { - beganTransaction = TransactionUtil.begin(); - - Connection connection = null; - Statement stmt = null; - ResultSet rs = null; - - try { - connection = ConnectionFactory.getConnection(SequenceUtil.this.helperInfo); - } catch (SQLException sqle) { - Debug.logWarning("[SequenceUtil.SequenceBank.fillBank]: Unable to esablish a connection with the database... Error was:" + sqle.toString(), module); - throw sqle; - } catch (GenericEntityException e) { - Debug.logWarning("[SequenceUtil.SequenceBank.fillBank]: Unable to esablish a connection with the database... Error was: " + e.toString(), module); - throw e; - } - - if (connection == null) { - throw new GenericEntityException("[SequenceUtil.SequenceBank.fillBank]: Unable to esablish a connection with the database, connection was null..."); - } - - String sql = null; + connection = ConnectionFactory.getConnection(SequenceUtil.this.helperInfo); + } catch (SQLException sqle) { + Debug.logWarning("Unable to esablish a connection with the database. Error was:" + sqle.toString(), module); + throw sqle; + } catch (GenericEntityException e) { + Debug.logWarning("Unable to esablish a connection with the database. Error was: " + e.toString(), module); + throw e; + } + if (connection == null) { + throw new GenericEntityException("Unable to esablish a connection with the database, connection was null..."); + } + try { + stmt = connection.createStatement(); + String sql = null; + // 1 - run an update with no changes to get a lock on the record + if (stmt.executeUpdate(updateForLockStatement) <= 0) { + Debug.logWarning("Lock failed; no sequence row was found, will try to add a new one for sequence: " + seqName, module); + sql = "INSERT INTO " + SequenceUtil.this.tableName + " (" + SequenceUtil.this.nameColName + ", " + SequenceUtil.this.idColName + ") VALUES ('" + this.seqName + "', " + startSeqId + ")"; try { - // we shouldn't need this, and some TX managers complain about it, so not including it: connection.setAutoCommit(false); - - stmt = connection.createStatement(); - if (clustered) { - sql = "SELECT " + SequenceUtil.this.idColName + " FROM " + SequenceUtil.this.tableName + " WHERE " + SequenceUtil.this.nameColName + "='" + this.seqName + "'" + " FOR UPDATE"; - } else { - sql = "SELECT " + SequenceUtil.this.idColName + " FROM " + SequenceUtil.this.tableName + " WHERE " + SequenceUtil.this.nameColName + "='" + this.seqName + "'"; - } - rs = stmt.executeQuery(sql); - boolean gotVal1 = false; - if (rs.next()) { - val1 = rs.getLong(SequenceUtil.this.idColName); - gotVal1 = true; - } - rs.close(); - - if (!gotVal1) { - Debug.logWarning("[SequenceUtil.SequenceBank.fillBank] first select failed: will try to add new row, result set was empty for sequence [" + seqName + "] \nUsed SQL: " + sql + " \n Thread Name is: " + Thread.currentThread().getName() + ":" + Thread.currentThread().toString(), module); - sql = "INSERT INTO " + SequenceUtil.this.tableName + " (" + SequenceUtil.this.nameColName + ", " + SequenceUtil.this.idColName + ") VALUES ('" + this.seqName + "', " + startSeqId + ")"; - if (stmt.executeUpdate(sql) <= 0) { - throw new GenericEntityException("No rows changed when trying insert new sequence row with this SQL: " + sql); - } - continue; - } - - sql = "UPDATE " + SequenceUtil.this.tableName + " SET " + SequenceUtil.this.idColName + "=" + SequenceUtil.this.idColName + "+" + bankSize + " WHERE " + SequenceUtil.this.nameColName + "='" + this.seqName + "'"; - if (stmt.executeUpdate(sql) <= 0) { - throw new GenericEntityException("[SequenceUtil.SequenceBank.fillBank] update failed, no rows changes for seqName: " + seqName); - } - if (clustered) { - sql = "SELECT " + SequenceUtil.this.idColName + " FROM " + SequenceUtil.this.tableName + " WHERE " + SequenceUtil.this.nameColName + "='" + this.seqName + "'" + " FOR UPDATE"; - - } else { - sql = "SELECT " + SequenceUtil.this.idColName + " FROM " + SequenceUtil.this.tableName + " WHERE " + SequenceUtil.this.nameColName + "='" + this.seqName + "'"; - } - rs = stmt.executeQuery(sql); - boolean gotVal2 = false; - if (rs.next()) { - val2 = rs.getLong(SequenceUtil.this.idColName); - gotVal2 = true; - } - - rs.close(); - - if (!gotVal2) { - throw new GenericEntityException("[SequenceUtil.SequenceBank.fillBank] second select failed: aborting, result set was empty for sequence: " + seqName); - } - - // got val1 and val2 at this point, if we don't have the right difference between them, force a rollback (with - //setRollbackOnly and NOT with an exception because we don't want to break from the loop, just err out and - //continue), then flow out to allow the wait and loop thing to happen - if (val1 + bankSize != val2) { - TransactionUtil.setRollbackOnly("Forcing transaction rollback in sequence increment because we didn't get a clean update, ie a conflict was found, so not saving the results", null); - } + stmt.executeUpdate(sql); } catch (SQLException sqle) { - Debug.logWarning(sqle, "[SequenceUtil.SequenceBank.fillBank] SQL Exception while executing the following:\n" + sql + "\nError was:" + sqle.getMessage(), module); - throw sqle; - } finally { - try { - if (stmt != null) stmt.close(); - } catch (SQLException sqle) { - Debug.logWarning(sqle, "Error closing statement in sequence util", module); - } - try { - if (connection != null) connection.close(); - } catch (SQLException sqle) { - Debug.logWarning(sqle, "Error closing connection in sequence util", module); + // insert failed: this means that another thread inserted the record; then retry to run an update with no changes to get a lock on the record + if (stmt.executeUpdate(updateForLockStatement) <= 0) { + // This should never happen + throw new GenericEntityException("No rows changed when trying insert new sequence: " + seqName); } - } - } catch (Exception e) { - String errMsg = "General error in getting a sequenced ID"; - Debug.logError(e, errMsg, module); - try { - TransactionUtil.rollback(beganTransaction, errMsg, e); - } catch (GenericTransactionException gte2) { - Debug.logError(gte2, "Unable to rollback transaction", module); - } - // error, break out of the loop to not try to continue forever - break; - } finally { - try { - TransactionUtil.commit(beganTransaction); - } catch (GenericTransactionException gte) { - Debug.logError(gte, "Unable to commit sequence increment transaction, continuing anyway though", module); } } - } catch (GenericTransactionException e) { - Debug.logError(e, "System Error suspending transaction in sequence util", module); + // 2 - select the record (now locked) to get the curSeqId + rs = stmt.executeQuery(selectSequenceStatement); + boolean sequenceFound = rs.next(); + if (sequenceFound) { + curSeqId = rs.getLong(SequenceUtil.this.idColName); + } + rs.close(); + if (!sequenceFound) { + throw new GenericEntityException("Failed to find the sequence record for sequence: " + seqName); + } + // 3 - increment the sequence + sql = "UPDATE " + SequenceUtil.this.tableName + " SET " + SequenceUtil.this.idColName + "=" + SequenceUtil.this.idColName + "+" + bankSize + " WHERE " + SequenceUtil.this.nameColName + "='" + this.seqName + "'"; + if (stmt.executeUpdate(sql) <= 0) { + throw new GenericEntityException("Update failed, no rows changes for seqName: " + seqName); + } + + TransactionUtil.commit(beganTransaction); + + } catch (SQLException sqle) { + Debug.logWarning(sqle, "SQL Exception:" + sqle.getMessage(), module); + throw sqle; } finally { - if (suspendedTransaction != null) { - try { - TransactionUtil.resume(suspendedTransaction); - } catch (GenericTransactionException e) { - Debug.logError(e, "Error resuming suspended transaction in sequence util", module); - } + try { + if (stmt != null) stmt.close(); + } catch (SQLException sqle) { + Debug.logWarning(sqle, "Error closing statement in sequence util", module); + } + try { + if (connection != null) connection.close(); + } catch (SQLException sqle) { + Debug.logWarning(sqle, "Error closing connection in sequence util", module); } } - } - - if (val1 + bankSize != val2) { - if (numTries >= maxTries) { - String errMsg = "[SequenceUtil.SequenceBank.fillBank] maxTries (" + maxTries + ") reached for seqName [" + this.seqName + "], giving up."; - Debug.logError(errMsg, module); - return; + } catch (Exception e) { + // reset the sequence fields and return (note: it would be better to throw an exception) + curSeqId = 0; + maxSeqId = 0; + String errMsg = "General error in getting a sequenced ID"; + Debug.logError(e, errMsg, module); + try { + TransactionUtil.rollback(beganTransaction, errMsg, e); + } catch (GenericTransactionException gte2) { + Debug.logError(gte2, "Unable to rollback transaction", module); } - - // collision happened, wait a bounded random amount of time then continue - long waitTime = (long) (Math.random() * (maxWaitMillis - minWaitMillis) + minWaitMillis); - - Debug.logWarning("[SequenceUtil.SequenceBank.fillBank] Collision found for seqName [" + seqName + "], val1=" + val1 + ", val2=" + val2 + ", val1+bankSize=" + (val1 + bankSize) + ", bankSize=" + bankSize + ", waitTime=" + waitTime, module); - + return; + } + } catch (GenericTransactionException e) { + Debug.logError(e, "System Error suspending transaction in sequence util", module); + // reset the sequence fields and return (note: it would be better to throw an exception) + curSeqId = 0; + maxSeqId = 0; + return; + } finally { + if (suspendedTransaction != null) { try { - // using the Thread.sleep to more reliably lock this thread: this.wait(waitTime); - java.lang.Thread.sleep(waitTime); - } catch (Exception e) { - Debug.logWarning(e, "Error waiting in sequence util", module); + TransactionUtil.resume(suspendedTransaction); + } catch (GenericTransactionException e) { + Debug.logError(e, "Error resuming suspended transaction in sequence util", module); + // reset the sequence fields and return (note: it would be better to throw an exception) + curSeqId = 0; + maxSeqId = 0; return; } } - - numTries++; } - curSeqId = val1; - maxSeqId = val2; + maxSeqId = curSeqId + bankSize; if (Debug.infoOn()) Debug.logInfo("Got bank of sequenced IDs for [" + this.seqName + "]; curSeqId=" + curSeqId + ", maxSeqId=" + maxSeqId + ", bankSize=" + bankSize, module); - //Debug.logWarning("[SequenceUtil.SequenceBank.fillBank] Ending fillBank Thread Name is: " + Thread.currentThread().getName() + ":" + Thread.currentThread().toString(), module); } } } + |
Free forum by Nabble | Edit this page |