Browse Source

Add descriptions to various tasks (#76700)

Adds a more detailed description to get-snapshots, get-shard-snapshot,
snapshots-status and field-capabilities tasks since these may somtimes
be long-running things and it may occasionally be necessary to track
down why they are running and what they're doing.
David Turner 4 years ago
parent
commit
e87337fa38

+ 11 - 0
server/src/main/java/org/elasticsearch/action/admin/cluster/snapshots/get/GetSnapshotsRequest.java

@@ -25,6 +25,7 @@ import org.elasticsearch.tasks.TaskId;
 
 
 import java.io.IOException;
 import java.io.IOException;
 import java.nio.charset.StandardCharsets;
 import java.nio.charset.StandardCharsets;
+import java.util.Arrays;
 import java.util.Base64;
 import java.util.Base64;
 import java.util.Map;
 import java.util.Map;
 
 
@@ -422,4 +423,14 @@ public class GetSnapshotsRequest extends MasterNodeRequest<GetSnapshotsRequest>
             out.writeString(snapshotName);
             out.writeString(snapshotName);
         }
         }
     }
     }
+
+    @Override
+    public String getDescription() {
+        final StringBuilder stringBuilder = new StringBuilder("repositories[");
+        Strings.collectionToDelimitedStringWithLimit(Arrays.asList(repositories), ",", "", "", 512, stringBuilder);
+        stringBuilder.append("], snapshots[");
+        Strings.collectionToDelimitedStringWithLimit(Arrays.asList(snapshots), ",", "", "", 1024, stringBuilder);
+        stringBuilder.append("]");
+        return stringBuilder.toString();
+    }
 }
 }

+ 9 - 0
server/src/main/java/org/elasticsearch/action/admin/cluster/snapshots/get/shard/GetShardSnapshotRequest.java

@@ -10,6 +10,7 @@ package org.elasticsearch.action.admin.cluster.snapshots.get.shard;
 
 
 import org.elasticsearch.action.ActionRequestValidationException;
 import org.elasticsearch.action.ActionRequestValidationException;
 import org.elasticsearch.action.support.master.MasterNodeRequest;
 import org.elasticsearch.action.support.master.MasterNodeRequest;
+import org.elasticsearch.common.Strings;
 import org.elasticsearch.common.io.stream.StreamInput;
 import org.elasticsearch.common.io.stream.StreamInput;
 import org.elasticsearch.common.io.stream.StreamOutput;
 import org.elasticsearch.common.io.stream.StreamOutput;
 import org.elasticsearch.index.shard.ShardId;
 import org.elasticsearch.index.shard.ShardId;
@@ -102,4 +103,12 @@ public class GetShardSnapshotRequest extends MasterNodeRequest<GetShardSnapshotR
     public int hashCode() {
     public int hashCode() {
         return Objects.hash(repositories, shardId);
         return Objects.hash(repositories, shardId);
     }
     }
+
+    @Override
+    public String getDescription() {
+        final StringBuilder stringBuilder = new StringBuilder("shard").append(shardId).append(", repositories[");
+        Strings.collectionToDelimitedStringWithLimit(repositories, ",", "", "", 1024, stringBuilder);
+        stringBuilder.append("]");
+        return stringBuilder.toString();
+    }
 }
 }

+ 9 - 0
server/src/main/java/org/elasticsearch/action/admin/cluster/snapshots/status/SnapshotsStatusRequest.java

@@ -18,6 +18,7 @@ import org.elasticsearch.tasks.Task;
 import org.elasticsearch.tasks.TaskId;
 import org.elasticsearch.tasks.TaskId;
 
 
 import java.io.IOException;
 import java.io.IOException;
+import java.util.Arrays;
 import java.util.Map;
 import java.util.Map;
 
 
 import static org.elasticsearch.action.ValidateActions.addValidationError;
 import static org.elasticsearch.action.ValidateActions.addValidationError;
@@ -146,4 +147,12 @@ public class SnapshotsStatusRequest extends MasterNodeRequest<SnapshotsStatusReq
     public boolean ignoreUnavailable() {
     public boolean ignoreUnavailable() {
         return ignoreUnavailable;
         return ignoreUnavailable;
     }
     }
+
+    @Override
+    public String getDescription() {
+        final StringBuilder stringBuilder = new StringBuilder("repository[").append(repository).append("], snapshots[");
+        Strings.collectionToDelimitedStringWithLimit(Arrays.asList(snapshots), ",", "", "", 1024, stringBuilder);
+        stringBuilder.append("]");
+        return stringBuilder.toString();
+    }
 }
 }

+ 11 - 0
server/src/main/java/org/elasticsearch/action/fieldcaps/FieldCapabilitiesRequest.java

@@ -222,4 +222,15 @@ public final class FieldCapabilitiesRequest extends ActionRequest implements Ind
         result = 31 * result + Arrays.hashCode(fields);
         result = 31 * result + Arrays.hashCode(fields);
         return result;
         return result;
     }
     }
+
+    @Override
+    public String getDescription() {
+        final StringBuilder stringBuilder = new StringBuilder("indices[");
+        Strings.collectionToDelimitedStringWithLimit(Arrays.asList(indices), ",", "", "", 1024, stringBuilder);
+        stringBuilder.append("], fields[");
+        Strings.collectionToDelimitedStringWithLimit(Arrays.asList(fields), ",", "", "", 1024, stringBuilder);
+        stringBuilder.append("]");
+        return stringBuilder.toString();
+    }
+
 }
 }

+ 39 - 0
server/src/main/java/org/elasticsearch/common/Strings.java

@@ -627,6 +627,45 @@ public class Strings {
         }
         }
     }
     }
 
 
+    /**
+     * Converts a collection of items to a string like {@link #collectionToDelimitedString(Iterable, String, String, String, StringBuilder)}
+     * except that it stops if the string gets too long and just indicates how many items were omitted.
+     *
+     * @param coll        the collection of items to display
+     * @param delim       the delimiter to write between the items (usually {@code ","})
+     * @param prefix      a string to write before each item (usually {@code ""} or {@code "["})
+     * @param suffix      a string to write after each item (usually {@code ""} or {@code "]"})
+     * @param appendLimit if this many characters have been appended to the string and there are still items to display then the remaining
+     *                    items are omitted
+     */
+    public static void collectionToDelimitedStringWithLimit(
+        Iterable<?> coll,
+        String delim,
+        String prefix,
+        String suffix,
+        int appendLimit,
+        StringBuilder sb
+    ) {
+        final Iterator<?> it = coll.iterator();
+        final long lengthLimit = sb.length() + appendLimit; // long to avoid overflow
+        int count = 0;
+        while (it.hasNext()) {
+            sb.append(prefix).append(it.next()).append(suffix);
+            count += 1;
+            if (it.hasNext()) {
+                sb.append(delim);
+                if (sb.length() > lengthLimit) {
+                    int omitted = 0;
+                    while (it.hasNext()) {
+                        it.next();
+                        omitted += 1;
+                    }
+                    sb.append("... (").append(count + omitted).append(" in total, ").append(omitted).append(" omitted)");
+                }
+            }
+        }
+    }
+
     /**
     /**
      * Convenience method to return a Collection as a delimited (e.g. CSV)
      * Convenience method to return a Collection as a delimited (e.g. CSV)
      * String. E.g. useful for <code>toString()</code> implementations.
      * String. E.g. useful for <code>toString()</code> implementations.

+ 9 - 0
server/src/test/java/org/elasticsearch/action/admin/cluster/snapshots/get/GetSnapshotsRequestTests.java

@@ -12,6 +12,7 @@ import org.elasticsearch.search.sort.SortOrder;
 import org.elasticsearch.test.ESTestCase;
 import org.elasticsearch.test.ESTestCase;
 
 
 import static org.hamcrest.Matchers.containsString;
 import static org.hamcrest.Matchers.containsString;
+import static org.hamcrest.Matchers.equalTo;
 
 
 public class GetSnapshotsRequestTests extends ESTestCase {
 public class GetSnapshotsRequestTests extends ESTestCase {
 
 
@@ -62,4 +63,12 @@ public class GetSnapshotsRequestTests extends ESTestCase {
             assertThat(e.getMessage(), containsString("can't use after and offset simultaneously"));
             assertThat(e.getMessage(), containsString("can't use after and offset simultaneously"));
         }
         }
     }
     }
+
+    public void testGetDescription() {
+        final GetSnapshotsRequest request = new GetSnapshotsRequest(
+            new String[] { "repo1", "repo2" },
+            new String[] { "snapshotA", "snapshotB" }
+        );
+        assertThat(request.getDescription(), equalTo("repositories[repo1,repo2], snapshots[snapshotA,snapshotB]"));
+    }
 }
 }

+ 22 - 0
server/src/test/java/org/elasticsearch/action/admin/cluster/snapshots/get/shard/GetShardSnapshotRequestSerializationTests.java

@@ -14,8 +14,13 @@ import org.elasticsearch.index.shard.ShardId;
 import org.elasticsearch.test.AbstractWireSerializingTestCase;
 import org.elasticsearch.test.AbstractWireSerializingTestCase;
 
 
 import java.io.IOException;
 import java.io.IOException;
+import java.util.Arrays;
 import java.util.List;
 import java.util.List;
 
 
+import static org.hamcrest.Matchers.containsString;
+import static org.hamcrest.Matchers.equalTo;
+import static org.hamcrest.Matchers.lessThanOrEqualTo;
+
 public class GetShardSnapshotRequestSerializationTests extends AbstractWireSerializingTestCase<GetShardSnapshotRequest> {
 public class GetShardSnapshotRequestSerializationTests extends AbstractWireSerializingTestCase<GetShardSnapshotRequest> {
     @Override
     @Override
     protected Writeable.Reader<GetShardSnapshotRequest> instanceReader() {
     protected Writeable.Reader<GetShardSnapshotRequest> instanceReader() {
@@ -46,4 +51,21 @@ public class GetShardSnapshotRequestSerializationTests extends AbstractWireSeria
     private ShardId randomShardId() {
     private ShardId randomShardId() {
         return new ShardId(randomAlphaOfLength(10), UUIDs.randomBase64UUID(), randomIntBetween(0, 100));
         return new ShardId(randomAlphaOfLength(10), UUIDs.randomBase64UUID(), randomIntBetween(0, 100));
     }
     }
+
+    public void testGetDescription() {
+        final GetShardSnapshotRequest request = new GetShardSnapshotRequest(Arrays.asList("repo1", "repo2"), new ShardId("idx", "uuid", 0));
+        assertThat(request.getDescription(), equalTo("shard[idx][0], repositories[repo1,repo2]"));
+
+        final GetShardSnapshotRequest randomRequest = createTestInstance();
+        final String description = randomRequest.getDescription();
+        assertThat(description, containsString(randomRequest.getShardId().toString()));
+        assertThat(
+            description,
+            description.length(),
+            lessThanOrEqualTo(
+                ("shard" + randomRequest.getShardId() + ", repositories[").length() + 1024 + 100 + ",... (999 in total, 999 omitted)"
+                    .length()
+            )
+        );
+    }
 }
 }

+ 57 - 0
server/src/test/java/org/elasticsearch/action/fieldcaps/FieldCapabilitiesRequestTests.java

@@ -30,6 +30,13 @@ import java.util.List;
 import java.util.function.Consumer;
 import java.util.function.Consumer;
 
 
 import static java.util.Collections.singletonMap;
 import static java.util.Collections.singletonMap;
+import static org.hamcrest.Matchers.allOf;
+import static org.hamcrest.Matchers.anyOf;
+import static org.hamcrest.Matchers.containsString;
+import static org.hamcrest.Matchers.endsWith;
+import static org.hamcrest.Matchers.equalTo;
+import static org.hamcrest.Matchers.lessThanOrEqualTo;
+import static org.hamcrest.Matchers.startsWith;
 
 
 public class FieldCapabilitiesRequestTests extends AbstractWireSerializingTestCase<FieldCapabilitiesRequest> {
 public class FieldCapabilitiesRequestTests extends AbstractWireSerializingTestCase<FieldCapabilitiesRequest> {
 
 
@@ -138,4 +145,54 @@ public class FieldCapabilitiesRequestTests extends AbstractWireSerializingTestCa
         ActionRequestValidationException exception = request.validate();
         ActionRequestValidationException exception = request.validate();
         assertNotNull(exception);
         assertNotNull(exception);
     }
     }
+
+    public void testGetDescription() {
+        final FieldCapabilitiesRequest request = new FieldCapabilitiesRequest();
+        assertThat(request.getDescription(), equalTo("indices[], fields[]"));
+
+        request.fields("a", "b");
+        assertThat(request.getDescription(), anyOf(
+            equalTo("indices[], fields[a,b]"),
+            equalTo("indices[], fields[b,a]")));
+
+        request.indices("x", "y", "z");
+        request.fields("a");
+        assertThat(request.getDescription(), equalTo("indices[x,y,z], fields[a]"));
+
+        final String[] lots = new String[between(1024, 2048)];
+        for (int i = 0; i < lots.length; i++) {
+            lots[i] = "s" + i;
+        }
+
+        request.indices("x","y","z");
+        request.fields(lots);
+        assertThat(request.getDescription(), allOf(
+            startsWith("indices[x,y,z], fields["),
+            containsString("..."),
+            containsString(lots.length + " in total"),
+            containsString("omitted")));
+        assertThat(request.getDescription().length(), lessThanOrEqualTo(
+            1024 + ("indices[x,y,z], fields[" + "s9999,... (9999 in total, 9999 omitted)]").length()));
+
+        request.fields("a");
+        request.indices(lots);
+        assertThat(request.getDescription(), allOf(
+            startsWith("indices[s0,s1,s2,s3"),
+            containsString("..."),
+            containsString(lots.length + " in total"),
+            containsString("omitted"),
+            endsWith("], fields[a]")));
+        assertThat(request.getDescription().length(), lessThanOrEqualTo(
+            1024 + ("indices[" + "s9999,... (9999 in total, 9999 omitted)], fields[a]").length()));
+
+        final FieldCapabilitiesRequest randomRequest = createTestInstance();
+        final String description = randomRequest.getDescription();
+        for (String index : randomRequest.indices()) {
+            assertThat(description, containsString(index));
+        }
+        for (String field : randomRequest.fields()) {
+            assertThat(description, containsString(field));
+        }
+    }
+
 }
 }

+ 92 - 0
server/src/test/java/org/elasticsearch/common/StringsTests.java

@@ -13,10 +13,16 @@ import org.elasticsearch.common.xcontent.ToXContent;
 import org.elasticsearch.common.xcontent.ToXContentObject;
 import org.elasticsearch.common.xcontent.ToXContentObject;
 import org.elasticsearch.test.ESTestCase;
 import org.elasticsearch.test.ESTestCase;
 
 
+import java.util.ArrayList;
 import java.util.Collections;
 import java.util.Collections;
+import java.util.List;
 
 
+import static org.hamcrest.Matchers.allOf;
 import static org.hamcrest.Matchers.containsString;
 import static org.hamcrest.Matchers.containsString;
+import static org.hamcrest.Matchers.endsWith;
+import static org.hamcrest.Matchers.equalTo;
 import static org.hamcrest.Matchers.is;
 import static org.hamcrest.Matchers.is;
+import static org.hamcrest.Matchers.lessThanOrEqualTo;
 
 
 public class StringsTests extends ESTestCase {
 public class StringsTests extends ESTestCase {
 
 
@@ -111,4 +117,90 @@ public class StringsTests extends ESTestCase {
         assertEquals(Strings.tokenizeByCommaToSet("   aa   "), Sets.newHashSet("aa"));
         assertEquals(Strings.tokenizeByCommaToSet("   aa   "), Sets.newHashSet("aa"));
         assertEquals(Strings.tokenizeByCommaToSet("   "), Sets.newHashSet());
         assertEquals(Strings.tokenizeByCommaToSet("   "), Sets.newHashSet());
     }
     }
+
+    public void testCollectionToDelimitedStringWithLimitZero() {
+        final String delimiter = randomFrom("", ",", ", ", "/");
+        final String prefix = randomFrom("", "[");
+        final String suffix = randomFrom("", "]");
+
+        final int count = between(0, 100);
+        final List<String> strings = new ArrayList<>(count);
+        while (strings.size() < count) {
+            // avoid starting with a sequence of empty appends, it makes the assertions much messier
+            final int minLength = strings.isEmpty() && delimiter.isEmpty() && prefix.isEmpty() && suffix.isEmpty() ? 1 : 0;
+            strings.add(randomAlphaOfLength(between(minLength, 10)));
+        }
+
+        final StringBuilder stringBuilder = new StringBuilder();
+        Strings.collectionToDelimitedStringWithLimit(strings, delimiter, prefix, suffix, 0, stringBuilder);
+        final String completelyTruncatedDescription = stringBuilder.toString();
+
+        if (count == 0) {
+            assertThat(completelyTruncatedDescription, equalTo(""));
+        } else if (count == 1) {
+            assertThat(completelyTruncatedDescription, equalTo(prefix + strings.get(0) + suffix));
+        } else {
+            assertThat(completelyTruncatedDescription, equalTo(prefix + strings.get(0) + suffix + delimiter +
+                "... (" + count + " in total, " + (count - 1) + " omitted)"));
+        }
+    }
+
+    public void testCollectionToDelimitedStringWithLimitTruncation() {
+        final String delimiter = randomFrom("", ",", ", ", "/");
+        final String prefix = randomFrom("", "[");
+        final String suffix = randomFrom("", "]");
+
+        final int count = between(2, 100);
+        final List<String> strings = new ArrayList<>(count);
+        while (strings.size() < count) {
+            // avoid empty appends, it makes the assertions much messier
+            final int minLength = delimiter.isEmpty() && prefix.isEmpty() && suffix.isEmpty() ? 1 : 0;
+            strings.add(randomAlphaOfLength(between(minLength, 10)));
+        }
+
+        final int fullDescriptionLength = Strings.collectionToDelimitedString(strings, delimiter, prefix, suffix).length();
+        final int lastItemSize = prefix.length() + strings.get(count-1).length() + suffix.length();
+        final int truncatedLength = between(0, fullDescriptionLength - lastItemSize - 1);
+        final StringBuilder stringBuilder = new StringBuilder();
+        Strings.collectionToDelimitedStringWithLimit(strings, delimiter, prefix, suffix, truncatedLength, stringBuilder);
+        final String truncatedDescription = stringBuilder.toString();
+
+        assertThat(truncatedDescription, allOf(
+            containsString("... (" + count + " in total,"),
+            endsWith(" omitted)")
+        ));
+
+        assertThat(truncatedDescription, truncatedDescription.length(), lessThanOrEqualTo(
+            truncatedLength + (prefix + "0123456789" + suffix + delimiter + "... (999 in total, 999 omitted)").length()
+        ));
+    }
+
+    public void testCollectionToDelimitedStringWithLimitNoTruncation() {
+        final String delimiter = randomFrom("", ",", ", ", "/");
+        final String prefix = randomFrom("", "[");
+        final String suffix = randomFrom("", "]");
+
+        final int count = between(1, 100);
+        final List<String> strings = new ArrayList<>(count);
+        while (strings.size() < count) {
+            strings.add(randomAlphaOfLength(between(0, 10)));
+        }
+
+        final String fullDescription = Strings.collectionToDelimitedString(strings, delimiter, prefix, suffix);
+        for (String string : strings) {
+            assertThat(fullDescription, containsString(prefix + string + suffix));
+        }
+
+        final int lastItemSize = prefix.length() + strings.get(count-1).length() + suffix.length();
+        final int minLimit = fullDescription.length() - lastItemSize;
+        final int limit = randomFrom(
+            between(minLimit, fullDescription.length()),
+            between(minLimit, Integer.MAX_VALUE),
+            Integer.MAX_VALUE
+        );
+
+        final StringBuilder stringBuilder = new StringBuilder();
+        Strings.collectionToDelimitedStringWithLimit(strings, delimiter, prefix, suffix, limit, stringBuilder);
+        assertThat(stringBuilder.toString(), equalTo(fullDescription));
+    }
 }
 }