Skip to content

Commit f2fc378

Browse files
authored
introduce CachedValue to prevent overhead of repeated process exectuion (#1489)
1 parent 8d0aa2a commit f2fc378

File tree

3 files changed

+120
-1
lines changed

3 files changed

+120
-1
lines changed
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
package com.devonfw.tools.ide.cache;
2+
3+
import java.util.function.Supplier;
4+
5+
/**
6+
* A cached value that allows a good balance between reducing computing overhead and still providing accuracy.
7+
*
8+
* @param <T> type of the {@link #get() value}.
9+
*/
10+
public class CachedValue<T> implements Supplier<T> {
11+
12+
private static final long DEFAULT_RETENTION = 20 * 1000; // 20 seconds
13+
14+
private final Supplier<T> supplier;
15+
16+
private final long retention;
17+
18+
private long timestamp;
19+
20+
private T value;
21+
22+
/**
23+
* The constructor.
24+
*
25+
* @param supplier the {@link Supplier} function to compute the actual value.
26+
*/
27+
public CachedValue(Supplier<T> supplier) {
28+
this(supplier, DEFAULT_RETENTION);
29+
}
30+
31+
32+
/**
33+
* The constructor.
34+
*
35+
* @param supplier the {@link Supplier} function to compute the actual value.
36+
*/
37+
public CachedValue(Supplier<T> supplier, long retention) {
38+
super();
39+
this.supplier = supplier;
40+
this.retention = retention;
41+
}
42+
43+
@Override
44+
public T get() {
45+
46+
long now = System.currentTimeMillis();
47+
if ((now - this.timestamp) > retention) {
48+
this.timestamp = now;
49+
this.value = this.supplier.get();
50+
}
51+
return this.value;
52+
}
53+
54+
/**
55+
* Invalidates a potentially cached value.
56+
*/
57+
public void invalidate() {
58+
59+
this.timestamp = 0;
60+
this.value = null;
61+
}
62+
}

cli/src/main/java/com/devonfw/tools/ide/tool/npm/NpmBasedCommandlet.java

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
import java.util.List;
55
import java.util.Set;
66

7+
import com.devonfw.tools.ide.cache.CachedValue;
78
import com.devonfw.tools.ide.common.Tag;
89
import com.devonfw.tools.ide.context.IdeContext;
910
import com.devonfw.tools.ide.process.ProcessContext;
@@ -19,6 +20,8 @@
1920
*/
2021
public abstract class NpmBasedCommandlet extends LocalToolCommandlet {
2122

23+
private final CachedValue<VersionIdentifier> installedVersion;
24+
2225
/**
2326
* The constructor.
2427
*
@@ -29,6 +32,7 @@ public abstract class NpmBasedCommandlet extends LocalToolCommandlet {
2932
public NpmBasedCommandlet(IdeContext context, String tool, Set<Tag> tags) {
3033

3134
super(context, tool, tags);
35+
this.installedVersion = new CachedValue<>(() -> runNpmGetInstalledPackageVersion(getNpmPackage()));
3236
}
3337

3438
@Override
@@ -76,7 +80,7 @@ protected VersionIdentifier runNpmGetInstalledPackageVersion(String npmPackage)
7680
@Override
7781
public VersionIdentifier getInstalledVersion() {
7882

79-
return runNpmGetInstalledPackageVersion(getNpmPackage());
83+
return this.installedVersion.get();
8084
}
8185

8286
@Override
@@ -94,12 +98,14 @@ protected void performToolInstallation(ToolRepository toolRepository, VersionIde
9498

9599
// runNpmUninstall(getNpmPackage()); // first uninstall a previously installed version
96100
runNpmInstall(getNpmPackage() + "@" + resolvedVersion);
101+
this.installedVersion.invalidate();
97102
}
98103

99104
@Override
100105
protected void performUninstall(Path toolPath) {
101106

102107
runNpmUninstall(getNpmPackage());
108+
this.installedVersion.invalidate();
103109
}
104110

105111
/**
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
package com.devonfw.tools.ide.cache;
2+
3+
import java.util.function.Supplier;
4+
5+
import org.assertj.core.api.Assertions;
6+
import org.junit.jupiter.api.Test;
7+
8+
/**
9+
* Test of {@link CachedValue}.
10+
*/
11+
public class CachedValueTest extends Assertions {
12+
13+
/**
14+
* Test that {@link CachedValue#get()} updates only if retention expired or invalidated.
15+
*/
16+
@Test
17+
public void testFlow() throws Exception {
18+
19+
// arrange
20+
Sequence sequence = new Sequence();
21+
CachedValue<Integer> cachedValue = new CachedValue<>(sequence, 1000);
22+
sequence.increment();
23+
// act
24+
assertThat(cachedValue.get()).isEqualTo(1);
25+
assertThat(cachedValue.get()).isEqualTo(1);
26+
sequence.increment();
27+
assertThat(cachedValue.get()).isEqualTo(1);
28+
Thread.sleep(2000);
29+
assertThat(cachedValue.get()).isEqualTo(2);
30+
sequence.increment();
31+
assertThat(cachedValue.get()).isEqualTo(2);
32+
cachedValue.invalidate();
33+
assertThat(cachedValue.get()).isEqualTo(3);
34+
}
35+
36+
private static class Sequence implements Supplier<Integer> {
37+
38+
private int value;
39+
40+
@Override
41+
public Integer get() {
42+
43+
return this.value;
44+
}
45+
46+
public void increment() {
47+
this.value++;
48+
}
49+
}
50+
51+
}

0 commit comments

Comments
 (0)