15
15
import org .elasticsearch .common .settings .Settings ;
16
16
import org .elasticsearch .common .unit .ByteSizeUnit ;
17
17
import org .elasticsearch .common .unit .ByteSizeValue ;
18
+ import org .elasticsearch .common .util .concurrent .ReleasableLock ;
18
19
19
20
import java .nio .file .Files ;
20
21
import java .nio .file .Path ;
22
+ import java .util .concurrent .locks .ReentrantReadWriteLock ;
21
23
import java .util .function .Predicate ;
22
24
23
25
/**
@@ -31,6 +33,8 @@ public class CacheService extends AbstractLifecycleComponent {
31
33
new ByteSizeValue (Long .MAX_VALUE , ByteSizeUnit .BYTES ), // max
32
34
Setting .Property .NodeScope );
33
35
36
+ private final ReleasableLock cacheInvalidationLock ;
37
+ private final ReleasableLock cacheAccessLock ;
34
38
private final Cache <String , CacheFile > cache ;
35
39
36
40
public CacheService (final Settings settings ) {
@@ -41,6 +45,13 @@ public CacheService(final Settings settings) {
41
45
// are done with reading/writing the cache file
42
46
.removalListener (notification -> Releasables .closeWhileHandlingException (notification .getValue ()))
43
47
.build ();
48
+
49
+ // Prevent new CacheFile objects to be added to the cache while the cache is being fully or partially invalidated
50
+ // This can happen because CacheFile objects might execute listeners at eviction time, potentially forcing more
51
+ // objects (like CacheDirectory's index inputs) to get new CacheFile.
52
+ final ReentrantReadWriteLock cacheLock = new ReentrantReadWriteLock ();
53
+ this .cacheInvalidationLock = new ReleasableLock (cacheLock .writeLock ());
54
+ this .cacheAccessLock = new ReleasableLock (cacheLock .readLock ());
44
55
}
45
56
46
57
@ Override
@@ -50,7 +61,9 @@ protected void doStart() {
50
61
51
62
@ Override
52
63
protected void doStop () {
53
- cache .invalidateAll ();
64
+ try (ReleasableLock ignored = cacheInvalidationLock .acquire ()) {
65
+ cache .invalidateAll ();
66
+ }
54
67
}
55
68
56
69
@ Override
@@ -66,17 +79,18 @@ private void ensureLifecycleStarted() {
66
79
67
80
public CacheFile get (final String name , final long length , final Path file ) throws Exception {
68
81
ensureLifecycleStarted ();
69
- return cache . computeIfAbsent ( toCacheKey ( file ), key -> {
82
+ try ( ReleasableLock ignored = cacheAccessLock . acquire ()) {
70
83
ensureLifecycleStarted ();
84
+ return cache .computeIfAbsent (toCacheKey (file ), key -> {
85
+ // generate a random UUID for the name of the cache file on disk
86
+ final String uuid = UUIDs .randomBase64UUID ();
87
+ // resolve the cache file on disk w/ the expected cached file
88
+ final Path path = file .getParent ().resolve (uuid );
89
+ assert Files .notExists (path ) : "cache file already exists " + path ;
71
90
72
- // generate a random UUID for the name of the cache file on disk
73
- final String uuid = UUIDs .randomBase64UUID ();
74
- // resolve the cache file on disk w/ the expected cached file
75
- final Path path = file .getParent ().resolve (uuid );
76
- assert Files .notExists (path ) : "cache file already exists " + path ;
77
-
78
- return new CacheFile (name , length , path );
79
- });
91
+ return new CacheFile (name , length , path );
92
+ });
93
+ }
80
94
}
81
95
82
96
/**
0 commit comments