Legacy sstables with  multi block range tombstones create invalid bound 
sequences

Patch by Blake Eggleston; Reviewed by Sam Tunnicliffe and Aleksey Yeschenko for 
CASSANDRA-14823


Project: http://git-wip-us.apache.org/repos/asf/cassandra/repo
Commit: http://git-wip-us.apache.org/repos/asf/cassandra/commit/285153f6
Tree: http://git-wip-us.apache.org/repos/asf/cassandra/tree/285153f6
Diff: http://git-wip-us.apache.org/repos/asf/cassandra/diff/285153f6

Branch: refs/heads/trunk
Commit: 285153f621a1e67212ef61686188eff3e9c80a4e
Parents: 5e969e9
Author: Blake Eggleston <bdeggles...@gmail.com>
Authored: Fri Oct 12 16:21:03 2018 -0700
Committer: Blake Eggleston <bdeggles...@gmail.com>
Committed: Tue Oct 16 12:37:24 2018 -0700

----------------------------------------------------------------------
 CHANGES.txt                                     |   1 +
 .../columniterator/SSTableReversedIterator.java | 118 +++++++++++++++----
 ...acted_multi_block_rt-ka-4-CompressionInfo.db | Bin 0 -> 43 bytes
 ...acy_ka_compacted_multi_block_rt-ka-4-Data.db | Bin 0 -> 17848 bytes
 ...ka_compacted_multi_block_rt-ka-4-Digest.sha1 |   1 +
 ...y_ka_compacted_multi_block_rt-ka-4-Filter.db | Bin 0 -> 176 bytes
 ...cy_ka_compacted_multi_block_rt-ka-4-Index.db | Bin 0 -> 303 bytes
 ..._compacted_multi_block_rt-ka-4-Statistics.db | Bin 0 -> 4490 bytes
 ..._ka_compacted_multi_block_rt-ka-4-Summary.db | Bin 0 -> 92 bytes
 ...acy_ka_compacted_multi_block_rt-ka-4-TOC.txt |   8 ++
 ...ushed_multi_block_rt-ka-1-CompressionInfo.db | Bin 0 -> 43 bytes
 ...egacy_ka_flushed_multi_block_rt-ka-1-Data.db | Bin 0 -> 17569 bytes
 ...y_ka_flushed_multi_block_rt-ka-1-Digest.sha1 |   1 +
 ...acy_ka_flushed_multi_block_rt-ka-1-Filter.db | Bin 0 -> 16 bytes
 ...gacy_ka_flushed_multi_block_rt-ka-1-Index.db | Bin 0 -> 306 bytes
 ...ka_flushed_multi_block_rt-ka-1-Statistics.db | Bin 0 -> 4478 bytes
 ...cy_ka_flushed_multi_block_rt-ka-1-Summary.db | Bin 0 -> 92 bytes
 ...egacy_ka_flushed_multi_block_rt-ka-1-TOC.txt |   8 ++
 .../cassandra/io/sstable/LegacySSTableTest.java |  26 ++++
 19 files changed, 140 insertions(+), 23 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/cassandra/blob/285153f6/CHANGES.txt
----------------------------------------------------------------------
diff --git a/CHANGES.txt b/CHANGES.txt
index 6ca14a0..78c0c47 100644
--- a/CHANGES.txt
+++ b/CHANGES.txt
@@ -1,4 +1,5 @@
 3.0.18
+ * Legacy sstables with  multi block range tombstones create invalid bound 
sequences (CASSANDRA-14823)
  * Expand range tombstone validation checks to multiple interim request stages 
(CASSANDRA-14824)
  * Reverse order reads can return incomplete results (CASSANDRA-14803)
  * Avoid calling iter.next() in a loop when notifying indexers about range 
tombstones (CASSANDRA-14794)

http://git-wip-us.apache.org/repos/asf/cassandra/blob/285153f6/src/java/org/apache/cassandra/db/columniterator/SSTableReversedIterator.java
----------------------------------------------------------------------
diff --git 
a/src/java/org/apache/cassandra/db/columniterator/SSTableReversedIterator.java 
b/src/java/org/apache/cassandra/db/columniterator/SSTableReversedIterator.java
index f8c0e77..2d95dab 100644
--- 
a/src/java/org/apache/cassandra/db/columniterator/SSTableReversedIterator.java
+++ 
b/src/java/org/apache/cassandra/db/columniterator/SSTableReversedIterator.java
@@ -20,7 +20,7 @@ package org.apache.cassandra.db.columniterator;
 import java.io.IOException;
 import java.util.*;
 
-import com.google.common.base.Preconditions;
+import com.google.common.base.Verify;
 
 import org.apache.cassandra.config.CFMetaData;
 import org.apache.cassandra.db.*;
@@ -79,6 +79,8 @@ public class SSTableReversedIterator extends 
AbstractSSTableIterator
         protected boolean skipFirstIteratedItem;
         protected boolean skipLastIteratedItem;
 
+        protected Unfiltered mostRecentlyEmitted = null;
+
         private ReverseReader(FileDataInput file, boolean shouldCloseFile)
         {
             super(file, shouldCloseFile);
@@ -122,7 +124,7 @@ public class SSTableReversedIterator extends 
AbstractSSTableIterator
                 // Note that we can reuse that buffer between slices (we could 
alternatively re-read from disk
                 // every time, but that feels more wasteful) so we want to 
include everything from the beginning.
                 // We can stop at the slice end however since any following 
slice will be before that.
-                loadFromDisk(null, slice.end(), true, false, false);
+                loadFromDisk(null, slice.end(), false, false, null, null);
             }
             setIterator(slice);
         }
@@ -155,7 +157,9 @@ public class SSTableReversedIterator extends 
AbstractSSTableIterator
         {
             if (!hasNext())
                 throw new NoSuchElementException();
-            return iterator.next();
+            Unfiltered next = iterator.next();
+            mostRecentlyEmitted = next;
+            return next;
         }
 
         protected boolean stopReadingDisk()
@@ -163,13 +167,20 @@ public class SSTableReversedIterator extends 
AbstractSSTableIterator
             return false;
         }
 
+        // checks if left prefix precedes right prefix
+        private boolean precedes(ClusteringPrefix left, ClusteringPrefix right)
+        {
+            return metadata().comparator.compare(left, right) < 0;
+        }
+
         // Reads the unfiltered from disk and load them into the reader 
buffer. It stops reading when either the partition
         // is fully read, or when stopReadingDisk() returns true.
         protected void loadFromDisk(Slice.Bound start,
                                     Slice.Bound end,
-                                    boolean includeFirst,
                                     boolean hasPreviousBlock,
-                                    boolean hasNextBlock) throws IOException
+                                    boolean hasNextBlock,
+                                    ClusteringPrefix currentFirstName,
+                                    ClusteringPrefix nextLastName) throws 
IOException
         {
             // start != null means it's the block covering the beginning of 
the slice, so it has to be the last block for this slice.
             assert start == null || !hasNextBlock;
@@ -220,16 +231,82 @@ public class SSTableReversedIterator extends 
AbstractSSTableIterator
                    && !stopReadingDisk())
             {
                 Unfiltered unfiltered = deserializer.readNext();
-                // We may get empty row for the same reason expressed on 
UnfilteredSerializer.deserializeOne.
-                if (!unfiltered.isEmpty() && (!isFirst || includeFirst))
-                    buffer.add(unfiltered);
 
-                isFirst = false;
+                if (isFirst && openMarker == null
+                    && currentFirstName != null && nextLastName != null
+                    && (precedes(currentFirstName, nextLastName) || 
precedes(unfiltered.clustering(), currentFirstName)))
+                {
+                    // Range tombstones spanning multiple index blocks when 
reading legacy sstables need special handling.
+                    // Pre-3.0, the column index didn't encode open markers. 
Instead, open range tombstones were rewritten
+                    // at the start of index blocks they at least partially 
covered. These rewritten RTs found at the
+                    // beginning of index blocks need to be handled as though 
they were an open marker, otherwise iterator
+                    // validation will fail and/or some rows will be excluded 
from the result. These rewritten RTs can be
+                    // detected based on their relation to the current index 
block and the next one depending on what wrote
+                    // the sstable. For sstables coming from a memtable flush, 
a rewritten RT will have a clustering value
+                    // less than the first name of its index block. For 
sstables coming from compaction, the index block
+                    // first name will be the RT open bound, which will be 
less than the last name of the next block. So,
+                    // here we compare the first name of this block to the 
last name of the next block to detect the
+                    // compaction case, and clustering value of the unfiltered 
we just read to the index block's first name
+                    // to detect the flush case.
+                    Verify.verify(!sstable.descriptor.version.storeRows());
+                    Verify.verify(openMarker == null);
+                    Verify.verify(!skipLastIteratedItem);
+                    Verify.verify(unfiltered.isRangeTombstoneMarker());
+                    buffer.add(unfiltered);
+                    if (hasNextBlock)
+                        skipLastIteratedItem = true;
+                }
+                else if (isFirst && nextLastName != null && 
!precedes(nextLastName, unfiltered.clustering()))
+                {
+                    // When dealing with old format sstable, we have the 
problem that a row can span 2 index block, i.e. it can
+                    // start at the end of a block and end at the beginning of 
the next one. That's not a problem per se for
+                    // UnfilteredDeserializer.OldFormatSerializer, since it 
always read rows entirely, even if they span index
+                    // blocks, but as we reading index block in reverse we 
must be careful to not read the end of the row at
+                    // beginning of a block before we're reading the beginning 
of that row. So what we do is that if we detect
+                    // that the row starting this block is also the row ending 
the next one we're read (previous on disk), then
+                    // we'll skip that first result and  let it be read with 
the next block.
+                    Verify.verify(!sstable.descriptor.version.storeRows());
+                    isFirst = false;
+                }
+                else if (unfiltered.isEmpty())
+                {
+                    isFirst = false;
+                }
+                else
+                {
+                    buffer.add(unfiltered);
+                    isFirst = false;
+                }
 
                 if (unfiltered.isRangeTombstoneMarker())
                     updateOpenMarker((RangeTombstoneMarker)unfiltered);
             }
 
+            if (!sstable.descriptor.version.storeRows()
+                && deserializer.hasNext()
+                && (end == null || deserializer.compareNextTo(end) < 0))
+            {
+                // Range tombstone start and end bounds are stored together in 
legacy sstables. When we read one, we
+                // stash the closing bound until we reach the appropriate 
place to emit it, which is immediately before
+                // the next unfiltered with a greater clustering.
+                // If SSTRI considers the block exhausted before encountering 
such a clustering though, this end marker
+                // will never be emitted. So here we just check if there's a 
closing bound left in the deserializer.
+                // If there is, we compare it against the most recently 
emitted unfiltered (i.e.: the last unfiltered
+                // that this RT would enclose. And we have to do THAT 
comparison because the last name field on the
+                // current index block will be whatever was written at the end 
of the index block (i.e. the last name
+                // physically in the block), not the closing bound of the 
range tombstone (i.e. the last name logically
+                // in the block). If all this indicates that there is indeed a 
range tombstone we're missing, we add it
+                // to the buffer and update the open marker field.
+                Unfiltered unfiltered = deserializer.readNext();
+                RangeTombstoneMarker marker = 
unfiltered.isRangeTombstoneMarker() ? (RangeTombstoneMarker) unfiltered : null;
+                if (marker != null && marker.isClose(false)
+                    && (mostRecentlyEmitted == null || 
precedes(marker.clustering(), mostRecentlyEmitted.clustering())))
+                {
+                    buffer.add(marker);
+                    updateOpenMarker(marker);
+                }
+            }
+
             // If we have an open marker, we should close it before finishing
             if (openMarker != null)
             {
@@ -333,7 +410,7 @@ public class SSTableReversedIterator extends 
AbstractSSTableIterator
                 // formats (see next comment)
                 if (!iterator.hasNext() && nextBlockIdx > lastBlockIdx)
                 {
-                    
Preconditions.checkState(!sstable.descriptor.version.storeRows());
+                    Verify.verify(!sstable.descriptor.version.storeRows());
                     continue;
                 }
 
@@ -362,26 +439,21 @@ public class SSTableReversedIterator extends 
AbstractSSTableIterator
             boolean canIncludeSliceStart = !hasNextBlock;
             boolean canIncludeSliceEnd = !hasPreviousBlock;
 
-            // When dealing with old format sstable, we have the problem that 
a row can span 2 index block, i.e. it can
-            // start at the end of a block and end at the beginning of the 
next one. That's not a problem per se for
-            // UnfilteredDeserializer.OldFormatSerializer, since it always 
read rows entirely, even if they span index
-            // blocks, but as we reading index block in reverse we must be 
careful to not read the end of the row at
-            // beginning of a block before we're reading the beginning of that 
row. So what we do is that if we detect
-            // that the row starting this block is also the row ending the 
next one we're read (previous on disk), then
-            // we'll skip that first result and  let it be read with the next 
block.
-            boolean includeFirst = true;
+            ClusteringPrefix currentFirstName = null;
+            ClusteringPrefix nextLastName = null;
             if (!sstable.descriptor.version.storeRows() && currentBlock > 0)
             {
-                ClusteringPrefix lastOfNext = indexState.index(currentBlock - 
1).lastName;
-                ClusteringPrefix firstOfCurrent = 
indexState.index(currentBlock).firstName;
-                includeFirst = metadata().comparator.compare(lastOfNext, 
firstOfCurrent) != 0;
+                currentFirstName = indexState.index(currentBlock).firstName;
+                nextLastName = indexState.index(currentBlock - 1).lastName;
             }
 
             loadFromDisk(canIncludeSliceStart ? slice.start() : null,
                          canIncludeSliceEnd ? slice.end() : null,
-                         includeFirst,
                          hasPreviousBlock,
-                         hasNextBlock);
+                         hasNextBlock,
+                         currentFirstName,
+                         nextLastName
+            );
             setIterator(slice);
         }
 

http://git-wip-us.apache.org/repos/asf/cassandra/blob/285153f6/test/data/legacy-sstables/ka/legacy_tables/legacy_ka_compacted_multi_block_rt/legacy_tables-legacy_ka_compacted_multi_block_rt-ka-4-CompressionInfo.db
----------------------------------------------------------------------
diff --git 
a/test/data/legacy-sstables/ka/legacy_tables/legacy_ka_compacted_multi_block_rt/legacy_tables-legacy_ka_compacted_multi_block_rt-ka-4-CompressionInfo.db
 
b/test/data/legacy-sstables/ka/legacy_tables/legacy_ka_compacted_multi_block_rt/legacy_tables-legacy_ka_compacted_multi_block_rt-ka-4-CompressionInfo.db
new file mode 100644
index 0000000..d320406
Binary files /dev/null and 
b/test/data/legacy-sstables/ka/legacy_tables/legacy_ka_compacted_multi_block_rt/legacy_tables-legacy_ka_compacted_multi_block_rt-ka-4-CompressionInfo.db
 differ

http://git-wip-us.apache.org/repos/asf/cassandra/blob/285153f6/test/data/legacy-sstables/ka/legacy_tables/legacy_ka_compacted_multi_block_rt/legacy_tables-legacy_ka_compacted_multi_block_rt-ka-4-Data.db
----------------------------------------------------------------------
diff --git 
a/test/data/legacy-sstables/ka/legacy_tables/legacy_ka_compacted_multi_block_rt/legacy_tables-legacy_ka_compacted_multi_block_rt-ka-4-Data.db
 
b/test/data/legacy-sstables/ka/legacy_tables/legacy_ka_compacted_multi_block_rt/legacy_tables-legacy_ka_compacted_multi_block_rt-ka-4-Data.db
new file mode 100644
index 0000000..775b68c
Binary files /dev/null and 
b/test/data/legacy-sstables/ka/legacy_tables/legacy_ka_compacted_multi_block_rt/legacy_tables-legacy_ka_compacted_multi_block_rt-ka-4-Data.db
 differ

http://git-wip-us.apache.org/repos/asf/cassandra/blob/285153f6/test/data/legacy-sstables/ka/legacy_tables/legacy_ka_compacted_multi_block_rt/legacy_tables-legacy_ka_compacted_multi_block_rt-ka-4-Digest.sha1
----------------------------------------------------------------------
diff --git 
a/test/data/legacy-sstables/ka/legacy_tables/legacy_ka_compacted_multi_block_rt/legacy_tables-legacy_ka_compacted_multi_block_rt-ka-4-Digest.sha1
 
b/test/data/legacy-sstables/ka/legacy_tables/legacy_ka_compacted_multi_block_rt/legacy_tables-legacy_ka_compacted_multi_block_rt-ka-4-Digest.sha1
new file mode 100644
index 0000000..63993fc
--- /dev/null
+++ 
b/test/data/legacy-sstables/ka/legacy_tables/legacy_ka_compacted_multi_block_rt/legacy_tables-legacy_ka_compacted_multi_block_rt-ka-4-Digest.sha1
@@ -0,0 +1 @@
+3417730863
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/cassandra/blob/285153f6/test/data/legacy-sstables/ka/legacy_tables/legacy_ka_compacted_multi_block_rt/legacy_tables-legacy_ka_compacted_multi_block_rt-ka-4-Filter.db
----------------------------------------------------------------------
diff --git 
a/test/data/legacy-sstables/ka/legacy_tables/legacy_ka_compacted_multi_block_rt/legacy_tables-legacy_ka_compacted_multi_block_rt-ka-4-Filter.db
 
b/test/data/legacy-sstables/ka/legacy_tables/legacy_ka_compacted_multi_block_rt/legacy_tables-legacy_ka_compacted_multi_block_rt-ka-4-Filter.db
new file mode 100644
index 0000000..aa97e86
Binary files /dev/null and 
b/test/data/legacy-sstables/ka/legacy_tables/legacy_ka_compacted_multi_block_rt/legacy_tables-legacy_ka_compacted_multi_block_rt-ka-4-Filter.db
 differ

http://git-wip-us.apache.org/repos/asf/cassandra/blob/285153f6/test/data/legacy-sstables/ka/legacy_tables/legacy_ka_compacted_multi_block_rt/legacy_tables-legacy_ka_compacted_multi_block_rt-ka-4-Index.db
----------------------------------------------------------------------
diff --git 
a/test/data/legacy-sstables/ka/legacy_tables/legacy_ka_compacted_multi_block_rt/legacy_tables-legacy_ka_compacted_multi_block_rt-ka-4-Index.db
 
b/test/data/legacy-sstables/ka/legacy_tables/legacy_ka_compacted_multi_block_rt/legacy_tables-legacy_ka_compacted_multi_block_rt-ka-4-Index.db
new file mode 100644
index 0000000..f425226
Binary files /dev/null and 
b/test/data/legacy-sstables/ka/legacy_tables/legacy_ka_compacted_multi_block_rt/legacy_tables-legacy_ka_compacted_multi_block_rt-ka-4-Index.db
 differ

http://git-wip-us.apache.org/repos/asf/cassandra/blob/285153f6/test/data/legacy-sstables/ka/legacy_tables/legacy_ka_compacted_multi_block_rt/legacy_tables-legacy_ka_compacted_multi_block_rt-ka-4-Statistics.db
----------------------------------------------------------------------
diff --git 
a/test/data/legacy-sstables/ka/legacy_tables/legacy_ka_compacted_multi_block_rt/legacy_tables-legacy_ka_compacted_multi_block_rt-ka-4-Statistics.db
 
b/test/data/legacy-sstables/ka/legacy_tables/legacy_ka_compacted_multi_block_rt/legacy_tables-legacy_ka_compacted_multi_block_rt-ka-4-Statistics.db
new file mode 100644
index 0000000..2580202
Binary files /dev/null and 
b/test/data/legacy-sstables/ka/legacy_tables/legacy_ka_compacted_multi_block_rt/legacy_tables-legacy_ka_compacted_multi_block_rt-ka-4-Statistics.db
 differ

http://git-wip-us.apache.org/repos/asf/cassandra/blob/285153f6/test/data/legacy-sstables/ka/legacy_tables/legacy_ka_compacted_multi_block_rt/legacy_tables-legacy_ka_compacted_multi_block_rt-ka-4-Summary.db
----------------------------------------------------------------------
diff --git 
a/test/data/legacy-sstables/ka/legacy_tables/legacy_ka_compacted_multi_block_rt/legacy_tables-legacy_ka_compacted_multi_block_rt-ka-4-Summary.db
 
b/test/data/legacy-sstables/ka/legacy_tables/legacy_ka_compacted_multi_block_rt/legacy_tables-legacy_ka_compacted_multi_block_rt-ka-4-Summary.db
new file mode 100644
index 0000000..c85b4a8
Binary files /dev/null and 
b/test/data/legacy-sstables/ka/legacy_tables/legacy_ka_compacted_multi_block_rt/legacy_tables-legacy_ka_compacted_multi_block_rt-ka-4-Summary.db
 differ

http://git-wip-us.apache.org/repos/asf/cassandra/blob/285153f6/test/data/legacy-sstables/ka/legacy_tables/legacy_ka_compacted_multi_block_rt/legacy_tables-legacy_ka_compacted_multi_block_rt-ka-4-TOC.txt
----------------------------------------------------------------------
diff --git 
a/test/data/legacy-sstables/ka/legacy_tables/legacy_ka_compacted_multi_block_rt/legacy_tables-legacy_ka_compacted_multi_block_rt-ka-4-TOC.txt
 
b/test/data/legacy-sstables/ka/legacy_tables/legacy_ka_compacted_multi_block_rt/legacy_tables-legacy_ka_compacted_multi_block_rt-ka-4-TOC.txt
new file mode 100644
index 0000000..3fc5eec
--- /dev/null
+++ 
b/test/data/legacy-sstables/ka/legacy_tables/legacy_ka_compacted_multi_block_rt/legacy_tables-legacy_ka_compacted_multi_block_rt-ka-4-TOC.txt
@@ -0,0 +1,8 @@
+Summary.db
+Digest.sha1
+CompressionInfo.db
+TOC.txt
+Filter.db
+Data.db
+Index.db
+Statistics.db

http://git-wip-us.apache.org/repos/asf/cassandra/blob/285153f6/test/data/legacy-sstables/ka/legacy_tables/legacy_ka_flushed_multi_block_rt/legacy_tables-legacy_ka_flushed_multi_block_rt-ka-1-CompressionInfo.db
----------------------------------------------------------------------
diff --git 
a/test/data/legacy-sstables/ka/legacy_tables/legacy_ka_flushed_multi_block_rt/legacy_tables-legacy_ka_flushed_multi_block_rt-ka-1-CompressionInfo.db
 
b/test/data/legacy-sstables/ka/legacy_tables/legacy_ka_flushed_multi_block_rt/legacy_tables-legacy_ka_flushed_multi_block_rt-ka-1-CompressionInfo.db
new file mode 100644
index 0000000..2336902
Binary files /dev/null and 
b/test/data/legacy-sstables/ka/legacy_tables/legacy_ka_flushed_multi_block_rt/legacy_tables-legacy_ka_flushed_multi_block_rt-ka-1-CompressionInfo.db
 differ

http://git-wip-us.apache.org/repos/asf/cassandra/blob/285153f6/test/data/legacy-sstables/ka/legacy_tables/legacy_ka_flushed_multi_block_rt/legacy_tables-legacy_ka_flushed_multi_block_rt-ka-1-Data.db
----------------------------------------------------------------------
diff --git 
a/test/data/legacy-sstables/ka/legacy_tables/legacy_ka_flushed_multi_block_rt/legacy_tables-legacy_ka_flushed_multi_block_rt-ka-1-Data.db
 
b/test/data/legacy-sstables/ka/legacy_tables/legacy_ka_flushed_multi_block_rt/legacy_tables-legacy_ka_flushed_multi_block_rt-ka-1-Data.db
new file mode 100644
index 0000000..e7a9fd7
Binary files /dev/null and 
b/test/data/legacy-sstables/ka/legacy_tables/legacy_ka_flushed_multi_block_rt/legacy_tables-legacy_ka_flushed_multi_block_rt-ka-1-Data.db
 differ

http://git-wip-us.apache.org/repos/asf/cassandra/blob/285153f6/test/data/legacy-sstables/ka/legacy_tables/legacy_ka_flushed_multi_block_rt/legacy_tables-legacy_ka_flushed_multi_block_rt-ka-1-Digest.sha1
----------------------------------------------------------------------
diff --git 
a/test/data/legacy-sstables/ka/legacy_tables/legacy_ka_flushed_multi_block_rt/legacy_tables-legacy_ka_flushed_multi_block_rt-ka-1-Digest.sha1
 
b/test/data/legacy-sstables/ka/legacy_tables/legacy_ka_flushed_multi_block_rt/legacy_tables-legacy_ka_flushed_multi_block_rt-ka-1-Digest.sha1
new file mode 100644
index 0000000..bfe4bc3
--- /dev/null
+++ 
b/test/data/legacy-sstables/ka/legacy_tables/legacy_ka_flushed_multi_block_rt/legacy_tables-legacy_ka_flushed_multi_block_rt-ka-1-Digest.sha1
@@ -0,0 +1 @@
+3995406674
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/cassandra/blob/285153f6/test/data/legacy-sstables/ka/legacy_tables/legacy_ka_flushed_multi_block_rt/legacy_tables-legacy_ka_flushed_multi_block_rt-ka-1-Filter.db
----------------------------------------------------------------------
diff --git 
a/test/data/legacy-sstables/ka/legacy_tables/legacy_ka_flushed_multi_block_rt/legacy_tables-legacy_ka_flushed_multi_block_rt-ka-1-Filter.db
 
b/test/data/legacy-sstables/ka/legacy_tables/legacy_ka_flushed_multi_block_rt/legacy_tables-legacy_ka_flushed_multi_block_rt-ka-1-Filter.db
new file mode 100644
index 0000000..606783d
Binary files /dev/null and 
b/test/data/legacy-sstables/ka/legacy_tables/legacy_ka_flushed_multi_block_rt/legacy_tables-legacy_ka_flushed_multi_block_rt-ka-1-Filter.db
 differ

http://git-wip-us.apache.org/repos/asf/cassandra/blob/285153f6/test/data/legacy-sstables/ka/legacy_tables/legacy_ka_flushed_multi_block_rt/legacy_tables-legacy_ka_flushed_multi_block_rt-ka-1-Index.db
----------------------------------------------------------------------
diff --git 
a/test/data/legacy-sstables/ka/legacy_tables/legacy_ka_flushed_multi_block_rt/legacy_tables-legacy_ka_flushed_multi_block_rt-ka-1-Index.db
 
b/test/data/legacy-sstables/ka/legacy_tables/legacy_ka_flushed_multi_block_rt/legacy_tables-legacy_ka_flushed_multi_block_rt-ka-1-Index.db
new file mode 100644
index 0000000..1faa378
Binary files /dev/null and 
b/test/data/legacy-sstables/ka/legacy_tables/legacy_ka_flushed_multi_block_rt/legacy_tables-legacy_ka_flushed_multi_block_rt-ka-1-Index.db
 differ

http://git-wip-us.apache.org/repos/asf/cassandra/blob/285153f6/test/data/legacy-sstables/ka/legacy_tables/legacy_ka_flushed_multi_block_rt/legacy_tables-legacy_ka_flushed_multi_block_rt-ka-1-Statistics.db
----------------------------------------------------------------------
diff --git 
a/test/data/legacy-sstables/ka/legacy_tables/legacy_ka_flushed_multi_block_rt/legacy_tables-legacy_ka_flushed_multi_block_rt-ka-1-Statistics.db
 
b/test/data/legacy-sstables/ka/legacy_tables/legacy_ka_flushed_multi_block_rt/legacy_tables-legacy_ka_flushed_multi_block_rt-ka-1-Statistics.db
new file mode 100644
index 0000000..0070c96
Binary files /dev/null and 
b/test/data/legacy-sstables/ka/legacy_tables/legacy_ka_flushed_multi_block_rt/legacy_tables-legacy_ka_flushed_multi_block_rt-ka-1-Statistics.db
 differ

http://git-wip-us.apache.org/repos/asf/cassandra/blob/285153f6/test/data/legacy-sstables/ka/legacy_tables/legacy_ka_flushed_multi_block_rt/legacy_tables-legacy_ka_flushed_multi_block_rt-ka-1-Summary.db
----------------------------------------------------------------------
diff --git 
a/test/data/legacy-sstables/ka/legacy_tables/legacy_ka_flushed_multi_block_rt/legacy_tables-legacy_ka_flushed_multi_block_rt-ka-1-Summary.db
 
b/test/data/legacy-sstables/ka/legacy_tables/legacy_ka_flushed_multi_block_rt/legacy_tables-legacy_ka_flushed_multi_block_rt-ka-1-Summary.db
new file mode 100644
index 0000000..d2adbfa
Binary files /dev/null and 
b/test/data/legacy-sstables/ka/legacy_tables/legacy_ka_flushed_multi_block_rt/legacy_tables-legacy_ka_flushed_multi_block_rt-ka-1-Summary.db
 differ

http://git-wip-us.apache.org/repos/asf/cassandra/blob/285153f6/test/data/legacy-sstables/ka/legacy_tables/legacy_ka_flushed_multi_block_rt/legacy_tables-legacy_ka_flushed_multi_block_rt-ka-1-TOC.txt
----------------------------------------------------------------------
diff --git 
a/test/data/legacy-sstables/ka/legacy_tables/legacy_ka_flushed_multi_block_rt/legacy_tables-legacy_ka_flushed_multi_block_rt-ka-1-TOC.txt
 
b/test/data/legacy-sstables/ka/legacy_tables/legacy_ka_flushed_multi_block_rt/legacy_tables-legacy_ka_flushed_multi_block_rt-ka-1-TOC.txt
new file mode 100644
index 0000000..3fc5eec
--- /dev/null
+++ 
b/test/data/legacy-sstables/ka/legacy_tables/legacy_ka_flushed_multi_block_rt/legacy_tables-legacy_ka_flushed_multi_block_rt-ka-1-TOC.txt
@@ -0,0 +1,8 @@
+Summary.db
+Digest.sha1
+CompressionInfo.db
+TOC.txt
+Filter.db
+Data.db
+Index.db
+Statistics.db

http://git-wip-us.apache.org/repos/asf/cassandra/blob/285153f6/test/unit/org/apache/cassandra/io/sstable/LegacySSTableTest.java
----------------------------------------------------------------------
diff --git a/test/unit/org/apache/cassandra/io/sstable/LegacySSTableTest.java 
b/test/unit/org/apache/cassandra/io/sstable/LegacySSTableTest.java
index f6467e1..d58ce8a 100644
--- a/test/unit/org/apache/cassandra/io/sstable/LegacySSTableTest.java
+++ b/test/unit/org/apache/cassandra/io/sstable/LegacySSTableTest.java
@@ -232,6 +232,32 @@ public class LegacySSTableTest
     }
 
     @Test
+    public void testMultiBlockRangeTombstones() throws Exception
+    {
+        /**
+         * During upgrades from 2.1 to 3.0, reading old sstables in reverse 
order would generate invalid sequences of
+         * range tombstone bounds if their range tombstones spanned multiple 
column index blocks. The read would fail
+         * in different ways depending on whether the 2.1 tables were produced 
by a flush or a compaction.
+         */
+
+        String version = "ka";
+        for (String tableFmt : new 
String[]{"legacy_%s_compacted_multi_block_rt%s", 
"legacy_%s_flushed_multi_block_rt%s"})
+        {
+            String table = String.format(tableFmt, version, "");
+            QueryProcessor.executeOnceInternal(String.format("CREATE TABLE 
legacy_tables.%s " +
+                                                             "(k int, c1 int, 
c2 int, v1 blob, v2 blob, " +
+                                                             "PRIMARY KEY (k, 
c1, c2))", table));
+            loadLegacyTable(tableFmt, version, "");
+
+            UntypedResultSet forward = 
QueryProcessor.executeOnceInternal(String.format("SELECT * FROM 
legacy_tables.%s WHERE k=100", table));
+            UntypedResultSet reverse = 
QueryProcessor.executeOnceInternal(String.format("SELECT * FROM 
legacy_tables.%s WHERE k=100 ORDER BY c1 DESC, c2 DESC", table));
+
+            Assert.assertFalse(forward.isEmpty());
+            Assert.assertEquals(table, forward.size(), reverse.size());
+        }
+    }
+
+    @Test
     public void testVerifyOldSSTables() throws Exception
     {
         for (String legacyVersion : legacyVersions)


---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscr...@cassandra.apache.org
For additional commands, e-mail: commits-h...@cassandra.apache.org

Reply via email to