Data base deadlocks. How to deal with them (retries)
authorgu.martinm@gmail.com <gu.martinm@gmail.com>
Thu, 9 Oct 2014 11:14:59 +0000 (13:14 +0200)
committergu.martinm@gmail.com <gu.martinm@gmail.com>
Thu, 9 Oct 2014 11:14:59 +0000 (13:14 +0200)
SpringJava/DeadLocksSQL/src/main/database/example.sql [new file with mode: 0644]
SpringJava/DeadLocksSQL/src/main/java/de/example/sql/deadlocks/Main.java
SpringJava/DeadLocksSQL/src/main/java/de/example/sql/deadlocks/annotation/DeadlockRetry.java
SpringJava/DeadLocksSQL/src/main/java/de/example/sql/deadlocks/aspect/DeadlockRetryAspect.java
SpringJava/DeadLocksSQL/src/main/java/de/example/sql/deadlocks/example/CustomAnnotationExample.java [deleted file]
SpringJava/DeadLocksSQL/src/main/java/de/example/sql/deadlocks/example/FirstTransaction.java [new file with mode: 0644]
SpringJava/DeadLocksSQL/src/main/java/de/example/sql/deadlocks/example/SecondTransaction.java [new file with mode: 0644]
SpringJava/DeadLocksSQL/src/main/resources/spring-config.xml

diff --git a/SpringJava/DeadLocksSQL/src/main/database/example.sql b/SpringJava/DeadLocksSQL/src/main/database/example.sql
new file mode 100644 (file)
index 0000000..498cfb6
--- /dev/null
@@ -0,0 +1,78 @@
+-- MySQL dump 10.13  Distrib 5.5.35, for debian-linux-gnu (x86_64)
+--
+-- Host: localhost    Database: example
+-- ------------------------------------------------------
+-- Server version      5.5.35-0+wheezy1-log
+
+/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */;
+/*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */;
+/*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */;
+/*!40101 SET NAMES utf8 */;
+/*!40103 SET @OLD_TIME_ZONE=@@TIME_ZONE */;
+/*!40103 SET TIME_ZONE='+00:00' */;
+/*!40014 SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0 */;
+/*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */;
+/*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */;
+/*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */;
+
+--
+-- Table structure for table `children`
+--
+
+DROP TABLE IF EXISTS `children`;
+/*!40101 SET @saved_cs_client     = @@character_set_client */;
+/*!40101 SET character_set_client = utf8 */;
+CREATE TABLE `children` (
+  `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
+  `name` varchar(500) COLLATE utf8_unicode_ci NOT NULL,
+  `parent_id` bigint(20) unsigned NOT NULL,
+  PRIMARY KEY (`id`),
+  KEY `parent_id` (`parent_id`),
+  CONSTRAINT `children_ibfk_1` FOREIGN KEY (`parent_id`) REFERENCES `parents` (`id`) ON DELETE CASCADE
+) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
+/*!40101 SET character_set_client = @saved_cs_client */;
+
+--
+-- Dumping data for table `children`
+--
+
+LOCK TABLES `children` WRITE;
+/*!40000 ALTER TABLE `children` DISABLE KEYS */;
+INSERT INTO `children` VALUES (1,'Peter',1),(2,'Mary',2);
+/*!40000 ALTER TABLE `children` ENABLE KEYS */;
+UNLOCK TABLES;
+
+--
+-- Table structure for table `parents`
+--
+
+DROP TABLE IF EXISTS `parents`;
+/*!40101 SET @saved_cs_client     = @@character_set_client */;
+/*!40101 SET character_set_client = utf8 */;
+CREATE TABLE `parents` (
+  `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
+  `name` varchar(500) COLLATE utf8_unicode_ci NOT NULL,
+  PRIMARY KEY (`id`)
+) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
+/*!40101 SET character_set_client = @saved_cs_client */;
+
+--
+-- Dumping data for table `parents`
+--
+
+LOCK TABLES `parents` WRITE;
+/*!40000 ALTER TABLE `parents` DISABLE KEYS */;
+INSERT INTO `parents` VALUES (1,'John'),(2,'Foo');
+/*!40000 ALTER TABLE `parents` ENABLE KEYS */;
+UNLOCK TABLES;
+/*!40103 SET TIME_ZONE=@OLD_TIME_ZONE */;
+
+/*!40101 SET SQL_MODE=@OLD_SQL_MODE */;
+/*!40014 SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS */;
+/*!40014 SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS */;
+/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */;
+/*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */;
+/*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */;
+/*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */;
+
+-- Dump completed on 2014-10-09 11:35:50
index b545307..0a7c982 100644 (file)
@@ -1,23 +1,61 @@
 package de.example.sql.deadlocks;
 
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.FutureTask;
+
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
-import de.example.sql.deadlocks.example.CustomAnnotationExample;
+import de.example.sql.deadlocks.example.FirstTransaction;
+import de.example.sql.deadlocks.example.SecondTransaction;
 
 
 public class Main {
        private static final Logger logger = LoggerFactory.getLogger(Main.class);
        
-       /**
-        * @param args
-        */
+
        public static void main(String[] args) {
                logger.info("Starting application");
-               
-               CustomAnnotationExample customAnnotation =
-                               (CustomAnnotationExample) SpringContextLocator.getInstance().getBean("customAnnotation");
-               customAnnotation.doCustomAnnotationExample();
+
+               final FutureTask<Void> taskFirst = new FutureTask<Void>
+               (
+                       new Runnable(){
+
+                               @Override
+                               public void run() {
+                                       final FirstTransaction first = (FirstTransaction) SpringContextLocator.getInstance().getBean("firstTransaction");
+                                       first.doTransaction();
+                               }
+                       },
+                       null
+               );
+               new Thread(taskFirst).start();
+
+               final FutureTask<Void> taskSecond = new FutureTask<Void>
+               (
+                       new Runnable(){
+
+                               @Override
+                               public void run() {
+                                       final SecondTransaction second = (SecondTransaction) SpringContextLocator.getInstance().getBean("secondTransaction");
+                                       second.doTransaction();
+                               }
+                       },
+                       null
+               );
+               new Thread(taskSecond).start();
+
+               // Wait for end.
+               try {
+                       taskFirst.get();
+                       taskSecond.get();
+               } catch (final InterruptedException e) {
+                       logger.error("Error", e);
+               } catch (final ExecutionException e) {
+                       logger.error("Error", e);
+               }
+
+
                logger.info("End application");
        }
 }
index ba8cc7d..f18c2b9 100644 (file)
@@ -22,5 +22,5 @@ public @interface DeadlockRetry {
         * 
         * @return interval in milliseconds
         */
-       int interval() default 1000;
+       long interval() default 1000;
 }
index 96c9ed2..c90d9a1 100644 (file)
@@ -8,6 +8,7 @@ import org.aspectj.lang.annotation.Around;
 import org.aspectj.lang.reflect.MethodSignature;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
+import org.springframework.core.Ordered;
 
 import com.mysql.jdbc.exceptions.jdbc4.MySQLTransactionRollbackException;
 
@@ -18,13 +19,14 @@ import de.example.sql.deadlocks.annotation.DeadlockRetry;
  *
  */
 @Aspect
-public class DeadlockRetryAspect {
+public class DeadlockRetryAspect implements Ordered {
        private static final Logger logger = LoggerFactory.getLogger(DeadlockRetryAspect.class);
+       private static final int ORDER = 99;
 
-       @Around(value = "@annotation(de.example.sql.deadlocks.annotation.DeadlockRetry)", argNames = "deadlockRetry")
+       @Around(value = "@annotation(deadlockRetry)", argNames = "deadlockRetry")
        public Object doAround(final ProceedingJoinPoint pjp, final DeadlockRetry deadlockRetry) throws Throwable {
-               logger.info("GUSUSUSUUS");
-               final Integer maxTries = deadlockRetry.maxTries();
+
+               final int maxTries = deadlockRetry.maxTries();
         final long interval = deadlockRetry.interval();
 
         final Object target = pjp.getTarget();
@@ -61,11 +63,15 @@ public class DeadlockRetryAspect {
                                }
                        }
                }
-       } while (count <= maxTries);
+       } while (count < maxTries);
 
        throw new RuntimeException("DeadlockRetry failed, deadlock in all retry attempts.", deadLockException);
     }
 
+       @Override
+       public int getOrder() {
+               return ORDER;
+       }
 
        private boolean isDeadLock(Throwable ex) {
                do {
diff --git a/SpringJava/DeadLocksSQL/src/main/java/de/example/sql/deadlocks/example/CustomAnnotationExample.java b/SpringJava/DeadLocksSQL/src/main/java/de/example/sql/deadlocks/example/CustomAnnotationExample.java
deleted file mode 100644 (file)
index 9eef75a..0000000
+++ /dev/null
@@ -1,17 +0,0 @@
-package de.example.sql.deadlocks.example;
-
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-import org.springframework.transaction.annotation.Transactional;
-
-import de.example.sql.deadlocks.annotation.DeadlockRetry;
-
-//@Transactional
-public class CustomAnnotationExample {
-       private static final Logger logger = LoggerFactory.getLogger(CustomAnnotationExample.class);
-       
-       @DeadlockRetry(maxTries = 10, interval = 5000)
-    public void doCustomAnnotationExample() {
-               logger.info("Running doCustomAnnotationExample");
-    } 
-}
diff --git a/SpringJava/DeadLocksSQL/src/main/java/de/example/sql/deadlocks/example/FirstTransaction.java b/SpringJava/DeadLocksSQL/src/main/java/de/example/sql/deadlocks/example/FirstTransaction.java
new file mode 100644 (file)
index 0000000..99c385c
--- /dev/null
@@ -0,0 +1,41 @@
+package de.example.sql.deadlocks.example;
+
+import javax.sql.DataSource;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.jdbc.core.JdbcOperations;
+import org.springframework.jdbc.core.JdbcTemplate;
+import org.springframework.transaction.annotation.Transactional;
+
+import de.example.sql.deadlocks.annotation.DeadlockRetry;
+
+@Transactional
+public class FirstTransaction {
+       private static final Logger logger = LoggerFactory.getLogger(FirstTransaction.class);
+       private DataSource dataSource;
+
+       @DeadlockRetry(maxTries = 10, interval = 5000)
+    public void doTransaction() {
+               logger.info("Running doTransaction");
+
+               final JdbcOperations jdbcTemplate = new JdbcTemplate(dataSource);
+        jdbcTemplate.execute("UPDATE children SET name='Bilbo', parent_id='2' WHERE id='1'");
+        jdbcTemplate.execute("UPDATE parents SET name='Smith' WHERE id='1'");
+
+        try {
+                       Thread.sleep(100000);
+               } catch (final InterruptedException e) {
+                       logger.warn("First transaction thread interrupt");
+
+                       // Restore interrupt status.
+                       Thread.currentThread().interrupt();
+               }
+
+        logger.info("Running endTransaction");
+    }
+
+       public void setDataSource(DataSource dataSource) {
+               this.dataSource = dataSource;
+       }
+}
diff --git a/SpringJava/DeadLocksSQL/src/main/java/de/example/sql/deadlocks/example/SecondTransaction.java b/SpringJava/DeadLocksSQL/src/main/java/de/example/sql/deadlocks/example/SecondTransaction.java
new file mode 100644 (file)
index 0000000..16cb126
--- /dev/null
@@ -0,0 +1,32 @@
+package de.example.sql.deadlocks.example;
+
+import javax.sql.DataSource;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.jdbc.core.JdbcOperations;
+import org.springframework.jdbc.core.JdbcTemplate;
+import org.springframework.transaction.annotation.Transactional;
+
+import de.example.sql.deadlocks.annotation.DeadlockRetry;
+
+@Transactional
+public class SecondTransaction {
+       private static final Logger logger = LoggerFactory.getLogger(SecondTransaction.class);
+       private DataSource dataSource;
+
+       @DeadlockRetry(maxTries = 10, interval = 5000)
+    public void doTransaction() {
+               logger.info("Running doTransaction");
+               
+               final JdbcOperations jdbcTemplate = new JdbcTemplate(dataSource);
+        jdbcTemplate.execute("UPDATE children SET name='Frodo', parent_id='2' WHERE id='2'");
+        jdbcTemplate.execute("UPDATE parents SET name='Smith' WHERE id='1'");
+        
+        logger.info("Running endTransaction");
+    }
+
+       public void setDataSource(DataSource dataSource) {
+               this.dataSource = dataSource;
+       } 
+}
index 7fc8b38..a3d6e2a 100644 (file)
@@ -29,6 +29,7 @@
     <aop:aspectj-autoproxy/>
 
 
+
     <!--
         Spring makes the nasty work for us (it searches the annotations) 
         See: http://static.springsource.org/spring/docs/3.1.0.RC1/spring-framework-reference/html/beans.html#beans-annotation-config
@@ -39,7 +40,7 @@
         @Component, @Repository, @Service, or @Controller because we do not
         need here that feature.
 
-        NOTA: VER SI ASPECTJ REALMENTE NECESITA ESTO. YO CREO, QUE NO!!!! PROBAR A BORRAR ESTO Y VER QUE PASA.
+        I do not really need the component-scan thing but I hope, it will improve performance (not sure)
     -->
     <context:component-scan base-package="de.example.sql.deadlocks" use-default-filters="false">
         <context:include-filter 
@@ -62,7 +63,7 @@
     -->
        <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource" destroy-method="close">
                 <property name="user" value="root"/>
-                <property name="password" value=""/>
+                <property name="password" value="root"/>
                 <property name="driverClass" value="com.mysql.jdbc.Driver"/>
                 <property name="jdbcUrl" value="jdbc:mysql://127.0.0.1:3306/example?autoReconnect=true"/>
                 <property name="initialPoolSize" value="5"/>
 
 
 
-    <bean id="customAnnotation" class="de.example.sql.deadlocks.example.CustomAnnotationExample" >
+    <bean id="firstTransaction" class="de.example.sql.deadlocks.example.FirstTransaction" >
+        <property name="dataSource" ref="dataSource"/>
+    </bean>
+
+    <bean id="secondTransaction" class="de.example.sql.deadlocks.example.SecondTransaction" >
+        <property name="dataSource" ref="dataSource"/>
     </bean>
  
 </beans>