-
Notifications
You must be signed in to change notification settings - Fork 776
Expand file tree
/
Copy pathGitHubSanityCachedValue.java
More file actions
69 lines (63 loc) · 2.56 KB
/
GitHubSanityCachedValue.java
File metadata and controls
69 lines (63 loc) · 2.56 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
package org.kohsuke.github;
import org.kohsuke.github.function.SupplierThrows;
import java.time.Instant;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
import java.util.function.Function;
/**
* GitHubSanityCachedValue limits queries for a particular value to once per second.
*/
class GitHubSanityCachedValue<T> {
private long lastQueriedAtEpochSeconds = 0;
private T lastResult = null;
// Allow concurrent readers while a refresh is not needed.
private final ReentrantReadWriteLock lock = new ReentrantReadWriteLock();
private final Lock readLock = lock.readLock();
private final Lock writeLock = lock.writeLock();
/**
* Gets the value from the cache or calls the supplier if the cache is empty or out of date.
*
* @param isExpired
* a supplier that returns true if the cached value is no longer valid.
* @param query
* a supplier the returns an updated value. Only called if the cache is empty or out of date.
* @return the value from the cache or the value returned from the supplier.
* @throws E
* the exception thrown by the supplier if it fails.
*/
<E extends Throwable> T get(Function<T, Boolean> isExpired, SupplierThrows<T, E> query) throws E {
readLock.lock();
try {
boolean expired = Instant.now().getEpochSecond() > lastQueriedAtEpochSeconds || isExpired.apply(lastResult);
if (!expired) {
return lastResult;
}
} finally {
readLock.unlock();
}
writeLock.lock();
try {
boolean stillExpired = Instant.now().getEpochSecond() > lastQueriedAtEpochSeconds
|| isExpired.apply(lastResult);
if (stillExpired) {
lastResult = query.get();
lastQueriedAtEpochSeconds = Instant.now().getEpochSecond();
}
return lastResult;
} finally {
writeLock.unlock();
}
}
/**
* Gets the value from the cache or calls the supplier if the cache is empty or out of date.
*
* @param query
* a supplier the returns an updated value. Only called if the cache is empty or out of date.
* @return the value from the cache or the value returned from the supplier.
* @throws E
* the exception thrown by the supplier if it fails.
*/
<E extends Throwable> T get(SupplierThrows<T, E> query) throws E {
return get((value) -> Boolean.FALSE, query);
}
}