View Javadoc

1   package com.atlassian.cache.hazelcast;
2   
3   import java.util.Random;
4   
5   import com.atlassian.cache.CacheSettings;
6   
7   import com.hazelcast.config.EvictionPolicy;
8   import com.hazelcast.config.MapConfig;
9   import com.hazelcast.config.MaxSizeConfig;
10  import com.hazelcast.config.NearCacheConfig;
11  
12  import static com.hazelcast.config.MaxSizeConfig.MaxSizePolicy.PER_NODE;
13  
14  /**
15   * Performs the (re)configuration of Hazelcast {@link MapConfig} objects.
16   *
17   * @since 2.4.0
18   */
19  public class HazelcastMapConfigConfigurator
20  {
21      // If the cache is a hybrid cache, only the entry versions are tracked in the IMap. These versions should remain
22      // cached longer than the values in local caches. A multiplier is applied to the config parameters that affect
23      // cache eviction to enforce this.
24      public static final int HYBRID_MULTIPLIER = 2;
25  
26      public static final int NEAR_CACHE_EXPIRY_RATIO = Integer.getInteger("atlassian.cache.nearCacheExpiryRatio", 75);
27  
28      public MapConfig configureMapConfig(CacheSettings settings, MapConfig mapConfig)
29      {
30          boolean hybrid = !settings.getReplicateViaCopy(true);
31          Integer multiplier = hybrid ? HYBRID_MULTIPLIER : 1;
32          Integer maxEntries = settings.getMaxEntries();
33  
34          final NearCacheConfig nearCacheConfig = mapConfig.getNearCacheConfig() == null ?
35                  new NearCacheConfig() :
36                  new NearCacheConfig(mapConfig.getNearCacheConfig());
37  
38          if (maxEntries != null)
39          {
40              final int maxSize = multiplier * maxEntries;
41  
42              mapConfig.setMaxSizeConfig(new MaxSizeConfig().setMaxSizePolicy(PER_NODE).setSize(maxSize));
43              mapConfig.setEvictionPolicy(EvictionPolicy.LFU);
44  
45              nearCacheConfig.setMaxSize(maxSize);
46              nearCacheConfig.setEvictionPolicy(EvictionPolicy.LFU.name());
47          }
48  
49          final Long expireAfterAccess = settings.getExpireAfterAccess();
50          if (expireAfterAccess != null)
51          {
52              final int maxIdleSeconds = multiplier * roundUpToWholeSeconds(expireAfterAccess);
53  
54              mapConfig.setMaxIdleSeconds(maxIdleSeconds);
55  
56              // Near-cache hits don't reset the last-accessed timestamp on the underlying IMap entry.
57              // So make the near cache have a TTL that is less than the maxIdle of the IMap:
58              //
59              // nearCacheTTL = (0.75 + jitter) * maxIdleSeconds
60              //
61              // That way the near cache entry will always expire before the IMap - calling back through
62              // and refreshing the IMap idle timer. We also add some random jitter (+ or - 0.15) so all nodes don't expire
63              // at the same time.
64  
65              int jitter = new Random().nextInt(30) - 15;
66              int nearCacheTtl = (int) Math.round(((NEAR_CACHE_EXPIRY_RATIO + jitter) * maxIdleSeconds) / 100.0);
67              nearCacheConfig.setTimeToLiveSeconds(Math.max(1, nearCacheTtl));
68          }
69  
70          final Long expireAfterWrite = settings.getExpireAfterWrite();
71          if (expireAfterWrite != null)
72          {
73              final int timeToLiveSeconds = multiplier * roundUpToWholeSeconds(expireAfterWrite);
74  
75              mapConfig.setTimeToLiveSeconds(timeToLiveSeconds);
76  
77              nearCacheConfig.setTimeToLiveSeconds(timeToLiveSeconds);
78          }
79  
80          final boolean nearCache = settings.getReplicateAsynchronously(true);
81          if (nearCache)
82          {
83              mapConfig.setNearCacheConfig(nearCacheConfig);
84          }
85          else
86          {
87              mapConfig.setNearCacheConfig(null);
88          }
89  
90          return mapConfig;
91      }
92  
93      private static int roundUpToWholeSeconds(final Long expireAfterAccess)
94      {
95          return (int) Math.ceil(expireAfterAccess / 1000d);
96      }
97  }