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

Per-environment configuration for ServerEnvironment parts #1274

Merged

Conversation

serhii-lekariev
Copy link
Contributor

@serhii-lekariev serhii-lekariev commented May 19, 2020

EnvSetting

This PR introduces a new class for values that may differ between environments - EnvSetting<P>, which allows users to configure their value so that it's different between environments like so:

EnvSetting<StorageFactory> storage = new EnvSetting<>();
StorageFactory factory = InMemoryStorageFactory.newInstance();

// `Tests` is a type exposed by `base`. 
storage.use(factory, Tests.type());

Optional<StorageFactory> productionStorage = storage.value(Production.type());
assertThat(productionStorage).isEmpty();

Optional<StorageFactory> storageForTests = storage.value(Tests.type());
assertThat(storageForTests).isPresent();
assertThat(storageForTests.get()).isSameInstanceAs(factory);

ServerEnvironment changes

Previously, ServerEnvironment allowed to use different StorageFactory instances for different environments.

This functionality has been expanded to also allow configuration of:

  • Delivery: defaults to Delivery.local() if no value has been configured;
  • TracerFactory: does not default to any value, it's accessor still returns an Optional<TracerFactory>;
  • TransportFactory: defaults to an InMemoryTransportFactory for tests, still requires explicit configuration for all other environments.

Deprecation notes

The following ServerEnvironment methods have been deprecated:

  • configureStorageForTests: prefer use(storage, Tests.class);
  • configureStorage: prefer use(storage, Production.class); // or any other env type;
  • configureDelivery: prefer use(delivery, Production.class); // or any other env type.

Removed methods

ServerEnvironment#configureTransport(TransportFactory) was removed. ServerEnvironment#use(TransportFactory, Class<? extends EnvironmentType) can be used instead.

Fixes #615.

@serhii-lekariev serhii-lekariev self-assigned this May 19, 2020
@serhii-lekariev serhii-lekariev requested a review from armiol May 19, 2020 12:23
@serhii-lekariev serhii-lekariev marked this pull request as ready for review May 19, 2020 12:23
@codecov
Copy link

codecov bot commented May 19, 2020

Codecov Report

Merging #1274 into master will decrease coverage by 0.05%.
The diff coverage is 81.51%.

@@             Coverage Diff              @@
##             master    #1274      +/-   ##
============================================
- Coverage     91.12%   91.07%   -0.06%     
- Complexity     4696     4710      +14     
============================================
  Files           605      606       +1     
  Lines         14920    14992      +72     
  Branches        852      851       -1     
============================================
+ Hits          13596    13654      +58     
- Misses         1057     1072      +15     
+ Partials        267      266       -1     

Copy link
Contributor

@armiol armiol left a comment

Choose a reason for hiding this comment

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

@serhii-lekariev please see my comments.

Also, as you change is an API change, please make sure to bump the own library version.

* @param <P>
* the type of value
*/
public class EnvironmentDependantValue<P> {
Copy link
Contributor

Choose a reason for hiding this comment

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

This is way too complex for a name. Please think of some other options; if it's difficult to pick, leave a comment with any ideas that come to your mind. We'll then discuss.

Also, this class should 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.

ConfigurableValue is too generic, but sort of true?

It's a wrapper around two values, both of which may be absent, and those values have "production" and "testing" semantics, which I would call two different environments.

public class EnvironmentDependantValue<P> {

private @Nullable P productionValue;
private final UnaryOperator<P> productionWrappingFunction;
Copy link
Contributor

Choose a reason for hiding this comment

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

Please shorten the name.

Copy link
Contributor

Choose a reason for hiding this comment

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

Why is this resolved?

}

@Test
@DisplayName("wrap test storage")
void wrapTestStorage() {
ServerEnvironment serverEnv = ServerEnvironment.instance();
StorageFactory testStorage = InMemoryStorageFactory.newInstance();
serverEnv.configureStorageForTests(testStorage);
serverEnv.configureStorage().tests(testStorage);
Copy link
Contributor

Choose a reason for hiding this comment

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

I would rather have something like

 serverEnv.useStorageFactory(factory).inTests();

That's a lot closer to the way we speak.

@serhii-lekariev serhii-lekariev requested a review from armiol May 20, 2020 12:14
Copy link
Contributor

@armiol armiol left a comment

Choose a reason for hiding this comment

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

@serhii-lekariev please see my comments.

public class EnvironmentDependantValue<P> {

private @Nullable P productionValue;
private final UnaryOperator<P> productionWrappingFunction;
Copy link
Contributor

Choose a reason for hiding this comment

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

Why is this resolved?

@@ -313,11 +326,17 @@ public void close() throws Exception {
if (tracerFactory != null) {
tracerFactory.close();
}
if (transportFactory != null) {
transportFactory.close();
if (transportFactories.production()
Copy link
Contributor

Choose a reason for hiding this comment

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

To elimitate the repeating if (... .production().isPresent()) { ...} please introduce the corresponding API to the EnvironmentDependantValue itself.

I can see at least four cases only in this class.

@serhii-lekariev serhii-lekariev requested a review from armiol May 20, 2020 13:47
…ances if possible.

Add a hint to Javadocs. Partly convert the tests to a `Class<? extends EnvironmentType>` API.
Copy link
Contributor

@armiol armiol left a comment

Choose a reason for hiding this comment

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

@serhii-lekariev please see my comments.

* <p>For example:
* <pre>
*
* {@literal EnvSetting <StorageFactory>} storageFactory = new EnvSetting<>();
Copy link
Contributor

Choose a reason for hiding this comment

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

Let's kill that extra space character.

Copy link
Contributor

Choose a reason for hiding this comment

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

The extra symbol is still there.

Copy link
Contributor

Choose a reason for hiding this comment

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

Try to generate Javadocs themselves. Let's see if new EnvSetting<>(); portion renders as expected.

}

/**
* Represents an operation over a value that returns no result and may finish with an error.
Copy link
Contributor

Choose a reason for hiding this comment

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

This name is way too generic for this particular case. That's why I proposed to name it more specifically.

*
* <p>If it is not set, runs the specified supplier, configures and returns the supplied value.
*/
V assignOrDefault(Supplier<V> defaultValue, EnvironmentType type) {
Copy link
Contributor

Choose a reason for hiding this comment

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

Now:

private final EnvSetting<MyFactory> setting = new EnvSetting<>();

//...


return setting.assignOrDefault(    // this method says it writes, but in fact it reads — and sometimes writes.
        () -> create();
        type;
);

Proposed change:

private final EnvSetting<MyFactory> setting = 
                new EnvSetting<>(type, () -> create());  // default `fallback` value for `type`.

private final EnvSetting<AnotherFactory> anotherSetting = new EnvSetting<>();  // no fallbacks.



//...

MyFactory value = setting.value();  // mandatory setting, e.g. `StorageFactory`; throws ISE if `empty()`.

Optional<AnotherFactory> optionalValue = anotherSetting.optionalValue();  // optional setting, e.g. `Tracer`.                 

@serhii-lekariev
Copy link
Contributor Author

@armiol, PTAL again. Build fails because this branch depends on the base 1.5.18. I can either decrease the version here, or wait until base is merged and published.

@serhii-lekariev serhii-lekariev requested a review from armiol June 12, 2020 15:49
* <p>For example:
* <pre>
*
* {@literal EnvSetting <StorageFactory>} storageFactory = new EnvSetting<>();
Copy link
Contributor

Choose a reason for hiding this comment

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

The extra symbol is still there.

* <p>For example:
* <pre>
*
* {@literal EnvSetting <StorageFactory>} storageFactory = new EnvSetting<>();
Copy link
Contributor

Choose a reason for hiding this comment

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

Try to generate Javadocs themselves. Let's see if new EnvSetting<>(); portion renders as expected.

* the type of value
*/
@Internal
public final class EnvSetting<V> {
Copy link
Contributor

Choose a reason for hiding this comment

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

I don't understand why this class is public.

@@ -334,6 +338,10 @@ public static Delivery local() {
* <p>The returned instance of {@code Delivery} is configured to use
* {@linkplain UniformAcrossAllShards#singleShard() the single shard}.
*
* <p>To construct a {@code Delivery} instance, a {@code StorageFactory} is needed.
* If it was not configured in the {@code ServerEnvironment}, uses a new {@code
Copy link
Contributor

Choose a reason for hiding this comment

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

Who uses?

@@ -221,6 +225,9 @@ public DeliveryBuilder setInboxStorage(InboxStorage inboxStorage) {
*
* <p>If none set, the storage is initialized by the {@code StorageFactory} specific for
* this {@code ServerEnvironment}.
*
* <p>If no {@code StorageFactory} is present in the {@code ServerEnvironment}, a new
* {@code InMemoryStorage} is used.
Copy link
Contributor

Choose a reason for hiding this comment

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

There is no such thing as InMemoryStorage. Please review your changeset for this copy-paste mistake.

.isInstanceOf(SystemAwareStorageFactory.class);
assertThat(((SystemAwareStorageFactory) serverEnvironment.storageFactory()).delegate())
.isSameInstanceAs(storageFactory);

Copy link
Contributor

Choose a reason for hiding this comment

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

Please kill this line.

void reset() {
serverEnvironment.reset();
Environment.instance()
.register(new Local());
Copy link
Contributor

Choose a reason for hiding this comment

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

Not sure if this piece is migrated to the latest base API yet, but you may easily use a ctor instead of a new instance here.

Environment.instance()
.register(new Local());
Environment.instance()
.setTo(new Local());
Copy link
Contributor

Choose a reason for hiding this comment

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

See how weird this is? You have several pieces of the same inconvenient code in this test. How much easier that would be if you could treat setTo as registration as well?

@@ -25,6 +25,6 @@
* as we want to manage the versions in a single source.
*/

val spineBaseVersion: String by extra("1.5.12")
val spineBaseVersion: String by extra("1.5.18")
val spineTimeVersion: String by extra("1.5.12")
Copy link
Contributor

Choose a reason for hiding this comment

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

Let's migrate to the latest time once it's published.

Also, let's set the core-java version to be the same as base.

@serhii-lekariev serhii-lekariev requested a review from armiol June 13, 2020 14:48
@serhii-lekariev
Copy link
Contributor Author

@armiol, PTAL again.

Copy link
Contributor

@armiol armiol left a comment

Choose a reason for hiding this comment

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

@serhii-lekariev LGTM in general. Please see my comments. Also, make sure to use the latest base and time versions before this PR is merged.

@@ -47,6 +47,29 @@

/**
* The server conditions and configuration under which the application operates.
*
* <h1>Configuration</h1>
* <p>Some parts of the {@code ServerEnvironment} can be customized based on the {@code
Copy link
Contributor

Choose a reason for hiding this comment

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

Please have an empty line above.

* .use(testingStorageFactory, Tests.class)
* .use(memoizingTracerFactory, Production.class)
* </pre>
* A custom environment type may also be used:
Copy link
Contributor

Choose a reason for hiding this comment

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

Add <p> and one more empty line above.

* // if `Staging` expects dependencies to its constructor.
* ServerEnvironment.use(inMemoryStorageFactory, Staging.class);
* </pre>
* If {@code Staging} is {@link Environment#type() enabled}, the specified value is going to be
Copy link
Contributor

Choose a reason for hiding this comment

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

Start with <p> and add an empty line above.

* <p>Some parts of the {@code ServerEnvironment} can be customized based on the {@code
* EnvironmentType}. To do so, one of the overloads of the {@code use} method can be called.
* Two environment types exist out of the box: {@link Tests} and {@link Production}. For example:
* <pre>
Copy link
Contributor

Choose a reason for hiding this comment

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

Let's have an empty line above.

*
* <pre>
* // This `Supplier` is calculated only once.
* {@literal Supplier<StorageFactory>} fallbackStorage = InMemoryStorageFactory::newInstance;
*
* {@literal EnvSetting<StorageFactory>} setting = {@literal new EnvSetting<>(Tests.class, fallbackStorage);}
* {@literal EnvSetting<StorageFactory>} setting =
Copy link
Contributor

Choose a reason for hiding this comment

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

Can we fit this into a single literal and in a single line?

{@literal EnvSetting<StorageFactory> setting = new EnvSetting<>(Tests.class, fallbackStorage);}

@serhii-lekariev serhii-lekariev merged commit ed31f7e into master Jun 15, 2020
@serhii-lekariev serhii-lekariev deleted the per-environment-storage-configuration-server-env branch June 16, 2020 14:09
@yuri-sergiichuk
Copy link
Contributor

@serhii-lekariev so ServerEnvironment.configureTransport is removed completely. Maybe worth mention that? That's kind of a breaking change (a tiny one, but still).

@serhii-lekariev
Copy link
Contributor Author

@serhii-lekariev so ServerEnvironment.configureTransport is removed completely. Maybe worth mention that? That's kind of a breaking change (a tiny one, but still).

I've updated the PR description.

@dmitrykuzmin dmitrykuzmin mentioned this pull request Sep 8, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

TransportFactorySwitch for test and production mode
3 participants