From fe86f72e7a78cbcfa23d42d9e2f3a425fe75d660 Mon Sep 17 00:00:00 2001 From: Chunxu Tang Date: Mon, 30 Jan 2023 16:00:32 -0800 Subject: [PATCH] Add the two choice random cache eviction policy --- .../cache/evictor/TwoChoiceRandomEvictor.java | 94 +++++++++++++++++++ .../cache/TwoChoiceRandomEvictorTest.java | 81 ++++++++++++++++ 2 files changed, 175 insertions(+) create mode 100644 core/client/fs/src/main/java/alluxio/client/file/cache/evictor/TwoChoiceRandomEvictor.java create mode 100644 core/client/fs/src/test/java/alluxio/client/file/cache/TwoChoiceRandomEvictorTest.java diff --git a/core/client/fs/src/main/java/alluxio/client/file/cache/evictor/TwoChoiceRandomEvictor.java b/core/client/fs/src/main/java/alluxio/client/file/cache/evictor/TwoChoiceRandomEvictor.java new file mode 100644 index 000000000000..8778431f2204 --- /dev/null +++ b/core/client/fs/src/main/java/alluxio/client/file/cache/evictor/TwoChoiceRandomEvictor.java @@ -0,0 +1,94 @@ +/* + * The Alluxio Open Foundation licenses this work under the Apache License, version 2.0 + * (the "License"). You may not use this work except in compliance with the License, which is + * available at www.apache.org/licenses/LICENSE-2.0 + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, + * either express or implied, as more fully set forth in the License. + * + * See the NOTICE file distributed with this work for information regarding copyright ownership. + */ + +package alluxio.client.file.cache.evictor; + +import alluxio.client.file.cache.PageId; + +import java.time.Instant; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Random; +import java.util.function.Predicate; +import javax.annotation.Nullable; +import javax.annotation.concurrent.ThreadSafe; + +/** + * Two Choice Random client-side cache eviction policy. + * It selects two random page IDs and evicts the one least-recently used. + */ +@ThreadSafe +public class TwoChoiceRandomEvictor implements CacheEvictor { + private final Map mCache = Collections.synchronizedMap(new HashMap<>()); + + /** + * Constructor. + * @param options + */ + public TwoChoiceRandomEvictor(CacheEvictorOptions options) { + } + + @Override + public void updateOnGet(PageId pageId) { + mCache.put(pageId, Instant.now().toEpochMilli()); + } + + @Override + public void updateOnPut(PageId pageId) { + mCache.put(pageId, Instant.now().toEpochMilli()); + } + + @Override + public void updateOnDelete(PageId pageId) { + mCache.remove(pageId); + } + + @Nullable + @Override + public PageId evict() { + synchronized (mCache) { + if (mCache.isEmpty()) { + return null; + } + + // TODO(chunxu): improve the performance here + List keys = new ArrayList<>(mCache.keySet()); + Random rand = new Random(); + PageId key1 = keys.get(rand.nextInt(keys.size())); + PageId key2 = keys.get(rand.nextInt(keys.size())); + if (mCache.get(key1) < mCache.get(key2)) { + return key1; + } + return key2; + } + } + + @Nullable + @Override + public PageId evictMatching(Predicate criterion) { + synchronized (mCache) { + for (PageId candidate : mCache.keySet()) { + if (criterion.test(candidate)) { + return candidate; + } + } + return null; + } + } + + @Override + public void reset() { + mCache.clear(); + } +} diff --git a/core/client/fs/src/test/java/alluxio/client/file/cache/TwoChoiceRandomEvictorTest.java b/core/client/fs/src/test/java/alluxio/client/file/cache/TwoChoiceRandomEvictorTest.java new file mode 100644 index 000000000000..81248c9cd859 --- /dev/null +++ b/core/client/fs/src/test/java/alluxio/client/file/cache/TwoChoiceRandomEvictorTest.java @@ -0,0 +1,81 @@ +/* + * The Alluxio Open Foundation licenses this work under the Apache License, version 2.0 + * (the "License"). You may not use this work except in compliance with the License, which is + * available at www.apache.org/licenses/LICENSE-2.0 + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, + * either express or implied, as more fully set forth in the License. + * + * See the NOTICE file distributed with this work for information regarding copyright ownership. + */ + +package alluxio.client.file.cache; + +import alluxio.client.file.cache.evictor.CacheEvictorOptions; +import alluxio.client.file.cache.evictor.TwoChoiceRandomEvictor; + +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; + +/** + * Tests for the {@link TwoChoiceRandomEvictor} class. + */ +public class TwoChoiceRandomEvictorTest { + private TwoChoiceRandomEvictor mEvictor; + private final PageId mFirst = new PageId("1L", 2L); + private final PageId mSecond = new PageId("3L", 4L); + private final PageId mThird = new PageId("5L", 6L); + + /** + * Sets up the instances. + */ + @Before + public void before() { + mEvictor = new TwoChoiceRandomEvictor(new CacheEvictorOptions()); + } + + @Test + public void evictGetOrder() { + mEvictor.updateOnGet(mFirst); + Assert.assertEquals(mFirst, mEvictor.evict()); + mEvictor.updateOnGet(mSecond); + Assert.assertEquals(mSecond, mEvictor.evict()); + } + + @Test + public void evictPutOrder() { + mEvictor.updateOnPut(mFirst); + Assert.assertEquals(mFirst, mEvictor.evict()); + mEvictor.updateOnPut(mSecond); + mEvictor.updateOnPut(mFirst); + PageId evictedPage = mEvictor.evict(); + Assert.assertTrue(evictedPage.equals(mFirst) || evictedPage.equals(mSecond)); + } + + @Test + public void evictAfterDelete() { + mEvictor.updateOnPut(mFirst); + mEvictor.updateOnPut(mSecond); + mEvictor.updateOnPut(mThird); + mEvictor.updateOnDelete(mSecond); + mEvictor.updateOnDelete(mThird); + Assert.assertEquals(mFirst, mEvictor.evict()); + } + + @Test + public void evictEmpty() { + Assert.assertNull(mEvictor.evict()); + } + + @Test + public void evictAllGone() { + mEvictor.updateOnPut(mFirst); + mEvictor.updateOnPut(mSecond); + mEvictor.updateOnPut(mThird); + mEvictor.updateOnDelete(mFirst); + mEvictor.updateOnDelete(mSecond); + mEvictor.updateOnDelete(mThird); + Assert.assertNull(mEvictor.evict()); + } +}