View Javadoc
1   package com.atlassian.plugin.manager.store;
2   
3   import com.atlassian.annotations.Internal;
4   import com.atlassian.plugin.manager.PluginPersistentState;
5   import com.atlassian.plugin.manager.PluginPersistentStateStore;
6   import io.atlassian.util.concurrent.ManagedLock;
7   import io.atlassian.util.concurrent.ManagedLocks;
8   import org.slf4j.Logger;
9   import org.slf4j.LoggerFactory;
10  
11  import java.util.concurrent.TimeUnit;
12  import java.util.concurrent.atomic.AtomicInteger;
13  import java.util.concurrent.locks.Condition;
14  import java.util.concurrent.locks.Lock;
15  import java.util.concurrent.locks.ReadWriteLock;
16  import java.util.concurrent.locks.ReentrantLock;
17  import java.util.concurrent.locks.ReentrantReadWriteLock;
18  import java.util.function.Supplier;
19  
20  import static com.atlassian.plugin.util.EnumUtils.enumValueFromProperty;
21  import static com.google.common.base.Preconditions.checkNotNull;
22  
23  /**
24   * A wrapper to add exclusion and logging to a {@link PluginPersistentStateStore}.
25   *
26   * This wrapper is intended as an interim measure to investigate and experiment with locking to avoid the worst symptoms of
27   * PLUG-1106. It's not designed to fix the underlying causes of the concurrency issues.
28   *
29   * @since 3.2.12
30   */
31  public class SynchronizedPluginPersistentStateStore implements PluginPersistentStateStore {
32      private static final Logger log = LoggerFactory.getLogger(SynchronizedPluginPersistentStateStore.class);
33  
34      @Internal
35      public enum LockMode {
36          UNLOCKED {
37              @Override
38              ReadWriteLock getReadWriteLock() {
39                  return new CommonReadWriteLock(new NoOpLock());
40              }
41          },
42          SIMPLE {
43              @Override
44              ReadWriteLock getReadWriteLock() {
45                  return new CommonReadWriteLock(new ReentrantLock());
46              }
47  
48          },
49          FAIRSIMPLE {
50              @Override
51              ReadWriteLock getReadWriteLock() {
52                  return new CommonReadWriteLock(new ReentrantLock(true));
53              }
54          },
55          READWRITE {
56              @Override
57              ReadWriteLock getReadWriteLock() {
58                  return new ReentrantReadWriteLock();
59              }
60          },
61          FAIRREADWRITE {
62              @Override
63              ReadWriteLock getReadWriteLock() {
64                  return new ReentrantReadWriteLock(true);
65              }
66          };
67  
68          private static final String PROPERTY_NAME = SynchronizedPluginPersistentStateStore.class.getName() + ".lockMode";
69  
70          static LockMode current() {
71              return enumValueFromProperty(PROPERTY_NAME, LockMode.values(), LockMode.READWRITE);
72          }
73  
74          abstract ReadWriteLock getReadWriteLock();
75  
76      }
77  
78      @Internal
79      public static String getLockModeProperty() {
80          return LockMode.PROPERTY_NAME;
81      }
82  
83      private final PluginPersistentStateStore delegate;
84      private final ManagedLock.ReadWrite lock;
85      private final AtomicInteger loadConcurrency = new AtomicInteger(0);
86      private final AtomicInteger saveConcurrency = new AtomicInteger(0);
87  
88      public SynchronizedPluginPersistentStateStore(final PluginPersistentStateStore delegate) {
89          this(delegate, LockMode.current().getReadWriteLock());
90      }
91  
92      public SynchronizedPluginPersistentStateStore(final PluginPersistentStateStore delegate, final ReadWriteLock lock) {
93          this.delegate = checkNotNull(delegate);
94          this.lock = ManagedLocks.manageReadWrite(checkNotNull(lock));
95      }
96  
97      @Override
98      public void save(final PluginPersistentState state) {
99          final int writes = saveConcurrency.incrementAndGet();
100         log.debug("save concurrency {}", writes);
101         lock.write().withLock(() -> delegate.save(state));
102         saveConcurrency.decrementAndGet();
103     }
104 
105     @Override
106     public PluginPersistentState load() {
107         final int reads = loadConcurrency.incrementAndGet();
108         log.debug("load concurrency {}", reads);
109         final PluginPersistentState pluginPersistentState = lock.read().withLock((Supplier<PluginPersistentState>) delegate::load);
110         loadConcurrency.decrementAndGet();
111         return pluginPersistentState;
112     }
113 
114     static class NoOpLock implements Lock {
115         @Override
116         public void lock() {
117         }
118 
119         @Override
120         public void lockInterruptibly() {
121         }
122 
123         @Override
124         public boolean tryLock() {
125             return true;
126         }
127 
128         @Override
129         public boolean tryLock(final long time, final TimeUnit unit) {
130             return true;
131         }
132 
133         @Override
134         public void unlock() {
135         }
136 
137         @Override
138         public Condition newCondition() {
139             throw new UnsupportedOperationException("Not implemented");
140         }
141     }
142 
143     static class CommonReadWriteLock implements ReadWriteLock {
144         private final Lock lock;
145 
146         public CommonReadWriteLock(final Lock lock) {
147             this.lock = lock;
148         }
149 
150         @Override
151         public Lock readLock() {
152             return lock;
153         }
154 
155         @Override
156         public Lock writeLock() {
157             return lock;
158         }
159     }
160 }