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

[CALCITE-4787] Replace ImmutableBeans with Immutables #2531

Merged
merged 1 commit into from
Oct 4, 2021

Conversation

jacques-n
Copy link
Contributor

Replace the use of reflection/dynamic proxies with the AnnotationProcessor provided by Immutables

NOTE: This is an initial patch that only changes one ImmutableBean to Immutables to show what the changes look like

@jcamachor
Copy link
Contributor

jcamachor commented Sep 20, 2021

This seems like a good idea (rather than relying on our own code). I assume we should also deprecate ImmutableBeans if we want to move in this direction.

@jacques-n
Copy link
Contributor Author

I assume we should also deprecate ImmutableBeans if we want to move in this direction.

Do we consider this an external api? I figured we would just delete...

@jcamachor
Copy link
Contributor

jcamachor commented Sep 21, 2021

I mentioned that because ImmutableBeans is part of the core, publicly accessible and was introduced a few releases ago in 1.22, so I am not sure anyone is relying on it and it seems to be relied upon by other projects. My understanding is that in most (all?) of these cases, we mark as deprecated and delete the classes / APIs after a couple of releases.

@jacques-n
Copy link
Contributor Author

I mentioned that because ImmutableBeans is part of the core, publicly accessible and was introduced a few releases ago in 1.22, so I am not sure anyone is relying on it (not sure they should). My understanding is that in most (all?) of these cases, we mark as deprecated and delete the classes / APIs after a couple of releases. If that is not the case, it should be fine to simply delete it.

It isn't marked as internal/experimental so I guess deprecated is the right approach. Sadness. I was so hopeful this would be a net negative patch. I love those!

core/build.gradle.kts Outdated Show resolved Hide resolved
@@ -360,7 +353,7 @@ public SqlPrettyWriter() {

/** Creates a {@link SqlWriterConfig} with Calcite's SQL dialect. */
public static SqlWriterConfig config() {
return CONFIG;
return SqlWriterConfig.of().withDialect(CalciteSqlDialect.DEFAULT);
Copy link
Contributor

Choose a reason for hiding this comment

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

I guess this is a slight change of behavior as this is not a constant but a new instance each time?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

fixed.

@@ -433,4 +424,6 @@ SqlWriterConfig withWhereListItemsOnSeparateLines(
/** Wrap always. Items are on separate lines. */
TALL
}

static SqlWriterConfig DEFAULT = ImmutableSqlWriterConfig.DEFAULT;
Copy link
Contributor

@jamesstarr jamesstarr Sep 21, 2021

Choose a reason for hiding this comment

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

Do we need this? Couldn't we simply reference ImmutableSqlWriterConfig.DEFAULT directly in SqlPrettyWriter.config? Or alternatively deprecate the config function SqlPrettyWriter?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Modified. This isn't included anymore

@julianhyde
Copy link
Contributor

As @jacques-n said, this is a minimal change - in fact it's just a proof-of-concept. The real change is going to affect hundreds of files, and dozens of lines in each, because there are 142 sub-classes of RelRule.Config. And these configs are public APIs.

Removing the as method is a net positive.

Removing the with methods is a massive negative because of the disruption to dependent projects.

Does Immutables have a mode where it generates copy-on-write beans (with with methods) as opposed to builders? That's the crux of the problem here.

Is there anything in Immutables that converts a bean into a builder (with the same property values)? That will be necessary.

@vlsi
Copy link
Contributor

vlsi commented Sep 22, 2021

An interesting side effect is that build fails at Travis: https://app.travis-ci.com/github/apache/calcite/jobs/538524980#L633

It might be related to a relatively "old" Java 1.8 (Travis uses 1.8u252 vs GitHub 1.8u302)

javac 1.8.0_252
...
java.lang.OutOfMemoryError: GC overhead limit exceeded
	at com.sun.tools.javac.code.Types.subst(Types.java:2984)
	at com.sun.tools.javac.comp.Infer$InferenceContext.asUndetVar(Infer.java:2064)
	at com.sun.tools.javac.comp.Resolve$MethodCheckContext.compatible(Resolve.java:967)
	at com.sun.tools.javac.comp.Check.checkType(Check.java:546)
	at com.sun.tools.javac.comp.Attr$ResultInfo.check(Attr.java:482)

@jacques-n
Copy link
Contributor Author

jacques-n commented Sep 22, 2021

Just a small correction @julianhyde

Removing the with methods is a massive negative because of the disruption to dependent projects.

That's incorrect interpretation of the proposed changes. The with methods remain without any disruption. Only the RelRule.Config.as method and the RelRule.Config.EMPTY singleton would be removed. The latter is removed because it doesn't make sense without the as method.

@julianhyde
Copy link
Contributor

Ah, so is https://www.diffchecker.com/zqTmEKdz not minimal? Could you use withers rather than a builder when initializing DEFAULT?

@jacques-n
Copy link
Contributor Author

jacques-n commented Sep 22, 2021

Ah, so is https://www.diffchecker.com/zqTmEKdz not minimal? Could you use withers rather than a builder when initializing DEFAULT?

Good point. Yes, it could be less impactful. The key difference is that you must use a builder if a property is marked as non-nullable and has no default (since you can't use a 'withFoo' operation until you have a fully valid pojo). When I did the change I was thinking that one or more of those properties were required and not yet default defined. I'll do an update to clarify.

@julianhyde
Copy link
Contributor

julianhyde commented Sep 22, 2021

(I removed this comment. Let's have the conversation in JIRA.)

@jacques-n
Copy link
Contributor Author

Updated simpler example: https://www.diffchecker.com/Ba5a0agP

This is possible because the current definition of RelRule.Config declares a default for the OperandTransformer. It's a little icky because the default throws an exception and I'd prefer to delegate the error on missing value to the generated code just like any other missing value.

@asereda-gs
Copy link
Member

Does Immutables have a mode where it generates copy-on-write beans (with with methods) as opposed to builders? That's the crux of the problem here.

Yes. ImmutableBean will have with method which acts as copy-on-write.

Is there anything in Immutables that converts a bean into a builder (with the same property values)? That will be necessary.

One can use from() method of a builder:

ImmutableBean.builder()
    .from(other) // merges attribute value into builder
    .value("foo")
   .build();

@jacques-n jacques-n marked this pull request as draft September 22, 2021 22:57
@jacques-n
Copy link
Contributor Author

Converting to draft until I have a newer patch ready to review.

core/build.gradle.kts Outdated Show resolved Hide resolved
@asereda-gs
Copy link
Member

The key difference is that you must use a builder if a property is marked as non-nullable and has no default (since you can't use a 'withFoo' operation until you have a fully valid pojo).

You can use @Value.Parameter (which generates static method of(param1, param2)) to avoid using builder when a class can be constructed from few parameters.

@jacques-n jacques-n force-pushed the immutables branch 3 times, most recently from e319c38 to 11262c9 Compare September 26, 2021 17:38
@jacques-n
Copy link
Contributor Author

@vlsi, I can't figure out what is the right way to express the annotationProcessor dependency constraint in the bom. Any chance you could tell me how to do that?

@vlsi
Copy link
Contributor

vlsi commented Sep 27, 2021

@jacques-n , please try the following in

calcite/build.gradle.kts

Lines 302 to 306 in e533f4b

plugins.withId("java-library") {
dependencies {
"implementation"(platform(project(":bom")))
}
}

     plugins.withId("java-library") {
         dependencies {
+            "annotationProcessor"(platform(project(":bom")))
             "implementation"(platform(project(":bom")))
         }
     }

I am not sure if testAnnotationProcessor is needed there or not.

@jacques-n
Copy link
Contributor Author

jacques-n commented Sep 27, 2021

Thanks @vlsi, that resolved it. Just needed annotationProcessor, not testAnnotationProcessor.

Scratch that, testAnnotationProcessor is also required.

@jacques-n jacques-n marked this pull request as ready for review September 27, 2021 16:16
bom/build.gradle.kts Outdated Show resolved Hide resolved
@jacques-n jacques-n force-pushed the immutables branch 2 times, most recently from 47861a4 to 47448dd Compare September 29, 2021 05:20
core/build.gradle.kts Outdated Show resolved Hide resolved
core/build.gradle.kts Outdated Show resolved Hide resolved
core/build.gradle.kts Outdated Show resolved Hide resolved
@jacques-n jacques-n force-pushed the immutables branch 2 times, most recently from bd7fb01 to 2fd74ec Compare September 30, 2021 16:29
@jacques-n
Copy link
Contributor Author

Hey @vlsi, I've pulled out use of immutables in test for this first patch. It's already pretty large. I also cleaned up the gradle stuff. LMK if you think it looks good to merge. Can do a test patch as follow-up.

core/build.gradle.kts Outdated Show resolved Hide resolved
@jacques-n
Copy link
Contributor Author

@vlsi , updated to change condition of task. It seems to work the same in both cases (original and new condition) but I'm happy changing it.

@@ -14,7 +14,7 @@
# See the License for the specific language governing permissions and
# limitations under the License.
#
org.gradle.jvmargs=-Xmx512m -XX:MaxMetaspaceSize=512m
org.gradle.jvmargs=-XX:+UseG1GC -Xmx512m -XX:MaxMetaspaceSize=512m
Copy link
Contributor

Choose a reason for hiding this comment

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

Why G1GC?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

If you profile the gradle daemon build using old and g1, g1 performs better. It more effectively clears things away. The patch builds with either though.

In general, the gradle settings seem low given the amount of continuous memory builds take. In many cases I see non trivial amounts of time against gc while profiling.

@@ -16,6 +16,9 @@
*/
import com.github.vlsi.gradle.crlf.CrLfSpec
import com.github.vlsi.gradle.crlf.LineEndings
import com.github.vlsi.gradle.ide.dsl.settings
import com.github.vlsi.gradle.ide.dsl.taskTriggers
import org.jetbrains.kotlin.backend.common.onlyIf
Copy link
Contributor

Choose a reason for hiding this comment

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

this onlyIf looks weird

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Not clear if you're asking for a change. It works and was the way I found others doing this. Keep in mind that this is my first effort in gradle and kotlin.

Copy link
Contributor

Choose a reason for hiding this comment

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

Please drop onlyIf import. The package name org.jetbrains.kotlin.backend.common looks as if it has something to do with kotlin backend which is not something you want to use here. I belive the import has been added by mistake. You should call Task#onlyIf method which should not require an import.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

You were right. IDE (not)helpfulness. Fixed.

Comment on lines 206 to 207
// only if we aren't running compileJava, since doing twice fails.
onlyIf { project.gradle.taskGraph.hasTask("compileJava") }
Copy link
Contributor

Choose a reason for hiding this comment

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

by the way, compileJava is available from sourceSet.getCompileTaskName("java"), so onlyIf could be moved to configureAnnotationSet

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Nice, updated.

@jacques-n jacques-n force-pushed the immutables branch 2 times, most recently from 82d1983 to c1e5c33 Compare September 30, 2021 18:58
options.compilerArgs.add("-proc:only")
org.gradle.api.plugins.internal.JvmPluginsHelper.configureAnnotationProcessorPath(sourceSet, sourceSet.java, options, project)
// only if we aren't running compileJava, since doing twice fails.
onlyIf { project.gradle.taskGraph.hasTask(sourceSet.getCompileTaskName("java")) }
Copy link
Contributor

Choose a reason for hiding this comment

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

I belive hasTask condition should be reversed.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Nice catch.

The jvm bug is apparently only triggered in the test kotlin/java combination (which is why the build still worked here).

Fixed now.

- Move core rules to use Immutables instead of ImmutableBeans
- Update ImmutableBeans to ignore default property return values used
  by Immutables
- Adjust EnumerableProjectToCalcRule so it doesn't try to use proxy
  behavior with non-empty config
- Update MaterializedViewProjectAggregateRule.DEFAULT so it sets
  fastBailout config property
- Update SqlValidator, SqlWriter, SqlParser, SqlToRelConverter,
  RelDecorrelator, Hoist and RelBuilder to use Immutables for
  configuration
- Switch gradle to use G1 Garbage collector to improve GC performance
- Update ConverterRule to have default properties since properties
  were declared required but had no default values.
- Update gradle build to automatically generate annotation source on
  Intellij and Eclipse sync operations
- Add travis_wait to jdk11 checkerframework build to avoid premature
  termination due to core compile time.
- Make ExchangeRemoveConstantKeysRule and ValuesReduceRule consistent
  with SubqueryRemoveRule by making MatchHandler type parameter concrete.

Co-authored-by: Vladimir Sitnikov <sitnikov.vladimir@gmail.com>
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.

7 participants