Skip to content

Conversation

@eolivelli
Copy link
Contributor

@eolivelli eolivelli commented Aug 11, 2021

  • load the DefaultImplementation only once with reflection
  • use standard Java calls to access the Pulsar Implementation classes
  • reduce the overall overhead of using Reflection
  • clarify the relationship between the API and the Impl classes
  • add explicit linking between the impl and api classes, this way it is easier to read the code and perform refactorings
  • allow to handle correctly the Exception thrown by implementation classes

Motivation

Currently the link between the Pulsar Client API and the Implementation is done at runtime, using Java reflection, in the DefaultImplementation class.

This comes with a few problems:

  • it is not clear the Classloader who is loading the classes of the Implementation, DefaultImplementation relied on "RelectionUtils", with surprising effects we saw during the past months, expecially in Pulsar Functions/Pulsar II
  • the old DefaultImplementation class used to catch all the checked exception and hide them
  • the old reflection approach used "strings" to refer to implementation classes, making it harder to perform refactorings, understand the code flow (by reading), using the help of the IDE
  • using reflection is generally "slower", with this fix we are going to save CPU cycles, and also the JVM will be able to understand better what's happening and (probably) perform more runtime optimizations (at least we are going to save a few method calls and stack...)

Modifications

  • Introduce a new Java interface PulsarClientImplementationBinding that defines the connections from the "public API" and the "implementation", this interface contains all the methods of the old "DefaultImplementation"
  • Move the Implementation to the pulsar-client module, in the concrete class PulsarClientImplementationBindingImpl
  • PulsarClientImplementationBindingImpl can access directly, without reflection all the implementation classes, this is faster and also it allows us to have direct linking (for human reading, for IDE help....)
  • Load the PulsarClientImplementationBindingImpl class only once
  • Rewrite all the references to the old DefaultImplementation.staticMethod(xxx) to DefaultImplementation.getDefaultImplementation().method(xxx)
  • Fix some places in which hidden IOException may have bubbled up breaking things without notice

Verifying this change

This change is a trivial rework / code cleanup without any test coverage.

Documentation

No need for docs

- load the DefaultImplementation only once with reflection
- use standard Java calls to access the Pulsar Implementation classes
- reduce the overall overhead of using Reflection
- clarify the relationsship between the classes
- add explicit linking between the impl and api classes, this way it is easier to read the code and perform refactorings
- allow to handle correctly the Exception thrown by implementation classes
@eolivelli eolivelli marked this pull request as ready for review August 11, 2021 08:50
@eolivelli eolivelli requested a review from ivankelly August 11, 2021 14:39
@eolivelli eolivelli changed the title Java Client: remove usage of reflection for loading Pulsar Implementation classes Java Client: remove usage of reflection while using Pulsar Implementation classes Aug 11, 2021
Copy link
Contributor

@rdhabalia rdhabalia left a comment

Choose a reason for hiding this comment

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

LGTM

Copy link
Contributor

@merlimat merlimat left a comment

Choose a reason for hiding this comment

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

I really like this approach. I just have a question on how we can guarantee that we're picking the right classloader.

}

public ClientBuilder newClientBuilder() {
return new org.apache.pulsar.client.impl.ClientBuilderImpl();
Copy link
Contributor

Choose a reason for hiding this comment

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

Wouldn't this be using the class loader from the current context? How can we be sure it's going to be the same as PulsarClientImplementationBindingImpl?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

See the JVM specs here
https://docs.oracle.com/javase/specs/jvms/se7/html/jvms-5.html
chapter "5.3. Creation and Loading"
look for "If D was defined by a user-defined class loader, then that same user-defined class loader initiates loading of C"

basically if a class "D" (where "D" is not a system class) triggers the loading of a class "C", then the same Classloader that loaded "D" will initiate the loading of "C"

So in this case the same classloader which loaded PulsarClientImplementationBindingImpl will initiate the loading of ClientBuilderImpl if the class has not been loaded yet

Copy link
Contributor

Choose a reason for hiding this comment

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

Got it. thanks!

}

public MessageId newMessageId(long ledgerId, long entryId, int partitionIndex) {
return new org.apache.pulsar.client.impl.MessageIdImpl(ledgerId, entryId, partitionIndex);
Copy link
Contributor

Choose a reason for hiding this comment

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

Couldn't we import the classes here instead of using the full 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.

Fixed.

I left the FQCN only because I converted the class semi automatically, before of this change they where simple strings.

@Anonymitaet Anonymitaet added the doc-not-needed Your PR changes do not impact docs label Aug 12, 2021
@Anonymitaet
Copy link
Member

@eolivelli thanks for providing doc-related info. If your PR does not need to update doc, could you please help label the PR with no-need-doc in the future? Thanks

@eolivelli
Copy link
Contributor Author

@eolivelli thanks for providing doc-related info. If your PR does not need to update doc, could you please help label the PR with no-need-doc in the future? Thanks
sure. thanks for the reminder

@eolivelli
Copy link
Contributor Author

@merlimat I have removed the usage of Fully qualified class names.
I have answered to your question about classloading (I had to verify this in the specs).

After merging this patch I will add new integration tests in Pulsar Functions in order to verify that we are loading correctly all (of most of them) the classes referred by PulsarClientImplementationBindingImpl (Schema.XXX, RecordSchemaBuilder, BatcherBuilder, KeyValueSchema....)

@merlimat merlimat merged commit 683c2d6 into apache:master Aug 12, 2021
bharanic-dev pushed a commit to bharanic-dev/pulsar that referenced this pull request Mar 18, 2022
…tion classes (apache#11636)

* Java Client: remove usage of reflection for loading Pulsar Impl classes
- load the DefaultImplementation only once with reflection
- use standard Java calls to access the Pulsar Implementation classes
- reduce the overall overhead of using Reflection
- clarify the relationsship between the classes
- add explicit linking between the impl and api classes, this way it is easier to read the code and perform refactorings
- allow to handle correctly the Exception thrown by implementation classes

* Fix checkstyle and build

* fix unit test

* Add some javadoc and remove use of fully qualified class names

Co-authored-by: Enrico Olivelli <eolivelli@apache.org>
@eolivelli eolivelli deleted the impl/api-impl-binding branch March 24, 2022 12:28
eolivelli added a commit to eolivelli/pulsar that referenced this pull request Apr 3, 2022
…tion classes (apache#11636)

* Java Client: remove usage of reflection for loading Pulsar Impl classes
- load the DefaultImplementation only once with reflection
- use standard Java calls to access the Pulsar Implementation classes
- reduce the overall overhead of using Reflection
- clarify the relationsship between the classes
- add explicit linking between the impl and api classes, this way it is easier to read the code and perform refactorings
- allow to handle correctly the Exception thrown by implementation classes

* Fix checkstyle and build

* fix unit test

* Add some javadoc and remove use of fully qualified class names

Co-authored-by: Enrico Olivelli <eolivelli@apache.org>
(cherry picked from commit 683c2d6)
eolivelli added a commit to eolivelli/pulsar that referenced this pull request Apr 3, 2022
…tion classes (apache#11636)

* Java Client: remove usage of reflection for loading Pulsar Impl classes
- load the DefaultImplementation only once with reflection
- use standard Java calls to access the Pulsar Implementation classes
- reduce the overall overhead of using Reflection
- clarify the relationsship between the classes
- add explicit linking between the impl and api classes, this way it is easier to read the code and perform refactorings
- allow to handle correctly the Exception thrown by implementation classes

* Fix checkstyle and build

* fix unit test

* Add some javadoc and remove use of fully qualified class names

Co-authored-by: Enrico Olivelli <eolivelli@apache.org>
(cherry picked from commit 683c2d6)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants