27
27
import com .uber .cadence .internal .metrics .MetricsType ;
28
28
import com .uber .m3 .tally .Scope ;
29
29
import java .util .Objects ;
30
+ import java .util .Random ;
30
31
import java .util .UUID ;
31
- import org .slf4j .Logger ;
32
- import org .slf4j .LoggerFactory ;
32
+ import java .util .concurrent .TimeUnit ;
33
+ import java .util .concurrent .locks .Lock ;
34
+ import java .util .concurrent .locks .ReentrantLock ;
33
35
34
36
public final class DeciderCache {
35
37
private final String evictionEntryId = UUID .randomUUID ().toString ();
36
38
private final int maxCacheSize ;
37
39
private final Scope metricsScope ;
38
40
private LoadingCache <String , WeightedCacheEntry <Decider >> cache ;
39
- private static final Logger log = LoggerFactory .getLogger (DeciderCache .class );
41
+ private Lock evictionLock = new ReentrantLock ();
42
+ Random rand = new Random ();
40
43
41
44
public DeciderCache (int maxCacheSize , Scope scope ) {
42
45
Preconditions .checkArgument (maxCacheSize > 0 , "Max cache size must be greater than 0" );
@@ -45,6 +48,7 @@ public DeciderCache(int maxCacheSize, Scope scope) {
45
48
this .cache =
46
49
CacheBuilder .newBuilder ()
47
50
.maximumWeight (maxCacheSize )
51
+ .concurrencyLevel (1 )
48
52
.weigher (
49
53
(Weigher <String , WeightedCacheEntry <Decider >>) (key , value ) -> value .getWeight ())
50
54
.removalListener (
@@ -70,7 +74,7 @@ public Decider getOrCreate(
70
74
String runId = decisionTask .getWorkflowExecution ().getRunId ();
71
75
metricsScope .gauge (MetricsType .STICKY_CACHE_SIZE ).update (size ());
72
76
if (isFullHistory (decisionTask )) {
73
- cache . invalidate (runId );
77
+ invalidate (decisionTask );
74
78
return cache .get (
75
79
runId , () -> new WeightedCacheEntry <>(createReplayDecider .apply (decisionTask ), 1 ))
76
80
.entry ;
@@ -89,22 +93,43 @@ public Decider getUnchecked(String runId) throws Exception {
89
93
}
90
94
}
91
95
92
- public void evictNext () {
93
- metricsScope .gauge (MetricsType .STICKY_CACHE_SIZE ).update (size ());
94
- int remainingSpace = (int ) (maxCacheSize - cache .size ());
95
- // Force eviction to happen
96
- cache .put (evictionEntryId , new WeightedCacheEntry <>(null , remainingSpace + 1 ));
97
- invalidate (evictionEntryId );
96
+ public void evictNext () throws InterruptedException {
97
+ // Timeout is to guard against workflows trying to evict each other.
98
+ if (!evictionLock .tryLock (rand .nextInt (4 ), TimeUnit .SECONDS )) {
99
+ return ;
100
+ }
101
+ try {
102
+ 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 );
112
+ metricsScope .counter (MetricsType .STICKY_CACHE_THREAD_FORCED_EVICTION ).inc (1 );
113
+ } finally {
114
+ evictionLock .unlock ();
115
+ }
98
116
}
99
117
100
- public void invalidate (PollForDecisionTaskResponse decisionTask ) {
118
+ public void invalidate (PollForDecisionTaskResponse decisionTask ) throws InterruptedException {
101
119
String runId = decisionTask .getWorkflowExecution ().getRunId ();
102
120
invalidate (runId );
103
121
}
104
122
105
- public void invalidate (String runId ) {
106
- metricsScope .counter (MetricsType .STICKY_CACHE_TOTAL_FORCED_EVICTION ).inc (1 );
107
- cache .invalidate (runId );
123
+ private void invalidate (String runId ) throws InterruptedException {
124
+ if (!evictionLock .tryLock (rand .nextInt (4 ), TimeUnit .SECONDS )) {
125
+ return ;
126
+ }
127
+ try {
128
+ cache .invalidate (runId );
129
+ metricsScope .counter (MetricsType .STICKY_CACHE_TOTAL_FORCED_EVICTION ).inc (1 );
130
+ } finally {
131
+ evictionLock .unlock ();
132
+ }
108
133
}
109
134
110
135
public long size () {
0 commit comments