/* * Copyright 2017 ThoughtWorks, 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 com.thoughtworks.go.server.dao; import java.util.Arrays; import java.util.HashSet; import com.thoughtworks.go.domain.NotificationFilter; import com.thoughtworks.go.domain.StageEvent; import com.thoughtworks.go.domain.User; import com.thoughtworks.go.server.cache.GoCache; import org.hamcrest.Matchers; import org.hibernate.SessionFactory; import org.hibernate.stat.SecondLevelCacheStatistics; import org.junit.After; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; import org.springframework.util.StopWatch; import static org.hamcrest.CoreMatchers.not; import static org.hamcrest.CoreMatchers.nullValue; import static org.hamcrest.core.Is.is; import static org.junit.Assert.assertThat; @RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration(locations = { "classpath:WEB-INF/applicationContext-global.xml", "classpath:WEB-INF/applicationContext-dataLocalAccess.xml", "classpath:WEB-INF/applicationContext-acegi-security.xml" }) public class UserSqlMapDaoCachingTest { @Autowired private UserSqlMapDao userDao; @Autowired private DatabaseAccessHelper dbHelper; @Autowired private GoCache goCache; @Autowired private SessionFactory sessionFactory; @Before public void setup() throws Exception { sessionFactory.getStatistics().clear(); dbHelper.onSetUp(); } @After public void teardown() throws Exception { dbHelper.onTearDown(); sessionFactory.getStatistics().clear(); } @Test public void shouldCacheUserOnFind() { User first = new User("first"); first.addNotificationFilter(new NotificationFilter("pipline", "stage1", StageEvent.Fails, true)); first.addNotificationFilter(new NotificationFilter("pipline", "stage2", StageEvent.Fails, true)); int originalUserCacheSize = sessionFactory.getStatistics().getSecondLevelCacheStatistics(User.class.getCanonicalName()).getEntries().size(); int originalNotificationsCacheSize = sessionFactory.getStatistics().getSecondLevelCacheStatistics(User.class.getCanonicalName() + ".notificationFilters").getEntries().size(); userDao.saveOrUpdate(first); long userId = userDao.findUser("first").getId(); assertThat(sessionFactory.getStatistics().getSecondLevelCacheStatistics(User.class.getCanonicalName()).getEntries().size(), is(originalUserCacheSize + 1)); SecondLevelCacheStatistics notificationFilterCollectionCache = sessionFactory.getStatistics().getSecondLevelCacheStatistics(User.class.getCanonicalName() + ".notificationFilters"); assertThat(notificationFilterCollectionCache.getEntries().size(), is(originalNotificationsCacheSize + 1)); assertThat(notificationFilterCollectionCache.getEntries().get(userId), is(Matchers.notNullValue())); } @Test public void shouldRemoveEnabledUserCountFromCacheWhenAUserIsSaved() throws Exception { makeSureThatCacheIsInitialized(); userDao.saveOrUpdate(new User("some-random-user")); assertThatEnabledUserCacheHasBeenCleared(); } @Test public void shouldRemoveEnabledUserCountFromCacheWhenAUserIsDisabled() throws Exception { userDao.saveOrUpdate(new User("some-random-user")); makeSureThatCacheIsInitialized(); userDao.disableUsers(Arrays.asList("some-random-user")); assertThatEnabledUserCacheHasBeenCleared(); } @Test public void shouldRemoveEnabledUserCountFromCacheWhenAUserIsEnabled() throws Exception { userDao.saveOrUpdate(new User("some-random-user")); makeSureThatCacheIsInitialized(); userDao.enableUsers(Arrays.asList("some-random-user")); assertThatEnabledUserCacheHasBeenCleared(); } @Test public void shouldRemoveEnabledUserCountFromCacheWhenAllUsersAreDeleted() throws Exception { makeSureThatCacheIsInitialized(); userDao.deleteAll(); assertThatEnabledUserCacheHasBeenCleared(); } @Test public void shouldNOTRemoveEnabledUserCountFromCacheWhenFindUserHappens() throws Exception { makeSureThatCacheIsInitialized(); userDao.findUser("some-random-user"); assertThatEnabledUserCacheExists(); } @Test public void shouldNOTRemoveEnabledUserCountFromCacheWhenAllUsersAreLoaded() throws Exception { makeSureThatCacheIsInitialized(); userDao.allUsers(); assertThatEnabledUserCacheExists(); } @Test public void shouldNOTRemoveEnabledUserCountFromCacheWhenEnabledUsersAreLoaded() throws Exception { makeSureThatCacheIsInitialized(); userDao.enabledUsers(); assertThatEnabledUserCacheExists(); } @Test public void shouldNOTRemoveEnabledUserCountFromCacheWhenFindUsernamesForIds() throws Exception { userDao.saveOrUpdate(new User("some-random-user")); User user = userDao.findUser("some-random-user"); HashSet<Long> userIds = new HashSet<>(); userIds.add(user.getId()); makeSureThatCacheIsInitialized(); userDao.findUsernamesForIds(userIds); assertThatEnabledUserCacheExists(); } @Test public void shouldNOTRemoveEnabledUserCountFromCacheWhenUserIsLoaded() throws Exception { userDao.saveOrUpdate(new User("some-random-user")); User user = userDao.findUser("some-random-user"); makeSureThatCacheIsInitialized(); userDao.load(user.getId()); assertThatEnabledUserCacheExists(); } @Test(timeout = 60000) public void enabledUserCacheShouldBeThreadSafe() throws Exception { ThreadSafetyChecker threadSafetyChecker = new ThreadSafetyChecker(10000); threadSafetyChecker.addOperation(new ThreadSafetyChecker.Operation() { @Override public void execute(int runIndex) { StopWatch stopWatch = new StopWatch("enabledUserCount"); stopWatch.start("enabledUserCount"); userDao.enabledUserCount(); stopWatch.stop(); System.out.println(stopWatch.shortSummary()); } }); threadSafetyChecker.addOperation(new ThreadSafetyChecker.Operation() { @Override public void execute(int runIndex) { StopWatch stopWatch = new StopWatch("deleteAll"); stopWatch.start("deleteAll"); userDao.deleteAll(); stopWatch.stop(); System.out.println(stopWatch.shortSummary()); } }); threadSafetyChecker.addOperation(new ThreadSafetyChecker.Operation() { @Override public void execute(int runIndex) { StopWatch stopWatch = new StopWatch("saveOrUpdate"); stopWatch.start("saveOrUpdate"); userDao.saveOrUpdate(new User("some-random-user " + runIndex)); stopWatch.stop(); System.out.println(stopWatch.shortSummary()); } }); threadSafetyChecker.addOperation(new ThreadSafetyChecker.Operation() { @Override public void execute(int runIndex) { StopWatch stopWatch = new StopWatch("enableUsers"); stopWatch.start("enableUsers"); userDao.enableUsers(Arrays.asList("some-random-user " + runIndex)); stopWatch.stop(); System.out.println(stopWatch.shortSummary()); } }); threadSafetyChecker.run(250); } private void assertThatEnabledUserCacheHasBeenCleared() { assertThat(goCache.get(UserSqlMapDao.ENABLED_USER_COUNT_CACHE_KEY), is(nullValue())); } private void assertThatEnabledUserCacheExists() { assertThat(goCache.get(UserSqlMapDao.ENABLED_USER_COUNT_CACHE_KEY), is(not(nullValue()))); } private void makeSureThatCacheIsInitialized() { Integer getCountSoThatCacheIsInitialized = userDao.enabledUserCount(); assertThatEnabledUserCacheExists(); } }