This is an automated email from the ASF dual-hosted git repository. marcuse pushed a commit to branch cassandra-3.0 in repository https://gitbox.apache.org/repos/asf/cassandra.git
The following commit(s) were added to refs/heads/cassandra-3.0 by this push: new f02e535 Improve empty/corrupt hint file handling on startup f02e535 is described below commit f02e53568dbc193b7ac75cc19b0a7751d5514b95 Author: Marcus Eriksson <marc...@apache.org> AuthorDate: Fri Oct 2 08:23:05 2020 +0200 Improve empty/corrupt hint file handling on startup Patch by marcuse; reviewed by Benjamin Lerer and Yifan Cai for CASSANDRA-16162 --- CHANGES.txt | 1 + .../apache/cassandra/hints/HintsDescriptor.java | 28 ++++++++++++++++- .../cassandra/hints/HintsDescriptorTest.java | 35 ++++++++++++++++++---- 3 files changed, 58 insertions(+), 6 deletions(-) diff --git a/CHANGES.txt b/CHANGES.txt index 5cb4f1d..ee4cf6e 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -1,4 +1,5 @@ 3.0.24: + * Improve empty hint file handling during startup (CASSANDRA-16162) * Allow empty string in collections with COPY FROM in cqlsh (CASSANDRA-16372) * Fix skipping on pre-3.0 created compact storage sstables due to missing primary key liveness (CASSANDRA-16226) * Fix DecimalDeserializer#toString OOM (CASSANDRA-14925) diff --git a/src/java/org/apache/cassandra/hints/HintsDescriptor.java b/src/java/org/apache/cassandra/hints/HintsDescriptor.java index e9e1c30..d1f1116 100644 --- a/src/java/org/apache/cassandra/hints/HintsDescriptor.java +++ b/src/java/org/apache/cassandra/hints/HintsDescriptor.java @@ -21,6 +21,7 @@ import java.io.DataInput; import java.io.IOException; import java.io.RandomAccessFile; import java.nio.charset.StandardCharsets; +import java.nio.file.Files; import java.nio.file.Path; import java.util.Map; import java.util.Optional; @@ -28,6 +29,7 @@ import java.util.UUID; import java.util.regex.Pattern; import java.util.zip.CRC32; +import com.google.common.annotations.VisibleForTesting; import com.google.common.base.MoreObjects; import com.google.common.base.Objects; import com.google.common.collect.ImmutableMap; @@ -148,11 +150,35 @@ final class HintsDescriptor } catch (IOException e) { - logger.error("Failed to deserialize hints descriptor {}", path.toString(), e); + handleDescriptorIOE(e, path); return Optional.empty(); } } + @VisibleForTesting + static void handleDescriptorIOE(IOException e, Path path) + { + try + { + if (Files.size(path) > 0) + { + String newFileName = path.getFileName().toString().replace(".hints", ".corrupt.hints"); + Path target = path.getParent().resolve(newFileName); + logger.error("Failed to deserialize hints descriptor {} - saving file as {}", path.toString(), target, e); + Files.move(path, target); + } + else + { + logger.warn("Found empty hints file {} on startup, removing", path.toString()); + Files.delete(path); + } + } + catch (IOException ex) + { + logger.error("Error handling corrupt hints file {}", path.toString(), ex); + } + } + static HintsDescriptor readFromFile(Path path) { try (RandomAccessFile raf = new RandomAccessFile(path.toFile(), "r")) diff --git a/test/unit/org/apache/cassandra/hints/HintsDescriptorTest.java b/test/unit/org/apache/cassandra/hints/HintsDescriptorTest.java index 08487d1..bab2356 100644 --- a/test/unit/org/apache/cassandra/hints/HintsDescriptorTest.java +++ b/test/unit/org/apache/cassandra/hints/HintsDescriptorTest.java @@ -20,11 +20,14 @@ package org.apache.cassandra.hints; import java.io.DataInput; import java.io.File; import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.Collections; import java.util.UUID; import com.google.common.collect.ImmutableMap; import com.google.common.io.ByteStreams; -import com.google.common.io.Files; +import org.junit.Assert; import org.junit.Test; import org.apache.cassandra.io.compress.LZ4Compressor; @@ -33,6 +36,8 @@ import org.apache.cassandra.io.util.DataOutputBuffer; import static junit.framework.Assert.assertEquals; import static junit.framework.Assert.assertNotSame; import static junit.framework.Assert.fail; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; public class HintsDescriptorTest { @@ -100,21 +105,41 @@ public class HintsDescriptorTest ImmutableMap<String, Object> parameters = ImmutableMap.of(); HintsDescriptor expected = new HintsDescriptor(hostId, version, timestamp, parameters); - File directory = Files.createTempDir(); + Path directory = Files.createTempDirectory("hints"); try { - try (HintsWriter ignored = HintsWriter.create(directory, expected)) + try (HintsWriter ignored = HintsWriter.create(directory.toFile(), expected)) { } - HintsDescriptor actual = HintsDescriptor.readFromFile(new File(directory, expected.fileName()).toPath()); + HintsDescriptor actual = HintsDescriptor.readFromFile(directory.resolve(expected.fileName())); assertEquals(expected, actual); } finally { - directory.deleteOnExit(); + directory.toFile().deleteOnExit(); } } + @Test + public void testHandleIOE() throws IOException + { + Path p = Files.createTempFile("testing", ".hints"); + // empty file; + assertTrue(p.toFile().exists()); + Assert.assertEquals(0, Files.size(p)); + HintsDescriptor.handleDescriptorIOE(new IOException("test"), p); + assertFalse(Files.exists(p)); + + // non-empty + p = Files.createTempFile("testing", ".hints"); + Files.write(p, Collections.singleton("hello")); + HintsDescriptor.handleDescriptorIOE(new IOException("test"), p); + File newFile = new File(p.getParent().toFile(), p.getFileName().toString().replace(".hints", ".corrupt.hints")); + assertFalse(Files.exists(p)); + assertTrue(newFile.toString(), newFile.exists()); + newFile.deleteOnExit(); + } + private static void testSerializeDeserializeLoop(HintsDescriptor descriptor) throws IOException { // serialize to a byte array --------------------------------------------------------------------- To unsubscribe, e-mail: commits-unsubscr...@cassandra.apache.org For additional commands, e-mail: commits-h...@cassandra.apache.org