From: gu.martinm@gmail.com Date: Thu, 9 Oct 2014 11:14:59 +0000 (+0200) Subject: Data base deadlocks. How to deal with them (retries) X-Git-Url: https://git.gumartinm.name/?a=commitdiff_plain;h=eab9b906a5d7550f40b929a15ec8d76fddcf75ba;p=JavaForFun Data base deadlocks. How to deal with them (retries) --- diff --git a/SpringJava/DeadLocksSQL/src/main/database/example.sql b/SpringJava/DeadLocksSQL/src/main/database/example.sql new file mode 100644 index 0000000..498cfb6 --- /dev/null +++ b/SpringJava/DeadLocksSQL/src/main/database/example.sql @@ -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 diff --git a/SpringJava/DeadLocksSQL/src/main/java/de/example/sql/deadlocks/Main.java b/SpringJava/DeadLocksSQL/src/main/java/de/example/sql/deadlocks/Main.java index b545307..0a7c982 100644 --- a/SpringJava/DeadLocksSQL/src/main/java/de/example/sql/deadlocks/Main.java +++ b/SpringJava/DeadLocksSQL/src/main/java/de/example/sql/deadlocks/Main.java @@ -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 taskFirst = new FutureTask + ( + new Runnable(){ + + @Override + public void run() { + final FirstTransaction first = (FirstTransaction) SpringContextLocator.getInstance().getBean("firstTransaction"); + first.doTransaction(); + } + }, + null + ); + new Thread(taskFirst).start(); + + final FutureTask taskSecond = new FutureTask + ( + 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"); } } diff --git a/SpringJava/DeadLocksSQL/src/main/java/de/example/sql/deadlocks/annotation/DeadlockRetry.java b/SpringJava/DeadLocksSQL/src/main/java/de/example/sql/deadlocks/annotation/DeadlockRetry.java index ba8cc7d..f18c2b9 100644 --- a/SpringJava/DeadLocksSQL/src/main/java/de/example/sql/deadlocks/annotation/DeadlockRetry.java +++ b/SpringJava/DeadLocksSQL/src/main/java/de/example/sql/deadlocks/annotation/DeadlockRetry.java @@ -22,5 +22,5 @@ public @interface DeadlockRetry { * * @return interval in milliseconds */ - int interval() default 1000; + long interval() default 1000; } diff --git a/SpringJava/DeadLocksSQL/src/main/java/de/example/sql/deadlocks/aspect/DeadlockRetryAspect.java b/SpringJava/DeadLocksSQL/src/main/java/de/example/sql/deadlocks/aspect/DeadlockRetryAspect.java index 96c9ed2..c90d9a1 100644 --- a/SpringJava/DeadLocksSQL/src/main/java/de/example/sql/deadlocks/aspect/DeadlockRetryAspect.java +++ b/SpringJava/DeadLocksSQL/src/main/java/de/example/sql/deadlocks/aspect/DeadlockRetryAspect.java @@ -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 index 9eef75a..0000000 --- a/SpringJava/DeadLocksSQL/src/main/java/de/example/sql/deadlocks/example/CustomAnnotationExample.java +++ /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 index 0000000..99c385c --- /dev/null +++ b/SpringJava/DeadLocksSQL/src/main/java/de/example/sql/deadlocks/example/FirstTransaction.java @@ -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 index 0000000..16cb126 --- /dev/null +++ b/SpringJava/DeadLocksSQL/src/main/java/de/example/sql/deadlocks/example/SecondTransaction.java @@ -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; + } +} diff --git a/SpringJava/DeadLocksSQL/src/main/resources/spring-config.xml b/SpringJava/DeadLocksSQL/src/main/resources/spring-config.xml index 7fc8b38..a3d6e2a 100644 --- a/SpringJava/DeadLocksSQL/src/main/resources/spring-config.xml +++ b/SpringJava/DeadLocksSQL/src/main/resources/spring-config.xml @@ -29,6 +29,7 @@ + - + @@ -82,7 +83,12 @@ - + + + + + +