You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@ofbiz.apache.org by ad...@apache.org on 2014/09/20 19:02:43 UTC

svn commit: r1626480 - in /ofbiz/branches/release13.07: ./ framework/common/config/ framework/entity/src/org/ofbiz/entity/ framework/entity/src/org/ofbiz/entity/test/ framework/entity/src/org/ofbiz/entity/util/

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);
         }
     }
 }
+