diff --git a/CHANGES.txt b/CHANGES.txt index bb30b15519..5a4f693998 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -48,6 +48,7 @@ Merged from 4.1: * Internode legacy SSL storage port certificate is not hot reloaded on update (CASSANDRA-18681) * Nodetool paxos-only repair is no longer incremental (CASSANDRA-18466) Merged from 4.0: + * Fix filtering system ks sstables for relocation on startup (CASSANDRA-18963) * Remove completed coordinator sessions (CASSANDRA-18903) * Gossip NPE due to shutdown event corrupting empty statuses (CASSANDRA-18913) * Update hdrhistogram to 2.1.12 (CASSANDRA-18893) diff --git a/src/java/org/apache/cassandra/service/CassandraDaemon.java b/src/java/org/apache/cassandra/service/CassandraDaemon.java index e0bc642a73..8d6758e2a0 100644 --- a/src/java/org/apache/cassandra/service/CassandraDaemon.java +++ b/src/java/org/apache/cassandra/service/CassandraDaemon.java @@ -480,9 +480,7 @@ public class CassandraDaemon try (Stream keyspaceChildren = Files.list(keyspaceDirectory)) { Path[] tableDirectories = keyspaceChildren.filter(Files::isDirectory) - .filter(p -> !SystemKeyspace.TABLES_SPLIT_ACROSS_MULTIPLE_DISKS - .contains(p.getFileName() - .toString())) + .filter(p -> SystemKeyspace.TABLES_SPLIT_ACROSS_MULTIPLE_DISKS.stream().noneMatch(t -> p.getFileName().toString().startsWith(t + '-'))) .toArray(Path[]::new); for (Path tableDirectory : tableDirectories) diff --git a/test/distributed/org/apache/cassandra/distributed/test/SystemKeyspacesDataLocationTest.java b/test/distributed/org/apache/cassandra/distributed/test/SystemKeyspacesDataLocationTest.java new file mode 100644 index 0000000000..5213a8c756 --- /dev/null +++ b/test/distributed/org/apache/cassandra/distributed/test/SystemKeyspacesDataLocationTest.java @@ -0,0 +1,88 @@ +/* + * 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.IOException; +import java.util.Set; +import java.util.concurrent.ExecutionException; +import java.util.stream.Collectors; + +import org.junit.Test; + +import org.apache.cassandra.cql3.QueryHandler; +import org.apache.cassandra.cql3.QueryProcessor; +import org.apache.cassandra.db.ColumnFamilyStore; +import org.apache.cassandra.db.SystemKeyspace; +import org.apache.cassandra.distributed.Cluster; +import org.apache.cassandra.distributed.api.IInvokableInstance; +import org.apache.cassandra.schema.Schema; +import org.apache.cassandra.schema.SchemaConstants; +import org.apache.cassandra.schema.TableMetadata; + +import static com.datastax.driver.core.ParseUtils.doubleQuote; +import static org.assertj.core.api.Assertions.assertThat; + +public class SystemKeyspacesDataLocationTest extends TestBaseImpl +{ + @Test + public void preparedStatementsShouldNotBeMovedOnStartupTest() throws IOException, ExecutionException, InterruptedException + { + try (Cluster cluster = Cluster.build(1).withDataDirCount(3).start()) + { + // to test the behaviour we need have sstables of prepared statements in more than one data directory + cluster.get(1).runOnInstance(() -> { + ColumnFamilyStore preparedStatementsCFS = ColumnFamilyStore.getIfExists(SchemaConstants.SYSTEM_KEYSPACE_NAME, SystemKeyspace.PREPARED_STATEMENTS); + preparedStatementsCFS.disableAutoCompaction(); + createSSTablesForPreparedStatementsTable(preparedStatementsCFS); + }); + + Set preparedStatementsDataLocationsBefore = getPreparedStatementsDataLocations(cluster.get(1)); + assertThat(preparedStatementsDataLocationsBefore).hasSizeGreaterThan(1); + cluster.get(1).shutdown().get(); + + // we expect that for prepared statements, sstables will not be moved to the first data directory on startup + cluster.get(1).startup(); + Set preparedStatementsDataLocationsAfter = getPreparedStatementsDataLocations(cluster.get(1)); + assertThat(preparedStatementsDataLocationsAfter).isEqualTo(preparedStatementsDataLocationsBefore); + } + } + + private static Set getPreparedStatementsDataLocations(IInvokableInstance i) + { + return i.callOnInstance(() -> ColumnFamilyStore.getIfExists(SchemaConstants.SYSTEM_KEYSPACE_NAME, SystemKeyspace.PREPARED_STATEMENTS) + .getLiveSSTables().stream() + // sstables are located at // so we need to go up two directories to get the data location + .map(sstr -> sstr.descriptor.directory.parent().parent().toString()) + .collect(Collectors.toSet())); + } + + private static void createSSTablesForPreparedStatementsTable(ColumnFamilyStore preparedStatementsCFS) + { + for (String keyspaceName : SchemaConstants.LOCAL_SYSTEM_KEYSPACE_NAMES) + { + for (TableMetadata tableMetadata : Schema.instance.getKeyspaceMetadata(keyspaceName).tables) + { + String query = String.format("select * from %s.%s", keyspaceName, doubleQuote(tableMetadata.name)); + QueryHandler.Prepared prepared = QueryProcessor.prepareInternal(query); + QueryProcessor.storePreparedStatement(query, keyspaceName, prepared); + preparedStatementsCFS.forceBlockingFlush(ColumnFamilyStore.FlushReason.UNIT_TESTS); + } + } + } +}