Tags: hxtcoder/caffeine
Tags
Fix NPE when on the bootclasspath (fixes ben-manes#481) To reduce runtime overhead due to fields needed only by certain configurations, code generation is used and reflection loads the optimal implementation classes. This trades off a small amount of disk space for reduced memory usage. Unfortunately the wrong classloader method was used, which is not available on the bootclasspath (null value). This fails when the cache is used by a compiler, e.g. ErrorProne's javac fork. Instead Class.forName is now used which should resolve to a non-null, acceptable class loader. It appears as if this should be the same loader as before, or else the system loader if on the bootclasspath.
Fix write-time optimization for variable expiration (fixes ben-manes#478 ) A high write rate to the same key can overwhelm the write buffer as it may not drop entries and has a maximum capacity. When full this causes backpressure to allow for the mainance task to catch up. A write only needs to be recorded in this buffer when a major event occurs, such as the entry's size changed or expiration time differs. To improve throughput for expireAfterWrite a tolerance of 1s is used to allow for skipping the write buffer and recording into the read buffer instead. This improves throughput by 5x in a same key write benchmark. This optimization was not updated to take into account variable expiration, where the expire time may change based on the calculation determined by configured Expiry. This would cause the entry to not be reordered in the TimerWheel, possibly delaying the automic removal indefinitely. Now when the variable time differs by +/- 1s then the write-time reordering is required. Co-authored-by: Christopher Ng <[email protected]>
Fixed race causing an incorrect removal cause (fixes ben-manes#412) This bug manifests due to an incorrectly optimized `Map.remove(key)` implementation when there is no CacheWriter specified. If there is, an alternative implementation is used which does not suffer from this mistake. This change now uses that version in all cases. The explicit removal and an eviction of the same entry can occur concurrently. If the removal wins the race, it is responsible for retiring the entry. However even after that entry is discarded, an further evictions may be necessary to meet the bounding criteria (e.g. weighted entries). Therefore the eviction thread eagerly discards the entry from the policy's data structures so that it can continue its evaluation. While the entry is synchronized in both cases, the eviction thread could lose the removal but retire the entry first. This lead to the removal seeing a dead entry and miscommunicating the cause to the listener. There is a modest performance difference in a microbenchmark because of a slightly coarser locking. However most of the differences between libraries is whether they decide to be linearizable or perform an optimistic check to no-op if the entry is absent. The optimistic check avoids locking but also can lead to a removal to not block waiting for an in-flight computation to complete. While a valid choice, some users have expressed a desire for removal to be pessimistic and is rare enough that we follow their wishes.
PreviousNext