/*
* Copyright 2016 Wim Deblauwe. All Rights Reserved.
*
* 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
*
* http://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.github.benmanes.caffeine.examples.writebehind.rxjava;
import java.time.ZoneId;
import java.time.ZonedDateTime;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicReference;
import java.util.function.BinaryOperator;
import org.awaitility.Awaitility;
import org.junit.Assert;
import org.junit.Test;
import com.github.benmanes.caffeine.cache.Cache;
import com.github.benmanes.caffeine.cache.Caffeine;
/**
* An example of a write-behind cache.
*
* @author wim.deblauwe@gmail.com (Wim Deblauwe)
*/
public final class WriteBehindCacheWriterTest {
@Test
public void givenCacheUpdate_writeBehindIsCalled() {
AtomicBoolean writerCalled = new AtomicBoolean(false);
// Given this cache...
Cache<Long, ZonedDateTime> cache = Caffeine.newBuilder()
.writer(new WriteBehindCacheWriter.Builder<Long, ZonedDateTime>()
.bufferTime(1, TimeUnit.SECONDS)
.coalesce(BinaryOperator.maxBy(ZonedDateTime::compareTo))
.writeAction(entries -> writerCalled.set(true))
.build())
.build();
// When this cache update happens ...
cache.put(1L, ZonedDateTime.now());
// Then the write behind action is called
Awaitility.await().untilTrue(writerCalled);
}
@Test
public void givenCacheUpdateOnMultipleKeys_writeBehindIsCalled() {
AtomicBoolean writerCalled = new AtomicBoolean(false);
AtomicInteger numberOfEntries = new AtomicInteger(0);
// Given this cache...
Cache<Long, ZonedDateTime> cache = Caffeine.newBuilder()
.writer(new WriteBehindCacheWriter.Builder<Long, ZonedDateTime>()
.bufferTime(1, TimeUnit.SECONDS)
.coalesce(BinaryOperator.maxBy(ZonedDateTime::compareTo))
.writeAction(entries -> {
numberOfEntries.set(entries.size());
writerCalled.set(true);
}).build())
.build();
// When these cache updates happen ...
cache.put(1L, ZonedDateTime.now());
cache.put(2L, ZonedDateTime.now());
cache.put(3L, ZonedDateTime.now());
// Then the write behind action gets 3 entries to write
Awaitility.await().untilTrue(writerCalled);
Assert.assertEquals(3, numberOfEntries.intValue());
}
@Test
public void givenMultipleCacheUpdatesOnSameKey_writeBehindIsCalledWithMostRecentTime() {
AtomicBoolean writerCalled = new AtomicBoolean(false);
AtomicInteger numberOfEntries = new AtomicInteger(0);
AtomicReference<ZonedDateTime> timeInWriteBehind = new AtomicReference<>();
// Given this cache...
Cache<Long, ZonedDateTime> cache = Caffeine.newBuilder()
.writer(new WriteBehindCacheWriter.Builder<Long, ZonedDateTime>()
.bufferTime(1, TimeUnit.SECONDS)
.coalesce(BinaryOperator.maxBy(ZonedDateTime::compareTo))
.writeAction(entries -> {
// We might get here before the cache has been written to,
// so just wait for the next time we are called
if (entries.isEmpty()) {
return;
}
numberOfEntries.set(entries.size());
ZonedDateTime zonedDateTime = entries.values().iterator().next();
timeInWriteBehind.set(zonedDateTime);
writerCalled.set(true);
}).build())
.build();
// When these cache updates happen ...
cache.put(1L, ZonedDateTime.of(2016, 6, 26, 8, 0, 0, 0, ZoneId.systemDefault()));
cache.put(1L, ZonedDateTime.of(2016, 6, 26, 8, 0, 0, 100, ZoneId.systemDefault()));
cache.put(1L, ZonedDateTime.of(2016, 6, 26, 8, 0, 0, 300, ZoneId.systemDefault()));
ZonedDateTime mostRecentTime = ZonedDateTime.of(
2016, 6, 26, 8, 0, 0, 500, ZoneId.systemDefault());
cache.put(1L, mostRecentTime);
// Then the write behind action gets 1 entry to write with the most recent time
Awaitility.await().untilTrue(writerCalled);
Assert.assertEquals(1, numberOfEntries.intValue());
Assert.assertEquals(mostRecentTime, timeInWriteBehind.get());
}
}