| /* |
| * 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.cassandra.distributed.test; |
| |
| import java.io.File; |
| import java.io.IOException; |
| import java.nio.file.Files; |
| import java.util.Iterator; |
| |
| import org.junit.After; |
| import org.junit.AfterClass; |
| import org.junit.Assert; |
| import org.junit.Before; |
| import org.junit.BeforeClass; |
| import org.junit.Test; |
| |
| import org.apache.cassandra.config.DatabaseDescriptor; |
| import org.apache.cassandra.db.ColumnFamilyStore; |
| import org.apache.cassandra.db.Keyspace; |
| import org.apache.cassandra.distributed.Cluster; |
| import org.apache.cassandra.distributed.api.IInvokableInstance; |
| import org.apache.cassandra.io.sstable.Component; |
| import org.apache.cassandra.io.sstable.Descriptor; |
| import org.apache.cassandra.io.sstable.format.SSTableReader; |
| |
| public class MultipleDataDirectoryTest extends TestBaseImpl |
| { |
| private static Cluster CLUSTER; |
| private static IInvokableInstance NODE; |
| |
| @BeforeClass |
| public static void before() throws IOException |
| { |
| CLUSTER = init(Cluster.build().withNodes(1).withDataDirCount(3).start()); |
| NODE = CLUSTER.get(1); |
| CLUSTER.schemaChange(withKeyspace("CREATE TABLE %s.cf (k text, c1 text, c2 text, PRIMARY KEY (k)) WITH compaction = {'class': 'LeveledCompactionStrategy', 'enabled': 'false'}")); |
| Assert.assertEquals(3, NODE.callsOnInstance(() -> DatabaseDescriptor.getAllDataFileLocations().length).call().intValue()); |
| } |
| |
| @AfterClass |
| public static void after() |
| { |
| if (CLUSTER != null) |
| CLUSTER.close(); |
| } |
| |
| @Before |
| public void populateData() |
| { |
| final int rowsPerFile = 500; |
| final int files = 5; |
| for (int k = 0; k < files; k++) |
| { |
| for (int i = k * rowsPerFile; i < k * rowsPerFile + rowsPerFile; ++i) |
| NODE.executeInternal(withKeyspace("INSERT INTO %s.cf (k, c1, c2) VALUES (?, 'value1', 'value2');"), Integer.toString(i)); |
| NODE.nodetool("flush"); |
| } |
| } |
| |
| @After |
| public void cleanupData() |
| { |
| NODE.runOnInstance(() -> { |
| Keyspace.open(KEYSPACE).getColumnFamilyStore("cf").truncateBlocking(); |
| }); |
| } |
| |
| @Test |
| public void testSSTablesAreInCorrectLocation() |
| { |
| NODE.runOnInstance(() -> { |
| ColumnFamilyStore cfs = Keyspace.open(KEYSPACE).getColumnFamilyStore("cf"); |
| Assert.assertFalse("All SSTables should be in the correct location", |
| cfs.hasMisplacedSSTables()); |
| }); |
| } |
| |
| @Test |
| public void testDetectSSTableMisplaced() |
| { |
| setupMisplacedSSTables(); |
| NODE.runOnInstance(() -> { |
| ColumnFamilyStore cfs = Keyspace.open(KEYSPACE).getColumnFamilyStore("cf"); |
| Assert.assertTrue("Some SSTable should be misplaced", |
| cfs.hasMisplacedSSTables()); |
| }); |
| } |
| |
| @Test |
| public void testNodeToolRelocateSSTablesFindNoFilesToMove() |
| { |
| long logStartLoc = NODE.logs().mark(); |
| NODE.nodetoolResult("relocatesstables", KEYSPACE, "cf") |
| .asserts() |
| .success(); |
| String expectedLog = String.format("No sstables to RELOCATE for %s.%s", KEYSPACE, "cf"); |
| Assert.assertEquals("relocatesstables should find no sstables to move", |
| 1, NODE.logs().grep(logStartLoc, expectedLog).getResult().size()); |
| } |
| |
| @Test |
| public void testNodeToolRelocateSSTables() |
| { |
| setupMisplacedSSTables(); |
| long logStartLoc = NODE.logs().mark(); |
| NODE.nodetoolResult("relocatesstables", KEYSPACE, "cf") |
| .asserts() |
| .success(); |
| String expectedLog = String.format("Finished Relocate sstables to correct disk for %s.%s successfully", KEYSPACE, "cf"); |
| Assert.assertEquals("relocatesstables should find sstables to move", |
| 1, NODE.logs().grep(logStartLoc, expectedLog).getResult().size()); |
| NODE.runOnInstance(() -> { |
| ColumnFamilyStore cfs = Keyspace.open(KEYSPACE).getColumnFamilyStore("cf"); |
| Assert.assertFalse("All SSTables should be in the correct location", |
| cfs.hasMisplacedSSTables()); |
| }); |
| } |
| |
| // by moving all sstables from the first data directory to the second. |
| private void setupMisplacedSSTables() |
| { |
| NODE.runOnInstance(() -> { |
| ColumnFamilyStore cfs = Keyspace.open(KEYSPACE).getColumnFamilyStore("cf"); |
| Assert.assertNotEquals(0, cfs.getLiveSSTables().size()); |
| Iterator<SSTableReader> sstables = cfs.getLiveSSTables().iterator(); |
| // finding 2 descriptors that live in different data directory |
| Descriptor first = sstables.next().descriptor; |
| Descriptor second = null; |
| while (sstables.hasNext() && second == null) { |
| second = sstables.next().descriptor; |
| if (first.directory.equals(second.directory)) |
| second = null; |
| } |
| Assert.assertNotNull("There should be SSTables in multiple data directories", second); |
| // getting a new file index in order to move SSTable between directories. |
| second = cfs.newSSTableDescriptor(second.directory); |
| // now we just move all sstables from first to second |
| for (Component component : SSTableReader.componentsFor(first)) |
| { |
| File file = new File(first.filenameFor(component)); |
| if (file.exists()) |
| { |
| try |
| { |
| Files.copy(file.toPath(), new File(second.filenameFor(component)).toPath()); |
| } |
| catch (IOException e) |
| { |
| throw new RuntimeException("Something wrong with copying sstables", e); |
| } |
| } |
| } |
| ColumnFamilyStore.loadNewSSTables(KEYSPACE, "cf"); |
| }); |
| } |
| } |