blob: 7272ab3f7d35e0919673ca2229cb7b2565ff2cdf [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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.apache.ignite.internal.processors.cache.persistence;
import java.io.BufferedOutputStream;
import java.io.DataOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.FilenameFilter;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.List;
import org.apache.ignite.Ignite;
import org.apache.ignite.IgniteCheckedException;
import org.apache.ignite.cache.affinity.rendezvous.RendezvousAffinityFunction;
import org.apache.ignite.configuration.CacheConfiguration;
import org.apache.ignite.configuration.DataRegionConfiguration;
import org.apache.ignite.configuration.DataStorageConfiguration;
import org.apache.ignite.configuration.IgniteConfiguration;
import org.apache.ignite.internal.IgniteEx;
import org.apache.ignite.internal.processors.cache.DynamicCacheDescriptor;
import org.apache.ignite.internal.processors.cache.GridCacheSharedContext;
import org.apache.ignite.internal.processors.cache.StoredCacheData;
import org.apache.ignite.internal.processors.cache.persistence.file.FilePageStoreManager;
import org.apache.ignite.marshaller.Marshaller;
import org.apache.ignite.marshaller.jdk.JdkMarshaller;
import org.apache.ignite.testframework.GridTestUtils;
import org.apache.ignite.testframework.junits.common.GridCommonAbstractTest;
import org.junit.Test;
import static org.apache.ignite.internal.processors.cache.persistence.file.FilePageStoreManager.CACHE_DATA_FILENAME;
import static org.apache.ignite.internal.processors.cache.persistence.file.FilePageStoreManager.CACHE_DATA_TMP_FILENAME;
/**
* Tests that ignite can start when caches' configurations with same name in different groups stored.
*/
public class IgnitePdsCacheConfigurationFileConsistencyCheckTest extends GridCommonAbstractTest {
/** */
private static final int CACHES = 4;
/** */
private static final int NODES = 4;
/** */
private static final String ODD_GROUP_NAME = "group-odd";
/** */
private static final String EVEN_GROUP_NAME = "group-even";
/** {@inheritDoc} */
@Override protected IgniteConfiguration getConfiguration(String igniteInstanceName) throws Exception {
IgniteConfiguration cfg = super.getConfiguration(igniteInstanceName);
return cfg.setDataStorageConfiguration(new DataStorageConfiguration()
.setDefaultDataRegionConfiguration(new DataRegionConfiguration()
.setMaxSize(200 * 1024 * 1024)
.setPersistenceEnabled(true)));
}
/** {@inheritDoc} */
@Override protected void beforeTest() throws Exception {
super.beforeTest();
stopAllGrids();
cleanPersistenceDir();
}
/** {@inheritDoc} */
@Override protected void afterTest() throws Exception {
stopAllGrids();
cleanPersistenceDir();
super.afterTest();
}
/**
* Tests that ignite can start when caches' configurations with same name in different groups stored.
*
* @throws Exception If fails.
*/
@Test
public void testStartDuplicatedCacheConfigurations() throws Exception {
IgniteEx ig0 = (IgniteEx)startGrids(NODES);
ig0.cluster().active(true);
startCaches(ig0);
DynamicCacheDescriptor desc = ig0.context().cache().cacheDescriptor(cacheName(3));
storeInvalidCacheData(desc);
stopAllGrids();
startGrids(NODES);
desc = ig0.context().cache().cacheDescriptor(cacheName(3));
assertEquals("expected that group of " + cacheName(3) + " is " + EVEN_GROUP_NAME, EVEN_GROUP_NAME,
desc.groupDescriptor().groupName());
}
/**
* Check that cache_data.dat.tmp files are deleted after node restarts.
*
* @throws Exception If failed.
*/
@Test
public void testTmpCacheConfigurationsDelete() throws Exception {
IgniteEx ig0 = (IgniteEx)startGrids(NODES);
ig0.cluster().active(true);
startCaches(ig0);
DynamicCacheDescriptor desc = ig0.context().cache().cacheDescriptor(cacheName(3));
storeTmpCacheData(desc);
stopAllGrids();
startGrids(NODES);
for (int i = 0; i < NODES; i++) {
IgniteEx ig = grid(i);
GridCacheSharedContext sharedCtx = ig.context().cache().context();
FilePageStoreManager pageStore = (FilePageStoreManager) sharedCtx.pageStore();
File[] tmpFile = pageStore.cacheWorkDir(true, ODD_GROUP_NAME).listFiles(new FilenameFilter() {
@Override public boolean accept(File dir, String name) {
return name.endsWith(CACHE_DATA_TMP_FILENAME);
}
});
assertNotNull(tmpFile);
assertEquals(0, tmpFile.length);
}
}
/**
* Check that exception contains proper filename when trying to read corrupted cache configuration file.
*
* @throws Exception If failed.
*/
@Test
public void testCorruptedCacheConfigurationsValidation() throws Exception {
IgniteEx ig0 = (IgniteEx)startGrids(NODES);
ig0.cluster().active(true);
startCaches(ig0);
DynamicCacheDescriptor desc = ig0.context().cache().cacheDescriptor(cacheName(2));
corruptCacheData(desc);
stopAllGrids();
GridTestUtils.assertThrowsAnyCause(log, () -> startGrids(NODES), IgniteCheckedException.class,
desc.cacheName() + CACHE_DATA_FILENAME);
}
/**
* Store cache descriptor to PDS with invalid group name.
*
* @param cacheDescr Cache descr.
* @throws IgniteCheckedException If fails.
*/
private void storeInvalidCacheData(DynamicCacheDescriptor cacheDescr) throws IgniteCheckedException {
for (int i = 0; i < NODES; i++) {
IgniteEx ig = grid(i);
GridCacheSharedContext sharedCtx = ig.context().cache().context();
FilePageStoreManager pageStore = (FilePageStoreManager) sharedCtx.pageStore();
StoredCacheData corrData = cacheDescr.toStoredData(ig.context().cache().splitter());
corrData.config().setGroupName(ODD_GROUP_NAME);
pageStore.storeCacheData(corrData, true);
}
}
/**
* Store temp cache descriptor to PDS.
*
* @param cacheDescr Cache descr.
* @throws IgniteCheckedException If fails.
*/
private void storeTmpCacheData(DynamicCacheDescriptor cacheDescr) throws Exception {
Marshaller marshaller = new JdkMarshaller();
for (int i = 0; i < NODES; i++) {
IgniteEx ig = grid(i);
GridCacheSharedContext sharedCtx = ig.context().cache().context();
FilePageStoreManager pageStore = (FilePageStoreManager) sharedCtx.pageStore();
StoredCacheData data = cacheDescr.toStoredData(ig.context().cache().splitter());
data.config().setGroupName(ODD_GROUP_NAME);
File tmp = new File(pageStore.cacheWorkDir(true, ODD_GROUP_NAME), data.config().getName() + CACHE_DATA_TMP_FILENAME);
try (OutputStream stream = new BufferedOutputStream(new FileOutputStream(tmp))) {
marshaller.marshal(data, stream);
}
}
}
/**
* Store temp cache descriptor to PDS.
*
* @param cacheDescr Cache descr.
* @throws IgniteCheckedException If fails.
*/
private void corruptCacheData(DynamicCacheDescriptor cacheDescr) throws Exception {
for (int i = 0; i < NODES; i++) {
IgniteEx ig = grid(i);
GridCacheSharedContext sharedCtx = ig.context().cache().context();
FilePageStoreManager pageStore = (FilePageStoreManager) sharedCtx.pageStore();
StoredCacheData data = cacheDescr.toStoredData(ig.context().cache().splitter());
data.config().setGroupName(ODD_GROUP_NAME);
File config = new File(pageStore.cacheWorkDir(true, ODD_GROUP_NAME), data.config().getName() + CACHE_DATA_FILENAME);
try (DataOutputStream os = new DataOutputStream(new FileOutputStream(config))) {
os.writeLong(-1L);
}
}
}
/**
* @param ignite Ignite instance.
*/
private void startCaches(Ignite ignite) {
List<CacheConfiguration> ccfg = new ArrayList<>(CACHES);
for (int i = 0; i < CACHES; i++) {
ccfg.add(new CacheConfiguration<>(cacheName(i))
.setGroupName(i % 2 == 0 ? ODD_GROUP_NAME : EVEN_GROUP_NAME)
.setBackups(1)
.setAffinity(new RendezvousAffinityFunction(false, 32)));
}
ignite.createCaches(ccfg);
}
/**
* Generate cache name from idx.
*
* @param idx Index.
*/
private String cacheName(int idx) {
return "cache" + idx;
}
}