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 com.atlassian.util.concurrent.ManagedLock;
7   import com.atlassian.util.concurrent.ManagedLocks;
8   import com.atlassian.util.concurrent.Supplier;
9   import org.slf4j.Logger;
10  import org.slf4j.LoggerFactory;
11  
12  import java.util.concurrent.TimeUnit;
13  import java.util.concurrent.atomic.AtomicInteger;
14  import java.util.concurrent.locks.Condition;
15  import java.util.concurrent.locks.Lock;
16  import java.util.concurrent.locks.ReadWriteLock;
17  import java.util.concurrent.locks.ReentrantLock;
18  import java.util.concurrent.locks.ReentrantReadWriteLock;
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(new Runnable() {
102             @Override
103             public void run() {
104                 delegate.save(state);
105             }
106         });
107         saveConcurrency.decrementAndGet();
108     }
109 
110     @Override
111     public PluginPersistentState load() {
112         final int reads = loadConcurrency.incrementAndGet();
113         log.debug("load concurrency {}", reads);
114         final PluginPersistentState pluginPersistentState = lock.read().withLock(new Supplier<PluginPersistentState>() {
115             @Override
116             public PluginPersistentState get() {
117                 return delegate.load();
118             }
119         });
120         loadConcurrency.decrementAndGet();
121         return pluginPersistentState;
122     }
123 
124     static class NoOpLock implements Lock {
125         @Override
126         public void lock() {
127         }
128 
129         @Override
130         public void lockInterruptibly() {
131         }
132 
133         @Override
134         public boolean tryLock() {
135             return true;
136         }
137 
138         @Override
139         public boolean tryLock(final long time, final TimeUnit unit) {
140             return true;
141         }
142 
143         @Override
144         public void unlock() {
145         }
146 
147         @Override
148         public Condition newCondition() {
149             throw new UnsupportedOperationException("Not implemented");
150         }
151     }
152 
153     static class CommonReadWriteLock implements ReadWriteLock {
154         private final Lock lock;
155 
156         public CommonReadWriteLock(final Lock lock) {
157             this.lock = lock;
158         }
159 
160         @Override
161         public Lock readLock() {
162             return lock;
163         }
164 
165         @Override
166         public Lock writeLock() {
167             return lock;
168         }
169     }
170 }