mirror of https://github.com/apache/cassandra
Automatic sstable upgrades
Patch by marcuse; reviewed by Ariel Weisberg for CASSANDRA-14197
This commit is contained in:
parent
6b0247576d
commit
d14a9266c7
|
|
@ -1,4 +1,5 @@
|
|||
4.0
|
||||
* Automatic sstable upgrades (CASSANDRA-14197)
|
||||
* Replace deprecated junit.framework.Assert usages with org.junit.Assert (CASSANDRA-14431)
|
||||
* cassandra-stress throws NPE if insert section isn't specified in user profile (CASSSANDRA-14426)
|
||||
* List clients by protocol versions `nodetool clientstats --by-protocol` (CASSANDRA-14335)
|
||||
|
|
|
|||
3
NEWS.txt
3
NEWS.txt
|
|
@ -38,6 +38,9 @@ using the provided 'sstableupgrade' tool.
|
|||
|
||||
New features
|
||||
------------
|
||||
- There is now an option to automatically upgrade sstables after Cassandra upgrade, enable
|
||||
either in `cassandra.yaml:automatic_sstable_upgrade` or via JMX during runtime. See
|
||||
CASSANDRA-14197.
|
||||
- `nodetool refresh` has been deprecated in favour of `nodetool import` - see CASSANDRA-6719
|
||||
for details
|
||||
- An experimental option to compare all merkle trees together has been added - for example, in
|
||||
|
|
|
|||
|
|
@ -1178,3 +1178,9 @@ back_pressure_strategy:
|
|||
# The full query log will recrusively delete the contents of this path at
|
||||
# times. Don't place links in this directory to other parts of the filesystem.
|
||||
#full_query_log_dir: /tmp/cassandrafullquerylog
|
||||
|
||||
# Automatically upgrade sstables after upgrade - if there is no ordinary compaction to do, the
|
||||
# oldest non-upgraded sstable will get upgraded to the latest version
|
||||
# automatic_sstable_upgrade: false
|
||||
# Limit the number of concurrent sstable upgrades
|
||||
# max_concurrent_automatic_sstable_upgrades: 1
|
||||
|
|
|
|||
|
|
@ -377,6 +377,8 @@ public class Config
|
|||
// parameters to adjust how much to delay startup until a certain amount of the cluster is connect to and marked alive
|
||||
public int block_for_peers_percentage = 70;
|
||||
public int block_for_peers_timeout_in_secs = 10;
|
||||
public volatile boolean automatic_sstable_upgrade = false;
|
||||
public volatile int max_concurrent_automatic_sstable_upgrades = 1;
|
||||
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -748,6 +748,8 @@ public class DatabaseDescriptor
|
|||
|
||||
if (conf.otc_coalescing_enough_coalesced_messages <= 0)
|
||||
throw new ConfigurationException("otc_coalescing_enough_coalesced_messages must be positive", false);
|
||||
|
||||
validateMaxConcurrentAutoUpgradeTasksConf(conf.max_concurrent_automatic_sstable_upgrades);
|
||||
}
|
||||
|
||||
private static String storagedirFor(String type)
|
||||
|
|
@ -2544,4 +2546,37 @@ public class DatabaseDescriptor
|
|||
{
|
||||
return conf.block_for_peers_timeout_in_secs;
|
||||
}
|
||||
|
||||
public static boolean automaticSSTableUpgrade()
|
||||
{
|
||||
return conf.automatic_sstable_upgrade;
|
||||
}
|
||||
|
||||
public static void setAutomaticSSTableUpgradeEnabled(boolean enabled)
|
||||
{
|
||||
if (conf.automatic_sstable_upgrade != enabled)
|
||||
logger.debug("Changing automatic_sstable_upgrade to {}", enabled);
|
||||
conf.automatic_sstable_upgrade = enabled;
|
||||
}
|
||||
|
||||
public static int maxConcurrentAutoUpgradeTasks()
|
||||
{
|
||||
return conf.max_concurrent_automatic_sstable_upgrades;
|
||||
}
|
||||
|
||||
public static void setMaxConcurrentAutoUpgradeTasks(int value)
|
||||
{
|
||||
if (conf.max_concurrent_automatic_sstable_upgrades != value)
|
||||
logger.debug("Changing max_concurrent_automatic_sstable_upgrades to {}", value);
|
||||
validateMaxConcurrentAutoUpgradeTasksConf(value);
|
||||
conf.max_concurrent_automatic_sstable_upgrades = value;
|
||||
}
|
||||
|
||||
private static void validateMaxConcurrentAutoUpgradeTasksConf(int value)
|
||||
{
|
||||
if (value < 0)
|
||||
throw new ConfigurationException("max_concurrent_automatic_sstable_upgrades can't be negative");
|
||||
if (value > getConcurrentCompactors())
|
||||
logger.warn("max_concurrent_automatic_sstable_upgrades ({}) is larger than concurrent_compactors ({})", value, getConcurrentCompactors());
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -22,6 +22,7 @@ import java.io.IOException;
|
|||
import java.lang.management.ManagementFactory;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.*;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import java.util.function.LongPredicate;
|
||||
import java.util.stream.Collectors;
|
||||
import javax.management.MBeanServer;
|
||||
|
|
@ -41,6 +42,7 @@ import org.apache.cassandra.cache.AutoSavingCache;
|
|||
import org.apache.cassandra.concurrent.DebuggableThreadPoolExecutor;
|
||||
import org.apache.cassandra.concurrent.JMXEnabledThreadPoolExecutor;
|
||||
import org.apache.cassandra.concurrent.NamedThreadFactory;
|
||||
import org.apache.cassandra.exceptions.ConfigurationException;
|
||||
import org.apache.cassandra.repair.ValidationPartitionIterator;
|
||||
import org.apache.cassandra.schema.TableMetadata;
|
||||
import org.apache.cassandra.config.DatabaseDescriptor;
|
||||
|
|
@ -94,6 +96,9 @@ public class CompactionManager implements CompactionManagerMBean
|
|||
private static final Logger logger = LoggerFactory.getLogger(CompactionManager.class);
|
||||
public static final CompactionManager instance;
|
||||
|
||||
@VisibleForTesting
|
||||
public final AtomicInteger currentlyBackgroundUpgrading = new AtomicInteger(0);
|
||||
|
||||
public static final int NO_GC = Integer.MIN_VALUE;
|
||||
public static final int GC_ALL = Integer.MAX_VALUE;
|
||||
|
||||
|
|
@ -256,6 +261,7 @@ public class CompactionManager implements CompactionManagerMBean
|
|||
|
||||
// the actual sstables to compact are not determined until we run the BCT; that way, if new sstables
|
||||
// are created between task submission and execution, we execute against the most up-to-date information
|
||||
@VisibleForTesting
|
||||
class BackgroundCompactionCandidate implements Runnable
|
||||
{
|
||||
private final ColumnFamilyStore cfs;
|
||||
|
|
@ -268,6 +274,7 @@ public class CompactionManager implements CompactionManagerMBean
|
|||
|
||||
public void run()
|
||||
{
|
||||
boolean ranCompaction = false;
|
||||
try
|
||||
{
|
||||
logger.trace("Checking {}.{}", cfs.keyspace.getName(), cfs.name);
|
||||
|
|
@ -281,17 +288,51 @@ public class CompactionManager implements CompactionManagerMBean
|
|||
AbstractCompactionTask task = strategy.getNextBackgroundTask(getDefaultGcBefore(cfs, FBUtilities.nowInSeconds()));
|
||||
if (task == null)
|
||||
{
|
||||
logger.trace("No tasks available");
|
||||
return;
|
||||
if (DatabaseDescriptor.automaticSSTableUpgrade())
|
||||
ranCompaction = maybeRunUpgradeTask(strategy);
|
||||
}
|
||||
else
|
||||
{
|
||||
task.execute(metrics);
|
||||
ranCompaction = true;
|
||||
}
|
||||
task.execute(metrics);
|
||||
}
|
||||
finally
|
||||
{
|
||||
compactingCF.remove(cfs);
|
||||
}
|
||||
submitBackground(cfs);
|
||||
if (ranCompaction) // only submit background if we actually ran a compaction - otherwise we end up in an infinite loop submitting noop background tasks
|
||||
submitBackground(cfs);
|
||||
}
|
||||
|
||||
boolean maybeRunUpgradeTask(CompactionStrategyManager strategy)
|
||||
{
|
||||
logger.debug("Checking for upgrade tasks {}.{}", cfs.keyspace.getName(), cfs.getTableName());
|
||||
try
|
||||
{
|
||||
if (currentlyBackgroundUpgrading.incrementAndGet() <= DatabaseDescriptor.maxConcurrentAutoUpgradeTasks())
|
||||
{
|
||||
AbstractCompactionTask upgradeTask = strategy.findUpgradeSSTableTask();
|
||||
if (upgradeTask != null)
|
||||
{
|
||||
upgradeTask.execute(metrics);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
currentlyBackgroundUpgrading.decrementAndGet();
|
||||
}
|
||||
logger.trace("No tasks available");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
public BackgroundCompactionCandidate getBackgroundCompactionCandidate(ColumnFamilyStore cfs)
|
||||
{
|
||||
return new BackgroundCompactionCandidate(cfs);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -1834,6 +1875,33 @@ public class CompactionManager implements CompactionManagerMBean
|
|||
viewBuildExecutor.setMaximumPoolSize(number);
|
||||
}
|
||||
|
||||
public boolean getAutomaticSSTableUpgradeEnabled()
|
||||
{
|
||||
return DatabaseDescriptor.automaticSSTableUpgrade();
|
||||
}
|
||||
|
||||
public void setAutomaticSSTableUpgradeEnabled(boolean enabled)
|
||||
{
|
||||
DatabaseDescriptor.setAutomaticSSTableUpgradeEnabled(enabled);
|
||||
}
|
||||
|
||||
public int getMaxConcurrentAutoUpgradeTasks()
|
||||
{
|
||||
return DatabaseDescriptor.maxConcurrentAutoUpgradeTasks();
|
||||
}
|
||||
|
||||
public void setMaxConcurrentAutoUpgradeTasks(int value)
|
||||
{
|
||||
try
|
||||
{
|
||||
DatabaseDescriptor.setMaxConcurrentAutoUpgradeTasks(value);
|
||||
}
|
||||
catch (ConfigurationException e)
|
||||
{
|
||||
throw new RuntimeException(e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Try to stop all of the compactions for given ColumnFamilies.
|
||||
*
|
||||
|
|
|
|||
|
|
@ -138,4 +138,25 @@ public interface CompactionManagerMBean
|
|||
* @param number New maximum of view build threads
|
||||
*/
|
||||
public void setMaximumViewBuildThreads(int number);
|
||||
|
||||
/**
|
||||
* Get automatic sstable upgrade enabled
|
||||
*/
|
||||
public boolean getAutomaticSSTableUpgradeEnabled();
|
||||
/**
|
||||
* Set if automatic sstable upgrade should be enabled
|
||||
*/
|
||||
public void setAutomaticSSTableUpgradeEnabled(boolean enabled);
|
||||
|
||||
/**
|
||||
* Get the number of concurrent sstable upgrade tasks we should run
|
||||
* when automatic sstable upgrades are enabled
|
||||
*/
|
||||
public int getMaxConcurrentAutoUpgradeTasks();
|
||||
|
||||
/**
|
||||
* Set the number of concurrent sstable upgrade tasks we should run
|
||||
* when automatic sstable upgrades are enabled
|
||||
*/
|
||||
public void setMaxConcurrentAutoUpgradeTasks(int value);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -18,6 +18,7 @@
|
|||
package org.apache.cassandra.db.compaction;
|
||||
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.Callable;
|
||||
|
|
@ -29,7 +30,9 @@ import java.util.stream.Stream;
|
|||
import com.google.common.annotations.VisibleForTesting;
|
||||
import com.google.common.collect.Iterables;
|
||||
import com.google.common.collect.Lists;
|
||||
import com.google.common.primitives.Longs;
|
||||
|
||||
import org.apache.cassandra.config.DatabaseDescriptor;
|
||||
import org.apache.cassandra.db.DiskBoundaries;
|
||||
import org.apache.cassandra.index.Index;
|
||||
|
||||
|
|
@ -38,12 +41,12 @@ import org.slf4j.LoggerFactory;
|
|||
|
||||
import org.apache.cassandra.db.ColumnFamilyStore;
|
||||
import org.apache.cassandra.db.Directories;
|
||||
import org.apache.cassandra.db.Memtable;
|
||||
import org.apache.cassandra.db.SerializationHeader;
|
||||
import org.apache.cassandra.db.lifecycle.LifecycleTransaction;
|
||||
import org.apache.cassandra.db.lifecycle.SSTableSet;
|
||||
import org.apache.cassandra.dht.Range;
|
||||
import org.apache.cassandra.dht.Token;
|
||||
import org.apache.cassandra.io.sstable.Component;
|
||||
import org.apache.cassandra.io.sstable.Descriptor;
|
||||
import org.apache.cassandra.io.sstable.SSTableMultiWriter;
|
||||
import org.apache.cassandra.io.sstable.format.SSTableReader;
|
||||
|
|
@ -202,6 +205,36 @@ public class CompactionStrategyManager implements INotificationConsumer
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* finds the oldest (by modification date) non-latest-version sstable on disk and creates an upgrade task for it
|
||||
* @return
|
||||
*/
|
||||
@VisibleForTesting
|
||||
AbstractCompactionTask findUpgradeSSTableTask()
|
||||
{
|
||||
if (!isEnabled() || !DatabaseDescriptor.automaticSSTableUpgrade())
|
||||
return null;
|
||||
Set<SSTableReader> compacting = cfs.getTracker().getCompacting();
|
||||
List<SSTableReader> potentialUpgrade = cfs.getLiveSSTables()
|
||||
.stream()
|
||||
.filter(s -> !compacting.contains(s) && !s.descriptor.version.isLatestVersion())
|
||||
.sorted((o1, o2) -> {
|
||||
File f1 = new File(o1.descriptor.filenameFor(Component.DATA));
|
||||
File f2 = new File(o2.descriptor.filenameFor(Component.DATA));
|
||||
return Longs.compare(f1.lastModified(), f2.lastModified());
|
||||
}).collect(Collectors.toList());
|
||||
for (SSTableReader sstable : potentialUpgrade)
|
||||
{
|
||||
LifecycleTransaction txn = cfs.getTracker().tryModify(sstable, OperationType.UPGRADE_SSTABLES);
|
||||
if (txn != null)
|
||||
{
|
||||
logger.debug("Running automatic sstable upgrade for {}", sstable);
|
||||
return getCompactionStrategyFor(sstable).getCompactionTask(txn, Integer.MIN_VALUE, Long.MAX_VALUE);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public boolean isEnabled()
|
||||
{
|
||||
return enabled && isActive;
|
||||
|
|
|
|||
|
|
@ -95,6 +95,8 @@ public class TableMetrics
|
|||
public final Gauge<Integer> pendingCompactions;
|
||||
/** Number of SSTables on disk for this CF */
|
||||
public final Gauge<Integer> liveSSTableCount;
|
||||
/** Number of SSTables with old version on disk for this CF */
|
||||
public final Gauge<Integer> oldVersionSSTableCount;
|
||||
/** Disk space used by SSTables belonging to this table */
|
||||
public final Counter liveDiskSpaceUsed;
|
||||
/** Total disk space used by SSTables belonging to this table, including obsolete ones waiting to be GC'd */
|
||||
|
|
@ -541,6 +543,17 @@ public class TableMetrics
|
|||
return cfs.getTracker().getView().liveSSTables().size();
|
||||
}
|
||||
});
|
||||
oldVersionSSTableCount = createTableGauge("OldVersionSSTableCount", new Gauge<Integer>()
|
||||
{
|
||||
public Integer getValue()
|
||||
{
|
||||
int count = 0;
|
||||
for (SSTableReader sstable : cfs.getLiveSSTables())
|
||||
if (!sstable.descriptor.version.isLatestVersion())
|
||||
count++;
|
||||
return count;
|
||||
}
|
||||
});
|
||||
liveDiskSpaceUsed = createTableCounter("LiveDiskSpaceUsed");
|
||||
totalDiskSpaceUsed = createTableCounter("TotalDiskSpaceUsed");
|
||||
minPartitionSize = createTableGauge("MinPartitionSize", "MinRowSize", new Gauge<Long>()
|
||||
|
|
|
|||
|
|
@ -1399,6 +1399,7 @@ public class NodeProbe implements AutoCloseable
|
|||
case "EstimatedPartitionCount":
|
||||
case "KeyCacheHitRate":
|
||||
case "LiveSSTableCount":
|
||||
case "OldVersionSSTableCount":
|
||||
case "MaxPartitionSize":
|
||||
case "MeanPartitionSize":
|
||||
case "MemtableColumnsCount":
|
||||
|
|
|
|||
|
|
@ -29,6 +29,7 @@ public class StatsTable
|
|||
public boolean isIndex;
|
||||
public boolean isLeveledSstable = false;
|
||||
public Object sstableCount;
|
||||
public Object oldSSTableCount;
|
||||
public String spaceUsedLive;
|
||||
public String spaceUsedTotal;
|
||||
public String spaceUsedBySnapshotsTotal;
|
||||
|
|
|
|||
|
|
@ -209,6 +209,7 @@ public class TableStatsHolder implements StatsHolder
|
|||
statsTable.tableName = tableName;
|
||||
statsTable.isIndex = tableName.contains(".");
|
||||
statsTable.sstableCount = probe.getColumnFamilyMetric(keyspaceName, tableName, "LiveSSTableCount");
|
||||
statsTable.oldSSTableCount = probe.getColumnFamilyMetric(keyspaceName, tableName, "OldVersionSSTableCount");
|
||||
int[] leveledSStables = table.getSSTableCountPerLevel();
|
||||
if (leveledSStables != null)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -75,6 +75,7 @@ public class TableStatsPrinter
|
|||
{
|
||||
out.println(indent + "Table" + (table.isIndex ? " (index): " + table.tableName : ": ") + tableDisplayName);
|
||||
out.println(indent + "SSTable count: " + table.sstableCount);
|
||||
out.println(indent + "Old SSTable count: " + table.oldSSTableCount);
|
||||
if (table.isLeveledSstable)
|
||||
out.println(indent + "SSTables in each level: [" + String.join(", ",
|
||||
table.sstablesInEachLevel) + "]");
|
||||
|
|
|
|||
|
|
@ -24,6 +24,8 @@ import java.util.Arrays;
|
|||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import com.google.common.collect.Sets;
|
||||
|
|
@ -76,6 +78,10 @@ public class CompactionStrategyManagerTest
|
|||
* disk assignment based on its generation - See {@link this#getSSTableIndex(Integer[], SSTableReader)}
|
||||
*/
|
||||
originalPartitioner = StorageService.instance.setPartitionerUnsafe(ByteOrderedPartitioner.instance);
|
||||
SchemaLoader.createKeyspace(KS_PREFIX,
|
||||
KeyspaceParams.simple(1),
|
||||
SchemaLoader.standardCFMD(KS_PREFIX, TABLE_PREFIX)
|
||||
.compaction(CompactionParams.scts(Collections.emptyMap())));
|
||||
}
|
||||
|
||||
@AfterClass
|
||||
|
|
@ -90,10 +96,6 @@ public class CompactionStrategyManagerTest
|
|||
{
|
||||
// Creates 100 SSTables with keys 0-99
|
||||
int numSSTables = 100;
|
||||
SchemaLoader.createKeyspace(KS_PREFIX,
|
||||
KeyspaceParams.simple(1),
|
||||
SchemaLoader.standardCFMD(KS_PREFIX, TABLE_PREFIX)
|
||||
.compaction(CompactionParams.scts(Collections.emptyMap())));
|
||||
ColumnFamilyStore cfs = Keyspace.open(KS_PREFIX).getColumnFamilyStore(TABLE_PREFIX);
|
||||
cfs.disableAutoCompaction();
|
||||
Set<SSTableReader> previousSSTables = cfs.getLiveSSTables();
|
||||
|
|
@ -177,6 +179,81 @@ public class CompactionStrategyManagerTest
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@Test
|
||||
public void testAutomaticUpgradeConcurrency() throws Exception
|
||||
{
|
||||
ColumnFamilyStore cfs = Keyspace.open(KS_PREFIX).getColumnFamilyStore(TABLE_PREFIX);
|
||||
DatabaseDescriptor.setAutomaticSSTableUpgradeEnabled(true);
|
||||
DatabaseDescriptor.setMaxConcurrentAutoUpgradeTasks(1);
|
||||
|
||||
// latch to block CompactionManager.BackgroundCompactionCandidate#maybeRunUpgradeTask
|
||||
// inside the currentlyBackgroundUpgrading check - with max_concurrent_auto_upgrade_tasks = 1 this will make
|
||||
// sure that BackgroundCompactionCandidate#maybeRunUpgradeTask returns false until the latch has been counted down
|
||||
CountDownLatch latch = new CountDownLatch(1);
|
||||
AtomicInteger upgradeTaskCount = new AtomicInteger(0);
|
||||
MockCFSForCSM mock = new MockCFSForCSM(cfs, latch, upgradeTaskCount);
|
||||
|
||||
CompactionManager.BackgroundCompactionCandidate r = CompactionManager.instance.getBackgroundCompactionCandidate(mock);
|
||||
CompactionStrategyManager mgr = mock.getCompactionStrategyManager();
|
||||
// basic idea is that we start a thread which will be able to get in to the currentlyBackgroundUpgrading-guarded
|
||||
// code in CompactionManager, then we try to run a bunch more of the upgrade tasks which should return false
|
||||
// due to the currentlyBackgroundUpgrading count being >= max_concurrent_auto_upgrade_tasks
|
||||
Thread t = new Thread(() -> r.maybeRunUpgradeTask(mgr));
|
||||
t.start();
|
||||
Thread.sleep(100); // let the thread start and grab the task
|
||||
assertEquals(1, CompactionManager.instance.currentlyBackgroundUpgrading.get());
|
||||
assertFalse(r.maybeRunUpgradeTask(mgr));
|
||||
assertFalse(r.maybeRunUpgradeTask(mgr));
|
||||
latch.countDown();
|
||||
t.join();
|
||||
assertEquals(1, upgradeTaskCount.get()); // we should only call findUpgradeSSTableTask once when concurrency = 1
|
||||
assertEquals(0, CompactionManager.instance.currentlyBackgroundUpgrading.get());
|
||||
|
||||
DatabaseDescriptor.setAutomaticSSTableUpgradeEnabled(false);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAutomaticUpgradeConcurrency2() throws Exception
|
||||
{
|
||||
ColumnFamilyStore cfs = Keyspace.open(KS_PREFIX).getColumnFamilyStore(TABLE_PREFIX);
|
||||
DatabaseDescriptor.setAutomaticSSTableUpgradeEnabled(true);
|
||||
DatabaseDescriptor.setMaxConcurrentAutoUpgradeTasks(2);
|
||||
// latch to block CompactionManager.BackgroundCompactionCandidate#maybeRunUpgradeTask
|
||||
// inside the currentlyBackgroundUpgrading check - with max_concurrent_auto_upgrade_tasks = 1 this will make
|
||||
// sure that BackgroundCompactionCandidate#maybeRunUpgradeTask returns false until the latch has been counted down
|
||||
CountDownLatch latch = new CountDownLatch(1);
|
||||
AtomicInteger upgradeTaskCount = new AtomicInteger();
|
||||
MockCFSForCSM mock = new MockCFSForCSM(cfs, latch, upgradeTaskCount);
|
||||
|
||||
CompactionManager.BackgroundCompactionCandidate r = CompactionManager.instance.getBackgroundCompactionCandidate(mock);
|
||||
CompactionStrategyManager mgr = mock.getCompactionStrategyManager();
|
||||
|
||||
// basic idea is that we start 2 threads who will be able to get in to the currentlyBackgroundUpgrading-guarded
|
||||
// code in CompactionManager, then we try to run a bunch more of the upgrade task which should return false
|
||||
// due to the currentlyBackgroundUpgrading count being >= max_concurrent_auto_upgrade_tasks
|
||||
Thread t = new Thread(() -> r.maybeRunUpgradeTask(mgr));
|
||||
t.start();
|
||||
Thread t2 = new Thread(() -> r.maybeRunUpgradeTask(mgr));
|
||||
t2.start();
|
||||
Thread.sleep(100); // let the threads start and grab the task
|
||||
assertEquals(2, CompactionManager.instance.currentlyBackgroundUpgrading.get());
|
||||
assertFalse(r.maybeRunUpgradeTask(mgr));
|
||||
assertFalse(r.maybeRunUpgradeTask(mgr));
|
||||
assertFalse(r.maybeRunUpgradeTask(mgr));
|
||||
assertEquals(2, CompactionManager.instance.currentlyBackgroundUpgrading.get());
|
||||
latch.countDown();
|
||||
t.join();
|
||||
t2.join();
|
||||
assertEquals(2, upgradeTaskCount.get());
|
||||
assertEquals(0, CompactionManager.instance.currentlyBackgroundUpgrading.get());
|
||||
|
||||
DatabaseDescriptor.setMaxConcurrentAutoUpgradeTasks(1);
|
||||
DatabaseDescriptor.setAutomaticSSTableUpgradeEnabled(false);
|
||||
}
|
||||
|
||||
|
||||
private MockCFS createJBODMockCFS(int disks)
|
||||
{
|
||||
// Create #disks data directories to simulate JBOD
|
||||
|
|
@ -306,4 +383,50 @@ public class CompactionStrategyManagerTest
|
|||
super(cfs.keyspace, cfs.getTableName(), 0, cfs.metadata, dirs, false, false, true);
|
||||
}
|
||||
}
|
||||
|
||||
private static class MockCFSForCSM extends ColumnFamilyStore
|
||||
{
|
||||
private final CountDownLatch latch;
|
||||
private final AtomicInteger upgradeTaskCount;
|
||||
|
||||
private MockCFSForCSM(ColumnFamilyStore cfs, CountDownLatch latch, AtomicInteger upgradeTaskCount)
|
||||
{
|
||||
super(cfs.keyspace, cfs.name, 10, cfs.metadata, cfs.getDirectories(), true, false, false);
|
||||
this.latch = latch;
|
||||
this.upgradeTaskCount = upgradeTaskCount;
|
||||
}
|
||||
@Override
|
||||
public CompactionStrategyManager getCompactionStrategyManager()
|
||||
{
|
||||
return new MockCSM(this, latch, upgradeTaskCount);
|
||||
}
|
||||
}
|
||||
|
||||
private static class MockCSM extends CompactionStrategyManager
|
||||
{
|
||||
private final CountDownLatch latch;
|
||||
private final AtomicInteger upgradeTaskCount;
|
||||
|
||||
private MockCSM(ColumnFamilyStore cfs, CountDownLatch latch, AtomicInteger upgradeTaskCount)
|
||||
{
|
||||
super(cfs);
|
||||
this.latch = latch;
|
||||
this.upgradeTaskCount = upgradeTaskCount;
|
||||
}
|
||||
|
||||
@Override
|
||||
public AbstractCompactionTask findUpgradeSSTableTask()
|
||||
{
|
||||
try
|
||||
{
|
||||
latch.await();
|
||||
upgradeTaskCount.incrementAndGet();
|
||||
}
|
||||
catch (InterruptedException e)
|
||||
{
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -42,6 +42,8 @@ import org.apache.cassandra.cql3.QueryProcessor;
|
|||
import org.apache.cassandra.cql3.UntypedResultSet;
|
||||
import org.apache.cassandra.db.ColumnFamilyStore;
|
||||
import org.apache.cassandra.db.Keyspace;
|
||||
import org.apache.cassandra.db.compaction.AbstractCompactionTask;
|
||||
import org.apache.cassandra.db.compaction.CompactionManager;
|
||||
import org.apache.cassandra.db.compaction.Verifier;
|
||||
import org.apache.cassandra.db.streaming.CassandraOutgoingFile;
|
||||
import org.apache.cassandra.dht.IPartitioner;
|
||||
|
|
@ -61,6 +63,8 @@ import org.apache.cassandra.streaming.StreamOperation;
|
|||
import org.apache.cassandra.utils.ByteBufferUtil;
|
||||
import org.apache.cassandra.utils.FBUtilities;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.junit.Assert.fail;
|
||||
|
||||
/**
|
||||
|
|
@ -234,6 +238,39 @@ public class LegacySSTableTest
|
|||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAutomaticUpgrade() throws Exception
|
||||
{
|
||||
for (String legacyVersion : legacyVersions)
|
||||
{
|
||||
logger.info("Loading legacy version: {}", legacyVersion);
|
||||
truncateLegacyTables(legacyVersion);
|
||||
loadLegacyTables(legacyVersion);
|
||||
ColumnFamilyStore cfs = Keyspace.open("legacy_tables").getColumnFamilyStore(String.format("legacy_%s_simple", legacyVersion));
|
||||
AbstractCompactionTask act = cfs.getCompactionStrategyManager().getNextBackgroundTask(0);
|
||||
// there should be no compactions to run with auto upgrades disabled:
|
||||
assertEquals(null, act);
|
||||
}
|
||||
|
||||
DatabaseDescriptor.setAutomaticSSTableUpgradeEnabled(true);
|
||||
for (String legacyVersion : legacyVersions)
|
||||
{
|
||||
logger.info("Loading legacy version: {}", legacyVersion);
|
||||
truncateLegacyTables(legacyVersion);
|
||||
loadLegacyTables(legacyVersion);
|
||||
ColumnFamilyStore cfs = Keyspace.open("legacy_tables").getColumnFamilyStore(String.format("legacy_%s_simple", legacyVersion));
|
||||
if (cfs.getLiveSSTables().stream().anyMatch(s -> !s.descriptor.version.isLatestVersion()))
|
||||
assertTrue(cfs.metric.oldVersionSSTableCount.getValue() > 0);
|
||||
while (cfs.getLiveSSTables().stream().anyMatch(s -> !s.descriptor.version.isLatestVersion()))
|
||||
{
|
||||
CompactionManager.instance.submitBackground(cfs);
|
||||
Thread.sleep(100);
|
||||
}
|
||||
assertTrue(cfs.metric.oldVersionSSTableCount.getValue() == 0);
|
||||
}
|
||||
DatabaseDescriptor.setAutomaticSSTableUpgradeEnabled(false);
|
||||
}
|
||||
|
||||
private void streamLegacyTables(String legacyVersion) throws Exception
|
||||
{
|
||||
logger.info("Streaming legacy version {}", legacyVersion);
|
||||
|
|
|
|||
|
|
@ -35,6 +35,7 @@ public class TableStatsPrinterTest extends TableStatsTestBase
|
|||
public static final String expectedDefaultTable1Output =
|
||||
"\tTable: %s\n" +
|
||||
"\tSSTable count: 60000\n" +
|
||||
"\tOld SSTable count: 0\n" +
|
||||
"\tSpace used (live): 0\n" +
|
||||
"\tSpace used (total): 9001\n" +
|
||||
"\tSpace used by snapshots (total): 1111\n" +
|
||||
|
|
@ -65,6 +66,7 @@ public class TableStatsPrinterTest extends TableStatsTestBase
|
|||
public static final String expectedDefaultTable2Output =
|
||||
"\tTable: %s\n" +
|
||||
"\tSSTable count: 3000\n" +
|
||||
"\tOld SSTable count: 0\n" +
|
||||
"\tSpace used (live): 22\n" +
|
||||
"\tSpace used (total): 1024\n" +
|
||||
"\tSpace used by snapshots (total): 222\n" +
|
||||
|
|
@ -100,6 +102,7 @@ public class TableStatsPrinterTest extends TableStatsTestBase
|
|||
public static final String expectedDefaultTable3Output =
|
||||
"\tTable: %s\n" +
|
||||
"\tSSTable count: 50000\n" +
|
||||
"\tOld SSTable count: 0\n" +
|
||||
"\tSpace used (live): 0\n" +
|
||||
"\tSpace used (total): 512\n" +
|
||||
"\tSpace used by snapshots (total): 0\n" +
|
||||
|
|
@ -130,6 +133,7 @@ public class TableStatsPrinterTest extends TableStatsTestBase
|
|||
public static final String expectedDefaultTable4Output =
|
||||
"\tTable: %s\n" +
|
||||
"\tSSTable count: 2000\n" +
|
||||
"\tOld SSTable count: 0\n" +
|
||||
"\tSpace used (live): 4444\n" +
|
||||
"\tSpace used (total): 256\n" +
|
||||
"\tSpace used by snapshots (total): 44\n" +
|
||||
|
|
@ -165,6 +169,7 @@ public class TableStatsPrinterTest extends TableStatsTestBase
|
|||
public static final String expectedDefaultTable5Output =
|
||||
"\tTable: %s\n" +
|
||||
"\tSSTable count: 40000\n" +
|
||||
"\tOld SSTable count: 0\n" +
|
||||
"\tSpace used (live): 55555\n" +
|
||||
"\tSpace used (total): 64\n" +
|
||||
"\tSpace used by snapshots (total): 55555\n" +
|
||||
|
|
@ -195,6 +200,7 @@ public class TableStatsPrinterTest extends TableStatsTestBase
|
|||
public static final String expectedDefaultTable6Output =
|
||||
"\tTable: %s\n" +
|
||||
"\tSSTable count: 1000\n" +
|
||||
"\tOld SSTable count: 0\n" +
|
||||
"\tSpace used (live): 666666\n" +
|
||||
"\tSpace used (total): 0\n" +
|
||||
"\tSpace used by snapshots (total): 0\n" +
|
||||
|
|
|
|||
|
|
@ -70,6 +70,7 @@ public class TableStatsTestBase
|
|||
template.tableName = new String(tableName);
|
||||
template.isIndex = false;
|
||||
template.sstableCount = 0L;
|
||||
template.oldSSTableCount = 0L;
|
||||
template.spaceUsedLive = "0";
|
||||
template.spaceUsedTotal = "0";
|
||||
template.spaceUsedBySnapshotsTotal = "0";
|
||||
|
|
|
|||
Loading…
Reference in New Issue