Skip to content

Commit 94ea101

Browse files
committed
bring along ReadAheadService for early start IO
1 parent eb32843 commit 94ea101

File tree

2 files changed

+110
-1
lines changed

2 files changed

+110
-1
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
package org.eclipse.jifa.server.service.impl.netflix;
2+
3+
import java.io.FileInputStream;
4+
import java.io.IOException;
5+
import java.nio.file.Path;
6+
import java.time.Instant;
7+
import java.util.concurrent.atomic.AtomicInteger;
8+
9+
import org.springframework.stereotype.Component;
10+
11+
import java.util.concurrent.ExecutorService;
12+
import java.util.concurrent.Executors;
13+
import java.util.concurrent.ConcurrentHashMap;
14+
import java.util.concurrent.ConcurrentMap;
15+
import lombok.extern.slf4j.Slf4j;
16+
17+
@Component
18+
@Slf4j
19+
public class ReadAheadService {
20+
final static int CHUNK_SIZE = 8*1024*1024;
21+
final static int READ_THREADS = 16;
22+
23+
final static int MAX_AGE_SECONDS = 300;
24+
final static AtomicInteger threadCount = new AtomicInteger(0);
25+
final ExecutorService readers = Executors.newFixedThreadPool(READ_THREADS, r -> new Thread(r, "ReadAheader-" + threadCount.getAndIncrement()));
26+
27+
final ConcurrentMap<String, Instant> readAheadRequests = new ConcurrentHashMap<>();
28+
29+
static final String[] INDEXES = new String[]{
30+
// this assumes the files are all based on heapdump.hprof
31+
32+
// any order
33+
"heapdump.i2sv2.index", // retained size cache (histogram & leak suspects)
34+
"heapdump.domOut.index", // dominator tree outbound
35+
"heapdump.o2ret.index", // retained size for object
36+
"heapdump.o2hprof.index", // file pointer
37+
"heapdump.o2c.index", // class id for object
38+
"heapdump.a2s.index", // array to size
39+
"heapdump.inbound.index", // inbound pointers for an object
40+
"heapdump.outbound.index", // outbound pointers for object
41+
"heapdump.domIn.index", // dominator tree inbound
42+
43+
// last
44+
// "heapdump.idx.index",
45+
// "heapdump.index",
46+
47+
// of course, also the hprof
48+
// "heapdump.hprof"
49+
};
50+
51+
// the readahead request is likely to come through concurrently, so maybe
52+
// it will occur again before completing
53+
// note that because we use standard CompletableFuture it will run on fj pool
54+
55+
public ReadAheadService() {
56+
log.info("init(): setting up readahead, read_threads = {}, chunk_size = {}, max_age_seconds = {}",
57+
READ_THREADS, CHUNK_SIZE, MAX_AGE_SECONDS);
58+
}
59+
60+
public synchronized void issueForPath(Path hprofFile) {
61+
Path path = hprofFile.getParent();
62+
for (String index : INDEXES) {
63+
issueReadFile(path.resolve(index));
64+
}
65+
}
66+
67+
void issueReadFile(final Path file) {
68+
String filename = file.toString();
69+
Instant existingRequest = readAheadRequests.get(filename);
70+
if (needsLoad(existingRequest)) {
71+
readAheadRequests.put(filename, Instant.now());
72+
log.info("issueReadFile(): issuing for {}", filename);
73+
readers.submit(() -> {
74+
readAheadRequests.put(filename, Instant.now());
75+
76+
log.info("issueReadFile(): issued for {}", filename);
77+
try {
78+
byte[] chunk = new byte[CHUNK_SIZE];
79+
FileInputStream fis = new FileInputStream(filename);
80+
while(fis.read(chunk) > 0) {
81+
// read again
82+
}
83+
log.info("issueReadFile(): completed for {}", filename);
84+
} catch (IOException e) {
85+
log.info("issueReadFile(): failed for {}, {}", filename, e);
86+
}
87+
88+
readAheadRequests.put(filename, Instant.now());
89+
});
90+
}
91+
}
92+
93+
boolean needsLoad(Instant instant) {
94+
if (instant == null) {
95+
return true;
96+
}
97+
98+
Instant OLDEST_OK_AGE = Instant.now().minusSeconds(MAX_AGE_SECONDS);
99+
if (instant.isBefore(OLDEST_OK_AGE)) {
100+
return true;
101+
}
102+
103+
return false;
104+
}
105+
}

server/src/main/java/org/eclipse/jifa/server/service/impl/netflix/ReadOnlyFileServiceImpl.java

+5-1
Original file line numberDiff line numberDiff line change
@@ -48,10 +48,12 @@ public class ReadOnlyFileServiceImpl extends ConfigurationAccessor implements Fi
4848
private Path basePath;
4949
private final FileAccessService fileUserAccessService;
5050
private final PathLookupService pathLookupService;
51+
private final ReadAheadService readAheadService;
5152

52-
public ReadOnlyFileServiceImpl(FileAccessService fileUserAccessService, PathLookupService pathLookupService) {
53+
public ReadOnlyFileServiceImpl(FileAccessService fileUserAccessService, PathLookupService pathLookupService, ReadAheadService readAheadService) {
5354
this.fileUserAccessService = fileUserAccessService;
5455
this.pathLookupService = pathLookupService;
56+
this.readAheadService = readAheadService;
5557
}
5658

5759
@PostConstruct
@@ -111,6 +113,8 @@ public FileEntity getFileByUniqueName(String uniqueName, FileType expectedFileTy
111113

112114
fileUserAccessService.checkAuthorityForCurrentUser(file);
113115

116+
readAheadService.issueForPath(realPath);
117+
114118
return file;
115119
} catch (IOException e) {
116120
throw new IllegalArgumentException("file information not present");

0 commit comments

Comments
 (0)