diff --git a/foundation/org.eclipse.persistence.core/src/main/java/org/eclipse/persistence/internal/helper/ConcurrencyManager.java b/foundation/org.eclipse.persistence.core/src/main/java/org/eclipse/persistence/internal/helper/ConcurrencyManager.java index 44d641741a5..18fdf3f63c0 100644 --- a/foundation/org.eclipse.persistence.core/src/main/java/org/eclipse/persistence/internal/helper/ConcurrencyManager.java +++ b/foundation/org.eclipse.persistence.core/src/main/java/org/eclipse/persistence/internal/helper/ConcurrencyManager.java @@ -37,8 +37,12 @@ import java.util.Set; import java.util.Vector; import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicLong; +import java.util.concurrent.locks.Condition; +import java.util.concurrent.locks.Lock; +import java.util.concurrent.locks.ReentrantLock; /** * INTERNAL: @@ -83,6 +87,9 @@ public class ConcurrencyManager implements Serializable { // was set to 0. It should happen if an entity being shared by two threads. private final AtomicLong totalNumberOfKeysReleasedForReadingBlewUpExceptionDueToCacheKeyHavingReachedCounterZero = new AtomicLong(0); + private final Lock instanceLock = new ReentrantLock(); + private final Condition instanceLockCondition = instanceLock.newCondition(); + private static final Map THREADS_TO_WAIT_ON_ACQUIRE_READ_LOCK = new ConcurrentHashMap<>(); private static final Map THREADS_TO_WAIT_ON_ACQUIRE_READ_LOCK_NAME_OF_METHOD_CREATING_TRACE = new ConcurrentHashMap<>(); private static final Map THREADS_TO_WAIT_ON_ACQUIRE = new ConcurrentHashMap<>(); @@ -123,55 +130,60 @@ public void acquire() throws ConcurrencyException { * This should be called before entering a critical section. * called with true from the merge process, if true then the refresh will not refresh the object */ - public synchronized void acquire(boolean forMerge) throws ConcurrencyException { - //Flag the time when we start the while loop - final long whileStartTimeMillis = System.currentTimeMillis(); - Thread currentThread = Thread.currentThread(); - DeferredLockManager lockManager = getDeferredLockManager(currentThread); - ReadLockManager readLockManager = getReadLockManager(currentThread); - - // Waiting to acquire cache key will now start on the while loop - // NOTE: this step bares no influence in acquiring or not acquiring locks - // is just storing debug metadata that we can use when we detect the system is frozen in a dead lock - final boolean currentThreadWillEnterTheWhileWait = ((this.activeThread != null) || (this.numberOfReaders.get() > 0)) && (this.activeThread != currentThread); - if(currentThreadWillEnterTheWhileWait) { - putThreadAsWaitingToAcquireLockForWriting(currentThread, ACQUIRE_METHOD_NAME); - } - while (((this.activeThread != null) || (this.numberOfReaders.get() > 0)) && (this.activeThread != Thread.currentThread())) { - // This must be in a while as multiple threads may be released, or another thread may rush the acquire after one is released. - try { - this.numberOfWritersWaiting.incrementAndGet(); - wait(ConcurrencyUtil.SINGLETON.getAcquireWaitTime()); - // Run a method that will fire up an exception if we having been sleeping for too long - ConcurrencyUtil.SINGLETON.determineIfReleaseDeferredLockAppearsToBeDeadLocked(this, whileStartTimeMillis, lockManager, readLockManager, ConcurrencyUtil.SINGLETON.isAllowInterruptedExceptionFired()); - } catch (InterruptedException exception) { - // If the thread is interrupted we want to make sure we release all of the locks the thread was owning - releaseAllLocksAcquiredByThread(lockManager); - // Improve concurrency manager metadata - // Waiting to acquire cache key is is over - if (currentThreadWillEnterTheWhileWait) { - removeThreadNoLongerWaitingToAcquireLockForWriting(currentThread); + public void acquire(boolean forMerge) throws ConcurrencyException { + instanceLock.lock(); + try { + //Flag the time when we start the while loop + final long whileStartTimeMillis = System.currentTimeMillis(); + Thread currentThread = Thread.currentThread(); + DeferredLockManager lockManager = getDeferredLockManager(currentThread); + ReadLockManager readLockManager = getReadLockManager(currentThread); + + // Waiting to acquire cache key will now start on the while loop + // NOTE: this step bares no influence in acquiring or not acquiring locks + // is just storing debug metadata that we can use when we detect the system is frozen in a dead lock + final boolean currentThreadWillEnterTheWhileWait = ((this.activeThread != null) || (this.numberOfReaders.get() > 0)) && (this.activeThread != currentThread); + if (currentThreadWillEnterTheWhileWait) { + putThreadAsWaitingToAcquireLockForWriting(currentThread, ACQUIRE_METHOD_NAME); + } + while (((this.activeThread != null) || (this.numberOfReaders.get() > 0)) && (this.activeThread != Thread.currentThread())) { + // This must be in a while as multiple threads may be released, or another thread may rush the acquire after one is released. + try { + this.numberOfWritersWaiting.incrementAndGet(); + instanceLockCondition.await(ConcurrencyUtil.SINGLETON.getAcquireWaitTime(), TimeUnit.MILLISECONDS); + // Run a method that will fire up an exception if we having been sleeping for too long + ConcurrencyUtil.SINGLETON.determineIfReleaseDeferredLockAppearsToBeDeadLocked(this, whileStartTimeMillis, lockManager, readLockManager, ConcurrencyUtil.SINGLETON.isAllowInterruptedExceptionFired()); + } catch (InterruptedException exception) { + // If the thread is interrupted we want to make sure we release all of the locks the thread was owning + releaseAllLocksAcquiredByThread(lockManager); + // Improve concurrency manager metadata + // Waiting to acquire cache key is is over + if (currentThreadWillEnterTheWhileWait) { + removeThreadNoLongerWaitingToAcquireLockForWriting(currentThread); + } + throw ConcurrencyException.waitWasInterrupted(exception.getMessage()); + } finally { + // Since above we increments the number of writers + // whether or not the thread is exploded by an interrupt + // we need to make sure we decrement the number of writer to not allow the code to be corrupted + this.numberOfWritersWaiting.decrementAndGet(); } - throw ConcurrencyException.waitWasInterrupted(exception.getMessage()); - } finally { - // Since above we increments the number of writers - // whether or not the thread is exploded by an interrupt - // we need to make sure we decrement the number of writer to not allow the code to be corrupted - this.numberOfWritersWaiting.decrementAndGet(); + } // end of while loop + // Waiting to acquire cahe key is is over + if (currentThreadWillEnterTheWhileWait) { + removeThreadNoLongerWaitingToAcquireLockForWriting(currentThread); } - } // end of while loop - // Waiting to acquire cahe key is is over - if(currentThreadWillEnterTheWhileWait) { - removeThreadNoLongerWaitingToAcquireLockForWriting(currentThread); - } - if (this.activeThread == null) { - this.activeThread = Thread.currentThread(); - if (shouldTrackStack){ - this.stack = new Exception(); + if (this.activeThread == null) { + this.activeThread = Thread.currentThread(); + if (shouldTrackStack) { + this.stack = new Exception(); + } } + this.lockedByMergeManager = forMerge; + this.depth.incrementAndGet(); + } finally { + instanceLock.unlock(); } - this.lockedByMergeManager = forMerge; - this.depth.incrementAndGet(); } /** @@ -189,13 +201,18 @@ public boolean acquireNoWait() throws ConcurrencyException { * Added for CR 2317 * called with true from the merge process, if true then the refresh will not refresh the object */ - public synchronized boolean acquireNoWait(boolean forMerge) throws ConcurrencyException { - if ((this.activeThread == null && this.numberOfReaders.get() == 0) || (this.activeThread == Thread.currentThread())) { - //if I own the lock increment depth - acquire(forMerge); - return true; - } else { - return false; + public boolean acquireNoWait(boolean forMerge) throws ConcurrencyException { + instanceLock.lock(); + try { + if ((this.activeThread == null && this.numberOfReaders.get() == 0) || (this.activeThread == Thread.currentThread())) { + //if I own the lock increment depth + acquire(forMerge); + return true; + } else { + return false; + } + } finally { + instanceLock.unlock(); } } @@ -205,27 +222,32 @@ public synchronized boolean acquireNoWait(boolean forMerge) throws ConcurrencyEx * Added for CR 2317 * called with true from the merge process, if true then the refresh will not refresh the object */ - public synchronized boolean acquireWithWait(boolean forMerge, int wait) throws ConcurrencyException { - final Thread currentThread = Thread.currentThread(); - if ((this.activeThread == null && this.numberOfReaders.get() == 0) || (this.activeThread == currentThread)) { - // if I own the lock increment depth - acquire(forMerge); - return true; - } else { - try { - putThreadAsWaitingToAcquireLockForWriting(currentThread, ACQUIRE_WITH_WAIT_METHOD_NAME); - wait(wait); - } catch (InterruptedException e) { - return false; - } finally { - removeThreadNoLongerWaitingToAcquireLockForWriting(currentThread); - } - if ((this.activeThread == null && this.numberOfReaders.get() == 0) - || (this.activeThread == currentThread)) { + public boolean acquireWithWait(boolean forMerge, int wait) throws ConcurrencyException { + instanceLock.lock(); + try { + final Thread currentThread = Thread.currentThread(); + if ((this.activeThread == null && this.numberOfReaders.get() == 0) || (this.activeThread == currentThread)) { + // if I own the lock increment depth acquire(forMerge); return true; + } else { + try { + putThreadAsWaitingToAcquireLockForWriting(currentThread, ACQUIRE_WITH_WAIT_METHOD_NAME); + instanceLockCondition.await(wait, TimeUnit.MILLISECONDS); + } catch (InterruptedException e) { + return false; + } finally { + removeThreadNoLongerWaitingToAcquireLockForWriting(currentThread); + } + if ((this.activeThread == null && this.numberOfReaders.get() == 0) + || (this.activeThread == currentThread)) { + acquire(forMerge); + return true; + } + return false; } - return false; + } finally { + instanceLock.unlock(); } } @@ -235,14 +257,19 @@ public synchronized boolean acquireWithWait(boolean forMerge, int wait) throws C * Added for Bug 5840635 * Call with true from the merge process, if true then the refresh will not refresh the object. */ - public synchronized boolean acquireIfUnownedNoWait(boolean forMerge) throws ConcurrencyException { - // Only acquire lock if active thread is null. Do not check current thread. - if (this.activeThread == null && this.numberOfReaders.get() == 0) { - // if lock is unowned increment depth - acquire(forMerge); - return true; - } else { - return false; + public boolean acquireIfUnownedNoWait(boolean forMerge) throws ConcurrencyException { + instanceLock.lock(); + try { + // Only acquire lock if active thread is null. Do not check current thread. + if (this.activeThread == null && this.numberOfReaders.get() == 0) { + // if lock is unowned increment depth + acquire(forMerge); + return true; + } else { + return false; + } + } finally { + instanceLock.unlock(); } } @@ -258,7 +285,8 @@ public void acquireDeferredLock() throws ConcurrencyException { putDeferredLock(currentThread, lockManager); } lockManager.incrementDepth(); - synchronized (this) { + instanceLock.lock(); + try { final long whileStartTimeMillis = System.currentTimeMillis(); final boolean currentThreadWillEnterTheWhileWait = this.numberOfReaders.get() != 0; if(currentThreadWillEnterTheWhileWait) { @@ -273,7 +301,7 @@ public void acquireDeferredLock() throws ConcurrencyException { //the object is not being built. try { this.numberOfWritersWaiting.incrementAndGet(); - wait(ConcurrencyUtil.SINGLETON.getAcquireWaitTime()); + instanceLockCondition.await(ConcurrencyUtil.SINGLETON.getAcquireWaitTime(), TimeUnit.MILLISECONDS); ConcurrencyUtil.SINGLETON.determineIfReleaseDeferredLockAppearsToBeDeadLocked(this, whileStartTimeMillis, lockManager, readLockManager, ConcurrencyUtil.SINGLETON.isAllowInterruptedExceptionFired()); } catch (InterruptedException exception) { // If the thread is interrupted we want to make sure we release all of the locks the thread was owning @@ -298,6 +326,8 @@ public void acquireDeferredLock() throws ConcurrencyException { AbstractSessionLog.getLog().log(SessionLog.FINER, SessionLog.CACHE, "acquiring_deferred_lock", ((CacheKey)this).getObject(), currentThread.getName()); } } + } finally { + instanceLock.unlock(); } } @@ -331,48 +361,58 @@ public void checkReadLock() throws ConcurrencyException { * Wait on any writer. * Allow concurrent reads. */ - public synchronized void acquireReadLock() throws ConcurrencyException { - final Thread currentThread = Thread.currentThread(); - final long whileStartTimeMillis = System.currentTimeMillis(); - DeferredLockManager lockManager = getDeferredLockManager(currentThread); - ReadLockManager readLockManager = getReadLockManager(currentThread); - final boolean currentThreadWillEnterTheWhileWait = (this.activeThread != null) && (this.activeThread != currentThread); - if (currentThreadWillEnterTheWhileWait) { - putThreadAsWaitingToAcquireLockForReading(currentThread, ACQUIRE_READ_LOCK_METHOD_NAME); - } - // Cannot check for starving writers as will lead to deadlocks. - while ((this.activeThread != null) && (this.activeThread != Thread.currentThread())) { - try { - wait(ConcurrencyUtil.SINGLETON.getAcquireWaitTime()); - ConcurrencyUtil.SINGLETON.determineIfReleaseDeferredLockAppearsToBeDeadLocked(this, whileStartTimeMillis, lockManager, readLockManager, ConcurrencyUtil.SINGLETON.isAllowInterruptedExceptionFired()); - } catch (InterruptedException exception) { - releaseAllLocksAcquiredByThread(lockManager); - if (currentThreadWillEnterTheWhileWait) { - removeThreadNoLongerWaitingToAcquireLockForReading(currentThread); + public void acquireReadLock() throws ConcurrencyException { + instanceLock.lock(); + try { + final Thread currentThread = Thread.currentThread(); + final long whileStartTimeMillis = System.currentTimeMillis(); + DeferredLockManager lockManager = getDeferredLockManager(currentThread); + ReadLockManager readLockManager = getReadLockManager(currentThread); + final boolean currentThreadWillEnterTheWhileWait = (this.activeThread != null) && (this.activeThread != currentThread); + if (currentThreadWillEnterTheWhileWait) { + putThreadAsWaitingToAcquireLockForReading(currentThread, ACQUIRE_READ_LOCK_METHOD_NAME); + } + // Cannot check for starving writers as will lead to deadlocks. + while ((this.activeThread != null) && (this.activeThread != Thread.currentThread())) { + try { + instanceLockCondition.await(ConcurrencyUtil.SINGLETON.getAcquireWaitTime(), TimeUnit.MILLISECONDS); + ConcurrencyUtil.SINGLETON.determineIfReleaseDeferredLockAppearsToBeDeadLocked(this, whileStartTimeMillis, lockManager, readLockManager, ConcurrencyUtil.SINGLETON.isAllowInterruptedExceptionFired()); + } catch (InterruptedException exception) { + releaseAllLocksAcquiredByThread(lockManager); + if (currentThreadWillEnterTheWhileWait) { + removeThreadNoLongerWaitingToAcquireLockForReading(currentThread); + } + throw ConcurrencyException.waitWasInterrupted(exception.getMessage()); } - throw ConcurrencyException.waitWasInterrupted(exception.getMessage()); } - } - if (currentThreadWillEnterTheWhileWait) { - removeThreadNoLongerWaitingToAcquireLockForReading(currentThread); - } - try { - addReadLockToReadLockManager(); + if (currentThreadWillEnterTheWhileWait) { + removeThreadNoLongerWaitingToAcquireLockForReading(currentThread); + } + try { + addReadLockToReadLockManager(); + } finally { + this.numberOfReaders.incrementAndGet(); + this.totalNumberOfKeysAcquiredForReading.incrementAndGet(); + } } finally { - this.numberOfReaders.incrementAndGet(); - this.totalNumberOfKeysAcquiredForReading.incrementAndGet(); + instanceLock.unlock(); } } /** * If this is acquired return false otherwise acquire readlock and return true */ - public synchronized boolean acquireReadLockNoWait() { - if ((this.activeThread == null) || (this.activeThread == Thread.currentThread())) { - acquireReadLock(); - return true; - } else { - return false; + public boolean acquireReadLockNoWait() { + instanceLock.lock(); + try { + if ((this.activeThread == null) || (this.activeThread == Thread.currentThread())) { + acquireReadLock(); + return true; + } else { + return false; + } + } finally { + instanceLock.unlock(); } } @@ -587,19 +627,24 @@ public void putDeferredLock(Thread thread, DeferredLockManager lockManager) { * The notify will release the first thread waiting on the object, * if no threads are waiting it will do nothing. */ - public synchronized void release() throws ConcurrencyException { - if (this.depth.get() == 0) { - throw ConcurrencyException.signalAttemptedBeforeWait(); - } else { - this.depth.decrementAndGet(); - } - if (this.depth.get() == 0) { - this.activeThread = null; - if (shouldTrackStack){ - this.stack = null; + public void release() throws ConcurrencyException { + instanceLock.lock(); + try { + if (this.depth.get() == 0) { + throw ConcurrencyException.signalAttemptedBeforeWait(); + } else { + this.depth.decrementAndGet(); } - this.lockedByMergeManager = false; - notifyAll(); + if (this.depth.get() == 0) { + this.activeThread = null; + if (shouldTrackStack) { + this.stack = null; + } + this.lockedByMergeManager = false; + instanceLockCondition.signalAll(); + } + } finally { + instanceLock.unlock(); } } @@ -691,25 +736,30 @@ public void releaseDeferredLock() throws ConcurrencyException { /** * Decrement the number of readers. Used to allow concurrent reads. */ - public synchronized void releaseReadLock() throws ConcurrencyException { - if (this.numberOfReaders.get() == 0) { - this.totalNumberOfKeysReleasedForReadingBlewUpExceptionDueToCacheKeyHavingReachedCounterZero.incrementAndGet(); - try { - removeReadLockFromReadLockManager(); - } catch (Exception e) { - AbstractSessionLog.getLog().logThrowable(SessionLog.SEVERE, SessionLog.CACHE, e); + public void releaseReadLock() throws ConcurrencyException { + instanceLock.lock(); + try { + if (this.numberOfReaders.get() == 0) { + this.totalNumberOfKeysReleasedForReadingBlewUpExceptionDueToCacheKeyHavingReachedCounterZero.incrementAndGet(); + try { + removeReadLockFromReadLockManager(); + } catch (Exception e) { + AbstractSessionLog.getLog().logThrowable(SessionLog.SEVERE, SessionLog.CACHE, e); + } + throw ConcurrencyException.signalAttemptedBeforeWait(); + } else { + try { + removeReadLockFromReadLockManager(); + } finally { + this.numberOfReaders.decrementAndGet(); + this.totalNumberOfKeysReleasedForReading.incrementAndGet(); + } } - throw ConcurrencyException.signalAttemptedBeforeWait(); - } else { - try { - removeReadLockFromReadLockManager(); - } finally { - this.numberOfReaders.decrementAndGet(); - this.totalNumberOfKeysReleasedForReading.incrementAndGet(); + if (this.numberOfReaders.get() == 0) { + instanceLockCondition.signalAll(); } - } - if (this.numberOfReaders.get() == 0) { - notifyAll(); + } finally { + instanceLock.unlock(); } } @@ -758,15 +808,20 @@ protected void setNumberOfWritersWaiting(int numberOfWritersWaiting) { this.numberOfWritersWaiting.set(numberOfWritersWaiting); } - public synchronized void transitionToDeferredLock() { - Thread currentThread = Thread.currentThread(); - DeferredLockManager lockManager = getDeferredLockManager(currentThread); - if (lockManager == null) { - lockManager = new DeferredLockManager(); - putDeferredLock(currentThread, lockManager); + public void transitionToDeferredLock() { + instanceLock.lock(); + try { + Thread currentThread = Thread.currentThread(); + DeferredLockManager lockManager = getDeferredLockManager(currentThread); + if (lockManager == null) { + lockManager = new DeferredLockManager(); + putDeferredLock(currentThread, lockManager); + } + lockManager.incrementDepth(); + lockManager.addActiveLock(this); + } finally { + instanceLock.unlock(); } - lockManager.incrementDepth(); - lockManager.addActiveLock(this); } /** @@ -1046,4 +1101,12 @@ public static void clearJustificationWhyMethodIsBuildingObjectCompleteReturnsFal public static void setJustificationWhyMethodIsBuildingObjectCompleteReturnsFalse(String justification) { THREADS_WAITING_TO_RELEASE_DEFERRED_LOCKS_BUILD_OBJECT_COMPLETE_GOES_NOWHERE.put(Thread.currentThread(), justification); } + + public Lock getInstanceLock() { + return this.instanceLock; + } + + public Condition getInstanceLockCondition() { + return this.instanceLockCondition; + } } diff --git a/foundation/org.eclipse.persistence.core/src/main/java/org/eclipse/persistence/internal/helper/ReadLockManager.java b/foundation/org.eclipse.persistence.core/src/main/java/org/eclipse/persistence/internal/helper/ReadLockManager.java index a9c9a23b7da..292585c0d45 100644 --- a/foundation/org.eclipse.persistence.core/src/main/java/org/eclipse/persistence/internal/helper/ReadLockManager.java +++ b/foundation/org.eclipse.persistence.core/src/main/java/org/eclipse/persistence/internal/helper/ReadLockManager.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, 2023 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2020, 2024 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at @@ -20,6 +20,8 @@ import java.util.List; import java.util.Map; import java.util.Vector; +import java.util.concurrent.locks.Lock; +import java.util.concurrent.locks.ReentrantLock; public class ReadLockManager { @@ -45,21 +47,28 @@ public class ReadLockManager { */ private final List removeReadLockProblemsDetected = new ArrayList<>(); + private final Lock instanceLock = new ReentrantLock(); + /** * add a concurrency manager as deferred locks to the DLM */ - public synchronized void addReadLock(ConcurrencyManager concurrencyManager) { - final Thread currentThread = Thread.currentThread(); - final long currentThreadId = currentThread.getId(); - ReadLockAcquisitionMetadata readLockAcquisitionMetadata = ConcurrencyUtil.SINGLETON.createReadLockAcquisitionMetadata(concurrencyManager); - - this.readLocks.add(FIRST_INDEX_OF_COLLECTION, concurrencyManager); - if(!mapThreadToReadLockAcquisitionMetadata.containsKey(currentThreadId)) { - List newList = Collections.synchronizedList(new ArrayList<>()); - mapThreadToReadLockAcquisitionMetadata.put(currentThreadId, newList ); + public void addReadLock(ConcurrencyManager concurrencyManager) { + instanceLock.lock(); + try { + final Thread currentThread = Thread.currentThread(); + final long currentThreadId = currentThread.getId(); + ReadLockAcquisitionMetadata readLockAcquisitionMetadata = ConcurrencyUtil.SINGLETON.createReadLockAcquisitionMetadata(concurrencyManager); + + this.readLocks.add(FIRST_INDEX_OF_COLLECTION, concurrencyManager); + if (!mapThreadToReadLockAcquisitionMetadata.containsKey(currentThreadId)) { + List newList = Collections.synchronizedList(new ArrayList<>()); + mapThreadToReadLockAcquisitionMetadata.put(currentThreadId, newList); + } + List acquiredReadLocksInCurrentTransactionList = mapThreadToReadLockAcquisitionMetadata.get(currentThreadId); + acquiredReadLocksInCurrentTransactionList.add(FIRST_INDEX_OF_COLLECTION, readLockAcquisitionMetadata); + } finally { + instanceLock.unlock(); } - List acquiredReadLocksInCurrentTransactionList = mapThreadToReadLockAcquisitionMetadata.get(currentThreadId); - acquiredReadLocksInCurrentTransactionList.add(FIRST_INDEX_OF_COLLECTION, readLockAcquisitionMetadata); } /** @@ -70,46 +79,56 @@ public synchronized void addReadLock(ConcurrencyManager concurrencyManager) { * @param concurrencyManager * the concurrency cache key that is about to be decrement in number of readers. */ - public synchronized void removeReadLock(ConcurrencyManager concurrencyManager) { - final Thread currentThread = Thread.currentThread(); - final long currentThreadId = currentThread.getId(); - boolean readLockManagerHasTracingAboutAddedReadLocksForCurrentThread = mapThreadToReadLockAcquisitionMetadata.containsKey(currentThreadId); - - if (!readLockManagerHasTracingAboutAddedReadLocksForCurrentThread) { - String errorMessage = ConcurrencyUtil.SINGLETON.readLockManagerProblem02ReadLockManageHasNoEntriesForThread(concurrencyManager, currentThreadId); - removeReadLockProblemsDetected.add(errorMessage); - return; - } + public void removeReadLock(ConcurrencyManager concurrencyManager) { + instanceLock.lock(); + try { + final Thread currentThread = Thread.currentThread(); + final long currentThreadId = currentThread.getId(); + boolean readLockManagerHasTracingAboutAddedReadLocksForCurrentThread = mapThreadToReadLockAcquisitionMetadata.containsKey(currentThreadId); + + if (!readLockManagerHasTracingAboutAddedReadLocksForCurrentThread) { + String errorMessage = ConcurrencyUtil.SINGLETON.readLockManagerProblem02ReadLockManageHasNoEntriesForThread(concurrencyManager, currentThreadId); + removeReadLockProblemsDetected.add(errorMessage); + return; + } - List readLocksAcquiredDuringCurrentThread = mapThreadToReadLockAcquisitionMetadata.get(currentThreadId); - ReadLockAcquisitionMetadata readLockAquisitionMetadataToRemove = null; - for (ReadLockAcquisitionMetadata currentReadLockAcquisitionMetadata : readLocksAcquiredDuringCurrentThread) { - ConcurrencyManager currentCacheKeyObjectToCheck = currentReadLockAcquisitionMetadata.getCacheKeyWhoseNumberOfReadersThreadIsIncrementing(); - boolean dtoToRemoveFound = concurrencyManager.getConcurrencyManagerId() == currentCacheKeyObjectToCheck.getConcurrencyManagerId(); - if (dtoToRemoveFound) { - readLockAquisitionMetadataToRemove = currentReadLockAcquisitionMetadata; - break; + List readLocksAcquiredDuringCurrentThread = mapThreadToReadLockAcquisitionMetadata.get(currentThreadId); + ReadLockAcquisitionMetadata readLockAquisitionMetadataToRemove = null; + for (ReadLockAcquisitionMetadata currentReadLockAcquisitionMetadata : readLocksAcquiredDuringCurrentThread) { + ConcurrencyManager currentCacheKeyObjectToCheck = currentReadLockAcquisitionMetadata.getCacheKeyWhoseNumberOfReadersThreadIsIncrementing(); + boolean dtoToRemoveFound = concurrencyManager.getConcurrencyManagerId() == currentCacheKeyObjectToCheck.getConcurrencyManagerId(); + if (dtoToRemoveFound) { + readLockAquisitionMetadataToRemove = currentReadLockAcquisitionMetadata; + break; + } } - } - if (readLockAquisitionMetadataToRemove == null) { - String errorMessage = ConcurrencyUtil.SINGLETON.readLockManagerProblem03ReadLockManageHasNoEntriesForThread(concurrencyManager, currentThreadId); - removeReadLockProblemsDetected.add(errorMessage); - return; - } - this.readLocks.remove(concurrencyManager); - readLocksAcquiredDuringCurrentThread.remove(readLockAquisitionMetadataToRemove); + if (readLockAquisitionMetadataToRemove == null) { + String errorMessage = ConcurrencyUtil.SINGLETON.readLockManagerProblem03ReadLockManageHasNoEntriesForThread(concurrencyManager, currentThreadId); + removeReadLockProblemsDetected.add(errorMessage); + return; + } + this.readLocks.remove(concurrencyManager); + readLocksAcquiredDuringCurrentThread.remove(readLockAquisitionMetadataToRemove); - if (readLocksAcquiredDuringCurrentThread.isEmpty()) { - mapThreadToReadLockAcquisitionMetadata.remove(currentThreadId); + if (readLocksAcquiredDuringCurrentThread.isEmpty()) { + mapThreadToReadLockAcquisitionMetadata.remove(currentThreadId); + } + } finally { + instanceLock.unlock(); } } /** * Return a set of the deferred locks */ - public synchronized List getReadLocks() { - return Collections.unmodifiableList(readLocks); + public List getReadLocks() { + instanceLock.lock(); + try { + return Collections.unmodifiableList(readLocks); + } finally { + instanceLock.unlock(); + } } /** @@ -119,8 +138,13 @@ public synchronized List getReadLocks() { * @param problemDetected * the detected problem */ - public synchronized void addRemoveReadLockProblemsDetected(String problemDetected) { - removeReadLockProblemsDetected.add(problemDetected); + public void addRemoveReadLockProblemsDetected(String problemDetected) { + instanceLock.lock(); + try { + removeReadLockProblemsDetected.add(problemDetected); + } finally { + instanceLock.unlock(); + } } /** Getter for {@link #mapThreadToReadLockAcquisitionMetadata} */ @@ -142,8 +166,13 @@ public List getRemoveReadLockProblemsDetected() { * any read lock acquired in the tracing we definitely do not want this object instance to be thrown out * from our main tracing. It is probably revealing problems in read lock acquisition and released. */ - public synchronized boolean isEmpty() { - return readLocks.isEmpty() && removeReadLockProblemsDetected.isEmpty(); + public boolean isEmpty() { + instanceLock.lock(); + try { + return readLocks.isEmpty() && removeReadLockProblemsDetected.isEmpty(); + } finally { + instanceLock.unlock(); + } } /** @@ -156,16 +185,20 @@ public synchronized boolean isEmpty() { * or to go about doing */ @Override - public synchronized ReadLockManager clone() { - ReadLockManager clone = new ReadLockManager(); - clone.readLocks.addAll(this.readLocks); - for (Map.Entry> currentEntry : this.mapThreadToReadLockAcquisitionMetadata.entrySet()) { - Long key = currentEntry.getKey(); - List value = currentEntry.getValue(); - clone.mapThreadToReadLockAcquisitionMetadata.put(key, new ArrayList<>(value)); + public ReadLockManager clone() { + instanceLock.lock(); + try { + ReadLockManager clone = new ReadLockManager(); + clone.readLocks.addAll(this.readLocks); + for (Map.Entry> currentEntry : this.mapThreadToReadLockAcquisitionMetadata.entrySet()) { + Long key = currentEntry.getKey(); + List value = currentEntry.getValue(); + clone.mapThreadToReadLockAcquisitionMetadata.put(key, new ArrayList<>(value)); + } + clone.removeReadLockProblemsDetected.addAll(this.removeReadLockProblemsDetected); + return clone; + } finally { + instanceLock.unlock(); } - clone.removeReadLockProblemsDetected.addAll(this.removeReadLockProblemsDetected); - return clone; } - } diff --git a/foundation/org.eclipse.persistence.core/src/main/java/org/eclipse/persistence/internal/helper/WriteLockManager.java b/foundation/org.eclipse.persistence.core/src/main/java/org/eclipse/persistence/internal/helper/WriteLockManager.java index 8036731fdf3..05bc45a7900 100644 --- a/foundation/org.eclipse.persistence.core/src/main/java/org/eclipse/persistence/internal/helper/WriteLockManager.java +++ b/foundation/org.eclipse.persistence.core/src/main/java/org/eclipse/persistence/internal/helper/WriteLockManager.java @@ -44,6 +44,10 @@ import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.Semaphore; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.locks.Condition; +import java.util.concurrent.locks.Lock; +import java.util.concurrent.locks.ReentrantLock; import static java.util.Collections.unmodifiableMap; @@ -127,6 +131,10 @@ public class WriteLockManager { /* the first element in this list will be the prevailing thread */ protected ExposedNodeLinkedList prevailingQueue; + private final Lock toWaitOnLock = new ReentrantLock(); + private final Lock instancePrevailingQueueLock = new ReentrantLock(); + private final Condition toWaitOnLockCondition = toWaitOnLock.newCondition(); + public WriteLockManager() { this.prevailingQueue = new ExposedNodeLinkedList(); } @@ -172,14 +180,17 @@ public Map acquireLocksForClone(Object objectForClone, ClassDescriptor descripto // using the exact same approach we have been adding to the concurrency manager ConcurrencyUtil.SINGLETON.determineIfReleaseDeferredLockAppearsToBeDeadLocked(toWaitOn, whileStartTimeMillis, lockManager, readLockManager, ALLOW_INTERRUPTED_EXCEPTION_TO_BE_FIRED_UP_TRUE); - synchronized (toWaitOn) { + toWaitOnLock.lock(); + try { try { if (toWaitOn.isAcquired()) {//last minute check to insure it is still locked. - toWaitOn.wait(ConcurrencyUtil.SINGLETON.getAcquireWaitTime());// wait for lock on object to be released + toWaitOnLockCondition.await(ConcurrencyUtil.SINGLETON.getAcquireWaitTime(), TimeUnit.MILLISECONDS);// wait for lock on object to be released } } catch (InterruptedException ex) { // Ignore exception thread should continue. } + } finally { + toWaitOnLock.unlock(); } Object waitObject = toWaitOn.getObject(); // Object may be null for loss of identity. @@ -424,8 +435,11 @@ private void acquireRequiredLocksInternal(MergeManager mergeManager, UnitOfWorkC // set the QueueNode to be the node from the // linked list for quick removal upon // acquiring all locks - synchronized (this.prevailingQueue) { + instancePrevailingQueueLock.lock(); + try { mergeManager.setQueueNode(this.prevailingQueue.addLastElement(mergeManager)); + } finally { + instancePrevailingQueueLock.unlock(); } } @@ -435,14 +449,15 @@ private void acquireRequiredLocksInternal(MergeManager mergeManager, UnitOfWorkC try { if (activeCacheKey != null){ //wait on the lock of the object that we couldn't get. - synchronized (activeCacheKey) { + activeCacheKey.getInstanceLock().lock(); + try { // verify that the cache key is still locked before we wait on it, as //it may have been released since we tried to acquire it. if (activeCacheKey.isAcquired() && (activeCacheKey.getActiveThread() != Thread.currentThread())) { Thread thread = activeCacheKey.getActiveThread(); if (thread.isAlive()){ long time = System.currentTimeMillis(); - activeCacheKey.wait(MAX_WAIT); + activeCacheKey.getInstanceLockCondition().await(MAX_WAIT, TimeUnit.MILLISECONDS); if (System.currentTimeMillis() - time >= MAX_WAIT){ Object[] params = new Object[]{MAX_WAIT /1000, descriptor.getJavaClassName(), activeCacheKey.getKey(), thread.getName()}; StringBuilder buffer = new StringBuilder(TraceLocalization.buildMessage("max_time_exceeded_for_acquirerequiredlocks_wait", params)); @@ -464,6 +479,8 @@ private void acquireRequiredLocksInternal(MergeManager mergeManager, UnitOfWorkC } } } + } finally { + activeCacheKey.getInstanceLock().unlock(); } } } catch (InterruptedException exception) { @@ -502,8 +519,11 @@ private void acquireRequiredLocksInternal(MergeManager mergeManager, UnitOfWorkC }finally { if (mergeManager.getWriteLockQueued() != null) { //the merge manager entered the wait queue and must be cleaned up - synchronized(this.prevailingQueue) { + instancePrevailingQueueLock.lock(); + try { this.prevailingQueue.remove(mergeManager.getQueueNode()); + } finally { + instancePrevailingQueueLock.unlock(); } mergeManager.setWriteLockQueued(null); } diff --git a/foundation/org.eclipse.persistence.core/src/main/java/org/eclipse/persistence/internal/identitymaps/CacheKey.java b/foundation/org.eclipse.persistence.core/src/main/java/org/eclipse/persistence/internal/identitymaps/CacheKey.java index dc88e3dafcc..c8c98722481 100644 --- a/foundation/org.eclipse.persistence.core/src/main/java/org/eclipse/persistence/internal/identitymaps/CacheKey.java +++ b/foundation/org.eclipse.persistence.core/src/main/java/org/eclipse/persistence/internal/identitymaps/CacheKey.java @@ -21,6 +21,8 @@ import org.eclipse.persistence.sessions.DataRecord; import org.eclipse.persistence.sessions.DatabaseRecord; +import java.util.concurrent.TimeUnit; + /** *

Purpose: Container class for storing objects in an IdentityMap. *

Responsibilities:

    @@ -604,18 +606,23 @@ public void setTransactionId(Object transactionId) { this.transactionId = transactionId; } - public synchronized Object waitForObject(){ + public Object waitForObject(){ + getInstanceLock().lock(); try { - int count = 0; - while (this.object == null && isAcquired()) { - if (count > MAX_WAIT_TRIES) - throw ConcurrencyException.maxTriesLockOnBuildObjectExceded(getActiveThread(), Thread.currentThread()); - wait(10); - ++count; + try { + int count = 0; + while (this.object == null && isAcquired()) { + if (count > MAX_WAIT_TRIES) + throw ConcurrencyException.maxTriesLockOnBuildObjectExceded(getActiveThread(), Thread.currentThread()); + getInstanceLockCondition().await(10, TimeUnit.MILLISECONDS); + ++count; + } + } catch(InterruptedException ex) { + //ignore as the loop is broken } - } catch(InterruptedException ex) { - //ignore as the loop is broken + return this.object; + } finally { + getInstanceLock().unlock(); } - return this.object; } } diff --git a/performance/eclipselink.perf.test/el-test.performance.properties b/performance/eclipselink.perf.test/el-test.performance.properties index 50bc1e7033e..d34504e7953 100644 --- a/performance/eclipselink.perf.test/el-test.performance.properties +++ b/performance/eclipselink.perf.test/el-test.performance.properties @@ -12,5 +12,4 @@ warmup.iterations=20 run.iterations=20 -jmh.resultFile=jmh-result.csv jmh.resultFormat=csv diff --git a/performance/eclipselink.perf.test/pom.xml b/performance/eclipselink.perf.test/pom.xml index 237919ecf57..b60a635f8c2 100644 --- a/performance/eclipselink.perf.test/pom.xml +++ b/performance/eclipselink.perf.test/pom.xml @@ -189,15 +189,15 @@ - test-performance-benchmark + test-core-performance-benchmark ${warmup.iterations} ${run.iterations} - ${project.build.directory}/${jmh.resultFile} + ${project.build.directory}/jmh-core-result.txt ${jmh.resultFormat} - org.eclipse.persistence.testing.perf.Benchmarks + org.eclipse.persistence.testing.perf.CoreBenchmarks test @@ -205,12 +205,44 @@ - test-performance-jpa-benchmark + test-moxy-performance-benchmark ${warmup.iterations} ${run.iterations} - ${project.build.directory}/jpa-${jmh.resultFile} + ${project.build.directory}/jmh-moxy-result.txt + ${jmh.resultFormat} + + org.eclipse.persistence.testing.perf.MOXyBenchmarks + + test + + java + + + + test-jpa-metadata-performance-benchmark + + + ${warmup.iterations} + ${run.iterations} + ${project.build.directory}/jmh-jpa-metadata-result.txt + ${jmh.resultFormat} + + org.eclipse.persistence.testing.perf.JPAMetadataBenchmarks + + test + + java + + + + test-jpa-performance-benchmark + + + ${warmup.iterations} + ${run.iterations} + ${project.build.directory}/jmh-jpa-result.txt ${jmh.resultFormat} org.eclipse.persistence.testing.perf.JPABenchmarks diff --git a/performance/eclipselink.perf.test/src/test/java/org/eclipse/persistence/testing/perf/CoreBenchmarks.java b/performance/eclipselink.perf.test/src/test/java/org/eclipse/persistence/testing/perf/CoreBenchmarks.java new file mode 100644 index 00000000000..4e263a66564 --- /dev/null +++ b/performance/eclipselink.perf.test/src/test/java/org/eclipse/persistence/testing/perf/CoreBenchmarks.java @@ -0,0 +1,59 @@ +/* + * Copyright (c) 2024 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0, + * or the Eclipse Distribution License v. 1.0 which is available at + * http://www.eclipse.org/org/documents/edl-v10.php. + * + * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause + */ + +// Contributors: +// Oracle - initial implementation +package org.eclipse.persistence.testing.perf; + +import org.eclipse.persistence.testing.perf.core.ConcurrencyManagerBenchmark; +import org.openjdk.jmh.results.format.ResultFormatType; +import org.openjdk.jmh.runner.Runner; +import org.openjdk.jmh.runner.RunnerException; +import org.openjdk.jmh.runner.options.Options; +import org.openjdk.jmh.runner.options.OptionsBuilder; + +/** + * This class wraps core module related benchmarks. It is analogy to JUnit suites. + * + */ +public class CoreBenchmarks { + public static void main(String[] args) throws RunnerException { + + int warmupIterations = 20; + int measurementIterations = 20; + String resultFile = "jmh-core-result.txt"; + String resultFormat = "text"; + + if (null != args && args.length == 4) { + warmupIterations = Integer.parseInt(args[0]); + measurementIterations = Integer.parseInt(args[1]); + resultFile = args[2]; + resultFormat = args[3]; + } + + Options opt = new OptionsBuilder() + .include(getInclude(ConcurrencyManagerBenchmark.class)) + .result(resultFile) + .resultFormat(ResultFormatType.valueOf(resultFormat.toUpperCase())) + .warmupIterations(warmupIterations) + .measurementIterations(measurementIterations) + .forks(1) + .threads(50) + .build(); + + new Runner(opt).run(); + } + + private static String getInclude(Class cls) { + return ".*" + cls.getSimpleName() + ".*"; + } +} diff --git a/performance/eclipselink.perf.test/src/test/java/org/eclipse/persistence/testing/perf/JPABenchmarks.java b/performance/eclipselink.perf.test/src/test/java/org/eclipse/persistence/testing/perf/JPABenchmarks.java index f62429949b7..13eb2306ff4 100644 --- a/performance/eclipselink.perf.test/src/test/java/org/eclipse/persistence/testing/perf/JPABenchmarks.java +++ b/performance/eclipselink.perf.test/src/test/java/org/eclipse/persistence/testing/perf/JPABenchmarks.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, 2020 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2024 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at @@ -11,11 +11,13 @@ */ // Contributors: -// ljungmann - initial implementation +// Oracle - initial implementation package org.eclipse.persistence.testing.perf; -import org.eclipse.persistence.testing.perf.jpa.tests.basic.JPAMetadataProcessingTests; -import org.eclipse.persistence.testing.perf.jpa.tests.basic.MethodHandleComparisonTests; +import org.eclipse.persistence.testing.perf.jpa.tests.basic.JPAReadLargeAmmountCacheTests; +import org.eclipse.persistence.testing.perf.jpa.tests.basic.JPAReadLargeAmmountNoCacheTests; +import org.eclipse.persistence.testing.perf.jpa.tests.basic.JPAReadSmallAmmountCacheTests; +import org.eclipse.persistence.testing.perf.jpa.tests.basic.JPAReadSmallAmmountNoCacheTests; import org.openjdk.jmh.results.format.ResultFormatType; import org.openjdk.jmh.runner.Runner; import org.openjdk.jmh.runner.RunnerException; @@ -28,6 +30,7 @@ public static void main(String[] args) throws RunnerException { int warmupIterations = 20; int measurementIterations = 20; + int threads = 10; String resultFile = "jmh-jpa-result.txt"; String resultFormat = "text"; @@ -39,14 +42,17 @@ public static void main(String[] args) throws RunnerException { } Options opt = new OptionsBuilder() - .include(getInclude(JPAMetadataProcessingTests.class)) - .include(getInclude(MethodHandleComparisonTests.class)) + .include(getInclude(JPAReadSmallAmmountCacheTests.class)) + .include(getInclude(JPAReadSmallAmmountNoCacheTests.class)) + .include(getInclude(JPAReadLargeAmmountCacheTests.class)) + .include(getInclude(JPAReadLargeAmmountNoCacheTests.class)) .jvmArgsPrepend("-javaagent:" + System.getProperty("eclipselink.agent")) .result(resultFile) .resultFormat(ResultFormatType.valueOf(resultFormat.toUpperCase())) .warmupIterations(warmupIterations) .measurementIterations(measurementIterations) .forks(1) + .threads(threads) .build(); new Runner(opt).run(); diff --git a/performance/eclipselink.perf.test/src/test/java/org/eclipse/persistence/testing/perf/JPAMetadataBenchmarks.java b/performance/eclipselink.perf.test/src/test/java/org/eclipse/persistence/testing/perf/JPAMetadataBenchmarks.java new file mode 100644 index 00000000000..d49e37ecf65 --- /dev/null +++ b/performance/eclipselink.perf.test/src/test/java/org/eclipse/persistence/testing/perf/JPAMetadataBenchmarks.java @@ -0,0 +1,59 @@ +/* + * Copyright (c) 2016, 2024 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0, + * or the Eclipse Distribution License v. 1.0 which is available at + * http://www.eclipse.org/org/documents/edl-v10.php. + * + * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause + */ + +// Contributors: +// ljungmann - initial implementation +package org.eclipse.persistence.testing.perf; + +import org.eclipse.persistence.testing.perf.jpa.tests.basic.JPAMetadataProcessingTests; +import org.eclipse.persistence.testing.perf.jpa.tests.basic.MethodHandleComparisonTests; + +import org.openjdk.jmh.results.format.ResultFormatType; +import org.openjdk.jmh.runner.Runner; +import org.openjdk.jmh.runner.RunnerException; +import org.openjdk.jmh.runner.options.Options; +import org.openjdk.jmh.runner.options.OptionsBuilder; + +public class JPAMetadataBenchmarks { + + public static void main(String[] args) throws RunnerException { + + int warmupIterations = 20; + int measurementIterations = 20; + String resultFile = "jmh-jpa-metadata-result.txt"; + String resultFormat = "text"; + + if (null != args && args.length == 4) { + warmupIterations = Integer.parseInt(args[0]); + measurementIterations = Integer.parseInt(args[1]); + resultFile = args[2]; + resultFormat = args[3]; + } + + Options opt = new OptionsBuilder() + .include(getInclude(JPAMetadataProcessingTests.class)) + .include(getInclude(MethodHandleComparisonTests.class)) + .jvmArgsPrepend("-javaagent:" + System.getProperty("eclipselink.agent")) + .result(resultFile) + .resultFormat(ResultFormatType.valueOf(resultFormat.toUpperCase())) + .warmupIterations(warmupIterations) + .measurementIterations(measurementIterations) + .forks(1) + .build(); + + new Runner(opt).run(); + } + + private static String getInclude(Class cls) { + return ".*" + cls.getSimpleName() + ".*"; + } +} diff --git a/performance/eclipselink.perf.test/src/test/java/org/eclipse/persistence/testing/perf/Benchmarks.java b/performance/eclipselink.perf.test/src/test/java/org/eclipse/persistence/testing/perf/MOXyBenchmarks.java similarity index 97% rename from performance/eclipselink.perf.test/src/test/java/org/eclipse/persistence/testing/perf/Benchmarks.java rename to performance/eclipselink.perf.test/src/test/java/org/eclipse/persistence/testing/perf/MOXyBenchmarks.java index 1d1bb13c35f..53e86aadc93 100644 --- a/performance/eclipselink.perf.test/src/test/java/org/eclipse/persistence/testing/perf/Benchmarks.java +++ b/performance/eclipselink.perf.test/src/test/java/org/eclipse/persistence/testing/perf/MOXyBenchmarks.java @@ -33,12 +33,12 @@ * @author Martin Vojtek (martin.vojtek@oracle.com) * */ -public class Benchmarks { +public class MOXyBenchmarks { public static void main(String[] args) throws RunnerException { int warmupIterations = 20; int measurementIterations = 20; - String resultFile = "jmh-result.txt"; + String resultFile = "jmh-moxy-result.txt"; String resultFormat = "text"; if (null != args && args.length == 4) { diff --git a/performance/eclipselink.perf.test/src/test/java/org/eclipse/persistence/testing/perf/core/ConcurrencyManagerBenchmark.java b/performance/eclipselink.perf.test/src/test/java/org/eclipse/persistence/testing/perf/core/ConcurrencyManagerBenchmark.java new file mode 100644 index 00000000000..2f91b436ac8 --- /dev/null +++ b/performance/eclipselink.perf.test/src/test/java/org/eclipse/persistence/testing/perf/core/ConcurrencyManagerBenchmark.java @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2024 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0, + * or the Eclipse Distribution License v. 1.0 which is available at + * http://www.eclipse.org/org/documents/edl-v10.php. + * + * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause + */ + +// Contributors: +// Oracle - initial implementation +package org.eclipse.persistence.testing.perf.core; + +import org.eclipse.persistence.internal.helper.ConcurrencyManager; +import org.openjdk.jmh.annotations.Benchmark; +import org.openjdk.jmh.annotations.Scope; +import org.openjdk.jmh.annotations.State; +import org.openjdk.jmh.infra.Blackhole; + +/** + * This benchmark verify performance of {@code org.eclipse.persistence.internal.helper.ConcurrencyManager}. + * + */ +@State(Scope.Benchmark) +public class ConcurrencyManagerBenchmark { + + @Benchmark + public void testAcquireRelease(Blackhole bh) throws Exception { + ConcurrencyManager concurrencyManager = new ConcurrencyManager(); + concurrencyManager.acquire(); + concurrencyManager.release(); + } +} diff --git a/performance/eclipselink.perf.test/src/test/java/org/eclipse/persistence/testing/perf/jpa/model/basic/DetailEntity.java b/performance/eclipselink.perf.test/src/test/java/org/eclipse/persistence/testing/perf/jpa/model/basic/DetailEntity.java new file mode 100644 index 00000000000..28bf004f7ba --- /dev/null +++ b/performance/eclipselink.perf.test/src/test/java/org/eclipse/persistence/testing/perf/jpa/model/basic/DetailEntity.java @@ -0,0 +1,79 @@ +/* + * Copyright (c) 2024 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0, + * or the Eclipse Distribution License v. 1.0 which is available at + * http://www.eclipse.org/org/documents/edl-v10.php. + * + * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause + */ + +// Contributors: +// Oracle - initial implementation +package org.eclipse.persistence.testing.perf.jpa.model.basic; + +import jakarta.persistence.Entity; +import jakarta.persistence.Id; +import jakarta.persistence.JoinColumn; +import jakarta.persistence.ManyToOne; +import jakarta.persistence.Table; + +@Entity +@Table(name = "P2_DETAIL") +public class DetailEntity { + @Id + private long id; + + private String name; + + @ManyToOne() + @JoinColumn(name = "MASTER_ID_FK") + private MasterEntity master; + + public DetailEntity() { + } + + public DetailEntity(long id) { + this.id = id; + } + + public DetailEntity(long id, String name, MasterEntity master) { + this.id = id; + this.name = name; + this.master = master; + } + + public long getId() { + return id; + } + + public void setId(long id) { + this.id = id; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public MasterEntity getMaster() { + return master; + } + + public void setMaster(MasterEntity master) { + this.master = master; + } + + @Override + public String toString() { + return "DetailEntity{" + + "id=" + id + + ", name='" + name + + '}'; + } +} diff --git a/performance/eclipselink.perf.test/src/test/java/org/eclipse/persistence/testing/perf/jpa/model/basic/MasterEntity.java b/performance/eclipselink.perf.test/src/test/java/org/eclipse/persistence/testing/perf/jpa/model/basic/MasterEntity.java new file mode 100644 index 00000000000..839c214aeef --- /dev/null +++ b/performance/eclipselink.perf.test/src/test/java/org/eclipse/persistence/testing/perf/jpa/model/basic/MasterEntity.java @@ -0,0 +1,89 @@ +/* + * Copyright (c) 2024 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0, + * or the Eclipse Distribution License v. 1.0 which is available at + * http://www.eclipse.org/org/documents/edl-v10.php. + * + * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause + */ + +// Contributors: +// Oracle - initial implementation +package org.eclipse.persistence.testing.perf.jpa.model.basic; + +import jakarta.persistence.Entity; +import jakarta.persistence.FetchType; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.GenerationType; +import jakarta.persistence.Id; +import jakarta.persistence.OneToMany; +import jakarta.persistence.Table; + +import java.util.ArrayList; +import java.util.List; + +@Entity +@Table(name = "P2_MASTER") +public class MasterEntity { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private long id; + + private String name; + + @OneToMany(mappedBy = "master", fetch = FetchType.EAGER) + private List details = new ArrayList<>(); + + public MasterEntity() { + } + + public MasterEntity(long id) { + this.id = id; + } + + public MasterEntity(long id, String name) { + this.id = id; + this.name = name; + } + + public MasterEntity(String name) { + this.name = name; + } + + public long getId() { + return id; + } + + public void setId(long id) { + this.id = id; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public List getDetails() { + return details; + } + + public void setDetails(List details) { + this.details = details; + } + + @Override + public String toString() { + return "MasterEntity{" + + "id=" + id + + ", name='" + name + '\'' + + ", details=" + details + + '}'; + } +} diff --git a/performance/eclipselink.perf.test/src/test/java/org/eclipse/persistence/testing/perf/jpa/tests/basic/JPAReadAbstract.java b/performance/eclipselink.perf.test/src/test/java/org/eclipse/persistence/testing/perf/jpa/tests/basic/JPAReadAbstract.java new file mode 100644 index 00000000000..8d38f14fcef --- /dev/null +++ b/performance/eclipselink.perf.test/src/test/java/org/eclipse/persistence/testing/perf/jpa/tests/basic/JPAReadAbstract.java @@ -0,0 +1,102 @@ +/* + * Copyright (c) 2024 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0, + * or the Eclipse Distribution License v. 1.0 which is available at + * http://www.eclipse.org/org/documents/edl-v10.php. + * + * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause + */ + +// Contributors: +// Oracle - initial implementation +package org.eclipse.persistence.testing.perf.jpa.tests.basic; + +import jakarta.persistence.EntityManager; +import jakarta.persistence.Persistence; +import jakarta.persistence.EntityManagerFactory; + +import org.eclipse.persistence.testing.perf.jpa.model.basic.DetailEntity; +import org.eclipse.persistence.testing.perf.jpa.model.basic.MasterEntity; +import org.openjdk.jmh.annotations.Benchmark; +import org.openjdk.jmh.annotations.Setup; +import org.openjdk.jmh.annotations.TearDown; + +/** + * Benchmarks for JPA reading data. + * + * @author Oracle + */ +public abstract class JPAReadAbstract { + + public static final int DETAIL_ID_STEP = 10000; + + private EntityManagerFactory emf = Persistence.createEntityManagerFactory(getPersistenceUnitName()); + + @Setup + public void setup() { + EntityManager em = emf.createEntityManager(); + prepareData(em); + } + + @TearDown + public void tearDown() { + emf.close(); + } + + /** + * Read MasterEntity and DetailEntity (fetch = FetchType.EAGER). + */ + @Benchmark + public void testReadEntity() { + EntityManager em = null; + try { + em = emf.createEntityManager(); + for (long i = 1; i <= getMasterSize(); i++) { + MasterEntity masterEntity = em.find(MasterEntity.class, i); + if (masterEntity == null) { + throw new RuntimeException("MasterEntity is null!"); + } + if (masterEntity.getDetails().size() < getDetailSize()) { + throw new RuntimeException("No of DetailEntities is |" + masterEntity.getDetails().size() + "| less than expected |" + getDetailSize() + "|!"); + } + } + } catch (Throwable e) { + throw new RuntimeException(e); + } finally { + if (em != null) { + em.close(); + } + } + } + + private void prepareData(EntityManager em) { + try { + em.getTransaction().begin(); + for (int i = 1; i <= getMasterSize(); i++) { + MasterEntity masterEntity = new MasterEntity(i, "Master name " + i); + em.persist(masterEntity); + for (int j = 1; j <= getDetailSize(); j++) { + DetailEntity detailEntity = new DetailEntity(i * DETAIL_ID_STEP + j, "Detail name " + j, masterEntity); + masterEntity.getDetails().add(detailEntity); + em.persist(detailEntity); + } + } + em.getTransaction().commit(); + } catch (Throwable e) { + throw new RuntimeException(e); + } finally { + if (em.getTransaction().isActive()) { + em.getTransaction().rollback(); + } + } + } + + public abstract String getPersistenceUnitName(); + + public abstract int getMasterSize(); + + public abstract int getDetailSize(); +} diff --git a/performance/eclipselink.perf.test/src/test/java/org/eclipse/persistence/testing/perf/jpa/tests/basic/JPAReadLargeAmmountAbstract.java b/performance/eclipselink.perf.test/src/test/java/org/eclipse/persistence/testing/perf/jpa/tests/basic/JPAReadLargeAmmountAbstract.java new file mode 100644 index 00000000000..37f189500d4 --- /dev/null +++ b/performance/eclipselink.perf.test/src/test/java/org/eclipse/persistence/testing/perf/jpa/tests/basic/JPAReadLargeAmmountAbstract.java @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2024 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0, + * or the Eclipse Distribution License v. 1.0 which is available at + * http://www.eclipse.org/org/documents/edl-v10.php. + * + * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause + */ + +// Contributors: +// Oracle - initial implementation +package org.eclipse.persistence.testing.perf.jpa.tests.basic; + +/** + * Benchmarks for JPA reading data (large amount - 10000 rows per each request em.find()). + * + * @author Oracle + */ +public abstract class JPAReadLargeAmmountAbstract extends JPAReadAbstract { + + public int getMasterSize() { + return 10; + } + + public int getDetailSize() { + return DETAIL_ID_STEP; + } +} diff --git a/performance/eclipselink.perf.test/src/test/java/org/eclipse/persistence/testing/perf/jpa/tests/basic/JPAReadLargeAmmountCacheTests.java b/performance/eclipselink.perf.test/src/test/java/org/eclipse/persistence/testing/perf/jpa/tests/basic/JPAReadLargeAmmountCacheTests.java new file mode 100644 index 00000000000..e673461e66f --- /dev/null +++ b/performance/eclipselink.perf.test/src/test/java/org/eclipse/persistence/testing/perf/jpa/tests/basic/JPAReadLargeAmmountCacheTests.java @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2024 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0, + * or the Eclipse Distribution License v. 1.0 which is available at + * http://www.eclipse.org/org/documents/edl-v10.php. + * + * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause + */ + +// Contributors: +// Oracle - initial implementation +package org.eclipse.persistence.testing.perf.jpa.tests.basic; + +import org.openjdk.jmh.annotations.Scope; +import org.openjdk.jmh.annotations.State; + +/** + * Benchmarks for JPA reading data (large amount - 10000 rows per each request em.find()) with JPA L2 cache enabled. + * + * @author Oracle + */ +@State(Scope.Benchmark) +//@BenchmarkMode(Mode.AverageTime) +public class JPAReadLargeAmmountCacheTests extends JPAReadLargeAmmountAbstract { + + public String getPersistenceUnitName() { + return "jpa-performance-read-cache"; + } +} diff --git a/performance/eclipselink.perf.test/src/test/java/org/eclipse/persistence/testing/perf/jpa/tests/basic/JPAReadLargeAmmountNoCacheTests.java b/performance/eclipselink.perf.test/src/test/java/org/eclipse/persistence/testing/perf/jpa/tests/basic/JPAReadLargeAmmountNoCacheTests.java new file mode 100644 index 00000000000..e056f33d47f --- /dev/null +++ b/performance/eclipselink.perf.test/src/test/java/org/eclipse/persistence/testing/perf/jpa/tests/basic/JPAReadLargeAmmountNoCacheTests.java @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2024 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0, + * or the Eclipse Distribution License v. 1.0 which is available at + * http://www.eclipse.org/org/documents/edl-v10.php. + * + * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause + */ + +// Contributors: +// Oracle - initial implementation +package org.eclipse.persistence.testing.perf.jpa.tests.basic; + +import org.openjdk.jmh.annotations.Scope; +import org.openjdk.jmh.annotations.State; + +/** + * Benchmarks for JPA reading data (large amount - 10000 rows per each request em.find()) with JPA L2 cache disabled. + * + * @author Oracle + */ +@State(Scope.Benchmark) +//@BenchmarkMode(Mode.AverageTime) +public class JPAReadLargeAmmountNoCacheTests extends JPAReadLargeAmmountAbstract { + + public String getPersistenceUnitName() { + return "jpa-performance-read-no-cache"; + } +} diff --git a/performance/eclipselink.perf.test/src/test/java/org/eclipse/persistence/testing/perf/jpa/tests/basic/JPAReadSmallAmmountAbstract.java b/performance/eclipselink.perf.test/src/test/java/org/eclipse/persistence/testing/perf/jpa/tests/basic/JPAReadSmallAmmountAbstract.java new file mode 100644 index 00000000000..a36b4927442 --- /dev/null +++ b/performance/eclipselink.perf.test/src/test/java/org/eclipse/persistence/testing/perf/jpa/tests/basic/JPAReadSmallAmmountAbstract.java @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2024 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0, + * or the Eclipse Distribution License v. 1.0 which is available at + * http://www.eclipse.org/org/documents/edl-v10.php. + * + * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause + */ + +// Contributors: +// Oracle - initial implementation +package org.eclipse.persistence.testing.perf.jpa.tests.basic; + +/** + * Benchmarks for JPA reading data (small amount - up 20 rows per each request em.find()). + * + * @author Oracle + */ +public abstract class JPAReadSmallAmmountAbstract extends JPAReadAbstract { + + public int getMasterSize() { + return 10; + } + + public int getDetailSize() { + return 10; + } +} diff --git a/performance/eclipselink.perf.test/src/test/java/org/eclipse/persistence/testing/perf/jpa/tests/basic/JPAReadSmallAmmountCacheTests.java b/performance/eclipselink.perf.test/src/test/java/org/eclipse/persistence/testing/perf/jpa/tests/basic/JPAReadSmallAmmountCacheTests.java new file mode 100644 index 00000000000..9bf24a6515b --- /dev/null +++ b/performance/eclipselink.perf.test/src/test/java/org/eclipse/persistence/testing/perf/jpa/tests/basic/JPAReadSmallAmmountCacheTests.java @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2024 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0, + * or the Eclipse Distribution License v. 1.0 which is available at + * http://www.eclipse.org/org/documents/edl-v10.php. + * + * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause + */ + +// Contributors: +// Oracle - initial implementation +package org.eclipse.persistence.testing.perf.jpa.tests.basic; + +import org.openjdk.jmh.annotations.Scope; +import org.openjdk.jmh.annotations.State; + +/** + * Benchmarks for JPA reading data (small amount - up 20 rows per each request em.find()) with JPA L2 cache enabled. + * + * @author Oracle + */ +@State(Scope.Benchmark) +//@BenchmarkMode(Mode.AverageTime) +public class JPAReadSmallAmmountCacheTests extends JPAReadSmallAmmountAbstract { + + public String getPersistenceUnitName() { + return "jpa-performance-read-cache"; + } +} diff --git a/performance/eclipselink.perf.test/src/test/java/org/eclipse/persistence/testing/perf/jpa/tests/basic/JPAReadSmallAmmountNoCacheTests.java b/performance/eclipselink.perf.test/src/test/java/org/eclipse/persistence/testing/perf/jpa/tests/basic/JPAReadSmallAmmountNoCacheTests.java new file mode 100644 index 00000000000..42b72102bca --- /dev/null +++ b/performance/eclipselink.perf.test/src/test/java/org/eclipse/persistence/testing/perf/jpa/tests/basic/JPAReadSmallAmmountNoCacheTests.java @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2024 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0, + * or the Eclipse Distribution License v. 1.0 which is available at + * http://www.eclipse.org/org/documents/edl-v10.php. + * + * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause + */ + +// Contributors: +// Oracle - initial implementation +package org.eclipse.persistence.testing.perf.jpa.tests.basic; + +import org.openjdk.jmh.annotations.Scope; +import org.openjdk.jmh.annotations.State; + +/** + * Benchmarks for JPA reading data (small amount - up 20 rows per each request em.find()) with JPA L2 cache disabled. + * + * @author Oracle + */ +@State(Scope.Benchmark) +//@BenchmarkMode(Mode.AverageTime) +public class JPAReadSmallAmmountNoCacheTests extends JPAReadSmallAmmountAbstract { + + public String getPersistenceUnitName() { + return "jpa-performance-read-no-cache"; + } +} diff --git a/performance/eclipselink.perf.test/src/test/resources/META-INF/persistence.xml b/performance/eclipselink.perf.test/src/test/resources/META-INF/persistence.xml index 1d8d897fe13..b27ca1492da 100644 --- a/performance/eclipselink.perf.test/src/test/resources/META-INF/persistence.xml +++ b/performance/eclipselink.perf.test/src/test/resources/META-INF/persistence.xml @@ -46,4 +46,31 @@ + + org.eclipse.persistence.jpa.PersistenceProvider + org.eclipse.persistence.testing.perf.jpa.model.basic.MasterEntity + org.eclipse.persistence.testing.perf.jpa.model.basic.DetailEntity + + + + + + + + + + org.eclipse.persistence.jpa.PersistenceProvider + org.eclipse.persistence.testing.perf.jpa.model.basic.MasterEntity + org.eclipse.persistence.testing.perf.jpa.model.basic.DetailEntity + + + + + + + + + + +