/blueprints-core/src/main/java/com/tinkerpop/blueprints/util/TransactionRetryStrategy.java

https://github.com/pangloss/blueprints · Java · 179 lines · 112 code · 33 blank · 34 comment · 8 complexity · b527b721df374114d4f80d650798e8ab MD5 · raw file

  1. package com.tinkerpop.blueprints.util;
  2. import com.tinkerpop.blueprints.TransactionalGraph;
  3. import java.util.HashSet;
  4. import java.util.Set;
  5. /**
  6. * The strategy for executing a transaction.
  7. *
  8. * @author Stephen Mallette (http://stephen.genoprime.com)
  9. */
  10. public interface TransactionRetryStrategy<T> {
  11. /**
  12. * Executes the TransactionWork with a give strategy.
  13. *
  14. * @param graph The TransactionalGraph to mutate.
  15. * @param work The work to do on the Graph.
  16. * @return The return value from TransactionWork.
  17. */
  18. public T execute(TransactionalGraph graph, TransactionWork<T> work);
  19. /**
  20. * Executes the work committing if possible and rolling back on failure. On failure, an exception is reported.
  21. */
  22. public static class OneAndDone<T> implements TransactionRetryStrategy<T> {
  23. public T execute(final TransactionalGraph graph, final TransactionWork<T> work) {
  24. T returnValue;
  25. try {
  26. returnValue = work.execute(graph);
  27. graph.commit();
  28. } catch (Exception e) {
  29. graph.rollback();
  30. throw new RuntimeException(e);
  31. }
  32. return returnValue;
  33. }
  34. }
  35. /**
  36. * Executes the work committing if possible and rolling back on failure. On failure no exception is reported.
  37. */
  38. public static class FireAndForget<T> implements TransactionRetryStrategy<T> {
  39. public T execute(final TransactionalGraph graph, final TransactionWork<T> work) {
  40. T returnValue = null;
  41. try {
  42. returnValue = work.execute(graph);
  43. graph.commit();
  44. } catch (Exception e) {
  45. graph.rollback();
  46. }
  47. return returnValue;
  48. }
  49. }
  50. /**
  51. * Executes the work with a number of retries and with a number of milliseconds delay between each try.
  52. */
  53. public static class DelayedRetry<T> extends AbstractRetryStrategy<T> {
  54. public static final long DEFAULT_DELAY_MS = 20;
  55. private final long delayBetweenRetry;
  56. public DelayedRetry() {
  57. this(DEFAULT_TRIES, DEFAULT_DELAY_MS);
  58. }
  59. public DelayedRetry(final int tries, final long delayBetweenRetry) {
  60. this(tries, delayBetweenRetry, new HashSet<Class>());
  61. }
  62. public DelayedRetry(final int tries, final long delayBetweenRetry, final Set<Class> exceptionsToRetryOn) {
  63. super(tries, exceptionsToRetryOn);
  64. this.delayBetweenRetry = delayBetweenRetry;
  65. }
  66. @Override
  67. long getDelay(final int retryCount) {
  68. return this.delayBetweenRetry;
  69. }
  70. }
  71. /**
  72. * Executes the work with a number of retries and with a exponentially increasing number of milliseconds
  73. * between each retry.
  74. */
  75. public static class ExponentialBackoff<T> extends AbstractRetryStrategy<T> {
  76. public static final long DEFAULT_DELAY_MS = 20;
  77. private final long initialDelay;
  78. public ExponentialBackoff() {
  79. this(DEFAULT_TRIES, DEFAULT_DELAY_MS);
  80. }
  81. public ExponentialBackoff(final int tries, final long initialDelay) {
  82. this(tries, initialDelay, new HashSet<Class>());
  83. }
  84. public ExponentialBackoff(final int tries, final long initialDelay, final Set<Class> exceptionsToRetryOn) {
  85. super(tries, exceptionsToRetryOn);
  86. this.initialDelay = initialDelay;
  87. }
  88. @Override
  89. long getDelay(final int retryCount) {
  90. return (long) (initialDelay * Math.pow(2, retryCount));
  91. }
  92. }
  93. /**
  94. * Base class for strategy that require a retry.
  95. */
  96. public static abstract class AbstractRetryStrategy<T> implements TransactionRetryStrategy<T> {
  97. public static final int DEFAULT_TRIES = 8;
  98. protected final int tries;
  99. protected final Set<Class> exceptionsToRetryOn;
  100. public AbstractRetryStrategy() {
  101. this(DEFAULT_TRIES, new HashSet<Class>());
  102. }
  103. public AbstractRetryStrategy(final int tries, final Set<Class> exceptionsToRetryOn) {
  104. this.tries = tries;
  105. this.exceptionsToRetryOn = exceptionsToRetryOn;
  106. }
  107. abstract long getDelay(int retryCount);
  108. public T execute(final TransactionalGraph graph, final TransactionWork<T> work) {
  109. T returnValue;
  110. // this is the default exception...it may get reassgined during retries
  111. Exception previousException = new RuntimeException("Exception initialized when trying commit.");
  112. // try to commit a few times
  113. for (int ix = 0; ix < tries; ix++) {
  114. // increase time after each failed attempt though there is no delay on the first try
  115. if (ix > 0)
  116. try { Thread.sleep(getDelay(ix)); } catch (InterruptedException ie) { }
  117. try {
  118. returnValue = work.execute(graph);
  119. graph.commit();
  120. // need to exit the function here so that retries don't happen
  121. return returnValue;
  122. } catch (Exception ex) {
  123. graph.rollback();
  124. // retry if this is an allowed exception otherwise, just throw and go
  125. boolean retry = false;
  126. if (this.exceptionsToRetryOn.size() == 0)
  127. retry = true;
  128. else {
  129. for (Class exceptionToRetryOn : this.exceptionsToRetryOn) {
  130. if (ex.getClass().equals(exceptionToRetryOn)) {
  131. retry = true;
  132. break;
  133. }
  134. }
  135. }
  136. if (!retry) {
  137. throw new RuntimeException(ex);
  138. }
  139. previousException = ex;
  140. }
  141. }
  142. // the exception just won't go away after all the retries
  143. throw new RuntimeException(previousException);
  144. }
  145. }
  146. }