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

apply multi-tenancy and sdk client in Connector (Create + Get + Delete) #3382

Merged
merged 1 commit into from
Jan 13, 2025

Conversation

dhrubo-os
Copy link
Collaborator

@dhrubo-os dhrubo-os commented Jan 11, 2025

Description

[apply multi-tenancy and sdk client in Connector (Create + Get + Delete)]

By default, this is single tenant. If we enable multi-tenancy settings, then the request will expect an header x-tenant-id. And one tenant won't be able to access other tenant's resource.

Related Issues

Resolves #[Issue number to be closed when this PR is merged]

Check List

  • New functionality includes testing.
  • New functionality has been documented.
  • API changes companion pull request created.
  • Commits are signed per the DCO using --signoff.
  • Public documentation issue/PR created.

By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 license.
For more information on following Developer Certificate of Origin and signing off your commits, please check here.

@dhrubo-os dhrubo-os changed the title apply multi-tenancy and sdk client in Connector (Create + Get + Delete) [Draft] apply multi-tenancy and sdk client in Connector (Create + Get + Delete) Jan 11, 2025
Signed-off-by: Dhrubo Saha <dhrubo@amazon.com>
Copy link
Member

@dbwiddis dbwiddis left a comment

Choose a reason for hiding this comment

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

Generally LGTM, with some comments/questions.

client.execute(MLModelDeleteAction.INSTANCE, mlModelDeleteRequest, ActionListener.wrap(deleteResponse -> {
listener.onResponse(deleteResponse);
}, listener::onFailure));
client.execute(MLModelDeleteAction.INSTANCE, mlModelDeleteRequest, ActionListener.wrap(listener::onResponse, listener::onFailure));
Copy link
Member

Choose a reason for hiding this comment

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

I'm missing the need to wrap the action listener here without doing anything else with it.

(Same comment for multiple of these.)

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Sorry I didn't clearly follow you here.

Copy link
Collaborator

Choose a reason for hiding this comment

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

if you are not writing custom OnResponse or onFailure rule, you can do similar

client.execute(MLModelDeleteAction.INSTANCE, mlModelDeleteRequest, listener);

Copy link
Member

Choose a reason for hiding this comment

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

^^^ what @mingshl said. No need to wrap it, it adds no value.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Well I haven't changed much here. Just added minor changes of code refactoring what intellij editor suggested :D. So I'll keep that way for now. If we need to change, I'll raise another PR only targeting to remove the wrapper.

deleteConnector(connectorId, tenantId, actionFuture);
return actionFuture;
}

void deleteConnector(String connectorId, ActionListener<DeleteResponse> listener);
Copy link
Member

Choose a reason for hiding this comment

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

Do we want this one to delegate to the other one with a default (null) tenant ID?

Comment on lines +245 to +247
if (streamInputVersion.onOrAfter(VERSION_2_19_0)) {
this.tenantId = input.readOptionalString();
}
Copy link
Member

Choose a reason for hiding this comment

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

Does this work with BWC tests on 3.x branch? I seem to remember having to commit with CURRENT and wait until 2.x backport to make this change and once it's on 2.19.0-SNAPSHOT changing it to this on main.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Yeah I think it works. Main vs 2.x

Copy link
Member

Choose a reason for hiding this comment

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

Yeah it needs to eventually be that way.

The issue is for BWC tests on this PR; if this change isn't merged to 2.x yet, it won't try to send/read that optional string on 2.19.0-SNAPSHOT, but a 3.0.0 node will detect that the stream is 2.19.0 and try to send/read. So rolling BWC will fail until it's backported.

@Getter
String connectorId;
private final String connectorId;
private final String tenantId;
Copy link
Member

Choose a reason for hiding this comment

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

making this final creates a lot of extra noise elsewhere.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Could you please clarify in more detail what kind of noise you are referring to?

Copy link
Member

Choose a reason for hiding this comment

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

Having to do the else block below, for example.

this.tenantId = input.readOptionalString();
} else {
this.tenantId = null;
}
Copy link
Member

Choose a reason for hiding this comment

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

If you choose to keep tenantId final this may be more readable with a ternary operator

@dhrubo-os dhrubo-os changed the title [Draft] apply multi-tenancy and sdk client in Connector (Create + Get + Delete) apply multi-tenancy and sdk client in Connector (Create + Get + Delete) Jan 13, 2025
* @param encryptor encryptor
* @param tenantId tenantId
*/
void initModel(MLModel model, Map<String, Object> params, Encryptor encryptor, String tenantId);
Copy link
Collaborator

Choose a reason for hiding this comment

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

Not sure why we need this new dedicated interface only for multi-tenant. Could we just include the tenantId as a parameter in the existing params Map?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Good call out. Yeah Initially I wanted to have a separate interface for multi-tenancy but then I ended up having tenantId in the Encryptor class. I will remove this interface class in the next follow up PR if that's ok.

Copy link
Collaborator

Choose a reason for hiding this comment

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

Sure, I am OK.

client.execute(MLModelDeleteAction.INSTANCE, mlModelDeleteRequest, ActionListener.wrap(deleteResponse -> {
listener.onResponse(deleteResponse);
}, listener::onFailure));
client.execute(MLModelDeleteAction.INSTANCE, mlModelDeleteRequest, ActionListener.wrap(listener::onResponse, listener::onFailure));
Copy link
Collaborator

Choose a reason for hiding this comment

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

if you are not writing custom OnResponse or onFailure rule, you can do similar

client.execute(MLModelDeleteAction.INSTANCE, mlModelDeleteRequest, listener);

searchRequest,
ActionListener.wrap(searchResponse -> { listener.onResponse(searchResponse); }, listener::onFailure)
);
client.execute(MLModelSearchAction.INSTANCE, searchRequest, ActionListener.wrap(listener::onResponse, listener::onFailure));
Copy link
Collaborator

Choose a reason for hiding this comment

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

same here, if you are not writing custom OnResponse or onFailure rule, you can do similar

client.execute(MLModelSearchAction.INSTANCE, searchRequest, listener);

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

same comment here.

try (ThreadContext.StoredContext context = client.threadPool().getThreadContext().stashContext()) {
ActionListener<DeleteResponse> restoringListener = ActionListener.runBefore(actionListener, context::restore);
SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();
sourceBuilder.query(QueryBuilders.matchQuery(MLModel.CONNECTOR_ID_FIELD, connectorId));
Copy link
Collaborator

Choose a reason for hiding this comment

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

will suggest using term query here because when finding connector id, it's case sensitive, and non-tokenized, it's better to use exact match with term.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Well the scope of this PR is to add multi-tenancy related changes. I haven't modified the search functionality. So I'll keep this as it is.

} catch (Exception e) {
log.error("Failed to validate Access for connector:" + connectorId, e);
listener.onFailure(e);
}
}

public void validateConnectorAccess(
Copy link
Collaborator

Choose a reason for hiding this comment

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

Will the above method be removed once all APIs are refactored?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Yes

+ Arrays.toString(modelIds.toArray(new String[0])),
RestStatus.CONFLICT
)
new OpenSearchStatusException("Failed to parse search response", RestStatus.INTERNAL_SERVER_ERROR)
Copy link
Collaborator

Choose a reason for hiding this comment

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

If the response has this exception message Failed to parse search response, user might be confused as to why search is being invoked in delete.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Do you have any suggestion?

Comment on lines +38 to +41
public Connector getMlConnector() {
return mlConnector;
}

Copy link
Collaborator

Choose a reason for hiding this comment

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

Use @Getter instead of this function?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Sure, will address that in the subsequent PR

) {

sdkClient
.getDataObjectAsync(getDataObjectRequest, client.threadPool().executor(GENERAL_THREAD_POOL))
Copy link
Collaborator

Choose a reason for hiding this comment

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

It turns out we are using the single general_thread_pool for all create/get/delete actions. The general_thread_pool is for some infrequent usages with small pool size which might cause some performance issues in edge cases.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Do you have any suggestion what should we use here?

Copy link
Member

Choose a reason for hiding this comment

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

I ran into issues on flow framework with too-small threadpools I was repurposing. I plan to create a new threadpool just for these client wrapper calls.

Copy link
Collaborator

Choose a reason for hiding this comment

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

Yeah, agree with Dan on having a new pool with a larger size which would be better.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

FixedExecutorBuilder sdkClientThreadPool = new FixedExecutorBuilder(
                settings,
                SDK_CLIENT_THREAD_POOL,
                OpenSearchExecutors.allocatedProcessors(settings) * 4,
                10000,
                ML_THREAD_POOL_PREFIX + SDK_CLIENT_THREAD_POOL,
                false
        );

I'm planning to add this in my subsequent PR and to use this for all the sdkClient related operations. Does this sound good?

Copy link
Collaborator

Choose a reason for hiding this comment

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

Yeah, LGTM, thanks.

@@ -45,8 +51,8 @@ public List<Route> routes() {
@Override
protected RestChannelConsumer prepareRequest(RestRequest request, NodeClient client) throws IOException {
String connectorId = request.param(PARAMETER_CONNECTOR_ID);

MLConnectorDeleteRequest mlConnectorDeleteRequest = new MLConnectorDeleteRequest(connectorId);
String tenantId = getTenantID(mlFeatureEnabledSetting.isMultiTenancyEnabled(), request);
Copy link
Collaborator

Choose a reason for hiding this comment

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

Can this setting be modified by users?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

This won't be a dynamic settings [ref]

Copy link
Collaborator

Choose a reason for hiding this comment

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

Got it, thanks for the ref.

@mingshl
Copy link
Collaborator

mingshl commented Jan 13, 2025

LGTM, please link the following PR to address the remaining comments.

@dhrubo-os dhrubo-os merged commit bcb00d1 into opensearch-project:main Jan 13, 2025
9 checks passed
opensearch-trigger-bot bot pushed a commit that referenced this pull request Jan 14, 2025
…e) (#3382)

Signed-off-by: Dhrubo Saha <dhrubo@amazon.com>
(cherry picked from commit bcb00d1)
dhrubo-os added a commit that referenced this pull request Jan 14, 2025
…e) (#3382) (#3385)

Signed-off-by: Dhrubo Saha <dhrubo@amazon.com>
(cherry picked from commit bcb00d1)

Co-authored-by: Dhrubo Saha <dhrubo@amazon.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants