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

What must the set of property names returned by Config#getPropertyNames() consist of? #431

Closed
ljnelson opened this issue Jul 18, 2019 · 29 comments · Fixed by #538
Closed
Assignees
Labels
break-change The change breaks backward compatibility clarification 🏆 A clarification (PR) or request for clarification (issue) in the spec or API incompatible change ⚠️ A change that introduces an incompatibility compared to previous releases; major versions only
Milestone

Comments

@ljnelson
Copy link
Contributor

ljnelson commented Jul 18, 2019

The javadocs for Config#getPropertyNames() say, in their entirety, and I see no other language related to this topic in the specification documents:

Return a collection of property names.
Returns:
the names of all configured keys of the underlying configuration.

It is hard to know what to make of this. Does this mean:

  1. The set of property names returned by Config#getPropertyNames() must consist only of the combination of the sets of property names returned by each ConfigSource present in the return value of Config#getConfigSources() and nothing else
  2. The set of property names returned by Config#getPropertyNames() may contain a subset of the combination of the sets of property names returned by each ConfigSource present in the return value of Config#getConfigSources()
  3. The set of property names returned by Config#getPropertyNames() can be anything the implementor wants, regardless of whether it is represented in part or in whole by any ConfigSource notionally contained by the Config
    …? It must be one of these. The specification does not say, and there is no TCK test.

I am assuming that (1) is the proper answer (modulo any information related to #370). However, given that the specification says nothing and there is no TCK1, (3) is the de facto answer at the moment.

If (1) is the proper answer, should a TCK test exist for this?

If (1) is not the proper answer, why does this method exist?

1 There is

@Test
public void testGetPropertyNames() {
String configKey = "some.arbitrary.key";
String configValue = "value";
System.setProperty(configKey, configValue);
AtomicBoolean foundKey = new AtomicBoolean(false);
config.getConfigSources().forEach(c -> {
if(c.getPropertyNames().contains(configKey)) {
foundKey.set(true);
}
});
Assert.assertTrue(foundKey.get(), "Unable to find property "+configKey);
}
which indirectly tests only that Config#getPropertyNames() must contain at least one key returned by the getPropertyNames() methods of the ConfigSource instances returned by getConfigSources() at the moment of the Config#getPropertyNames() call.

@ljnelson
Copy link
Contributor Author

Related: #370, #371, #395

@Emily-Jiang
Copy link
Member

@ljnelson It should be the following interpretation:

The set of property names returned by Config#getPropertyNames() must consist only of the combination of the sets of property names returned by each ConfigSource present in the return value of Config#getConfigSources() and nothing else

@ljnelson
Copy link
Contributor Author

ljnelson commented Aug 9, 2019

@Emily-Jiang Thanks very much. Shall I contribute a TCK test for this?

@Emily-Jiang
Copy link
Member

@ljnelson please feel free to contribute. Thank you!

@Emily-Jiang
Copy link
Member

Let's decide the real usage for this method as we plan to deprecate ConfigSource.getPropertiesNames and ConfigSource.getProperties.

@dmlloyd
Copy link
Contributor

dmlloyd commented Oct 11, 2019

If you have a configuration which can detect invalid properties within a namespace, as we do in Quarkus, you need a way to know what configuration properties are present. The only way to do that in the MP Config API is by traversing all the property names of all the sources (ConfigSource#getPropertyNames), which is exactly what Config#getPropertyNames does.

Given that there is a clear use case, it does not make sense to talk about deprecation. You'd need an overpoweringly significant reason to justify doing so.

@dmlloyd
Copy link
Contributor

dmlloyd commented Oct 11, 2019

The question of deprecating org.eclipse.microprofile.config.spi.ConfigSource#getProperties is very different. Nothing uses it in the specification (other than the default implementation of the getPropertyNames method), and nothing in our SmallRye implementation uses it either.

@ljnelson
Copy link
Contributor Author

@Emily-Jiang Please do not deprecate Config#getPropertyNames(). There are many use cases where it is used and significant. Additionally, given your earlier comment please do not deprecate ConfigSource#getProperties() either. Or at least please don't deprecate ConfigSource#getPropertyNames().

@jmesnil
Copy link
Contributor

jmesnil commented Oct 18, 2019

Given that there are use case for ConfigSource.getPropertyNames(), we should keep the method.
It seems however there is no objection to deprecate (and plan the removal) of ConfigSource.getProperties().

This would impact ConfigSource.getPropertyNames() which provides a default implementation:

default Set<String> getPropertyNames() {
        return getProperties().keySet();
}

The default implementation should be eventually removed and let the implementation of ConfigSource provides it.

There is also a question about the relevance of Config.getPropertyNames().
Does this method add anything to the API that would not be achievable by iterating on the Config.getConfigSources() and collect their propertyNames?

Finally, if we keep both Config.getPropertyNames() and ConfigSource.getPropertyNames(), would it make sense to align them and use the same types for both. For now, Config.getPropertyNames() returns Iterable<String> (which may contain duplicate property names) while ConfigSource.getPropertyNames() returns a Set<String>.

Shouldn't Config.getPropertyNames() return a Set<String> too so that the collection of property names controlled by this Config does not contain duplicates by construction?

@Emily-Jiang
Copy link
Member

@jmesnil please see the comment from @ljnelson regarding the usage of getProperties. With that, I would leave them along.
As for changing the return type, we can discuss when it is time for a breaking change.

@dmlloyd
Copy link
Contributor

dmlloyd commented Oct 18, 2019

@ljnelson can you please explain why you would need ConfigSource#getProperties() in addition to ConfigSource#getPropertyNames()? I read it as a miscommunication but maybe I am wrong?

@dmlloyd
Copy link
Contributor

dmlloyd commented Oct 18, 2019

FWIW changing the return type doesn't have to be a breaking change. A second method could be added under a different name, with the original delegating to it, or a bridge could be introduced by introducing a non-public superinterface that only has the Iterable<String> variant, with Config then extending that hidden interface overriding the method with one returning Set<String>, and you've preserved binary compatibility as well as source compatibility.

@ljnelson
Copy link
Contributor Author

@dmlloyd What I was referring to was a comment that @Emily-Jiang made in an earlier issue where it was explained that what was intended was that Config#getProperties() should be defined in terms of ConfigSource#getProperties(). Obviously if you remove ConfigSource#getProperties() then you cannot define Config#getProperties() in terms of it.

If we are talking about removing both Config#getProperties() and ConfigSource#getProperties() I have no problem with that.

As mentioned previously please keep Config#getPropertyNames() and define it (I suppose?) in terms of ConfigSource#getPropertyNames().

@dmlloyd
Copy link
Contributor

dmlloyd commented Oct 18, 2019

Ah I see. Well there is no such method as Config#getProperties() so I think we're safe on that account. Our implementation (SmallRye) doesn't actually ever use ConfigSource#getProperties() for any purpose.

@ljnelson
Copy link
Contributor Author

Guess I read carelessly; carry on.

@dmlloyd
Copy link
Contributor

dmlloyd commented Oct 18, 2019

I guess everyone did; no worries :)

@Emily-Jiang I think this means that there is no further reason to prevent deprecation of ConfigSource#getProperties() for removal as @jmesnil has proposed.

@OndroMih
Copy link
Contributor

I see there's a usecase for ConfigSource.getPropertyNames in some cases, especially during initialization. However, we should ensure that the result is not constant and shouldn't be cached for a long time. I'm not sure this is ensured now, os it?
ConfigSources can be mutable and new properties can be added dynamically, so thry don't know the list of all their props at initialization. There are also good usecases to support completely dynamic sources that derive the value for some properties from their name, so a new property can be created as late as an app asks for it.

If we deprecate getProperties and later remove it, we could change the default impl for getPropertyNames to return an empty set. This would indicate that it's OK to return an empty set of properties and still provide values later. Expensive config sources could decide to return an empty or incomplete set of property names and still work.

Even if we leave getProperties, we can provide a default impl that returns an empty set.

I would also consider how calling getPropertyNames/getProperties can slow down the app. If we allow configsources to return an empty/incomplete set of props and still provide more values later, that might solve this problem too.

@smillidge
Copy link
Contributor

These methods are very useful for tooling. For example the Payara administration console uses these methods to display all known properties in some config sources for runtime configuration. I don't believe any of these methods should be deprecated.

@dmlloyd
Copy link
Contributor

dmlloyd commented Oct 21, 2019

I see there's a usecase for ConfigSource.getPropertyNames in some cases, especially during initialization. However, we should ensure that the result is not constant and shouldn't be cached for a long time. I'm not sure this is ensured now, os it?

No this is not ensured. The set should at least be safe to access indefinitely though, and I don't think there's any reason not to specify that it must be thread safe also. But then, there isn't really any reasonable way to put an upper bound on how long users may cache it. To me the simplest implementation in the presence of mutable configuration (discussed in a different topic) will always be a snapshot as it satisfies all the basic requirements here.

If we deprecate getProperties and later remove it, we could change the default impl for getPropertyNames to return an empty set. This would indicate that it's OK to return an empty set of properties and still provide values later. Expensive config sources could decide to return an empty or incomplete set of property names and still work.

Even if we leave getProperties, we can provide a default impl that returns an empty set.

Good idea!

I would also consider how calling getPropertyNames/getProperties can slow down the app. If we allow configsources to return an empty/incomplete set of props and still provide more values later, that might solve this problem too.

I agree we definitely should explicitly allow getPropertyNames to return a subset of the properties.

@dmlloyd
Copy link
Contributor

dmlloyd commented Oct 21, 2019

These methods are very useful for tooling. For example the Payara administration console uses these methods to display all known properties in some config sources for runtime configuration. I don't believe any of these methods should be deprecated.

@smillidge is there a use case in Payara for getProperties that cannot be easily satisfied by getPropertyNames + getValue?

@smillidge
Copy link
Contributor

Yes if you want to display all the values getProperties is more efficient than the iteration. However in our case the number of properties is likely small.

@dmlloyd
Copy link
Contributor

dmlloyd commented Oct 22, 2019

Understood, so using the getPropertyNames approach does work but might be slower for some implementations. This is reasonable.

Maybe we can find a middle ground between having to support a full, random-access Map and having to look up each property value: like an iterable sequence of key-value pairs. Then the getPropertyNames method could (by default) be implemented in terms of that method, and the getProperties method could be deprecated with a default implementation which constructs e.g. a LinkedHashMap consisting of all of the entries from the new method?

Emily-Jiang added a commit to Emily-Jiang/microprofile-config that referenced this issue Nov 22, 2019
Signed-off-by: Emily Jiang <emijiang6@googlemail.com>
Emily-Jiang added a commit that referenced this issue Dec 13, 2019
…hanges

#431 - undo this change as it has binary incompatible changes
@Emily-Jiang
Copy link
Member

Emily-Jiang commented Dec 13, 2019

Back to drawing board:
Based on today's hangout with @dmlloyd @kenfinnigan , one idea is to introduce a better performant method e.g. getNameValuePairs() in this release. In Config 2.0, we can delete getProperties() method. Thoughts?

@Emily-Jiang
Copy link
Member

I've done some javadoc update under #333 . Let's revisit this in Config 2.0 release in June to properly fix this.

@Emily-Jiang Emily-Jiang removed this from the MP Config 1.4 milestone Jan 21, 2020
@dmlloyd dmlloyd added the clarification 🏆 A clarification (PR) or request for clarification (issue) in the spec or API label Mar 4, 2020
@Emily-Jiang
Copy link
Member

I'll redo the PR I did last time.

@Emily-Jiang Emily-Jiang added this to the MP Config 2.0 milestone Mar 5, 2020
@Emily-Jiang Emily-Jiang assigned Emily-Jiang and unassigned dmlloyd Mar 6, 2020
Emily-Jiang added a commit to Emily-Jiang/microprofile-config that referenced this issue Mar 6, 2020
Signed-off-by: Emily Jiang <emijiang6@googlemail.com>
dmlloyd added a commit that referenced this issue Mar 10, 2020
@Emily-Jiang
Copy link
Member

I believe this issue can be closed. Please object in the next couple of days if you think otherwise.

@ljnelson
Copy link
Contributor Author

May I request a one sentence summary of the resolution at the bottom of this issue before it is closed? I am still not sure what the final outcome is, even with #534 being merged. Are there requirements or restrictions on the elements that can be returned in the Set? That must be returned in the Set? Is the Set immutable? Is the method deterministic? Etc.

@dmlloyd
Copy link
Contributor

dmlloyd commented Mar 11, 2020

I do think this needs a little more clarification. I'll work up a more specific PR.

dmlloyd added a commit to dmlloyd/microprofile-config that referenced this issue Mar 11, 2020
dmlloyd added a commit to dmlloyd/microprofile-config that referenced this issue Mar 11, 2020
@dmlloyd
Copy link
Contributor

dmlloyd commented Mar 11, 2020

#538 contains my attempt to clarify the thread safety requirements.

dmlloyd added a commit to dmlloyd/microprofile-config that referenced this issue Mar 11, 2020
dmlloyd added a commit to dmlloyd/microprofile-config that referenced this issue Mar 12, 2020
@Emily-Jiang Emily-Jiang added the break-change The change breaks backward compatibility label May 26, 2020
@Emily-Jiang Emily-Jiang added the incompatible change ⚠️ A change that introduces an incompatibility compared to previous releases; major versions only label Jul 23, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
break-change The change breaks backward compatibility clarification 🏆 A clarification (PR) or request for clarification (issue) in the spec or API incompatible change ⚠️ A change that introduces an incompatibility compared to previous releases; major versions only
Projects
None yet
Development

Successfully merging a pull request may close this issue.

6 participants