Caches for internal stores and files

This adds optional caches for configuration stores and
backing data files for data stores.

These stores can be enabled using the system properties
`scm.storeCache.enabled=true` and `scm.cache.dataFileCache.enabled=true`.

In addition, this adds the possibility to overwrite cache configurations
from the guice cache (see file `gcache.xml`) with system properties.
The maximum size of the external group cache for example can be
overwritten with the system property `scm.cache.externalGroups.maximumSize`.

Co-authored-by: René Pfeuffer <rene.pfeuffer@cloudogu.com>
This commit is contained in:
Eduard Heimbuch
2023-06-28 12:38:15 +02:00
parent 21a7995354
commit cc54e2ce6b
18 changed files with 706 additions and 205 deletions

View File

@@ -21,10 +21,8 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package sonia.scm.cache;
//~--- non-JDK imports --------------------------------------------------------
package sonia.scm.cache;
import com.google.common.base.MoreObjects;
@@ -35,215 +33,138 @@ import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;
import java.io.Serializable;
//~--- JDK imports ------------------------------------------------------------
/**
*
* @author Sebastian Sdorra
*/
@XmlRootElement(name = "cache")
@XmlAccessorType(XmlAccessType.FIELD)
public class GuavaCacheConfiguration implements Serializable
{
@XmlAccessorType(XmlAccessType.PROPERTY)
public class GuavaCacheConfiguration implements Serializable {
/** Field description */
private static final long serialVersionUID = -8734373158089010603L;
//~--- methods --------------------------------------------------------------
private Integer concurrencyLevel;
private CopyStrategy copyStrategy;
private Long expireAfterAccess;
private Long expireAfterWrite;
private Integer initialCapacity;
private Long maximumSize;
private Long maximumWeight;
private Boolean recordStats;
private Boolean softValues;
private Boolean weakKeys;
private Boolean weakValues;
/**
* Method description
*
*
* @return
*/
@Override
public String toString()
{
//J-
public String toString() {
return MoreObjects.toStringHelper(this)
.add("concurrencyLevel", concurrencyLevel)
.add("copyStrategy", copyStrategy)
.add("expireAfterAccess", expireAfterAccess)
.add("expireAfterWrite", expireAfterWrite)
.add("initialCapacity", initialCapacity)
.add("maximumSize", maximumSize)
.add("maximumWeight", maximumWeight)
.add("recordStats", recordStats)
.add("softValues", softValues)
.add("weakKeys", weakKeys)
.add("weakValues", weakValues)
.omitNullValues().toString();
//J+
.add("concurrencyLevel", concurrencyLevel)
.add("copyStrategy", copyStrategy)
.add("expireAfterAccess", expireAfterAccess)
.add("expireAfterWrite", expireAfterWrite)
.add("initialCapacity", initialCapacity)
.add("maximumSize", maximumSize)
.add("maximumWeight", maximumWeight)
.add("recordStats", recordStats)
.add("softValues", softValues)
.add("weakKeys", weakKeys)
.add("weakValues", weakValues)
.omitNullValues().toString();
}
//~--- get methods ----------------------------------------------------------
/**
* Method description
*
*
* @return
*/
public Integer getConcurrencyLevel()
{
public Integer getConcurrencyLevel() {
return concurrencyLevel;
}
/**
* Method description
*
*
* @return
*/
public CopyStrategy getCopyStrategy()
{
public CopyStrategy getCopyStrategy() {
return copyStrategy;
}
/**
* Method description
*
*
* @return
*/
public Long getExpireAfterAccess()
{
public Long getExpireAfterAccess() {
return expireAfterAccess;
}
/**
* Method description
*
*
* @return
*/
public Long getExpireAfterWrite()
{
public Long getExpireAfterWrite() {
return expireAfterWrite;
}
/**
* Method description
*
*
* @return
*/
public Integer getInitialCapacity()
{
public Integer getInitialCapacity() {
return initialCapacity;
}
/**
* Method description
*
*
* @return
*/
public Long getMaximumSize()
{
public Long getMaximumSize() {
return maximumSize;
}
/**
* Method description
*
*
* @return
*/
public Long getMaximumWeight()
{
public Long getMaximumWeight() {
return maximumWeight;
}
/**
* Method description
*
*
* @return
*/
public Boolean getRecordStats()
{
public Boolean getRecordStats() {
return recordStats;
}
/**
* Method description
*
*
* @return
*/
public Boolean getSoftValues()
{
public Boolean getSoftValues() {
return softValues;
}
/**
* Method description
*
*
* @return
*/
public Boolean getWeakKeys()
{
public Boolean getWeakKeys() {
return weakKeys;
}
/**
* Method description
*
*
* @return
*/
public Boolean getWeakValues()
{
public Boolean getWeakValues() {
return weakValues;
}
//~--- fields ---------------------------------------------------------------
/** Field description */
@XmlAttribute
private Integer concurrencyLevel;
void setConcurrencyLevel(Integer concurrencyLevel) {
this.concurrencyLevel = concurrencyLevel;
}
/** Field description */
@XmlAttribute
@XmlJavaTypeAdapter(XmlCopyStrategyAdapter.class)
private CopyStrategy copyStrategy;
void setCopyStrategy(CopyStrategy copyStrategy) {
this.copyStrategy = copyStrategy;
}
/** Field description */
@XmlAttribute
private Long expireAfterAccess;
void setExpireAfterAccess(Long expireAfterAccess) {
this.expireAfterAccess = expireAfterAccess;
}
/** Field description */
@XmlAttribute
private Long expireAfterWrite;
void setExpireAfterWrite(Long expireAfterWrite) {
this.expireAfterWrite = expireAfterWrite;
}
/** Field description */
@XmlAttribute
private Integer initialCapacity;
void setInitialCapacity(Integer initialCapacity) {
this.initialCapacity = initialCapacity;
}
/** Field description */
@XmlAttribute
private Long maximumSize;
void setMaximumSize(Long maximumSize) {
this.maximumSize = maximumSize;
}
/** Field description */
@XmlAttribute
private Long maximumWeight;
void setMaximumWeight(Long maximumWeight) {
this.maximumWeight = maximumWeight;
}
/** Field description */
@XmlAttribute
private Boolean recordStats;
void setRecordStats(Boolean recordStats) {
this.recordStats = recordStats;
}
/** Field description */
@XmlAttribute
private Boolean softValues;
void setSoftValues(Boolean softValues) {
this.softValues = softValues;
}
/** Field description */
@XmlAttribute
private Boolean weakKeys;
void setWeakKeys(Boolean weakKeys) {
this.weakKeys = weakKeys;
}
/** Field description */
@XmlAttribute
private Boolean weakValues;
void setWeakValues(Boolean weakValues) {
this.weakValues = weakValues;
}
}

View File

@@ -21,44 +21,133 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package sonia.scm.cache;
//~--- JDK imports ------------------------------------------------------------
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlAttribute;
import javax.xml.bind.annotation.XmlRootElement;
import java.util.Optional;
import java.util.function.Consumer;
import java.util.function.Function;
import static java.util.Optional.empty;
import static java.util.Optional.of;
/**
*
* @author Sebastian Sdorra
*/
@XmlRootElement(name = "cache")
@XmlAccessorType(XmlAccessType.FIELD)
public class GuavaNamedCacheConfiguration extends GuavaCacheConfiguration
{
@XmlAccessorType(XmlAccessType.PROPERTY)
public class GuavaNamedCacheConfiguration extends GuavaCacheConfiguration {
/** Field description */
private static final long serialVersionUID = -624795324874828475L;
private static final Logger LOG = LoggerFactory.getLogger(GuavaNamedCacheConfiguration.class);
//~--- get methods ----------------------------------------------------------
private String name;
/**
* Method description
*
*
* @return
*/
public String getName()
{
return name;
}
//~--- fields ---------------------------------------------------------------
/** Field description */
@XmlAttribute
private String name;
private void setName(String name) {
this.name = name;
}
@Override
void setConcurrencyLevel(Integer concurrencyLevel) {
setIntegerValue("concurrencyLevel", concurrencyLevel, super::setConcurrencyLevel);
}
@Override
void setCopyStrategy(CopyStrategy copyStrategy) {
setValue("copyStrategy", copyStrategy, super::setCopyStrategy, propertyName -> of(System.getProperty(propertyName)).map(CopyStrategy::valueOf).orElse(null));
}
@Override
void setExpireAfterAccess(Long expireAfterAccess) {
setLongValue("expireAfterAccess", expireAfterAccess, super::setExpireAfterAccess);
}
@Override
void setExpireAfterWrite(Long expireAfterWrite) {
setLongValue("expireAfterWrite", expireAfterWrite, super::setExpireAfterWrite);
}
@Override
void setInitialCapacity(Integer initialCapacity) {
setIntegerValue("initialCapacity", initialCapacity, super::setInitialCapacity);
}
@Override
void setMaximumSize(Long maximumSize) {
setLongValue("maximumSize", maximumSize, super::setMaximumSize);
}
@Override
void setMaximumWeight(Long maximumWeight) {
setLongValue("maximumWeight", maximumWeight, super::setMaximumWeight);
}
@Override
void setRecordStats(Boolean recordStats) {
setBooleanValue("recordStats", recordStats, super::setRecordStats);
}
@Override
void setSoftValues(Boolean softValues) {
setBooleanValue("softValues", softValues, super::setSoftValues);
}
@Override
void setWeakKeys(Boolean weakKeys) {
setBooleanValue("weakKeys", weakKeys, super::setWeakKeys);
}
@Override
void setWeakValues(Boolean weakValues) {
setBooleanValue("weakValues", weakValues, super::setWeakValues);
}
private void setIntegerValue(String propertyName, Integer originalValue, Consumer<Integer> setter) {
setValue(propertyName, originalValue, setter, Integer::getInteger);
}
private void setLongValue(String propertyName, Long originalValue, Consumer<Long> setter) {
setValue(propertyName, originalValue, setter, Long::getLong);
}
private void setBooleanValue(String propertyName, Boolean originalValue, Consumer<Boolean> setter) {
setValue(propertyName, originalValue, setter, Boolean::getBoolean);
}
private <T> void setValue(String propertyName, T originalValue, Consumer<T> setter, Function<String, T> systemPropertyReader) {
setter.accept(originalValue);
createPropertyName(propertyName)
.map(systemPropertyReader)
.ifPresent(value -> {
logOverwrite(propertyName, originalValue, value);
setter.accept(value);
});
}
private void logOverwrite(String propertyName, Object originalValue, Object overwrittenValue) {
LOG.debug("overwrite {} of cache '{}' with system property value {} (original value: {})", propertyName, name, overwrittenValue, originalValue);
}
private Optional<String> createPropertyName(String fieldName) {
if (name == null) {
LOG.warn("failed to overwrite cache configuration with system properties, because name has not been set yet");
return empty();
}
if (name.startsWith("sonia.cache.")) {
return of("scm.cache." + name.substring("sonia.cache.".length()) + "." + fieldName);
}
return empty();
}
}

View File

@@ -28,14 +28,19 @@ import com.google.inject.AbstractModule;
import com.google.inject.TypeLiteral;
import com.google.inject.multibindings.Multibinder;
import com.google.inject.throwingproviders.ThrowingProviderBinder;
import io.micrometer.core.instrument.MeterRegistry;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import sonia.scm.SCMContext;
import sonia.scm.SCMContextProvider;
import sonia.scm.cache.CacheManager;
import sonia.scm.cache.GuavaCacheManager;
import sonia.scm.io.DefaultFileSystem;
import sonia.scm.io.FileSystem;
import sonia.scm.lifecycle.DefaultRestarter;
import sonia.scm.lifecycle.Restarter;
import sonia.scm.metrics.MeterRegistryProvider;
import sonia.scm.metrics.MonitoringSystem;
import sonia.scm.plugin.PluginLoader;
import sonia.scm.repository.DefaultRepositoryExportingCheck;
import sonia.scm.repository.EventDrivenRepositoryArchiveCheck;
@@ -121,6 +126,14 @@ public class BootstrapModule extends AbstractModule {
bind(RepositoryUpdateIterator.class, FileRepositoryUpdateIterator.class);
bind(StoreUpdateStepUtilFactory.class, FileStoreUpdateStepUtilFactory.class);
bind(new TypeLiteral<UpdateStepRepositoryMetadataAccess<Path>>() {}).to(new TypeLiteral<MetadataStore>() {});
// bind metrics
bind(MeterRegistry.class).toProvider(MeterRegistryProvider.class).asEagerSingleton();
Multibinder.newSetBinder(binder(), MonitoringSystem.class);
// bind cache
bind(CacheManager.class, GuavaCacheManager.class);
bind(org.apache.shiro.cache.CacheManager.class, GuavaCacheManager.class);
}
private <T> void bind(Class<T> clazz, Class<? extends T> defaultImplementation) {

View File

@@ -30,7 +30,6 @@ import com.google.inject.multibindings.Multibinder;
import com.google.inject.servlet.RequestScoped;
import com.google.inject.servlet.ServletModule;
import com.google.inject.throwingproviders.ThrowingProviderBinder;
import io.micrometer.core.instrument.MeterRegistry;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import sonia.scm.Default;
@@ -39,14 +38,13 @@ import sonia.scm.PushStateDispatcher;
import sonia.scm.PushStateDispatcherProvider;
import sonia.scm.RootURL;
import sonia.scm.Undecorated;
import sonia.scm.admin.ScmConfigurationStore;
import sonia.scm.api.rest.ObjectMapperProvider;
import sonia.scm.api.v2.resources.BranchLinkProvider;
import sonia.scm.api.v2.resources.DefaultBranchLinkProvider;
import sonia.scm.api.v2.resources.DefaultRepositoryLinkProvider;
import sonia.scm.api.v2.resources.RepositoryLinkProvider;
import sonia.scm.auditlog.AuditLogConfigurationStoreDecoratorFactory;
import sonia.scm.cache.CacheManager;
import sonia.scm.cache.GuavaCacheManager;
import sonia.scm.config.ScmConfiguration;
import sonia.scm.event.ScmEventBus;
import sonia.scm.group.DefaultGroupCollector;
@@ -65,7 +63,6 @@ import sonia.scm.initialization.InitializationCookieIssuer;
import sonia.scm.initialization.InitializationFinisher;
import sonia.scm.io.ContentTypeResolver;
import sonia.scm.io.DefaultContentTypeResolver;
import sonia.scm.metrics.MeterRegistryProvider;
import sonia.scm.migration.MigrationDAO;
import sonia.scm.net.SSLContextProvider;
import sonia.scm.net.TrustManagerProvider;
@@ -134,7 +131,6 @@ import sonia.scm.user.UserManager;
import sonia.scm.user.UserManagerProvider;
import sonia.scm.user.xml.XmlUserDAO;
import sonia.scm.util.DebugServlet;
import sonia.scm.admin.ScmConfigurationStore;
import sonia.scm.web.UserAgentParser;
import sonia.scm.web.cgi.CGIExecutorFactory;
import sonia.scm.web.cgi.DefaultCGIExecutorFactory;
@@ -200,8 +196,6 @@ class ScmServletModule extends ServletModule {
// bind extensions
pluginLoader.getExtensionProcessor().processAutoBindExtensions(binder());
// bind metrics
bind(MeterRegistry.class).toProvider(MeterRegistryProvider.class).asEagerSingleton();
// bind security stuff
bind(LoginAttemptHandler.class).to(ConfigurableLoginAttemptHandler.class);
@@ -210,10 +204,6 @@ class ScmServletModule extends ServletModule {
bind(SecuritySystem.class).to(DefaultSecuritySystem.class);
bind(AdministrationContext.class, DefaultAdministrationContext.class);
// bind cache
bind(CacheManager.class, GuavaCacheManager.class);
bind(org.apache.shiro.cache.CacheManager.class, GuavaCacheManager.class);
// bind dao
bind(GroupDAO.class, XmlGroupDAO.class);
bind(UserDAO.class, XmlUserDAO.class);