java/code/src/com/redhat/rhn/frontend/events/NewCloneErrataAction.java | 1
java/code/src/com/redhat/rhn/frontend/events/NewCloneErrataEvent.java | 13 +
java/code/src/com/redhat/rhn/frontend/xmlrpc/errata/ErrataHandler.java | 27 ---
java/code/src/com/redhat/rhn/manager/errata/AsyncErrataCloneCounter.java | 74
++++++++++
java/code/src/com/redhat/rhn/manager/errata/ErrataManager.java | 12 +
java/spacewalk-java.spec | 5
rel-eng/packages/spacewalk-java | 2
7 files changed, 110 insertions(+), 24 deletions(-)
New commits:
commit 2de3ceb59ad5116cf6bab7c09724cb3552406c36
Author: Stephen Herr <sherr(a)redhat.com>
Date: Tue Jun 26 14:46:23 2012 -0400
Automatic commit of package [spacewalk-java] release [1.8.98-1].
diff --git a/java/spacewalk-java.spec b/java/spacewalk-java.spec
index dd8204c..2b65ffe 100644
--- a/java/spacewalk-java.spec
+++ b/java/spacewalk-java.spec
@@ -23,7 +23,7 @@ Name: spacewalk-java
Summary: Spacewalk Java site packages
Group: Applications/Internet
License: GPLv2
-Version: 1.8.97
+Version: 1.8.98
Release: 1%{?dist}
URL:
https://fedorahosted.org/spacewalk
Source0:
https://fedorahosted.org/releases/s/p/spacewalk/%{name}-%{version}.tar.gz
@@ -615,6 +615,9 @@ fi
%{jardir}/postgresql-jdbc.jar
%changelog
+* Tue Jun 26 2012 Stephen Herr <sherr(a)redhat.com> 1.8.98-1
+- 829485 - resolveing potential deadlock during asynchronous errata clone
+
* Tue Jun 26 2012 Jan Pazdziora 1.8.97-1
- Each dataset must have a different name.
- Add CSV downloader for scap search page.
diff --git a/rel-eng/packages/spacewalk-java b/rel-eng/packages/spacewalk-java
index a6ddb83..5b9f9ed 100644
--- a/rel-eng/packages/spacewalk-java
+++ b/rel-eng/packages/spacewalk-java
@@ -1 +1 @@
-1.8.97-1 java/
+1.8.98-1 java/
commit 7713d370ccb509980056ecd18a5846960f964458
Author: Stephen Herr <sherr(a)redhat.com>
Date: Tue Jun 26 14:46:08 2012 -0400
829485 - resolveing potential deadlock during asynchronous errata clone
diff --git a/java/code/src/com/redhat/rhn/frontend/events/NewCloneErrataAction.java
b/java/code/src/com/redhat/rhn/frontend/events/NewCloneErrataAction.java
index 74a64a5..d2db2ea 100644
--- a/java/code/src/com/redhat/rhn/frontend/events/NewCloneErrataAction.java
+++ b/java/code/src/com/redhat/rhn/frontend/events/NewCloneErrataAction.java
@@ -43,5 +43,6 @@ extends AbstractDatabaseAction {
errata.add(ErrataFactory.lookupById(eid));
ErrataManager.cloneErrataApi(msg.getChan(), errata,
msg.getUser(), msg.isInheritPackages());
+ msg.deregister();
}
}
diff --git a/java/code/src/com/redhat/rhn/frontend/events/NewCloneErrataEvent.java
b/java/code/src/com/redhat/rhn/frontend/events/NewCloneErrataEvent.java
index 458d126..7b8f173 100644
--- a/java/code/src/com/redhat/rhn/frontend/events/NewCloneErrataEvent.java
+++ b/java/code/src/com/redhat/rhn/frontend/events/NewCloneErrataEvent.java
@@ -20,6 +20,7 @@ import com.redhat.rhn.domain.channel.Channel;
import com.redhat.rhn.domain.channel.ChannelFactory;
import com.redhat.rhn.domain.user.User;
import com.redhat.rhn.domain.user.UserFactory;
+import com.redhat.rhn.manager.errata.AsyncErrataCloneCounter;
import org.hibernate.Transaction;
@@ -128,5 +129,17 @@ public class NewCloneErrataEvent implements EventDatabaseMessage {
return UserFactory.lookupById(userId);
}
+ /**
+ * Register the async clone event with the counter
+ */
+ public void register() {
+ AsyncErrataCloneCounter.getInstance().addAsyncErrataCloneJob(chanId);
+ }
+ /**
+ * De-regiser the async clone event from the counter
+ */
+ public void deregister() {
+ AsyncErrataCloneCounter.getInstance().removeAsyncErrataCloneJob(chanId);
+ }
}
diff --git a/java/code/src/com/redhat/rhn/frontend/xmlrpc/errata/ErrataHandler.java
b/java/code/src/com/redhat/rhn/frontend/xmlrpc/errata/ErrataHandler.java
index d557ee7..49aca00 100644
--- a/java/code/src/com/redhat/rhn/frontend/xmlrpc/errata/ErrataHandler.java
+++ b/java/code/src/com/redhat/rhn/frontend/xmlrpc/errata/ErrataHandler.java
@@ -865,11 +865,6 @@ public class ErrataHandler extends BaseHandler {
*
* @xmlrpc.doc Clone a list of errata into the specified channel.
*
- * Warning: Issuing a synchronous clone request for a channel that has asynchronous
- * errata cloning jobs pending can result in a database deadlock! Adding more
- * asynchronous jobs is safe. If deadlock occurs one must re-schedule both the
- * synchronous and asynchronous jobs to ensure all errata are cloned.
- *
* @xmlrpc.param #session_key()
* @xmlrpc.param #param("string", "channel_label")
* @xmlrpc.param
@@ -896,11 +891,6 @@ public class ErrataHandler extends BaseHandler {
*
* @xmlrpc.doc Asynchronously clone a list of errata into the specified channel.
*
- * Warning: Issuing a synchronous clone request for a channel that has asynchronous
- * errata cloning jobs pending can result in a database deadlock! Adding more
- * asynchronous jobs is safe. If deadlock occurs one must re-schedule both the
- * synchronous and asynchronous jobs to ensure all errata are cloned.
- *
* @xmlrpc.param #session_key()
* @xmlrpc.param #param("string", "channel_label")
* @xmlrpc.param
@@ -968,6 +958,13 @@ public class ErrataHandler extends BaseHandler {
inheritPackages);
return new ArrayList<Errata>().toArray();
}
+ else if (ErrataManager.channelHasPendingAsyncCloneJobs(channel)) {
+ throw new InvalidChannelException(
+ "Channel " +
+ channel.getLabel() +
+ " has pending asynchronous errata clone jobs. You must
wait" +
+ "until asychronous errata clone jobs are done.");
+ }
return ErrataManager.cloneErrataApi(channel, errataToClone,
loggedInUser, inheritPackages);
}
@@ -987,11 +984,6 @@ public class ErrataHandler extends BaseHandler {
* @xmlrpc.doc Clones a list of errata into a specified cloned channel
* according the original erratas.
*
- * Warning: Issuing a synchronous cloneAsOriginal request for a channel that has
- * asynchronous errata cloning jobs pending can result in a database deadlock!
Adding
- * more asynchronous jobs is safe. If deadlock occurs one must re-schedule both the
- * synchronous and asynchronous jobs to ensure all errata are cloned.
- *
* @xmlrpc.param #session_key()
* @xmlrpc.param #param("string", "channel_label")
* @xmlrpc.param
@@ -1020,11 +1012,6 @@ public class ErrataHandler extends BaseHandler {
* @xmlrpc.doc Asynchronously clones a list of errata into a specified cloned
channel
* according the original erratas
*
- * Warning: Issuing a synchronous cloneAsOriginal request for a channel that has
- * asynchronous errata cloning jobs pending can result in a database deadlock!
Adding
- * more asynchronous jobs is safe. If deadlock occurs one must re-schedule both the
- * synchronous and asynchronous jobs to ensure all errata are cloned.
- *
* @xmlrpc.param #session_key()
* @xmlrpc.param #param("string", "channel_label")
* @xmlrpc.param
diff --git a/java/code/src/com/redhat/rhn/manager/errata/AsyncErrataCloneCounter.java
b/java/code/src/com/redhat/rhn/manager/errata/AsyncErrataCloneCounter.java
new file mode 100644
index 0000000..ed3d528
--- /dev/null
+++ b/java/code/src/com/redhat/rhn/manager/errata/AsyncErrataCloneCounter.java
@@ -0,0 +1,74 @@
+package com.redhat.rhn.manager.errata;
+
+import java.util.Hashtable;
+
+/**
+ * If someone attempts to schedule a synchronous errata clone into a
+ * channel that has asynchronous errata clones taking place in the
+ * background, a database deadlock will occur. There is no way to
+ * avoid the deadlock, so instead we'll keep track of how many
+ * pending jobs we have here. The jobs themselves are not persistent,
+ * residing in tomcat memory only, so the counter should not be
+ * persistent. That is why I implemented the counter as a singeton
+ * class instead of a database table or some other such implementation.
+ *
+ * @author Stephen Herr <sherr(a)redhat.com>
+ *
+ */
+public class AsyncErrataCloneCounter {
+ // hashtable is syncronized, so is threadsafe
+ private final Hashtable<Long, Integer> count;
+ private static AsyncErrataCloneCounter instance = new AsyncErrataCloneCounter();
+
+ private AsyncErrataCloneCounter() {
+ count = new Hashtable<Long, Integer>();
+ }
+
+ /**
+ * Get the one and only instance of AsyncErrataCloneCounter
+ * @return the singleton instance
+ */
+ public static AsyncErrataCloneCounter getInstance() {
+ return instance;
+ }
+
+ /**
+ * Check if a channel has pending asyncrhonous errata clone jobs
+ * @param cid channel id
+ * @return True if there are pending jobs for a channel, false otherwise
+ */
+ public boolean channelHasPendingJobs(Long cid) {
+ Integer n = count.get(cid);
+ if (n == null || n == 0) {
+ return false;
+ }
+ return true;
+ }
+
+ /**
+ * Call this with the channel id when scheduling a new asyncronous
+ * errata clone job
+ * @param cid channel id
+ */
+ public void addAsyncErrataCloneJob(Long cid) {
+ Integer n = count.get(cid);
+ if (n != null) {
+ count.put(cid, n + 1);
+ }
+ else {
+ count.put(cid, 1);
+ }
+ }
+
+ /**
+ * Call this with channel id when completeing an asynchronous
+ * errata clone job
+ * @param cid channel id
+ */
+ public void removeAsyncErrataCloneJob(Long cid) {
+ Integer n = count.get(cid);
+ if (n != null && n > 0) {
+ count.put(cid, n - 1);
+ }
+ }
+}
diff --git a/java/code/src/com/redhat/rhn/manager/errata/ErrataManager.java
b/java/code/src/com/redhat/rhn/manager/errata/ErrataManager.java
index 3e3f5c8..4ee5a65 100644
--- a/java/code/src/com/redhat/rhn/manager/errata/ErrataManager.java
+++ b/java/code/src/com/redhat/rhn/manager/errata/ErrataManager.java
@@ -1274,12 +1274,20 @@ public class ErrataManager extends BaseManager {
for (long eid : errata) {
NewCloneErrataEvent neve = new NewCloneErrataEvent(chan, eid, user,
inheritPackages);
+ neve.register();
MessageQueue.publish(neve);
}
}
-
-
+ /**
+ * Check if the channel has pending asynchronous errata clone jobs
+ * @param channel channel to check
+ * @return true if there are pending jobs, false otherwise
+ */
+ public static boolean channelHasPendingAsyncCloneJobs(Channel channel) {
+ return AsyncErrataCloneCounter.getInstance().channelHasPendingJobs(
+ channel.getId());
+ }
/**
* Send errata notifications for a particular errata and channel