Skip to content

Commit

Permalink
HSEARCH-4316 Add a configuration option to set the exhaustive list of…
Browse files Browse the repository at this point in the history
… tenant IDs
  • Loading branch information
yrodiere committed Nov 30, 2021
1 parent 6c7c39c commit a9b20b0
Show file tree
Hide file tree
Showing 13 changed files with 155 additions and 10 deletions.
Expand Up @@ -22,6 +22,7 @@
import org.hibernate.search.engine.common.spi.SearchIntegrationPartialBuildState;
import org.hibernate.search.engine.environment.bean.BeanReference;
import org.hibernate.search.engine.environment.bean.spi.BeanProvider;
import org.hibernate.search.engine.tenancy.spi.TenancyMode;
import org.hibernate.search.mapper.javabean.cfg.spi.JavaBeanMapperSpiSettings;
import org.hibernate.search.mapper.javabean.impl.JavaBeanMappingInitiator;
import org.hibernate.search.mapper.javabean.log.impl.Log;
Expand Down Expand Up @@ -149,7 +150,7 @@ public <E> SearchMappingBuilder addEntityType(Class<E> type, String entityName,
}

public SearchMappingBuilder multiTenancyEnabled(boolean multiTenancyEnabled) {
mappingInitiator.multiTenancyEnabled( multiTenancyEnabled );
mappingInitiator.tenancyMode( multiTenancyEnabled ? TenancyMode.MULTI_TENANCY : TenancyMode.SINGLE_TENANCY );
return this;
}

Expand Down
Expand Up @@ -125,6 +125,9 @@ public CompletableFuture<?> start(CoordinationStrategyStartContext context) {

ConfigurationPropertySource configurationSource = context.configurationPropertySource();

// TODO HSEARCH-4316 Use context.tenancyConfiguration() to determine the tenant IDs and create
// one set of event processors per tenant

if ( EVENT_PROCESSOR_ENABLED.get( configurationSource ) ) {
initializeEventProcessors( context );
}
Expand Down
Expand Up @@ -158,6 +158,17 @@ private HibernateOrmMapperSettings() {
*/
public static final String COORDINATION_STRATEGY = PREFIX + Radicals.COORDINATION_STRATEGY;

/**
* An exhaustive list of all tenant identifiers that can be used by the application when multi-tenancy is enabled.
* <p>
* Expects either a String representing multiple tenant IDs separated by commas,
* or a {@code Collection<String>} containing tenant IDs.
* <p>
* No default; this property may have to be set explicitly depending on the
* {@link #COORDINATION_STRATEGY coordination strategy}.
*/
public static final String MULTI_TENANCY_TENANT_IDS = PREFIX + Radicals.MULTI_TENANCY_TENANT_IDS;

/**
* Configuration property keys without the {@link #PREFIX prefix}.
*/
Expand Down Expand Up @@ -185,6 +196,9 @@ private Radicals() {
public static final String COORDINATION = "coordination";
public static final String COORDINATION_PREFIX = COORDINATION + ".";
public static final String COORDINATION_STRATEGY = COORDINATION_PREFIX + CoordinationRadicals.STRATEGY;
public static final String MULTI_TENANCY = "multi_tenancy";
public static final String MULTI_TENANCY_PREFIX = MULTI_TENANCY + ".";
public static final String MULTI_TENANCY_TENANT_IDS = MULTI_TENANCY_PREFIX + MultiTenancyRadicals.TENANT_IDS;
}

/**
Expand Down Expand Up @@ -216,6 +230,17 @@ private CoordinationRadicals() {
public static final String STRATEGY = "strategy";
}

/**
* Configuration property keys without the {@link #PREFIX prefix} + {@link Radicals#MULTI_TENANCY_PREFIX}.
*/
public static final class MultiTenancyRadicals {

private MultiTenancyRadicals() {
}

public static final String TENANT_IDS = "tenant_ids";
}

/**
* Default values for the different settings if no values are given.
*/
Expand Down
Expand Up @@ -13,6 +13,7 @@
import org.hibernate.search.engine.environment.thread.spi.ThreadPoolProvider;
import org.hibernate.search.engine.reporting.spi.ContextualFailureCollector;
import org.hibernate.search.mapper.orm.automaticindexing.spi.AutomaticIndexingMappingContext;
import org.hibernate.search.mapper.orm.tenancy.spi.TenancyConfiguration;

public interface CoordinationStrategyStartContext {

Expand Down Expand Up @@ -55,4 +56,6 @@ public interface CoordinationStrategyStartContext {
*/
AutomaticIndexingMappingContext mapping();

TenancyConfiguration tenancyConfiguration();

}
Expand Up @@ -13,6 +13,7 @@

import java.util.Collection;
import java.util.List;
import java.util.Set;

import org.hibernate.ScrollMode;
import org.hibernate.SessionFactory;
Expand Down Expand Up @@ -233,4 +234,15 @@ SearchException foundMultipleEntitiesForDocumentId(String entityName, String doc
@Message(id = ID_OFFSET + 53, value = "Configuration property '%1$s' is deprecated; use '%2$s' instead.")
void automaticIndexingStrategyIsDeprecated(String resolveOrRaw, String resolveOrRaw1);

@Message(id = ID_OFFSET + 54, value = "Cannot determine the set of all possible tenant identifiers."
+ " You must provide this information by setting configuration property '%1$s'"
+ " to a comma-separated string containing all possible tenant identifiers.")
SearchException missingTenantIdConfiguration(String tenantIdsConfigurationPropertyKey);

@Message(id = ID_OFFSET + 55, value = "Cannot target tenant '%1$s' because this tenant identifier"
+ " was not listed in the configuration provided on startup."
+ " To target this tenant, you must provide the tenant identifier through configuration property '%3$s',"
+ " which should be set to a comma-separated string containing all possible tenant identifiers."
+ " Currently configured tenant identifiers: %2$s." )
SearchException invalidTenantId(String tenantId, Set<String> allTenantIds, String tenantIdsConfigurationPropertyKey);
}
Expand Up @@ -16,18 +16,21 @@
import org.hibernate.search.mapper.orm.automaticindexing.spi.AutomaticIndexingMappingContext;
import org.hibernate.search.mapper.orm.coordination.common.spi.CoordinationStrategyStartContext;
import org.hibernate.search.mapper.orm.cfg.HibernateOrmMapperSettings;
import org.hibernate.search.mapper.orm.tenancy.spi.TenancyConfiguration;

public class CoordinationStrategyStartContextImpl implements CoordinationStrategyStartContext {
private final AutomaticIndexingMappingContext mapping;
private final MappingStartContext delegate;
private final ConfigurationPropertySource configurationPropertySource;
private final TenancyConfiguration tenancyConfiguration;

public CoordinationStrategyStartContextImpl(AutomaticIndexingMappingContext mapping,
MappingStartContext delegate) {
MappingStartContext delegate, TenancyConfiguration tenancyConfiguration) {
this.mapping = mapping;
this.delegate = delegate;
this.configurationPropertySource = delegate.configurationPropertySource()
.withMask( HibernateOrmMapperSettings.Radicals.COORDINATION );
this.tenancyConfiguration = tenancyConfiguration;
}

@Override
Expand Down Expand Up @@ -59,4 +62,9 @@ public Clock clock() {
public AutomaticIndexingMappingContext mapping() {
return mapping;
}

@Override
public TenancyConfiguration tenancyConfiguration() {
return tenancyConfiguration;
}
}
Expand Up @@ -60,6 +60,7 @@
import org.hibernate.search.mapper.orm.session.impl.HibernateOrmSearchSessionMappingContext;
import org.hibernate.search.mapper.orm.session.impl.HibernateOrmSessionTypeContext;
import org.hibernate.search.mapper.orm.spi.BatchMappingContext;
import org.hibernate.search.mapper.orm.tenancy.spi.TenancyConfiguration;
import org.hibernate.search.mapper.pojo.mapping.spi.AbstractPojoMappingImplementor;
import org.hibernate.search.mapper.pojo.mapping.spi.PojoMappingDelegate;
import org.hibernate.search.mapper.pojo.massindexing.spi.PojoMassIndexerAgent;
Expand Down Expand Up @@ -162,11 +163,17 @@ public CompletableFuture<?> start(MappingStartContext context) {
}
SearchScopeImpl<Object> scope = scopeOptional.get();

TenancyConfiguration tenancyConfiguration =
TenancyConfiguration.create( delegate().tenancyMode(), context.configurationPropertySource() );
// TODO HSEARCH-4321 use the tenancy configuration to have a default list of tenant IDs
// to target when creating the mass indexer from the mapping (not from a session)

// Schema management
PojoScopeSchemaManager schemaManager = scope.schemaManagerDelegate();
return schemaManagementListener.onStart( context, schemaManager )
.thenCompose( ignored -> coordinationStrategyHolder.get()
.start( new CoordinationStrategyStartContextImpl( this, context ) ) );
.thenCompose( ignored -> coordinationStrategyHolder.get().start(
new CoordinationStrategyStartContextImpl( this, context, tenancyConfiguration )
) );
}

@Override
Expand Down
Expand Up @@ -19,6 +19,7 @@
import org.hibernate.search.engine.environment.bean.BeanReference;
import org.hibernate.search.engine.mapper.mapping.building.spi.MappingBuildContext;
import org.hibernate.search.engine.mapper.mapping.building.spi.MappingConfigurationCollector;
import org.hibernate.search.engine.tenancy.spi.TenancyMode;
import org.hibernate.search.mapper.orm.bootstrap.impl.HibernateSearchPreIntegrationService;
import org.hibernate.search.mapper.orm.cfg.HibernateOrmMapperSettings;
import org.hibernate.search.mapper.orm.common.impl.HibernateOrmUtils;
Expand Down Expand Up @@ -91,9 +92,8 @@ private HibernateOrmMappingInitiator(HibernateOrmBasicTypeMetadataProvider basic
MultiTenancyStrategy multiTenancyStrategy =
MultiTenancyStrategy.determineMultiTenancyStrategy( ormConfigurationService.getSettings() );

multiTenancyEnabled(
!MultiTenancyStrategy.NONE.equals( multiTenancyStrategy )
);
tenancyMode( MultiTenancyStrategy.NONE.equals( multiTenancyStrategy ) ? TenancyMode.SINGLE_TENANCY
: TenancyMode.MULTI_TENANCY );

this.preIntegrationService = preIntegrationService;
}
Expand Down
@@ -0,0 +1,74 @@
/*
* Hibernate Search, full-text search for your domain model
*
* License: GNU Lesser General Public License (LGPL), version 2.1 or later
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
*/
package org.hibernate.search.mapper.orm.tenancy.spi;

import java.lang.invoke.MethodHandles;
import java.util.Collections;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Optional;
import java.util.Set;

import org.hibernate.search.engine.cfg.ConfigurationPropertySource;
import org.hibernate.search.engine.cfg.spi.ConfigurationProperty;
import org.hibernate.search.engine.cfg.spi.OptionalConfigurationProperty;
import org.hibernate.search.engine.tenancy.spi.TenancyMode;
import org.hibernate.search.mapper.orm.cfg.HibernateOrmMapperSettings;
import org.hibernate.search.mapper.orm.logging.impl.Log;
import org.hibernate.search.util.common.AssertionFailure;
import org.hibernate.search.util.common.SearchException;
import org.hibernate.search.util.common.impl.Contracts;
import org.hibernate.search.util.common.logging.impl.LoggerFactory;

public class TenancyConfiguration {

private static final Log log = LoggerFactory.make( Log.class, MethodHandles.lookup() );

private static final OptionalConfigurationProperty<List<String>> MULTI_TENANCY_TENANT_IDS =
ConfigurationProperty.forKey( HibernateOrmMapperSettings.Radicals.MULTI_TENANCY_TENANT_IDS )
.asString().multivalued()
.validate( value -> Contracts.assertNotNullNorEmpty( value, "value" ) )
.build();

public static TenancyConfiguration create(TenancyMode tenancyMode,
ConfigurationPropertySource configurationPropertySource) {
String tenantIdsConfigurationPropertyKey = MULTI_TENANCY_TENANT_IDS.resolveOrRaw( configurationPropertySource );
switch ( tenancyMode ) {
case SINGLE_TENANCY:
return new TenancyConfiguration( Optional.of( Collections.emptySet() ),
tenantIdsConfigurationPropertyKey );
case MULTI_TENANCY:
return new TenancyConfiguration(
MULTI_TENANCY_TENANT_IDS.getAndMap( configurationPropertySource, LinkedHashSet::new ),
tenantIdsConfigurationPropertyKey );
}
throw new AssertionFailure( "Unknown tenancy mode: " + tenancyMode );
}

private final Optional<Set<String>> tenantIds;
private final String tenantIdsConfigurationPropertyKey;

private TenancyConfiguration(Optional<Set<String>> tenantIds, String tenantIdsConfigurationPropertyKey) {
this.tenantIds = tenantIds;
this.tenantIdsConfigurationPropertyKey = tenantIdsConfigurationPropertyKey;
}

/**
* @return A set of all possible tenant IDs, or an empty set if the application is single-tenant.
* @throws org.hibernate.search.util.common.SearchException if the application is multi-tenant
* and the full list of tenant IDs was not configured.
*/
public Set<String> tenantIdsOrFail() {
// This will only fail when using multi-tenancy,
// because the set is always defined when using single-tenancy.
return tenantIds.orElseThrow( () -> log.missingTenantIdConfiguration( tenantIdsConfigurationPropertyKey ) );
}

public SearchException invalidTenantId(String tenantId) {
return log.invalidTenantId( tenantId, tenantIds.orElse( Collections.emptySet() ), tenantIdsConfigurationPropertyKey );
}
}
Expand Up @@ -305,7 +305,7 @@ public MPBS prepareBuild() throws MappingAbortedException {
}

mappingDelegate = new PojoMappingDelegateImpl(
threadPoolProvider, failureHandler,
threadPoolProvider, failureHandler, tenancyMode,
indexedTypeManagerContainerBuilder.build(),
containedTypeManagerContainerBuilder.build()
);
Expand Down
Expand Up @@ -15,6 +15,7 @@
import org.hibernate.search.engine.backend.work.execution.DocumentRefreshStrategy;
import org.hibernate.search.engine.environment.thread.spi.ThreadPoolProvider;
import org.hibernate.search.engine.reporting.FailureHandler;
import org.hibernate.search.engine.tenancy.spi.TenancyMode;
import org.hibernate.search.mapper.pojo.work.impl.PojoIndexingPlanEventProcessingStrategy;
import org.hibernate.search.mapper.pojo.work.impl.PojoIndexingPlanEventSendingStrategy;
import org.hibernate.search.mapper.pojo.work.impl.PojoIndexingPlanImpl;
Expand All @@ -39,15 +40,18 @@ public class PojoMappingDelegateImpl implements PojoMappingDelegate {

private final ThreadPoolProvider threadPoolProvider;
private final FailureHandler failureHandler;
private final TenancyMode tenancyMode;
private final PojoIndexedTypeManagerContainer indexedTypeManagers;
private final PojoContainedTypeManagerContainer containedTypeManagers;

public PojoMappingDelegateImpl(ThreadPoolProvider threadPoolProvider,
FailureHandler failureHandler,
TenancyMode tenancyMode,
PojoIndexedTypeManagerContainer indexedTypeManagers,
PojoContainedTypeManagerContainer containedTypeManagers) {
this.threadPoolProvider = threadPoolProvider;
this.failureHandler = failureHandler;
this.tenancyMode = tenancyMode;
this.indexedTypeManagers = indexedTypeManagers;
this.containedTypeManagers = containedTypeManagers;
}
Expand All @@ -70,6 +74,11 @@ public FailureHandler failureHandler() {
return failureHandler;
}

@Override
public TenancyMode tenancyMode() {
return tenancyMode;
}

@Override
public <R, E, C> PojoScopeDelegate<R, E, C> createPojoScope(
PojoScopeMappingContext mappingContext,
Expand Down
Expand Up @@ -101,8 +101,8 @@ public void containedEntityIdentityMappingRequired(boolean required) {
this.containedEntityIdentityMappingMode = required ? IdentityMappingMode.REQUIRED : IdentityMappingMode.OPTIONAL;
}

public void multiTenancyEnabled(boolean multiTenancyEnabled) {
this.tenancyMode = multiTenancyEnabled ? TenancyMode.MULTI_TENANCY : TenancyMode.SINGLE_TENANCY;
public void tenancyMode(TenancyMode tenancyMode) {
this.tenancyMode = tenancyMode;
}

public void defaultReindexOnUpdate(ReindexOnUpdate defaultReindexOnUpdate) {
Expand Down
Expand Up @@ -13,6 +13,7 @@
import org.hibernate.search.engine.backend.work.execution.DocumentRefreshStrategy;
import org.hibernate.search.engine.environment.thread.spi.ThreadPoolProvider;
import org.hibernate.search.engine.reporting.FailureHandler;
import org.hibernate.search.engine.tenancy.spi.TenancyMode;
import org.hibernate.search.mapper.pojo.work.spi.PojoIndexingQueueEventProcessingPlan;
import org.hibernate.search.mapper.pojo.work.spi.PojoIndexingQueueEventSendingPlan;
import org.hibernate.search.mapper.pojo.model.spi.PojoRawTypeIdentifier;
Expand All @@ -32,6 +33,8 @@ public interface PojoMappingDelegate extends AutoCloseable {

FailureHandler failureHandler();

TenancyMode tenancyMode();

<R, E, C> PojoScopeDelegate<R, E, C> createPojoScope(
PojoScopeMappingContext mappingContext,
Collection<? extends PojoRawTypeIdentifier<? extends E>> targetedTypes,
Expand Down

0 comments on commit a9b20b0

Please sign in to comment.