Skip to content

Use plugin class loader for the operations of IcebergPageSource#13078

Merged
findepi merged 2 commits intotrinodb:masterfrom
findinpath:iceberg-class-loader-safe
Jul 20, 2022
Merged

Use plugin class loader for the operations of IcebergPageSource#13078
findepi merged 2 commits intotrinodb:masterfrom
findinpath:iceberg-class-loader-safe

Conversation

@findinpath
Copy link
Copy Markdown
Contributor

@findinpath findinpath commented Jul 4, 2022

Description

The problem of using the App ClassLoader instead of the Plugin ClassLoader was uncovered within: 4753ecc

Since then, there are multiple operations that relate to core SQL functionality on Iceberg connector.
See for reference:

This PR makes use of wrapper classes in order to hide the aspect related to
ensuring the usage of the plugin class loader before calling the
page source operations.

NOTE that this PR fixed the class loader safe handling only for the Iceberg connector. Other connectors need to be eventually handled separately as well.

Is this change a fix, improvement, new feature, refactoring, or other?

Fix

Is this a change to the core query engine, a connector, client library, or the SPI interfaces? (be specific)

Iceberg connector

How would you describe this change to a non-technical end user or system administrator?

Basic SQL statements like DELETE or CREATE TABLE AS are failing on Iceberg due to the fact that engine refuses to make use of the app class loader in situations where it is expected to work with the plugin class loader.
This PR ensures that this condition is met for the Iceberg connector operations.

Related issues, pull requests, and links

Relates to the PR: #13036

The mentioned PR tries to handle in a generic fashion the issue of dealing with a class loader safe Connector PageSource.
However, Iceberg deals with an UpdatablePageSource which would imply using instanceof checks within ClassLoaderSafeConnectorPageSourceProvider.

Documentation

(x) No documentation is needed.
( ) Sufficient documentation is included in this PR.
( ) Documentation PR is available with #prnumber.
( ) Documentation issue #issuenumber is filed, and can be handled later.

Release notes

( ) No release notes entries required.
(x) Release notes entries required with the following suggested text:

# Iceberg
* Fix query failure when reading an Iceberg table with deletion files. ({issue}`13035`)

Fixes #13035

Copy link
Copy Markdown
Member

@losipiuk losipiuk left a comment

Choose a reason for hiding this comment

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

Generally LGTM but should you change the tests which expose the problem you are fixing.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Do you need to wrap internal method calls or would just this be enough:

        Supplier<ConnectorPageSink> positionDeleteSink = () -> {
            try (ThreadContextClassLoader ignored = new ThreadContextClassLoader(classLoader)) {
                // Make sure we are using the PluginClassLoader when initialising and using the sink instance
                return new IcebergPositionDeletePageSink(
                        split.getPath(),
                        partitionSpec,
                        partition,
                        locationProvider,
                        fileWriterFactory,
                        hdfsEnvironment,
                        hdfsContext,
                        jsonCodec,
                        session,
                        split.getFileFormat(),
                        table.getStorageProperties(),
                        split.getFileRecordCount());
            };

The alternative mathces what we do in ClassLoaderSafeConnectorPageSourceProvider

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I think it is enough to either do try/catch here or use ClassLoaderSafeConnectorPageSink

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

try/catch is used for initialising the IcebergPositionDeletePageSink.
I thought that the ClassLoaderSafeConnectorPageSink wrapper is needed because the page sink is used within the methods of UpdateablePageSource (deleteRows/updateRows).
However, in the meantime I've wrapped the UpdateablePageSource with a class loader safe wrapper, so probably there is no need anymore to do the wrapping for the page sinks as well.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

If you use ClassLoaderSafeConnectorPageSink there is no nee to do this try/catch here it is done inside

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Same here, maybe I am missing something but I think it is enough to use ClassLoaderSafeConnectorPageSink without try/catch above. You can see for example how ClassLoaderSafeConnectorPageSinkProvider is used

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Also I don't understand why here you did this double wrapping and below with IcebergPageSource you didn't

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

@findinpath
Copy link
Copy Markdown
Contributor Author

After addressing the changes I got to a similar state as what is present in #13036
I still need to find relevant tests to pinpoint the classloader issue to accompany this PR.

cc @homar

@findinpath findinpath force-pushed the iceberg-class-loader-safe branch from 42375b4 to 11ad9f3 Compare July 4, 2022 19:11
@findinpath findinpath changed the title Ensure usage of plugin class loader for Iceberg page source and sink Use plugin class loader for the operations of IcebergPageSource Jul 4, 2022
Copy link
Copy Markdown
Member

@electrum electrum left a comment

Choose a reason for hiding this comment

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

Let’s change ConfigurationInstantiator to simply use the correct class loader.

@findinpath
Copy link
Copy Markdown
Contributor Author

Let’s change ConfigurationInstantiator to simply use the correct class loader.

Can you please detail this request?

In the master version of Trino, the methods of IcebergPageSource are called from within the engine and they use therefore the AppClassLoader. This is why I opted to call the the methods on IcebergPageSource through a class loader safe UpdateableSource wrapper.

There are (at the moment) two ConfigurationInstantiator classes. (one in hive, one in phoenix).
The changes from 4753ecc were thought to uncover possible class loader related issues that would otherwise lead to bogus class not found errors.

@findinpath
Copy link
Copy Markdown
Contributor Author

@electrum I have added a new commit to the PR which is supposed to ensure the usage of the plugin class loader while instantiating a Configuration object.

This commit undoes part of changes from the commit 4753ecc

cc @findepi

@findinpath findinpath requested a review from electrum July 6, 2022 11:38
@codope codope mentioned this pull request Jul 13, 2022
@lhofhansl
Copy link
Copy Markdown
Member

It seems that Iceberg (v2) support has to be considered non-functional without this (and #13112).

@findepi findepi requested review from electrum and removed request for electrum July 19, 2022 15:13
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

test with TestClassLoaderSafeWrappers

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

remove @ForClassLoaderSafe.
the class is not going to be used in Guice.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

use io.trino.spi.classloader.ThreadContextClassLoader

(add trino-spi as a dep)

Make use of wrapper class `ClassLoaderSafeUpdatablePageSource` in order to
hide the aspect related to ensuring the usage of the plugin class loader
before calling the `IcebergPageSource` operations.
During the instantiation Configuration captures current thread context class loader.
Ensure that the class loader captured corresponds to the plugin class loader.
@findinpath
Copy link
Copy Markdown
Contributor Author

In the context of accepting these changes, do we still need the class loader related changes made into RollbackToSnapshotProcedure:

// this line guarantees that classLoader that we stored in the field will be used inside try/catch
// as we captured reference to PluginClassLoader during initialization of this class
// we can use it now to correctly execute the procedure
try (ThreadContextClassLoader ignored = new ThreadContextClassLoader(classLoader)) {
SchemaTableName schemaTableName = new SchemaTableName(schema, table);
Table icebergTable = catalogFactory.create(clientSession.getIdentity()).loadTable(clientSession, schemaTableName);
icebergTable.manageSnapshots().setCurrentSnapshot(snapshotId).commit();
}

?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Development

Successfully merging this pull request may close these issues.

Reading Iceberg table with delete files sometimes fail due to invalid class loader

6 participants