This is an automated email from the ASF dual-hosted git repository.

danhaywood pushed a commit to branch CAUSEWAY-3799
in repository https://gitbox.apache.org/repos/asf/causeway.git


The following commit(s) were added to refs/heads/CAUSEWAY-3799 by this push:
     new 068ee03987 CAUSEWAY-3799: introduces DeadlockRecognizer so that audit 
...
068ee03987 is described below

commit 068ee039871596155032e1ac97cfa094cff6b881
Author: Dan Haywood <[email protected]>
AuthorDate: Fri Jun 28 13:32:40 2024 +0100

    CAUSEWAY-3799: introduces DeadlockRecognizer so that audit ...
    
    ... that is, EntityChangeTrackerDefault, doesn't swallow deadlock 
exceptions when obtaining the pre- or post- values.
    Instead, we propagate upwards so that they are dealt with correctly
---
 core/metamodel/src/main/java/module-info.java      |  1 +
 .../services/deadlock/DeadlockRecognizer.java      | 39 ++++++++++++++++
 .../objectlifecycle/PropertyChangeRecord.java      | 21 +++++----
 .../commons/CausewayModulePersistenceCommons.java  |  5 +++
 .../changetracking/EntityChangeTrackerDefault.java | 10 +++--
 .../deadlock/DeadlockRecognizerDefault.java        | 52 ++++++++++++++++++++++
 6 files changed, 117 insertions(+), 11 deletions(-)

diff --git a/core/metamodel/src/main/java/module-info.java 
b/core/metamodel/src/main/java/module-info.java
index e3ead26984..01b5fc6d8b 100644
--- a/core/metamodel/src/main/java/module-info.java
+++ b/core/metamodel/src/main/java/module-info.java
@@ -142,6 +142,7 @@ open module org.apache.causeway.core.metamodel {
     exports org.apache.causeway.core.metamodel.valuesemantics.temporal.legacy;
     exports org.apache.causeway.core.metamodel.valuetypes;
     exports org.apache.causeway.core.metamodel.spi;
+    exports org.apache.causeway.core.metamodel.services.deadlock;
 
 
     requires jakarta.activation;
diff --git 
a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/services/deadlock/DeadlockRecognizer.java
 
b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/services/deadlock/DeadlockRecognizer.java
new file mode 100644
index 0000000000..5bb43677f2
--- /dev/null
+++ 
b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/services/deadlock/DeadlockRecognizer.java
@@ -0,0 +1,39 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *        http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ */
+package org.apache.causeway.core.metamodel.services.deadlock;
+
+import lombok.SneakyThrows;
+
+/**
+ *
+ */
+public interface DeadlockRecognizer {
+
+    boolean isDeadlock(Throwable e);
+
+    @SneakyThrows
+    default <E extends Exception> void rethrowIfDeadlock(E e) {
+        if (isDeadlock(e)) {
+            throw e;
+        }
+    }
+
+}
+
+
diff --git 
a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/services/objectlifecycle/PropertyChangeRecord.java
 
b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/services/objectlifecycle/PropertyChangeRecord.java
index c958276a36..8639a93c36 100644
--- 
a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/services/objectlifecycle/PropertyChangeRecord.java
+++ 
b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/services/objectlifecycle/PropertyChangeRecord.java
@@ -27,6 +27,7 @@ import 
org.apache.causeway.applib.services.xactn.TransactionId;
 import org.apache.causeway.core.metamodel.consent.InteractionInitiatedBy;
 import org.apache.causeway.core.metamodel.object.ManagedObject;
 import org.apache.causeway.core.metamodel.object.MmUnwrapUtils;
+import org.apache.causeway.core.metamodel.services.deadlock.DeadlockRecognizer;
 import org.apache.causeway.core.metamodel.spec.feature.OneToOneAssociation;
 
 import lombok.EqualsAndHashCode;
@@ -55,9 +56,10 @@ public final class PropertyChangeRecord implements 
Comparable<PropertyChangeReco
     }
 
     public static PropertyChangeRecord ofCurrent(
-            final @NonNull PropertyChangeRecordId pcrId) {
+            final @NonNull PropertyChangeRecordId pcrId,
+            final DeadlockRecognizer deadlockRecognizer) {
         return new PropertyChangeRecord(pcrId)
-                        .withPreValueSetToCurrentElseUnknown();
+                        
.withPreValueSetToCurrentElseUnknown(deadlockRecognizer);
     }
 
     public static PropertyChangeRecord ofCurrent(
@@ -68,9 +70,10 @@ public final class PropertyChangeRecord implements 
Comparable<PropertyChangeReco
     }
 
     public static PropertyChangeRecord ofDeleting(
-            final @NonNull PropertyChangeRecordId id) {
+            final @NonNull PropertyChangeRecordId id,
+            final DeadlockRecognizer deadlockRecognizer) {
         return new PropertyChangeRecord(id)
-                .withPreValueSetToCurrentElseUnknown()
+                .withPreValueSetToCurrentElseUnknown(deadlockRecognizer)
                 .withPostValueSetToDeleted();
     }
 
@@ -84,10 +87,11 @@ public final class PropertyChangeRecord implements 
Comparable<PropertyChangeReco
         return target.getLogicalTypeName() + "#" + propertyId;
     }
 
-    public PropertyChangeRecord withPreValueSetToCurrentElseUnknown() {
+    public PropertyChangeRecord 
withPreValueSetToCurrentElseUnknown(DeadlockRecognizer deadlockRecognizer) {
         try {
             return withPreValueSetToCurrent();
         } catch (Exception ex) {
+            deadlockRecognizer.rethrowIfDeadlock(ex);
             return withPreValueSetToUnknown();
         }
     }
@@ -109,10 +113,11 @@ public final class PropertyChangeRecord implements 
Comparable<PropertyChangeReco
         return this;
     }
 
-    public PropertyChangeRecord withPostValueSetToCurrentElseUnknown() {
+    public PropertyChangeRecord 
withPostValueSetToCurrentElseUnknown(DeadlockRecognizer deadlockRecognizer) {
         try {
             return withPostValueSetToCurrent();
         } catch (Exception ex) {
+            deadlockRecognizer.rethrowIfDeadlock(ex);
             return withPostValueSetToUnknown();
         }
     }
@@ -121,11 +126,11 @@ public final class PropertyChangeRecord implements 
Comparable<PropertyChangeReco
         return withPostValueSetTo(PropertyValuePlaceholder.DELETED);
     }
 
-    private PropertyChangeRecord withPostValueSetToCurrent() {
+    public PropertyChangeRecord withPostValueSetToCurrent() {
         return withPostValueSetTo(getPropertyValue());
     }
 
-    private PropertyChangeRecord withPostValueSetToUnknown() {
+    public PropertyChangeRecord withPostValueSetToUnknown() {
         return withPostValueSetTo(PropertyValuePlaceholder.UNKNOWN);
     }
 
diff --git 
a/persistence/commons/src/main/java/org/apache/causeway/persistence/commons/CausewayModulePersistenceCommons.java
 
b/persistence/commons/src/main/java/org/apache/causeway/persistence/commons/CausewayModulePersistenceCommons.java
index 3434956056..0816be9929 100644
--- 
a/persistence/commons/src/main/java/org/apache/causeway/persistence/commons/CausewayModulePersistenceCommons.java
+++ 
b/persistence/commons/src/main/java/org/apache/causeway/persistence/commons/CausewayModulePersistenceCommons.java
@@ -18,6 +18,8 @@
  */
 package org.apache.causeway.persistence.commons;
 
+import 
org.apache.causeway.persistence.commons.integration.deadlock.DeadlockRecognizerDefault;
+
 import org.springframework.context.annotation.Configuration;
 import org.springframework.context.annotation.Import;
 
@@ -35,6 +37,9 @@ import 
org.apache.causeway.persistence.commons.integration.repository.Repository
         EntityChangeTrackerDefault.class,
         PreAndPostValueEvaluatorServiceDefault.class,
 
+        // @Component's
+        DeadlockRecognizerDefault.class,
+
         // @Repository's
         RepositoryServiceDefault.class,
 
diff --git 
a/persistence/commons/src/main/java/org/apache/causeway/persistence/commons/integration/changetracking/EntityChangeTrackerDefault.java
 
b/persistence/commons/src/main/java/org/apache/causeway/persistence/commons/integration/changetracking/EntityChangeTrackerDefault.java
index d14d54a7ff..4a18e7a56a 100644
--- 
a/persistence/commons/src/main/java/org/apache/causeway/persistence/commons/integration/changetracking/EntityChangeTrackerDefault.java
+++ 
b/persistence/commons/src/main/java/org/apache/causeway/persistence/commons/integration/changetracking/EntityChangeTrackerDefault.java
@@ -42,6 +42,7 @@ import javax.inject.Provider;
 import org.apache.causeway.applib.jaxb.JavaSqlXMLGregorianCalendarMarshalling;
 import org.apache.causeway.core.metamodel.execution.InteractionInternal;
 
+import org.apache.causeway.core.metamodel.services.deadlock.DeadlockRecognizer;
 import org.apache.causeway.schema.chg.v2.ChangesDto;
 import org.apache.causeway.schema.chg.v2.ObjectsDto;
 import org.apache.causeway.schema.common.v2.OidsDto;
@@ -240,7 +241,7 @@ implements
                     if 
(MmEntityUtils.getEntityState(rec.getEntity()).isTransientOrRemoved()) {
                         rec.withPostValueSetToDeleted();
                     } else {
-                        rec.withPostValueSetToCurrentElseUnknown();
+                        
rec.withPostValueSetToCurrentElseUnknown(deadlockRecognizer);
                     }
                 })
                 .filter(managedProperty -> 
shouldPublish(managedProperty.getPreAndPostValue()))
@@ -582,7 +583,9 @@ implements
             } else {
                 // home-grown approach
                 
MmEntityUtils.streamPropertyChangeRecordIdsForChangePublishing(entity)
-                    .forEach(pcrId -> addPropertyChangeRecordIfAbsent(pcrId, 
PropertyChangeRecord.ofCurrent(pcrId)));
+                    .forEach(pcrId -> {
+                        addPropertyChangeRecordIfAbsent(pcrId, 
PropertyChangeRecord.ofCurrent(pcrId, deadlockRecognizer));
+                    });
             }
         });
     }
@@ -604,7 +607,7 @@ implements
 
                 
MmEntityUtils.streamPropertyChangeRecordIdsForChangePublishing(entity)
                     .forEach(pcrId -> {
-                        addPropertyChangeRecordIfAbsent(pcrId, 
PropertyChangeRecord::ofDeleting);
+                        addPropertyChangeRecordIfAbsent(pcrId, id -> 
PropertyChangeRecord.ofDeleting(id, deadlockRecognizer));
                     });
             }
         });
@@ -675,5 +678,6 @@ implements
 
     @Inject private Configuration configuration;
     @Inject private CausewayConfiguration causewayConfiguration;
+    @Inject private DeadlockRecognizer deadlockRecognizer;
 
 }
\ No newline at end of file
diff --git 
a/persistence/commons/src/main/java/org/apache/causeway/persistence/commons/integration/deadlock/DeadlockRecognizerDefault.java
 
b/persistence/commons/src/main/java/org/apache/causeway/persistence/commons/integration/deadlock/DeadlockRecognizerDefault.java
new file mode 100644
index 0000000000..c42be5bb8b
--- /dev/null
+++ 
b/persistence/commons/src/main/java/org/apache/causeway/persistence/commons/integration/deadlock/DeadlockRecognizerDefault.java
@@ -0,0 +1,52 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *        http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ */
+package org.apache.causeway.persistence.commons.integration.deadlock;
+
+import lombok.RequiredArgsConstructor;
+import lombok.extern.log4j.Log4j2;
+import lombok.val;
+
+import javax.annotation.Priority;
+import javax.inject.Inject;
+
+import org.apache.causeway.applib.annotation.PriorityPrecedence;
+
+import org.apache.causeway.core.metamodel.services.deadlock.DeadlockRecognizer;
+
+import org.springframework.dao.DeadlockLoserDataAccessException;
+import org.springframework.stereotype.Component;
+
+@Component
+@Priority(PriorityPrecedence.LATE)
+@RequiredArgsConstructor(onConstructor_ = {@Inject})
+@Log4j2
+public class DeadlockRecognizerDefault implements DeadlockRecognizer {
+
+    @Override
+    public boolean isDeadlock(Throwable ex) {
+        val whetherDeadlock = ex instanceof DeadlockLoserDataAccessException ||
+                (ex.getMessage() != null && ex.getMessage().contains("chosen 
as the deadlock victim"));
+        if (whetherDeadlock) {
+            log.warn("Detected deadlock");
+            log.debug("Detected deadlock details:", ex);
+        }
+        return whetherDeadlock;
+    }
+
+}

Reply via email to