-
Notifications
You must be signed in to change notification settings - Fork 11.1k
Description
API(s)
com.google.common.cache.LocalCache$Segment.lockedGetOrLoad
How do you want it to be improved?
This load should be possible without using synchronized
.
Why do we need it to be improved?
Using synchronized here is not performant when the current Thread is a Java 21 VirtualThread
and the loader does some blocking operation, because the synchronized
prevents the JVM from unmounting the Thread. The performance could be drastically improved by using p.e. ReentrantLock
.
Example
Thread.ofVirtual().start( () -> {
Database database;
var databaseLoader = new CacheLoader<String, Object>() {
@Override
public Object load(String key) {
return database.load(key); // loads an object by querying a database over the network
}
};
var cache = CacheBuilder.newBuilder().build(databaseLoader);
var fooObject = cache.getOrLoad("foo"); // cache miss, invokes the loader
});
Current Behavior
In the above example, the loader is invoked. This invocation happens inside a synchronized
block in com.google.common.cache.LocalCache$Segment.lockedGetOrLoad
. Because the loader does I/O but is inside the synchronized
block the VirtualThread
can not be unmounted and therefore blocks its carrier. This is bad for the throughput of the application. This behavior can be observed using any loader that does a blocking operation when it is invoked from a VirtualThread
(just start your JVM with -Djdk.tracePinnedThreads=full
). The performance impact is dependent on how long the loader takes, but since generally one only caches things that are expensive to do I believe this to be a problem for pretty much anyone using LocalCache
in combination with VirtualThreads
.
Desired Behavior
com.google.common.cache.LocalCache$Segment.lockedGetOrLoad
should work gracefully with VirtualThreads and not block the carrier Thread if a loader does I/O or some other blocking operation.
The method should therefore be refactored to lock using some other method, p.e. a ReentrantLock
.
Concrete Use Cases
We use a LocalCache
to cache frequently used database objects. Our loader therefore executes a SQL query over the network, which is a blocking operation and takes comparatively long. We are currently experimenting with using VirtualThreads
in order to improve the throughput of I/O heavy workloads.
Checklist
-
I agree to follow the code of conduct.
-
I have read and understood the contribution guidelines.
-
I have read and understood Guava's philosophy, and I strongly believe that this proposal aligns with it.