-
Notifications
You must be signed in to change notification settings - Fork 25.6k
Protect replicated data streams against local rollovers #64710
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
f0d9076
10d7ffc
5514ae8
461773d
ef71529
7237930
e9b6627
e031d72
b1c57d1
0c22ce1
73877d6
5cbaf3d
c9d410f
5efa943
312a788
56074c3
57941ac
1b49909
5700b67
5528da4
a21454e
ef26773
5051eaf
676c7a8
8e21fc7
9c7a071
23c59b8
a80f777
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,38 @@ | ||
| [role="xpack"] | ||
| [[promote-data-stream-api]] | ||
| === Promote Data Stream API | ||
| ++++ | ||
| <titleabbrev>Promote data stream api</titleabbrev> | ||
| ++++ | ||
|
|
||
| The purpose of the promote data stream api is to turn | ||
| a data stream that is replicated by CCR into a regular | ||
| data stream. | ||
|
|
||
| Via CCR Auto Following, a data stream from a remote cluster | ||
| can be replicated to the local cluster. These data streams | ||
| can't be rolled over in the local cluster. Only if the upstream | ||
| data stream rolls over then these replicated data streams roll | ||
| over as well. In the event that the remote cluster is no longer | ||
| available, the data stream in the local cluster can be promoted | ||
| to a regular data stream, which allows these data streams to | ||
| be rolled over in the local cluster. | ||
|
|
||
| [source,console] | ||
| ---- | ||
| POST /_data_stream/_promote/my-data-stream | ||
| ---- | ||
| // TEST[catch:missing] | ||
|
|
||
| [[promote-data-stream-api-request]] | ||
| ==== {api-request-title} | ||
|
|
||
| `POST /_data_stream/_promote/<data-stream>` | ||
|
|
||
|
|
||
| [[promote-data-stream-api-path-params]] | ||
| ==== {api-path-parms-title} | ||
|
|
||
| `<data-stream>`:: | ||
| (Required, string) | ||
| The name of the data stream to promote. |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -44,26 +44,29 @@ public final class DataStream extends AbstractDiffable<DataStream> implements To | |
|
|
||
| public static final String BACKING_INDEX_PREFIX = ".ds-"; | ||
| public static final Version HIDDEN_VERSION = Version.V_7_11_0; | ||
| public static final Version REPLICATED_VERSION = Version.V_8_0_0; | ||
|
|
||
| private final String name; | ||
| private final TimestampField timeStampField; | ||
| private final List<Index> indices; | ||
| private final long generation; | ||
| private final Map<String, Object> metadata; | ||
| private final boolean hidden; | ||
| private final boolean replicated; | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Should we name this "allowRollover" or "followed"? I am okay with "replicated" if you prefer.
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Maybe something that indicates whether a data stream can be rolled over is a better name. I will think about this. |
||
|
|
||
| public DataStream(String name, TimestampField timeStampField, List<Index> indices, long generation, Map<String, Object> metadata) { | ||
| this(name, timeStampField, indices, generation, metadata, false); | ||
| this(name, timeStampField, indices, generation, metadata, false, false); | ||
| } | ||
|
|
||
| public DataStream(String name, TimestampField timeStampField, List<Index> indices, long generation, Map<String, Object> metadata, | ||
| boolean hidden) { | ||
| boolean hidden, boolean replicated) { | ||
| this.name = name; | ||
| this.timeStampField = timeStampField; | ||
| this.indices = Collections.unmodifiableList(indices); | ||
| this.generation = generation; | ||
| this.metadata = metadata; | ||
| this.hidden = hidden; | ||
| this.replicated = replicated; | ||
| assert indices.size() > 0; | ||
| } | ||
|
|
||
|
|
@@ -100,6 +103,16 @@ public boolean isHidden() { | |
| return hidden; | ||
| } | ||
|
|
||
| /** | ||
| * Determines whether this data stream is replicated from elsewhere, | ||
| * for example a remote cluster. | ||
| * | ||
| * @return Whether this data stream is replicated. | ||
| */ | ||
| public boolean isReplicated() { | ||
| return replicated; | ||
| } | ||
|
|
||
| /** | ||
| * Performs a rollover on a {@code DataStream} instance and returns a new instance containing | ||
| * the updated list of backing indices and incremented generation. | ||
|
|
@@ -110,9 +123,14 @@ public boolean isHidden() { | |
| */ | ||
| public DataStream rollover(Index newWriteIndex) { | ||
| assert newWriteIndex.getName().equals(getDefaultBackingIndexName(name, generation + 1)); | ||
| if (replicated) { | ||
| throw new IllegalArgumentException("data stream [" + name + "] cannot be rolled over, " + | ||
| "because it is a replicated data stream"); | ||
| } | ||
|
|
||
| List<Index> backingIndices = new ArrayList<>(indices); | ||
| backingIndices.add(newWriteIndex); | ||
| return new DataStream(name, timeStampField, backingIndices, generation + 1, metadata, hidden); | ||
| return new DataStream(name, timeStampField, backingIndices, generation + 1, metadata, hidden, replicated); | ||
| } | ||
|
|
||
| /** | ||
|
|
@@ -126,7 +144,7 @@ public DataStream removeBackingIndex(Index index) { | |
| List<Index> backingIndices = new ArrayList<>(indices); | ||
| backingIndices.remove(index); | ||
| assert backingIndices.size() == indices.size() - 1; | ||
| return new DataStream(name, timeStampField, backingIndices, generation, metadata, hidden); | ||
| return new DataStream(name, timeStampField, backingIndices, generation, metadata, hidden, replicated); | ||
| } | ||
|
|
||
| /** | ||
|
|
@@ -151,7 +169,11 @@ public DataStream replaceBackingIndex(Index existingBackingIndex, Index newBacki | |
| "it is the write index", existingBackingIndex.getName(), name)); | ||
| } | ||
| backingIndices.set(backingIndexPosition, newBackingIndex); | ||
| return new DataStream(name, timeStampField, backingIndices, generation, metadata, hidden); | ||
| return new DataStream(name, timeStampField, backingIndices, generation, metadata, hidden, replicated); | ||
| } | ||
|
|
||
| public DataStream promoteDataStream() { | ||
| return new DataStream(name, timeStampField, indices, getGeneration(), metadata, hidden, false); | ||
| } | ||
|
|
||
| /** | ||
|
|
@@ -169,7 +191,8 @@ public static String getDefaultBackingIndexName(String dataStreamName, long gene | |
| public DataStream(StreamInput in) throws IOException { | ||
| this(in.readString(), new TimestampField(in), in.readList(Index::new), in.readVLong(), | ||
| in.getVersion().onOrAfter(Version.V_7_11_0) ? in.readMap(): null, | ||
| in.getVersion().onOrAfter(HIDDEN_VERSION) && in.readBoolean()); | ||
| in.getVersion().onOrAfter(HIDDEN_VERSION) && in.readBoolean(), | ||
| in.getVersion().onOrAfter(REPLICATED_VERSION) && in.readBoolean()); | ||
| } | ||
|
|
||
| public static Diff<DataStream> readDiffFrom(StreamInput in) throws IOException { | ||
|
|
@@ -188,6 +211,9 @@ public void writeTo(StreamOutput out) throws IOException { | |
| if (out.getVersion().onOrAfter(HIDDEN_VERSION)) { | ||
| out.writeBoolean(hidden); | ||
| } | ||
| if (out.getVersion().onOrAfter(REPLICATED_VERSION)) { | ||
| out.writeBoolean(replicated); | ||
| } | ||
| } | ||
|
|
||
| public static final ParseField NAME_FIELD = new ParseField("name"); | ||
|
|
@@ -196,11 +222,12 @@ public void writeTo(StreamOutput out) throws IOException { | |
| public static final ParseField GENERATION_FIELD = new ParseField("generation"); | ||
| public static final ParseField METADATA_FIELD = new ParseField("_meta"); | ||
| public static final ParseField HIDDEN_FIELD = new ParseField("hidden"); | ||
| public static final ParseField REPLICATED_FIELD = new ParseField("replicated"); | ||
|
|
||
| @SuppressWarnings("unchecked") | ||
| private static final ConstructingObjectParser<DataStream, Void> PARSER = new ConstructingObjectParser<>("data_stream", | ||
| args -> new DataStream((String) args[0], (TimestampField) args[1], (List<Index>) args[2], (Long) args[3], | ||
| (Map<String, Object>) args[4], args[5] != null && (boolean) args[5])); | ||
| (Map<String, Object>) args[4], args[5] != null && (boolean) args[5], args[6] != null && (boolean) args[6])); | ||
|
|
||
| static { | ||
| PARSER.declareString(ConstructingObjectParser.constructorArg(), NAME_FIELD); | ||
|
|
@@ -209,6 +236,7 @@ public void writeTo(StreamOutput out) throws IOException { | |
| PARSER.declareLong(ConstructingObjectParser.constructorArg(), GENERATION_FIELD); | ||
| PARSER.declareObject(ConstructingObjectParser.optionalConstructorArg(), (p, c) -> p.map(), METADATA_FIELD); | ||
| PARSER.declareBoolean(ConstructingObjectParser.optionalConstructorArg(), HIDDEN_FIELD); | ||
| PARSER.declareBoolean(ConstructingObjectParser.optionalConstructorArg(), REPLICATED_FIELD); | ||
| } | ||
|
|
||
| public static DataStream fromXContent(XContentParser parser) throws IOException { | ||
|
|
@@ -226,6 +254,7 @@ public XContentBuilder toXContent(XContentBuilder builder, Params params) throws | |
| builder.field(METADATA_FIELD.getPreferredName(), metadata); | ||
| } | ||
| builder.field(HIDDEN_FIELD.getPreferredName(), hidden); | ||
| builder.field(REPLICATED_FIELD.getPreferredName(), replicated); | ||
| builder.endObject(); | ||
| return builder; | ||
| } | ||
|
|
@@ -239,12 +268,14 @@ public boolean equals(Object o) { | |
| timeStampField.equals(that.timeStampField) && | ||
| indices.equals(that.indices) && | ||
| generation == that.generation && | ||
| Objects.equals(metadata, that.metadata); | ||
| Objects.equals(metadata, that.metadata) && | ||
| hidden == that.hidden && | ||
| replicated == that.replicated; | ||
| } | ||
|
|
||
| @Override | ||
| public int hashCode() { | ||
| return Objects.hash(name, timeStampField, indices, generation, metadata); | ||
| return Objects.hash(name, timeStampField, indices, generation, metadata, hidden, replicated); | ||
| } | ||
|
|
||
| public static final class TimestampField implements Writeable, ToXContentObject { | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should we have a separate method that ensures this datastream can be rolled over instead?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think that this is what happens in this
rolloverDataStream(...)method. Anything before the if statement is for validating purposes and after the if statement is for actually performing the rollover (theDataStream#rollover(...)method is then also invoked). I just added theDataStream#rollover(...)invocation here, so that validation that is in this method is also performed when just validating the rollover.