View Javadoc

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