Skip to content

Commit

Permalink
Try nullable.
Browse files Browse the repository at this point in the history
  • Loading branch information
kennethshackleton committed Jun 22, 2024
1 parent 25f45e6 commit 2ad524b
Show file tree
Hide file tree
Showing 3 changed files with 83 additions and 23 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
/*
* Copyright 2024 Bloomberg Finance L.P.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package com.bloomberg.selekt.cache.benchmarks

import com.bloomberg.selekt.cache.StampedCache
import org.openjdk.jmh.annotations.Benchmark
import org.openjdk.jmh.annotations.BenchmarkMode
import org.openjdk.jmh.annotations.Level
import org.openjdk.jmh.annotations.Mode
import org.openjdk.jmh.annotations.Scope
import org.openjdk.jmh.annotations.Setup
import org.openjdk.jmh.annotations.State

@State(Scope.Thread)
open class StampedCacheInput {
internal lateinit var cache: StampedCache<Any>

@Setup(Level.Iteration)
fun setUp() {
cache = StampedCache(1) {}
}
}

open class StampedCacheBenchmark {
@Benchmark
@BenchmarkMode(Mode.Throughput)
fun getEntry(input: StampedCacheInput) = input.cache.run {
get("1") {}
}

@Benchmark
@BenchmarkMode(Mode.Throughput)
fun getEntryWithEviction(input: StampedCacheInput) = input.cache.run {
get("1") {}
get("2") {}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,61 +18,65 @@ package com.bloomberg.selekt.cache

class CommonLruCache<T : Any>(
@PublishedApi
@JvmField
internal val maxSize: Int,
disposal: (T) -> Unit
) {
@PublishedApi
internal var cache: Any = StampedCache(maxSize, disposal)
@JvmField
internal var cache: StampedCache<T>? = StampedCache(maxSize, disposal)
@PublishedApi
@JvmField
internal var linkedCache: LinkedLruCache<T>? = null

fun evict(key: String) {
when (val cache = cache) {
is StampedCache<*> -> cache.evict(key)
is LinkedLruCache<*> -> cache.evict(key)
else -> error("Unrecognized cache class: {}")
cache?.let {
it.evict(key)
return
}
linkedCache!!.evict(key)
}

fun evictAll() {
when (val cache = cache) {
is StampedCache<*> -> cache.evictAll()
is LinkedLruCache<*> -> cache.evictAll()
else -> error("Unrecognized cache class: {}")
cache?.let {
it.evictAll()
return
}
linkedCache!!.evictAll()
}

@Suppress("UNCHECKED_CAST")
inline fun get(key: String, supplier: () -> T): T = when (cache) {
is StampedCache<*> -> (cache as StampedCache<T>).let {
it.get(key) {
inline fun get(key: String, supplier: () -> T): T {
cache?.let {
return it.get(key) {
supplier().also { value ->
if (it.shouldTransform()) {
// Adding another entry to the cache will necessitate the removal of the
// least recently used entry first to honour our maximum size constraint.
// For the implementation of the store currently assigned, this is an O(N)
// operation. We transform to an O(1) implementation.
transform()
(this@CommonLruCache.cache as LinkedLruCache<T>).store.put(key, value)
linkedCache!!.store.put(key, value)
}
}
}
}
is LinkedLruCache<*> -> (cache as LinkedLruCache<T>).get(key, supplier)
else -> error("Unrecognized cache class: {}")
return linkedCache!!.get(key, supplier)
}

fun containsKey(key: String) = when (val cache = cache) {
is StampedCache<*> -> cache.containsKey(key)
is LinkedLruCache<*> -> cache.containsKey(key)
else -> error("Unrecognized cache class: {}")
fun containsKey(key: String): Boolean {
cache?.let {
return it.containsKey(key)
}
return linkedCache!!.containsKey(key)
}

@PublishedApi
internal fun StampedCache<T>.shouldTransform() = (store.size >= maxSize)

@PublishedApi
internal fun transform() {
(cache as StampedCache<*>).asLruCache().also {
cache = it
linkedCache = cache!!.asLruCache().also {
cache = null
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@

package com.bloomberg.selekt.cache

import org.junit.jupiter.api.Assertions.assertNull
import org.junit.jupiter.api.Test
import org.junit.jupiter.api.assertThrows
import org.mockito.kotlin.anyOrNull
Expand All @@ -28,6 +29,7 @@ import org.mockito.kotlin.times
import org.mockito.kotlin.verify
import org.mockito.kotlin.whenever
import kotlin.test.assertFalse
import kotlin.test.assertNotNull
import kotlin.test.assertSame
import kotlin.test.assertTrue
import kotlin.test.fail
Expand All @@ -44,6 +46,7 @@ internal class CommonLruCacheTest {
val cache = CommonLruCache(1, disposal)
cache.get("1") { first }
assertSame(first, cache.get("1") { fail() })
assertNotNull(cache.cache)
}

@Test
Expand All @@ -53,6 +56,7 @@ internal class CommonLruCacheTest {
cache.get("2") { second }
assertSame(first, cache.get("1") { fail() })
assertSame(second, cache.get("2") { fail() })
assertNotNull(cache.cache)
}

@Test
Expand All @@ -62,6 +66,7 @@ internal class CommonLruCacheTest {
cache.get("2") { second }
assertFalse(cache.containsKey("1"))
assertSame(second, cache.get("2") { fail() })
assertNull(cache.cache)
}

@Test
Expand All @@ -83,8 +88,8 @@ internal class CommonLruCacheTest {
cache.get("2") { second }
cache.evictAll()
inOrder(disposal) {
verify(disposal, times(1)).invoke(same(first))
verify(disposal, times(1)).invoke(same(second))
verify(disposal, times(1)).invoke(same(first))
}
}

Expand Down

0 comments on commit 2ad524b

Please sign in to comment.