Skip to content

Commit 822b742

Browse files
committed
Forbid read-only-allow-delete block in blocks API (#58727)
The read-only-allow-delete block is not really under the user's control since Elasticsearch adds/removes it automatically. This commit removes support for it from the new API for adding blocks to indices that was introduced in #58094.
1 parent a0df96b commit 822b742

File tree

6 files changed

+59
-29
lines changed

6 files changed

+59
-29
lines changed

docs/reference/index-modules/blocks.asciidoc

Lines changed: 21 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -22,16 +22,6 @@ index:
2222
Set to `true` to make the index and index metadata read only, `false` to
2323
allow writes and metadata changes.
2424

25-
`index.blocks.read_only_allow_delete`::
26-
27-
Similar to `index.blocks.read_only`, but also allows deleting the index to
28-
make more resources available. The <<shard-allocation-awareness,disk-based shard
29-
allocator>> may add and remove this block automatically.
30-
31-
Deleting documents from an index to release resources - rather than deleting the index itself - can increase the index size over time. When `index.blocks.read_only_allow_delete` is set to `true`, deleting documents is not permitted. However, deleting the index itself releases the read-only index block and makes resources available almost immediately.
32-
33-
IMPORTANT: {es} adds and removes the read-only index block automatically when the disk utilization falls below the high watermark, controlled by <<cluster-routing-flood_stage,cluster.routing.allocation.disk.watermark.flood_stage>>.
34-
3525
`index.blocks.read`::
3626

3727
Set to `true` to disable read operations against the index.
@@ -40,12 +30,32 @@ IMPORTANT: {es} adds and removes the read-only index block automatically when th
4030

4131
Set to `true` to disable data write operations against the index. Unlike `read_only`,
4232
this setting does not affect metadata. For instance, you can close an index with a `write`
43-
block, but not an index with a `read_only` block.
33+
block, but you cannot close an index with a `read_only` block.
4434

4535
`index.blocks.metadata`::
4636

4737
Set to `true` to disable index metadata reads and writes.
4838

39+
`index.blocks.read_only_allow_delete`::
40+
41+
Similar to `index.blocks.read_only`, but also allows deleting the index to
42+
make more resources available. The <<disk-based-shard-allocation,disk-based shard
43+
allocator>> adds and removes this block automatically.
44+
45+
Deleting documents from an index - rather than deleting the index itself - can
46+
in fact increase the index size. When you are running out of disk space
47+
`index.blocks.read_only_allow_delete` is set to `true`, preventing you from
48+
consuming more disk space by deleting some documents. However, this block does
49+
permit you to delete the index itself since this does not require any extra
50+
disk space. When you delete an index the data is removed from disk almost
51+
immediately, freeing the space it consumes.
52+
53+
IMPORTANT: {es} adds the read-only-allow-delete index block automatically when
54+
disk utilisation exceeds the <<cluster-routing-flood_stage,flood-stage
55+
watermark>> and removes it again when disk utilisation is below the
56+
<<cluster-routing-watermark-high,high watermark>>. You should not apply this
57+
block yourself.
58+
4959
[discrete]
5060
[[add-index-block]]
5161
=== Add index block API
@@ -94,11 +104,6 @@ Disable read operations.
94104
`read_only`::
95105
Disable write operations and metadata changes.
96106
97-
`read_only_allow_delete`::
98-
Disable write operations and metadata changes.
99-
Document deletion is disabled.
100-
However, index deletion is still allowed.
101-
102107
`write`::
103108
Disable write operations. However, metadata changes are still allowed.
104109
====

docs/reference/modules/cluster/disk_allocator.asciidoc

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ file or updated dynamically on a live cluster with the
1313

1414
Defaults to `true`. Set to `false` to disable the disk allocation decider.
1515

16+
[[cluster-routing-watermark-low]]
1617
`cluster.routing.allocation.disk.watermark.low`::
1718

1819
Controls the low watermark for disk usage. It defaults to `85%`, meaning
@@ -22,6 +23,7 @@ file or updated dynamically on a live cluster with the
2223
amount of space is available. This setting has no effect on the primary
2324
shards of newly-created indices but will prevent their replicas from being allocated.
2425

26+
[[cluster-routing-watermark-high]]
2527
`cluster.routing.allocation.disk.watermark.high`::
2628

2729
Controls the high watermark. It defaults to `90%`, meaning that

rest-api-spec/src/main/resources/rest-api-spec/api/indices.add_block.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@
1919
},
2020
"block":{
2121
"type":"string",
22-
"description":"The block to add (one of read, write, read_only, metadata, read_only_allow_delete)"
22+
"description":"The block to add (one of read, write, read_only or metadata)"
2323
}
2424
}
2525
}

server/src/internalClusterTest/java/org/elasticsearch/blocks/SimpleBlocksIT.java

Lines changed: 30 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
import org.elasticsearch.action.ActionRequestValidationException;
2424
import org.elasticsearch.action.admin.indices.create.CreateIndexResponse;
2525
import org.elasticsearch.action.admin.indices.exists.indices.IndicesExistsResponse;
26+
import org.elasticsearch.action.admin.indices.readonly.AddIndexBlockRequestBuilder;
2627
import org.elasticsearch.action.admin.indices.settings.put.UpdateSettingsRequestBuilder;
2728
import org.elasticsearch.action.index.IndexRequestBuilder;
2829
import org.elasticsearch.action.index.IndexResponse;
@@ -187,6 +188,9 @@ public void testAddBlocksWhileExistingBlocks() {
187188
ensureGreen("test");
188189

189190
for (APIBlock otherBlock : APIBlock.values()) {
191+
if (otherBlock == APIBlock.READ_ONLY_ALLOW_DELETE) {
192+
continue;
193+
}
190194

191195
for (APIBlock block : Arrays.asList(APIBlock.READ, APIBlock.WRITE)) {
192196
try {
@@ -226,20 +230,20 @@ public void testAddBlocksWhileExistingBlocks() {
226230

227231
public void testAddBlockToMissingIndex() {
228232
IndexNotFoundException e = expectThrows(IndexNotFoundException.class, () -> client().admin().indices()
229-
.prepareAddBlock(randomFrom(APIBlock.values()),"test").get());
233+
.prepareAddBlock(randomAddableBlock(), "test").get());
230234
assertThat(e.getMessage(), is("no such index [test]"));
231235
}
232236

233237
public void testAddBlockToOneMissingIndex() {
234238
createIndex("test1");
235239
final IndexNotFoundException e = expectThrows(IndexNotFoundException.class,
236-
() -> client().admin().indices().prepareAddBlock(randomFrom(APIBlock.values()),"test1", "test2").get());
240+
() -> client().admin().indices().prepareAddBlock(randomAddableBlock(), "test1", "test2").get());
237241
assertThat(e.getMessage(), is("no such index [test2]"));
238242
}
239243

240244
public void testCloseOneMissingIndexIgnoreMissing() throws Exception {
241245
createIndex("test1");
242-
final APIBlock block = randomFrom(APIBlock.values());
246+
final APIBlock block = randomAddableBlock();
243247
try {
244248
assertBusy(() -> assertAcked(client().admin().indices().prepareAddBlock(block, "test1", "test2")
245249
.setIndicesOptions(lenientExpandOpen())));
@@ -251,13 +255,20 @@ public void testCloseOneMissingIndexIgnoreMissing() throws Exception {
251255

252256
public void testAddBlockNoIndex() {
253257
final ActionRequestValidationException e = expectThrows(ActionRequestValidationException.class,
254-
() -> client().admin().indices().prepareAddBlock(randomFrom(APIBlock.values())).get());
258+
() -> client().admin().indices().prepareAddBlock(randomAddableBlock()).get());
255259
assertThat(e.getMessage(), containsString("index is missing"));
256260
}
257261

258262
public void testAddBlockNullIndex() {
259263
expectThrows(NullPointerException.class,
260-
() -> client().admin().indices().prepareAddBlock(randomFrom(APIBlock.values()), (String[])null));
264+
() -> client().admin().indices().prepareAddBlock(randomAddableBlock(), (String[])null));
265+
}
266+
267+
public void testCannotAddReadOnlyAllowDeleteBlock() {
268+
createIndex("test1");
269+
final AddIndexBlockRequestBuilder request = client().admin().indices().prepareAddBlock(APIBlock.READ_ONLY_ALLOW_DELETE, "test1");
270+
final ActionRequestValidationException e = expectThrows(ActionRequestValidationException.class, request::get);
271+
assertThat(e.getMessage(), containsString("read_only_allow_delete block is for internal use only"));
261272
}
262273

263274
public void testAddIndexBlock() throws Exception {
@@ -268,7 +279,7 @@ public void testAddIndexBlock() throws Exception {
268279
indexRandom(randomBoolean(), false, randomBoolean(), IntStream.range(0, nbDocs)
269280
.mapToObj(i -> client().prepareIndex(indexName, "zzz").setId(String.valueOf(i)).setSource("num", i)).collect(toList()));
270281

271-
final APIBlock block = randomFrom(APIBlock.values());
282+
final APIBlock block = randomAddableBlock();
272283
try {
273284
assertAcked(client().admin().indices().prepareAddBlock(block, indexName));
274285
assertIndexHasBlock(block, indexName);
@@ -288,7 +299,7 @@ public void testSameBlockTwice() throws Exception {
288299
indexRandom(randomBoolean(), false, randomBoolean(), IntStream.range(0, randomIntBetween(1, 10))
289300
.mapToObj(i -> client().prepareIndex(indexName, "zzz").setId(String.valueOf(i)).setSource("num", i)).collect(toList()));
290301
}
291-
final APIBlock block = randomFrom(APIBlock.values());
302+
final APIBlock block = randomAddableBlock();
292303
try {
293304
assertAcked(client().admin().indices().prepareAddBlock(block, indexName));
294305
assertIndexHasBlock(block, indexName);
@@ -310,7 +321,7 @@ public void testAddBlockToUnassignedIndex() throws Exception {
310321
assertThat(clusterState.metadata().indices().get(indexName).getState(), is(IndexMetadata.State.OPEN));
311322
assertThat(clusterState.routingTable().allShards().stream().allMatch(ShardRouting::unassigned), is(true));
312323

313-
final APIBlock block = randomFrom(APIBlock.values());
324+
final APIBlock block = randomAddableBlock();
314325
try {
315326
assertAcked(client().admin().indices().prepareAddBlock(block, indexName));
316327
assertIndexHasBlock(block, indexName);
@@ -331,7 +342,7 @@ public void testConcurrentAddBlock() throws InterruptedException {
331342
final CountDownLatch startClosing = new CountDownLatch(1);
332343
final Thread[] threads = new Thread[randomIntBetween(2, 5)];
333344

334-
final APIBlock block = randomFrom(APIBlock.values());
345+
final APIBlock block = randomAddableBlock();
335346

336347
try {
337348
for (int i = 0; i < threads.length; i++) {
@@ -366,7 +377,7 @@ public void testAddBlockWhileIndexingDocuments() throws Exception {
366377
final String indexName = randomAlphaOfLength(10).toLowerCase(Locale.ROOT);
367378
createIndex(indexName);
368379

369-
final APIBlock block = randomFrom(APIBlock.values());
380+
final APIBlock block = randomAddableBlock();
370381

371382
int nbDocs = 0;
372383

@@ -411,7 +422,7 @@ public void testAddBlockWhileDeletingIndices() throws Exception {
411422
final List<Thread> threads = new ArrayList<>();
412423
final CountDownLatch latch = new CountDownLatch(1);
413424

414-
final APIBlock block = randomFrom(APIBlock.values());
425+
final APIBlock block = randomAddableBlock();
415426

416427
Consumer<Exception> exceptionConsumer = t -> {
417428
Throwable cause = ExceptionsHelper.unwrapCause(t);
@@ -489,4 +500,12 @@ static void assertIndexHasBlock(APIBlock block, final String... indices) {
489500
public static void disableIndexBlock(String index, APIBlock block) {
490501
disableIndexBlock(index, block.settingName());
491502
}
503+
504+
/**
505+
* The read-only-allow-delete block cannot be added via the add index block API; this method chooses randomly from the values that
506+
* the add index block API does support.
507+
*/
508+
private static APIBlock randomAddableBlock() {
509+
return randomValueOtherThan(APIBlock.READ_ONLY_ALLOW_DELETE, () -> randomFrom(APIBlock.values()));
510+
}
492511
}

server/src/main/java/org/elasticsearch/action/admin/indices/readonly/AddIndexBlockRequest.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,9 @@ public ActionRequestValidationException validate() {
6363
if (CollectionUtils.isEmpty(indices)) {
6464
validationException = addValidationError("index is missing", validationException);
6565
}
66+
if (block == APIBlock.READ_ONLY_ALLOW_DELETE) {
67+
validationException = addValidationError("read_only_allow_delete block is for internal use only", validationException);
68+
}
6669
return validationException;
6770
}
6871

test/framework/src/main/java/org/elasticsearch/test/ESIntegTestCase.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1572,7 +1572,8 @@ public static void disableIndexBlock(String index, String block) {
15721572

15731573
/** Enables an index block for the specified index */
15741574
public static void enableIndexBlock(String index, String block) {
1575-
if (randomBoolean()) {
1575+
if (IndexMetadata.APIBlock.fromSetting(block) == IndexMetadata.APIBlock.READ_ONLY_ALLOW_DELETE || randomBoolean()) {
1576+
// the read-only-allow-delete block isn't supported by the add block API so we must use the update settings API here.
15761577
Settings settings = Settings.builder().put(block, true).build();
15771578
client().admin().indices().prepareUpdateSettings(index).setSettings(settings).get();
15781579
} else {

0 commit comments

Comments
 (0)