mirror of https://github.com/apache/cassandra
Limit the maximum hints size per host
patch by Yifan Cai; reviewed by Dinesh Joshi, Francisco Guerrero for CASSANDRA-17142
This commit is contained in:
parent
ce7502a110
commit
3a6f690731
3
NEWS.txt
3
NEWS.txt
|
|
@ -38,6 +38,9 @@ using the provided 'sstableupgrade' tool.
|
|||
|
||||
New features
|
||||
------------
|
||||
- New configuration max_hints_size_per_host to limit the size of local hints files per host in megabytes. Setting to
|
||||
non-positive value disables the limit, which is the default behavior. Setting to a positive value to ensure
|
||||
the total size of the hints files per host does not exceed the limit.
|
||||
- Added ability to configure auth caches through corresponding `nodetool` commands.
|
||||
- CDC data flushing now can be configured to be non-blocking with the configuration cdc_block_writes. Setting to true,
|
||||
any writes to the CDC-enabled tables will be blocked when reaching to the limit for CDC data on disk, which is the
|
||||
|
|
|
|||
|
|
@ -86,6 +86,11 @@ hints_flush_period: 10000ms
|
|||
# Maximum size for a single hints file, in megabytes.
|
||||
max_hints_file_size: 128MiB
|
||||
|
||||
# The file size limit to store hints for an unreachable host, in megabytes.
|
||||
# Once the local hints files have reached the limit, no more new hints will be created.
|
||||
# Set a non-positive value will disable the size limit.
|
||||
# max_hints_size_per_host: 0MiB
|
||||
|
||||
# Enable / disable automatic cleanup for the expired and orphaned hints file.
|
||||
# Disable the option in order to preserve those hints on the disk.
|
||||
auto_hints_cleanup_enabled: false
|
||||
|
|
|
|||
|
|
@ -365,6 +365,7 @@ public class Config
|
|||
public SmallestDurationMilliseconds hints_flush_period = new SmallestDurationMilliseconds("10s");
|
||||
@Replaces(oldName = "max_hints_file_size_in_mb", converter = Converters.MEBIBYTES_DATA_STORAGE, deprecated = true)
|
||||
public SmallestDataStorageMebibytes max_hints_file_size = new SmallestDataStorageMebibytes("128MiB");
|
||||
public volatile DataStorageSpec max_hints_size_per_host = new DataStorageSpec("0B"); // 0 means disabled
|
||||
|
||||
public ParameterizedClass hints_compression;
|
||||
public volatile boolean auto_hints_cleanup_enabled = false;
|
||||
|
|
|
|||
|
|
@ -2643,6 +2643,21 @@ public class DatabaseDescriptor
|
|||
return conf.max_hint_window.toMillisecondsAsInt();
|
||||
}
|
||||
|
||||
public static void setMaxHintsSizePerHostInMb(int value)
|
||||
{
|
||||
conf.max_hints_size_per_host = DataStorageSpec.inMebibytes(value);
|
||||
}
|
||||
|
||||
public static int getMaxHintsSizePerHostInMb()
|
||||
{
|
||||
return conf.max_hints_size_per_host.toMebibytesAsInt();
|
||||
}
|
||||
|
||||
public static long getMaxHintsSizePerHost()
|
||||
{
|
||||
return conf.max_hints_size_per_host.toBytes();
|
||||
}
|
||||
|
||||
public static File getHintsDirectory()
|
||||
{
|
||||
return new File(conf.hints_directory);
|
||||
|
|
|
|||
|
|
@ -209,6 +209,16 @@ final class HintsDescriptor
|
|||
return String.format("%s-%s-%s.crc32", hostId, timestamp, version);
|
||||
}
|
||||
|
||||
File file(File hintsDirectory)
|
||||
{
|
||||
return new File(hintsDirectory, fileName());
|
||||
}
|
||||
|
||||
File checksumFile(File hintsDirectory)
|
||||
{
|
||||
return new File(hintsDirectory, checksumFileName());
|
||||
}
|
||||
|
||||
int messagingVersion()
|
||||
{
|
||||
return messagingVersion(version);
|
||||
|
|
|
|||
|
|
@ -276,7 +276,7 @@ final class HintsDispatchExecutor
|
|||
|
||||
private boolean deliver(HintsDescriptor descriptor, InetAddressAndPort address)
|
||||
{
|
||||
File file = new File(hintsDirectory, descriptor.fileName());
|
||||
File file = descriptor.file(hintsDirectory);
|
||||
InputPosition offset = store.getDispatchOffset(descriptor);
|
||||
|
||||
BooleanSupplier shouldAbort = () -> !isAlive.test(address) || isPaused.get();
|
||||
|
|
@ -321,7 +321,7 @@ final class HintsDispatchExecutor
|
|||
// for each hint in the hints file for a node that isn't part of the ring anymore, write RF hints for each replica
|
||||
private void convert(HintsDescriptor descriptor)
|
||||
{
|
||||
File file = new File(hintsDirectory, descriptor.fileName());
|
||||
File file = descriptor.file(hintsDirectory);
|
||||
|
||||
try (HintsReader reader = HintsReader.open(file, rateLimiter))
|
||||
{
|
||||
|
|
|
|||
|
|
@ -246,6 +246,19 @@ public final class HintsService implements HintsServiceMBean
|
|||
HintsServiceDiagnostics.dispatchingResumed(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the total size in bytes of all the hints files associating with the host on disk.
|
||||
* @param hostId, belonging host
|
||||
* @return total file size, in bytes
|
||||
*/
|
||||
public long getTotalHintsSize(UUID hostId)
|
||||
{
|
||||
HintsStore store = catalog.getNullable(hostId);
|
||||
if (store == null)
|
||||
return 0;
|
||||
return store.getTotalFileSize();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gracefully and blockingly shut down the service.
|
||||
*
|
||||
|
|
|
|||
|
|
@ -209,7 +209,7 @@ final class HintsStore
|
|||
|
||||
void delete(HintsDescriptor descriptor)
|
||||
{
|
||||
File hintsFile = new File(hintsDirectory, descriptor.fileName());
|
||||
File hintsFile = descriptor.file(hintsDirectory);
|
||||
if (hintsFile.tryDelete())
|
||||
logger.info("Deleted hint file {}", descriptor.fileName());
|
||||
else if (hintsFile.exists())
|
||||
|
|
@ -218,7 +218,7 @@ final class HintsStore
|
|||
logger.info("Already deleted hint file {}", descriptor.fileName());
|
||||
|
||||
//noinspection ResultOfMethodCallIgnored
|
||||
new File(hintsDirectory, descriptor.checksumFileName()).tryDelete();
|
||||
descriptor.checksumFile(hintsDirectory).tryDelete();
|
||||
}
|
||||
|
||||
boolean hasFiles()
|
||||
|
|
@ -236,6 +236,20 @@ final class HintsStore
|
|||
dispatchPositions.put(descriptor, inputPosition);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @return the total size of all files belonging to the hints store, in bytes.
|
||||
*/
|
||||
long getTotalFileSize()
|
||||
{
|
||||
long total = 0;
|
||||
for (HintsDescriptor descriptor : Iterables.concat(dispatchDequeue, corruptedFiles))
|
||||
{
|
||||
total += descriptor.file(hintsDirectory).length();
|
||||
}
|
||||
return total;
|
||||
}
|
||||
|
||||
void cleanUp(HintsDescriptor descriptor)
|
||||
{
|
||||
dispatchPositions.remove(descriptor);
|
||||
|
|
|
|||
|
|
@ -67,7 +67,7 @@ class HintsWriter implements AutoCloseable
|
|||
@SuppressWarnings("resource") // HintsWriter owns channel
|
||||
static HintsWriter create(File directory, HintsDescriptor descriptor) throws IOException
|
||||
{
|
||||
File file = new File(directory, descriptor.fileName());
|
||||
File file = descriptor.file(directory);
|
||||
|
||||
FileChannel channel = FileChannel.open(file.toPath(), StandardOpenOption.WRITE, StandardOpenOption.CREATE_NEW);
|
||||
int fd = NativeLibrary.getfd(channel);
|
||||
|
|
@ -102,7 +102,7 @@ class HintsWriter implements AutoCloseable
|
|||
|
||||
private void writeChecksum()
|
||||
{
|
||||
File checksumFile = new File(directory, descriptor.checksumFileName());
|
||||
File checksumFile = descriptor.checksumFile(directory);
|
||||
try (OutputStream out = Files.newOutputStream(checksumFile.toPath()))
|
||||
{
|
||||
out.write(Integer.toHexString((int) globalCRC.getValue()).getBytes(StandardCharsets.UTF_8));
|
||||
|
|
|
|||
|
|
@ -2275,16 +2275,40 @@ public class StorageProxy implements StorageProxyMBean
|
|||
DatabaseDescriptor.setMaxHintWindow(ms);
|
||||
}
|
||||
|
||||
public int getMaxHintsSizePerHostInMb()
|
||||
{
|
||||
return DatabaseDescriptor.getMaxHintsSizePerHostInMb();
|
||||
}
|
||||
|
||||
public void setMaxHintsSizePerHostInMb(int value)
|
||||
{
|
||||
DatabaseDescriptor.setMaxHintsSizePerHostInMb(value);
|
||||
}
|
||||
|
||||
public static boolean shouldHint(Replica replica)
|
||||
{
|
||||
return shouldHint(replica, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines whether a hint should be stored or not.
|
||||
* It rejects early if any of the condition is met:
|
||||
* - Hints disabled entirely or for the belonging datacetner of the replica
|
||||
* - The replica is transient or is the self node
|
||||
* - The replica is no longer part of the ring
|
||||
* - The hint window has expired
|
||||
* - The hints have reached to the size limit for the node
|
||||
* Otherwise, it permits.
|
||||
*
|
||||
* @param replica, the replica for the hint
|
||||
* @param tryEnablePersistentWindow, true to consider hint_window_persistent_enabled; otherwise, ignores
|
||||
* @return true to permit or false to reject hint
|
||||
*/
|
||||
public static boolean shouldHint(Replica replica, boolean tryEnablePersistentWindow)
|
||||
{
|
||||
if (!DatabaseDescriptor.hintedHandoffEnabled())
|
||||
return false;
|
||||
if (replica.isTransient() || replica.isSelf())
|
||||
if (!DatabaseDescriptor.hintedHandoffEnabled()
|
||||
|| replica.isTransient()
|
||||
|| replica.isSelf())
|
||||
return false;
|
||||
|
||||
Set<String> disabledDCs = DatabaseDescriptor.hintedHandoffDisabledDCs();
|
||||
|
|
@ -2303,26 +2327,40 @@ public class StorageProxy implements StorageProxyMBean
|
|||
long endpointDowntime = Gossiper.instance.getEndpointDowntime(endpoint);
|
||||
boolean hintWindowExpired = endpointDowntime > maxHintWindow;
|
||||
|
||||
UUID hostIdForEndpoint = StorageService.instance.getHostIdForEndpoint(endpoint);
|
||||
if (hostIdForEndpoint == null)
|
||||
{
|
||||
Tracing.trace("Discarding hint for endpoint not part of ring: {}", endpoint);
|
||||
return false;
|
||||
}
|
||||
|
||||
// if persisting hints window, hintWindowExpired might be updated according to the timestamp of the earliest hint
|
||||
if (tryEnablePersistentWindow && !hintWindowExpired && DatabaseDescriptor.hintWindowPersistentEnabled())
|
||||
{
|
||||
UUID hostIdForEndpoint = StorageService.instance.getHostIdForEndpoint(endpoint);
|
||||
if (hostIdForEndpoint != null)
|
||||
{
|
||||
long earliestHint = HintsService.instance.getEarliestHintForHost(hostIdForEndpoint);
|
||||
hintWindowExpired = Clock.Global.currentTimeMillis() - maxHintWindow > earliestHint;
|
||||
if (hintWindowExpired)
|
||||
Tracing.trace("Not hinting {} for which there is the earliest hint stored at {}", replica, earliestHint);
|
||||
}
|
||||
}
|
||||
else if (hintWindowExpired)
|
||||
{
|
||||
Tracing.trace("Not hinting {} which has been down {} ms", replica, endpointDowntime);
|
||||
long earliestHint = HintsService.instance.getEarliestHintForHost(hostIdForEndpoint);
|
||||
hintWindowExpired = Clock.Global.currentTimeMillis() - maxHintWindow > earliestHint;
|
||||
if (hintWindowExpired)
|
||||
Tracing.trace("Not hinting {} for which there is the earliest hint stored at {}", replica, earliestHint);
|
||||
}
|
||||
|
||||
if (hintWindowExpired)
|
||||
HintsService.instance.metrics.incrPastWindow(replica.endpoint());
|
||||
{
|
||||
HintsService.instance.metrics.incrPastWindow(endpoint);
|
||||
Tracing.trace("Not hinting {} which has been down {} ms", endpoint, endpointDowntime);
|
||||
return false;
|
||||
}
|
||||
|
||||
return !hintWindowExpired;
|
||||
long maxHintsSize = DatabaseDescriptor.getMaxHintsSizePerHost();
|
||||
long actualTotalHintsSize = HintsService.instance.getTotalHintsSize(hostIdForEndpoint);
|
||||
boolean hasHintsReachedMaxSize = maxHintsSize > 0 && actualTotalHintsSize > maxHintsSize;
|
||||
if (hasHintsReachedMaxSize)
|
||||
{
|
||||
Tracing.trace("Not hinting {} which has reached to the max hints size {} bytes on disk. The actual hints size on disk: {}",
|
||||
endpoint, maxHintsSize, actualTotalHintsSize);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -32,6 +32,8 @@ public interface StorageProxyMBean
|
|||
public Set<String> getHintedHandoffDisabledDCs();
|
||||
public int getMaxHintWindow();
|
||||
public void setMaxHintWindow(int ms);
|
||||
public int getMaxHintsSizePerHostInMb();
|
||||
public void setMaxHintsSizePerHostInMb(int value);
|
||||
public int getMaxHintsInProgress();
|
||||
public void setMaxHintsInProgress(int qs);
|
||||
public int getHintsInProgress();
|
||||
|
|
|
|||
|
|
@ -98,8 +98,8 @@ public class HintsWriteThenReadTest
|
|||
|
||||
private static void verifyChecksum(File directory, HintsDescriptor descriptor) throws IOException
|
||||
{
|
||||
File hintsFile = new File(directory, descriptor.fileName());
|
||||
File checksumFile = new File(directory, descriptor.checksumFileName());
|
||||
File hintsFile = descriptor.file(directory);
|
||||
File checksumFile = descriptor.checksumFile(directory);
|
||||
|
||||
assertTrue(checksumFile.exists());
|
||||
|
||||
|
|
@ -114,7 +114,7 @@ public class HintsWriteThenReadTest
|
|||
long baseTimestamp = descriptor.timestamp;
|
||||
int index = 0;
|
||||
|
||||
try (HintsReader reader = HintsReader.open(new File(directory, descriptor.fileName())))
|
||||
try (HintsReader reader = HintsReader.open(descriptor.file(directory)))
|
||||
{
|
||||
for (HintsReader.Page page : reader)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -103,7 +103,7 @@ public abstract class AlteredHints
|
|||
}
|
||||
}
|
||||
|
||||
try (HintsReader reader = HintsReader.open(new File(dir, descriptor.fileName())))
|
||||
try (HintsReader reader = HintsReader.open(descriptor.file(dir)))
|
||||
{
|
||||
Assert.assertTrue(looksLegit(reader.getInput()));
|
||||
List<Hint> deserialized = new ArrayList<>(hintNum);
|
||||
|
|
|
|||
|
|
@ -19,7 +19,6 @@ package org.apache.cassandra.hints;
|
|||
|
||||
import java.io.IOException;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.file.Files;
|
||||
import java.util.*;
|
||||
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
|
|
@ -28,9 +27,12 @@ import org.apache.cassandra.db.Mutation;
|
|||
import org.apache.cassandra.io.util.File;
|
||||
import org.apache.cassandra.schema.KeyspaceParams;
|
||||
import org.apache.cassandra.schema.Schema;
|
||||
import org.apache.cassandra.utils.Clock;
|
||||
import org.apache.cassandra.utils.FBUtilities;
|
||||
import org.junit.BeforeClass;
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.junit.rules.TemporaryFolder;
|
||||
|
||||
import static junit.framework.Assert.*;
|
||||
import static org.apache.cassandra.Util.dk;
|
||||
|
|
@ -54,18 +56,14 @@ public class HintsCatalogTest
|
|||
SchemaLoader.standardCFMD(KEYSPACE, TABLE2));
|
||||
}
|
||||
|
||||
@Rule
|
||||
public TemporaryFolder testFolder = new TemporaryFolder();
|
||||
|
||||
@Test
|
||||
public void loadCompletenessAndOrderTest() throws IOException
|
||||
{
|
||||
File directory = new File(Files.createTempDirectory(null));
|
||||
try
|
||||
{
|
||||
loadCompletenessAndOrderTest(directory);
|
||||
}
|
||||
finally
|
||||
{
|
||||
directory.deleteOnExit();
|
||||
}
|
||||
File directory = new File(testFolder.newFolder());
|
||||
loadCompletenessAndOrderTest(directory);
|
||||
}
|
||||
|
||||
private void loadCompletenessAndOrderTest(File directory) throws IOException
|
||||
|
|
@ -73,10 +71,10 @@ public class HintsCatalogTest
|
|||
UUID hostId1 = UUID.randomUUID();
|
||||
UUID hostId2 = UUID.randomUUID();
|
||||
|
||||
long timestamp1 = System.currentTimeMillis();
|
||||
long timestamp2 = System.currentTimeMillis() + 1;
|
||||
long timestamp3 = System.currentTimeMillis() + 2;
|
||||
long timestamp4 = System.currentTimeMillis() + 3;
|
||||
long timestamp1 = Clock.Global.currentTimeMillis();
|
||||
long timestamp2 = Clock.Global.currentTimeMillis() + 1;
|
||||
long timestamp3 = Clock.Global.currentTimeMillis() + 2;
|
||||
long timestamp4 = Clock.Global.currentTimeMillis() + 3;
|
||||
|
||||
HintsDescriptor descriptor1 = new HintsDescriptor(hostId1, timestamp1);
|
||||
HintsDescriptor descriptor2 = new HintsDescriptor(hostId2, timestamp3);
|
||||
|
|
@ -107,10 +105,10 @@ public class HintsCatalogTest
|
|||
@Test
|
||||
public void deleteHintsTest() throws IOException
|
||||
{
|
||||
File directory = new File(Files.createTempDirectory(null));
|
||||
File directory = new File(testFolder.newFolder());
|
||||
UUID hostId1 = UUID.randomUUID();
|
||||
UUID hostId2 = UUID.randomUUID();
|
||||
long now = System.currentTimeMillis();
|
||||
long now = Clock.Global.currentTimeMillis();
|
||||
writeDescriptor(directory, new HintsDescriptor(hostId1, now));
|
||||
writeDescriptor(directory, new HintsDescriptor(hostId1, now + 1));
|
||||
writeDescriptor(directory, new HintsDescriptor(hostId2, now + 2));
|
||||
|
|
@ -138,14 +136,27 @@ public class HintsCatalogTest
|
|||
@Test
|
||||
public void exciseHintFiles() throws IOException
|
||||
{
|
||||
File directory = new File(Files.createTempDirectory(null));
|
||||
try
|
||||
File directory = new File(testFolder.newFolder());
|
||||
exciseHintFiles(directory);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void hintsTotalSizeTest() throws IOException
|
||||
{
|
||||
File directory = new File(testFolder.newFolder());
|
||||
UUID hostId = UUID.randomUUID();
|
||||
long now = Clock.Global.currentTimeMillis();
|
||||
long totalSize = 0;
|
||||
HintsCatalog catalog = HintsCatalog.load(directory, ImmutableMap.of());
|
||||
HintsStore store = catalog.get(hostId);
|
||||
assertEquals(totalSize, store.getTotalFileSize());
|
||||
for (int i = 0; i < 3; i++)
|
||||
{
|
||||
exciseHintFiles(directory);
|
||||
}
|
||||
finally
|
||||
{
|
||||
directory.deleteOnExit();
|
||||
HintsDescriptor descriptor = new HintsDescriptor(hostId, now + i);
|
||||
writeDescriptor(directory, descriptor);
|
||||
store.offerLast(descriptor);
|
||||
assertTrue("Total file size should increase after writing more hints", store.getTotalFileSize() > totalSize);
|
||||
totalSize = store.getTotalFileSize();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -153,10 +164,10 @@ public class HintsCatalogTest
|
|||
{
|
||||
UUID hostId = UUID.randomUUID();
|
||||
|
||||
HintsDescriptor descriptor1 = new HintsDescriptor(hostId, System.currentTimeMillis());
|
||||
HintsDescriptor descriptor2 = new HintsDescriptor(hostId, System.currentTimeMillis() + 1);
|
||||
HintsDescriptor descriptor3 = new HintsDescriptor(hostId, System.currentTimeMillis() + 2);
|
||||
HintsDescriptor descriptor4 = new HintsDescriptor(hostId, System.currentTimeMillis() + 3);
|
||||
HintsDescriptor descriptor1 = new HintsDescriptor(hostId, Clock.Global.currentTimeMillis());
|
||||
HintsDescriptor descriptor2 = new HintsDescriptor(hostId, Clock.Global.currentTimeMillis() + 1);
|
||||
HintsDescriptor descriptor3 = new HintsDescriptor(hostId, Clock.Global.currentTimeMillis() + 2);
|
||||
HintsDescriptor descriptor4 = new HintsDescriptor(hostId, Clock.Global.currentTimeMillis() + 3);
|
||||
|
||||
createHintFile(directory, descriptor1);
|
||||
createHintFile(directory, descriptor2);
|
||||
|
|
|
|||
|
|
@ -110,7 +110,7 @@ public class HintsDescriptorTest
|
|||
try (HintsWriter ignored = HintsWriter.create(directory, expected))
|
||||
{
|
||||
}
|
||||
HintsDescriptor actual = HintsDescriptor.readFromFile(new File(directory, expected.fileName()));
|
||||
HintsDescriptor actual = HintsDescriptor.readFromFile(expected.file(directory));
|
||||
assertEquals(expected, actual);
|
||||
}
|
||||
finally
|
||||
|
|
|
|||
|
|
@ -108,7 +108,7 @@ public class HintsReaderTest
|
|||
{
|
||||
long baseTimestamp = descriptor.timestamp;
|
||||
int index = 0;
|
||||
try (HintsReader reader = HintsReader.open(new File(directory, descriptor.fileName())))
|
||||
try (HintsReader reader = HintsReader.open(descriptor.file(directory)))
|
||||
{
|
||||
for (HintsReader.Page page : reader)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -20,6 +20,8 @@ package org.apache.cassandra.net;
|
|||
|
||||
import java.util.UUID;
|
||||
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
import org.junit.BeforeClass;
|
||||
import org.junit.Test;
|
||||
|
||||
|
|
@ -32,6 +34,7 @@ import org.apache.cassandra.db.partitions.PartitionUpdate;
|
|||
import org.apache.cassandra.locator.InetAddressAndPort;
|
||||
import org.apache.cassandra.schema.MockSchema;
|
||||
import org.apache.cassandra.schema.TableMetadata;
|
||||
import org.apache.cassandra.service.StorageService;
|
||||
import org.apache.cassandra.service.paxos.Commit;
|
||||
import org.apache.cassandra.utils.ByteBufferUtil;
|
||||
|
||||
|
|
@ -39,12 +42,27 @@ import static org.apache.cassandra.locator.ReplicaUtils.full;
|
|||
|
||||
public class WriteCallbackInfoTest
|
||||
{
|
||||
private InetAddressAndPort testEp;
|
||||
|
||||
@BeforeClass
|
||||
public static void initDD()
|
||||
{
|
||||
DatabaseDescriptor.daemonInitialization();
|
||||
}
|
||||
|
||||
@Before
|
||||
public void setup() throws Exception
|
||||
{
|
||||
testEp = InetAddressAndPort.getByName("192.168.1.1");
|
||||
StorageService.instance.getTokenMetadata().updateHostId(UUID.randomUUID(), testEp);
|
||||
}
|
||||
|
||||
@After
|
||||
public void teardown()
|
||||
{
|
||||
StorageService.instance.getTokenMetadata().removeEndpoint(testEp);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testShouldHint() throws Exception
|
||||
{
|
||||
|
|
@ -57,14 +75,14 @@ public class WriteCallbackInfoTest
|
|||
}
|
||||
}
|
||||
|
||||
private void testShouldHint(Verb verb, ConsistencyLevel cl, boolean allowHints, boolean expectHint) throws Exception
|
||||
private void testShouldHint(Verb verb, ConsistencyLevel cl, boolean allowHints, boolean expectHint)
|
||||
{
|
||||
TableMetadata metadata = MockSchema.newTableMetadata("", "");
|
||||
Object payload = verb == Verb.PAXOS_COMMIT_REQ
|
||||
? new Commit(UUID.randomUUID(), new PartitionUpdate.Builder(metadata, ByteBufferUtil.EMPTY_BYTE_BUFFER, RegularAndStaticColumns.NONE, 1).build())
|
||||
: new Mutation(PartitionUpdate.simpleBuilder(metadata, "").build());
|
||||
|
||||
RequestCallbacks.WriteCallbackInfo wcbi = new RequestCallbacks.WriteCallbackInfo(Message.out(verb, payload), full(InetAddressAndPort.getByName("192.168.1.1")), null, cl, allowHints);
|
||||
RequestCallbacks.WriteCallbackInfo wcbi = new RequestCallbacks.WriteCallbackInfo(Message.out(verb, payload), full(testEp), null, cl, allowHints);
|
||||
Assert.assertEquals(expectHint, wcbi.shouldHint());
|
||||
if (expectHint)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -0,0 +1,121 @@
|
|||
/*
|
||||
* 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.service;
|
||||
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
import com.google.common.util.concurrent.Uninterruptibles;
|
||||
import org.junit.BeforeClass;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
import org.apache.cassandra.ServerTestUtils;
|
||||
import org.apache.cassandra.config.DatabaseDescriptor;
|
||||
import org.apache.cassandra.gms.EndpointState;
|
||||
import org.apache.cassandra.gms.Gossiper;
|
||||
import org.apache.cassandra.gms.HeartBeatState;
|
||||
import org.apache.cassandra.locator.InetAddressAndPort;
|
||||
import org.apache.cassandra.locator.Replica;
|
||||
import org.jboss.byteman.contrib.bmunit.BMRule;
|
||||
import org.jboss.byteman.contrib.bmunit.BMUnitRunner;
|
||||
|
||||
import static org.apache.cassandra.locator.ReplicaUtils.full;
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
@RunWith(BMUnitRunner.class)
|
||||
public class StorageProxyTest
|
||||
{
|
||||
@BeforeClass
|
||||
public static void initDD()
|
||||
{
|
||||
DatabaseDescriptor.daemonInitialization();
|
||||
ServerTestUtils.mkdirs();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testShouldHint() throws Exception
|
||||
{
|
||||
// HAPPY PATH with all defaults
|
||||
shouldHintTest(replica -> {
|
||||
assertThat(StorageProxy.shouldHint(replica)).isTrue();
|
||||
assertThat(StorageProxy.shouldHint(replica, /* tryEnablePersistentWindow */ false)).isTrue();
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testShouldHintOnWindowExpiry() throws Exception
|
||||
{
|
||||
shouldHintTest(replica -> {
|
||||
// wait for 5 ms, we will shorten the hints window later
|
||||
Uninterruptibles.sleepUninterruptibly(5, TimeUnit.MILLISECONDS);
|
||||
|
||||
final int originalHintWindow = DatabaseDescriptor.getMaxHintWindow();
|
||||
try
|
||||
{
|
||||
DatabaseDescriptor.setMaxHintWindow(1); // 1 ms. It should not hint
|
||||
assertThat(StorageProxy.shouldHint(replica)).isFalse();
|
||||
}
|
||||
finally
|
||||
{
|
||||
DatabaseDescriptor.setMaxHintWindow(originalHintWindow);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
@BMRule(name = "Hints size exceeded the limit",
|
||||
targetClass="org.apache.cassandra.hints.HintsService",
|
||||
targetMethod="getTotalHintsSize",
|
||||
action="return 2097152;") // 2MB
|
||||
public void testShouldHintOnExceedingSize() throws Exception
|
||||
{
|
||||
shouldHintTest(replica -> {
|
||||
final int originalHintsSizeLimit = DatabaseDescriptor.getMaxHintsSizePerHostInMb();
|
||||
try
|
||||
{
|
||||
DatabaseDescriptor.setMaxHintsSizePerHostInMb(1);
|
||||
assertThat(StorageProxy.shouldHint(replica)).isFalse();
|
||||
}
|
||||
finally
|
||||
{
|
||||
DatabaseDescriptor.setMaxHintsSizePerHostInMb(originalHintsSizeLimit);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void shouldHintTest(Consumer<Replica> test) throws Exception
|
||||
{
|
||||
InetAddressAndPort testEp = InetAddressAndPort.getByName("192.168.1.1");
|
||||
Replica replica = full(testEp);
|
||||
StorageService.instance.getTokenMetadata().updateHostId(UUID.randomUUID(), testEp);
|
||||
EndpointState state = new EndpointState(new HeartBeatState(0, 0));
|
||||
Gossiper.runInGossipStageBlocking(() -> Gossiper.instance.markDead(replica.endpoint(), state));
|
||||
|
||||
try
|
||||
{
|
||||
test.accept(replica);
|
||||
}
|
||||
finally
|
||||
{
|
||||
StorageService.instance.getTokenMetadata().removeEndpoint(testEp);
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue