Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add the two-choice random cache eviction policy #16828

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -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<PageId, Long> 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<PageId> 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<PageId> criterion) {
synchronized (mCache) {
for (PageId candidate : mCache.keySet()) {
if (criterion.test(candidate)) {
return candidate;
}
}
return null;
}
}

@Override
public void reset() {
mCache.clear();
}
}
Original file line number Diff line number Diff line change
@@ -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());
}
}