Skip to content

Commit

Permalink
[GEOS-11526] GeoFence: slow GeoServer response when there are many ro…
Browse files Browse the repository at this point in the history
…les and layergroups
  • Loading branch information
etj authored and aaime committed Sep 19, 2024
1 parent 4c94d99 commit b50682b
Show file tree
Hide file tree
Showing 16 changed files with 552 additions and 158 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,8 @@
import org.geoserver.catalog.impl.LocalWorkspaceCatalog;
import org.geoserver.geofence.config.GeoFenceConfiguration;
import org.geoserver.geofence.config.GeoFenceConfigurationManager;
import org.geoserver.geofence.containers.ContainerAccessResolver;
import org.geoserver.geofence.containers.ContainerLimitResolver;
import org.geoserver.geofence.core.model.LayerAttribute;
import org.geoserver.geofence.core.model.enums.AccessType;
import org.geoserver.geofence.core.model.enums.GrantType;
Expand Down Expand Up @@ -119,6 +121,7 @@ enum PropertyAccessMode {
Catalog catalog;

private final GeoFenceConfigurationManager configurationManager;
private ContainerAccessResolver containerAccessResolver;

private LayerGroupContainmentCache groupsCache;

Expand All @@ -128,12 +131,14 @@ public GeofenceAccessManager(
RuleReaderService rulesService,
Catalog catalog,
GeoFenceConfigurationManager configurationManager,
ContainerAccessResolver containerAccessResolver,
WPSHelper wpsHelper) {

this.rulesService = rulesService;
this.catalog = new LocalWorkspaceCatalog(catalog);
this.configurationManager = configurationManager;
this.groupsCache = new LayerGroupContainmentCache(catalog);
this.containerAccessResolver = containerAccessResolver;
this.wpsHelper = wpsHelper;
}

Expand Down Expand Up @@ -285,6 +290,7 @@ public StyleAccessLimits getAccessLimits(Authentication user, StyleInfo style) {

@Override
public LayerGroupAccessLimits getAccessLimits(Authentication user, LayerGroupInfo layerInfo) {
LOGGER.log(Level.FINE, "Getting access limits for LayerGroup {0}", layerInfo.getName());
return getAccessLimits(user, layerInfo, Collections.emptyList());
}

Expand All @@ -307,6 +313,7 @@ public DataAccessLimits getAccessLimits(Authentication user, ResourceInfo resour
@Override
public DataAccessLimits getAccessLimits(
Authentication user, LayerInfo layer, List<LayerGroupInfo> containers) {
LOGGER.log(Level.FINE, "Getting access limits for Layer {0} + containers", layer.getName());
String workspace = layer.getResource().getStore().getWorkspace().getName();
String layerName = layer.getName();
return (DataAccessLimits) getAccessLimits(user, layer, layerName, workspace, containers);
Expand All @@ -315,6 +322,10 @@ public DataAccessLimits getAccessLimits(
@Override
public LayerGroupAccessLimits getAccessLimits(
Authentication user, LayerGroupInfo layerGroup, List<LayerGroupInfo> containers) {
LOGGER.log(
Level.FINE,
"Getting access limits for Layer {0} + containers",
layerGroup.getName());
WorkspaceInfo ws = layerGroup.getWorkspace();
String workspace = ws != null ? ws.getName() : null;
String layer = layerGroup.getName();
Expand Down Expand Up @@ -369,7 +380,7 @@ private AccessLimits getAccessLimits(
// if a single group is present we don't apply any limit from containers.
if (!anySingle && !allOpaque)
processingResult =
getContainerResolverResult(
this.containerAccessResolver.getContainerResolverResult(
info,
layer,
workspace,
Expand All @@ -383,7 +394,7 @@ private AccessLimits getAccessLimits(
// layer is requested in context of a layer group.
// we need to process the containers limits.
processingResult =
getContainerResolverResult(
this.containerAccessResolver.getContainerResolverResult(
info,
layer,
workspace,
Expand Down Expand Up @@ -665,53 +676,6 @@ AccessLimits buildLayerGroupAccessLimits(AccessInfo accessInfo) {
else return new LayerGroupAccessLimits(convert(accessInfo.getCatalogMode()));
}

private ContainerLimitResolver.ProcessingResult getContainerResolverResult(
CatalogInfo resourceInfo,
String layer,
String workspace,
GeoFenceConfiguration configuration,
String callerIp,
Authentication user,
List<LayerGroupInfo> containers,
Collection<LayerGroupContainmentCache.LayerGroupSummary> summaries) {
ContainerLimitResolver resolver;
if (summaries != null)
resolver =
new ContainerLimitResolver(
summaries,
rulesService,
user,
layer,
workspace,
callerIp,
configuration);
else
resolver =
new ContainerLimitResolver(
containers,
rulesService,
user,
layer,
workspace,
callerIp,
configuration);

ContainerLimitResolver.ProcessingResult result = resolver.resolveResourceInGroupLimits();
Geometry intersect = result.getIntersectArea();
Geometry clip = result.getClipArea();
// areas might be in a srid different from the one of the resource
// being requested.
CoordinateReferenceSystem crs = GeomHelper.getCRSFromInfo(resourceInfo);
if (intersect != null) {
intersect = GeomHelper.reprojectGeometry(intersect, crs);
result.setIntersectArea(intersect);
}
if (clip != null) {
clip = GeomHelper.reprojectGeometry(clip, crs);
result.setClipArea(clip);
}
return result;
}
// get the catalogMode for the resource privileging the container one if passed
private CatalogMode getCatalogMode(
AccessInfo accessInfo, ContainerLimitResolver.ProcessingResult resultLimits) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
import org.springframework.security.core.GrantedAuthority;

/** Builder class for a {@link RuleFilter}. */
class RuleFilterBuilder {
public class RuleFilterBuilder {

private Request owsRequest;
private String ipAddress;
Expand All @@ -30,7 +30,7 @@ class RuleFilterBuilder {

private static final Logger LOGGER = Logging.getLogger(RuleFilterBuilder.class);

RuleFilterBuilder(GeoFenceConfiguration configuration) {
public RuleFilterBuilder(GeoFenceConfiguration configuration) {
this.config = configuration;
}

Expand All @@ -40,7 +40,7 @@ class RuleFilterBuilder {
* @param request the OWS Request.
* @return this builder.
*/
RuleFilterBuilder withRequest(Request request) {
public RuleFilterBuilder withRequest(Request request) {
this.owsRequest = request;
return this;
}
Expand All @@ -51,7 +51,7 @@ RuleFilterBuilder withRequest(Request request) {
* @param ipAddress the ipAddress.
* @return this builder.
*/
RuleFilterBuilder withIpAddress(String ipAddress) {
public RuleFilterBuilder withIpAddress(String ipAddress) {
this.ipAddress = ipAddress;
return this;
}
Expand All @@ -62,7 +62,7 @@ RuleFilterBuilder withIpAddress(String ipAddress) {
* @param workspace the workspace name.
* @return this builder.
*/
RuleFilterBuilder withWorkspace(String workspace) {
public RuleFilterBuilder withWorkspace(String workspace) {
this.workspace = workspace;
return this;
}
Expand All @@ -73,7 +73,7 @@ RuleFilterBuilder withWorkspace(String workspace) {
* @param layer the layer name.
* @return this builder.
*/
RuleFilterBuilder withLayer(String layer) {
public RuleFilterBuilder withLayer(String layer) {
this.layer = layer;
return this;
}
Expand All @@ -84,7 +84,7 @@ RuleFilterBuilder withLayer(String layer) {
* @param authentication the authentication object.
* @return this builder.
*/
RuleFilterBuilder withUser(Authentication authentication) {
public RuleFilterBuilder withUser(Authentication authentication) {
this.user = authentication;
return this;
}
Expand All @@ -94,7 +94,7 @@ RuleFilterBuilder withUser(Authentication authentication) {
*
* @return a {@link RuleFilter} instance.
*/
RuleFilter build() {
public RuleFilter build() {
RuleFilter ruleFilter = new RuleFilter(RuleFilter.SpecialFilterType.ANY);
setRuleFilterUserAndRole(ruleFilter);
ruleFilter.setInstance(config.getInstanceName());
Expand Down Expand Up @@ -196,7 +196,7 @@ private void setByRole(RuleFilter ruleFilter) {
}
}

List<String> getFilteredRoles() {
public List<String> getFilteredRoles() {
boolean getAllRoles = config.getRoles().contains("*");
Set<String> excluded =
config.getRoles().stream()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,11 @@
import java.util.concurrent.atomic.AtomicLong;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.geoserver.geofence.cache.CachedRuleLoaders.NamePw;
import javax.annotation.PostConstruct;
import org.geoserver.geofence.cache.RuleCacheLoaderFactory.NamePw;
import org.geoserver.geofence.config.GeoFenceConfigurationManager;
import org.geoserver.geofence.containers.ContainerAccessCacheLoaderFactory;
import org.geoserver.geofence.containers.ContainerLimitResolver;
import org.geoserver.geofence.services.dto.AccessInfo;
import org.geoserver.geofence.services.dto.AuthUser;
import org.geoserver.geofence.services.dto.RuleFilter;
Expand All @@ -31,26 +34,50 @@ public class CacheManager {

static final Logger LOGGER = Logging.getLogger(CacheManager.class);

private CachedRuleLoaders cachedRuleLoaders;
private RuleCacheLoaderFactory ruleServiceLoaderFactory;
private ContainerAccessCacheLoaderFactory containerAccessCacheLoaderFactory;

private LoadingCache<RuleFilter, AccessInfo> ruleCache;
private LoadingCache<NamePw, AuthUser> userCache;
private LoadingCache<RuleFilter, AccessInfo> authCache;
private LoadingCache<
ContainerAccessCacheLoaderFactory.ResolveParams,
ContainerLimitResolver.ProcessingResult>
contCache;

private final GeoFenceConfigurationManager configurationManager;

/** Latest configuration used */
private CacheConfiguration cacheConfiguration = new CacheConfiguration();

/**
* This is a do-it-all constructor, that also calls the init() method. Useful whien testing. You
* may want to use the simpler constructor + setters in a running environment, in order to avoid
* circular bean dependencies.
*/
public CacheManager(
GeoFenceConfigurationManager configurationManager,
CachedRuleLoaders cachedRuleLoaders) {
RuleCacheLoaderFactory cachedRuleLoaders,
ContainerAccessCacheLoaderFactory containerAccessCacheLoaderFactory) {

this(configurationManager);
setRuleServiceLoaderFactory(ruleServiceLoaderFactory);
setContainerAccessCacheLoaderFactory(containerAccessCacheLoaderFactory);
init();
}

public CacheManager(GeoFenceConfigurationManager configurationManager) {

this.configurationManager = configurationManager;
this.cachedRuleLoaders = cachedRuleLoaders;
}

// pull config when initializing
init();
public final void setRuleServiceLoaderFactory(RuleCacheLoaderFactory ruleServiceLoaderFactory) {
this.ruleServiceLoaderFactory = ruleServiceLoaderFactory;
}

public final void setContainerAccessCacheLoaderFactory(
ContainerAccessCacheLoaderFactory containerAccessCacheLoaderFactory) {
this.containerAccessCacheLoaderFactory = containerAccessCacheLoaderFactory;
}

/**
Expand All @@ -59,13 +86,17 @@ public CacheManager(
* <p>Please use {@link #getCacheInitParams() } to set the cache parameters before <code>init()
* </code>ting the cache
*/
@PostConstruct
public final void init() {

cacheConfiguration = configurationManager.getCacheConfiguration();

ruleCache = getCacheBuilder().build(cachedRuleLoaders.new RuleLoader());
userCache = getCacheBuilder().build(cachedRuleLoaders.new UserLoader());
authCache = getCacheBuilder().build(cachedRuleLoaders.new AuthLoader());
ruleCache = getCacheBuilder().build(ruleServiceLoaderFactory.createRuleLoader());
userCache = getCacheBuilder().build(ruleServiceLoaderFactory.createUserLoader());
authCache = getCacheBuilder().build(ruleServiceLoaderFactory.createAuthLoader());
contCache =
getCacheBuilder()
.build(containerAccessCacheLoaderFactory.createProcessingResultLoader());
}

protected CacheBuilder<Object, Object> getCacheBuilder() {
Expand Down Expand Up @@ -98,6 +129,7 @@ public void invalidateAll() {
ruleCache.invalidateAll();
userCache.invalidateAll();
authCache.invalidateAll();
contCache.invalidateAll();
}

private AtomicLong dumpCnt = new AtomicLong(0);
Expand All @@ -109,6 +141,7 @@ public void logStats() {
LOGGER.info("Rules :" + ruleCache.stats());
LOGGER.info("Users :" + userCache.stats());
LOGGER.info("Auth :" + authCache.stats());
LOGGER.info("Cont :" + contCache.stats());
LOGGER.fine("params :" + cacheConfiguration);
}
}
Expand All @@ -120,20 +153,34 @@ public CacheConfiguration getCacheInitParams() {
}

public LoadingCache<RuleFilter, AccessInfo> getRuleCache() {
if (ruleCache == null)
throw new IllegalStateException("CacheManager is not properly inizialized");
logStats();
return ruleCache;
}

public LoadingCache<NamePw, AuthUser> getUserCache() {
if (userCache == null)
throw new IllegalStateException("CacheManager is not properly inizialized");
logStats();
return userCache;
}

public LoadingCache<RuleFilter, AccessInfo> getAuthCache() {
if (authCache == null)
throw new IllegalStateException("CacheManager is not properly inizialized");
logStats();
return authCache;
}

public LoadingCache<
ContainerAccessCacheLoaderFactory.ResolveParams,
ContainerLimitResolver.ProcessingResult>
getContainerCache() {
logStats();
return contCache;
}

@Override
public String toString() {
return getClass().getSimpleName()
Expand All @@ -144,6 +191,8 @@ public String toString() {
+ userCache.stats()
+ " Auth:"
+ authCache.stats()
+ " Cont:"
+ contCache.stats()
+ " "
+ cacheConfiguration
+ "]";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
import java.util.concurrent.ExecutionException;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.geoserver.geofence.cache.CachedRuleLoaders.NamePw;
import org.geoserver.geofence.cache.RuleCacheLoaderFactory.NamePw;
import org.geoserver.geofence.services.RuleReaderService;
import org.geoserver.geofence.services.dto.AccessInfo;
import org.geoserver.geofence.services.dto.AuthUser;
Expand All @@ -25,9 +25,15 @@ public class CachedRuleReader implements RuleReaderService {

static final Logger LOGGER = Logging.getLogger(CachedRuleReader.class);

private final CacheManager cacheManager;
private CacheManager cacheManager;

public CachedRuleReader() {}

public CachedRuleReader(CacheManager cacheManager) {
setCacheManager(cacheManager);
}

public void setCacheManager(CacheManager cacheManager) {
this.cacheManager = cacheManager;
}

Expand Down
Loading

0 comments on commit b50682b

Please sign in to comment.