Skip to content

Commit

Permalink
Add UnmodifiableOnRestore property to index.knn setting (#2445)
Browse files Browse the repository at this point in the history
* Add UnmodifiableOnRestore property to index.knn setting

Signed-off-by: AnnTian Shao <[email protected]>

* Update tests with helper methods

Signed-off-by: AnnTian Shao <[email protected]>

* Update test names

Signed-off-by: AnnTian Shao <[email protected]>

* Add to changeLog and fixes to tests

Signed-off-by: AnnTian Shao <[email protected]>

---------

Signed-off-by: AnnTian Shao <[email protected]>
Co-authored-by: AnnTian Shao <[email protected]>
(cherry picked from commit 1d2c4c0)
  • Loading branch information
anntians authored and AnnTian Shao committed Jan 30, 2025
1 parent c61eff4 commit 8d5e0d3
Show file tree
Hide file tree
Showing 4 changed files with 145 additions and 1 deletion.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
* Fixing it to retrieve space_type from index setting when both method and top level don't have the value. [#2374](https://github.com/opensearch-project/k-NN/pull/2374)
* Fixing the bug where setting rescore as false for on_disk knn_vector query is a no-op (#2399)[https://github.com/opensearch-project/k-NN/pull/2399]
* Fixing bug where mapping accepts both dimension and model-id (#2410)[https://github.com/opensearch-project/k-NN/pull/2410]
* Fixing the bug to prevent index.knn setting from being modified or removed on restore snapshot (#2445)[https://github.com/opensearch-project/k-NN/pull/2445]
### Infrastructure
* Updated C++ version in JNI from c++11 to c++17 [#2259](https://github.com/opensearch-project/k-NN/pull/2259)
* Upgrade bytebuddy and objenesis version to match OpenSearch core and, update github ci runner for macos [#2279](https://github.com/opensearch-project/k-NN/pull/2279)
Expand Down
9 changes: 8 additions & 1 deletion src/main/java/org/opensearch/knn/index/KNNSettings.java
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@
import static org.opensearch.common.settings.Setting.Property.IndexScope;
import static org.opensearch.common.settings.Setting.Property.NodeScope;
import static org.opensearch.common.settings.Setting.Property.Final;
import static org.opensearch.common.settings.Setting.Property.UnmodifiableOnRestore;
import static org.opensearch.common.unit.MemorySizeValue.parseBytesSizeValueOrHeapRatio;
import static org.opensearch.core.common.unit.ByteSizeValue.parseBytesSizeValue;
import static org.opensearch.knn.common.featureflags.KNNFeatureFlags.getFeatureFlags;
Expand Down Expand Up @@ -271,7 +272,13 @@ public class KNNSettings {
/**
* This setting identifies KNN index.
*/
public static final Setting<Boolean> IS_KNN_INDEX_SETTING = Setting.boolSetting(KNN_INDEX, false, IndexScope, Final);
public static final Setting<Boolean> IS_KNN_INDEX_SETTING = Setting.boolSetting(
KNN_INDEX,
false,
IndexScope,
Final,
UnmodifiableOnRestore
);

/**
* index_thread_quantity - the parameter specifies how many threads the nms library should use to create the graph.
Expand Down
108 changes: 108 additions & 0 deletions src/test/java/org/opensearch/knn/index/RestoreSnapshotIT.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
/*
* Copyright OpenSearch Contributors
* SPDX-License-Identifier: Apache-2.0
*/

package org.opensearch.knn.index;

import org.opensearch.client.Request;
import org.opensearch.client.Response;
import org.opensearch.client.ResponseException;
import org.opensearch.common.xcontent.json.JsonXContent;
import org.opensearch.core.xcontent.XContentBuilder;
import org.opensearch.knn.KNNRestTestCase;
import org.junit.Before;
import org.junit.Test;
import lombok.SneakyThrows;
import static org.hamcrest.Matchers.containsString;

public class RestoreSnapshotIT extends KNNRestTestCase {

private String index = "test-index";;
private String snapshot = "snapshot-" + index;
private String repository = "repo";

@Before
@SneakyThrows
public void setUp() {
super.setUp();
setupSnapshotRestore(index, snapshot, repository);
}

@Test
@SneakyThrows
public void testKnnSettingIsModifiable_whenRestore_thenSuccess() {
// valid restore
XContentBuilder restoreCommand = JsonXContent.contentBuilder().startObject();
restoreCommand.field("indices", index);
restoreCommand.field("rename_pattern", index);
restoreCommand.field("rename_replacement", "restored-" + index);
restoreCommand.startObject("index_settings");
{
restoreCommand.field("knn.model.index.number_of_shards", 1);
}
restoreCommand.endObject();
restoreCommand.endObject();
Request restoreRequest = new Request("POST", "/_snapshot/" + repository + "/" + snapshot + "/_restore");
restoreRequest.addParameter("wait_for_completion", "true");
restoreRequest.setJsonEntity(restoreCommand.toString());

final Response restoreResponse = client().performRequest(restoreRequest);
assertEquals(200, restoreResponse.getStatusLine().getStatusCode());
}

@Test
@SneakyThrows
public void testKnnSettingIsUnmodifiable_whenRestore_thenFailure() {
// invalid restore
XContentBuilder restoreCommand = JsonXContent.contentBuilder().startObject();
restoreCommand.field("indices", index);
restoreCommand.field("rename_pattern", index);
restoreCommand.field("rename_replacement", "restored-" + index);
restoreCommand.startObject("index_settings");
{
restoreCommand.field("index.knn", false);
}
restoreCommand.endObject();
restoreCommand.endObject();
Request restoreRequest = new Request("POST", "/_snapshot/" + repository + "/" + snapshot + "/_restore");
restoreRequest.addParameter("wait_for_completion", "true");
restoreRequest.setJsonEntity(restoreCommand.toString());
final ResponseException error = expectThrows(ResponseException.class, () -> client().performRequest(restoreRequest));
assertThat(error.getMessage(), containsString("cannot modify UnmodifiableOnRestore setting [index.knn]" + " on restore"));
}

@Test
@SneakyThrows
public void testKnnSettingCanBeIgnored_whenRestore_thenSuccess() {
// valid restore
XContentBuilder restoreCommand = JsonXContent.contentBuilder().startObject();
restoreCommand.field("indices", index);
restoreCommand.field("rename_pattern", index);
restoreCommand.field("rename_replacement", "restored-" + index);
restoreCommand.field("ignore_index_settings", "knn.model.index.number_of_shards");
restoreCommand.endObject();
Request restoreRequest = new Request("POST", "/_snapshot/" + repository + "/" + snapshot + "/_restore");
restoreRequest.addParameter("wait_for_completion", "true");
restoreRequest.setJsonEntity(restoreCommand.toString());
final Response restoreResponse = client().performRequest(restoreRequest);
assertEquals(200, restoreResponse.getStatusLine().getStatusCode());
}

@Test
@SneakyThrows
public void testKnnSettingCannotBeIgnored_whenRestore_thenFailure() {
// invalid restore
XContentBuilder restoreCommand = JsonXContent.contentBuilder().startObject();
restoreCommand.field("indices", index);
restoreCommand.field("rename_pattern", index);
restoreCommand.field("rename_replacement", "restored-" + index);
restoreCommand.field("ignore_index_settings", "index.knn");
restoreCommand.endObject();
Request restoreRequest = new Request("POST", "/_snapshot/" + repository + "/" + snapshot + "/_restore");
restoreRequest.addParameter("wait_for_completion", "true");
restoreRequest.setJsonEntity(restoreCommand.toString());
final ResponseException error = expectThrows(ResponseException.class, () -> client().performRequest(restoreRequest));
assertThat(error.getMessage(), containsString("cannot remove UnmodifiableOnRestore setting [index.knn] on restore"));
}
}
28 changes: 28 additions & 0 deletions src/testFixtures/java/org/opensearch/knn/KNNRestTestCase.java
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
import org.apache.hc.core5.http.io.entity.EntityUtils;
import org.apache.hc.core5.net.URIBuilder;
import org.opensearch.Version;
import org.opensearch.common.xcontent.support.XContentMapValues;
import org.opensearch.core.common.bytes.BytesReference;
import org.opensearch.common.xcontent.XContentHelper;
import org.opensearch.core.xcontent.DeprecationHandler;
Expand Down Expand Up @@ -73,6 +74,8 @@
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

import static org.hamcrest.Matchers.hasSize;
import static org.hamcrest.Matchers.notNullValue;
import static org.opensearch.knn.common.KNNConstants.DIMENSION;
import static org.opensearch.knn.common.KNNConstants.ENCODER_PARAMETER_PQ_CODE_SIZE;
import static org.opensearch.knn.common.KNNConstants.ENCODER_PARAMETER_PQ_M;
Expand Down Expand Up @@ -1980,4 +1983,29 @@ protected boolean isApproximateThresholdSupported(final Optional<String> bwcVers
protected static String randomLowerCaseString() {
return randomAlphaOfLengthBetween(MIN_CODE_UNITS, MAX_CODE_UNITS).toLowerCase(Locale.ROOT);
}

@SneakyThrows
protected void setupSnapshotRestore(String index, String snapshot, String repository) {
Request clusterSettingsRequest = new Request("GET", "/_cluster/settings");
clusterSettingsRequest.addParameter("include_defaults", "true");
Response clusterSettingsResponse = client().performRequest(clusterSettingsRequest);
Map<String, Object> clusterSettings = entityAsMap(clusterSettingsResponse);

@SuppressWarnings("unchecked")
List<String> pathRepos = (List<String>) XContentMapValues.extractValue("defaults.path.repo", clusterSettings);
assertThat(pathRepos, notNullValue());
assertThat(pathRepos, hasSize(1));

final String pathRepo = pathRepos.get(0);

// create index
createIndex(index, getDefaultIndexSettings());

// create repo
Settings repoSettings = Settings.builder().put("compress", randomBoolean()).put("location", pathRepo).build();
registerRepository(repository, "fs", true, repoSettings);

// create snapshot
createSnapshot(repository, snapshot, true);
}
}

0 comments on commit 8d5e0d3

Please sign in to comment.