Skip to content
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

Add UnmodifiableOnRestore property to index.knn setting #2445

Merged
merged 4 commits into from
Jan 27, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
* Fix filter rewrite logic which was resulting in getting inconsistent / incorrect results for cases where filter was getting rewritten for shards (#2359)[https://github.com/opensearch-project/k-NN/pull/2359]
* 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 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 @@ -269,7 +270,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
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think there are any existing helper methods in OpenSearchRestTestCase/upstream classes for restore snapshot that take in setting updates and throws back validation errors

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.http.client.utils.URIBuilder;
import org.apache.http.util.EntityUtils;
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);
}
}
Loading