فهرست منبع

Fix segment stats in tsdb (#89754)

The serialization for segment stats was broken for tsdb because we
return a *slightly* different sort configuration. That caused
`_segments` and `_cat/segments` to break when any shard of the tsdb
index is hosted on another node.

Closes #89609
Nik Everett 3 سال پیش
والد
کامیت
188990fea7

+ 6 - 0
docs/changelog/89754.yaml

@@ -0,0 +1,6 @@
+pr: 89754
+summary: Fix segment stats in tsdb
+area: TSDB
+type: bug
+issues:
+ - 89609

+ 37 - 0
rest-api-spec/src/yamlRestTest/resources/rest-api-spec/test/cat.segments/10_basic.yml

@@ -169,3 +169,40 @@
   - match:
       $body: |
                /^(ba(r|z) \n?){2}$/
+
+---
+tsdb:
+  - skip:
+      version: " - 8.4.99"
+      reason: Serialization for segment stats fixed in 8.5.0
+
+  - do:
+      indices.create:
+        index: tsdb
+        body:
+          settings:
+            number_of_shards: 1
+            number_of_replicas: 0
+            index:
+              mode: time_series
+              routing_path: metricset
+          mappings:
+            properties:
+              metricset:
+                type: keyword
+                time_series_dimension: true
+
+  - do:
+      index:
+        index: tsdb
+        refresh: true
+        body:
+          metricset: bar
+          "@timestamp": 2022-01-01T00:00:00Z
+
+  - do:
+      cat.segments: {}
+  - match:
+      $body: |
+        /^(tsdb \s+ 0 \s+ p \s+ \d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3} \s+ _\d (\s\d){3} \s+
+        (\d+|\d+[.]\d+)(kb|b) \s+ \d+ (\s+ (false|true)){2} \s+  \d+\.\d+(\.\d+)? \s+ (false|true) \s? \n?)$/

+ 36 - 10
server/src/main/java/org/elasticsearch/index/engine/Segment.java

@@ -172,6 +172,13 @@ public class Segment implements Writeable {
         }
     }
 
+    private static final byte SORT_STRING_SET = 0;
+    private static final byte SORT_INT = 1;
+    private static final byte SORT_FLOAT = 2;
+    private static final byte SORT_DOUBLE = 3;
+    private static final byte SORT_LONG = 4;
+    private static final byte SORT_STRING_SINGLE = 5;
+
     private static Sort readSegmentSort(StreamInput in) throws IOException {
         int size = in.readVInt();
         if (size == 0) {
@@ -181,7 +188,7 @@ public class Segment implements Writeable {
         for (int i = 0; i < size; i++) {
             String field = in.readString();
             byte type = in.readByte();
-            if (type == 0) {
+            if (type == SORT_STRING_SET) {
                 Boolean missingFirst = in.readOptionalBoolean();
                 boolean max = in.readBoolean();
                 boolean reverse = in.readBoolean();
@@ -189,15 +196,22 @@ public class Segment implements Writeable {
                 if (missingFirst != null) {
                     fields[i].setMissingValue(missingFirst ? SortedSetSortField.STRING_FIRST : SortedSetSortField.STRING_LAST);
                 }
+            } else if (type == SORT_STRING_SINGLE) {
+                Boolean missingFirst = in.readOptionalBoolean();
+                boolean reverse = in.readBoolean();
+                fields[i] = new SortField(field, SortField.Type.STRING, reverse);
+                if (missingFirst != null) {
+                    fields[i].setMissingValue(missingFirst ? SortedSetSortField.STRING_FIRST : SortedSetSortField.STRING_LAST);
+                }
             } else {
                 Object missing = in.readGenericValue();
                 boolean max = in.readBoolean();
                 boolean reverse = in.readBoolean();
                 final SortField.Type numericType = switch (type) {
-                    case 1 -> SortField.Type.INT;
-                    case 2 -> SortField.Type.FLOAT;
-                    case 3 -> SortField.Type.DOUBLE;
-                    case 4 -> SortField.Type.LONG;
+                    case SORT_INT -> SortField.Type.INT;
+                    case SORT_FLOAT -> SortField.Type.FLOAT;
+                    case SORT_DOUBLE -> SortField.Type.DOUBLE;
+                    case SORT_LONG -> SortField.Type.LONG;
                     default -> throw new IOException("invalid index sort type:[" + type + "] for numeric field:[" + field + "]");
                 };
                 fields[i] = new SortedNumericSortField(
@@ -222,21 +236,33 @@ public class Segment implements Writeable {
         out.writeArray((o, field) -> {
             o.writeString(field.getField());
             if (field instanceof SortedSetSortField) {
-                o.writeByte((byte) 0);
+                o.writeByte(SORT_STRING_SET);
                 o.writeOptionalBoolean(field.getMissingValue() == null ? null : field.getMissingValue() == SortField.STRING_FIRST);
                 o.writeBoolean(((SortedSetSortField) field).getSelector() == SortedSetSelector.Type.MAX);
                 o.writeBoolean(field.getReverse());
             } else if (field instanceof SortedNumericSortField) {
                 switch (((SortedNumericSortField) field).getNumericType()) {
-                    case INT -> o.writeByte((byte) 1);
-                    case FLOAT -> o.writeByte((byte) 2);
-                    case DOUBLE -> o.writeByte((byte) 3);
-                    case LONG -> o.writeByte((byte) 4);
+                    case INT -> o.writeByte(SORT_INT);
+                    case FLOAT -> o.writeByte(SORT_FLOAT);
+                    case DOUBLE -> o.writeByte(SORT_DOUBLE);
+                    case LONG -> o.writeByte(SORT_LONG);
                     default -> throw new IOException("invalid index sort field:" + field);
                 }
                 o.writeGenericValue(field.getMissingValue());
                 o.writeBoolean(((SortedNumericSortField) field).getSelector() == SortedNumericSelector.Type.MAX);
                 o.writeBoolean(field.getReverse());
+            } else if (field.getType().equals(SortField.Type.STRING)) {
+                if (o.getVersion().before(Version.V_8_5_0)) {
+                    // The closest supported version before 8.5.0 was SortedSet fields, so we mimic that
+                    o.writeByte(SORT_STRING_SET);
+                    o.writeOptionalBoolean(field.getMissingValue() == null ? null : field.getMissingValue() == SortField.STRING_FIRST);
+                    o.writeBoolean(true);
+                    o.writeBoolean(field.getReverse());
+                } else {
+                    o.writeByte(SORT_STRING_SINGLE);
+                    o.writeOptionalBoolean(field.getMissingValue() == null ? null : field.getMissingValue() == SortField.STRING_FIRST);
+                    o.writeBoolean(field.getReverse());
+                }
             } else {
                 throw new IOException("invalid index sort field:" + field);
             }

+ 31 - 20
server/src/test/java/org/elasticsearch/index/engine/SegmentTests.java

@@ -25,28 +25,39 @@ import java.util.Objects;
 
 public class SegmentTests extends ESTestCase {
     static SortField randomSortField() {
-        if (randomBoolean()) {
-            SortedNumericSortField field = new SortedNumericSortField(
-                randomAlphaOfLengthBetween(1, 10),
-                SortField.Type.INT,
-                randomBoolean(),
-                randomBoolean() ? SortedNumericSelector.Type.MAX : SortedNumericSelector.Type.MIN
-            );
-            if (randomBoolean()) {
-                field.setMissingValue(randomInt());
+        return switch (between(0, 2)) {
+            case 0 -> {
+                SortedNumericSortField field = new SortedNumericSortField(
+                    randomAlphaOfLengthBetween(1, 10),
+                    SortField.Type.INT,
+                    randomBoolean(),
+                    randomBoolean() ? SortedNumericSelector.Type.MAX : SortedNumericSelector.Type.MIN
+                );
+                if (randomBoolean()) {
+                    field.setMissingValue(randomInt());
+                }
+                yield field;
             }
-            return field;
-        } else {
-            SortedSetSortField field = new SortedSetSortField(
-                randomAlphaOfLengthBetween(1, 10),
-                randomBoolean(),
-                randomBoolean() ? SortedSetSelector.Type.MAX : SortedSetSelector.Type.MIN
-            );
-            if (randomBoolean()) {
-                field.setMissingValue(randomBoolean() ? SortedSetSortField.STRING_FIRST : SortedSetSortField.STRING_LAST);
+            case 1 -> {
+                SortedSetSortField field = new SortedSetSortField(
+                    randomAlphaOfLengthBetween(1, 10),
+                    randomBoolean(),
+                    randomBoolean() ? SortedSetSelector.Type.MAX : SortedSetSelector.Type.MIN
+                );
+                if (randomBoolean()) {
+                    field.setMissingValue(randomBoolean() ? SortedSetSortField.STRING_FIRST : SortedSetSortField.STRING_LAST);
+                }
+                yield field;
             }
-            return field;
-        }
+            case 2 -> {
+                SortField field = new SortField(randomAlphaOfLengthBetween(1, 10), SortField.Type.STRING, randomBoolean());
+                if (randomBoolean()) {
+                    field.setMissingValue(randomBoolean() ? SortedSetSortField.STRING_FIRST : SortedSetSortField.STRING_LAST);
+                }
+                yield field;
+            }
+            default -> throw new UnsupportedOperationException();
+        };
     }
 
     static Sort randomIndexSort() {