21
21
import com .google .common .cache .CacheBuilder ;
22
22
import com .google .common .cache .CacheLoader ;
23
23
import com .google .common .cache .LoadingCache ;
24
- import com .google .common .cache .Weigher ;
25
24
import com .uber .cadence .PollForDecisionTaskResponse ;
26
25
import com .uber .cadence .internal .common .ThrowableFunc1 ;
27
26
import com .uber .cadence .internal .metrics .MetricsType ;
28
27
import com .uber .m3 .tally .Scope ;
28
+ import java .util .Iterator ;
29
29
import java .util .Objects ;
30
30
import java .util .Random ;
31
- import java .util .UUID ;
31
+ import java .util .Set ;
32
32
import java .util .concurrent .TimeUnit ;
33
33
import java .util .concurrent .locks .Lock ;
34
34
import java .util .concurrent .locks .ReentrantLock ;
35
+ import org .slf4j .Logger ;
36
+ import org .slf4j .LoggerFactory ;
35
37
36
38
public final class DeciderCache {
37
- private final String evictionEntryId = UUID .randomUUID ().toString ();
38
- private final int maxCacheSize ;
39
39
private final Scope metricsScope ;
40
- private LoadingCache <String , WeightedCacheEntry < Decider > > cache ;
40
+ private LoadingCache <String , Decider > cache ;
41
41
private Lock evictionLock = new ReentrantLock ();
42
42
Random rand = new Random ();
43
43
44
+ private static final Logger log = LoggerFactory .getLogger (DeciderCache .class );
45
+
44
46
public DeciderCache (int maxCacheSize , Scope scope ) {
45
47
Preconditions .checkArgument (maxCacheSize > 0 , "Max cache size must be greater than 0" );
46
- this .maxCacheSize = maxCacheSize ;
47
48
this .metricsScope = Objects .requireNonNull (scope );
48
49
this .cache =
49
50
CacheBuilder .newBuilder ()
50
- .maximumWeight (maxCacheSize )
51
- .concurrencyLevel (1 )
52
- .weigher (
53
- (Weigher <String , WeightedCacheEntry <Decider >>) (key , value ) -> value .getWeight ())
51
+ .maximumSize (maxCacheSize )
54
52
.removalListener (
55
53
e -> {
56
- Decider entry = e .getValue (). entry ;
54
+ Decider entry = ( Decider ) e .getValue ();
57
55
if (entry != null ) {
58
56
entry .close ();
59
57
}
60
58
})
61
59
.build (
62
- new CacheLoader <String , WeightedCacheEntry < Decider > >() {
60
+ new CacheLoader <String , Decider >() {
63
61
@ Override
64
- public WeightedCacheEntry < Decider > load (String key ) {
62
+ public Decider load (String key ) {
65
63
return null ;
66
64
}
67
65
});
@@ -75,16 +73,14 @@ public Decider getOrCreate(
75
73
metricsScope .gauge (MetricsType .STICKY_CACHE_SIZE ).update (size ());
76
74
if (isFullHistory (decisionTask )) {
77
75
invalidate (decisionTask );
78
- return cache .get (
79
- runId , () -> new WeightedCacheEntry <>(createReplayDecider .apply (decisionTask ), 1 ))
80
- .entry ;
76
+ return cache .get (runId , () -> createReplayDecider .apply (decisionTask ));
81
77
}
82
78
return getUnchecked (runId );
83
79
}
84
80
85
81
public Decider getUnchecked (String runId ) throws Exception {
86
82
try {
87
- Decider cachedDecider = cache .getUnchecked (runId ). entry ;
83
+ Decider cachedDecider = cache .getUnchecked (runId );
88
84
metricsScope .counter (MetricsType .STICKY_CACHE_HIT ).inc (1 );
89
85
return cachedDecider ;
90
86
} catch (CacheLoader .InvalidCacheLoadException e ) {
@@ -93,22 +89,32 @@ public Decider getUnchecked(String runId) throws Exception {
93
89
}
94
90
}
95
91
96
- public void evictNext ( ) throws InterruptedException {
92
+ public void evictAny ( String runId ) throws InterruptedException {
97
93
// Timeout is to guard against workflows trying to evict each other.
98
94
if (!evictionLock .tryLock (rand .nextInt (4 ), TimeUnit .SECONDS )) {
99
95
return ;
100
96
}
101
97
try {
102
98
metricsScope .gauge (MetricsType .STICKY_CACHE_SIZE ).update (size ());
103
- int remainingSpace = (int ) (maxCacheSize - cache .size ());
104
- // Force eviction to happen. This assumes a concurrency level of 1 which implies a single
105
- // underlying segment and lock. If higher concurrency levels are assumed this may not work
106
- // since
107
- // the weight could be greater than the segment size and put will simply noop.
108
- // ConcurrenyLevel limits cache modification but reads and cache loading computations still
109
- // have concurrently.
110
- cache .put (evictionEntryId , new WeightedCacheEntry <>(null , remainingSpace + 1 ));
111
- invalidate (evictionEntryId );
99
+ Set <String > set = cache .asMap ().keySet ();
100
+ if (set .isEmpty ()) {
101
+ return ;
102
+ }
103
+ Iterator <String > iter = cache .asMap ().keySet ().iterator ();
104
+ String key = "" ;
105
+ while (iter .hasNext ()) {
106
+ key = iter .next ();
107
+ if (!key .equals (runId )) {
108
+ break ;
109
+ }
110
+ }
111
+
112
+ if (key .equals (runId )) {
113
+ log .warn (String .format ("%s attempted to self evict. Ignoring eviction" , runId ));
114
+ return ;
115
+ }
116
+ cache .invalidate (key );
117
+ metricsScope .gauge (MetricsType .STICKY_CACHE_SIZE ).update (size ());
112
118
metricsScope .counter (MetricsType .STICKY_CACHE_THREAD_FORCED_EVICTION ).inc (1 );
113
119
} finally {
114
120
evictionLock .unlock ();
@@ -146,25 +152,6 @@ public void invalidateAll() {
146
152
cache .invalidateAll ();
147
153
}
148
154
149
- // Used for eviction
150
- private static class WeightedCacheEntry <T > {
151
- private T entry ;
152
- private int weight ;
153
-
154
- private WeightedCacheEntry (T entry , int weight ) {
155
- this .entry = entry ;
156
- this .weight = weight ;
157
- }
158
-
159
- public T getEntry () {
160
- return entry ;
161
- }
162
-
163
- public int getWeight () {
164
- return weight ;
165
- }
166
- }
167
-
168
155
public static class EvictedException extends Exception {
169
156
170
157
public EvictedException (String runId ) {
0 commit comments