/*
* Copyright Terracotta, Inc.
*
* 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 org.ehcache.core;
import java.util.Collections;
import java.util.EnumSet;
import java.util.LinkedHashSet;
import java.util.Map;
import java.util.Set;
import org.ehcache.Status;
import org.ehcache.config.CacheConfiguration;
import org.ehcache.core.config.BaseCacheConfiguration;
import org.ehcache.core.config.ResourcePoolsHelper;
import org.ehcache.core.spi.store.Store;
import org.ehcache.core.spi.function.NullaryFunction;
import org.ehcache.core.statistics.BulkOps;
import org.ehcache.core.statistics.CacheOperationOutcomes;
import org.ehcache.spi.loaderwriter.BulkCacheWritingException;
import org.ehcache.core.spi.store.StoreAccessException;
import org.ehcache.expiry.Duration;
import org.ehcache.expiry.Expiry;
import org.ehcache.core.spi.function.Function;
import org.ehcache.spi.loaderwriter.CacheLoaderWriter;
import org.hamcrest.Matchers;
import org.junit.Test;
import org.mockito.ArgumentCaptor;
import org.mockito.Captor;
import org.mockito.InOrder;
import org.mockito.Mock;
import org.slf4j.LoggerFactory;
import static org.ehcache.core.EhcacheBasicBulkUtil.KEY_SET_A;
import static org.ehcache.core.EhcacheBasicBulkUtil.KEY_SET_B;
import static org.ehcache.core.EhcacheBasicBulkUtil.KEY_SET_C;
import static org.ehcache.core.EhcacheBasicBulkUtil.KEY_SET_D;
import static org.ehcache.core.EhcacheBasicBulkUtil.copyWithout;
import static org.ehcache.core.EhcacheBasicBulkUtil.fanIn;
import static org.ehcache.core.EhcacheBasicBulkUtil.getAltEntryMap;
import static org.ehcache.core.EhcacheBasicBulkUtil.getEntryMap;
import static org.ehcache.core.EhcacheBasicBulkUtil.union;
import static org.hamcrest.Matchers.empty;
import static org.hamcrest.Matchers.equalTo;
import static org.hamcrest.Matchers.everyItem;
import static org.hamcrest.Matchers.instanceOf;
import static org.hamcrest.Matchers.is;
import static org.hamcrest.Matchers.isIn;
import static org.junit.Assert.assertThat;
import static org.junit.Assert.fail;
import static org.mockito.Matchers.any;
import static org.mockito.Matchers.argThat;
import static org.mockito.Matchers.eq;
import static org.mockito.Mockito.atLeast;
import static org.mockito.Mockito.doThrow;
import static org.mockito.Mockito.inOrder;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyZeroInteractions;
import static org.mockito.Mockito.when;
import static org.ehcache.core.EhcacheBasicPutAllTest.getAnyEntryIterable;
import static org.ehcache.core.EhcacheBasicPutAllTest.getAnyEntryIterableFunction;
import static org.ehcache.core.EhcacheBasicPutAllTest.getAnyStringSet;
/**
* Provides testing of basic PUT_ALL operations on an {@code EhcacheWithLoaderWriter}.
* <p>
* In an effort compromise, this class intentionally omits test cases in which
* the {@code Store} is pre-populated with no entries, pre-populated only with
* entries having keys not in the {@code putAll} request map, and pre-populated
* with entries for all keys in the {@code putAll} request map. This reduces
* the potential test cases by about 70% without, hopefully, compromising code
* coverage.
* <p>
* Since the processing in {@link EhcacheWithLoaderWriter#putAll} relies on non-deterministically ordered Maps in several stages
* of processing, the result of {@code putAll} when handling failures is *not* deterministic -- changes in
* iteration order of the {@code putAll} request map can change the results of the {@code putAll} operation under
* error scenarios. The test assertions attempt to confirm results in aggregate over successes and failures and
* do not specify specific success and failures for each test.
*
* @author Clifford W. Johnson
*/
public class EhcacheWithLoaderWriterBasicPutAllTest extends EhcacheBasicCrudBase {
@Mock
private CacheLoaderWriter<String, String> cacheLoaderWriter;
/**
* A Mockito {@code ArgumentCaptor} for the {@code Set} argument to the
* {@link Store#bulkCompute(Set, Function, NullaryFunction)
* Store.bulkCompute(Set, Function, NullaryFunction} method.
*/
@Captor
private ArgumentCaptor<Set<String>> bulkComputeSetCaptor;
/**
* A Mockito {@code ArgumentCaptor} for the
* {@link BulkCacheWritingException BulkCacheWritingException}
* provided to the
* {@link org.ehcache.core.internal.resilience.ResilienceStrategy#putAllFailure(Map, StoreAccessException, BulkCacheWritingException)}
* ResilienceStrategy.putAllFailure(Iterable, StoreAccessException, BulkCacheWritingException)} method.
*/
@Captor
private ArgumentCaptor<BulkCacheWritingException> bulkExceptionCaptor;
/**
* Tests {@link EhcacheWithLoaderWriter#putAll(Map)} for
* <ul>
* <li>empty request map</li>
* <li>populated {@code Store} (keys not relevant)</li>
* <li>populated {@code CacheLoaderWriter} (keys not relevant)</li>
* </ul>
*/
@Test
public void testPutAllEmptyRequestWithWriter() throws Exception {
final Map<String, String> originalStoreContent = getEntryMap(KEY_SET_A, KEY_SET_B);
final FakeStore fakeStore = new FakeStore(originalStoreContent);
this.store = spy(fakeStore);
final FakeCacheLoaderWriter fakeLoaderWriter = new FakeCacheLoaderWriter(originalStoreContent);
this.cacheLoaderWriter = spy(fakeLoaderWriter);
final InternalCache<String, String> ehcache = this.getEhcache(this.cacheLoaderWriter);
ehcache.putAll(Collections.<String, String>emptyMap());
verify(this.store, never()).bulkCompute(eq(Collections.<String>emptySet()), getAnyEntryIterableFunction());
assertThat(fakeStore.getEntryMap(), equalTo(originalStoreContent));
assertThat(fakeLoaderWriter.getEntryMap(), equalTo(originalStoreContent));
verify(this.spiedResilienceStrategy, never()).putAllFailure(eq(Collections.<String, String>emptyMap()), any(StoreAccessException.class));
verify(this.cacheLoaderWriter, never()).writeAll(eq(Collections.<Map.Entry<String, String>>emptyList()));
validateStats(ehcache, EnumSet.noneOf(CacheOperationOutcomes.PutOutcome.class));
validateStats(ehcache, EnumSet.of(CacheOperationOutcomes.PutAllOutcome.SUCCESS));
assertThat(ehcache.getBulkMethodEntries().get(BulkOps.PUT_ALL).intValue(), is(0));
}
/**
* Tests {@link EhcacheWithLoaderWriter#putAll(Map)} for
* <ul>
* <li>non-empty request map</li>
* <li>populated {@code Store} - some keys overlap request</li>
* <li>populated {@code CacheLoaderWriter} - some keys overlap</li>
* <li>no {@link CacheLoaderWriter#writeAll(Iterable)} calls fail</li>
* </ul>
*/
@Test
public void testPutAllStoreSomeOverlapWriterNoOverlapNoneFail() throws Exception {
final Map<String, String> originalStoreContent = getEntryMap(KEY_SET_A, KEY_SET_B);
final FakeStore fakeStore = new FakeStore(originalStoreContent);
this.store = spy(fakeStore);
final Map<String, String> originalWriterContent = getEntryMap(KEY_SET_A, KEY_SET_B, KEY_SET_D);
final FakeCacheLoaderWriter fakeLoaderWriter = new FakeCacheLoaderWriter(originalWriterContent);
this.cacheLoaderWriter = spy(fakeLoaderWriter);
final InternalCache<String, String> ehcache = this.getEhcache(this.cacheLoaderWriter);
final Map<String, String> contentUpdates = getAltEntryMap("new_", fanIn(KEY_SET_A, KEY_SET_C));
ehcache.putAll(contentUpdates);
verify(this.store, atLeast(1)).bulkCompute(this.bulkComputeSetCaptor.capture(), getAnyEntryIterableFunction());
assertThat(this.getBulkComputeArgs(), equalTo(contentUpdates.keySet()));
assertThat(fakeStore.getEntryMap(), equalTo(union(originalStoreContent, contentUpdates)));
assertThat(fakeLoaderWriter.getEntryMap(), equalTo(union(originalWriterContent, contentUpdates)));
verifyZeroInteractions(this.spiedResilienceStrategy);
validateStats(ehcache, EnumSet.noneOf(CacheOperationOutcomes.PutOutcome.class));
validateStats(ehcache, EnumSet.of(CacheOperationOutcomes.PutAllOutcome.SUCCESS));
assertThat(ehcache.getBulkMethodEntries().get(BulkOps.PUT_ALL).intValue(), is(contentUpdates.size()));
}
/**
* Tests {@link EhcacheWithLoaderWriter#putAll(Map)} for
* <ul>
* <li>non-empty request map</li>
* <li>populated {@code Store} - some keys overlap request</li>
* <li>{@link Store#bulkCompute} throws before accessing writer</li>
* <li>populated {@code CacheLoaderWriter} - some keys overlap</li>
* <li>no {@link CacheLoaderWriter#writeAll(Iterable)} calls fail</li>
* </ul>
*/
@Test
public void testPutAllStoreSomeOverlapStoreAccessExceptionBeforeWriterNoOverlapNoneFail() throws Exception {
final Map<String, String> originalStoreContent = getEntryMap(KEY_SET_A, KEY_SET_B);
final FakeStore fakeStore = new FakeStore(originalStoreContent);
this.store = spy(fakeStore);
doThrow(new StoreAccessException("")).when(this.store)
.bulkCompute(getAnyStringSet(), getAnyEntryIterableFunction());
final Map<String, String> originalWriterContent = getEntryMap(KEY_SET_A, KEY_SET_B, KEY_SET_D);
final FakeCacheLoaderWriter fakeLoaderWriter = new FakeCacheLoaderWriter(originalWriterContent);
this.cacheLoaderWriter = spy(fakeLoaderWriter);
final InternalCache<String, String> ehcache = this.getEhcache(this.cacheLoaderWriter);
final Map<String, String> contentUpdates = getAltEntryMap("new_", fanIn(KEY_SET_A, KEY_SET_C));
ehcache.putAll(contentUpdates);
final InOrder ordered = inOrder(this.store, this.cacheLoaderWriter, this.spiedResilienceStrategy);
ordered.verify(this.store, atLeast(1)).bulkCompute(this.bulkComputeSetCaptor.capture(), getAnyEntryIterableFunction());
assertThat(this.getBulkComputeArgs(), everyItem(isIn(contentUpdates.keySet())));
// ResilienceStrategy invoked; no assertions about Store content
ordered.verify(this.cacheLoaderWriter, atLeast(1)).writeAll(getAnyEntryIterable());
assertThat(fakeLoaderWriter.getEntryMap(), equalTo(union(originalWriterContent, contentUpdates)));
ordered.verify(this.spiedResilienceStrategy)
.putAllFailure(eq(contentUpdates), any(StoreAccessException.class));
validateStats(ehcache, EnumSet.noneOf(CacheOperationOutcomes.PutOutcome.class));
validateStats(ehcache, EnumSet.of(CacheOperationOutcomes.PutAllOutcome.FAILURE));
assertThat(ehcache.getBulkMethodEntries().get(BulkOps.PUT_ALL).intValue(), is(0));
}
/**
* Tests {@link EhcacheWithLoaderWriter#putAll(Map)} for
* <ul>
* <li>non-empty request map</li>
* <li>populated {@code Store} - some keys overlap request</li>
* <li>{@link Store#bulkCompute} throws after accessing writer</li>
* <li>populated {@code CacheLoaderWriter} - some keys overlap</li>
* <li>no {@link CacheLoaderWriter#writeAll(Iterable)} calls fail</li>
* </ul>
*/
@Test
public void testPutAllStoreSomeOverlapStoreAccessExceptionAfterWriterNoOverlapNoneFail() throws Exception {
final Map<String, String> originalStoreContent = getEntryMap(KEY_SET_A, KEY_SET_B);
final FakeStore fakeStore = new FakeStore(originalStoreContent, Collections.singleton("keyA3"));
this.store = spy(fakeStore);
final Map<String, String> originalWriterContent = getEntryMap(KEY_SET_A, KEY_SET_B, KEY_SET_D);
final FakeCacheLoaderWriter fakeLoaderWriter = new FakeCacheLoaderWriter(originalWriterContent);
this.cacheLoaderWriter = spy(fakeLoaderWriter);
final InternalCache<String, String> ehcache = this.getEhcache(this.cacheLoaderWriter);
final Map<String, String> contentUpdates = getAltEntryMap("new_", fanIn(KEY_SET_A, KEY_SET_C));
ehcache.putAll(contentUpdates);
validateStats(ehcache, EnumSet.noneOf(CacheOperationOutcomes.PutOutcome.class));
final InOrder ordered = inOrder(this.store, this.cacheLoaderWriter, this.spiedResilienceStrategy);
ordered.verify(this.store, atLeast(1))
.bulkCompute(this.bulkComputeSetCaptor.capture(), getAnyEntryIterableFunction());
assertThat(this.getBulkComputeArgs(), everyItem(isIn(contentUpdates.keySet())));
// ResilienceStrategy invoked; no assertions about Store content
ordered.verify(this.cacheLoaderWriter, atLeast(1)).writeAll(getAnyEntryIterable());
assertThat(fakeLoaderWriter.getEntryMap(), equalTo(union(originalWriterContent, contentUpdates)));
ordered.verify(this.spiedResilienceStrategy)
.putAllFailure(eq(contentUpdates), any(StoreAccessException.class));
validateStats(ehcache, EnumSet.noneOf(CacheOperationOutcomes.PutOutcome.class));
validateStats(ehcache, EnumSet.of(CacheOperationOutcomes.PutAllOutcome.FAILURE));
assertThat(ehcache.getBulkMethodEntries().get(BulkOps.PUT_ALL).intValue(), is(0));
}
/**
* Tests {@link EhcacheWithLoaderWriter#putAll(Map)} for
* <ul>
* <li>non-empty request map</li>
* <li>populated {@code Store} - some keys overlap request</li>
* <li>populated {@code CacheLoaderWriter} - some keys overlap</li>
* <li>some {@link CacheLoaderWriter#writeAll(Iterable)} calls fail</li>
* </ul>
*/
@Test
public void testPutAllStoreSomeOverlapWriterNoOverlapSomeFail() throws Exception {
final Map<String, String> originalStoreContent = getEntryMap(KEY_SET_A, KEY_SET_B);
final FakeStore fakeStore = new FakeStore(originalStoreContent);
this.store = spy(fakeStore);
final Map<String, String> originalWriterContent = getEntryMap(KEY_SET_A, KEY_SET_B, KEY_SET_D);
final FakeCacheLoaderWriter fakeLoaderWriter = new FakeCacheLoaderWriter(originalWriterContent, KEY_SET_C);
this.cacheLoaderWriter = spy(fakeLoaderWriter);
final InternalCache<String, String> ehcache = this.getEhcache(this.cacheLoaderWriter);
final Map<String, String> contentUpdates = getAltEntryMap("new_", fanIn(KEY_SET_A, KEY_SET_C));
final Set<String> expectedFailures = KEY_SET_C;
final Map<String, String> expectedSuccesses = copyWithout(contentUpdates, expectedFailures);
try {
ehcache.putAll(contentUpdates);
fail();
} catch (BulkCacheWritingException e) {
// Expected
assertThat(e.getSuccesses(), Matchers.<Set<?>>equalTo(expectedSuccesses.keySet()));
assertThat(e.getFailures().keySet(), Matchers.<Set<?>>equalTo(expectedFailures));
}
verify(this.store, atLeast(1)).bulkCompute(this.bulkComputeSetCaptor.capture(), getAnyEntryIterableFunction());
assertThat(this.getBulkComputeArgs(), equalTo(contentUpdates.keySet()));
assertThat(fakeStore.getEntryMap(), equalTo(union(originalStoreContent, expectedSuccesses)));
assertThat(fakeLoaderWriter.getEntryMap(), equalTo(union(originalWriterContent, expectedSuccesses)));
verifyZeroInteractions(this.spiedResilienceStrategy);
validateStats(ehcache, EnumSet.noneOf(CacheOperationOutcomes.PutOutcome.class));
validateStats(ehcache, EnumSet.of(CacheOperationOutcomes.PutAllOutcome.FAILURE));
assertThat(ehcache.getBulkMethodEntries().get(BulkOps.PUT_ALL).intValue(), is(KEY_SET_A.size()));
}
/**
* Tests {@link EhcacheWithLoaderWriter#putAll(Map)} for
* <ul>
* <li>non-empty request map</li>
* <li>populated {@code Store} - some keys overlap request</li>
* <li>{@link Store#bulkCompute} throws before accessing writer</li>
* <li>populated {@code CacheLoaderWriter} - some keys overlap</li>
* <li>some {@link CacheLoaderWriter#writeAll(Iterable)} calls fail</li>
* </ul>
*/
@Test
public void testPutAllStoreSomeOverlapStoreAccessExceptionBeforeWriterNoOverlapSomeFail() throws Exception {
final Map<String, String> originalStoreContent = getEntryMap(KEY_SET_A, KEY_SET_B);
final FakeStore fakeStore = new FakeStore(originalStoreContent);
this.store = spy(fakeStore);
doThrow(new StoreAccessException("")).when(this.store)
.bulkCompute(getAnyStringSet(), getAnyEntryIterableFunction());
final Map<String, String> originalWriterContent = getEntryMap(KEY_SET_A, KEY_SET_B, KEY_SET_D);
final FakeCacheLoaderWriter fakeLoaderWriter = new FakeCacheLoaderWriter(originalWriterContent, KEY_SET_C);
this.cacheLoaderWriter = spy(fakeLoaderWriter);
final InternalCache<String, String> ehcache = this.getEhcache(this.cacheLoaderWriter);
final Map<String, String> contentUpdates = getAltEntryMap("new_", fanIn(KEY_SET_A, KEY_SET_C));
final Set<String> expectedFailures = KEY_SET_C;
final Map<String, String> expectedSuccesses = copyWithout(contentUpdates, expectedFailures);
try {
ehcache.putAll(contentUpdates);
fail();
} catch (BulkCacheWritingException e) {
// Expected
}
final InOrder ordered = inOrder(this.store, this.cacheLoaderWriter, this.spiedResilienceStrategy);
ordered.verify(this.store, atLeast(1)).bulkCompute(this.bulkComputeSetCaptor.capture(), getAnyEntryIterableFunction());
assertThat(this.getBulkComputeArgs(), everyItem(isIn(contentUpdates.keySet())));
// ResilienceStrategy invoked; no assertions about Store content
ordered.verify(this.cacheLoaderWriter, atLeast(1)).writeAll(getAnyEntryIterable());
assertThat(fakeLoaderWriter.getEntryMap(), equalTo(union(originalWriterContent, expectedSuccesses)));
ordered.verify(this.spiedResilienceStrategy)
.putAllFailure(eq(contentUpdates), any(StoreAccessException.class), this.bulkExceptionCaptor.capture());
assertThat(this.bulkExceptionCaptor.getValue().getSuccesses(), Matchers.<Set<?>>equalTo(expectedSuccesses.keySet()));
assertThat(this.bulkExceptionCaptor.getValue().getFailures().keySet(), Matchers.<Set<?>>equalTo(expectedFailures));
validateStats(ehcache, EnumSet.noneOf(CacheOperationOutcomes.PutOutcome.class));
validateStats(ehcache, EnumSet.of(CacheOperationOutcomes.PutAllOutcome.FAILURE));
assertThat(ehcache.getBulkMethodEntries().get(BulkOps.PUT_ALL).intValue(), is(0));
}
/**
* Tests {@link EhcacheWithLoaderWriter#putAll(Map)} for
* <ul>
* <li>non-empty request map</li>
* <li>populated {@code Store} - some keys overlap request</li>
* <li>{@link Store#bulkCompute} throws after accessing writer</li>
* <li>populated {@code CacheLoaderWriter} - some keys overlap</li>
* <li>some {@link CacheLoaderWriter#writeAll(Iterable)} calls fail</li>
* </ul>
*/
@Test
public void testPutAllStoreSomeOverlapStoreAccessExceptionAfterWriterNoOverlapSomeFail() throws Exception {
final Map<String, String> originalStoreContent = getEntryMap(KEY_SET_A, KEY_SET_B);
final FakeStore fakeStore = new FakeStore(originalStoreContent, Collections.singleton("keyA3"));
this.store = spy(fakeStore);
final Map<String, String> originalWriterContent = getEntryMap(KEY_SET_A, KEY_SET_B, KEY_SET_D);
final FakeCacheLoaderWriter fakeLoaderWriter = new FakeCacheLoaderWriter(originalWriterContent, KEY_SET_C);
this.cacheLoaderWriter = spy(fakeLoaderWriter);
final InternalCache<String, String> ehcache = this.getEhcache(this.cacheLoaderWriter);
final Map<String, String> contentUpdates = getAltEntryMap("new_", fanIn(KEY_SET_A, KEY_SET_C));
final Set<String> expectedFailures = KEY_SET_C;
final Map<String, String> expectedSuccesses = copyWithout(contentUpdates, expectedFailures);
try {
ehcache.putAll(contentUpdates);
fail();
} catch (BulkCacheWritingException e) {
// Expected
}
final InOrder ordered = inOrder(this.store, this.cacheLoaderWriter, this.spiedResilienceStrategy);
ordered.verify(this.store, atLeast(1)).bulkCompute(this.bulkComputeSetCaptor.capture(), getAnyEntryIterableFunction());
assertThat(this.getBulkComputeArgs(), everyItem(isIn(contentUpdates.keySet())));
// ResilienceStrategy invoked; no assertions about Store content
ordered.verify(this.cacheLoaderWriter, atLeast(1)).writeAll(getAnyEntryIterable());
assertThat(fakeLoaderWriter.getEntryMap(), equalTo(union(originalWriterContent, expectedSuccesses)));
ordered.verify(this.spiedResilienceStrategy)
.putAllFailure(eq(contentUpdates), any(StoreAccessException.class), this.bulkExceptionCaptor.capture());
assertThat(this.bulkExceptionCaptor.getValue().getSuccesses(), Matchers.<Set<?>>equalTo(expectedSuccesses.keySet()));
assertThat(this.bulkExceptionCaptor.getValue().getFailures().keySet(), Matchers.<Set<?>>equalTo(expectedFailures));
validateStats(ehcache, EnumSet.noneOf(CacheOperationOutcomes.PutOutcome.class));
validateStats(ehcache, EnumSet.of(CacheOperationOutcomes.PutAllOutcome.FAILURE));
assertThat(ehcache.getBulkMethodEntries().get(BulkOps.PUT_ALL).intValue(), is(0));
}
/**
* Tests {@link EhcacheWithLoaderWriter#putAll(Map)} for
* <ul>
* <li>non-empty request map</li>
* <li>populated {@code Store} - some keys overlap request</li>
* <li>populated {@code CacheLoaderWriter} - some keys overlap</li>
* <li>some {@link CacheLoaderWriter#writeAll(Iterable)} calls fail</li>
* <li>at least one {@link CacheLoaderWriter#writeAll(Iterable)} call aborts</li>
* </ul>
*/
@Test
public void testPutAllStoreSomeOverlapWriterNoOverlapSomeFailWithAbort() throws Exception {
final Map<String, String> originalStoreContent = getEntryMap(KEY_SET_A, KEY_SET_B);
final FakeStore fakeStore = new FakeStore(originalStoreContent);
this.store = spy(fakeStore);
final Map<String, String> originalWriterContent = getEntryMap(KEY_SET_A, KEY_SET_B, KEY_SET_D);
final FakeCacheLoaderWriter fakeLoaderWriter = new FakeCacheLoaderWriter(originalWriterContent, KEY_SET_C);
fakeLoaderWriter.setCompleteFailureKey("keyC4");
this.cacheLoaderWriter = spy(fakeLoaderWriter);
final InternalCache<String, String> ehcache = this.getEhcache(this.cacheLoaderWriter);
final Map<String, String> contentUpdates = getAltEntryMap("new_", fanIn(KEY_SET_A, KEY_SET_C));
final Set<String> expectedFailures = KEY_SET_C;
final Map<String, String> expectedSuccesses = copyWithout(contentUpdates, expectedFailures);
try {
ehcache.putAll(contentUpdates);
fail();
} catch (BulkCacheWritingException e) {
// Expected
assertThat(e.getSuccesses(), Matchers.<Set<?>>equalTo(expectedSuccesses.keySet()));
assertThat(e.getFailures().keySet(), Matchers.<Set<?>>equalTo(expectedFailures));
}
verify(this.store, atLeast(1)).bulkCompute(this.bulkComputeSetCaptor.capture(), getAnyEntryIterableFunction());
assertThat(this.getBulkComputeArgs(), everyItem(isIn(contentUpdates.keySet())));
assertThat(fakeStore.getEntryMap(), equalTo(union(originalStoreContent, expectedSuccesses)));
verify(this.cacheLoaderWriter, atLeast(1)).writeAll(getAnyEntryIterable());
assertThat(fakeLoaderWriter.getEntryMap(), equalTo(union(originalWriterContent, expectedSuccesses)));
verifyZeroInteractions(this.spiedResilienceStrategy);
validateStats(ehcache, EnumSet.noneOf(CacheOperationOutcomes.PutOutcome.class));
validateStats(ehcache, EnumSet.of(CacheOperationOutcomes.PutAllOutcome.FAILURE));
assertThat(ehcache.getBulkMethodEntries().get(BulkOps.PUT_ALL).intValue(), is(expectedSuccesses.size()));
}
/**
* Tests {@link EhcacheWithLoaderWriter#putAll(Map)} for
* <ul>
* <li>non-empty request map</li>
* <li>populated {@code Store} - some keys overlap request</li>
* <li>{@link Store#bulkCompute} throws before accessing writer</li>
* <li>populated {@code CacheLoaderWriter} - some keys overlap</li>
* <li>some {@link CacheLoaderWriter#writeAll(Iterable)} calls fail</li>
* <li>at least one {@link CacheLoaderWriter#writeAll(Iterable)} call aborts</li>
* </ul>
*/
@Test
public void testPutAllStoreSomeOverlapStoreAccessExceptionBeforeWriterOverlapSomeFailWithAbort() throws Exception {
final Map<String, String> originalStoreContent = getEntryMap(KEY_SET_A, KEY_SET_B);
final FakeStore fakeStore = new FakeStore(originalStoreContent);
this.store = spy(fakeStore);
doThrow(new StoreAccessException("")).when(this.store)
.bulkCompute(getAnyStringSet(), getAnyEntryIterableFunction());
final Map<String, String> originalWriterContent = getEntryMap(KEY_SET_A, KEY_SET_B, KEY_SET_D);
final FakeCacheLoaderWriter fakeLoaderWriter = new FakeCacheLoaderWriter(originalWriterContent, KEY_SET_C);
fakeLoaderWriter.setCompleteFailureKey("keyC4");
this.cacheLoaderWriter = spy(fakeLoaderWriter);
final InternalCache<String, String> ehcache = this.getEhcache(this.cacheLoaderWriter);
final Map<String, String> contentUpdates = getAltEntryMap("new_", fanIn(KEY_SET_A, KEY_SET_C));
final Set<String> expectedFailures = contentUpdates.keySet();
try {
ehcache.putAll(contentUpdates);
fail();
} catch (BulkCacheWritingException e) {
// Expected
}
final InOrder ordered = inOrder(this.store, this.cacheLoaderWriter, this.spiedResilienceStrategy);
ordered.verify(this.store, atLeast(1)).bulkCompute(this.bulkComputeSetCaptor.capture(), getAnyEntryIterableFunction());
assertThat(this.getBulkComputeArgs(), everyItem(isIn(contentUpdates.keySet())));
// ResilienceStrategy invoked; no assertions about Store content
ordered.verify(this.cacheLoaderWriter, atLeast(1)).writeAll(getAnyEntryIterable());
ordered.verify(this.spiedResilienceStrategy)
.putAllFailure(eq(contentUpdates), any(StoreAccessException.class), this.bulkExceptionCaptor.capture());
@SuppressWarnings("unchecked")
final Set<String> bcweSuccesses = (Set<String>)this.bulkExceptionCaptor.getValue().getSuccesses();
@SuppressWarnings("unchecked")
final Map<String, Exception> bcweFailures = (Map<String, Exception>)this.bulkExceptionCaptor.getValue().getFailures();
assertThat(bcweSuccesses.isEmpty(), is(true));
assertThat(bcweFailures.keySet(), equalTo(expectedFailures));
assertThatAllStoreEntriesWithoutFailuresMatchWriterState(fakeStore, fakeLoaderWriter, bcweFailures);
validateStats(ehcache, EnumSet.noneOf(CacheOperationOutcomes.PutOutcome.class));
validateStats(ehcache, EnumSet.of(CacheOperationOutcomes.PutAllOutcome.FAILURE));
assertThat(ehcache.getBulkMethodEntries().get(BulkOps.PUT_ALL).intValue(), is(0));
}
/**
* Tests {@link EhcacheWithLoaderWriter#putAll(Map)} for
* <ul>
* <li>non-empty request map</li>
* <li>populated {@code Store} - some keys overlap request</li>
* <li>{@link Store#bulkCompute} throws after accessing writer</li>
* <li>populated {@code CacheLoaderWriter} - some keys overlap</li>
* <li>some {@link CacheLoaderWriter#writeAll(Iterable)} calls fail</li>
* <li>at least one {@link CacheLoaderWriter#writeAll(Iterable)} call aborts</li>
* </ul>
*/
@Test
public void testPutAllStoreSomeOverlapStoreAccessExceptionAfterWriterOverlapSomeFailWithAbort() throws Exception {
final Map<String, String> originalStoreContent = getEntryMap(KEY_SET_A, KEY_SET_B);
final FakeStore fakeStore = new FakeStore(originalStoreContent, Collections.singleton("keyA3"));
this.store = spy(fakeStore);
final Map<String, String> originalWriterContent = getEntryMap(KEY_SET_A, KEY_SET_B, KEY_SET_D);
final FakeCacheLoaderWriter fakeLoaderWriter = new FakeCacheLoaderWriter(originalWriterContent, KEY_SET_C);
fakeLoaderWriter.setCompleteFailureKey("keyC4");
this.cacheLoaderWriter = spy(fakeLoaderWriter);
final InternalCache<String, String> ehcache = this.getEhcache(this.cacheLoaderWriter);
final Map<String, String> contentUpdates = getAltEntryMap("new_", fanIn(KEY_SET_A, KEY_SET_C));
final Set<String> expectedFailures = KEY_SET_C;
final Map<String, String> expectedSuccesses = copyWithout(contentUpdates, expectedFailures);
try {
ehcache.putAll(contentUpdates);
fail();
} catch (BulkCacheWritingException e) {
// Expected
}
final InOrder ordered = inOrder(this.store, this.cacheLoaderWriter, this.spiedResilienceStrategy);
ordered.verify(this.store, atLeast(1)).bulkCompute(this.bulkComputeSetCaptor.capture(), getAnyEntryIterableFunction());
assertThat(this.getBulkComputeArgs(), everyItem(isIn(contentUpdates.keySet())));
// ResilienceStrategy invoked; no assertions about Store content
ordered.verify(this.cacheLoaderWriter, atLeast(1)).writeAll(getAnyEntryIterable());
ordered.verify(this.spiedResilienceStrategy)
.putAllFailure(eq(contentUpdates), any(StoreAccessException.class), this.bulkExceptionCaptor.capture());
@SuppressWarnings("unchecked")
final Set<String> bcweSuccesses = (Set<String>)this.bulkExceptionCaptor.getValue().getSuccesses();
@SuppressWarnings("unchecked")
final Map<String, Exception> bcweFailures = (Map<String, Exception>)this.bulkExceptionCaptor.getValue().getFailures();
assertThat(union(bcweSuccesses, bcweFailures.keySet()), equalTo(contentUpdates.keySet()));
assertThat(Collections.disjoint(bcweSuccesses, bcweFailures.keySet()), is(true));
assertThatAllStoreEntriesWithoutFailuresMatchWriterState(fakeStore, fakeLoaderWriter, bcweFailures);
validateStats(ehcache, EnumSet.noneOf(CacheOperationOutcomes.PutOutcome.class));
validateStats(ehcache, EnumSet.of(CacheOperationOutcomes.PutAllOutcome.FAILURE));
assertThat(ehcache.getBulkMethodEntries().get(BulkOps.PUT_ALL).intValue(), is(0));
}
/**
* Tests {@link EhcacheWithLoaderWriter#putAll(Map)} for
* <ul>
* <li>non-empty request map</li>
* <li>populated {@code Store} - some keys overlap request</li>
* <li>populated {@code CacheLoaderWriter} - some keys overlap</li>
* <li>all {@link CacheLoaderWriter#writeAll(Iterable)} calls fail</li>
* </ul>
*/
@Test
public void testPutAllStoreSomeOverlapWriterNoOverlapAllFail() throws Exception {
final Map<String, String> originalStoreContent = getEntryMap(KEY_SET_A, KEY_SET_B);
final FakeStore fakeStore = new FakeStore(originalStoreContent);
this.store = spy(fakeStore);
final Map<String, String> originalWriterContent = getEntryMap(KEY_SET_A, KEY_SET_B, KEY_SET_D);
final FakeCacheLoaderWriter fakeLoaderWriter = new FakeCacheLoaderWriter(originalWriterContent);
this.cacheLoaderWriter = spy(fakeLoaderWriter);
doThrow(new Exception("writeAll failed")).when(this.cacheLoaderWriter).writeAll(getAnyEntryIterable());
final InternalCache<String, String> ehcache = this.getEhcache(this.cacheLoaderWriter);
final Map<String, String> contentUpdates = getAltEntryMap("new_", fanIn(KEY_SET_A, KEY_SET_C));
try {
ehcache.putAll(contentUpdates);
fail();
} catch (BulkCacheWritingException e) {
// Expected
assertThat(e.getSuccesses(), empty());
assertThat(e.getFailures().keySet(), Matchers.<Set<?>>equalTo(contentUpdates.keySet()));
}
verify(this.store, atLeast(1)).bulkCompute(this.bulkComputeSetCaptor.capture(), getAnyEntryIterableFunction());
assertThat(this.getBulkComputeArgs(), everyItem(isIn(contentUpdates.keySet())));
assertThat(fakeStore.getEntryMap(), equalTo(copyWithout(originalStoreContent, contentUpdates.keySet())));
verify(this.cacheLoaderWriter, atLeast(1)).writeAll(getAnyEntryIterable());
verifyZeroInteractions(this.spiedResilienceStrategy);
validateStats(ehcache, EnumSet.noneOf(CacheOperationOutcomes.PutOutcome.class));
validateStats(ehcache, EnumSet.of(CacheOperationOutcomes.PutAllOutcome.FAILURE));
assertThat(ehcache.getBulkMethodEntries().get(BulkOps.PUT_ALL).intValue(), is(0));
}
/**
* Tests {@link EhcacheWithLoaderWriter#putAll(Map)} for
* <ul>
* <li>non-empty request map</li>
* <li>populated {@code Store} - some keys overlap request</li>
* <li>{@link Store#bulkCompute} throws before accessing writer</li>
* <li>populated {@code CacheLoaderWriter} - some keys overlap</li>
* <li>all {@link CacheLoaderWriter#writeAll(Iterable)} calls fail</li>
* </ul>
*/
@Test
public void testPutAllStoreSomeOverlapStoreAccessExceptionBeforeWriterNoOverlapAllFail() throws Exception {
final Map<String, String> originalStoreContent = getEntryMap(KEY_SET_A, KEY_SET_B);
final FakeStore fakeStore = new FakeStore(originalStoreContent);
this.store = spy(fakeStore);
doThrow(new StoreAccessException("")).when(this.store)
.bulkCompute(getAnyStringSet(), getAnyEntryIterableFunction());
final Map<String, String> originalWriterContent = getEntryMap(KEY_SET_A, KEY_SET_B, KEY_SET_D);
final FakeCacheLoaderWriter fakeLoaderWriter = new FakeCacheLoaderWriter(originalWriterContent);
this.cacheLoaderWriter = spy(fakeLoaderWriter);
doThrow(new Exception("writeAll failed")).when(this.cacheLoaderWriter).writeAll(getAnyEntryIterable());
final InternalCache<String, String> ehcache = this.getEhcache(this.cacheLoaderWriter);
final Map<String, String> contentUpdates = getAltEntryMap("new_", fanIn(KEY_SET_A, KEY_SET_C));
try {
ehcache.putAll(contentUpdates);
fail();
} catch (BulkCacheWritingException e) {
// Expected
}
final InOrder ordered = inOrder(this.store, this.cacheLoaderWriter, this.spiedResilienceStrategy);
ordered.verify(this.store, atLeast(1)).bulkCompute(this.bulkComputeSetCaptor.capture(),
getAnyEntryIterableFunction());
assertThat(this.getBulkComputeArgs(), everyItem(isIn(contentUpdates.keySet())));
// ResilienceStrategy invoked; no assertions about Store content
ordered.verify(this.cacheLoaderWriter, atLeast(1)).writeAll(getAnyEntryIterable());
ordered.verify(this.spiedResilienceStrategy)
.putAllFailure(eq(contentUpdates), any(StoreAccessException.class), this.bulkExceptionCaptor.capture());
assertThat(this.bulkExceptionCaptor.getValue().getSuccesses(), empty());
assertThat(this.bulkExceptionCaptor.getValue().getFailures().keySet(),
Matchers.<Set<?>>equalTo(contentUpdates.keySet()));
validateStats(ehcache, EnumSet.noneOf(CacheOperationOutcomes.PutOutcome.class));
validateStats(ehcache, EnumSet.of(CacheOperationOutcomes.PutAllOutcome.FAILURE));
assertThat(ehcache.getBulkMethodEntries().get(BulkOps.PUT_ALL).intValue(), is(0));
}
/**
* Tests {@link EhcacheWithLoaderWriter#putAll(Map)} for
* <ul>
* <li>non-empty request map</li>
* <li>populated {@code Store} - some keys overlap request</li>
* <li>{@link Store#bulkCompute} throws after accessing writer</li>
* <li>populated {@code CacheLoaderWriter} - some keys overlap</li>
* <li>all {@link CacheLoaderWriter#writeAll(Iterable)} calls fail</li>
* </ul>
*/
@Test
public void testPutAllStoreSomeOverlapStoreAccessExceptionAfterWriterNoOverlapAllFail() throws Exception {
final Map<String, String> originalStoreContent = getEntryMap(KEY_SET_A, KEY_SET_B);
final FakeStore fakeStore = new FakeStore(originalStoreContent, Collections.singleton("keyA3"));
this.store = spy(fakeStore);
final Map<String, String> originalWriterContent = getEntryMap(KEY_SET_A, KEY_SET_B, KEY_SET_D);
final FakeCacheLoaderWriter fakeLoaderWriter = new FakeCacheLoaderWriter(originalWriterContent);
this.cacheLoaderWriter = spy(fakeLoaderWriter);
doThrow(new Exception("writeAll failed")).when(this.cacheLoaderWriter).writeAll(getAnyEntryIterable());
final InternalCache<String, String> ehcache = this.getEhcache(this.cacheLoaderWriter);
final Map<String, String> contentUpdates = getAltEntryMap("new_", fanIn(KEY_SET_A, KEY_SET_C));
try {
ehcache.putAll(contentUpdates);
fail();
} catch (BulkCacheWritingException e) {
// Expected
}
final InOrder ordered = inOrder(this.store, this.cacheLoaderWriter, this.spiedResilienceStrategy);
ordered.verify(this.store, atLeast(1)).bulkCompute(this.bulkComputeSetCaptor.capture(),
getAnyEntryIterableFunction());
assertThat(this.getBulkComputeArgs(), everyItem(isIn(contentUpdates.keySet())));
// ResilienceStrategy invoked; no assertions about Store content
ordered.verify(this.cacheLoaderWriter, atLeast(1)).writeAll(getAnyEntryIterable());
ordered.verify(this.spiedResilienceStrategy)
.putAllFailure(eq(contentUpdates), any(StoreAccessException.class), this.bulkExceptionCaptor.capture());
assertThat(this.bulkExceptionCaptor.getValue().getSuccesses(), empty());
assertThat(this.bulkExceptionCaptor.getValue().getFailures().keySet(),
Matchers.<Set<?>>equalTo(contentUpdates.keySet()));
validateStats(ehcache, EnumSet.noneOf(CacheOperationOutcomes.PutOutcome.class));
validateStats(ehcache, EnumSet.of(CacheOperationOutcomes.PutAllOutcome.FAILURE));
assertThat(ehcache.getBulkMethodEntries().get(BulkOps.PUT_ALL).intValue(), is(0));
}
/**
* Tests {@link EhcacheWithLoaderWriter#putAll(Map)} for
* <ul>
* <li>non-empty request map</li>
* <li>populated {@code Store} - some keys overlap request</li>
* <li>populated {@code CacheLoaderWriter} - some keys overlap</li>
* <li>no {@link CacheLoaderWriter#writeAll(Iterable)} calls fail</li>
* </ul>
*/
@Test
public void testPutAllStoreSomeOverlapWriterSomeOverlapNoneFail() throws Exception {
final Map<String, String> originalStoreContent = getEntryMap(KEY_SET_A, KEY_SET_B);
final FakeStore fakeStore = new FakeStore(originalStoreContent);
this.store = spy(fakeStore);
final Map<String, String> originalWriterContent = getEntryMap(KEY_SET_A, KEY_SET_B, KEY_SET_C);
final FakeCacheLoaderWriter fakeLoaderWriter = new FakeCacheLoaderWriter(originalWriterContent);
this.cacheLoaderWriter = spy(fakeLoaderWriter);
final InternalCache<String, String> ehcache = this.getEhcache(this.cacheLoaderWriter);
final Map<String, String> contentUpdates = getAltEntryMap("new_", fanIn(KEY_SET_A, KEY_SET_C, KEY_SET_D));
ehcache.putAll(contentUpdates);
verify(this.store, atLeast(1)).bulkCompute(this.bulkComputeSetCaptor.capture(), getAnyEntryIterableFunction());
assertThat(this.getBulkComputeArgs(), equalTo(contentUpdates.keySet()));
assertThat(fakeStore.getEntryMap(), equalTo(union(originalStoreContent, contentUpdates)));
verify(this.cacheLoaderWriter, atLeast(1)).writeAll(getAnyEntryIterable());
assertThat(fakeLoaderWriter.getEntryMap(), equalTo(union(originalWriterContent, contentUpdates)));
verifyZeroInteractions(this.spiedResilienceStrategy);
validateStats(ehcache, EnumSet.noneOf(CacheOperationOutcomes.PutOutcome.class));
validateStats(ehcache, EnumSet.of(CacheOperationOutcomes.PutAllOutcome.SUCCESS));
assertThat(ehcache.getBulkMethodEntries().get(BulkOps.PUT_ALL).intValue(), is(contentUpdates.size()));
}
/**
* Tests {@link EhcacheWithLoaderWriter#putAll(Map)} for
* <ul>
* <li>non-empty request map</li>
* <li>populated {@code Store} - some keys overlap request</li>
* <li>{@link Store#bulkCompute} throws before accessing writer</li>
* <li>populated {@code CacheLoaderWriter} - some keys overlap</li>
* <li>no {@link CacheLoaderWriter#writeAll(Iterable)} calls fail</li>
* </ul>
*/
@Test
public void testPutAllStoreSomeOverlapStoreAccessExceptionBeforeWriterSomeOverlapNoneFail() throws Exception {
final Map<String, String> originalStoreContent = getEntryMap(KEY_SET_A, KEY_SET_B);
final FakeStore fakeStore = new FakeStore(originalStoreContent);
this.store = spy(fakeStore);
doThrow(new StoreAccessException("")).when(this.store)
.bulkCompute(getAnyStringSet(), getAnyEntryIterableFunction());
final Map<String, String> originalWriterContent = getEntryMap(KEY_SET_A, KEY_SET_B, KEY_SET_C);
final FakeCacheLoaderWriter fakeLoaderWriter = new FakeCacheLoaderWriter(originalWriterContent);
this.cacheLoaderWriter = spy(fakeLoaderWriter);
final InternalCache<String, String> ehcache = this.getEhcache(this.cacheLoaderWriter);
final Map<String, String> contentUpdates = getAltEntryMap("new_", fanIn(KEY_SET_A, KEY_SET_C, KEY_SET_D));
ehcache.putAll(contentUpdates);
final InOrder ordered = inOrder(this.store, this.cacheLoaderWriter, this.spiedResilienceStrategy);
ordered.verify(this.store, atLeast(1))
.bulkCompute(this.bulkComputeSetCaptor.capture(), getAnyEntryIterableFunction());
assertThat(this.getBulkComputeArgs(), everyItem(isIn(contentUpdates.keySet())));
// ResilienceStrategy invoked; no assertions about Store content
ordered.verify(this.cacheLoaderWriter, atLeast(1)).writeAll(getAnyEntryIterable());
assertThat(fakeLoaderWriter.getEntryMap(), equalTo(union(originalWriterContent, contentUpdates)));
ordered.verify(this.spiedResilienceStrategy)
.putAllFailure(eq(contentUpdates), any(StoreAccessException.class));
validateStats(ehcache, EnumSet.noneOf(CacheOperationOutcomes.PutOutcome.class));
validateStats(ehcache, EnumSet.of(CacheOperationOutcomes.PutAllOutcome.FAILURE));
assertThat(ehcache.getBulkMethodEntries().get(BulkOps.PUT_ALL).intValue(), is(0));
}
/**
* Tests {@link EhcacheWithLoaderWriter#putAll(Map)} for
* <ul>
* <li>non-empty request map</li>
* <li>populated {@code Store} - some keys overlap request</li>
* <li>{@link Store#bulkCompute} throws after accessing writer</li>
* <li>populated {@code CacheLoaderWriter} - some keys overlap</li>
* <li>no {@link CacheLoaderWriter#writeAll(Iterable)} calls fail</li>
* </ul>
*/
@Test
public void testPutAllStoreSomeOverlapStoreAccessExceptionAfterWriterSomeOverlapNoneFail() throws Exception {
final Map<String, String> originalStoreContent = getEntryMap(KEY_SET_A, KEY_SET_B);
final FakeStore fakeStore = new FakeStore(originalStoreContent, Collections.singleton("keyA3"));
this.store = spy(fakeStore);
final Map<String, String> originalWriterContent = getEntryMap(KEY_SET_A, KEY_SET_B, KEY_SET_C);
final FakeCacheLoaderWriter fakeLoaderWriter = new FakeCacheLoaderWriter(originalWriterContent);
this.cacheLoaderWriter = spy(fakeLoaderWriter);
final InternalCache<String, String> ehcache = this.getEhcache(this.cacheLoaderWriter);
final Map<String, String> contentUpdates = getAltEntryMap("new_", fanIn(KEY_SET_A, KEY_SET_C, KEY_SET_D));
ehcache.putAll(contentUpdates);
final InOrder ordered = inOrder(this.store, this.cacheLoaderWriter, this.spiedResilienceStrategy);
ordered.verify(this.store, atLeast(1))
.bulkCompute(this.bulkComputeSetCaptor.capture(), getAnyEntryIterableFunction());
assertThat(this.getBulkComputeArgs(), everyItem(isIn(contentUpdates.keySet())));
// ResilienceStrategy invoked; no assertions about Store content
ordered.verify(this.cacheLoaderWriter, atLeast(1)).writeAll(getAnyEntryIterable());
assertThat(fakeLoaderWriter.getEntryMap(), equalTo(union(originalWriterContent, contentUpdates)));
ordered.verify(this.spiedResilienceStrategy)
.putAllFailure(eq(contentUpdates), any(StoreAccessException.class));
validateStats(ehcache, EnumSet.noneOf(CacheOperationOutcomes.PutOutcome.class));
validateStats(ehcache, EnumSet.of(CacheOperationOutcomes.PutAllOutcome.FAILURE));
assertThat(ehcache.getBulkMethodEntries().get(BulkOps.PUT_ALL).intValue(), is(0));
}
/**
* Tests {@link EhcacheWithLoaderWriter#putAll(Map)} for
* <ul>
* <li>non-empty request map</li>
* <li>populated {@code Store} - some keys overlap request</li>
* <li>populated {@code CacheLoaderWriter} - some keys overlap</li>
* <li>some {@link CacheLoaderWriter#writeAll(Iterable)} calls fail</li>
* </ul>
*/
@Test
public void testPutAllStoreSomeOverlapWriterSomeOverlapSomeFail() throws Exception {
final Map<String, String> originalStoreContent = getEntryMap(KEY_SET_A, KEY_SET_B);
final FakeStore fakeStore = new FakeStore(originalStoreContent);
this.store = spy(fakeStore);
final Map<String, String> originalWriterContent = getEntryMap(KEY_SET_A, KEY_SET_B, KEY_SET_C);
final FakeCacheLoaderWriter fakeLoaderWriter = new FakeCacheLoaderWriter(originalWriterContent, KEY_SET_D);
this.cacheLoaderWriter = spy(fakeLoaderWriter);
final InternalCache<String, String> ehcache = this.getEhcache(this.cacheLoaderWriter);
final Map<String, String> contentUpdates = getAltEntryMap("new_", fanIn(KEY_SET_A, KEY_SET_C, KEY_SET_D));
final Set<String> expectedFailures = KEY_SET_D;
final Map<String, String> expectedSuccesses = copyWithout(contentUpdates, expectedFailures);
try {
ehcache.putAll(contentUpdates);
fail();
} catch (BulkCacheWritingException e) {
// Expected
assertThat(e.getSuccesses(), Matchers.<Set<?>>equalTo(expectedSuccesses.keySet()));
assertThat(e.getFailures().keySet(), Matchers.<Set<?>>equalTo(expectedFailures));
}
verify(this.store, atLeast(1)).bulkCompute(this.bulkComputeSetCaptor.capture(), getAnyEntryIterableFunction());
assertThat(this.getBulkComputeArgs(), everyItem(isIn(contentUpdates.keySet())));
assertThat(fakeStore.getEntryMap(), equalTo(union(originalStoreContent, expectedSuccesses)));
verify(this.cacheLoaderWriter, atLeast(1)).writeAll(getAnyEntryIterable());
assertThat(fakeLoaderWriter.getEntryMap(), equalTo(union(originalWriterContent, expectedSuccesses)));
verifyZeroInteractions(this.spiedResilienceStrategy);
validateStats(ehcache, EnumSet.noneOf(CacheOperationOutcomes.PutOutcome.class));
validateStats(ehcache, EnumSet.of(CacheOperationOutcomes.PutAllOutcome.FAILURE));
assertThat(ehcache.getBulkMethodEntries().get(BulkOps.PUT_ALL).intValue(), is(expectedSuccesses.size()));
}
/**
* Tests {@link EhcacheWithLoaderWriter#putAll(Map)} for
* <ul>
* <li>non-empty request map</li>
* <li>populated {@code Store} - some keys overlap request</li>
* <li>{@link Store#bulkCompute} throws before accessing writer</li>
* <li>populated {@code CacheLoaderWriter} - some keys overlap</li>
* <li>some {@link CacheLoaderWriter#writeAll(Iterable)} calls fail</li>
* </ul>
*/
@Test
public void testPutAllStoreSomeOverlapStoreAccessExceptionBeforeWriterSomeOverlapSomeFail() throws Exception {
final Map<String, String> originalStoreContent = getEntryMap(KEY_SET_A, KEY_SET_B);
final FakeStore fakeStore = new FakeStore(originalStoreContent);
this.store = spy(fakeStore);
doThrow(new StoreAccessException("")).when(this.store)
.bulkCompute(getAnyStringSet(), getAnyEntryIterableFunction());
final Map<String, String> originalWriterContent = getEntryMap(KEY_SET_A, KEY_SET_B, KEY_SET_C);
final FakeCacheLoaderWriter fakeLoaderWriter = new FakeCacheLoaderWriter(originalWriterContent, KEY_SET_D);
this.cacheLoaderWriter = spy(fakeLoaderWriter);
final InternalCache<String, String> ehcache = this.getEhcache(this.cacheLoaderWriter);
final Map<String, String> contentUpdates = getAltEntryMap("new_", fanIn(KEY_SET_A, KEY_SET_C, KEY_SET_D));
final Set<String> expectedFailures = KEY_SET_D;
final Map<String, String> expectedSuccesses = copyWithout(contentUpdates, expectedFailures);
try {
ehcache.putAll(contentUpdates);
fail();
} catch (BulkCacheWritingException e) {
// Expected
}
final InOrder ordered = inOrder(this.store, this.cacheLoaderWriter, this.spiedResilienceStrategy);
ordered.verify(this.store, atLeast(1))
.bulkCompute(this.bulkComputeSetCaptor.capture(), getAnyEntryIterableFunction());
assertThat(this.getBulkComputeArgs(), everyItem(isIn(contentUpdates.keySet())));
// ResilienceStrategy invoked; no assertions about Store content
ordered.verify(this.cacheLoaderWriter, atLeast(1)).writeAll(getAnyEntryIterable());
assertThat(fakeLoaderWriter.getEntryMap(), equalTo(union(originalWriterContent, expectedSuccesses)));
ordered.verify(this.spiedResilienceStrategy)
.putAllFailure(eq(contentUpdates), any(StoreAccessException.class), this.bulkExceptionCaptor.capture());
assertThat(this.bulkExceptionCaptor.getValue().getSuccesses(), Matchers.<Set<?>>equalTo(expectedSuccesses.keySet()));
assertThat(this.bulkExceptionCaptor.getValue().getFailures().keySet(), Matchers.<Set<?>>equalTo(expectedFailures));
validateStats(ehcache, EnumSet.noneOf(CacheOperationOutcomes.PutOutcome.class));
validateStats(ehcache, EnumSet.of(CacheOperationOutcomes.PutAllOutcome.FAILURE));
assertThat(ehcache.getBulkMethodEntries().get(BulkOps.PUT_ALL).intValue(), is(0));
}
/**
* Tests {@link EhcacheWithLoaderWriter#putAll(Map)} for
* <ul>
* <li>non-empty request map</li>
* <li>populated {@code Store} - some keys overlap request</li>
* <li>{@link Store#bulkCompute} throws after accessing writer</li>
* <li>populated {@code CacheLoaderWriter} - some keys overlap</li>
* <li>some {@link CacheLoaderWriter#writeAll(Iterable)} calls fail</li>
* </ul>
*/
@Test
public void testPutAllStoreSomeOverlapStoreAccessExceptionAfterWriterSomeOverlapSomeFail() throws Exception {
final Map<String, String> originalStoreContent = getEntryMap(KEY_SET_A, KEY_SET_B);
final FakeStore fakeStore = new FakeStore(originalStoreContent, Collections.singleton("keyA3"));
this.store = spy(fakeStore);
final Map<String, String> originalWriterContent = getEntryMap(KEY_SET_A, KEY_SET_B, KEY_SET_C);
final FakeCacheLoaderWriter fakeLoaderWriter = new FakeCacheLoaderWriter(originalWriterContent, KEY_SET_D);
this.cacheLoaderWriter = spy(fakeLoaderWriter);
final InternalCache<String, String> ehcache = this.getEhcache(this.cacheLoaderWriter);
final Map<String, String> contentUpdates = getAltEntryMap("new_", fanIn(KEY_SET_A, KEY_SET_C, KEY_SET_D));
final Set<String> expectedFailures = KEY_SET_D;
final Map<String, String> expectedSuccesses = copyWithout(contentUpdates, expectedFailures);
try {
ehcache.putAll(contentUpdates);
fail();
} catch (BulkCacheWritingException e) {
// Expected
}
final InOrder ordered = inOrder(this.store, this.cacheLoaderWriter, this.spiedResilienceStrategy);
ordered.verify(this.store, atLeast(1))
.bulkCompute(this.bulkComputeSetCaptor.capture(), getAnyEntryIterableFunction());
assertThat(this.getBulkComputeArgs(), everyItem(isIn(contentUpdates.keySet())));
// ResilienceStrategy invoked; no assertions about Store content
ordered.verify(this.cacheLoaderWriter, atLeast(1)).writeAll(getAnyEntryIterable());
assertThat(fakeLoaderWriter.getEntryMap(), equalTo(union(originalWriterContent, expectedSuccesses)));
ordered.verify(this.spiedResilienceStrategy)
.putAllFailure(eq(contentUpdates), any(StoreAccessException.class), this.bulkExceptionCaptor.capture());
assertThat(this.bulkExceptionCaptor.getValue().getSuccesses(), Matchers.<Set<?>>equalTo(expectedSuccesses.keySet()));
assertThat(this.bulkExceptionCaptor.getValue().getFailures().keySet(), Matchers.<Set<?>>equalTo(expectedFailures));
validateStats(ehcache, EnumSet.noneOf(CacheOperationOutcomes.PutOutcome.class));
validateStats(ehcache, EnumSet.of(CacheOperationOutcomes.PutAllOutcome.FAILURE));
assertThat(ehcache.getBulkMethodEntries().get(BulkOps.PUT_ALL).intValue(), is(0));
}
/**
* Tests {@link EhcacheWithLoaderWriter#putAll(Map)} for
* <ul>
* <li>non-empty request map</li>
* <li>populated {@code Store} - some keys overlap request</li>
* <li>populated {@code CacheLoaderWriter} - some keys overlap</li>
* <li>some {@link CacheLoaderWriter#writeAll(Iterable)} calls fail</li>
* <li>at least one {@link CacheLoaderWriter#writeAll(Iterable)} call aborts</li>
* </ul>
*/
@Test
public void testPutAllStoreSomeOverlapWriterSomeOverlapSomeFailWithAbort() throws Exception {
final Map<String, String> originalStoreContent = getEntryMap(KEY_SET_A, KEY_SET_B);
final FakeStore fakeStore = new FakeStore(originalStoreContent);
this.store = spy(fakeStore);
final Map<String, String> originalWriterContent = getEntryMap(KEY_SET_A, KEY_SET_B, KEY_SET_C);
final FakeCacheLoaderWriter fakeLoaderWriter = new FakeCacheLoaderWriter(originalWriterContent, KEY_SET_D);
fakeLoaderWriter.setCompleteFailureKey("keyC4");
this.cacheLoaderWriter = spy(fakeLoaderWriter);
final InternalCache<String, String> ehcache = this.getEhcache(this.cacheLoaderWriter);
final Map<String, String> contentUpdates = getAltEntryMap("new_", fanIn(KEY_SET_A, KEY_SET_C, KEY_SET_D));
final Set<String> expectedFailures = union(KEY_SET_D, Collections.singleton("keyC4"));
final Map<String, String> expectedSuccesses = copyWithout(contentUpdates, expectedFailures);
try {
ehcache.putAll(contentUpdates);
fail();
} catch (BulkCacheWritingException e) {
// Expected
assertThat(e.getSuccesses(), Matchers.<Set<?>>equalTo(expectedSuccesses.keySet()));
assertThat(e.getFailures().keySet(), Matchers.<Set<?>>equalTo(expectedFailures));
}
verify(this.store, atLeast(1)).bulkCompute(this.bulkComputeSetCaptor.capture(), getAnyEntryIterableFunction());
assertThat(this.getBulkComputeArgs(), everyItem(isIn(contentUpdates.keySet())));
assertThat(fakeStore.getEntryMap(), equalTo(union(originalStoreContent, expectedSuccesses)));
verify(this.cacheLoaderWriter, atLeast(1)).writeAll(getAnyEntryIterable());
assertThat(fakeLoaderWriter.getEntryMap(), equalTo(union(originalWriterContent, expectedSuccesses)));
verifyZeroInteractions(this.spiedResilienceStrategy);
validateStats(ehcache, EnumSet.noneOf(CacheOperationOutcomes.PutOutcome.class));
validateStats(ehcache, EnumSet.of(CacheOperationOutcomes.PutAllOutcome.FAILURE));
assertThat(ehcache.getBulkMethodEntries().get(BulkOps.PUT_ALL).intValue(), is(expectedSuccesses.size()));
}
/**
* Tests {@link EhcacheWithLoaderWriter#putAll(Map)} for
* <ul>
* <li>non-empty request map</li>
* <li>populated {@code Store} - some keys overlap request</li>
* <li>{@link Store#bulkCompute} throws before accessing writer</li>
* <li>populated {@code CacheLoaderWriter} - some keys overlap</li>
* <li>some {@link CacheLoaderWriter#writeAll(Iterable)} calls fail</li>
* <li>at least one {@link CacheLoaderWriter#writeAll(Iterable)} call aborts</li>
* </ul>
*/
@Test
public void testPutAllStoreSomeOverlapStoreAccessExceptionBeforeWriterSomeOverlapSomeFailWithAbort() throws Exception {
final Map<String, String> originalStoreContent = getEntryMap(KEY_SET_A, KEY_SET_B);
final FakeStore fakeStore = new FakeStore(originalStoreContent);
this.store = spy(fakeStore);
doThrow(new StoreAccessException("")).when(this.store)
.bulkCompute(getAnyStringSet(), getAnyEntryIterableFunction());
final Map<String, String> originalWriterContent = getEntryMap(KEY_SET_A, KEY_SET_B, KEY_SET_C);
final FakeCacheLoaderWriter fakeLoaderWriter = new FakeCacheLoaderWriter(originalWriterContent, KEY_SET_D);
fakeLoaderWriter.setCompleteFailureKey("keyC4");
this.cacheLoaderWriter = spy(fakeLoaderWriter);
final InternalCache<String, String> ehcache = this.getEhcache(this.cacheLoaderWriter);
final Map<String, String> contentUpdates = getAltEntryMap("new_", fanIn(KEY_SET_A, KEY_SET_C, KEY_SET_D));
final Set<String> expectedFailures = union(KEY_SET_D, Collections.singleton("keyC4"));
final Map<String, String> expectedSuccesses = copyWithout(contentUpdates, expectedFailures);
try {
ehcache.putAll(contentUpdates);
fail();
} catch (BulkCacheWritingException e) {
// Expected
}
final InOrder ordered = inOrder(this.store, this.cacheLoaderWriter, this.spiedResilienceStrategy);
ordered.verify(this.store, atLeast(1)).bulkCompute(this.bulkComputeSetCaptor.capture(), getAnyEntryIterableFunction());
assertThat(this.getBulkComputeArgs(), everyItem(isIn(contentUpdates.keySet())));
// ResilienceStrategy invoked; no assertions about Store content
ordered.verify(this.cacheLoaderWriter, atLeast(1)).writeAll(getAnyEntryIterable());
ordered.verify(this.spiedResilienceStrategy)
.putAllFailure(eq(contentUpdates), any(StoreAccessException.class), this.bulkExceptionCaptor.capture());
@SuppressWarnings("unchecked")
final Set<String> bcweSuccesses = (Set<String>)this.bulkExceptionCaptor.getValue().getSuccesses();
@SuppressWarnings("unchecked")
final Map<String, Exception> bcweFailures = (Map<String, Exception>)this.bulkExceptionCaptor.getValue().getFailures();
assertThat(union(bcweSuccesses, bcweFailures.keySet()), equalTo(contentUpdates.keySet()));
assertThat(Collections.disjoint(bcweSuccesses, bcweFailures.keySet()), is(true));
assertThatAllStoreEntriesWithoutFailuresMatchWriterState(fakeStore, fakeLoaderWriter, bcweFailures);
validateStats(ehcache, EnumSet.noneOf(CacheOperationOutcomes.PutOutcome.class));
validateStats(ehcache, EnumSet.of(CacheOperationOutcomes.PutAllOutcome.FAILURE));
assertThat(ehcache.getBulkMethodEntries().get(BulkOps.PUT_ALL).intValue(), is(0));
}
/**
* Tests {@link EhcacheWithLoaderWriter#putAll(Map)} for
* <ul>
* <li>non-empty request map</li>
* <li>populated {@code Store} - some keys overlap request</li>
* <li>{@link Store#bulkCompute} throws after accessing writer</li>
* <li>populated {@code CacheLoaderWriter} - some keys overlap</li>
* <li>some {@link CacheLoaderWriter#writeAll(Iterable)} calls fail</li>
* <li>at least one {@link CacheLoaderWriter#writeAll(Iterable)} call aborts</li>
* </ul>
*/
@Test
public void testPutAllStoreSomeOverlapStoreAccessExceptionAfterWriterSomeOverlapSomeFailWithAbort() throws Exception {
final Map<String, String> originalStoreContent = getEntryMap(KEY_SET_A, KEY_SET_B);
final FakeStore fakeStore = new FakeStore(originalStoreContent, Collections.singleton("keyA3"));
this.store = spy(fakeStore);
final Map<String, String> originalWriterContent = getEntryMap(KEY_SET_A, KEY_SET_B, KEY_SET_C);
final FakeCacheLoaderWriter fakeLoaderWriter = new FakeCacheLoaderWriter(originalWriterContent, KEY_SET_D);
fakeLoaderWriter.setCompleteFailureKey("keyC4");
this.cacheLoaderWriter = spy(fakeLoaderWriter);
final InternalCache<String, String> ehcache = this.getEhcache(this.cacheLoaderWriter);
final Map<String, String> contentUpdates = getAltEntryMap("new_", fanIn(KEY_SET_A, KEY_SET_C, KEY_SET_D));
final Set<String> expectedFailures = union(KEY_SET_D, Collections.singleton("keyC4"));
final Map<String, String> expectedSuccesses = copyWithout(contentUpdates, expectedFailures);
try {
ehcache.putAll(contentUpdates);
fail();
} catch (BulkCacheWritingException e) {
// Expected
}
final InOrder ordered = inOrder(this.store, this.cacheLoaderWriter, this.spiedResilienceStrategy);
ordered.verify(this.store, atLeast(1)).bulkCompute(this.bulkComputeSetCaptor.capture(), getAnyEntryIterableFunction());
assertThat(this.getBulkComputeArgs(), everyItem(isIn(contentUpdates.keySet())));
// ResilienceStrategy invoked; no assertions about Store content
ordered.verify(this.cacheLoaderWriter, atLeast(1)).writeAll(getAnyEntryIterable());
ordered.verify(this.spiedResilienceStrategy)
.putAllFailure(eq(contentUpdates), any(StoreAccessException.class), this.bulkExceptionCaptor.capture());
@SuppressWarnings("unchecked")
final Set<String> bcweSuccesses = (Set<String>)this.bulkExceptionCaptor.getValue().getSuccesses();
@SuppressWarnings("unchecked")
final Map<String, Exception> bcweFailures = (Map<String, Exception>)this.bulkExceptionCaptor.getValue().getFailures();
assertThat(union(bcweSuccesses, bcweFailures.keySet()), equalTo(contentUpdates.keySet()));
assertThat(Collections.disjoint(bcweSuccesses, bcweFailures.keySet()), is(true));
assertThatAllStoreEntriesWithoutFailuresMatchWriterState(fakeStore, fakeLoaderWriter, bcweFailures);
validateStats(ehcache, EnumSet.noneOf(CacheOperationOutcomes.PutOutcome.class));
validateStats(ehcache, EnumSet.of(CacheOperationOutcomes.PutAllOutcome.FAILURE));
assertThat(ehcache.getBulkMethodEntries().get(BulkOps.PUT_ALL).intValue(), is(0));
}
/**
* Tests {@link EhcacheWithLoaderWriter#putAll(Map)} for
* <ul>
* <li>non-empty request map</li>
* <li>populated {@code Store} - some keys overlap request</li>
* <li>populated {@code CacheLoaderWriter} - some keys overlap</li>
* <li>all {@link CacheLoaderWriter#writeAll(Iterable)} calls fail</li>
* </ul>
*/
@Test
public void testPutAllStoreSomeOverlapWriterSomeOverlapAllFail() throws Exception {
final Map<String, String> originalStoreContent = getEntryMap(KEY_SET_A, KEY_SET_B);
final FakeStore fakeStore = new FakeStore(originalStoreContent);
this.store = spy(fakeStore);
final Map<String, String> originalWriterContent = getEntryMap(KEY_SET_A, KEY_SET_B, KEY_SET_C);
final FakeCacheLoaderWriter fakeLoaderWriter = new FakeCacheLoaderWriter(originalWriterContent);
this.cacheLoaderWriter = spy(fakeLoaderWriter);
doThrow(new Exception("writeAll failed")).when(this.cacheLoaderWriter).writeAll(getAnyEntryIterable());
final InternalCache<String, String> ehcache = this.getEhcache(this.cacheLoaderWriter);
final Map<String, String> contentUpdates = getAltEntryMap("new_", fanIn(KEY_SET_A, KEY_SET_C, KEY_SET_D));
try {
ehcache.putAll(contentUpdates);
fail();
} catch (BulkCacheWritingException e) {
// Expected
assertThat(e.getSuccesses(), empty());
assertThat(e.getFailures().keySet(), Matchers.<Set<?>>equalTo(contentUpdates.keySet()));
}
verify(this.store, atLeast(1)).bulkCompute(this.bulkComputeSetCaptor.capture(), getAnyEntryIterableFunction());
assertThat(this.getBulkComputeArgs(), everyItem(isIn(contentUpdates.keySet())));
assertThat(fakeStore.getEntryMap(), equalTo(getEntryMap(KEY_SET_B)));
verify(this.cacheLoaderWriter, atLeast(1)).writeAll(getAnyEntryIterable());
verifyZeroInteractions(this.spiedResilienceStrategy);
validateStats(ehcache, EnumSet.noneOf(CacheOperationOutcomes.PutOutcome.class));
validateStats(ehcache, EnumSet.of(CacheOperationOutcomes.PutAllOutcome.FAILURE));
assertThat(ehcache.getBulkMethodEntries().get(BulkOps.PUT_ALL).intValue(), is(0));
}
/**
* Tests {@link EhcacheWithLoaderWriter#putAll(Map)} for
* <ul>
* <li>non-empty request map</li>
* <li>populated {@code Store} - some keys overlap request</li>
* <li>{@link Store#bulkCompute} throws before accessing writer</li>
* <li>populated {@code CacheLoaderWriter} - some keys overlap</li>
* <li>all {@link CacheLoaderWriter#writeAll(Iterable)} calls fail</li>
* </ul>
*/
@Test
public void testPutAllStoreSomeOverlapStoreAccessExceptionBeforeWriterSomeOverlapAllFail() throws Exception {
final Map<String, String> originalStoreContent = getEntryMap(KEY_SET_A, KEY_SET_B);
final FakeStore fakeStore = new FakeStore(originalStoreContent);
this.store = spy(fakeStore);
doThrow(new StoreAccessException("")).when(this.store)
.bulkCompute(getAnyStringSet(), getAnyEntryIterableFunction());
final Map<String, String> originalWriterContent = getEntryMap(KEY_SET_A, KEY_SET_B, KEY_SET_C);
final FakeCacheLoaderWriter fakeLoaderWriter = new FakeCacheLoaderWriter(originalWriterContent);
this.cacheLoaderWriter = spy(fakeLoaderWriter);
doThrow(new Exception("writeAll failed")).when(this.cacheLoaderWriter).writeAll(getAnyEntryIterable());
final InternalCache<String, String> ehcache = this.getEhcache(this.cacheLoaderWriter);
final Map<String, String> contentUpdates = getAltEntryMap("new_", fanIn(KEY_SET_A, KEY_SET_C, KEY_SET_D));
try {
ehcache.putAll(contentUpdates);
fail();
} catch (BulkCacheWritingException e) {
// Expected
}
final InOrder ordered = inOrder(this.store, this.cacheLoaderWriter, this.spiedResilienceStrategy);
ordered.verify(this.store, atLeast(1))
.bulkCompute(this.bulkComputeSetCaptor.capture(), getAnyEntryIterableFunction());
assertThat(this.getBulkComputeArgs(), everyItem(isIn(contentUpdates.keySet())));
// ResilienceStrategy invoked; no assertions about Store content
ordered.verify(this.cacheLoaderWriter, atLeast(1)).writeAll(getAnyEntryIterable());
ordered.verify(this.spiedResilienceStrategy)
.putAllFailure(eq(contentUpdates), any(StoreAccessException.class), this.bulkExceptionCaptor.capture());
assertThat(this.bulkExceptionCaptor.getValue().getSuccesses(), empty());
assertThat(this.bulkExceptionCaptor.getValue().getFailures().keySet(),
Matchers.<Set<?>>equalTo(contentUpdates.keySet()));
validateStats(ehcache, EnumSet.noneOf(CacheOperationOutcomes.PutOutcome.class));
validateStats(ehcache, EnumSet.of(CacheOperationOutcomes.PutAllOutcome.FAILURE));
assertThat(ehcache.getBulkMethodEntries().get(BulkOps.PUT_ALL).intValue(), is(0));
}
/**
* Tests {@link EhcacheWithLoaderWriter#putAll(Map)} for
* <ul>
* <li>non-empty request map</li>
* <li>populated {@code Store} - some keys overlap request</li>
* <li>{@link Store#bulkCompute} throws after accessing writer</li>
* <li>populated {@code CacheLoaderWriter} - some keys overlap</li>
* <li>all {@link CacheLoaderWriter#writeAll(Iterable)} calls fail</li>
* </ul>
*/
@Test
public void testPutAllStoreSomeOverlapStoreAccessExceptionAfterWriterSomeOverlapAllFail() throws Exception {
final Map<String, String> originalStoreContent = getEntryMap(KEY_SET_A, KEY_SET_B);
final FakeStore fakeStore = new FakeStore(originalStoreContent, Collections.singleton("keyA3"));
this.store = spy(fakeStore);
final Map<String, String> originalWriterContent = getEntryMap(KEY_SET_A, KEY_SET_B, KEY_SET_C);
final FakeCacheLoaderWriter fakeLoaderWriter = new FakeCacheLoaderWriter(originalWriterContent);
this.cacheLoaderWriter = spy(fakeLoaderWriter);
doThrow(new Exception("writeAll failed")).when(this.cacheLoaderWriter).writeAll(getAnyEntryIterable());
final InternalCache<String, String> ehcache = this.getEhcache(this.cacheLoaderWriter);
final Map<String, String> contentUpdates = getAltEntryMap("new_", fanIn(KEY_SET_A, KEY_SET_C, KEY_SET_D));
try {
ehcache.putAll(contentUpdates);
fail();
} catch (BulkCacheWritingException e) {
// Expected
}
final InOrder ordered = inOrder(this.store, this.cacheLoaderWriter, this.spiedResilienceStrategy);
ordered.verify(this.store, atLeast(1))
.bulkCompute(this.bulkComputeSetCaptor.capture(), getAnyEntryIterableFunction());
assertThat(this.getBulkComputeArgs(), everyItem(isIn(contentUpdates.keySet())));
// ResilienceStrategy invoked; no assertions about Store content
ordered.verify(this.cacheLoaderWriter, atLeast(1)).writeAll(getAnyEntryIterable());
ordered.verify(this.spiedResilienceStrategy)
.putAllFailure(eq(contentUpdates), any(StoreAccessException.class), this.bulkExceptionCaptor.capture());
assertThat(this.bulkExceptionCaptor.getValue().getSuccesses(), empty());
assertThat(this.bulkExceptionCaptor.getValue().getFailures().keySet(),
Matchers.<Set<?>>equalTo(contentUpdates.keySet()));
validateStats(ehcache, EnumSet.noneOf(CacheOperationOutcomes.PutOutcome.class));
validateStats(ehcache, EnumSet.of(CacheOperationOutcomes.PutAllOutcome.FAILURE));
assertThat(ehcache.getBulkMethodEntries().get(BulkOps.PUT_ALL).intValue(), is(0));
}
/**
* Tests {@link EhcacheWithLoaderWriter#putAll(Map)} for
* <ul>
* <li>non-empty request map</li>
* <li>populated {@code Store} - some keys overlap request</li>
* <li>populated {@code CacheLoaderWriter} - all keys overlap</li>
* <li>no {@link CacheLoaderWriter#writeAll(Iterable)} calls fail</li>
* </ul>
*/
@Test
public void testPutAllStoreSomeOverlapWriterFullOverlapNoneFail() throws Exception {
final Map<String, String> originalStoreContent = getEntryMap(KEY_SET_A, KEY_SET_B);
final FakeStore fakeStore = new FakeStore(originalStoreContent);
this.store = spy(fakeStore);
final Map<String, String> originalWriterContent = getEntryMap(KEY_SET_A, KEY_SET_B, KEY_SET_C);
final FakeCacheLoaderWriter fakeLoaderWriter = new FakeCacheLoaderWriter(originalWriterContent);
this.cacheLoaderWriter = spy(fakeLoaderWriter);
final InternalCache<String, String> ehcache = this.getEhcache(this.cacheLoaderWriter);
final Map<String, String> contentUpdates = getAltEntryMap("new_", fanIn(KEY_SET_B, KEY_SET_C));
ehcache.putAll(contentUpdates);
verify(this.store, atLeast(1)).bulkCompute(this.bulkComputeSetCaptor.capture(), getAnyEntryIterableFunction());
assertThat(this.getBulkComputeArgs(), equalTo(contentUpdates.keySet()));
assertThat(fakeStore.getEntryMap(), equalTo(union(originalStoreContent, contentUpdates)));
verify(this.cacheLoaderWriter, atLeast(1)).writeAll(getAnyEntryIterable());
assertThat(fakeLoaderWriter.getEntryMap(), equalTo(union(originalWriterContent, contentUpdates)));
verifyZeroInteractions(this.spiedResilienceStrategy);
validateStats(ehcache, EnumSet.noneOf(CacheOperationOutcomes.PutOutcome.class));
validateStats(ehcache, EnumSet.of(CacheOperationOutcomes.PutAllOutcome.SUCCESS));
assertThat(ehcache.getBulkMethodEntries().get(BulkOps.PUT_ALL).intValue(), is(contentUpdates.size()));
}
/**
* Tests {@link EhcacheWithLoaderWriter#putAll(Map)} for
* <ul>
* <li>non-empty request map</li>
* <li>populated {@code Store} - some keys overlap request</li>
* <li>{@link Store#bulkCompute} throws before accessing writer</li>
* <li>populated {@code CacheLoaderWriter} - all keys overlap</li>
* <li>no {@link CacheLoaderWriter#writeAll(Iterable)} calls fail</li>
* </ul>
*/
@Test
public void testPutAllStoreSomeOverlapStoreAccessExceptionBeforeWriterFullOverlapNoneFail() throws Exception {
final Map<String, String> originalStoreContent = getEntryMap(KEY_SET_A, KEY_SET_B);
final FakeStore fakeStore = new FakeStore(originalStoreContent);
this.store = spy(fakeStore);
doThrow(new StoreAccessException("")).when(this.store)
.bulkCompute(getAnyStringSet(), getAnyEntryIterableFunction());
final Map<String, String> originalWriterContent = getEntryMap(KEY_SET_A, KEY_SET_B, KEY_SET_C);
final FakeCacheLoaderWriter fakeLoaderWriter = new FakeCacheLoaderWriter(originalWriterContent);
this.cacheLoaderWriter = spy(fakeLoaderWriter);
final InternalCache<String, String> ehcache = this.getEhcache(this.cacheLoaderWriter);
final Map<String, String> contentUpdates = getAltEntryMap("new_", fanIn(KEY_SET_B, KEY_SET_C));
ehcache.putAll(contentUpdates);
final InOrder ordered = inOrder(this.store, this.cacheLoaderWriter, this.spiedResilienceStrategy);
ordered.verify(this.store, atLeast(1))
.bulkCompute(this.bulkComputeSetCaptor.capture(), getAnyEntryIterableFunction());
assertThat(this.getBulkComputeArgs(), everyItem(isIn(contentUpdates.keySet())));
// ResilienceStrategy invoked; no assertions about Store content
ordered.verify(this.cacheLoaderWriter, atLeast(1)).writeAll(getAnyEntryIterable());
assertThat(fakeLoaderWriter.getEntryMap(), equalTo(union(originalWriterContent, contentUpdates)));
ordered.verify(this.spiedResilienceStrategy)
.putAllFailure(eq(contentUpdates), any(StoreAccessException.class));
validateStats(ehcache, EnumSet.noneOf(CacheOperationOutcomes.PutOutcome.class));
validateStats(ehcache, EnumSet.of(CacheOperationOutcomes.PutAllOutcome.FAILURE));
assertThat(ehcache.getBulkMethodEntries().get(BulkOps.PUT_ALL).intValue(), is(0));
}
/**
* Tests {@link EhcacheWithLoaderWriter#putAll(Map)} for
* <ul>
* <li>non-empty request map</li>
* <li>populated {@code Store} - some keys overlap request</li>
* <li>{@link Store#bulkCompute} throws after accessing writer</li>
* <li>populated {@code CacheLoaderWriter} - all keys overlap</li>
* <li>no {@link CacheLoaderWriter#writeAll(Iterable)} calls fail</li>
* </ul>
*/
@Test
public void testPutAllStoreSomeOverlapStoreAccessExceptionAfterWriterFullOverlapNoneFail() throws Exception {
final Map<String, String> originalStoreContent = getEntryMap(KEY_SET_A, KEY_SET_B);
final FakeStore fakeStore = new FakeStore(originalStoreContent, Collections.singleton("keyB3"));
this.store = spy(fakeStore);
final Map<String, String> originalWriterContent = getEntryMap(KEY_SET_A, KEY_SET_B, KEY_SET_C);
final FakeCacheLoaderWriter fakeLoaderWriter = new FakeCacheLoaderWriter(originalWriterContent);
this.cacheLoaderWriter = spy(fakeLoaderWriter);
final InternalCache<String, String> ehcache = this.getEhcache(this.cacheLoaderWriter);
final Map<String, String> contentUpdates = getAltEntryMap("new_", fanIn(KEY_SET_B, KEY_SET_C));
ehcache.putAll(contentUpdates);
final InOrder ordered = inOrder(this.store, this.cacheLoaderWriter, this.spiedResilienceStrategy);
ordered.verify(this.store, atLeast(1))
.bulkCompute(this.bulkComputeSetCaptor.capture(), getAnyEntryIterableFunction());
assertThat(this.getBulkComputeArgs(), everyItem(isIn(contentUpdates.keySet())));
// ResilienceStrategy invoked; no assertions about Store content
ordered.verify(this.cacheLoaderWriter, atLeast(1)).writeAll(getAnyEntryIterable());
assertThat(fakeLoaderWriter.getEntryMap(), equalTo(union(originalWriterContent, contentUpdates)));
ordered.verify(this.spiedResilienceStrategy)
.putAllFailure(eq(contentUpdates), any(StoreAccessException.class));
validateStats(ehcache, EnumSet.noneOf(CacheOperationOutcomes.PutOutcome.class));
validateStats(ehcache, EnumSet.of(CacheOperationOutcomes.PutAllOutcome.FAILURE));
assertThat(ehcache.getBulkMethodEntries().get(BulkOps.PUT_ALL).intValue(), is(0));
}
/**
* Tests {@link EhcacheWithLoaderWriter#putAll(Map)} for
* <ul>
* <li>non-empty request map</li>
* <li>populated {@code Store} - some keys overlap request</li>
* <li>populated {@code CacheLoaderWriter} - all keys overlap</li>
* <li>some {@link CacheLoaderWriter#writeAll(Iterable)} calls fail</li>
* </ul>
*/
@Test
public void testPutAllStoreSomeOverlapWriterFullOverlapSomeFail() throws Exception {
final Map<String, String> originalStoreContent = getEntryMap(KEY_SET_A, KEY_SET_B);
final FakeStore fakeStore = new FakeStore(originalStoreContent);
this.store = spy(fakeStore);
final Map<String, String> originalWriterContent = getEntryMap(KEY_SET_A, KEY_SET_B, KEY_SET_C, KEY_SET_D);
final FakeCacheLoaderWriter fakeLoaderWriter = new FakeCacheLoaderWriter(originalWriterContent, KEY_SET_D);
this.cacheLoaderWriter = spy(fakeLoaderWriter);
final InternalCache<String, String> ehcache = this.getEhcache(this.cacheLoaderWriter);
final Map<String, String> contentUpdates = getAltEntryMap("new_", fanIn(KEY_SET_B, KEY_SET_C, KEY_SET_D));
final Set<String> expectedFailures = KEY_SET_D;
final Map<String, String> expectedSuccesses = copyWithout(contentUpdates, expectedFailures);
try {
ehcache.putAll(contentUpdates);
fail();
} catch (BulkCacheWritingException e) {
// Expected
assertThat(e.getSuccesses(), Matchers.<Set<?>>equalTo(expectedSuccesses.keySet()));
assertThat(e.getFailures().keySet(), Matchers.<Set<?>>equalTo(expectedFailures));
}
verify(this.store, atLeast(1)).bulkCompute(this.bulkComputeSetCaptor.capture(), getAnyEntryIterableFunction());
assertThat(this.getBulkComputeArgs(), everyItem(isIn(contentUpdates.keySet())));
assertThat(fakeStore.getEntryMap(), equalTo(union(originalStoreContent, expectedSuccesses)));
verify(this.cacheLoaderWriter, atLeast(1)).writeAll(getAnyEntryIterable());
assertThat(fakeLoaderWriter.getEntryMap(), equalTo(union(originalWriterContent, expectedSuccesses)));
verifyZeroInteractions(this.spiedResilienceStrategy);
validateStats(ehcache, EnumSet.noneOf(CacheOperationOutcomes.PutOutcome.class));
validateStats(ehcache, EnumSet.of(CacheOperationOutcomes.PutAllOutcome.FAILURE));
assertThat(ehcache.getBulkMethodEntries().get(BulkOps.PUT_ALL).intValue(), is(KEY_SET_B.size() + KEY_SET_C.size()));
}
/**
* Tests {@link EhcacheWithLoaderWriter#putAll(Map)} for
* <ul>
* <li>non-empty request map</li>
* <li>populated {@code Store} - some keys overlap request</li>
* <li>{@link Store#bulkCompute} throws before accessing writer</li>
* <li>populated {@code CacheLoaderWriter} - all keys overlap</li>
* <li>some {@link CacheLoaderWriter#writeAll(Iterable)} calls fail</li>
* </ul>
*/
@Test
public void testPutAllStoreSomeOverlapStoreAccessExceptionBeforeWriterFullOverlapSomeFail() throws Exception {
final Map<String, String> originalStoreContent = getEntryMap(KEY_SET_A, KEY_SET_B);
final FakeStore fakeStore = new FakeStore(originalStoreContent);
this.store = spy(fakeStore);
doThrow(new StoreAccessException("")).when(this.store)
.bulkCompute(getAnyStringSet(), getAnyEntryIterableFunction());
final Map<String, String> originalWriterContent = getEntryMap(KEY_SET_A, KEY_SET_B, KEY_SET_C, KEY_SET_D);
final FakeCacheLoaderWriter fakeLoaderWriter = new FakeCacheLoaderWriter(originalWriterContent, KEY_SET_D);
this.cacheLoaderWriter = spy(fakeLoaderWriter);
final InternalCache<String, String> ehcache = this.getEhcache(this.cacheLoaderWriter);
final Map<String, String> contentUpdates = getAltEntryMap("new_", fanIn(KEY_SET_B, KEY_SET_C, KEY_SET_D));
final Set<String> expectedFailures = KEY_SET_D;
final Map<String, String> expectedSuccesses = copyWithout(contentUpdates, expectedFailures);
try {
ehcache.putAll(contentUpdates);
fail();
} catch (BulkCacheWritingException e) {
// Expected
}
final InOrder ordered = inOrder(this.store, this.cacheLoaderWriter, this.spiedResilienceStrategy);
ordered.verify(this.store, atLeast(1))
.bulkCompute(this.bulkComputeSetCaptor.capture(), getAnyEntryIterableFunction());
assertThat(this.getBulkComputeArgs(), everyItem(isIn(contentUpdates.keySet())));
// ResilienceStrategy invoked; no assertions about Store content
ordered.verify(this.cacheLoaderWriter, atLeast(1)).writeAll(getAnyEntryIterable());
assertThat(fakeLoaderWriter.getEntryMap(), equalTo(union(originalWriterContent, expectedSuccesses)));
ordered.verify(this.spiedResilienceStrategy)
.putAllFailure(eq(contentUpdates), any(StoreAccessException.class), this.bulkExceptionCaptor.capture());
assertThat(this.bulkExceptionCaptor.getValue().getSuccesses(), Matchers.<Set<?>>equalTo(expectedSuccesses.keySet()));
assertThat(this.bulkExceptionCaptor.getValue().getFailures().keySet(), Matchers.<Set<?>>equalTo(expectedFailures));
validateStats(ehcache, EnumSet.noneOf(CacheOperationOutcomes.PutOutcome.class));
validateStats(ehcache, EnumSet.of(CacheOperationOutcomes.PutAllOutcome.FAILURE));
assertThat(ehcache.getBulkMethodEntries().get(BulkOps.PUT_ALL).intValue(), is(0));
}
/**
* Tests {@link EhcacheWithLoaderWriter#putAll(Map)} for
* <ul>
* <li>non-empty request map</li>
* <li>populated {@code Store} - some keys overlap request</li>
* <li>{@link Store#bulkCompute} throws after accessing writer</li>
* <li>populated {@code CacheLoaderWriter} - all keys overlap</li>
* <li>some {@link CacheLoaderWriter#writeAll(Iterable)} calls fail</li>
* </ul>
*/
@Test
public void testPutAllStoreSomeOverlapStoreAccessExceptionAfterWriterFullOverlapSomeFail() throws Exception {
final Map<String, String> originalStoreContent = getEntryMap(KEY_SET_A, KEY_SET_B);
final FakeStore fakeStore = new FakeStore(originalStoreContent, Collections.singleton("keyB3"));
this.store = spy(fakeStore);
final Map<String, String> originalWriterContent = getEntryMap(KEY_SET_A, KEY_SET_B, KEY_SET_C, KEY_SET_D);
final FakeCacheLoaderWriter fakeLoaderWriter = new FakeCacheLoaderWriter(originalWriterContent, KEY_SET_D);
this.cacheLoaderWriter = spy(fakeLoaderWriter);
final InternalCache<String, String> ehcache = this.getEhcache(this.cacheLoaderWriter);
final Map<String, String> contentUpdates = getAltEntryMap("new_", fanIn(KEY_SET_B, KEY_SET_C, KEY_SET_D));
final Set<String> expectedFailures = KEY_SET_D;
final Map<String, String> expectedSuccesses = copyWithout(contentUpdates, expectedFailures);
try {
ehcache.putAll(contentUpdates);
fail();
} catch (BulkCacheWritingException e) {
// Expected
}
final InOrder ordered = inOrder(this.store, this.cacheLoaderWriter, this.spiedResilienceStrategy);
ordered.verify(this.store, atLeast(1))
.bulkCompute(this.bulkComputeSetCaptor.capture(), getAnyEntryIterableFunction());
assertThat(this.getBulkComputeArgs(), everyItem(isIn(contentUpdates.keySet())));
// ResilienceStrategy invoked; no assertions about Store content
ordered.verify(this.cacheLoaderWriter, atLeast(1)).writeAll(getAnyEntryIterable());
assertThat(fakeLoaderWriter.getEntryMap(), equalTo(union(originalWriterContent, expectedSuccesses)));
ordered.verify(this.spiedResilienceStrategy)
.putAllFailure(eq(contentUpdates), any(StoreAccessException.class), this.bulkExceptionCaptor.capture());
assertThat(this.bulkExceptionCaptor.getValue().getSuccesses(), Matchers.<Set<?>>equalTo(expectedSuccesses.keySet()));
assertThat(this.bulkExceptionCaptor.getValue().getFailures().keySet(), Matchers.<Set<?>>equalTo(expectedFailures));
validateStats(ehcache, EnumSet.noneOf(CacheOperationOutcomes.PutOutcome.class));
validateStats(ehcache, EnumSet.of(CacheOperationOutcomes.PutAllOutcome.FAILURE));
assertThat(ehcache.getBulkMethodEntries().get(BulkOps.PUT_ALL).intValue(), is(0));
}
/**
* Tests {@link EhcacheWithLoaderWriter#putAll(Map)} for
* <ul>
* <li>non-empty request map</li>
* <li>populated {@code Store} - some keys overlap request</li>
* <li>populated {@code CacheLoaderWriter} - all keys overlap</li>
* <li>some {@link CacheLoaderWriter#writeAll(Iterable)} calls fail</li>
* <li>at least one {@link CacheLoaderWriter#writeAll(Iterable)} call aborts</li>
* </ul>
*/
@Test
public void testPutAllStoreSomeOverlapWriterFullOverlapSomeFailWithAbort() throws Exception {
final Map<String, String> originalStoreContent = getEntryMap(KEY_SET_A, KEY_SET_B);
final FakeStore fakeStore = new FakeStore(originalStoreContent);
this.store = spy(fakeStore);
final Map<String, String> originalWriterContent = getEntryMap(KEY_SET_A, KEY_SET_B, KEY_SET_C, KEY_SET_D);
final FakeCacheLoaderWriter fakeLoaderWriter = new FakeCacheLoaderWriter(originalWriterContent, KEY_SET_D);
fakeLoaderWriter.setCompleteFailureKey("keyC4");
this.cacheLoaderWriter = spy(fakeLoaderWriter);
final InternalCache<String, String> ehcache = this.getEhcache(this.cacheLoaderWriter);
final Map<String, String> contentUpdates = getAltEntryMap("new_", fanIn(KEY_SET_B, KEY_SET_C, KEY_SET_D));
final Set<String> expectedFailures = union(KEY_SET_D, Collections.singleton("keyC4"));
final Map<String, String> expectedSuccesses = copyWithout(contentUpdates, expectedFailures);
try {
ehcache.putAll(contentUpdates);
fail();
} catch (BulkCacheWritingException e) {
// Expected
assertThat(e.getSuccesses(), Matchers.<Set<?>>equalTo(expectedSuccesses.keySet()));
assertThat(e.getFailures().keySet(), Matchers.<Set<?>>equalTo(expectedFailures));
}
verify(this.store, atLeast(1))
.bulkCompute(this.bulkComputeSetCaptor.capture(), getAnyEntryIterableFunction());
assertThat(this.getBulkComputeArgs(), everyItem(isIn(contentUpdates.keySet())));
assertThat(fakeStore.getEntryMap(), equalTo(union(originalStoreContent, expectedSuccesses)));
verify(this.cacheLoaderWriter, atLeast(1)).writeAll(getAnyEntryIterable());
assertThat(fakeLoaderWriter.getEntryMap(), equalTo(union(originalWriterContent, expectedSuccesses)));
verifyZeroInteractions(this.spiedResilienceStrategy);
validateStats(ehcache, EnumSet.noneOf(CacheOperationOutcomes.PutOutcome.class));
validateStats(ehcache, EnumSet.of(CacheOperationOutcomes.PutAllOutcome.FAILURE));
assertThat(ehcache.getBulkMethodEntries().get(BulkOps.PUT_ALL).intValue(), is(expectedSuccesses.size()));
}
/**
* Tests {@link EhcacheWithLoaderWriter#putAll(Map)} for
* <ul>
* <li>non-empty request map</li>
* <li>populated {@code Store} - some keys overlap request</li>
* <li>{@link Store#bulkCompute} throws before accessing writer</li>
* <li>populated {@code CacheLoaderWriter} - all keys overlap</li>
* <li>some {@link CacheLoaderWriter#writeAll(Iterable)} calls fail</li>
* <li>at least one {@link CacheLoaderWriter#writeAll(Iterable)} call aborts</li>
* </ul>
*/
@Test
public void testPutAllStoreSomeOverlapStoreAccessExceptionBeforeWriterFullOverlapSomeFailWithAbort() throws Exception {
final Map<String, String> originalStoreContent = getEntryMap(KEY_SET_A, KEY_SET_B);
final FakeStore fakeStore = new FakeStore(originalStoreContent);
this.store = spy(fakeStore);
doThrow(new StoreAccessException("")).when(this.store)
.bulkCompute(getAnyStringSet(), getAnyEntryIterableFunction());
final Map<String, String> originalWriterContent = getEntryMap(KEY_SET_A, KEY_SET_B, KEY_SET_C, KEY_SET_D);
final FakeCacheLoaderWriter fakeLoaderWriter = new FakeCacheLoaderWriter(originalWriterContent, KEY_SET_D);
fakeLoaderWriter.setCompleteFailureKey("keyC4");
this.cacheLoaderWriter = spy(fakeLoaderWriter);
final InternalCache<String, String> ehcache = this.getEhcache(this.cacheLoaderWriter);
final Map<String, String> contentUpdates = getAltEntryMap("new_", fanIn(KEY_SET_B, KEY_SET_C, KEY_SET_D));
final Set<String> expectedFailures = union(KEY_SET_D, Collections.singleton("keyC4"));
final Map<String, String> expectedSuccesses = copyWithout(contentUpdates, expectedFailures);
try {
ehcache.putAll(contentUpdates);
fail();
} catch (BulkCacheWritingException e) {
// Expected
}
final InOrder ordered = inOrder(this.store, this.cacheLoaderWriter, this.spiedResilienceStrategy);
ordered.verify(this.store, atLeast(1))
.bulkCompute(this.bulkComputeSetCaptor.capture(), getAnyEntryIterableFunction());
assertThat(this.getBulkComputeArgs(), everyItem(isIn(contentUpdates.keySet())));
// ResilienceStrategy invoked; no assertions about Store content
ordered.verify(this.cacheLoaderWriter, atLeast(1)).writeAll(getAnyEntryIterable());
ordered.verify(this.spiedResilienceStrategy)
.putAllFailure(eq(contentUpdates), any(StoreAccessException.class), this.bulkExceptionCaptor.capture());
@SuppressWarnings("unchecked")
final Set<String> bcweSuccesses = (Set<String>)this.bulkExceptionCaptor.getValue().getSuccesses();
@SuppressWarnings("unchecked")
final Map<String, Exception> bcweFailures = (Map<String, Exception>)this.bulkExceptionCaptor.getValue().getFailures();
assertThat(union(bcweSuccesses, bcweFailures.keySet()), equalTo(contentUpdates.keySet()));
assertThat(Collections.disjoint(bcweSuccesses, bcweFailures.keySet()), is(true));
assertThatAllStoreEntriesWithoutFailuresMatchWriterState(fakeStore, fakeLoaderWriter, bcweFailures);
validateStats(ehcache, EnumSet.noneOf(CacheOperationOutcomes.PutOutcome.class));
validateStats(ehcache, EnumSet.of(CacheOperationOutcomes.PutAllOutcome.FAILURE));
assertThat(ehcache.getBulkMethodEntries().get(BulkOps.PUT_ALL).intValue(), is(0));
}
/**
* Tests {@link EhcacheWithLoaderWriter#putAll(Map)} for
* <ul>
* <li>non-empty request map</li>
* <li>populated {@code Store} - some keys overlap request</li>
* <li>{@link Store#bulkCompute} throws after accessing writer</li>
* <li>populated {@code CacheLoaderWriter} - all keys overlap</li>
* <li>some {@link CacheLoaderWriter#writeAll(Iterable)} calls fail</li>
* <li>at least one {@link CacheLoaderWriter#writeAll(Iterable)} call aborts</li>
* </ul>
*/
@Test
public void testPutAllStoreSomeOverlapStoreAccessExceptionAfterWriterFullOverlapSomeFailWithAbort() throws Exception {
final Map<String, String> originalStoreContent = getEntryMap(KEY_SET_A, KEY_SET_B);
final FakeStore fakeStore = new FakeStore(originalStoreContent, Collections.singleton("keyB3"));
this.store = spy(fakeStore);
final Map<String, String> originalWriterContent = getEntryMap(KEY_SET_A, KEY_SET_B, KEY_SET_C, KEY_SET_D);
final FakeCacheLoaderWriter fakeLoaderWriter = new FakeCacheLoaderWriter(originalWriterContent, KEY_SET_D);
fakeLoaderWriter.setCompleteFailureKey("keyC4");
this.cacheLoaderWriter = spy(fakeLoaderWriter);
final InternalCache<String, String> ehcache = this.getEhcache(this.cacheLoaderWriter);
final Map<String, String> contentUpdates = getAltEntryMap("new_", fanIn(KEY_SET_B, KEY_SET_C, KEY_SET_D));
final Set<String> expectedFailures = union(KEY_SET_D, Collections.singleton("keyC4"));
final Map<String, String> expectedSuccesses = copyWithout(contentUpdates, expectedFailures);
try {
ehcache.putAll(contentUpdates);
fail();
} catch (BulkCacheWritingException e) {
// Expected
}
final InOrder ordered = inOrder(this.store, this.cacheLoaderWriter, this.spiedResilienceStrategy);
ordered.verify(this.store, atLeast(1))
.bulkCompute(this.bulkComputeSetCaptor.capture(), getAnyEntryIterableFunction());
assertThat(this.getBulkComputeArgs(), everyItem(isIn(contentUpdates.keySet())));
// ResilienceStrategy invoked; no assertions about Store content
ordered.verify(this.cacheLoaderWriter, atLeast(1)).writeAll(getAnyEntryIterable());
ordered.verify(this.spiedResilienceStrategy)
.putAllFailure(eq(contentUpdates), any(StoreAccessException.class), this.bulkExceptionCaptor.capture());
@SuppressWarnings("unchecked")
final Set<String> bcweSuccesses = (Set<String>)this.bulkExceptionCaptor.getValue().getSuccesses();
@SuppressWarnings("unchecked")
final Map<String, Exception> bcweFailures = (Map<String, Exception>)this.bulkExceptionCaptor.getValue().getFailures();
assertThat(union(bcweSuccesses, bcweFailures.keySet()), equalTo(contentUpdates.keySet()));
assertThat(Collections.disjoint(bcweSuccesses, bcweFailures.keySet()), is(true));
assertThatAllStoreEntriesWithoutFailuresMatchWriterState(fakeStore, fakeLoaderWriter, bcweFailures);
validateStats(ehcache, EnumSet.noneOf(CacheOperationOutcomes.PutOutcome.class));
validateStats(ehcache, EnumSet.of(CacheOperationOutcomes.PutAllOutcome.FAILURE));
assertThat(ehcache.getBulkMethodEntries().get(BulkOps.PUT_ALL).intValue(), is(0));
}
/**
* Tests {@link EhcacheWithLoaderWriter#putAll(Map)} for
* <ul>
* <li>non-empty request map</li>
* <li>populated {@code Store} - some keys overlap request</li>
* <li>populated {@code CacheLoaderWriter} - all keys overlap</li>
* <li>all {@link CacheLoaderWriter#writeAll(Iterable)} calls fail</li>
* </ul>
*/
@Test
public void testPutAllStoreSomeOverlapWriterFullOverlapAllFail() throws Exception {
final Map<String, String> originalStoreContent = getEntryMap(KEY_SET_A, KEY_SET_B);
final FakeStore fakeStore = new FakeStore(originalStoreContent);
this.store = spy(fakeStore);
final Map<String, String> originalWriterContent = getEntryMap(KEY_SET_A, KEY_SET_B, KEY_SET_C, KEY_SET_D);
final FakeCacheLoaderWriter fakeLoaderWriter = new FakeCacheLoaderWriter(originalWriterContent);
this.cacheLoaderWriter = spy(fakeLoaderWriter);
doThrow(new Exception("writeAll failed")).when(this.cacheLoaderWriter).writeAll(getAnyEntryIterable());
final InternalCache<String, String> ehcache = this.getEhcache(this.cacheLoaderWriter);
final Map<String, String> contentUpdates = getAltEntryMap("new_", fanIn(KEY_SET_B, KEY_SET_C, KEY_SET_D));
try {
ehcache.putAll(contentUpdates);
fail();
} catch (BulkCacheWritingException e) {
// Expected
assertThat(e.getSuccesses(), empty());
assertThat(e.getFailures().keySet(), Matchers.<Set<?>>equalTo(contentUpdates.keySet()));
}
verify(this.store, atLeast(1))
.bulkCompute(this.bulkComputeSetCaptor.capture(), getAnyEntryIterableFunction());
assertThat(this.getBulkComputeArgs(), everyItem(isIn(contentUpdates.keySet())));
assertThat(fakeStore.getEntryMap(), equalTo(getEntryMap(KEY_SET_A)));
verify(this.cacheLoaderWriter, atLeast(1)).writeAll(getAnyEntryIterable());
verifyZeroInteractions(this.spiedResilienceStrategy);
validateStats(ehcache, EnumSet.noneOf(CacheOperationOutcomes.PutOutcome.class));
validateStats(ehcache, EnumSet.of(CacheOperationOutcomes.PutAllOutcome.FAILURE));
assertThat(ehcache.getBulkMethodEntries().get(BulkOps.PUT_ALL).intValue(), is(0));
}
/**
* Tests {@link EhcacheWithLoaderWriter#putAll(Map)} for
* <ul>
* <li>non-empty request map</li>
* <li>populated {@code Store} - some keys overlap request</li>
* <li>{@link Store#bulkCompute} throws before accessing writer</li>
* <li>populated {@code CacheLoaderWriter} - all keys overlap</li>
* <li>all {@link CacheLoaderWriter#writeAll(Iterable)} calls fail</li>
* </ul>
*/
@Test
public void testPutAllStoreSomeOverlapStoreAccessExceptionBeforeWriterFullOverlapAllFail() throws Exception {
final Map<String, String> originalStoreContent = getEntryMap(KEY_SET_A, KEY_SET_B);
final FakeStore fakeStore = new FakeStore(originalStoreContent);
this.store = spy(fakeStore);
doThrow(new StoreAccessException("")).when(this.store)
.bulkCompute(getAnyStringSet(), getAnyEntryIterableFunction());
final Map<String, String> originalWriterContent = getEntryMap(KEY_SET_A, KEY_SET_B, KEY_SET_C, KEY_SET_D);
final FakeCacheLoaderWriter fakeLoaderWriter = new FakeCacheLoaderWriter(originalWriterContent);
this.cacheLoaderWriter = spy(fakeLoaderWriter);
doThrow(new Exception("writeAll failed")).when(this.cacheLoaderWriter).writeAll(getAnyEntryIterable());
final InternalCache<String, String> ehcache = this.getEhcache(this.cacheLoaderWriter);
final Map<String, String> contentUpdates = getAltEntryMap("new_", fanIn(KEY_SET_B, KEY_SET_C, KEY_SET_D));
try {
ehcache.putAll(contentUpdates);
fail();
} catch (BulkCacheWritingException e) {
// Expected
}
final InOrder ordered = inOrder(this.store, this.cacheLoaderWriter, this.spiedResilienceStrategy);
ordered.verify(this.store, atLeast(1))
.bulkCompute(this.bulkComputeSetCaptor.capture(), getAnyEntryIterableFunction());
assertThat(this.getBulkComputeArgs(), everyItem(isIn(contentUpdates.keySet())));
// ResilienceStrategy invoked; no assertions about Store content
ordered.verify(this.cacheLoaderWriter, atLeast(1)).writeAll(getAnyEntryIterable());
assertThat(fakeLoaderWriter.getEntryMap(), equalTo(originalWriterContent));
ordered.verify(this.spiedResilienceStrategy)
.putAllFailure(eq(contentUpdates), any(StoreAccessException.class), this.bulkExceptionCaptor.capture());
assertThat(this.bulkExceptionCaptor.getValue().getSuccesses(), empty());
assertThat(this.bulkExceptionCaptor.getValue().getFailures().keySet(),
Matchers.<Set<?>>equalTo(contentUpdates.keySet()));
validateStats(ehcache, EnumSet.noneOf(CacheOperationOutcomes.PutOutcome.class));
validateStats(ehcache, EnumSet.of(CacheOperationOutcomes.PutAllOutcome.FAILURE));
assertThat(ehcache.getBulkMethodEntries().get(BulkOps.PUT_ALL).intValue(), is(0));
}
/**
* Tests {@link EhcacheWithLoaderWriter#putAll(Map)} for
* <ul>
* <li>non-empty request map</li>
* <li>populated {@code Store} - some keys overlap request</li>
* <li>{@link Store#bulkCompute} throws after accessing writer</li>
* <li>populated {@code CacheLoaderWriter} - all keys overlap</li>
* <li>all {@link CacheLoaderWriter#writeAll(Iterable)} calls fail</li>
* </ul>
*/
@Test
public void testPutAllStoreSomeOverlapStoreAccessExceptionAfterWriterFullOverlapAllFail() throws Exception {
final Map<String, String> originalStoreContent = getEntryMap(KEY_SET_A, KEY_SET_B);
final FakeStore fakeStore = new FakeStore(originalStoreContent, Collections.singleton("keyB3"));
this.store = spy(fakeStore);
final Map<String, String> originalWriterContent = getEntryMap(KEY_SET_A, KEY_SET_B, KEY_SET_C, KEY_SET_D);
final FakeCacheLoaderWriter fakeLoaderWriter = new FakeCacheLoaderWriter(originalWriterContent);
this.cacheLoaderWriter = spy(fakeLoaderWriter);
doThrow(new Exception("writeAll failed")).when(this.cacheLoaderWriter).writeAll(getAnyEntryIterable());
final InternalCache<String, String> ehcache = this.getEhcache(this.cacheLoaderWriter);
final Map<String, String> contentUpdates = getAltEntryMap("new_", fanIn(KEY_SET_B, KEY_SET_C, KEY_SET_D));
try {
ehcache.putAll(contentUpdates);
fail();
} catch (BulkCacheWritingException e) {
// Expected
}
final InOrder ordered = inOrder(this.store, this.cacheLoaderWriter, this.spiedResilienceStrategy);
ordered.verify(this.store, atLeast(1))
.bulkCompute(this.bulkComputeSetCaptor.capture(), getAnyEntryIterableFunction());
assertThat(this.getBulkComputeArgs(), everyItem(isIn(contentUpdates.keySet())));
// ResilienceStrategy invoked; no assertions about Store content
ordered.verify(this.cacheLoaderWriter, atLeast(1)).writeAll(getAnyEntryIterable());
assertThat(fakeLoaderWriter.getEntryMap(), equalTo(originalWriterContent));
ordered.verify(this.spiedResilienceStrategy)
.putAllFailure(eq(contentUpdates), any(StoreAccessException.class), this.bulkExceptionCaptor.capture());
assertThat(this.bulkExceptionCaptor.getValue().getSuccesses(), empty());
assertThat(this.bulkExceptionCaptor.getValue().getFailures().keySet(),
Matchers.<Set<?>>equalTo(contentUpdates.keySet()));
validateStats(ehcache, EnumSet.noneOf(CacheOperationOutcomes.PutOutcome.class));
validateStats(ehcache, EnumSet.of(CacheOperationOutcomes.PutAllOutcome.FAILURE));
assertThat(ehcache.getBulkMethodEntries().get(BulkOps.PUT_ALL).intValue(), is(0));
}
@Test
public void testPutAllPartialIntersectionsImmediatelyExpiredCreatedEntries() throws Exception {
final Map<String, String> originalStoreContent = getEntryMap(KEY_SET_A, KEY_SET_B);
final FakeStore fakeStore = new FakeStore(originalStoreContent);
this.store = spy(fakeStore);
final Map<String, String> originalWriterContent = getEntryMap(KEY_SET_A, KEY_SET_B, KEY_SET_C);
final FakeCacheLoaderWriter fakeLoaderWriter = new FakeCacheLoaderWriter(originalWriterContent);
this.cacheLoaderWriter = spy(fakeLoaderWriter);
@SuppressWarnings("unchecked")
final Expiry<String, String> expiry = mock(Expiry.class);
when(expiry.getExpiryForCreation(any(String.class), any(String.class))).thenReturn(Duration.ZERO);
final EhcacheWithLoaderWriter<String, String> ehcache = this.getEhcache(cacheLoaderWriter, expiry);
final Map<String, String> contentUpdates = getAltEntryMap("new_", fanIn(KEY_SET_A, KEY_SET_C, KEY_SET_D));
ehcache.putAll(contentUpdates);
verify(this.store, atLeast(1)).bulkCompute(this.bulkComputeSetCaptor.capture(), getAnyEntryIterableFunction());
assertThat(this.getBulkComputeArgs(), equalTo(contentUpdates.keySet()));
assertThat(fakeStore.getEntryMap(), equalTo(union(getAltEntryMap("new_", KEY_SET_A), getEntryMap(KEY_SET_B))));
verify(this.cacheLoaderWriter, atLeast(1)).writeAll(getAnyEntryIterable());
assertThat(fakeLoaderWriter.getEntryMap(), equalTo(union(originalWriterContent, contentUpdates)));
verifyZeroInteractions(this.spiedResilienceStrategy);
validateStats(ehcache, EnumSet.noneOf(CacheOperationOutcomes.PutOutcome.class));
validateStats(ehcache, EnumSet.of(CacheOperationOutcomes.PutAllOutcome.SUCCESS));
assertThat(ehcache.getBulkMethodEntries().get(BulkOps.PUT_ALL).intValue(), is(KEY_SET_A.size()));
}
@Test
public void testPutAllPartialIntersectionsImmediatelyExpiredUpdatedEntries() throws Exception {
final Map<String, String> originalStoreContent = getEntryMap(KEY_SET_A, KEY_SET_B);
final FakeStore fakeStore = new FakeStore(originalStoreContent);
this.store = spy(fakeStore);
final Map<String, String> originalWriterContent = getEntryMap(KEY_SET_A, KEY_SET_B, KEY_SET_C);
final FakeCacheLoaderWriter fakeLoaderWriter = new FakeCacheLoaderWriter(originalWriterContent);
this.cacheLoaderWriter = spy(fakeLoaderWriter);
@SuppressWarnings("unchecked")
final Expiry<String, String> expiry = mock(Expiry.class);
when(expiry.getExpiryForUpdate(any(String.class), argThat(org.ehcache.core.util.Matchers.<String>holding(instanceOf(String.class))), any(String.class))).thenReturn(Duration.ZERO);
final EhcacheWithLoaderWriter<String, String> ehcache = this.getEhcache(cacheLoaderWriter, expiry);
final Map<String, String> contentUpdates = getAltEntryMap("new_", fanIn(KEY_SET_A, KEY_SET_C, KEY_SET_D));
ehcache.putAll(contentUpdates);
verify(this.store, atLeast(1)).bulkCompute(this.bulkComputeSetCaptor.capture(), getAnyEntryIterableFunction());
assertThat(this.getBulkComputeArgs(), equalTo(contentUpdates.keySet()));
assertThat(fakeStore.getEntryMap(), equalTo(union(getEntryMap(KEY_SET_B), getAltEntryMap("new_", union(KEY_SET_C, KEY_SET_D)))));
verify(this.cacheLoaderWriter, atLeast(1)).writeAll(getAnyEntryIterable());
assertThat(fakeLoaderWriter.getEntryMap(), equalTo(union(originalWriterContent, contentUpdates)));
verifyZeroInteractions(this.spiedResilienceStrategy);
validateStats(ehcache, EnumSet.noneOf(CacheOperationOutcomes.PutOutcome.class));
validateStats(ehcache, EnumSet.of(CacheOperationOutcomes.PutAllOutcome.SUCCESS));
assertThat(ehcache.getBulkMethodEntries().get(BulkOps.PUT_ALL).intValue(), is(KEY_SET_C.size() + KEY_SET_D.size()));
}
private void assertThatAllStoreEntriesWithoutFailuresMatchWriterState(FakeStore fakeStore, FakeCacheLoaderWriter fakeLoaderWriter, Map<String, Exception> bcweFailures) {
assertThat(copyWithout(fakeStore.getEntryMap(), bcweFailures.keySet()).entrySet(), everyItem(isIn(fakeLoaderWriter.getEntryMap()
.entrySet())));
}
private EhcacheWithLoaderWriter<String, String> getEhcache(final CacheLoaderWriter<String, String> cacheLoaderWriter) {
return getEhcache(cacheLoaderWriter, CACHE_CONFIGURATION);
}
private EhcacheWithLoaderWriter<String, String> getEhcache(final CacheLoaderWriter<String, String> cacheLoaderWriter, Expiry<String, String> expiry) {
CacheConfiguration<String, String> config = new BaseCacheConfiguration<String, String>(String.class, String.class, null, null,
expiry, ResourcePoolsHelper.createHeapOnlyPools());
return getEhcache(cacheLoaderWriter, config);
}
private EhcacheWithLoaderWriter<String, String> getEhcache(CacheLoaderWriter<String, String> cacheLoaderWriter, CacheConfiguration<String, String> config) {
final EhcacheWithLoaderWriter<String, String> ehcache = new EhcacheWithLoaderWriter<String, String>(config, this.store, cacheLoaderWriter, cacheEventDispatcher, LoggerFactory.getLogger(EhcacheWithLoaderWriter.class + "-" + "EhcacheBasicPutAllTest"));
ehcache.init();
assertThat("cache not initialized", ehcache.getStatus(), Matchers.is(Status.AVAILABLE));
this.spiedResilienceStrategy = this.setResilienceStrategySpy(ehcache);
return ehcache;
}
/**
* Collects all arguments captured by {@link #bulkComputeSetCaptor}.
*
* @return the argument values collected by {@link #bulkComputeSetCaptor}; the
* {@code Iterator} over the resulting {@code Set} returns the values
* in the order observed by the captor.
*/
private Set<String> getBulkComputeArgs() {
final Set<String> bulkComputeArgs = new LinkedHashSet<String>();
for (final Set<String> set : this.bulkComputeSetCaptor.getAllValues()) {
bulkComputeArgs.addAll(set);
}
return bulkComputeArgs;
}
}