Skip to content

Commit d11010b

Browse files
committed
[fix](catalog) restore DLF support for Iceberg and Paimon
1 parent b58b2c5 commit d11010b

30 files changed

Lines changed: 1133 additions & 76 deletions

File tree

‎fe/fe-connector/fe-connector-hms-hive-shade/pom.xml‎

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,22 @@ under the License.
6363
</description>
6464

6565
<dependencies>
66+
<!-- DLF's Hive-3 client must be shaded with the same thrift namespace as the Hive metastore
67+
classes below. Keeping that invariant lets Iceberg load ProxyMetaStoreClient entirely
68+
from its plugin instead of falling back to the removed host-side fat shade. -->
69+
<dependency>
70+
<groupId>com.aliyun.datalake</groupId>
71+
<artifactId>metastore-client-hive3</artifactId>
72+
<version>0.2.14</version>
73+
<optional>true</optional>
74+
<exclusions>
75+
<exclusion><groupId>com.aliyun</groupId><artifactId>tea</artifactId></exclusion>
76+
<exclusion><groupId>com.aliyun</groupId><artifactId>tea-openapi</artifactId></exclusion>
77+
<exclusion><groupId>com.aliyun</groupId><artifactId>tea-util</artifactId></exclusion>
78+
<exclusion><groupId>org.bouncycastle</groupId><artifactId>bcpkix-jdk15on</artifactId></exclusion>
79+
</exclusions>
80+
</dependency>
81+
6682
<!-- hive-standalone-metastore 3.1.3 supplies the REAL metastore implementation:
6783
org.apache.hadoop.hive.metastore.api.* (ThriftHiveMetastore + ~200 structs),
6884
IMetaStoreClient / MetastoreConf / MetaStoreUtils / HadoopThriftAuthBridge / txn.TxnUtils,
@@ -330,6 +346,8 @@ under the License.
330346
do not. commons-lang (2.x) stays for HiveConf.<clinit>. -->
331347
<artifactSet>
332348
<includes>
349+
<include>com.aliyun.datalake:*</include>
350+
<include>com.aliyun:datalake20200710</include>
333351
<include>org.apache.hive:hive-standalone-metastore</include>
334352
<include>org.apache.hive:hive-common</include>
335353
<include>org.apache.hive:hive-serde</include>
@@ -377,6 +395,10 @@ under the License.
377395
<pattern>org.apache.thrift</pattern>
378396
<shadedPattern>shade.doris.hive.org.apache.thrift</shadedPattern>
379397
</relocation>
398+
<relocation>
399+
<pattern>com.aliyun.tea</pattern>
400+
<shadedPattern>shade.doris.hive.com.aliyun.tea</shadedPattern>
401+
</relocation>
380402
<!-- defensive: hive drags an ancient fastutil via some transitives; relocate
381403
it so it cannot shadow the modern it.unimi.dsi:fastutil(-core) on the FE
382404
classpath (NoSuchMethodError). -->

‎fe/fe-connector/fe-connector-iceberg/src/main/java/org/apache/doris/connector/iceberg/IcebergCatalogFactory.java‎

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -354,6 +354,8 @@ public static String resolveCatalogImpl(String catalogType) {
354354
return DorisHiveCatalog.class.getName();
355355
case IcebergCatalogProperties.TYPE_GLUE:
356356
return "org.apache.iceberg.aws.glue.GlueCatalog";
357+
case IcebergCatalogProperties.TYPE_DLF:
358+
return "org.apache.doris.connector.iceberg.dlf.DLFCatalog";
357359
case IcebergCatalogProperties.TYPE_HADOOP:
358360
return "org.apache.iceberg.hadoop.HadoopCatalog";
359361
case IcebergCatalogProperties.TYPE_JDBC:
@@ -363,7 +365,7 @@ public static String resolveCatalogImpl(String catalogType) {
363365
default:
364366
throw new DorisConnectorException(
365367
"Unknown " + IcebergCatalogProperties.ICEBERG_CATALOG_TYPE + ": " + catalogType
366-
+ ". Supported types: rest, hms, glue, hadoop, jdbc, s3tables");
368+
+ ". Supported types: rest, hms, glue, dlf, hadoop, jdbc, s3tables");
367369
}
368370
}
369371

@@ -701,6 +703,16 @@ public static HiveConf assembleHiveConf(String confResources, Map<String, String
701703
return hiveConf;
702704
}
703705

706+
/** Builds the plugin-local Hadoop configuration consumed by the DLF metastore client. */
707+
public static Configuration buildDlfConfiguration(Map<String, String> dlfCatalogConf) {
708+
Configuration conf = new Configuration();
709+
conf.setClassLoader(IcebergCatalogFactory.class.getClassLoader());
710+
dlfCatalogConf.forEach(conf::set);
711+
conf.set("hive.metastore.type", "dlf");
712+
conf.set("type", "hms");
713+
return conf;
714+
}
715+
704716
/**
705717
* Resolves the FE's {@code hadoop_config_dir}. Mirrors {@code fe-filesystem-hdfs}'s
706718
* {@code HdfsConfigFileLoader.resolveHadoopConfigDir}: a connector plugin cannot import fe-core's

‎fe/fe-connector/fe-connector-iceberg/src/main/java/org/apache/doris/connector/iceberg/IcebergCatalogProperties.java‎

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -47,13 +47,14 @@
4747
*/
4848
public final class IcebergCatalogProperties {
4949

50-
/** Iceberg catalog backend type: rest, hms, glue, jdbc, hadoop, s3tables. */
50+
/** Iceberg catalog backend type: rest, hms, glue, dlf, jdbc, hadoop, s3tables. */
5151
public static final String ICEBERG_CATALOG_TYPE = "iceberg.catalog.type";
5252

5353
// ---- Flavor literals (the accepted iceberg.catalog.type values) ----
5454
public static final String TYPE_REST = "rest";
5555
public static final String TYPE_HMS = "hms";
5656
public static final String TYPE_GLUE = "glue";
57+
public static final String TYPE_DLF = "dlf";
5758
public static final String TYPE_JDBC = "jdbc";
5859
public static final String TYPE_HADOOP = "hadoop";
5960
public static final String TYPE_S3_TABLES = "s3tables";
@@ -81,7 +82,7 @@ public final class IcebergCatalogProperties {
8182
public static final String EXTERNAL_CATALOG_NAME = "external_catalog.name";
8283

8384
@ConnectorProperty(names = {ICEBERG_CATALOG_TYPE}, required = false,
84-
description = "The metastore backend: rest, hms, glue, jdbc, hadoop or s3tables.")
85+
description = "The metastore backend: rest, hms, glue, dlf, jdbc, hadoop or s3tables.")
8586
private String catalogType = "";
8687

8788
@ConnectorProperty(names = {ENABLE_MAPPING_VARBINARY}, required = false,

‎fe/fe-connector/fe-connector-iceberg/src/main/java/org/apache/doris/connector/iceberg/IcebergConnector.java‎

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@
1919

2020
import org.apache.doris.connector.cache.CatalogMetaCache;
2121
import org.apache.doris.connector.cache.ConnectorMetadataCache;
22+
import org.apache.doris.connector.iceberg.dlf.DLFCatalog;
23+
import org.apache.doris.connector.metastore.DlfMetaStoreProperties;
2224
import org.apache.doris.connector.metastore.iceberg.jdbc.IcebergJdbcMetaStoreProperties;
2325
import org.apache.doris.connector.metastore.iceberg.rest.IcebergRestMetaStoreProperties;
2426
import org.apache.doris.connector.metastore.spi.AbstractHmsMetaStoreProperties;
@@ -967,6 +969,10 @@ private Catalog createCatalog() {
967969
return createS3TablesCatalog(catalogName, chosenS3);
968970
}
969971

972+
if (IcebergCatalogProperties.TYPE_DLF.equals(flavor)) {
973+
return createDlfCatalog(catalogName, chosenS3);
974+
}
975+
970976
Map<String, String> catalogOptions =
971977
IcebergCatalogFactory.buildCatalogProperties(catalogProps, chosenS3);
972978
Map<String, String> storageHadoopConfig = buildStorageHadoopConfig();
@@ -1035,6 +1041,23 @@ static String appendHmsCacheKeys(String existing) {
10351041
return appendCacheKey(keys, "conf:hive.metastore.sasl.enabled");
10361042
}
10371043

1044+
private Catalog createDlfCatalog(String catalogName,
1045+
Optional<S3CompatibleFileSystemProperties> chosenS3) {
1046+
if (!chosenS3.isPresent() || !"OSS".equals(chosenS3.get().providerName())) {
1047+
throw new DorisConnectorException("Iceberg dlf catalog requires OSS storage properties");
1048+
}
1049+
DlfMetaStoreProperties dlf = (DlfMetaStoreProperties) MetaStoreProviders.bindForType(
1050+
IcebergCatalogProperties.TYPE_DLF, properties, buildStorageHadoopConfig());
1051+
Configuration conf = IcebergCatalogFactory.buildDlfConfiguration(dlf.toDlfCatalogConf());
1052+
Map<String, String> catalogOptions = IcebergCatalogFactory.buildBaseCatalogProperties(properties);
1053+
return buildCatalogAuthenticated(IcebergCatalogProperties.TYPE_DLF, () -> {
1054+
DLFCatalog catalog = new DLFCatalog(chosenS3.get());
1055+
catalog.setConf(conf);
1056+
catalog.initialize(catalogName, catalogOptions);
1057+
return catalog;
1058+
});
1059+
}
1060+
10381061
static String appendCacheKey(String existing, String required) {
10391062
if (StringUtils.isBlank(existing)) {
10401063
return required;
Lines changed: 126 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,126 @@
1+
// Licensed to the Apache Software Foundation (ASF) under one
2+
// or more contributor license agreements. See the NOTICE file
3+
// distributed with this work for additional information
4+
// regarding copyright ownership. The ASF licenses this file
5+
// to you under the Apache License, Version 2.0 (the
6+
// "License"); you may not use this file except in compliance
7+
// with the License. You may obtain a copy of the License at
8+
//
9+
// http://www.apache.org/licenses/LICENSE-2.0
10+
//
11+
// Unless required by applicable law or agreed to in writing,
12+
// software distributed under the License is distributed on an
13+
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14+
// KIND, either express or implied. See the License for the
15+
// specific language governing permissions and limitations
16+
// under the License.
17+
18+
package org.apache.doris.connector.iceberg.dlf;
19+
20+
import org.apache.doris.connector.iceberg.dlf.client.DLFCachedClientPool;
21+
import org.apache.doris.filesystem.properties.S3CompatibleFileSystemProperties;
22+
23+
import org.apache.commons.lang3.StringUtils;
24+
import org.apache.hadoop.conf.Configuration;
25+
import org.apache.iceberg.TableOperations;
26+
import org.apache.iceberg.aws.s3.S3FileIO;
27+
import org.apache.iceberg.catalog.TableIdentifier;
28+
import org.apache.iceberg.io.FileIO;
29+
import software.amazon.awssdk.auth.credentials.AwsBasicCredentials;
30+
import software.amazon.awssdk.auth.credentials.AwsCredentialsProvider;
31+
import software.amazon.awssdk.auth.credentials.AwsSessionCredentials;
32+
import software.amazon.awssdk.auth.credentials.DefaultCredentialsProvider;
33+
import software.amazon.awssdk.auth.credentials.StaticCredentialsProvider;
34+
import software.amazon.awssdk.auth.signer.AwsS3V4Signer;
35+
import software.amazon.awssdk.core.client.config.ClientOverrideConfiguration;
36+
import software.amazon.awssdk.core.client.config.SdkAdvancedClientOption;
37+
import software.amazon.awssdk.core.retry.RetryPolicy;
38+
import software.amazon.awssdk.core.retry.backoff.EqualJitterBackoffStrategy;
39+
import software.amazon.awssdk.http.urlconnection.UrlConnectionHttpClient;
40+
import software.amazon.awssdk.regions.Region;
41+
import software.amazon.awssdk.services.s3.S3Client;
42+
import software.amazon.awssdk.services.s3.S3Configuration;
43+
44+
import java.net.URI;
45+
import java.time.Duration;
46+
import java.util.Map;
47+
48+
/** Aliyun DLF Iceberg catalog backed by the DLF Hive-compatible metastore and OSS. */
49+
public class DLFCatalog extends HiveCompatibleCatalog {
50+
51+
private final S3CompatibleFileSystemProperties ossStorage;
52+
53+
public DLFCatalog(S3CompatibleFileSystemProperties ossStorage) {
54+
this.ossStorage = ossStorage;
55+
}
56+
57+
@Override
58+
public void initialize(String name, Map<String, String> properties) {
59+
super.initialize(name, initializeFileIO(properties, conf), new DLFCachedClientPool(conf, properties));
60+
}
61+
62+
@Override
63+
protected TableOperations newTableOps(TableIdentifier tableIdentifier) {
64+
return new DLFTableOperations(conf, clients, fileIO, catalogName,
65+
tableIdentifier.namespace().level(0), tableIdentifier.name());
66+
}
67+
68+
@Override
69+
protected FileIO initializeFileIO(Map<String, String> properties, Configuration hadoopConf) {
70+
String region = ossStorage.getRegion();
71+
boolean usePathStyle = Boolean.parseBoolean(ossStorage.getUsePathStyle());
72+
URI endpoint = URI.create(toS3CompatibleEndpoint(ossStorage.getEndpoint(), region));
73+
AwsCredentialsProvider credentials = buildCredentials(ossStorage);
74+
FileIO io = new S3FileIO(() -> buildOssS3Client(endpoint, region, credentials, usePathStyle));
75+
io.initialize(properties);
76+
return io;
77+
}
78+
79+
static String toS3CompatibleEndpoint(String endpoint, String region) {
80+
String s3Endpoint = endpoint.replace("oss-" + region, "s3.oss-" + region);
81+
if (!s3Endpoint.contains("://")) {
82+
s3Endpoint = "http://" + s3Endpoint;
83+
}
84+
return s3Endpoint;
85+
}
86+
87+
private static AwsCredentialsProvider buildCredentials(S3CompatibleFileSystemProperties oss) {
88+
if (!oss.hasStaticCredentials()) {
89+
return DefaultCredentialsProvider.create();
90+
}
91+
if (StringUtils.isBlank(oss.getSessionToken())) {
92+
return StaticCredentialsProvider.create(AwsBasicCredentials.create(oss.getAccessKey(), oss.getSecretKey()));
93+
}
94+
return StaticCredentialsProvider.create(
95+
AwsSessionCredentials.create(oss.getAccessKey(), oss.getSecretKey(), oss.getSessionToken()));
96+
}
97+
98+
private static S3Client buildOssS3Client(URI endpoint, String region, AwsCredentialsProvider credentials,
99+
boolean usePathStyle) {
100+
RetryPolicy retryPolicy = RetryPolicy.builder()
101+
.numRetries(3)
102+
.backoffStrategy(EqualJitterBackoffStrategy.builder()
103+
.baseDelay(Duration.ofSeconds(1))
104+
.maxBackoffTime(Duration.ofMinutes(1))
105+
.build())
106+
.build();
107+
ClientOverrideConfiguration clientConf = ClientOverrideConfiguration.builder()
108+
.retryPolicy(retryPolicy)
109+
.putAdvancedOption(SdkAdvancedClientOption.SIGNER, AwsS3V4Signer.create())
110+
.build();
111+
return S3Client.builder()
112+
.httpClient(UrlConnectionHttpClient.builder()
113+
.socketTimeout(Duration.ofSeconds(30))
114+
.connectionTimeout(Duration.ofSeconds(30))
115+
.build())
116+
.endpointOverride(endpoint)
117+
.credentialsProvider(credentials)
118+
.region(Region.of(region))
119+
.overrideConfiguration(clientConf)
120+
.serviceConfiguration(S3Configuration.builder()
121+
.chunkedEncodingEnabled(false)
122+
.pathStyleAccessEnabled(usePathStyle)
123+
.build())
124+
.build();
125+
}
126+
}
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
// Licensed to the Apache Software Foundation (ASF) under one
2+
// or more contributor license agreements. See the NOTICE file
3+
// distributed with this work for additional information
4+
// regarding copyright ownership. The ASF licenses this file
5+
// to you under the Apache License, Version 2.0 (the
6+
// "License"); you may not use this file except in compliance
7+
// with the License. You may obtain a copy of the License at
8+
//
9+
// http://www.apache.org/licenses/LICENSE-2.0
10+
//
11+
// Unless required by applicable law or agreed to in writing,
12+
// software distributed under the License is distributed on an
13+
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14+
// KIND, either express or implied. See the License for the
15+
// specific language governing permissions and limitations
16+
// under the License.
17+
18+
package org.apache.doris.connector.iceberg.dlf;
19+
20+
import org.apache.hadoop.conf.Configuration;
21+
import org.apache.hadoop.hive.metastore.IMetaStoreClient;
22+
import org.apache.iceberg.ClientPool;
23+
import org.apache.iceberg.hive.HiveTableOperations;
24+
import org.apache.iceberg.io.FileIO;
25+
import shade.doris.hive.org.apache.thrift.TException;
26+
27+
/** Iceberg table operations against a Hive-compatible Aliyun DLF metastore. */
28+
public class DLFTableOperations extends HiveTableOperations {
29+
30+
public DLFTableOperations(Configuration conf,
31+
ClientPool<IMetaStoreClient, TException> metaClients,
32+
FileIO fileIO,
33+
String catalogName,
34+
String database,
35+
String table) {
36+
super(conf, metaClients, fileIO, catalogName, database, table);
37+
}
38+
}

0 commit comments

Comments
 (0)