Skip to content

Commit 3b70e94

Browse files
authored
add is-write-index flag to aliases (#30942)
This commit adds the is-write-index flag for aliases. It allows requests to set the flag, and responses to display the flag. It does not validate and/or affect any indexing/getting/updating behavior of Elasticsearch -- this will be done in a follow-up PR.
1 parent eda4964 commit 3b70e94

24 files changed

+524
-40
lines changed

docs/reference/indices/aliases.asciidoc

+88
Original file line numberDiff line numberDiff line change
@@ -244,6 +244,94 @@ GET /alias2/_search?q=user:kimchy&routing=2,3
244244
// CONSOLE
245245
// TEST[continued]
246246

247+
[float]
248+
[[aliases-write-index]]
249+
==== Write Index
250+
251+
It is possible to associate the index pointed to by an alias as the write index.
252+
When specified, all index and update requests against an alias that point to multiple
253+
indices will attempt to resolve to the one index that is the write index.
254+
Only one index per alias can be assigned to be the write index at a time. If no write index is specified
255+
and there are multiple indices referenced by an alias, then writes will not be allowed.
256+
257+
It is possible to specify an index associated with an alias as a write index using both the aliases API
258+
and index creation API.
259+
260+
[source,js]
261+
--------------------------------------------------
262+
POST /_aliases
263+
{
264+
"actions" : [
265+
{
266+
"add" : {
267+
"index" : "test",
268+
"alias" : "alias1",
269+
"is_write_index" : true
270+
}
271+
}
272+
]
273+
}
274+
--------------------------------------------------
275+
// CONSOLE
276+
// TEST[s/^/PUT test\n/]
277+
278+
In this example, we associate the alias `alias1` to both `test` and `test2`, where
279+
`test` will be the index chosen for writing to.
280+
281+
[source,js]
282+
--------------------------------------------------
283+
PUT /alias1/_doc/1
284+
{
285+
"foo": "bar"
286+
}
287+
--------------------------------------------------
288+
// CONSOLE
289+
// TEST[continued]
290+
291+
The new document that was indexed to `/alias1/_doc/1` will be indexed as if it were
292+
`/test/_doc/1`.
293+
294+
[source,js]
295+
--------------------------------------------------
296+
GET /test/_doc/1
297+
--------------------------------------------------
298+
// CONSOLE
299+
// TEST[continued]
300+
301+
To swap which index is the write index for an alias, the Aliases API can be leveraged to
302+
do an atomic swap. The swap is not dependent on the ordering of the actions.
303+
304+
[source,js]
305+
--------------------------------------------------
306+
POST /_aliases
307+
{
308+
"actions" : [
309+
{
310+
"add" : {
311+
"index" : "test",
312+
"alias" : "alias1",
313+
"is_write_index" : true
314+
}
315+
}, {
316+
"add" : {
317+
"index" : "test2",
318+
"alias" : "alias1",
319+
"is_write_index" : false
320+
}
321+
}
322+
]
323+
}
324+
--------------------------------------------------
325+
// CONSOLE
326+
// TEST[s/^/PUT test\nPUT test2\n/]
327+
328+
[IMPORTANT]
329+
=====================================
330+
Aliases that do not explicitly set `is_write_index: true` for an index, and
331+
only reference one index, will have that referenced index behave as if it is the write index
332+
until an additional index is referenced. At that point, there will be no write index and
333+
writes will be rejected.
334+
=====================================
247335

248336
[float]
249337
[[alias-adding]]

rest-api-spec/src/main/resources/rest-api-spec/test/indices.create/10_basic.yml

+24-1
Original file line numberDiff line numberDiff line change
@@ -79,14 +79,37 @@
7979
indices.get_alias:
8080
index: test_index
8181

82-
- match: {test_index.aliases.test_alias: {}}
8382
- match: {test_index.aliases.test_blias.search_routing: b}
8483
- match: {test_index.aliases.test_blias.index_routing: b}
8584
- is_false: test_index.aliases.test_blias.filter
8685
- match: {test_index.aliases.test_clias.filter.term.field: value}
8786
- is_false: test_index.aliases.test_clias.index_routing
8887
- is_false: test_index.aliases.test_clias.search_routing
8988

89+
---
90+
"Create index with write aliases":
91+
- skip:
92+
version: " - 6.99.99"
93+
reason: is_write_index is not implemented in ES <= 6.x
94+
- do:
95+
indices.create:
96+
index: test_index
97+
body:
98+
aliases:
99+
test_alias: {}
100+
test_blias:
101+
is_write_index: false
102+
test_clias:
103+
is_write_index: true
104+
105+
- do:
106+
indices.get_alias:
107+
index: test_index
108+
109+
- is_false: test_index.aliases.test_alias.is_write_index
110+
- is_false: test_index.aliases.test_blias.is_write_index
111+
- is_true: test_index.aliases.test_clias.is_write_index
112+
90113
---
91114
"Create index with no type mappings":
92115
- do:

server/src/main/java/org/elasticsearch/action/admin/indices/alias/Alias.java

+34
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
package org.elasticsearch.action.admin.indices.alias;
2121

2222
import org.elasticsearch.ElasticsearchGenerationException;
23+
import org.elasticsearch.Version;
2324
import org.elasticsearch.common.Nullable;
2425
import org.elasticsearch.common.ParseField;
2526
import org.elasticsearch.common.Strings;
@@ -49,6 +50,7 @@ public class Alias implements Streamable, ToXContentFragment {
4950
private static final ParseField ROUTING = new ParseField("routing");
5051
private static final ParseField INDEX_ROUTING = new ParseField("index_routing", "indexRouting", "index-routing");
5152
private static final ParseField SEARCH_ROUTING = new ParseField("search_routing", "searchRouting", "search-routing");
53+
private static final ParseField IS_WRITE_INDEX = new ParseField("is_write_index");
5254

5355
private String name;
5456

@@ -61,6 +63,9 @@ public class Alias implements Streamable, ToXContentFragment {
6163
@Nullable
6264
private String searchRouting;
6365

66+
@Nullable
67+
private Boolean writeIndex;
68+
6469
private Alias() {
6570

6671
}
@@ -167,6 +172,21 @@ public Alias searchRouting(String searchRouting) {
167172
return this;
168173
}
169174

175+
/**
176+
* @return the write index flag for the alias
177+
*/
178+
public Boolean writeIndex() {
179+
return writeIndex;
180+
}
181+
182+
/**
183+
* Sets whether an alias is pointing to a write-index
184+
*/
185+
public Alias writeIndex(@Nullable Boolean writeIndex) {
186+
this.writeIndex = writeIndex;
187+
return this;
188+
}
189+
170190
/**
171191
* Allows to read an alias from the provided input stream
172192
*/
@@ -182,6 +202,11 @@ public void readFrom(StreamInput in) throws IOException {
182202
filter = in.readOptionalString();
183203
indexRouting = in.readOptionalString();
184204
searchRouting = in.readOptionalString();
205+
if (in.getVersion().onOrAfter(Version.V_7_0_0_alpha1)) {
206+
writeIndex = in.readOptionalBoolean();
207+
} else {
208+
writeIndex = null;
209+
}
185210
}
186211

187212
@Override
@@ -190,6 +215,9 @@ public void writeTo(StreamOutput out) throws IOException {
190215
out.writeOptionalString(filter);
191216
out.writeOptionalString(indexRouting);
192217
out.writeOptionalString(searchRouting);
218+
if (out.getVersion().onOrAfter(Version.V_7_0_0_alpha1)) {
219+
out.writeOptionalBoolean(writeIndex);
220+
}
193221
}
194222

195223
/**
@@ -219,6 +247,10 @@ public static Alias fromXContent(XContentParser parser) throws IOException {
219247
} else if (SEARCH_ROUTING.match(currentFieldName, parser.getDeprecationHandler())) {
220248
alias.searchRouting(parser.text());
221249
}
250+
} else if (token == XContentParser.Token.VALUE_BOOLEAN) {
251+
if (IS_WRITE_INDEX.match(currentFieldName, parser.getDeprecationHandler())) {
252+
alias.writeIndex(parser.booleanValue());
253+
}
222254
}
223255
}
224256
return alias;
@@ -245,6 +277,8 @@ public XContentBuilder toXContent(XContentBuilder builder, Params params) throws
245277
}
246278
}
247279

280+
builder.field(IS_WRITE_INDEX.getPreferredName(), writeIndex);
281+
248282
builder.endObject();
249283
return builder;
250284
}

server/src/main/java/org/elasticsearch/action/admin/indices/alias/IndicesAliasesRequest.java

+22
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
package org.elasticsearch.action.admin.indices.alias;
2121

2222
import org.elasticsearch.ElasticsearchGenerationException;
23+
import org.elasticsearch.Version;
2324
import org.elasticsearch.action.ActionRequestValidationException;
2425
import org.elasticsearch.action.AliasesRequest;
2526
import org.elasticsearch.action.support.IndicesOptions;
@@ -84,6 +85,7 @@ public static class AliasActions implements AliasesRequest, Writeable, ToXConten
8485
private static final ParseField ROUTING = new ParseField("routing");
8586
private static final ParseField INDEX_ROUTING = new ParseField("index_routing", "indexRouting", "index-routing");
8687
private static final ParseField SEARCH_ROUTING = new ParseField("search_routing", "searchRouting", "search-routing");
88+
private static final ParseField IS_WRITE_INDEX = new ParseField("is_write_index");
8789

8890
private static final ParseField ADD = new ParseField("add");
8991
private static final ParseField REMOVE = new ParseField("remove");
@@ -179,6 +181,7 @@ private static ObjectParser<AliasActions, Void> parser(String name, Supplier<Ali
179181
ADD_PARSER.declareField(AliasActions::routing, XContentParser::text, ROUTING, ValueType.INT);
180182
ADD_PARSER.declareField(AliasActions::indexRouting, XContentParser::text, INDEX_ROUTING, ValueType.INT);
181183
ADD_PARSER.declareField(AliasActions::searchRouting, XContentParser::text, SEARCH_ROUTING, ValueType.INT);
184+
ADD_PARSER.declareField(AliasActions::writeIndex, XContentParser::booleanValue, IS_WRITE_INDEX, ValueType.BOOLEAN);
182185
}
183186
private static final ObjectParser<AliasActions, Void> REMOVE_PARSER = parser(REMOVE.getPreferredName(), AliasActions::remove);
184187
private static final ObjectParser<AliasActions, Void> REMOVE_INDEX_PARSER = parser(REMOVE_INDEX.getPreferredName(),
@@ -215,6 +218,7 @@ private static ObjectParser<AliasActions, Void> parser(String name, Supplier<Ali
215218
private String routing;
216219
private String indexRouting;
217220
private String searchRouting;
221+
private Boolean writeIndex;
218222

219223
public AliasActions(AliasActions.Type type) {
220224
this.type = type;
@@ -231,6 +235,9 @@ public AliasActions(StreamInput in) throws IOException {
231235
routing = in.readOptionalString();
232236
searchRouting = in.readOptionalString();
233237
indexRouting = in.readOptionalString();
238+
if (in.getVersion().onOrAfter(Version.V_7_0_0_alpha1)) {
239+
writeIndex = in.readOptionalBoolean();
240+
}
234241
}
235242

236243
@Override
@@ -242,6 +249,9 @@ public void writeTo(StreamOutput out) throws IOException {
242249
out.writeOptionalString(routing);
243250
out.writeOptionalString(searchRouting);
244251
out.writeOptionalString(indexRouting);
252+
if (out.getVersion().onOrAfter(Version.V_7_0_0_alpha1)) {
253+
out.writeOptionalBoolean(writeIndex);
254+
}
245255
}
246256

247257
/**
@@ -401,6 +411,18 @@ public AliasActions filter(QueryBuilder filter) {
401411
}
402412
}
403413

414+
public AliasActions writeIndex(Boolean writeIndex) {
415+
if (type != AliasActions.Type.ADD) {
416+
throw new IllegalArgumentException("[is_write_index] is unsupported for [" + type + "]");
417+
}
418+
this.writeIndex = writeIndex;
419+
return this;
420+
}
421+
422+
public Boolean writeIndex() {
423+
return writeIndex;
424+
}
425+
404426
@Override
405427
public String[] aliases() {
406428
return aliases;

server/src/main/java/org/elasticsearch/action/admin/indices/alias/IndicesAliasesRequestBuilder.java

+12
Original file line numberDiff line numberDiff line change
@@ -130,6 +130,18 @@ public IndicesAliasesRequestBuilder addAlias(String index, String alias, QueryBu
130130
return this;
131131
}
132132

133+
/**
134+
* Adds an alias to the index.
135+
*
136+
* @param index The index
137+
* @param alias The alias
138+
* @param writeIndex write index flag
139+
*/
140+
public IndicesAliasesRequestBuilder addAlias(String index, String alias, boolean writeIndex) {
141+
request.addAliasAction(AliasActions.add().index(index).alias(alias).writeIndex(writeIndex));
142+
return this;
143+
}
144+
133145
/**
134146
* Removes an alias from the index.
135147
*

server/src/main/java/org/elasticsearch/action/admin/indices/alias/TransportIndicesAliasesAction.java

+2-1
Original file line numberDiff line numberDiff line change
@@ -100,7 +100,8 @@ protected void masterOperation(final IndicesAliasesRequest request, final Cluste
100100
switch (action.actionType()) {
101101
case ADD:
102102
for (String alias : concreteAliases(action, state.metaData(), index)) {
103-
finalActions.add(new AliasAction.Add(index, alias, action.filter(), action.indexRouting(), action.searchRouting()));
103+
finalActions.add(new AliasAction.Add(index, alias, action.filter(), action.indexRouting(),
104+
action.searchRouting(), action.writeIndex()));
104105
}
105106
break;
106107
case REMOVE:

server/src/main/java/org/elasticsearch/action/admin/indices/rollover/TransportRolloverAction.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -196,7 +196,7 @@ public void onFailure(Exception e) {
196196
static IndicesAliasesClusterStateUpdateRequest prepareRolloverAliasesUpdateRequest(String oldIndex, String newIndex,
197197
RolloverRequest request) {
198198
List<AliasAction> actions = unmodifiableList(Arrays.asList(
199-
new AliasAction.Add(newIndex, request.getAlias(), null, null, null),
199+
new AliasAction.Add(newIndex, request.getAlias(), null, null, null, null),
200200
new AliasAction.Remove(oldIndex, request.getAlias())));
201201
final IndicesAliasesClusterStateUpdateRequest updateRequest = new IndicesAliasesClusterStateUpdateRequest(actions)
202202
.ackTimeout(request.ackTimeout())

0 commit comments

Comments
 (0)