public Object getNextValue(Sequence sequence, AbstractSession writeSession) { String seqName = sequence.getName(); if (sequence.getPreallocationSize() > 1) { Queue sequencesForName = getPreallocationHandler().getPreallocated(seqName); // First try to get the next sequence value without locking. Object sequenceValue = sequencesForName.poll(); if (sequenceValue != null) { return sequenceValue; } // Sequences are empty, so must lock and allocate next batch of sequences. acquireLock(seqName); try { sequenceValue = sequencesForName.poll(); if (sequenceValue != null) { return sequenceValue; } Vector sequences = sequence.getGeneratedVector(null, writeSession); // Remove the first value before adding to the global cache to ensure this thread gets // one. sequenceValue = sequences.remove(0); // copy remaining values to global cache. getPreallocationHandler().setPreallocated(seqName, sequences); logDebugPreallocation(seqName, sequenceValue, sequences); } finally { releaseLock(seqName); } return sequenceValue; } else { // preallocation size is 1 - just return the first (and only) element of the allocated // vector. return sequence.getGeneratedVector(null, writeSession).firstElement(); } }
/** Release any locally allocated sequence back to the global sequence pool. */ void afterCommitInternal(Map localSequences, Accessor accessor) { Iterator it = localSequences.entrySet().iterator(); while (it.hasNext()) { Map.Entry entry = (Map.Entry) it.next(); String seqName = (String) entry.getKey(); Vector localSequenceForName = (Vector) entry.getValue(); if (!localSequenceForName.isEmpty()) { getPreallocationHandler().setPreallocated(seqName, localSequenceForName); // clear all localSequencesForName localSequenceForName.clear(); } } if (accessor != null) { getOwnerSession() .log( SessionLog.FINEST, SessionLog.SEQUENCING, "sequencing_afterTransactionCommitted", null, accessor); } else { getOwnerSession() .log( SessionLog.FINEST, SessionLog.SEQUENCING, "sequencing_afterTransactionCommitted", null); } }
protected void logDebugPreallocation( String seqName, Object firstSequenceValue, Vector sequences) { if (getOwnerSession().shouldLog(SessionLog.FINEST, SessionLog.SEQUENCING)) { // the first value has been already removed from sequences vector Object[] args = { seqName, new Integer(sequences.size() + 1), firstSequenceValue, sequences.lastElement() }; getOwnerSession() .log(SessionLog.FINEST, SessionLog.SEQUENCING, "sequencing_preallocation", args); } }
protected void logDebugLocalPreallocation( AbstractSession writeSession, String seqName, Vector sequences, Accessor accessor) { if (writeSession.shouldLog(SessionLog.FINEST, SessionLog.SEQUENCING)) { Object[] args = { seqName, new Integer(sequences.size()), sequences.firstElement(), sequences.lastElement() }; writeSession.log( SessionLog.FINEST, SessionLog.SEQUENCING, "sequencing_localPreallocation", args, accessor); } }
protected void initializeStates() { states = new State[NUMBER_OF_STATES]; Iterator itConnectedSequences = connectedSequences.iterator(); while (itConnectedSequences.hasNext()) { Sequence sequence = (Sequence) itConnectedSequences.next(); State state = getState(sequence.shouldUsePreallocation(), sequence.shouldUseTransaction()); if (state == null) { createState(sequence.shouldUsePreallocation(), sequence.shouldUseTransaction()); } } }
protected void logDebugSequencingConnected() { Vector[] sequenceVectors = new Vector[NUMBER_OF_STATES]; Iterator itConnectedSequences = connectedSequences.iterator(); while (itConnectedSequences.hasNext()) { Sequence sequence = (Sequence) itConnectedSequences.next(); int stateId = getStateId(sequence.shouldUsePreallocation(), sequence.shouldUseTransaction()); Vector v = sequenceVectors[stateId]; if (v == null) { v = new Vector(); sequenceVectors[stateId] = v; } v.addElement(sequence); } for (int i = 0; i < NUMBER_OF_STATES; i++) { Vector v = sequenceVectors[i]; if (v != null) { getOwnerSession() .log(SessionLog.FINEST, SessionLog.SEQUENCING, "sequencing_connected", states[i]); for (int j = 0; j < v.size(); j++) { Sequence sequence = (Sequence) v.elementAt(j); Object[] args = { sequence.getName(), Integer.toString(sequence.getPreallocationSize()), Integer.toString(sequence.getInitialValue()) }; getOwnerSession() .log(SessionLog.FINEST, SessionLog.SEQUENCING, "sequence_without_state", args); } } } }
protected void onDisconnectAllSequences() { RuntimeException exception = null; // defaultSequence has to disconnect the last for (int i = connectedSequences.size() - 1; i >= 0; i--) { try { Sequence sequenceToDisconnect = (Sequence) connectedSequences.elementAt(i); sequenceToDisconnect.onDisconnect(getOwnerSession().getDatasourcePlatform()); } catch (RuntimeException ex) { if (exception == null) { exception = ex; } } } connectedSequences = null; whenShouldAcquireValueForAll = UNDEFINED; atLeastOneSequenceShouldUseTransaction = false; atLeastOneSequenceShouldUsePreallocation = false; if (exception != null) { throw exception; } }
protected void onConnectAllSequences() { connectedSequences = new Vector(); boolean shouldUseTransaction = false; boolean shouldUsePreallocation = false; boolean shouldAcquireValueAfterInsert = false; Iterator descriptors = getOwnerSession().getDescriptors().values().iterator(); while (descriptors.hasNext()) { ClassDescriptor descriptor = (ClassDescriptor) descriptors.next(); // Find root sequence, because inheritance needs to be resolved here. // TODO: The way we initialize sequencing needs to be in line with descriptor init. ClassDescriptor parentDescriptor = descriptor; while (!parentDescriptor.usesSequenceNumbers() && parentDescriptor.isChildDescriptor()) { ClassDescriptor newDescriptor = getOwnerSession() .getDescriptor(parentDescriptor.getInheritancePolicy().getParentClass()); // Avoid issue with error cases of self parent, or null parent. if ((newDescriptor == null) || (newDescriptor == parentDescriptor)) { break; } parentDescriptor = newDescriptor; } if (!parentDescriptor.usesSequenceNumbers()) { continue; } String seqName = parentDescriptor.getSequenceNumberName(); Sequence sequence = getSequence(seqName); if (sequence == null) { sequence = new DefaultSequence(seqName); getOwnerSession().getDatasourcePlatform().addSequence(sequence); } // PERF: Initialize the sequence, this avoid having to look it up every time. descriptor.setSequence(sequence); if (connectedSequences.contains(sequence)) { continue; } try { if (sequence instanceof DefaultSequence && !connectedSequences.contains(getDefaultSequence())) { getDefaultSequence().onConnect(getOwnerSession().getDatasourcePlatform()); connectedSequences.add(0, getDefaultSequence()); shouldUseTransaction |= getDefaultSequence().shouldUseTransaction(); shouldUsePreallocation |= getDefaultSequence().shouldUsePreallocation(); shouldAcquireValueAfterInsert |= getDefaultSequence().shouldAcquireValueAfterInsert(); } sequence.onConnect(getOwnerSession().getDatasourcePlatform()); connectedSequences.addElement(sequence); shouldUseTransaction |= sequence.shouldUseTransaction(); shouldUsePreallocation |= sequence.shouldUsePreallocation(); shouldAcquireValueAfterInsert |= sequence.shouldAcquireValueAfterInsert(); } catch (RuntimeException ex) { // defaultSequence has to disconnect the last for (int i = connectedSequences.size() - 1; i >= 0; i--) { try { Sequence sequenceToDisconnect = (Sequence) connectedSequences.elementAt(i); sequenceToDisconnect.onDisconnect(getOwnerSession().getDatasourcePlatform()); } catch (RuntimeException ex2) { // ignore } } connectedSequences = null; throw ex; } } if (shouldAcquireValueAfterInsert && !shouldUsePreallocation) { whenShouldAcquireValueForAll = AFTER_INSERT; } else if (!shouldAcquireValueAfterInsert && shouldUsePreallocation) { whenShouldAcquireValueForAll = BEFORE_INSERT; } atLeastOneSequenceShouldUseTransaction = shouldUseTransaction; atLeastOneSequenceShouldUsePreallocation = shouldUsePreallocation; }
public Object getNextValue(Sequence sequence, AbstractSession writeSession) { String seqName = sequence.getName(); if (sequence.getPreallocationSize() > 1) { Queue sequencesForName = getPreallocationHandler().getPreallocated(seqName); // First try to get the next sequence value without locking. Object sequenceValue = sequencesForName.poll(); if (sequenceValue != null) { return sequenceValue; } // Sequences are empty, so must lock and allocate next batch of sequences. acquireLock(seqName); try { sequenceValue = sequencesForName.poll(); if (sequenceValue != null) { return sequenceValue; } // note that accessor.getLogin().shouldUseExternalTransactionController() // should be set to false Accessor accessor = getConnectionHandler().acquireAccessor(); try { accessor.beginTransaction(writeSession); try { Vector sequences = sequence.getGeneratedVector(accessor, writeSession); accessor.commitTransaction(writeSession); // Remove the first value before adding to the global cache to ensure this thread gets // one. sequenceValue = sequences.remove(0); // copy remaining values to global cache. getPreallocationHandler().setPreallocated(seqName, sequences); logDebugPreallocation(seqName, sequenceValue, sequences); } catch (RuntimeException ex) { try { // make sure to rollback the transaction we've begun accessor.rollbackTransaction(writeSession); } catch (Exception rollbackException) { // ignore rollback exception } // don't eat the original exception throw ex; } } finally { getConnectionHandler().releaseAccessor(accessor); } } finally { releaseLock(seqName); } return sequenceValue; } else { // note that accessor.getLogin().shouldUseExternalTransactionController() // should be set to false Accessor accessor = getConnectionHandler().acquireAccessor(); try { accessor.beginTransaction(writeSession); try { // preallocation size is 1 - just return the first (and only) element of the allocated // vector. Object sequenceValue = sequence.getGeneratedVector(accessor, writeSession).firstElement(); accessor.commitTransaction(writeSession); return sequenceValue; } catch (RuntimeException ex) { try { // make sure to rollback the transaction we've begun accessor.rollbackTransaction(writeSession); } catch (Exception rollbackException) { // ignore rollback exception } // don't eat the original exception throw ex; } } finally { getConnectionHandler().releaseAccessor(accessor); } } }
/** * Return the next sequence value. First check the global pool, if empty then allocate new * sequences locally. */ public Object getNextValue(Sequence sequence, AbstractSession writeSession) { String seqName = sequence.getName(); if (sequence.getPreallocationSize() > 1) { Queue sequencesForName = getPreallocationHandler().getPreallocated(seqName); // First grab the first sequence value without locking, a lock is only required if empty. Object sequenceValue = sequencesForName.poll(); if (sequenceValue != null) { return sequenceValue; } // KeepLocked indicates whether the sequence lock should be kept for the whole duration of // this method. // Of course the lock should be released in any case when the method returns or throws an // exception. // This is only used if a sequence transaction was begun by the unit of work, // and will be committed before the unit of work commit. boolean keepLocked = false; if (!getOwnerSession().getDatasourceLogin().shouldUseExternalTransactionController() && !writeSession.isInTransaction()) { // To prevent several threads from simultaneously allocating a separate bunch of // sequencing numbers each. With keepLocked==true the first thread locks out others // until it copies the obtained sequence numbers to the global storage. // Note that this optimization possible only in non-jts case when there is no transaction. acquireLock(seqName); try { sequenceValue = sequencesForName.poll(); if (sequenceValue != null) { return sequenceValue; } writeSession.beginTransaction(); // write accessor is set in begin keepLocked = true; } finally { if (!keepLocked) { releaseLock(seqName); } } } Accessor accessor; Vector localSequencesForName; if (!keepLocked) { writeSession.beginTransaction(); // write accessor is set in begin } try { accessor = writeSession.getAccessor(); SequencingCallbackImpl seqCallbackImpl = getCallbackImpl(writeSession, accessor); Map localSequences = seqCallbackImpl.getPreallocatedSequenceValues(); localSequencesForName = (Vector) localSequences.get(seqName); if ((localSequencesForName == null) || localSequencesForName.isEmpty()) { localSequencesForName = sequence.getGeneratedVector(null, writeSession); localSequences.put(seqName, localSequencesForName); logDebugLocalPreallocation(writeSession, seqName, localSequencesForName, accessor); } } catch (RuntimeException ex) { if (keepLocked) { releaseLock(seqName); } try { // make sure to rollback the transaction we've begun writeSession.rollbackTransaction(); } catch (Exception rollbackException) { // ignore rollback exception } // don't eat the original exception throw ex; } try { try { // commitTransaction may copy preallocated sequence numbers // from localSequences to preallocationHandler: that happens // if it isn't a nested transaction, and sequencingCallback.afterCommit // method has been called. // In this case: // 1. localSequences corresponding to the accessor // has been removed from accessorToPreallocated; // 2. All its members are empty (therefore localSequenceForName is empty). writeSession.commitTransaction(); } catch (DatabaseException ex) { try { // make sure to rollback the transaction we've begun writeSession.rollbackTransaction(); } catch (Exception rollbackException) { // ignore rollback exception } // don't eat the original exception throw ex; } if (!localSequencesForName.isEmpty()) { // localSeqencesForName is not empty, that means // sequencingCallback has not been called. sequenceValue = localSequencesForName.remove(0); return sequenceValue; } else { // localSeqencesForName is empty, that means // sequencingCallback has been called. sequenceValue = sequencesForName.poll(); if (sequenceValue != null) { return sequenceValue; } return getNextValue(sequence, writeSession); } } finally { if (keepLocked) { releaseLock(seqName); } } } else { writeSession.beginTransaction(); try { // preallocation size is 1 - just return the first (and only) element of the allocated // vector. Object sequenceValue = sequence.getGeneratedVector(null, writeSession).firstElement(); writeSession.commitTransaction(); return sequenceValue; } catch (RuntimeException ex) { try { // make sure to rollback the transaction we've begun writeSession.rollbackTransaction(); } catch (Exception rollbackException) { // ignore rollback exception } // don't eat the original exception throw ex; } } }