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