Skip to content

Commit 6536fe2

Browse files
author
Radek Felcman
authored
New eclipselink dead lock scenario ... enhancement and unit test (#2350) (#2449)
Fixes #2094 This is enhancement for case of possible deadlock which should happens in org.eclipse.persistence.internal.sessions.AbstractSession#getCacheKeyFromTargetSessionForMerge method if used org.eclipse.persistence.internal.identitymaps.CacheKey instance is locked by another thread. There is new system or persistence property eclipselink.concurrency.manager.allow.getcachekeyformerge.mode which can control org.eclipse.persistence.internal.sessions.AbstractSession#getCacheKeyFromTargetSessionForMerge logic to get org.eclipse.persistence.internal.identitymaps.CacheKey instance and object value behind this. There are two allowed values: ORIGIN (DEFAULT) - There is infinite java.lang.Object.wait() call in case of some conditions during time when object/entity referred from org.eclipse.persistence.internal.identitymaps.CacheKey is locked and modified by another thread. In some cases it should leads into deadlock. WAITLOOP - Merge manager will try in the loop with timeout wait cacheKey.wait(ConcurrencyUtil.SINGLETON.getAcquireWaitTime()); fetch object/entity from org.eclipse.persistence.internal.identitymaps.CacheKey. If fetch will be successful object/entity loop finish and continue with remaining code. If not java.lang.InterruptedException is thrown and caught and used org.eclipse.persistence.internal.identitymaps.CacheKey instance status is set into invalidation state. This strategy avoid deadlock issue, but there should be impact to the performance. (cherry picked from commit dd61247) Signed-off-by: Radek Felcman <radek.felcman@oracle.com>
1 parent 6405214 commit 6536fe2

15 files changed

Lines changed: 633 additions & 102 deletions

File tree

docs/docs.jpa/src/main/asciidoc/persistenceproperties_ref.adoc

Lines changed: 52 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
///////////////////////////////////////////////////////////////////////////////
22

3-
Copyright (c) 2022, 2024 Oracle and/or its affiliates. All rights reserved.
3+
Copyright (c) 2022, 2025 Oracle and/or its affiliates. All rights reserved.
44

55
This program and the accompanying materials are made available under the
66
terms of the Eclipse Public License v. 2.0, which is available at
@@ -213,6 +213,7 @@ EclipseLink includes the following persistence property extensions for
213213
concurrency management:
214214
215215
* link:#concurrency-manager-allow-concurrencyexception[`concurrency.manager.allow.concurrencyexception`]
216+
* link:#concurrency-manager-allow-getcachekeyformerge-mode[`concurrency.manager.allow.getcachekeyformerge.mode`]
216217
* link:#concurrency-manager-allow-interruptedexception[`concurrency.manager.allow.interruptedexception`]
217218
* link:#concurrency-manager-allow-readlockstacktrace[`concurrency.manager.allow.readlockstacktrace`]
218219
* link:#concurrency-manager-build-object-complete-waittime[`concurrency.manager.build.object.complete.waittime`]
@@ -286,6 +287,7 @@ The following lists the EclipseLink persistence property
286287
* link:#compositeunitmember[`composite-unit.member`]
287288
* link:#compositeunitproperties[`composite-unit.properties`]
288289
* link:#concurrency-manager-allow-concurrencyexception[`concurrency.manager.allow.concurrencyexception`]
290+
* link:#concurrency-manager-allow-getcachekeyformerge-mode[`concurrency.manager.allow.getcachekeyformerge.mode`]
289291
* link:#concurrency-manager-allow-interruptedexception[`concurrency.manager.allow.interruptedexception`]
290292
* link:#concurrency-manager-allow-readlockstacktrace[`concurrency.manager.allow.readlockstacktrace`]
291293
* link:#concurrency-manager-build-object-complete-waittime[`concurrency.manager.build.object.complete.waittime`]
@@ -3103,6 +3105,55 @@ persistence.xml_*
31033105
31043106
'''''
31053107
3108+
=== concurrency.manager.allow.getcachekeyformerge.mode
3109+
3110+
This property control in `org.eclipse.persistence.internal.sessions.AbstractSession#getCacheKeyFromTargetSessionForMerge(java.lang.Object, org.eclipse.persistence.internal.descriptors.ObjectBuilder, org.eclipse.persistence.descriptors.ClassDescriptor, org.eclipse.persistence.internal.sessions.MergeManager)`
3111+
strategy how `org.eclipse.persistence.internal.identitymaps.CacheKey` will be fetched from shared cache.
3112+
3113+
3114+
*Values*
3115+
3116+
link:#concurrency.manager.allow.getcachekeyformerge.mode[Table 5-30]
3117+
describes this persistence property's values.
3118+
3119+
[[concurrency.manager.allow.getcachekeyformerge.mode]]
3120+
3121+
*_Table 5-30 Valid Values for
3122+
concurrency.manager.allow.getcachekeyformerge.mode_*
3123+
3124+
|=======================================================================
3125+
|*Value* |*Description*
3126+
|ORIGIN |(Default) There is infinite `java.lang.Object.wait()` call in case
3127+
of some conditions during time when object/entity referred from
3128+
`org.eclipse.persistence.internal.identitymaps.CacheKey` is locked
3129+
and modified by another thread. In some cases it should leads into deadlock.
3130+
3131+
|WAITLOOP |Merge manager will try in the loop with timeout wait `cacheKey.wait(ConcurrencyUtil.SINGLETON.getAcquireWaitTime());`
3132+
fetch object/entity from `org.eclipse.persistence.internal.identitymaps.CacheKey`.
3133+
If fetch will be successful object/entity loop finish and continue with remaining code.
3134+
If not `java.lang.InterruptedException` is thrown and caught and used `org.eclipse.persistence.internal.identitymaps.CacheKey`
3135+
instance status is set into invalidation state. This strategy avoid deadlock issue,
3136+
but there should be impact to the performance.
3137+
|=======================================================================
3138+
3139+
*Examples*
3140+
3141+
link:#concurrency.manager.allow.getcachekeyformerge.mode[Example
3142+
5-24] shows how to use this property in the `persistence.xml` file.
3143+
3144+
[[concurrency.manager.allow.getcachekeyformerge.mode]]
3145+
3146+
*_Example 5-24 Using concurrency.manager.allow.getcachekeyformerge.mode in
3147+
persistence.xml_*
3148+
3149+
[source,oac_no_warn]
3150+
----
3151+
<property name="eclipselink.concurrency.manager.allow.getcachekeyformerge.mode" value="WAITLOOP" />
3152+
3153+
----
3154+
3155+
'''''
3156+
31063157
=== concurrency.manager.allow.concurrencyexception
31073158
31083159
See valid values table.
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
/*
2+
* Copyright (c) 2025 Oracle and/or its affiliates. All rights reserved.
3+
*
4+
* This program and the accompanying materials are made available under the
5+
* terms of the Eclipse Public License v. 2.0 which is available at
6+
* http://www.eclipse.org/legal/epl-2.0,
7+
* or the Eclipse Distribution License v. 1.0 which is available at
8+
* http://www.eclipse.org/org/documents/edl-v10.php.
9+
*
10+
* SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
11+
*/
12+
13+
// Contributors:
14+
// Oracle - initial API and implementation
15+
package org.eclipse.persistence.config;
16+
17+
/**
18+
* INTERNAL:
19+
* <p>
20+
* <b>Purpose</b>: It is data model behind {@linkplain org.eclipse.persistence.config.SystemProperties#CONCURRENCY_MANAGER_ALLOW_GET_CACHE_KEY_FOR_MERGE_MODE}
21+
* or {@linkplain org.eclipse.persistence.config.PersistenceUnitProperties#CONCURRENCY_MANAGER_ALLOW_GET_CACHE_KEY_FOR_MERGE_MODE}.
22+
*/
23+
public final class MergeManagerOperationMode {
24+
25+
/**
26+
* {@code ORIGIN} (DEFAULT) - There is infinite {@linkplain java.lang.Object#wait()} call in case of some conditions during time when object/entity referred from
27+
* {@code org.eclipse.persistence.internal.identitymaps.CacheKey} is locked and modified by another thread. In some cases it should leads into deadlock.
28+
*/
29+
public static final String ORIGIN = "ORIGIN";
30+
31+
/**
32+
* {@code WAITLOOP} - Merge manager will try in the loop with timeout wait {@code cacheKey.wait(ConcurrencyUtil.SINGLETON.getAcquireWaitTime());}
33+
* fetch object/entity from {@linkplain org.eclipse.persistence.internal.identitymaps.CacheKey}. If fetch will be successful object/entity loop finish and continue
34+
* with remaining code. If not @{code java.lang.InterruptedException} is thrown and caught and used {@linkplain org.eclipse.persistence.internal.identitymaps.CacheKey} instance
35+
* status is set into invalidation state. This strategy avoid deadlock issue, but there should be impact to the performance.
36+
*/
37+
public static final String WAITLOOP = "WAITLOOP";
38+
39+
private MergeManagerOperationMode() {
40+
// no instance please
41+
}
42+
}

foundation/org.eclipse.persistence.core/src/main/java/org/eclipse/persistence/config/PersistenceUnitProperties.java

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 1998, 2024 Oracle and/or its affiliates. All rights reserved.
2+
* Copyright (c) 1998, 2025 Oracle and/or its affiliates. All rights reserved.
33
* Copyright (c) 1998, 2024 IBM Corporation. All rights reserved.
44
*
55
* This program and the accompanying materials are made available under the
@@ -4045,6 +4045,23 @@ public class PersistenceUnitProperties {
40454045
*/
40464046
public static final String CONCURRENCY_MANAGER_ALLOW_INTERRUPTED_EXCEPTION = "eclipselink.concurrency.manager.allow.interruptedexception";
40474047

4048+
/**
4049+
* <p>
4050+
* This property control in {@link org.eclipse.persistence.internal.sessions.AbstractSession#getCacheKeyFromTargetSessionForMerge(java.lang.Object, org.eclipse.persistence.internal.descriptors.ObjectBuilder, org.eclipse.persistence.descriptors.ClassDescriptor, org.eclipse.persistence.internal.sessions.MergeManager)}
4051+
* strategy how {@code org.eclipse.persistence.internal.identitymaps.CacheKey} will be fetched from shared cache.
4052+
* <p>
4053+
* <b>Allowed Values</b> (case-sensitive String)<b>:</b>
4054+
* <ul>
4055+
* <li>{@code ORIGIN} (DEFAULT) - There is infinite {@linkplain java.lang.Object#wait()} call in case of some conditions during time when object/entity referred from
4056+
* {@code org.eclipse.persistence.internal.identitymaps.CacheKey} is locked and modified by another thread. In some cases it should leads into deadlock.
4057+
* <li>{@code WAITLOOP} - Merge manager will try in the loop with timeout wait {@code cacheKey.wait(ConcurrencyUtil.SINGLETON.getAcquireWaitTime());}
4058+
* fetch object/entity from {@linkplain org.eclipse.persistence.internal.identitymaps.CacheKey}. If fetch will be successful object/entity loop finish and continue
4059+
* with remaining code. If not @{code java.lang.InterruptedException} is thrown and caught and used {@linkplain org.eclipse.persistence.internal.identitymaps.CacheKey} instance
4060+
* status is set into invalidation state. This strategy avoid deadlock issue, but there should be impact to the performance.
4061+
* </ul>
4062+
*/
4063+
public static final String CONCURRENCY_MANAGER_ALLOW_GET_CACHE_KEY_FOR_MERGE_MODE = "eclipselink.concurrency.manager.allow.getcachekeyformerge.mode";
4064+
40484065
/**
40494066
* <p>
40504067
* This property control (enable/disable) if <code>ConcurrencyException</code> fired when dead-lock diagnostic is enabled.

foundation/org.eclipse.persistence.core/src/main/java/org/eclipse/persistence/config/SystemProperties.java

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 1998, 2024 Oracle and/or its affiliates. All rights reserved.
2+
* Copyright (c) 1998, 2025 Oracle and/or its affiliates. All rights reserved.
33
* Copyright (c) 2024 Contributors to the Eclipse Foundation. All rights reserved.
44
*
55
* This program and the accompanying materials are made available under the
@@ -158,6 +158,23 @@ public class SystemProperties {
158158
*/
159159
public static final String CONCURRENCY_MANAGER_ALLOW_INTERRUPTED_EXCEPTION = "eclipselink.concurrency.manager.allow.interruptedexception";
160160

161+
/**
162+
* <p>
163+
* This property control in {@link org.eclipse.persistence.internal.sessions.AbstractSession#getCacheKeyFromTargetSessionForMerge(java.lang.Object, org.eclipse.persistence.internal.descriptors.ObjectBuilder, org.eclipse.persistence.descriptors.ClassDescriptor, org.eclipse.persistence.internal.sessions.MergeManager)}
164+
* strategy how {@code org.eclipse.persistence.internal.identitymaps.CacheKey} will be fetched from shared cache.
165+
* <p>
166+
* <b>Allowed Values</b> (case-sensitive String)<b>:</b>
167+
* <ul>
168+
* <li>{@code ORIGIN} (DEFAULT) - There is infinite {@linkplain java.lang.Object#wait()} call in case of some conditions during time when object/entity referred from
169+
* {@code org.eclipse.persistence.internal.identitymaps.CacheKey} is locked and modified by another thread. In some cases it should leads into deadlock.
170+
* <li>{@code WAITLOOP} - Merge manager will try in the loop with timeout wait {@code cacheKey.wait(ConcurrencyUtil.SINGLETON.getAcquireWaitTime());}
171+
* fetch object/entity from {@linkplain org.eclipse.persistence.internal.identitymaps.CacheKey}. If fetch will be successful object/entity loop finish and continue
172+
* with remaining code. If not @{code java.lang.InterruptedException} is thrown and caught and used {@linkplain org.eclipse.persistence.internal.identitymaps.CacheKey} instance
173+
* status is set into invalidation state. This strategy avoid deadlock issue, but there should be impact to the performance.
174+
* </ul>
175+
*/
176+
public static final String CONCURRENCY_MANAGER_ALLOW_GET_CACHE_KEY_FOR_MERGE_MODE = "eclipselink.concurrency.manager.allow.getcachekeyformerge.mode";
177+
161178
/**
162179
* <p>
163180
* This property control (enable/disable) if {@code ConcurrencyException} fired when dead-lock diagnostic is enabled.

foundation/org.eclipse.persistence.core/src/main/java/org/eclipse/persistence/internal/helper/ConcurrencyManager.java

Lines changed: 25 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,6 @@
1919
import java.util.ArrayList;
2020
import java.util.Arrays;
2121
import java.util.Date;
22-
import java.util.HashMap;
23-
import java.util.HashSet;
2422
import java.util.IdentityHashMap;
2523
import java.util.Iterator;
2624
import java.util.List;
@@ -864,7 +862,7 @@ public void releaseAllLocksAcquiredByThread(DeferredLockManager lockManager) {
864862
* @return Never null if the read lock manager does not yet exist for the current thread. otherwise its read log
865863
* manager is returned.
866864
*/
867-
protected static ReadLockManager getReadLockManager(Thread thread) {
865+
public static ReadLockManager getReadLockManager(Thread thread) {
868866
Map<Thread, ReadLockManager> readLockManagers = getReadLockManagers();
869867
return readLockManagers.get(thread);
870868
}
@@ -1122,4 +1120,28 @@ public Lock getInstanceLock() {
11221120
public Condition getInstanceLockCondition() {
11231121
return this.instanceLockCondition;
11241122
}
1123+
1124+
/**
1125+
* Check if {@code org.eclipse.persistence.internal.helper.ConcurrencyManager} or child like {@code org.eclipse.persistence.internal.identitymaps.CacheKey} is currently being owned for writing
1126+
* and if that owning thread happens to be the current thread doing the check.
1127+
*
1128+
* @return {@code false} means either the thread is currently not owned by any other thread for writing purposes. Or otherwise if is owned by some thread
1129+
* but the thread is not the current thread. {@code false} is returned if and only if instance is being owned by some thread
1130+
* and that thread is not the current thread, it is some other competing thread.
1131+
*/
1132+
public boolean isAcquiredForWritingAndOwnedByDifferentThread() {
1133+
instanceLock.lock();
1134+
try {
1135+
if (!this.isAcquired()) {
1136+
return false;
1137+
}
1138+
if (this.activeThread == null) {
1139+
return false;
1140+
}
1141+
Thread currentThread = Thread.currentThread();
1142+
return this.activeThread != currentThread;
1143+
} finally {
1144+
instanceLock.unlock();
1145+
}
1146+
}
11251147
}

0 commit comments

Comments
 (0)