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

[Refactoring] Ec2 and GCS plugins build client lazily #31250

Conversation

albertzaharovits
Copy link
Contributor

@albertzaharovits albertzaharovits commented Jun 11, 2018

This PR is the refactoring mentioned here: #29135

TBC the clients (AmazonEC2 and Storage) were already built lazily (and cached and reused) for the afore mentioned plugins. This is the refactoring of that logic. The 'client settings' and the 'client instance' always had to go together which made the code bug prone, i.e. when the settings changed you had to make sure the client was destroyed so that it was lazily rebuilt using the new settings.

For the discovery-ec2 and repository-gcs plugins, it replaces the duality '(map) client settings' - '(map) client instance' with a lazy client supplier. The Supplier encapsulates the settings required to build the client. The lazy supplier caches the returned value once computed. This factors out the memoize supplier behavior (e.g.

) in a Lazy instance.
This change was not required for the repository-azure plugin as it does not caches client instances (hence only the settings are stored) because client instances are not thread-safe. This change cannot be applied to the repository-s3 plugin, until the feature branch reload-secure-store-action is merged, because this plugin explicitly requires the settings to be stored so that they can be overriden from the cluster state (
void overrideCredentialsFromClusterState(AwsS3Service awsService) {
), see also: 17d0155

Originally, the goal for this refactoring was much more ambitious: to have an abstract component that creates and caches clients for all plugins that have clients using secure settings. But this is currently not possible, as the plugin requirements are quite diverse, eg:

  • discovery-ec2 and repository-s3 have Closeable, thread-safe clients, but the repository-azure clients are not thread-safe. repository-gcs clients are thread-safe but not Closeable.
  • the repository plugins use a dictionary of clients, but the discovery-ec2 plugin only uses one client.

Relates #29135
CC @elastic/es-security

@elasticmachine
Copy link
Collaborator

Pinging @elastic/es-core-infra

Copy link
Member

@jaymode jaymode left a comment

Choose a reason for hiding this comment

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

I like the concept; left some requests for changes. Also, we should have unit tests for the lazy class

import java.util.Objects;
import java.util.function.Consumer;

public class Lazy<T, E extends Exception> {
Copy link
Member

Choose a reason for hiding this comment

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

Please add javadocs for this class and all methods. Can we make the class final? Also, what about LazyInitializable as the name

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done

throw new ElasticsearchException("This is unexpected", e);
}
// the count will be decreased by {@codee AmazonEc2Reference#close}
result.incRef();
Copy link
Member

Choose a reason for hiding this comment

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

we increment reference twice on creation?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yes, this was a mistake, good catch. Ideally this should've been catched by a test. I think there's a TODO (if not for this plugin, for S3) about this test missing. I will address in a follow-up PR.

try {
result = super.getOrCompute();
} catch (final Exception e) {
throw new ElasticsearchException("This is unexpected", e);
Copy link
Member

Choose a reason for hiding this comment

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

use throw ExceptionsHelper.convertToRuntime(e);

Copy link
Contributor Author

Choose a reason for hiding this comment

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

good to know, thanks!

// shutdown IdleConnectionReaper background thread
// it will be restarted on new client usage
IdleConnectionReaper.shutdown();
}

private static class LazyAmazonEc2Reference extends Lazy<AmazonEc2Reference, Exception> {
Copy link
Member

Choose a reason for hiding this comment

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

rather than a class for this, can we add a Consumer for the value on get/compute to increment the ref count? Then lazy can be final

Copy link
Contributor Author

Choose a reason for hiding this comment

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

done, I think it's cleaner like you proposed since you don't have to study the super implementation when extending.

return result == null ? maybeCompute(supplier) : result;
}

public synchronized void clear() {
Copy link
Member

Choose a reason for hiding this comment

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

maybe call this reset

@albertzaharovits
Copy link
Contributor Author

Thanks for the review @jaymode . I have polished Lazy -> LazyInitializable by following all your comments.

Can you please take another look?

Copy link
Member

@jaymode jaymode left a comment

Choose a reason for hiding this comment

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

I left a few naming suggestions. Otherwise LGTM

public final class LazyInitializable<T, E extends Exception> {

private final CheckedSupplier<T, E> supplier;
private final Consumer<T> primer;
Copy link
Member

Choose a reason for hiding this comment

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

how about onGet as a name instead of primer


private final CheckedSupplier<T, E> supplier;
private final Consumer<T> primer;
private final Consumer<T> finalizer;
Copy link
Member

Choose a reason for hiding this comment

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

how about onReset instead of finalizer? finalizer makes me think of garbage collection

@albertzaharovits
Copy link
Contributor Author

Thank you for the swift feedback! You are a rare breed of an engineer and a naming oracle 🎩

@albertzaharovits albertzaharovits merged commit 62debd1 into elastic:reload-secure-store-action Jun 16, 2018
@albertzaharovits albertzaharovits deleted the refactor-remove-double-dict branch June 16, 2018 17:56
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
:Core/Infra/Settings Settings infrastructure and APIs >refactoring
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants