Skip to content

Commit 7d52eab

Browse files
committed
delta-based index cache: added compacting of cache store
1 parent 2e7db46 commit 7d52eab

File tree

2 files changed

+92
-6
lines changed

2 files changed

+92
-6
lines changed

headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/index/cache/IndexCacheOnDiscDeltaBased.java

Lines changed: 40 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,10 @@ public class IndexCacheOnDiscDeltaBased implements IndexCache {
7474

7575
private final File cacheDirectory;
7676
private final Map<IndexCacheKey, ConcurrentMap<InternalFileIdentifier, Long>> timestamps;
77+
private final Map<IndexCacheKey, Integer> compactingCounter;
78+
private final int compactingCounterBoundary;
79+
80+
private static final int DEFAULT_COMPACTING_TRIGGER = 20;
7781

7882
private static final Logger log = LoggerFactory.getLogger(IndexCacheOnDiscDeltaBased.class);
7983

@@ -89,6 +93,8 @@ public IndexCacheOnDiscDeltaBased(File cacheDirectory) {
8993
}
9094

9195
this.timestamps = new ConcurrentHashMap<>();
96+
this.compactingCounter = new ConcurrentHashMap<>();
97+
this.compactingCounterBoundary = DEFAULT_COMPACTING_TRIGGER;
9298
}
9399

94100
@Override
@@ -116,6 +122,9 @@ public <T extends IndexCacheable> void store(IndexCacheKey cacheKey, String[] fi
116122
ConcurrentMap<InternalFileIdentifier, Long> timestampMap = timestampedFiles.entrySet().stream()
117123
.collect(Collectors.toConcurrentMap(e -> InternalFileIdentifier.fromPath(e.getKey()), e -> e.getValue()));
118124
this.timestamps.put(cacheKey, timestampMap);
125+
126+
this.compactingCounter.put(cacheKey, 0);
127+
deleteOutdatedCacheFiles(cacheKey);
119128
}
120129

121130
@SuppressWarnings("unchecked")
@@ -124,7 +133,8 @@ public <T extends IndexCacheable> Pair<T[], Multimap<String, String>> retrieve(I
124133
File cacheStore = new File(cacheDirectory, cacheKey.toString() + ".json");
125134
if (cacheStore.exists()) {
126135

127-
IndexCacheStore<T> store = retrieveStoreFromIncrementalStorage(cacheKey, type);
136+
Pair<IndexCacheStore<T>, Integer> result = retrieveStoreFromIncrementalStorage(cacheKey, type);
137+
IndexCacheStore<T> store = result.getLeft();
128138

129139
SortedMap<String, Long> timestampedFiles = Arrays.stream(files)
130140
.filter(file -> new File(file).exists())
@@ -153,6 +163,8 @@ public <T extends IndexCacheable> Pair<T[], Multimap<String, String>> retrieve(I
153163
ConcurrentMap<InternalFileIdentifier, Long> timestampMap = timestampedFiles.entrySet().stream()
154164
.collect(Collectors.toConcurrentMap(e -> InternalFileIdentifier.fromPath(e.getKey()), e -> e.getValue()));
155165
this.timestamps.put(cacheKey, timestampMap);
166+
this.compactingCounter.put(cacheKey, result.getRight());
167+
compact(cacheKey, type);
156168

157169
return Pair.of(
158170
(T[]) symbols.toArray((T[]) Array.newInstance(type, symbols.size())),
@@ -179,6 +191,9 @@ public <T extends IndexCacheable> void removeFiles(IndexCacheKey cacheKey, Strin
179191
timestampsMap.remove(InternalFileIdentifier.fromPath(file));
180192
}
181193
}
194+
195+
this.compactingCounter.merge(cacheKey, 1, Integer::sum);
196+
compact(cacheKey, type);
182197
}
183198

184199
@Override
@@ -190,6 +205,7 @@ public void remove(IndexCacheKey cacheKey) {
190205

191206
// update local timestamp cache
192207
this.timestamps.remove(cacheKey);
208+
this.compactingCounter.remove(cacheKey);
193209
}
194210

195211
@Override
@@ -212,6 +228,8 @@ public <T extends IndexCacheable> void update(IndexCacheKey cacheKey, String fil
212228
// update local timestamp cache
213229
Map<InternalFileIdentifier, Long> timestampsMap = this.timestamps.computeIfAbsent(cacheKey, (s) -> new ConcurrentHashMap<>());
214230
timestampsMap.put(InternalFileIdentifier.fromPath(file), lastModified);
231+
this.compactingCounter.merge(cacheKey, 1, Integer::sum);
232+
compact(cacheKey, type);
215233
}
216234

217235
@Override
@@ -238,6 +256,8 @@ public <T extends IndexCacheable> void update(IndexCacheKey cacheKey, String[] f
238256
for (int i = 0; i < files.length; i++) {
239257
timestampsMap.put(InternalFileIdentifier.fromPath(files[i]), lastModified[i]);
240258
}
259+
this.compactingCounter.merge(cacheKey, 1, Integer::sum);
260+
compact(cacheKey, type);
241261
}
242262

243263
@Override
@@ -254,6 +274,10 @@ public long getModificationTimestamp(IndexCacheKey cacheKey, String file) {
254274

255275
return 0;
256276
}
277+
278+
public int getCompactingCounterBoundary() {
279+
return compactingCounterBoundary;
280+
}
257281

258282
private boolean isFileMatch(SortedMap<String, Long> files1, SortedMap<String, Long> files2) {
259283
if (files1.size() != files2.size()) return false;
@@ -265,8 +289,18 @@ private boolean isFileMatch(SortedMap<String, Long> files1, SortedMap<String, Lo
265289

266290
return true;
267291
}
292+
293+
private <T extends IndexCacheable> void compact(IndexCacheKey cacheKey, Class<T> type) {
294+
if (this.compactingCounter.get(cacheKey) > this.compactingCounterBoundary) {
295+
IndexCacheStore<T> compactedData = retrieveStoreFromIncrementalStorage(cacheKey, type).getLeft();
296+
persist(cacheKey, new DeltaSnapshot<T>(compactedData), false);
297+
this.compactingCounter.put(cacheKey, 0);
298+
299+
deleteOutdatedCacheFiles(cacheKey);
300+
}
301+
}
268302

269-
private void cleanupCache(IndexCacheKey cacheKey) {
303+
private void deleteOutdatedCacheFiles(IndexCacheKey cacheKey) {
270304
File[] cacheFiles = this.cacheDirectory.listFiles();
271305

272306
for (int i = 0; i < cacheFiles.length; i++) {
@@ -298,16 +332,15 @@ private <T extends IndexCacheable> void persist(IndexCacheKey cacheKey, DeltaEle
298332
gson.toJson(deltaStorage, writer);
299333

300334
writer.write("\n");
301-
302-
cleanupCache(cacheKey);
303335
}
304336
catch (Exception e) {
305337
log.error("cannot write symbol cache", e);
306338
}
307339
}
308340

309-
private <T extends IndexCacheable> IndexCacheStore<T> retrieveStoreFromIncrementalStorage(IndexCacheKey cacheKey, Class<T> type) {
341+
private <T extends IndexCacheable> Pair<IndexCacheStore<T>, Integer> retrieveStoreFromIncrementalStorage(IndexCacheKey cacheKey, Class<T> type) {
310342
IndexCacheStore<T> store = new IndexCacheStore<>(new TreeMap<>(), new ArrayList<T>(), new HashMap<>(), type);
343+
int deltaCounter = 0;
311344

312345
File cacheStore = new File(cacheDirectory, cacheKey.toString() + ".json");
313346
if (cacheStore.exists()) {
@@ -319,14 +352,15 @@ private <T extends IndexCacheable> IndexCacheStore<T> retrieveStoreFromIncrement
319352
while (reader.peek() != JsonToken.END_DOCUMENT) {
320353
DeltaStorage<T> delta = gson.fromJson(reader, DeltaStorage.class);
321354
store = delta.storedElement.apply(store);
355+
deltaCounter++;
322356
}
323357

324358
}
325359
catch (Exception e) {
326360
log.error("error reading cached symbols", e);
327361
}
328362
}
329-
return store;
363+
return Pair.of(store, deltaCounter);
330364
}
331365

332366

headless-services/spring-boot-language-server/src/test/java/org/springframework/ide/vscode/boot/index/cache/test/IndexCacheOnDiscDeltaBasedTest.java

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -333,6 +333,58 @@ void testSymbolAddedToExistingFile() throws Exception {
333333
assertEquals(timeFile1.toMillis() + 2000, cache.getModificationTimestamp(CACHE_KEY_VERSION_1, file1.toString()));
334334
}
335335

336+
@Test
337+
void testStorageFileIncrementallyUpdatedAndCompacted() throws Exception {
338+
Path file1 = Paths.get(tempDir.toAbsolutePath().toString(), "tempFile1");
339+
Files.createFile(file1);
340+
341+
FileTime timeFile1 = Files.getLastModifiedTime(file1);
342+
String[] files = {file1.toAbsolutePath().toString()};
343+
344+
String doc1URI = UriUtil.toUri(file1.toFile()).toString();
345+
346+
List<CachedSymbol> generatedSymbols1 = new ArrayList<>();
347+
WorkspaceSymbol symbol1 = new WorkspaceSymbol("symbol1", SymbolKind.Field, Either.forLeft(new Location("docURI", new Range(new Position(3, 10), new Position(3, 20)))));
348+
EnhancedSymbolInformation enhancedSymbol1 = new EnhancedSymbolInformation(symbol1, null);
349+
generatedSymbols1.add(new CachedSymbol(doc1URI, timeFile1.toMillis(), enhancedSymbol1));
350+
351+
cache.store(CACHE_KEY_VERSION_1, files, generatedSymbols1, null, CachedSymbol.class);
352+
353+
Path path = tempDir.resolve(Paths.get(CACHE_KEY_VERSION_1.toString() + STORAGE_FILE_EXTENSION));
354+
long initialCacheStorageSize = Files.size(path);
355+
long lastCacheStorageSize = initialCacheStorageSize;
356+
357+
int compactingBoundary = cache.getCompactingCounterBoundary();
358+
359+
for (int i = 0; i < compactingBoundary; i++) {
360+
cache.update(CACHE_KEY_VERSION_1, file1.toAbsolutePath().toString(), timeFile1.toMillis() + (100 * i), generatedSymbols1, null, CachedSymbol.class);
361+
362+
// check storage size (to see if updates are stored incrementally
363+
long updatedCacheStorageSize = Files.size(path);
364+
assertTrue(updatedCacheStorageSize > lastCacheStorageSize, "cache storage size in iteration: " + i);
365+
366+
lastCacheStorageSize = updatedCacheStorageSize;
367+
368+
// check internal timestamp updates
369+
long newModificationTimestamp = cache.getModificationTimestamp(CACHE_KEY_VERSION_1, file1.toString());
370+
assertEquals(timeFile1.toMillis() + (100 * i), newModificationTimestamp);
371+
372+
}
373+
374+
// test compacting after trigger boundary
375+
cache.update(CACHE_KEY_VERSION_1, file1.toAbsolutePath().toString(), timeFile1.toMillis() + (100 * compactingBoundary), generatedSymbols1, null, CachedSymbol.class);
376+
377+
// check storage size (to see if updates are stored incrementally
378+
long updatedCacheStorageSize = Files.size(path);
379+
assertTrue(updatedCacheStorageSize < lastCacheStorageSize, "cache storage size after compacting");
380+
381+
lastCacheStorageSize = updatedCacheStorageSize;
382+
383+
// check internal timestamp updates
384+
long newModificationTimestamp = cache.getModificationTimestamp(CACHE_KEY_VERSION_1, file1.toString());
385+
assertEquals(timeFile1.toMillis() + (100 * compactingBoundary), newModificationTimestamp);
386+
}
387+
336388
@Test
337389
void testSymbolsAddedToMultipleFiles() throws Exception {
338390

0 commit comments

Comments
 (0)