/* * 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.core.spi.store.Store; import org.ehcache.core.statistics.CacheOperationOutcomes; import org.ehcache.spi.loaderwriter.BulkCacheWritingException; import org.ehcache.core.spi.store.StoreAccessException; import org.ehcache.core.spi.function.Function; import org.ehcache.core.spi.function.NullaryFunction; import org.ehcache.spi.loaderwriter.CacheLoaderWriter; import org.ehcache.core.statistics.BulkOps; 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.copyOnly; import static org.ehcache.core.EhcacheBasicBulkUtil.copyWithout; import static org.ehcache.core.EhcacheBasicBulkUtil.fanIn; 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.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.eq; import static org.mockito.Mockito.atLeast; import static org.mockito.Mockito.doThrow; import static org.mockito.Mockito.inOrder; 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.ehcache.core.EhcacheBasicRemoveAllTest.getAnyStringSet; import static org.ehcache.core.EhcacheBasicRemoveAllTest.getAnyEntryIterableFunction; import static org.ehcache.core.EhcacheBasicRemoveAllTest.getAnyStringIterable; /** * @author Abhilash * */ public class EhcacheWithLoaderWriterBasicRemoveAllTest extends EhcacheBasicCrudBase { @Mock protected 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 protected ArgumentCaptor<Set<String>> bulkComputeSetCaptor; /** * A Mockito {@code ArgumentCaptor} for the * {@link BulkCacheWritingException BulkCacheWritingException} * provided to the * {@link org.ehcache.core.internal.resilience.ResilienceStrategy#removeAllFailure(Iterable, StoreAccessException, BulkCacheWritingException)} * ResilienceStrategy.putAllFailure(Iterable, StoreAccessException, BulkCacheWritingException)} method. */ @Captor private ArgumentCaptor<BulkCacheWritingException> bulkExceptionCaptor; /** * Tests {@link EhcacheWithLoaderWriter#removeAll(Set)} for * <ul> * <li>empty request set</li> * <li>populated {@code Store} (keys not relevant)</li> * <li>populated {@code CacheLoaderWriter} (keys not relevant)</li> * </ul> */ @Test public void testRemoveAllEmptyRequestWithWriter() 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 EhcacheWithLoaderWriter<String, String> ehcache = this.getEhcache(this.cacheLoaderWriter); ehcache.removeAll(Collections.<String>emptySet()); verify(this.store, never()).bulkCompute(eq(Collections.<String>emptySet()), getAnyEntryIterableFunction()); assertThat(fakeStore.getEntryMap(), equalTo(originalStoreContent)); assertThat(fakeLoaderWriter.getEntryMap(), equalTo(originalStoreContent)); verify(this.cacheLoaderWriter, never()).deleteAll(eq(Collections.<String>emptySet())); verify(this.spiedResilienceStrategy, never()).removeAllFailure(eq(Collections.<String>emptySet()), any(StoreAccessException.class)); validateStats(ehcache, EnumSet.noneOf(CacheOperationOutcomes.RemoveOutcome.class)); validateStats(ehcache, EnumSet.of(CacheOperationOutcomes.RemoveAllOutcome.SUCCESS)); assertThat(ehcache.getBulkMethodEntries().get(BulkOps.REMOVE_ALL).intValue(), is(0)); } /** * Tests {@link EhcacheWithLoaderWriter#removeAll(Set)} for * <ul> * <li>non-empty request set</li> * <li>populated {@code Store} - some keys overlap request</li> * <li>populated {@code CacheLoaderWriter} - no keys overlap</li> * <li>no {@link CacheLoaderWriter#deleteAll(Iterable)} calls fail</li> * </ul> */ @Test public void testRemoveAllStoreSomeOverlapWriterNoOverlapNoneFail() 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_B, KEY_SET_D); final FakeCacheLoaderWriter fakeLoaderWriter = new FakeCacheLoaderWriter(originalWriterContent); this.cacheLoaderWriter = spy(fakeLoaderWriter); final EhcacheWithLoaderWriter<String, String> ehcache = this.getEhcache(this.cacheLoaderWriter); final Set<String> contentUpdates = fanIn(KEY_SET_A, KEY_SET_C); ehcache.removeAll(contentUpdates); verify(this.store, atLeast(1)).bulkCompute(this.bulkComputeSetCaptor.capture(), getAnyEntryIterableFunction()); assertThat(this.getBulkComputeArgs(), equalTo(contentUpdates)); assertThat(fakeStore.getEntryMap(), equalTo(copyWithout(originalStoreContent, contentUpdates))); assertThat(fakeLoaderWriter.getEntryMap(), equalTo(copyWithout(originalWriterContent, contentUpdates))); verifyZeroInteractions(this.spiedResilienceStrategy); validateStats(ehcache, EnumSet.noneOf(CacheOperationOutcomes.RemoveOutcome.class)); validateStats(ehcache, EnumSet.of(CacheOperationOutcomes.RemoveAllOutcome.SUCCESS)); assertThat(ehcache.getBulkMethodEntries().get(BulkOps.REMOVE_ALL).intValue(), is(KEY_SET_A.size())); } /** * Tests {@link EhcacheWithLoaderWriter#removeAll(Set)} for * <ul> * <li>non-empty request set</li> * <li>populated {@code Store} - some keys overlap request</li> * <li>{@link Store#bulkCompute} throws before accessing writer</li> * <li>populated {@code CacheLoaderWriter} - no keys overlap</li> * <li>no {@link CacheLoaderWriter#deleteAll(Iterable)} calls fail</li> * </ul> */ @Test public void testRemoveAllStoreSomeOverlapStoreAccessExceptionBeforeWriterNoOverlapNoneFail() 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_B, KEY_SET_D); final FakeCacheLoaderWriter fakeLoaderWriter = new FakeCacheLoaderWriter(originalWriterContent); this.cacheLoaderWriter = spy(fakeLoaderWriter); final EhcacheWithLoaderWriter<String, String> ehcache = this.getEhcache(this.cacheLoaderWriter); final Set<String> contentUpdates = fanIn(KEY_SET_A, KEY_SET_C); ehcache.removeAll(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))); // ResilienceStrategy invoked; no assertions about Store content ordered.verify(this.cacheLoaderWriter, atLeast(1)).deleteAll(getAnyStringIterable()); assertThat(fakeLoaderWriter.getEntryMap(), equalTo(copyWithout(originalWriterContent, contentUpdates))); ordered.verify(this.spiedResilienceStrategy) .removeAllFailure(eq(contentUpdates), any(StoreAccessException.class)); validateStats(ehcache, EnumSet.noneOf(CacheOperationOutcomes.RemoveOutcome.class)); validateStats(ehcache, EnumSet.of(CacheOperationOutcomes.RemoveAllOutcome.FAILURE)); assertThat(ehcache.getBulkMethodEntries().get(BulkOps.REMOVE_ALL).intValue(), is(0)); } /** * Tests {@link EhcacheWithLoaderWriter#removeAll(Set)} for * <ul> * <li>non-empty request set</li> * <li>populated {@code Store} - some keys overlap request</li> * <li>{@link Store#bulkCompute} throws after accessing writer</li> * <li>populated {@code CacheLoaderWriter} - no keys overlap</li> * <li>no {@link CacheLoaderWriter#deleteAll(Iterable)} calls fail</li> * </ul> */ @Test public void testRemoveAllStoreSomeOverlapStoreAccessExceptionAfterWriterNoOverlapNoneFail() 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_B, KEY_SET_D); final FakeCacheLoaderWriter fakeLoaderWriter = new FakeCacheLoaderWriter(originalWriterContent); this.cacheLoaderWriter = spy(fakeLoaderWriter); final EhcacheWithLoaderWriter<String, String> ehcache = this.getEhcache(this.cacheLoaderWriter); final Set<String> contentUpdates = fanIn(KEY_SET_A, KEY_SET_C); ehcache.removeAll(contentUpdates); validateStats(ehcache, EnumSet.noneOf(CacheOperationOutcomes.RemoveOutcome.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))); // ResilienceStrategy invoked; no assertions about Store content ordered.verify(this.cacheLoaderWriter, atLeast(1)).deleteAll(getAnyStringIterable()); assertThat(fakeLoaderWriter.getEntryMap(), equalTo(copyWithout(originalWriterContent, contentUpdates))); ordered.verify(this.spiedResilienceStrategy) .removeAllFailure(eq(contentUpdates), any(StoreAccessException.class)); validateStats(ehcache, EnumSet.noneOf(CacheOperationOutcomes.RemoveOutcome.class)); validateStats(ehcache, EnumSet.of(CacheOperationOutcomes.RemoveAllOutcome.FAILURE)); assertThat(ehcache.getBulkMethodEntries().get(BulkOps.REMOVE_ALL).intValue(), is(0)); } /** * Tests {@link EhcacheWithLoaderWriter#removeAll(Set)} for * <ul> * <li>non-empty request set</li> * <li>populated {@code Store} - some keys overlap request</li> * <li>populated {@code CacheLoaderWriter} - no keys overlap</li> * <li>some {@link CacheLoaderWriter#deleteAll(Iterable)} calls fail</li> * </ul> */ @Test public void testRemoveAllStoreSomeOverlapWriterNoOverlapSomeFail() 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_B, KEY_SET_D); final FakeCacheLoaderWriter fakeLoaderWriter = new FakeCacheLoaderWriter(originalWriterContent, KEY_SET_C); this.cacheLoaderWriter = spy(fakeLoaderWriter); final EhcacheWithLoaderWriter<String, String> ehcache = this.getEhcache(this.cacheLoaderWriter); final Set<String> contentUpdates = fanIn(KEY_SET_A, KEY_SET_C); final Set<String> expectedFailures = KEY_SET_C; final Set<String> expectedSuccesses = copyWithout(contentUpdates, expectedFailures); try { ehcache.removeAll(contentUpdates); fail(); } catch (BulkCacheWritingException e) { // Expected assertThat(e.getSuccesses(), Matchers.<Set<?>>equalTo(expectedSuccesses)); assertThat(e.getFailures().keySet(), Matchers.<Set<?>>equalTo(expectedFailures)); } verify(this.store, atLeast(1)).bulkCompute(this.bulkComputeSetCaptor.capture(), getAnyEntryIterableFunction()); assertThat(this.getBulkComputeArgs(), equalTo(contentUpdates)); assertThat(fakeStore.getEntryMap(), equalTo(copyWithout(originalStoreContent, expectedSuccesses))); assertThat(fakeLoaderWriter.getEntryMap(), equalTo(copyWithout(originalWriterContent, expectedSuccesses))); verifyZeroInteractions(this.spiedResilienceStrategy); validateStats(ehcache, EnumSet.noneOf(CacheOperationOutcomes.RemoveOutcome.class)); validateStats(ehcache, EnumSet.of(CacheOperationOutcomes.RemoveAllOutcome.FAILURE)); assertThat(ehcache.getBulkMethodEntries().get(BulkOps.REMOVE_ALL).intValue(), is(KEY_SET_A.size())); } /** * Tests {@link EhcacheWithLoaderWriter#removeAll(Set)} for * <ul> * <li>non-empty request set</li> * <li>populated {@code Store} - some keys overlap request</li> * <li>{@link Store#bulkCompute} throws before accessing writer</li> * <li>populated {@code CacheLoaderWriter} - no keys overlap</li> * <li>some {@link CacheLoaderWriter#deleteAll(Iterable)} calls fail</li> * </ul> */ @Test public void testRemoveAllStoreSomeOverlapStoreAccessExceptionBeforeWriterNoOverlapSomeFail() 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_B, KEY_SET_D); final FakeCacheLoaderWriter fakeLoaderWriter = new FakeCacheLoaderWriter(originalWriterContent, KEY_SET_C); this.cacheLoaderWriter = spy(fakeLoaderWriter); final EhcacheWithLoaderWriter<String, String> ehcache = this.getEhcache(this.cacheLoaderWriter); final Set<String> contentUpdates = fanIn(KEY_SET_A, KEY_SET_C); final Set<String> expectedFailures = KEY_SET_C; final Set<String> expectedSuccesses = copyWithout(contentUpdates, expectedFailures); try { ehcache.removeAll(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))); // ResilienceStrategy invoked; no assertions about Store content ordered.verify(this.cacheLoaderWriter, atLeast(1)).deleteAll(getAnyStringIterable()); assertThat(fakeLoaderWriter.getEntryMap(), equalTo(copyWithout(originalWriterContent, expectedSuccesses))); ordered.verify(this.spiedResilienceStrategy) .removeAllFailure(eq(contentUpdates), any(StoreAccessException.class), this.bulkExceptionCaptor.capture()); assertThat(this.bulkExceptionCaptor.getValue().getSuccesses(), Matchers.<Set<?>>equalTo(expectedSuccesses)); assertThat(this.bulkExceptionCaptor.getValue().getFailures().keySet(), Matchers.<Set<?>>equalTo(expectedFailures)); validateStats(ehcache, EnumSet.noneOf(CacheOperationOutcomes.RemoveOutcome.class)); validateStats(ehcache, EnumSet.of(CacheOperationOutcomes.RemoveAllOutcome.FAILURE)); assertThat(ehcache.getBulkMethodEntries().get(BulkOps.REMOVE_ALL).intValue(), is(0)); } /** * Tests {@link EhcacheWithLoaderWriter#removeAll(Set)} for * <ul> * <li>non-empty request set</li> * <li>populated {@code Store} - some keys overlap request</li> * <li>{@link Store#bulkCompute} throws after accessing writer</li> * <li>populated {@code CacheLoaderWriter} - no keys overlap</li> * <li>some {@link CacheLoaderWriter#deleteAll(Iterable)} calls fail</li> * </ul> */ @Test public void testRemoveAllStoreSomeOverlapStoreAccessExceptionAfterWriterNoOverlapSomeFail() 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_B, KEY_SET_D); final FakeCacheLoaderWriter fakeLoaderWriter = new FakeCacheLoaderWriter(originalWriterContent, KEY_SET_C); this.cacheLoaderWriter = spy(fakeLoaderWriter); final EhcacheWithLoaderWriter<String, String> ehcache = this.getEhcache(this.cacheLoaderWriter); final Set<String> contentUpdates = fanIn(KEY_SET_A, KEY_SET_C); final Set<String> expectedFailures = KEY_SET_C; final Set<String> expectedSuccesses = copyWithout(contentUpdates, expectedFailures); try { ehcache.removeAll(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))); // ResilienceStrategy invoked; no assertions about Store content ordered.verify(this.cacheLoaderWriter, atLeast(1)).deleteAll(getAnyStringIterable()); assertThat(fakeLoaderWriter.getEntryMap(), equalTo(copyWithout(originalWriterContent, expectedSuccesses))); ordered.verify(this.spiedResilienceStrategy) .removeAllFailure(eq(contentUpdates), any(StoreAccessException.class), this.bulkExceptionCaptor.capture()); assertThat(this.bulkExceptionCaptor.getValue().getSuccesses(), Matchers.<Set<?>>equalTo(expectedSuccesses)); assertThat(this.bulkExceptionCaptor.getValue().getFailures().keySet(), Matchers.<Set<?>>equalTo(expectedFailures)); validateStats(ehcache, EnumSet.noneOf(CacheOperationOutcomes.RemoveOutcome.class)); validateStats(ehcache, EnumSet.of(CacheOperationOutcomes.RemoveAllOutcome.FAILURE)); assertThat(ehcache.getBulkMethodEntries().get(BulkOps.REMOVE_ALL).intValue(), is(0)); } /** * Tests {@link EhcacheWithLoaderWriter#removeAll(Set)} for * <ul> * <li>non-empty request set</li> * <li>populated {@code Store} - some keys overlap request</li> * <li>populated {@code CacheLoaderWriter} - no keys overlap</li> * <li>some {@link CacheLoaderWriter#deleteAll(Iterable)} calls fail</li> * <li>at least one {@link CacheLoaderWriter#deleteAll(Iterable)} call aborts</li> * </ul> */ @Test public void testRemoveAllStoreSomeOverlapWriterNoOverlapSomeFailWithAbort() 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_B, KEY_SET_D); final FakeCacheLoaderWriter fakeLoaderWriter = new FakeCacheLoaderWriter(originalWriterContent, KEY_SET_C); fakeLoaderWriter.setCompleteFailureKey("keyC4"); this.cacheLoaderWriter = spy(fakeLoaderWriter); final EhcacheWithLoaderWriter<String, String> ehcache = this.getEhcache(this.cacheLoaderWriter); final Set<String> contentUpdates = fanIn(KEY_SET_A, KEY_SET_C); final Set<String> expectedFailures = KEY_SET_C; final Set<String> expectedSuccesses = copyWithout(contentUpdates, expectedFailures); try { ehcache.removeAll(contentUpdates); fail(); } catch (BulkCacheWritingException e) { // Expected assertThat(e.getSuccesses(), Matchers.<Set<?>>equalTo(expectedSuccesses)); assertThat(e.getFailures().keySet(), Matchers.<Set<?>>equalTo(expectedFailures)); } verify(this.store, atLeast(1)).bulkCompute(this.bulkComputeSetCaptor.capture(), getAnyEntryIterableFunction()); assertThat(this.getBulkComputeArgs(), everyItem(isIn(contentUpdates))); assertThat(fakeStore.getEntryMap(), equalTo(copyWithout(originalStoreContent, expectedSuccesses))); verify(this.cacheLoaderWriter, atLeast(1)).deleteAll(getAnyStringIterable()); assertThat(fakeLoaderWriter.getEntryMap(), equalTo(copyWithout(originalWriterContent, expectedSuccesses))); verifyZeroInteractions(this.spiedResilienceStrategy); validateStats(ehcache, EnumSet.noneOf(CacheOperationOutcomes.RemoveOutcome.class)); validateStats(ehcache, EnumSet.of(CacheOperationOutcomes.RemoveAllOutcome.FAILURE)); assertThat(ehcache.getBulkMethodEntries().get(BulkOps.REMOVE_ALL).intValue(), is(KEY_SET_A.size())); } /** * Tests {@link EhcacheWithLoaderWriter#removeAll(Set)} for * <ul> * <li>non-empty request set</li> * <li>populated {@code Store} - some keys overlap request</li> * <li>{@link Store#bulkCompute} throws before accessing writer</li> * <li>populated {@code CacheLoaderWriter} - no keys overlap</li> * <li>some {@link CacheLoaderWriter#deleteAll(Iterable)} calls fail</li> * <li>at least one {@link CacheLoaderWriter#deleteAll(Iterable)} call aborts</li> * </ul> */ @Test public void testRemoveAllStoreSomeOverlapStoreAccessExceptionBeforeWriterNoOverlapSomeFailWithAbort() 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_B, KEY_SET_D); final FakeCacheLoaderWriter fakeLoaderWriter = new FakeCacheLoaderWriter(originalWriterContent, KEY_SET_C); fakeLoaderWriter.setCompleteFailureKey("keyC4"); this.cacheLoaderWriter = spy(fakeLoaderWriter); final EhcacheWithLoaderWriter<String, String> ehcache = this.getEhcache(this.cacheLoaderWriter); final Set<String> contentUpdates = fanIn(KEY_SET_A, KEY_SET_C); final Set<String> expectedFailures = KEY_SET_C; final Set<String> expectedSuccesses = copyWithout(contentUpdates, expectedFailures); try { ehcache.removeAll(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))); // ResilienceStrategy invoked; no assertions about Store content ordered.verify(this.cacheLoaderWriter, atLeast(1)).deleteAll(getAnyStringIterable()); ordered.verify(this.spiedResilienceStrategy) .removeAllFailure(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)); assertThat(Collections.disjoint(bcweSuccesses, bcweFailures.keySet()), is(true)); assertThat(bcweSuccesses, everyItem(isIn(expectedSuccesses))); assertThat(expectedFailures, everyItem(isIn(bcweFailures.keySet()))); assertThat(copyWithout(fakeLoaderWriter.getEntryMap(), bcweFailures.keySet()), equalTo(copyWithout(copyWithout(originalWriterContent, copyOnly(contentUpdates, bcweSuccesses)), bcweFailures.keySet()))); validateStats(ehcache, EnumSet.noneOf(CacheOperationOutcomes.RemoveOutcome.class)); validateStats(ehcache, EnumSet.of(CacheOperationOutcomes.RemoveAllOutcome.FAILURE)); assertThat(ehcache.getBulkMethodEntries().get(BulkOps.REMOVE_ALL).intValue(), is(0)); } /** * Tests {@link EhcacheWithLoaderWriter#removeAll(Set)} for * <ul> * <li>non-empty request set</li> * <li>populated {@code Store} - some keys overlap request</li> * <li>{@link Store#bulkCompute} throws after accessing writer</li> * <li>populated {@code CacheLoaderWriter} - no keys overlap</li> * <li>some {@link CacheLoaderWriter#deleteAll(Iterable)} calls fail</li> * <li>at least one {@link CacheLoaderWriter#deleteAll(Iterable)} call aborts</li> * </ul> */ @Test public void testRemoveAllStoreSomeOverlapStoreAccessExceptionAfterWriterNoOverlapSomeFailWithAbort() 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_B, KEY_SET_D); final FakeCacheLoaderWriter fakeLoaderWriter = new FakeCacheLoaderWriter(originalWriterContent, KEY_SET_C); fakeLoaderWriter.setCompleteFailureKey("keyC4"); this.cacheLoaderWriter = spy(fakeLoaderWriter); final EhcacheWithLoaderWriter<String, String> ehcache = this.getEhcache(this.cacheLoaderWriter); final Set<String> contentUpdates = fanIn(KEY_SET_A, KEY_SET_C); final Set<String> expectedFailures = KEY_SET_C; final Set<String> expectedSuccesses = copyWithout(contentUpdates, expectedFailures); try { ehcache.removeAll(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))); // ResilienceStrategy invoked; no assertions about Store content ordered.verify(this.cacheLoaderWriter, atLeast(1)).deleteAll(getAnyStringIterable()); ordered.verify(this.spiedResilienceStrategy) .removeAllFailure(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)); assertThat(Collections.disjoint(bcweSuccesses, bcweFailures.keySet()), is(true)); assertThat(bcweSuccesses, everyItem(isIn(expectedSuccesses))); assertThat(expectedFailures, everyItem(isIn(bcweFailures.keySet()))); assertThat(copyWithout(fakeLoaderWriter.getEntryMap(), bcweFailures.keySet()), equalTo(copyWithout(copyWithout(originalWriterContent, copyOnly(contentUpdates, bcweSuccesses)), bcweFailures.keySet()))); validateStats(ehcache, EnumSet.noneOf(CacheOperationOutcomes.RemoveOutcome.class)); validateStats(ehcache, EnumSet.of(CacheOperationOutcomes.RemoveAllOutcome.FAILURE)); assertThat(ehcache.getBulkMethodEntries().get(BulkOps.REMOVE_ALL).intValue(), is(0)); } /** * Tests {@link EhcacheWithLoaderWriter#removeAll(Set)} for * <ul> * <li>non-empty request set</li> * <li>populated {@code Store} - some keys overlap request</li> * <li>populated {@code CacheLoaderWriter} - no keys overlap</li> * <li>all {@link CacheLoaderWriter#deleteAll(Iterable)} calls fail</li> * </ul> */ @Test public void testRemoveAllStoreSomeOverlapWriterNoOverlapAllFail() 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_B, KEY_SET_D); final FakeCacheLoaderWriter fakeLoaderWriter = new FakeCacheLoaderWriter(originalWriterContent); this.cacheLoaderWriter = spy(fakeLoaderWriter); doThrow(new Exception("deleteAll failed")).when(this.cacheLoaderWriter).deleteAll(getAnyStringIterable()); final EhcacheWithLoaderWriter<String, String> ehcache = this.getEhcache(this.cacheLoaderWriter); final Set<String> contentUpdates = fanIn(KEY_SET_A, KEY_SET_C); try { ehcache.removeAll(contentUpdates); fail(); } catch (BulkCacheWritingException e) { // Expected assertThat(e.getSuccesses(), empty()); assertThat(e.getFailures().keySet(), Matchers.<Set<?>>equalTo(contentUpdates)); } verify(this.store, atLeast(1)).bulkCompute(this.bulkComputeSetCaptor.capture(), getAnyEntryIterableFunction()); assertThat(this.getBulkComputeArgs(), everyItem(isIn(contentUpdates))); assertThat(fakeStore.getEntryMap(), equalTo(copyWithout(originalStoreContent, contentUpdates))); verify(this.cacheLoaderWriter, atLeast(1)).deleteAll(getAnyStringIterable()); verifyZeroInteractions(this.spiedResilienceStrategy); validateStats(ehcache, EnumSet.noneOf(CacheOperationOutcomes.RemoveOutcome.class)); validateStats(ehcache, EnumSet.of(CacheOperationOutcomes.RemoveAllOutcome.FAILURE)); assertThat(ehcache.getBulkMethodEntries().get(BulkOps.REMOVE_ALL).intValue(), is(0)); } /** * Tests {@link EhcacheWithLoaderWriter#removeAll(Set)} for * <ul> * <li>non-empty request set</li> * <li>populated {@code Store} - some keys overlap request</li> * <li>{@link Store#bulkCompute} throws before accessing writer</li> * <li>populated {@code CacheLoaderWriter} - no keys overlap</li> * <li>all {@link CacheLoaderWriter#deleteAll(Iterable)} calls fail</li> * </ul> */ @Test public void testRemoveAllStoreSomeOverlapStoreAccessExceptionBeforeWriterNoOverlapAllFail() 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_B, KEY_SET_D); final FakeCacheLoaderWriter fakeLoaderWriter = new FakeCacheLoaderWriter(originalWriterContent); this.cacheLoaderWriter = spy(fakeLoaderWriter); doThrow(new Exception("deleteAll failed")).when(this.cacheLoaderWriter).deleteAll(getAnyStringIterable()); final EhcacheWithLoaderWriter<String, String> ehcache = this.getEhcache(this.cacheLoaderWriter); final Set<String> contentUpdates = fanIn(KEY_SET_A, KEY_SET_C); try { ehcache.removeAll(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))); // ResilienceStrategy invoked; no assertions about Store content ordered.verify(this.cacheLoaderWriter, atLeast(1)).deleteAll(getAnyStringIterable()); ordered.verify(this.spiedResilienceStrategy) .removeAllFailure(eq(contentUpdates), any(StoreAccessException.class), this.bulkExceptionCaptor.capture()); assertThat(this.bulkExceptionCaptor.getValue().getSuccesses(), empty()); assertThat(this.bulkExceptionCaptor.getValue().getFailures().keySet(), Matchers.<Set<?>>equalTo(contentUpdates)); validateStats(ehcache, EnumSet.noneOf(CacheOperationOutcomes.RemoveOutcome.class)); validateStats(ehcache, EnumSet.of(CacheOperationOutcomes.RemoveAllOutcome.FAILURE)); assertThat(ehcache.getBulkMethodEntries().get(BulkOps.REMOVE_ALL).intValue(), is(0)); } /** * Tests {@link EhcacheWithLoaderWriter#removeAll(Set)} for * <ul> * <li>non-empty request set</li> * <li>populated {@code Store} - some keys overlap request</li> * <li>{@link Store#bulkCompute} throws after accessing writer</li> * <li>populated {@code CacheLoaderWriter} - no keys overlap</li> * <li>all {@link CacheLoaderWriter#deleteAll(Iterable)} calls fail</li> * </ul> */ @Test public void testRemoveAllStoreSomeOverlapStoreAccessExceptionAfterWriterNoOverlapAllFail() 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_B, KEY_SET_D); final FakeCacheLoaderWriter fakeLoaderWriter = new FakeCacheLoaderWriter(originalWriterContent); this.cacheLoaderWriter = spy(fakeLoaderWriter); doThrow(new Exception("deleteAll failed")).when(this.cacheLoaderWriter).deleteAll(getAnyStringIterable()); final EhcacheWithLoaderWriter<String, String> ehcache = this.getEhcache(this.cacheLoaderWriter); final Set<String> contentUpdates = fanIn(KEY_SET_A, KEY_SET_C); try { ehcache.removeAll(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))); // ResilienceStrategy invoked; no assertions about Store content ordered.verify(this.cacheLoaderWriter, atLeast(1)).deleteAll(getAnyStringIterable()); ordered.verify(this.spiedResilienceStrategy) .removeAllFailure(eq(contentUpdates), any(StoreAccessException.class), this.bulkExceptionCaptor.capture()); assertThat(this.bulkExceptionCaptor.getValue().getSuccesses(), empty()); assertThat(this.bulkExceptionCaptor.getValue().getFailures().keySet(), Matchers.<Set<?>>equalTo(contentUpdates)); validateStats(ehcache, EnumSet.noneOf(CacheOperationOutcomes.RemoveOutcome.class)); validateStats(ehcache, EnumSet.of(CacheOperationOutcomes.RemoveAllOutcome.FAILURE)); assertThat(ehcache.getBulkMethodEntries().get(BulkOps.REMOVE_ALL).intValue(), is(0)); } /** * Tests {@link EhcacheWithLoaderWriter#removeAll(Set)} for * <ul> * <li>non-empty request set</li> * <li>populated {@code Store} - some keys overlap request</li> * <li>populated {@code CacheLoaderWriter} - some keys overlap</li> * <li>no {@link CacheLoaderWriter#deleteAll(Iterable)} calls fail</li> * </ul> */ @Test public void testRemoveAllStoreSomeOverlapWriterSomeOverlapNoneFail() 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_B, KEY_SET_C); final FakeCacheLoaderWriter fakeLoaderWriter = new FakeCacheLoaderWriter(originalWriterContent); this.cacheLoaderWriter = spy(fakeLoaderWriter); final EhcacheWithLoaderWriter<String, String> ehcache = this.getEhcache(this.cacheLoaderWriter); final Set<String> contentUpdates = fanIn(KEY_SET_A, KEY_SET_C, KEY_SET_D); ehcache.removeAll(contentUpdates); verify(this.store, atLeast(1)).bulkCompute(this.bulkComputeSetCaptor.capture(), getAnyEntryIterableFunction()); assertThat(this.getBulkComputeArgs(), equalTo(contentUpdates)); assertThat(fakeStore.getEntryMap(), equalTo(copyWithout(originalStoreContent, contentUpdates))); verify(this.cacheLoaderWriter, atLeast(1)).deleteAll(getAnyStringIterable()); assertThat(fakeLoaderWriter.getEntryMap(), equalTo(copyWithout(originalWriterContent, contentUpdates))); verifyZeroInteractions(this.spiedResilienceStrategy); validateStats(ehcache, EnumSet.noneOf(CacheOperationOutcomes.RemoveOutcome.class)); validateStats(ehcache, EnumSet.of(CacheOperationOutcomes.RemoveAllOutcome.SUCCESS)); assertThat(ehcache.getBulkMethodEntries().get(BulkOps.REMOVE_ALL).intValue(), is(KEY_SET_A.size())); } /** * Tests {@link EhcacheWithLoaderWriter#removeAll(Set)} for * <ul> * <li>non-empty request set</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#deleteAll(Iterable)} calls fail</li> * </ul> */ @Test public void testRemoveAllStoreSomeOverlapStoreAccessExceptionBeforeWriterSomeOverlapNoneFail() 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_B, KEY_SET_C); final FakeCacheLoaderWriter fakeLoaderWriter = new FakeCacheLoaderWriter(originalWriterContent); this.cacheLoaderWriter = spy(fakeLoaderWriter); final EhcacheWithLoaderWriter<String, String> ehcache = this.getEhcache(this.cacheLoaderWriter); final Set<String> contentUpdates = fanIn(KEY_SET_A, KEY_SET_C, KEY_SET_D); ehcache.removeAll(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))); // ResilienceStrategy invoked; no assertions about Store content ordered.verify(this.cacheLoaderWriter, atLeast(1)).deleteAll(getAnyStringIterable()); assertThat(fakeLoaderWriter.getEntryMap(), equalTo(copyWithout(originalWriterContent, contentUpdates))); ordered.verify(this.spiedResilienceStrategy) .removeAllFailure(eq(contentUpdates), any(StoreAccessException.class)); validateStats(ehcache, EnumSet.noneOf(CacheOperationOutcomes.RemoveOutcome.class)); validateStats(ehcache, EnumSet.of(CacheOperationOutcomes.RemoveAllOutcome.FAILURE)); assertThat(ehcache.getBulkMethodEntries().get(BulkOps.REMOVE_ALL).intValue(), is(0)); } /** * Tests {@link EhcacheWithLoaderWriter#removeAll(Set)} for * <ul> * <li>non-empty request set</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#deleteAll(Iterable)} calls fail</li> * </ul> */ @Test public void testRemoveAllStoreSomeOverlapStoreAccessExceptionAfterWriterSomeOverlapNoneFail() 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_B, KEY_SET_C); final FakeCacheLoaderWriter fakeLoaderWriter = new FakeCacheLoaderWriter(originalWriterContent); this.cacheLoaderWriter = spy(fakeLoaderWriter); final EhcacheWithLoaderWriter<String, String> ehcache = this.getEhcache(this.cacheLoaderWriter); final Set<String> contentUpdates = fanIn(KEY_SET_A, KEY_SET_C, KEY_SET_D); ehcache.removeAll(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))); // ResilienceStrategy invoked; no assertions about Store content ordered.verify(this.cacheLoaderWriter, atLeast(1)).deleteAll(getAnyStringIterable()); assertThat(fakeLoaderWriter.getEntryMap(), equalTo(copyWithout(originalWriterContent, contentUpdates))); ordered.verify(this.spiedResilienceStrategy) .removeAllFailure(eq(contentUpdates), any(StoreAccessException.class)); validateStats(ehcache, EnumSet.noneOf(CacheOperationOutcomes.RemoveOutcome.class)); validateStats(ehcache, EnumSet.of(CacheOperationOutcomes.RemoveAllOutcome.FAILURE)); assertThat(ehcache.getBulkMethodEntries().get(BulkOps.REMOVE_ALL).intValue(), is(0)); } /** * Tests {@link EhcacheWithLoaderWriter#removeAll(Set)} for * <ul> * <li>non-empty request set</li> * <li>populated {@code Store} - some keys overlap request</li> * <li>populated {@code CacheLoaderWriter} - some keys overlap</li> * <li>some {@link CacheLoaderWriter#deleteAll(Iterable)} calls fail</li> * </ul> */ @Test public void testRemoveAllStoreSomeOverlapWriterSomeOverlapSomeFail() 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_B, KEY_SET_C); final FakeCacheLoaderWriter fakeLoaderWriter = new FakeCacheLoaderWriter(originalWriterContent, KEY_SET_D); this.cacheLoaderWriter = spy(fakeLoaderWriter); final EhcacheWithLoaderWriter<String, String> ehcache = this.getEhcache(this.cacheLoaderWriter); final Set<String> contentUpdates = fanIn(KEY_SET_A, KEY_SET_C, KEY_SET_D); final Set<String> expectedFailures = KEY_SET_D; final Set<String> expectedSuccesses = copyWithout(contentUpdates, expectedFailures); try { ehcache.removeAll(contentUpdates); fail(); } catch (BulkCacheWritingException e) { // Expected assertThat(e.getSuccesses(), Matchers.<Set<?>>equalTo(expectedSuccesses)); assertThat(e.getFailures().keySet(), Matchers.<Set<?>>equalTo(expectedFailures)); } verify(this.store, atLeast(1)).bulkCompute(this.bulkComputeSetCaptor.capture(), getAnyEntryIterableFunction()); assertThat(this.getBulkComputeArgs(), everyItem(isIn(contentUpdates))); assertThat(fakeStore.getEntryMap(), equalTo(copyWithout(originalStoreContent, expectedSuccesses))); verify(this.cacheLoaderWriter, atLeast(1)).deleteAll(getAnyStringIterable()); assertThat(fakeLoaderWriter.getEntryMap(), equalTo(copyWithout(originalWriterContent, expectedSuccesses))); verifyZeroInteractions(this.spiedResilienceStrategy); validateStats(ehcache, EnumSet.noneOf(CacheOperationOutcomes.RemoveOutcome.class)); validateStats(ehcache, EnumSet.of(CacheOperationOutcomes.RemoveAllOutcome.FAILURE)); assertThat(ehcache.getBulkMethodEntries().get(BulkOps.REMOVE_ALL).intValue(), is(KEY_SET_A.size())); } /** * Tests {@link EhcacheWithLoaderWriter#removeAll(Set)} for * <ul> * <li>non-empty request set</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#deleteAll(Iterable)} calls fail</li> * </ul> */ @Test public void testRemoveAllStoreSomeOverlapStoreAccessExceptionBeforeWriterSomeOverlapSomeFail() 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_B, KEY_SET_C); final FakeCacheLoaderWriter fakeLoaderWriter = new FakeCacheLoaderWriter(originalWriterContent, KEY_SET_D); this.cacheLoaderWriter = spy(fakeLoaderWriter); final EhcacheWithLoaderWriter<String, String> ehcache = this.getEhcache(this.cacheLoaderWriter); final Set<String> contentUpdates = fanIn(KEY_SET_A, KEY_SET_C, KEY_SET_D); final Set<String> expectedFailures = KEY_SET_D; final Set<String> expectedSuccesses = copyWithout(contentUpdates, expectedFailures); try { ehcache.removeAll(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))); // ResilienceStrategy invoked; no assertions about Store content ordered.verify(this.cacheLoaderWriter, atLeast(1)).deleteAll(getAnyStringIterable()); assertThat(fakeLoaderWriter.getEntryMap(), equalTo(copyWithout(originalWriterContent, expectedSuccesses))); ordered.verify(this.spiedResilienceStrategy) .removeAllFailure(eq(contentUpdates), any(StoreAccessException.class), this.bulkExceptionCaptor.capture()); assertThat(this.bulkExceptionCaptor.getValue().getSuccesses(), Matchers.<Set<?>>equalTo(expectedSuccesses)); assertThat(this.bulkExceptionCaptor.getValue().getFailures().keySet(), Matchers.<Set<?>>equalTo(expectedFailures)); validateStats(ehcache, EnumSet.noneOf(CacheOperationOutcomes.RemoveOutcome.class)); validateStats(ehcache, EnumSet.of(CacheOperationOutcomes.RemoveAllOutcome.FAILURE)); assertThat(ehcache.getBulkMethodEntries().get(BulkOps.REMOVE_ALL).intValue(), is(0)); } /** * Tests {@link EhcacheWithLoaderWriter#removeAll(Set)} for * <ul> * <li>non-empty request set</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#deleteAll(Iterable)} calls fail</li> * </ul> */ @Test public void testRemoveAllStoreSomeOverlapStoreAccessExceptionAfterWriterSomeOverlapSomeFail() 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_B, KEY_SET_C); final FakeCacheLoaderWriter fakeLoaderWriter = new FakeCacheLoaderWriter(originalWriterContent, KEY_SET_D); this.cacheLoaderWriter = spy(fakeLoaderWriter); final EhcacheWithLoaderWriter<String, String> ehcache = this.getEhcache(this.cacheLoaderWriter); final Set<String> contentUpdates = fanIn(KEY_SET_A, KEY_SET_C, KEY_SET_D); final Set<String> expectedFailures = KEY_SET_D; final Set<String> expectedSuccesses = copyWithout(contentUpdates, expectedFailures); try { ehcache.removeAll(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))); // ResilienceStrategy invoked; no assertions about Store content ordered.verify(this.cacheLoaderWriter, atLeast(1)).deleteAll(getAnyStringIterable()); assertThat(fakeLoaderWriter.getEntryMap(), equalTo(copyWithout(originalWriterContent, expectedSuccesses))); ordered.verify(this.spiedResilienceStrategy) .removeAllFailure(eq(contentUpdates), any(StoreAccessException.class), this.bulkExceptionCaptor.capture()); assertThat(this.bulkExceptionCaptor.getValue().getSuccesses(), Matchers.<Set<?>>equalTo(expectedSuccesses)); assertThat(this.bulkExceptionCaptor.getValue().getFailures().keySet(), Matchers.<Set<?>>equalTo(expectedFailures)); validateStats(ehcache, EnumSet.noneOf(CacheOperationOutcomes.RemoveOutcome.class)); validateStats(ehcache, EnumSet.of(CacheOperationOutcomes.RemoveAllOutcome.FAILURE)); assertThat(ehcache.getBulkMethodEntries().get(BulkOps.REMOVE_ALL).intValue(), is(0)); } /** * Tests {@link EhcacheWithLoaderWriter#removeAll(Set)} for * <ul> * <li>non-empty request set</li> * <li>populated {@code Store} - some keys overlap request</li> * <li>populated {@code CacheLoaderWriter} - some keys overlap</li> * <li>some {@link CacheLoaderWriter#deleteAll(Iterable)} calls fail</li> * <li>at least one {@link CacheLoaderWriter#deleteAll(Iterable)} call aborts</li> * </ul> */ @Test public void testRemoveAllStoreSomeOverlapWriterSomeOverlapSomeFailWithAbort() 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_B, KEY_SET_C); final FakeCacheLoaderWriter fakeLoaderWriter = new FakeCacheLoaderWriter(originalWriterContent, KEY_SET_D); fakeLoaderWriter.setCompleteFailureKey("keyC4"); this.cacheLoaderWriter = spy(fakeLoaderWriter); final EhcacheWithLoaderWriter<String, String> ehcache = this.getEhcache(this.cacheLoaderWriter); final Set<String> contentUpdates = fanIn(KEY_SET_A, KEY_SET_C, KEY_SET_D); final Set<String> expectedFailures = union(KEY_SET_D, Collections.singleton("keyC4")); final Set<String> expectedSuccesses = copyWithout(contentUpdates, expectedFailures); try { ehcache.removeAll(contentUpdates); fail(); } catch (BulkCacheWritingException e) { // Expected assertThat(e.getSuccesses(), Matchers.<Set<?>>equalTo(expectedSuccesses)); assertThat(e.getFailures().keySet(), Matchers.<Set<?>>equalTo(expectedFailures)); } verify(this.store, atLeast(1)).bulkCompute(this.bulkComputeSetCaptor.capture(), getAnyEntryIterableFunction()); assertThat(this.getBulkComputeArgs(), everyItem(isIn(contentUpdates))); assertThat(fakeStore.getEntryMap(), equalTo(copyWithout(originalStoreContent, expectedSuccesses))); verify(this.cacheLoaderWriter, atLeast(1)).deleteAll(getAnyStringIterable()); assertThat(fakeLoaderWriter.getEntryMap(), equalTo(copyWithout(originalWriterContent, expectedSuccesses))); verifyZeroInteractions(this.spiedResilienceStrategy); validateStats(ehcache, EnumSet.noneOf(CacheOperationOutcomes.RemoveOutcome.class)); validateStats(ehcache, EnumSet.of(CacheOperationOutcomes.RemoveAllOutcome.FAILURE)); assertThat(ehcache.getBulkMethodEntries().get(BulkOps.REMOVE_ALL).intValue(), is(KEY_SET_A.size())); } /** * Tests {@link EhcacheWithLoaderWriter#removeAll(Set)} for * <ul> * <li>non-empty request set</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#deleteAll(Iterable)} calls fail</li> * <li>at least one {@link CacheLoaderWriter#deleteAll(Iterable)} call aborts</li> * </ul> */ @Test public void testRemoveAllStoreSomeOverlapStoreAccessExceptionBeforeWriterSomeOverlapSomeFailWithAbort() 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_B, KEY_SET_C); final FakeCacheLoaderWriter fakeLoaderWriter = new FakeCacheLoaderWriter(originalWriterContent, KEY_SET_D); fakeLoaderWriter.setCompleteFailureKey("keyC4"); this.cacheLoaderWriter = spy(fakeLoaderWriter); final EhcacheWithLoaderWriter<String, String> ehcache = this.getEhcache(this.cacheLoaderWriter); final Set<String> contentUpdates = fanIn(KEY_SET_A, KEY_SET_C, KEY_SET_D); final Set<String> expectedFailures = union(KEY_SET_D, Collections.singleton("keyC4")); final Set<String> expectedSuccesses = copyWithout(contentUpdates, expectedFailures); try { ehcache.removeAll(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))); // ResilienceStrategy invoked; no assertions about Store content ordered.verify(this.cacheLoaderWriter, atLeast(1)).deleteAll(getAnyStringIterable()); ordered.verify(this.spiedResilienceStrategy) .removeAllFailure(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)); assertThat(Collections.disjoint(bcweSuccesses, bcweFailures.keySet()), is(true)); assertThat(bcweSuccesses, everyItem(isIn(expectedSuccesses))); assertThat(expectedFailures, everyItem(isIn(bcweFailures.keySet()))); assertThat(copyWithout(fakeLoaderWriter.getEntryMap(), bcweFailures.keySet()), equalTo(copyWithout(copyWithout(originalWriterContent, copyOnly(contentUpdates, bcweSuccesses)), bcweFailures.keySet()))); validateStats(ehcache, EnumSet.noneOf(CacheOperationOutcomes.RemoveOutcome.class)); validateStats(ehcache, EnumSet.of(CacheOperationOutcomes.RemoveAllOutcome.FAILURE)); assertThat(ehcache.getBulkMethodEntries().get(BulkOps.REMOVE_ALL).intValue(), is(0)); } /** * Tests {@link EhcacheWithLoaderWriter#removeAll(Set)} for * <ul> * <li>non-empty request set</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#deleteAll(Iterable)} calls fail</li> * <li>at least one {@link CacheLoaderWriter#deleteAll(Iterable)} call aborts</li> * </ul> */ @Test public void testRemoveAllStoreSomeOverlapStoreAccessExceptionAfterWriterSomeOverlapSomeFailWithAbort() 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_B, KEY_SET_C); final FakeCacheLoaderWriter fakeLoaderWriter = new FakeCacheLoaderWriter(originalWriterContent, KEY_SET_D); fakeLoaderWriter.setCompleteFailureKey("keyC4"); this.cacheLoaderWriter = spy(fakeLoaderWriter); final EhcacheWithLoaderWriter<String, String> ehcache = this.getEhcache(this.cacheLoaderWriter); final Set<String> contentUpdates = fanIn(KEY_SET_A, KEY_SET_C, KEY_SET_D); final Set<String> expectedFailures = union(KEY_SET_D, Collections.singleton("keyC4")); final Set<String> expectedSuccesses = copyWithout(contentUpdates, expectedFailures); try { ehcache.removeAll(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))); // ResilienceStrategy invoked; no assertions about Store content ordered.verify(this.cacheLoaderWriter, atLeast(1)).deleteAll(getAnyStringIterable()); ordered.verify(this.spiedResilienceStrategy) .removeAllFailure(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)); assertThat(Collections.disjoint(bcweSuccesses, bcweFailures.keySet()), is(true)); assertThat(bcweSuccesses, everyItem(isIn(expectedSuccesses))); assertThat(expectedFailures, everyItem(isIn(bcweFailures.keySet()))); assertThat(copyWithout(fakeLoaderWriter.getEntryMap(), bcweFailures.keySet()), equalTo(copyWithout(copyWithout(originalWriterContent, copyOnly(contentUpdates, bcweSuccesses)), bcweFailures.keySet()))); validateStats(ehcache, EnumSet.noneOf(CacheOperationOutcomes.RemoveOutcome.class)); validateStats(ehcache, EnumSet.of(CacheOperationOutcomes.RemoveAllOutcome.FAILURE)); assertThat(ehcache.getBulkMethodEntries().get(BulkOps.REMOVE_ALL).intValue(), is(0)); } /** * Tests {@link EhcacheWithLoaderWriter#removeAll(Set)} for * <ul> * <li>non-empty request set</li> * <li>populated {@code Store} - some keys overlap request</li> * <li>populated {@code CacheLoaderWriter} - some keys overlap</li> * <li>all {@link CacheLoaderWriter#deleteAll(Iterable)} calls fail</li> * </ul> */ @Test public void testRemoveAllStoreSomeOverlapWriterSomeOverlapAllFail() 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_B, KEY_SET_C); final FakeCacheLoaderWriter fakeLoaderWriter = new FakeCacheLoaderWriter(originalWriterContent); this.cacheLoaderWriter = spy(fakeLoaderWriter); doThrow(new Exception("deleteAll failed")).when(this.cacheLoaderWriter).deleteAll(getAnyStringIterable()); final EhcacheWithLoaderWriter<String, String> ehcache = this.getEhcache(this.cacheLoaderWriter); final Set<String> contentUpdates = fanIn(KEY_SET_A, KEY_SET_C, KEY_SET_D); try { ehcache.removeAll(contentUpdates); fail(); } catch (BulkCacheWritingException e) { // Expected assertThat(e.getSuccesses(), empty()); assertThat(e.getFailures().keySet(), Matchers.<Set<?>>equalTo(contentUpdates)); } verify(this.store, atLeast(1)).bulkCompute(this.bulkComputeSetCaptor.capture(), getAnyEntryIterableFunction()); assertThat(this.getBulkComputeArgs(), everyItem(isIn(contentUpdates))); assertThat(fakeStore.getEntryMap(), equalTo(copyWithout(originalStoreContent, contentUpdates))); verify(this.cacheLoaderWriter, atLeast(1)).deleteAll(getAnyStringIterable()); verifyZeroInteractions(this.spiedResilienceStrategy); validateStats(ehcache, EnumSet.noneOf(CacheOperationOutcomes.RemoveOutcome.class)); validateStats(ehcache, EnumSet.of(CacheOperationOutcomes.RemoveAllOutcome.FAILURE)); assertThat(ehcache.getBulkMethodEntries().get(BulkOps.REMOVE_ALL).intValue(), is(0)); } /** * Tests {@link EhcacheWithLoaderWriter#removeAll(Set)} for * <ul> * <li>non-empty request set</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#deleteAll(Iterable)} calls fail</li> * </ul> */ @Test public void testRemoveAllStoreSomeOverlapStoreAccessExceptionBeforeWriterSomeOverlapAllFail() 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_B, KEY_SET_C); final FakeCacheLoaderWriter fakeLoaderWriter = new FakeCacheLoaderWriter(originalWriterContent); this.cacheLoaderWriter = spy(fakeLoaderWriter); doThrow(new Exception("deleteAll failed")).when(this.cacheLoaderWriter).deleteAll(getAnyStringIterable()); final EhcacheWithLoaderWriter<String, String> ehcache = this.getEhcache(this.cacheLoaderWriter); final Set<String> contentUpdates = fanIn(KEY_SET_A, KEY_SET_C, KEY_SET_D); try { ehcache.removeAll(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))); // ResilienceStrategy invoked; no assertions about Store content ordered.verify(this.cacheLoaderWriter, atLeast(1)).deleteAll(getAnyStringIterable()); ordered.verify(this.spiedResilienceStrategy) .removeAllFailure(eq(contentUpdates), any(StoreAccessException.class), this.bulkExceptionCaptor.capture()); assertThat(this.bulkExceptionCaptor.getValue().getSuccesses(), empty()); assertThat(this.bulkExceptionCaptor.getValue().getFailures().keySet(), Matchers.<Set<?>>equalTo(contentUpdates)); validateStats(ehcache, EnumSet.noneOf(CacheOperationOutcomes.RemoveOutcome.class)); validateStats(ehcache, EnumSet.of(CacheOperationOutcomes.RemoveAllOutcome.FAILURE)); assertThat(ehcache.getBulkMethodEntries().get(BulkOps.REMOVE_ALL).intValue(), is(0)); } /** * Tests {@link EhcacheWithLoaderWriter#removeAll(Set)} for * <ul> * <li>non-empty request set</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#deleteAll(Iterable)} calls fail</li> * </ul> */ @Test public void testRemoveAllStoreSomeOverlapStoreAccessExceptionAfterWriterSomeOverlapAllFail() 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_B, KEY_SET_C); final FakeCacheLoaderWriter fakeLoaderWriter = new FakeCacheLoaderWriter(originalWriterContent); this.cacheLoaderWriter = spy(fakeLoaderWriter); doThrow(new Exception("deleteAll failed")).when(this.cacheLoaderWriter).deleteAll(getAnyStringIterable()); final EhcacheWithLoaderWriter<String, String> ehcache = this.getEhcache(this.cacheLoaderWriter); final Set<String> contentUpdates = fanIn(KEY_SET_A, KEY_SET_C, KEY_SET_D); try { ehcache.removeAll(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))); // ResilienceStrategy invoked; no assertions about Store content ordered.verify(this.cacheLoaderWriter, atLeast(1)).deleteAll(getAnyStringIterable()); ordered.verify(this.spiedResilienceStrategy) .removeAllFailure(eq(contentUpdates), any(StoreAccessException.class), this.bulkExceptionCaptor.capture()); assertThat(this.bulkExceptionCaptor.getValue().getSuccesses(), empty()); assertThat(this.bulkExceptionCaptor.getValue().getFailures().keySet(), Matchers.<Set<?>>equalTo(contentUpdates)); validateStats(ehcache, EnumSet.noneOf(CacheOperationOutcomes.RemoveOutcome.class)); validateStats(ehcache, EnumSet.of(CacheOperationOutcomes.RemoveAllOutcome.FAILURE)); assertThat(ehcache.getBulkMethodEntries().get(BulkOps.REMOVE_ALL).intValue(), is(0)); } /** * Tests {@link EhcacheWithLoaderWriter#removeAll(Set)} for * <ul> * <li>non-empty request set</li> * <li>populated {@code Store} - some keys overlap request</li> * <li>populated {@code CacheLoaderWriter} - all keys overlap</li> * <li>no {@link CacheLoaderWriter#deleteAll(Iterable)} calls fail</li> * </ul> */ @Test public void testRemoveAllStoreSomeOverlapWriterFullOverlapNoneFail() 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_B, KEY_SET_C); final FakeCacheLoaderWriter fakeLoaderWriter = new FakeCacheLoaderWriter(originalWriterContent); this.cacheLoaderWriter = spy(fakeLoaderWriter); final EhcacheWithLoaderWriter<String, String> ehcache = this.getEhcache(this.cacheLoaderWriter); final Set<String> contentUpdates = fanIn(KEY_SET_B, KEY_SET_C); ehcache.removeAll(contentUpdates); verify(this.store, atLeast(1)).bulkCompute(this.bulkComputeSetCaptor.capture(), getAnyEntryIterableFunction()); assertThat(this.getBulkComputeArgs(), equalTo(contentUpdates)); assertThat(fakeStore.getEntryMap(), equalTo(copyWithout(originalStoreContent, contentUpdates))); verify(this.cacheLoaderWriter, atLeast(1)).deleteAll(getAnyStringIterable()); assertThat(fakeLoaderWriter.getEntryMap(), equalTo(copyWithout(originalWriterContent, contentUpdates))); verifyZeroInteractions(this.spiedResilienceStrategy); validateStats(ehcache, EnumSet.noneOf(CacheOperationOutcomes.RemoveOutcome.class)); validateStats(ehcache, EnumSet.of(CacheOperationOutcomes.RemoveAllOutcome.SUCCESS)); assertThat(ehcache.getBulkMethodEntries().get(BulkOps.REMOVE_ALL).intValue(), is(KEY_SET_B.size())); } /** * Tests {@link EhcacheWithLoaderWriter#removeAll(Set)} for * <ul> * <li>non-empty request set</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#deleteAll(Iterable)} calls fail</li> * </ul> */ @Test public void testRemoveAllStoreSomeOverlapStoreAccessExceptionBeforeWriterFullOverlapNoneFail() 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_B, KEY_SET_C); final FakeCacheLoaderWriter fakeLoaderWriter = new FakeCacheLoaderWriter(originalWriterContent); this.cacheLoaderWriter = spy(fakeLoaderWriter); final EhcacheWithLoaderWriter<String, String> ehcache = this.getEhcache(this.cacheLoaderWriter); final Set<String> contentUpdates = fanIn(KEY_SET_B, KEY_SET_C); ehcache.removeAll(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))); // ResilienceStrategy invoked; no assertions about Store content ordered.verify(this.cacheLoaderWriter, atLeast(1)).deleteAll(getAnyStringIterable()); assertThat(fakeLoaderWriter.getEntryMap(), equalTo(copyWithout(originalWriterContent, contentUpdates))); ordered.verify(this.spiedResilienceStrategy) .removeAllFailure(eq(contentUpdates), any(StoreAccessException.class)); validateStats(ehcache, EnumSet.noneOf(CacheOperationOutcomes.RemoveOutcome.class)); validateStats(ehcache, EnumSet.of(CacheOperationOutcomes.RemoveAllOutcome.FAILURE)); assertThat(ehcache.getBulkMethodEntries().get(BulkOps.REMOVE_ALL).intValue(), is(0)); } /** * Tests {@link EhcacheWithLoaderWriter#removeAll(Set)} for * <ul> * <li>non-empty request set</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#deleteAll(Iterable)} calls fail</li> * </ul> */ @Test public void testRemoveAllStoreSomeOverlapStoreAccessExceptionAfterWriterFullOverlapNoneFail() 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_B, KEY_SET_C); final FakeCacheLoaderWriter fakeLoaderWriter = new FakeCacheLoaderWriter(originalWriterContent); this.cacheLoaderWriter = spy(fakeLoaderWriter); final EhcacheWithLoaderWriter<String, String> ehcache = this.getEhcache(this.cacheLoaderWriter); final Set<String> contentUpdates = fanIn(KEY_SET_B, KEY_SET_C); ehcache.removeAll(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))); // ResilienceStrategy invoked; no assertions about Store content ordered.verify(this.cacheLoaderWriter, atLeast(1)).deleteAll(getAnyStringIterable()); assertThat(fakeLoaderWriter.getEntryMap(), equalTo(copyWithout(originalWriterContent, contentUpdates))); ordered.verify(this.spiedResilienceStrategy) .removeAllFailure(eq(contentUpdates), any(StoreAccessException.class)); validateStats(ehcache, EnumSet.noneOf(CacheOperationOutcomes.RemoveOutcome.class)); validateStats(ehcache, EnumSet.of(CacheOperationOutcomes.RemoveAllOutcome.FAILURE)); assertThat(ehcache.getBulkMethodEntries().get(BulkOps.REMOVE_ALL).intValue(), is(0)); } /** * Tests {@link EhcacheWithLoaderWriter#removeAll(Set)} for * <ul> * <li>non-empty request set</li> * <li>populated {@code Store} - some keys overlap request</li> * <li>populated {@code CacheLoaderWriter} - all keys overlap</li> * <li>some {@link CacheLoaderWriter#deleteAll(Iterable)} calls fail</li> * </ul> */ @Test public void testRemoveAllStoreSomeOverlapWriterFullOverlapSomeFail() 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_B, KEY_SET_C, KEY_SET_D); final FakeCacheLoaderWriter fakeLoaderWriter = new FakeCacheLoaderWriter(originalWriterContent, KEY_SET_D); this.cacheLoaderWriter = spy(fakeLoaderWriter); final EhcacheWithLoaderWriter<String, String> ehcache = this.getEhcache(this.cacheLoaderWriter); final Set<String> contentUpdates = fanIn(KEY_SET_B, KEY_SET_C, KEY_SET_D); final Set<String> expectedFailures = KEY_SET_D; final Set<String> expectedSuccesses = copyWithout(contentUpdates, expectedFailures); try { ehcache.removeAll(contentUpdates); fail(); } catch (BulkCacheWritingException e) { // Expected assertThat(e.getSuccesses(), Matchers.<Set<?>>equalTo(expectedSuccesses)); assertThat(e.getFailures().keySet(), Matchers.<Set<?>>equalTo(expectedFailures)); } verify(this.store, atLeast(1)).bulkCompute(this.bulkComputeSetCaptor.capture(), getAnyEntryIterableFunction()); assertThat(this.getBulkComputeArgs(), everyItem(isIn(contentUpdates))); assertThat(fakeStore.getEntryMap(), equalTo(copyWithout(originalStoreContent, expectedSuccesses))); verify(this.cacheLoaderWriter, atLeast(1)).deleteAll(getAnyStringIterable()); assertThat(fakeLoaderWriter.getEntryMap(), equalTo(copyWithout(originalWriterContent, expectedSuccesses))); verifyZeroInteractions(this.spiedResilienceStrategy); validateStats(ehcache, EnumSet.noneOf(CacheOperationOutcomes.RemoveOutcome.class)); validateStats(ehcache, EnumSet.of(CacheOperationOutcomes.RemoveAllOutcome.FAILURE)); assertThat(ehcache.getBulkMethodEntries().get(BulkOps.REMOVE_ALL).intValue(), is(KEY_SET_B.size())); } /** * Tests {@link EhcacheWithLoaderWriter#removeAll(Set)} for * <ul> * <li>non-empty request set</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#deleteAll(Iterable)} calls fail</li> * </ul> */ @Test public void testRemoveAllStoreSomeOverlapStoreAccessExceptionBeforeWriterFullOverlapSomeFail() 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_B, KEY_SET_C, KEY_SET_D); final FakeCacheLoaderWriter fakeLoaderWriter = new FakeCacheLoaderWriter(originalWriterContent, KEY_SET_D); this.cacheLoaderWriter = spy(fakeLoaderWriter); final EhcacheWithLoaderWriter<String, String> ehcache = this.getEhcache(this.cacheLoaderWriter); final Set<String> contentUpdates = fanIn(KEY_SET_B, KEY_SET_C, KEY_SET_D); final Set<String> expectedFailures = KEY_SET_D; final Set<String> expectedSuccesses = copyWithout(contentUpdates, expectedFailures); try { ehcache.removeAll(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))); // ResilienceStrategy invoked; no assertions about Store content ordered.verify(this.cacheLoaderWriter, atLeast(1)).deleteAll(getAnyStringIterable()); assertThat(fakeLoaderWriter.getEntryMap(), equalTo(copyWithout(originalWriterContent, expectedSuccesses))); ordered.verify(this.spiedResilienceStrategy) .removeAllFailure(eq(contentUpdates), any(StoreAccessException.class), this.bulkExceptionCaptor.capture()); assertThat(this.bulkExceptionCaptor.getValue().getSuccesses(), Matchers.<Set<?>>equalTo(expectedSuccesses)); assertThat(this.bulkExceptionCaptor.getValue().getFailures().keySet(), Matchers.<Set<?>>equalTo(expectedFailures)); validateStats(ehcache, EnumSet.noneOf(CacheOperationOutcomes.RemoveOutcome.class)); validateStats(ehcache, EnumSet.of(CacheOperationOutcomes.RemoveAllOutcome.FAILURE)); assertThat(ehcache.getBulkMethodEntries().get(BulkOps.REMOVE_ALL).intValue(), is(0)); } /** * Tests {@link EhcacheWithLoaderWriter#removeAll(Set)} for * <ul> * <li>non-empty request set</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#deleteAll(Iterable)} calls fail</li> * </ul> */ @Test public void testRemoveAllStoreSomeOverlapStoreAccessExceptionAfterWriterFullOverlapSomeFail() 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_B, KEY_SET_C, KEY_SET_D); final FakeCacheLoaderWriter fakeLoaderWriter = new FakeCacheLoaderWriter(originalWriterContent, KEY_SET_D); this.cacheLoaderWriter = spy(fakeLoaderWriter); final EhcacheWithLoaderWriter<String, String> ehcache = this.getEhcache(this.cacheLoaderWriter); final Set<String> contentUpdates = fanIn(KEY_SET_B, KEY_SET_C, KEY_SET_D); final Set<String> expectedFailures = KEY_SET_D; final Set<String> expectedSuccesses = copyWithout(contentUpdates, expectedFailures); try { ehcache.removeAll(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))); // ResilienceStrategy invoked; no assertions about Store content ordered.verify(this.cacheLoaderWriter, atLeast(1)).deleteAll(getAnyStringIterable()); assertThat(fakeLoaderWriter.getEntryMap(), equalTo(copyWithout(originalWriterContent, expectedSuccesses))); ordered.verify(this.spiedResilienceStrategy) .removeAllFailure(eq(contentUpdates), any(StoreAccessException.class), this.bulkExceptionCaptor.capture()); assertThat(this.bulkExceptionCaptor.getValue().getSuccesses(), Matchers.<Set<?>>equalTo(expectedSuccesses)); assertThat(this.bulkExceptionCaptor.getValue().getFailures().keySet(), Matchers.<Set<?>>equalTo(expectedFailures)); validateStats(ehcache, EnumSet.noneOf(CacheOperationOutcomes.RemoveOutcome.class)); validateStats(ehcache, EnumSet.of(CacheOperationOutcomes.RemoveAllOutcome.FAILURE)); assertThat(ehcache.getBulkMethodEntries().get(BulkOps.REMOVE_ALL).intValue(), is(0)); } /** * Tests {@link EhcacheWithLoaderWriter#removeAll(Set)} for * <ul> * <li>non-empty request set</li> * <li>populated {@code Store} - some keys overlap request</li> * <li>populated {@code CacheLoaderWriter} - all keys overlap</li> * <li>some {@link CacheLoaderWriter#deleteAll(Iterable)} calls fail</li> * <li>at least one {@link CacheLoaderWriter#deleteAll(Iterable)} call aborts</li> * </ul> */ @Test public void testRemoveAllStoreSomeOverlapWriterFullOverlapSomeFailWithAbort() 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_B, KEY_SET_C, KEY_SET_D); final FakeCacheLoaderWriter fakeLoaderWriter = new FakeCacheLoaderWriter(originalWriterContent, KEY_SET_D); fakeLoaderWriter.setCompleteFailureKey("keyC4"); this.cacheLoaderWriter = spy(fakeLoaderWriter); final EhcacheWithLoaderWriter<String, String> ehcache = this.getEhcache(this.cacheLoaderWriter); final Set<String> contentUpdates = fanIn(KEY_SET_B, KEY_SET_C, KEY_SET_D); final Set<String> expectedFailures = union(KEY_SET_D, Collections.singleton("keyC4")); final Set<String> expectedSuccesses = copyWithout(contentUpdates, expectedFailures); try { ehcache.removeAll(contentUpdates); fail(); } catch (BulkCacheWritingException e) { // Expected assertThat(e.getSuccesses(), Matchers.<Set<?>>equalTo(expectedSuccesses)); assertThat(e.getFailures().keySet(), Matchers.<Set<?>>equalTo(expectedFailures)); } verify(this.store, atLeast(1)) .bulkCompute(this.bulkComputeSetCaptor.capture(), getAnyEntryIterableFunction()); assertThat(this.getBulkComputeArgs(), everyItem(isIn(contentUpdates))); assertThat(fakeStore.getEntryMap(), equalTo(copyWithout(originalStoreContent, expectedSuccesses))); verify(this.cacheLoaderWriter, atLeast(1)).deleteAll(getAnyStringIterable()); assertThat(fakeLoaderWriter.getEntryMap(), equalTo(copyWithout(originalWriterContent, expectedSuccesses))); verifyZeroInteractions(this.spiedResilienceStrategy); validateStats(ehcache, EnumSet.noneOf(CacheOperationOutcomes.RemoveOutcome.class)); validateStats(ehcache, EnumSet.of(CacheOperationOutcomes.RemoveAllOutcome.FAILURE)); assertThat(ehcache.getBulkMethodEntries().get(BulkOps.REMOVE_ALL).intValue(), is(KEY_SET_B.size())); } /** * Tests {@link EhcacheWithLoaderWriter#removeAll(Set)} for * <ul> * <li>non-empty request set</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#deleteAll(Iterable)} calls fail</li> * <li>at least one {@link CacheLoaderWriter#deleteAll(Iterable)} call aborts</li> * </ul> */ @Test public void testRemoveAllStoreSomeOverlapStoreAccessExceptionBeforeWriterFullOverlapSomeFailWithAbort() 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_B, KEY_SET_C, KEY_SET_D); final FakeCacheLoaderWriter fakeLoaderWriter = new FakeCacheLoaderWriter(originalWriterContent, KEY_SET_D); fakeLoaderWriter.setCompleteFailureKey("keyC4"); this.cacheLoaderWriter = spy(fakeLoaderWriter); final EhcacheWithLoaderWriter<String, String> ehcache = this.getEhcache(this.cacheLoaderWriter); final Set<String> contentUpdates = fanIn(KEY_SET_B, KEY_SET_C, KEY_SET_D); final Set<String> expectedFailures = union(KEY_SET_D, Collections.singleton("keyC4")); final Set<String> expectedSuccesses = copyWithout(contentUpdates, expectedFailures); try { ehcache.removeAll(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))); // ResilienceStrategy invoked; no assertions about Store content ordered.verify(this.cacheLoaderWriter, atLeast(1)).deleteAll(getAnyStringIterable()); ordered.verify(this.spiedResilienceStrategy) .removeAllFailure(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)); assertThat(Collections.disjoint(bcweSuccesses, bcweFailures.keySet()), is(true)); assertThat(bcweSuccesses, everyItem(isIn(expectedSuccesses))); assertThat(expectedFailures, everyItem(isIn(bcweFailures.keySet()))); assertThat(copyWithout(fakeLoaderWriter.getEntryMap(), bcweFailures.keySet()), equalTo(copyWithout(copyWithout(originalWriterContent, copyOnly(contentUpdates, bcweSuccesses)), bcweFailures.keySet()))); validateStats(ehcache, EnumSet.noneOf(CacheOperationOutcomes.RemoveOutcome.class)); validateStats(ehcache, EnumSet.of(CacheOperationOutcomes.RemoveAllOutcome.FAILURE)); assertThat(ehcache.getBulkMethodEntries().get(BulkOps.REMOVE_ALL).intValue(), is(0)); } /** * Tests {@link EhcacheWithLoaderWriter#removeAll(Set)} for * <ul> * <li>non-empty request set</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#deleteAll(Iterable)} calls fail</li> * <li>at least one {@link CacheLoaderWriter#deleteAll(Iterable)} call aborts</li> * </ul> */ @Test public void testRemoveAllStoreSomeOverlapStoreAccessExceptionAfterWriterFullOverlapSomeFailWithAbort() 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_B, KEY_SET_C, KEY_SET_D); final FakeCacheLoaderWriter fakeLoaderWriter = new FakeCacheLoaderWriter(originalWriterContent, KEY_SET_D); fakeLoaderWriter.setCompleteFailureKey("keyC4"); this.cacheLoaderWriter = spy(fakeLoaderWriter); final EhcacheWithLoaderWriter<String, String> ehcache = this.getEhcache(this.cacheLoaderWriter); final Set<String> contentUpdates = fanIn(KEY_SET_B, KEY_SET_C, KEY_SET_D); final Set<String> expectedFailures = union(KEY_SET_D, Collections.singleton("keyC4")); final Set<String> expectedSuccesses = copyWithout(contentUpdates, expectedFailures); try { ehcache.removeAll(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))); // ResilienceStrategy invoked; no assertions about Store content ordered.verify(this.cacheLoaderWriter, atLeast(1)).deleteAll(getAnyStringIterable()); ordered.verify(this.spiedResilienceStrategy) .removeAllFailure(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)); assertThat(Collections.disjoint(bcweSuccesses, bcweFailures.keySet()), is(true)); assertThat(bcweSuccesses, everyItem(isIn(expectedSuccesses))); assertThat(expectedFailures, everyItem(isIn(bcweFailures.keySet()))); assertThat(copyWithout(fakeLoaderWriter.getEntryMap(), bcweFailures.keySet()), equalTo(copyWithout(copyWithout(originalWriterContent, copyOnly(contentUpdates, bcweSuccesses)), bcweFailures.keySet()))); validateStats(ehcache, EnumSet.noneOf(CacheOperationOutcomes.RemoveOutcome.class)); validateStats(ehcache, EnumSet.of(CacheOperationOutcomes.RemoveAllOutcome.FAILURE)); assertThat(ehcache.getBulkMethodEntries().get(BulkOps.REMOVE_ALL).intValue(), is(0)); } /** * Tests {@link EhcacheWithLoaderWriter#removeAll(Set)} for * <ul> * <li>non-empty request set</li> * <li>populated {@code Store} - some keys overlap request</li> * <li>populated {@code CacheLoaderWriter} - all keys overlap</li> * <li>all {@link CacheLoaderWriter#deleteAll(Iterable)} calls fail</li> * </ul> */ @Test public void testRemoveAllStoreSomeOverlapWriterFullOverlapAllFail() 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_B, KEY_SET_C, KEY_SET_D); final FakeCacheLoaderWriter fakeLoaderWriter = new FakeCacheLoaderWriter(originalWriterContent); this.cacheLoaderWriter = spy(fakeLoaderWriter); doThrow(new Exception("deleteAll failed")).when(this.cacheLoaderWriter).deleteAll(getAnyStringIterable()); final EhcacheWithLoaderWriter<String, String> ehcache = this.getEhcache(this.cacheLoaderWriter); final Set<String> contentUpdates = fanIn(KEY_SET_B, KEY_SET_C, KEY_SET_D); try { ehcache.removeAll(contentUpdates); fail(); } catch (BulkCacheWritingException e) { // Expected assertThat(e.getSuccesses(), empty()); assertThat(e.getFailures().keySet(), Matchers.<Set<?>>equalTo(contentUpdates)); } verify(this.store, atLeast(1)) .bulkCompute(this.bulkComputeSetCaptor.capture(), getAnyEntryIterableFunction()); assertThat(this.getBulkComputeArgs(), everyItem(isIn(contentUpdates))); assertThat(fakeStore.getEntryMap(), equalTo(copyWithout(originalStoreContent, contentUpdates))); verify(this.cacheLoaderWriter, atLeast(1)).deleteAll(getAnyStringIterable()); verifyZeroInteractions(this.spiedResilienceStrategy); validateStats(ehcache, EnumSet.noneOf(CacheOperationOutcomes.RemoveOutcome.class)); validateStats(ehcache, EnumSet.of(CacheOperationOutcomes.RemoveAllOutcome.FAILURE)); assertThat(ehcache.getBulkMethodEntries().get(BulkOps.REMOVE_ALL).intValue(), is(0)); } /** * Tests {@link EhcacheWithLoaderWriter#removeAll(Set)} for * <ul> * <li>non-empty request set</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#deleteAll(Iterable)} calls fail</li> * </ul> */ @Test public void testRemoveAllStoreSomeOverlapStoreAccessExceptionBeforeWriterFullOverlapAllFail() 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_B, KEY_SET_C, KEY_SET_D); final FakeCacheLoaderWriter fakeLoaderWriter = new FakeCacheLoaderWriter(originalWriterContent); this.cacheLoaderWriter = spy(fakeLoaderWriter); doThrow(new Exception("deleteAll failed")).when(this.cacheLoaderWriter).deleteAll(getAnyStringIterable()); final EhcacheWithLoaderWriter<String, String> ehcache = this.getEhcache(this.cacheLoaderWriter); final Set<String> contentUpdates = fanIn(KEY_SET_B, KEY_SET_C, KEY_SET_D); try { ehcache.removeAll(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))); // ResilienceStrategy invoked; no assertions about Store content ordered.verify(this.cacheLoaderWriter, atLeast(1)).deleteAll(getAnyStringIterable()); assertThat(fakeLoaderWriter.getEntryMap(), equalTo(originalWriterContent)); ordered.verify(this.spiedResilienceStrategy) .removeAllFailure(eq(contentUpdates), any(StoreAccessException.class), this.bulkExceptionCaptor.capture()); assertThat(this.bulkExceptionCaptor.getValue().getSuccesses(), empty()); assertThat(this.bulkExceptionCaptor.getValue().getFailures().keySet(), Matchers.<Set<?>>equalTo(contentUpdates)); validateStats(ehcache, EnumSet.noneOf(CacheOperationOutcomes.RemoveOutcome.class)); validateStats(ehcache, EnumSet.of(CacheOperationOutcomes.RemoveAllOutcome.FAILURE)); assertThat(ehcache.getBulkMethodEntries().get(BulkOps.REMOVE_ALL).intValue(), is(0)); } /** * Tests {@link EhcacheWithLoaderWriter#removeAll(Set)} for * <ul> * <li>non-empty request set</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#deleteAll(Iterable)} calls fail</li> * </ul> */ @Test public void testRemoveAllStoreSomeOverlapStoreAccessExceptionAfterWriterFullOverlapAllFail() 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_B, KEY_SET_C, KEY_SET_D); final FakeCacheLoaderWriter fakeLoaderWriter = new FakeCacheLoaderWriter(originalWriterContent); this.cacheLoaderWriter = spy(fakeLoaderWriter); doThrow(new Exception("deleteAll failed")).when(this.cacheLoaderWriter).deleteAll(getAnyStringIterable()); final EhcacheWithLoaderWriter<String, String> ehcache = this.getEhcache(this.cacheLoaderWriter); final Set<String> contentUpdates = fanIn(KEY_SET_B, KEY_SET_C, KEY_SET_D); try { ehcache.removeAll(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))); // ResilienceStrategy invoked; no assertions about Store content ordered.verify(this.cacheLoaderWriter, atLeast(1)).deleteAll(getAnyStringIterable()); assertThat(fakeLoaderWriter.getEntryMap(), equalTo(originalWriterContent)); ordered.verify(this.spiedResilienceStrategy) .removeAllFailure(eq(contentUpdates), any(StoreAccessException.class), this.bulkExceptionCaptor.capture()); assertThat(this.bulkExceptionCaptor.getValue().getSuccesses(), empty()); assertThat(this.bulkExceptionCaptor.getValue().getFailures().keySet(), Matchers.<Set<?>>equalTo(contentUpdates)); validateStats(ehcache, EnumSet.noneOf(CacheOperationOutcomes.RemoveOutcome.class)); validateStats(ehcache, EnumSet.of(CacheOperationOutcomes.RemoveAllOutcome.FAILURE)); assertThat(ehcache.getBulkMethodEntries().get(BulkOps.REMOVE_ALL).intValue(), is(0)); } private EhcacheWithLoaderWriter<String, String> getEhcache(final CacheLoaderWriter<String, String> cacheLoaderWriter) { final EhcacheWithLoaderWriter<String, String> ehcache = new EhcacheWithLoaderWriter<String, String>(CACHE_CONFIGURATION, this.store, cacheLoaderWriter, cacheEventDispatcher, LoggerFactory.getLogger(EhcacheWithLoaderWriter.class + "-" + "EhcacheWithLoaderWriterBasicRemoveAllTest")); 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; } }