Skip to content
This repository has been archived by the owner on Nov 14, 2024. It is now read-only.

Excavator: Upgrades Baseline to the latest version #5169

Merged
merged 13 commits into from
Jan 26, 2021

Conversation

svc-excavator-bot
Copy link
Collaborator

excavator is a bot for automating changes across repositories.

Changes produced by the roomba/latest-baseline-oss check.

To enable or disable this check, please contact the maintainers of Excavator.

@svc-excavator-bot svc-excavator-bot force-pushed the roomba/latest-baseline-oss branch from 012da33 to 4c08e41 Compare December 29, 2020 01:54
@svc-excavator-bot svc-excavator-bot force-pushed the roomba/latest-baseline-oss branch 3 times, most recently from 7b38285 to 1859042 Compare January 5, 2021 22:48
@svc-excavator-bot svc-excavator-bot force-pushed the roomba/latest-baseline-oss branch from 6dab4d8 to b60ffa5 Compare January 6, 2021 13:23
@@ -23,7 +23,9 @@
* determine that it is safe (or, at least, not obviously unsafe) to service requests.
*/
@Value.Immutable
@SuppressWarnings("ClassInitializationDeadlock")
public interface TransactionManagerConsistencyResult {
Copy link
Contributor

Choose a reason for hiding this comment

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

The immutable is used outside of package

@@ -23,10 +23,11 @@
@JsonSerialize(as = ImmutableStreamStorePersistenceConfiguration.class)
@JsonDeserialize(as = ImmutableStreamStorePersistenceConfiguration.class)
@Value.Immutable
@SuppressWarnings("ClassInitializationDeadlock")
public interface StreamStorePersistenceConfiguration {
Copy link
Contributor

Choose a reason for hiding this comment

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

The immutable is used outside this package.

.failedUrlCooldown(Duration.ofMillis(1))
.maxNumRetries(5)
.clientQoS(ClientConfiguration.ClientQoS.DANGEROUS_DISABLE_SYMPATHETIC_CLIENT_QOS)
.build();

Copy link
Contributor

Choose a reason for hiding this comment

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

This constant is only used for testing.

Copy link
Contributor

Choose a reason for hiding this comment

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

I will assume that you verified this via a search on github? If so, great!

Copy link
Contributor

Choose a reason for hiding this comment

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

I verified there are. no usages on internal github.

@@ -19,6 +19,8 @@
import org.immutables.value.Value;

@Value.Immutable
@Value.Style(visibility = Value.Style.ImplementationVisibility.PACKAGE)
@SuppressWarnings("ClassInitializationDeadlock")
abstract class Token {
@Nullable
Copy link
Contributor

Choose a reason for hiding this comment

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

Instead of breaking the interface contract by moving the constant, we are setting the impl visibility to PACKAGE (this is done to avoid initializing the immutable first) and suppressing the error.

Copy link
Contributor

Choose a reason for hiding this comment

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

To clarify, is it the case that:

  1. setting the implementation visibility prevents the actual issue of initialisation deadlock?
  2. the check is not smart enough to determine this, and so it requires the warning to be suppressed?
  3. we cannot do this elsewhere because in other cases, the impl cannot be made package private? (In other words, why did we go with this solution here but not elsewhere, and vice versa?)

Copy link
Contributor

Choose a reason for hiding this comment

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

setting the implementation visibility only helps avoid external usages which could potentially cause a deadlock and within the package we have to be sensible while using the immutable.
The check is still right in this case to break, which is why we suppress it.
This cannot be done in the cases where the subclass has usages in other packages. In those cases we move out the constants to a separate util. To have a separate a util only for a constant seems a but janky which is why we have take the first approach if possible.

Copy link
Contributor

Choose a reason for hiding this comment

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

If we try to initialise ImmutableClass before Class, that will require us to initialise Class, but because of the static constant, that requires an initialisation of ImmutableClass and this deadlocks because there is a lock on ImmutableClass.

If you ensure that Class gets initialised first, then everything works fine, so by making access to ImmutableClass package private (to prevent external badness) and exposing a builder on the Class interface to be used instead of the ImmutableClass builder directly, we can avoid this issue

Copy link
Contributor

Choose a reason for hiding this comment

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

Package private may still be accessed externally. I considered special casing in the errorprone rule, but it would require additional validation that nothing accesses package-private generated immutables outside of the defining class, which we've seen more frequently than one would hope.

@@ -23,6 +23,7 @@
import java.util.Set;
import java.util.TreeMap;

@SuppressWarnings("ClassInitializationDeadlock")
public abstract class KeyValueServiceInstrumentation {
Copy link
Contributor

Choose a reason for hiding this comment

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

The CassandraKeyValueServiceInstrumentation is being used in tests in a different package and can not be made package private, would appreciate alternative ideas for fixing the ClassInitDeadlock issue here.

Copy link
Contributor

Choose a reason for hiding this comment

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

You can still make the generated code package private, and provide a builder in this class to avoid the issue outside of the package

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 only used in benchmarking, and not worth the effort to clean it up

Copy link
Contributor

@Jolyon-S Jolyon-S left a comment

Choose a reason for hiding this comment

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

🏁 00:10 | ⌛ 00:10

Thanks for doing this @sudiksha27. I think that there are two viable approaches for most of the fixes - deprecating and moving out those arbitrary constants (with a suppression of the warning in the mean time), and making package private and suppressing the warnings. However, I'm not too certain as to why we should use both approaches - are there times when one makes sense but not the other?

StreamStorePersistenceConfiguration DEFAULT_CONFIG =
ImmutableStreamStorePersistenceConfiguration.builder().build();

Copy link
Contributor

Choose a reason for hiding this comment

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

nit: unnecessary diff?

.failedUrlCooldown(Duration.ofMillis(1))
.maxNumRetries(5)
.clientQoS(ClientConfiguration.ClientQoS.DANGEROUS_DISABLE_SYMPATHETIC_CLIENT_QOS)
.build();

Copy link
Contributor

Choose a reason for hiding this comment

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

I will assume that you verified this via a search on github? If so, great!

@@ -19,6 +19,8 @@
import org.immutables.value.Value;

@Value.Immutable
@Value.Style(visibility = Value.Style.ImplementationVisibility.PACKAGE)
@SuppressWarnings("ClassInitializationDeadlock")
abstract class Token {
@Nullable
Copy link
Contributor

Choose a reason for hiding this comment

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

To clarify, is it the case that:

  1. setting the implementation visibility prevents the actual issue of initialisation deadlock?
  2. the check is not smart enough to determine this, and so it requires the warning to be suppressed?
  3. we cannot do this elsewhere because in other cases, the impl cannot be made package private? (In other words, why did we go with this solution here but not elsewhere, and vice versa?)

public class PostgresKeyValueServiceInstrumentation extends KeyValueServiceInstrumentation {
class PostgresKeyValueServiceInstrumentation extends KeyValueServiceInstrumentation {
Copy link
Contributor

Choose a reason for hiding this comment

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

Is this breaking?

Comment on lines 38 to 39
@Value.Style(visibility = Value.Style.ImplementationVisibility.PACKAGE)
@SuppressWarnings("ClassInitializationDeadlock")
Copy link
Contributor

Choose a reason for hiding this comment

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

nit: order is not consistent with the order of annotations on some of the other classes

Copy link
Contributor

@gmaretic gmaretic left a comment

Choose a reason for hiding this comment

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

Can you make sure that you have the builders on the interfaces / abstract classes that have the constants and that the immutable builders are not used directly?

@@ -23,7 +23,9 @@
* determine that it is safe (or, at least, not obviously unsafe) to service requests.
*/
@Value.Immutable
@SuppressWarnings("ClassInitializationDeadlock")
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
@SuppressWarnings("ClassInitializationDeadlock")
@SuppressWarnings("ClassInitializationDeadlock")
@Value.Style(visibility = Value.Style.ImplementationVisibility.PACKAGE)

Copy link
Contributor

Choose a reason for hiding this comment

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

And add a builder if necessary for outside use, the idea is not to expose the generated immutable

Copy link
Contributor

Choose a reason for hiding this comment

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

Curious why suppress an error-prone check for potential class initialization deadlock (see palantir/gradle-baseline#1598 that @carterkozak just added )? I'm assuming this is a fairly common pattern in many uses of immutables where there's either some constant defined as the empty object or similar. Do we need to consider pulling these types of constants out to a separate class (e.g. TransactionManagerConsistencyResults) or lazy initialization get of a memoized supplier?

@@ -23,7 +23,9 @@
@JsonSerialize(as = ImmutableStreamStorePersistenceConfiguration.class)
@JsonDeserialize(as = ImmutableStreamStorePersistenceConfiguration.class)
@Value.Immutable
@SuppressWarnings("ClassInitializationDeadlock")
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
@SuppressWarnings("ClassInitializationDeadlock")
@SuppressWarnings("ClassInitializationDeadlock")
@Value.Style(visibility = Value.Style.ImplementationVisibility.PACKAGE)

@@ -19,6 +19,8 @@
import org.immutables.value.Value;

@Value.Immutable
@Value.Style(visibility = Value.Style.ImplementationVisibility.PACKAGE)
@SuppressWarnings("ClassInitializationDeadlock")
abstract class Token {
@Nullable
Copy link
Contributor

Choose a reason for hiding this comment

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

If we try to initialise ImmutableClass before Class, that will require us to initialise Class, but because of the static constant, that requires an initialisation of ImmutableClass and this deadlocks because there is a lock on ImmutableClass.

If you ensure that Class gets initialised first, then everything works fine, so by making access to ImmutableClass package private (to prevent external badness) and exposing a builder on the Class interface to be used instead of the ImmutableClass builder directly, we can avoid this issue

@sudiksha27
Copy link
Contributor

We came up with two options →

  1. Parent class does not initialize subclass → Moving out those static immutable constants to a separate class. Still removing these constants from the interface would break the interface. For public interfaces, we can still keep the constant in the original place as well, mark it as deprecated and suppress the error. The we can remove the deprecated constants in later release.
  2. Ensure immutable class is initialized first → Set impl visibility of the ImmutableClass to package private (to prevent external badness), expose a builder on the interface to be used instead of the ImmutableClass builder directly and suppress the error. This could also be a breaking change for currently public immutableClass. We also have to be sensible that the immutable class is not initialized first within the package.

Given both changes can be breaking, we're inclined towards solution 1 as it will completely remove the code that is vulnerable to ClassInitDeadlock.

Copy link
Contributor

@gmaretic gmaretic left a comment

Choose a reason for hiding this comment

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

LGTM

@gmaretic gmaretic merged commit e75db01 into develop Jan 26, 2021
@svc-autorelease
Copy link
Collaborator

Released 0.289.0

@delete-merged-branch delete-merged-branch bot deleted the roomba/latest-baseline-oss branch January 26, 2021 11:06
@carterkozak
Copy link
Contributor

Looks like this may have raced another PR which added a potential deadlock

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

7 participants