Skip to content

Add RealmConfig #2015

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

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,6 @@
import jakarta.inject.Inject;
import java.nio.file.Path;
import org.apache.polaris.core.PolarisDiagnostics;
import org.apache.polaris.core.config.PolarisConfigurationStore;
import org.apache.polaris.core.context.RealmContext;
import org.apache.polaris.core.persistence.LocalPolarisMetaStoreManagerFactory;
import org.apache.polaris.core.persistence.PolarisMetaStoreManager;
Expand All @@ -47,13 +46,12 @@ public class EclipseLinkPolarisMetaStoreManagerFactory
@Inject PolarisStorageIntegrationProvider storageIntegrationProvider;

protected EclipseLinkPolarisMetaStoreManagerFactory() {
this(null, null);
this(null);
}

@Inject
protected EclipseLinkPolarisMetaStoreManagerFactory(
PolarisDiagnostics diagnostics, PolarisConfigurationStore configurationStore) {
super(diagnostics, configurationStore);
protected EclipseLinkPolarisMetaStoreManagerFactory(PolarisDiagnostics diagnostics) {
super(diagnostics);
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@
import org.apache.polaris.core.PolarisCallContext;
import org.apache.polaris.core.PolarisDefaultDiagServiceImpl;
import org.apache.polaris.core.PolarisDiagnostics;
import org.apache.polaris.core.config.PolarisConfigurationStore;
import org.apache.polaris.core.config.RealmConfig;
import org.apache.polaris.core.context.CallContext;
import org.apache.polaris.core.context.RealmContext;
import org.apache.polaris.core.entity.PolarisEntity;
Expand Down Expand Up @@ -78,7 +78,6 @@ public class JdbcMetaStoreManagerFactory implements MetaStoreManagerFactory {
@Inject PolarisStorageIntegrationProvider storageIntegrationProvider;
@Inject Instance<DataSource> dataSource;
@Inject RelationalJdbcConfiguration relationalJdbcConfiguration;
@Inject PolarisConfigurationStore configurationStore;

protected JdbcMetaStoreManagerFactory() {}

Expand Down Expand Up @@ -221,23 +220,23 @@ public synchronized Supplier<BasePersistence> getOrCreateSessionSupplier(

@Override
public synchronized StorageCredentialCache getOrCreateStorageCredentialCache(
RealmContext realmContext) {
RealmContext realmContext, RealmConfig realmConfig) {
if (!storageCredentialCacheMap.containsKey(realmContext.getRealmIdentifier())) {
storageCredentialCacheMap.put(
realmContext.getRealmIdentifier(),
new StorageCredentialCache(realmContext, configurationStore));
realmContext.getRealmIdentifier(), new StorageCredentialCache(realmConfig));
}

return storageCredentialCacheMap.get(realmContext.getRealmIdentifier());
}

@Override
public synchronized EntityCache getOrCreateEntityCache(RealmContext realmContext) {
public synchronized EntityCache getOrCreateEntityCache(
RealmContext realmContext, RealmConfig realmConfig) {
if (!entityCacheMap.containsKey(realmContext.getRealmIdentifier())) {
PolarisMetaStoreManager metaStoreManager = getOrCreateMetaStoreManager(realmContext);
entityCacheMap.put(
realmContext.getRealmIdentifier(),
new InMemoryEntityCache(realmContext, configurationStore, metaStoreManager));
new InMemoryEntityCache(realmConfig, metaStoreManager));
}

return entityCacheMap.get(realmContext.getRealmIdentifier());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@
import java.time.Clock;
import java.time.ZoneId;
import org.apache.polaris.core.config.PolarisConfigurationStore;
import org.apache.polaris.core.config.RealmConfig;
import org.apache.polaris.core.config.RealmConfigImpl;
import org.apache.polaris.core.context.CallContext;
import org.apache.polaris.core.context.RealmContext;
import org.apache.polaris.core.persistence.BasePersistence;
Expand All @@ -42,8 +44,9 @@ public class PolarisCallContext implements CallContext {

private final Clock clock;

// will make it final once we remove deprecated constructor
private RealmContext realmContext = null;
private final RealmContext realmContext;

private final RealmConfig realmConfig;

public PolarisCallContext(
@Nonnull RealmContext realmContext,
Expand All @@ -56,17 +59,19 @@ public PolarisCallContext(
this.diagServices = diagServices;
this.configurationStore = configurationStore;
this.clock = clock;
this.realmConfig = new RealmConfigImpl(this.configurationStore, this.realmContext);
}

public PolarisCallContext(
@Nonnull RealmContext realmContext,
@Nonnull BasePersistence metaStore,
@Nonnull PolarisDiagnostics diagServices) {
this.realmContext = realmContext;
this.metaStore = metaStore;
this.diagServices = diagServices;
this.configurationStore = new PolarisConfigurationStore() {};
this.clock = Clock.system(ZoneId.systemDefault());
this(
realmContext,
metaStore,
diagServices,
new PolarisConfigurationStore() {},
Clock.system(ZoneId.systemDefault()));
}

public BasePersistence getMetaStore() {
Expand All @@ -77,10 +82,6 @@ public PolarisDiagnostics getDiagServices() {
return diagServices;
}

public PolarisConfigurationStore getConfigurationStore() {
return configurationStore;
}

public Clock getClock() {
return clock;
}
Expand All @@ -90,6 +91,11 @@ public RealmContext getRealmContext() {
return realmContext;
}

@Override
public RealmConfig getRealmConfig() {
return realmConfig;
}

@Override
public PolarisCallContext getPolarisCallContext() {
return this;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,6 @@
import java.util.stream.Collectors;
import org.apache.iceberg.exceptions.ForbiddenException;
import org.apache.polaris.core.config.FeatureConfiguration;
import org.apache.polaris.core.config.PolarisConfigurationStore;
import org.apache.polaris.core.context.CallContext;
import org.apache.polaris.core.entity.PolarisBaseEntity;
import org.apache.polaris.core.entity.PolarisEntityConstants;
Expand Down Expand Up @@ -531,12 +530,8 @@ public class PolarisAuthorizerImpl implements PolarisAuthorizer {
List.of(TABLE_DETACH_POLICY, CATALOG_MANAGE_METADATA, CATALOG_MANAGE_CONTENT));
}

private final PolarisConfigurationStore featureConfig;

@Inject
public PolarisAuthorizerImpl(PolarisConfigurationStore featureConfig) {
this.featureConfig = featureConfig;
}
public PolarisAuthorizerImpl() {}

/**
* Checks whether the {@code grantedPrivilege} is sufficient to confer {@code desiredPrivilege},
Expand Down Expand Up @@ -583,9 +578,10 @@ public void authorizeOrThrow(
@Nullable List<PolarisResolvedPathWrapper> targets,
@Nullable List<PolarisResolvedPathWrapper> secondaries) {
boolean enforceCredentialRotationRequiredState =
featureConfig.getConfiguration(
callContext.getRealmContext(),
FeatureConfiguration.ENFORCE_PRINCIPAL_CREDENTIAL_ROTATION_REQUIRED_CHECKING);
callContext
.getRealmConfig()
.getConfig(
FeatureConfiguration.ENFORCE_PRINCIPAL_CREDENTIAL_ROTATION_REQUIRED_CHECKING);
if (enforceCredentialRotationRequiredState
&& authenticatedPrincipal
.getPrincipalEntity()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,11 +49,7 @@ protected FeatureConfiguration(
*/
public static void enforceFeatureEnabledOrThrow(
CallContext callContext, FeatureConfiguration<Boolean> featureConfig) {
boolean enabled =
callContext
.getPolarisCallContext()
.getConfigurationStore()
.getConfiguration(callContext.getRealmContext(), featureConfig);
boolean enabled = callContext.getRealmConfig().getConfig(featureConfig);
if (!enabled) {
throw new UnsupportedOperationException("Feature not enabled: " + featureConfig.key);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.polaris.core.config;

import jakarta.annotation.Nullable;
import org.apache.polaris.core.entity.CatalogEntity;

/** Realm-specific configuration used to retrieve runtime parameters. */
Copy link
Contributor

Choose a reason for hiding this comment

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

Is it really a Realm-specific configuration? This looks more like a configuration store, like PolarisConfigurationStore.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

depends on one's definition i guess, currently the interface allows retrieval of configuration values. in my book that makes it a "configuration".

do you want me to add "store" to the javadoc or to also rename the class? if the latter, any concrete name suggestions?

Copy link
Contributor

Choose a reason for hiding this comment

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

I think calling it a RealmConfigurationStore would be a better name, but that also only makes me think more that this needs to be reconciled with PolarisConfigurationStore

Copy link
Contributor

Choose a reason for hiding this comment

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

I would not mind the proposed renaming, but current name LGTM too.

public interface RealmConfig {

/**
* Retrieve the current value for a configuration key. May be null if not set.
*
* @param <T> the type of the configuration value
* @param configName the name of the configuration key to check
* @return the current value set for the configuration key, or null if not set
*/
<T> @Nullable T getConfig(String configName);

/**
* Retrieve the current value for a configuration key. If not set, return the non-null default
* value.
*
* @param <T> the type of the configuration value
* @param configName the name of the configuration key to check
* @param defaultValue the default value if the configuration key has no value
* @return the current value or the supplied default value
*/
<T> T getConfig(String configName, T defaultValue);

/**
* Retrieve the current value for a configuration.
*
* @param <T> the type of the configuration value
* @param config the configuration to load
* @return the current value set for the configuration key or null if not set
*/
<T> T getConfig(PolarisConfiguration<T> config);

/**
* Retrieve the current value for a configuration, overriding with a catalog config if it is
* present.
*
* @param <T> the type of the configuration value
* @param config the configuration to load
* @param catalogEntity the catalog to check for an override
* @return the current value set for the configuration key or null if not set
*/
<T> T getConfig(PolarisConfiguration<T> config, CatalogEntity catalogEntity);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.polaris.core.config;

import jakarta.annotation.Nullable;
import org.apache.polaris.core.context.RealmContext;
import org.apache.polaris.core.entity.CatalogEntity;

public class RealmConfigImpl implements RealmConfig {

private final PolarisConfigurationStore configurationStore;
Copy link
Contributor

Choose a reason for hiding this comment

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

If a new PolarisConfigurationStore implementation could achieve the same thing, we should try that approach first

Copy link
Contributor Author

@XN137 XN137 Jul 9, 2025

Choose a reason for hiding this comment

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

the idea of having the interface is to make the life of the callers easier as they most often (/always?) operate in the context of a single realm / CallContext.

whether configuration values are stored by a single store for all/multiple realms or not is an implementation detail that callers should not care about.
so afaict PolarisConfigurationStore can keep its current implementation unless in the future we notice any advantages of "splitting it up".

Copy link
Contributor

Choose a reason for hiding this comment

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

whether configuration values are stored by a single store for all/multiple realms or not is an implementation detail that callers should not care about.

Isn't that a reason that you'd want to just inject a PolarisConfigurationStore rather than handling something called a RealmConfig?

Copy link
Contributor

Choose a reason for hiding this comment

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

PolarisConfigurationStore is essentially multi-realm (note its explicit realm method parameters). This class acts as a request-scoped facade to automatically contextualize request-specific config lookup with realm IDs and pass those calls to PolarisConfigurationStore.

Copy link
Contributor

@eric-maynard eric-maynard Jul 10, 2025

Choose a reason for hiding this comment

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

I'm all for refactoring PolarisConfigurationStore to have both realm-agnostic and realm-gnostic (?) methods. But I do not think a new layer like this over the config store is necessary.

Copy link
Contributor

Choose a reason for hiding this comment

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

The new layer helps to properly delineate application-scoped data (PolarisConfigurationStore and/or it's config source) from request-scoped data (RealmConfig). Effectively, request-scoped code does not need to deal with explicit realm ID parameters, which, IMHO, is beneficial to code clarity and maintainability.

Copy link
Contributor

@eric-maynard eric-maynard Jul 10, 2025

Choose a reason for hiding this comment

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

Not having to deal with realms is definitely a great improvement, agreed. But my concern remains that this new class becomes the de facto “Polaris Configuration Store” and this redundancy does not improve clarity. Furthermore, the proposed structure could be interpreted as implying that these configs are set per-realm (while PolarisConfigurationStore confits are not) when in fact this is not the case.

private final RealmContext realmContext;

public RealmConfigImpl(PolarisConfigurationStore configurationStore, RealmContext realmContext) {
this.configurationStore = configurationStore;
this.realmContext = realmContext;
}

@Override
public <T> @Nullable T getConfig(String configName) {
return configurationStore.getConfiguration(realmContext, configName);
}

@Override
public <T> T getConfig(String configName, T defaultValue) {
return configurationStore.getConfiguration(realmContext, configName, defaultValue);
}

@Override
public <T> T getConfig(PolarisConfiguration<T> config) {
return configurationStore.getConfiguration(realmContext, config);
}

@Override
public <T> T getConfig(PolarisConfiguration<T> config, CatalogEntity catalogEntity) {
return configurationStore.getConfiguration(realmContext, catalogEntity, config);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
package org.apache.polaris.core.context;

import org.apache.polaris.core.PolarisCallContext;
import org.apache.polaris.core.config.RealmConfig;

/**
* Stores elements associated with an individual REST request such as RealmContext, caller
Expand Down Expand Up @@ -53,4 +54,6 @@ static void unsetCurrentContext() {
* @return the inner context used for delegating services
*/
PolarisCallContext getPolarisCallContext();

RealmConfig getRealmConfig();
}
Original file line number Diff line number Diff line change
Expand Up @@ -313,11 +313,8 @@ private void validateMaxAllowedLocations(
CallContext callContext, Collection<String> allowedLocations) {
int maxAllowedLocations =
callContext
.getPolarisCallContext()
.getConfigurationStore()
.getConfiguration(
callContext.getRealmContext(),
BehaviorChangeConfiguration.STORAGE_CONFIGURATION_MAX_LOCATIONS);
.getRealmConfig()
.getConfig(BehaviorChangeConfiguration.STORAGE_CONFIGURATION_MAX_LOCATIONS);
if (maxAllowedLocations != -1 && allowedLocations.size() > maxAllowedLocations) {
throw new IllegalArgumentException(
String.format(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1508,9 +1508,8 @@ private void revokeGrantRecord(
PolarisObjectMapperUtil.parseTaskState(entity);
long taskAgeTimeout =
callCtx
.getConfigurationStore()
.getConfiguration(
callCtx.getRealmContext(),
.getRealmConfig()
.getConfig(
PolarisTaskConstants.TASK_TIMEOUT_MILLIS_CONFIG,
PolarisTaskConstants.TASK_TIMEOUT_MILLIS);
return taskState == null
Expand Down
Loading
Loading