Skip to content

Commit

Permalink
speedup: use fast, async cache
Browse files Browse the repository at this point in the history
  • Loading branch information
Pfeil committed Aug 28, 2024
1 parent 86457be commit 6bb28b6
Show file tree
Hide file tree
Showing 8 changed files with 152 additions and 176 deletions.
4 changes: 2 additions & 2 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -57,8 +57,8 @@ dependencies {
// dependencies. It will automatically choose the fitting ones.
implementation("edu.kit.datamanager:service-base:1.2.0")
implementation("edu.kit.datamanager:repo-core:1.2.1")
// com.google.common, LoadingCache
implementation("com.google.guava:guava:33.2.1-jre")
// AsyncLoadingCache https://github.com/ben-manes/caffeine
implementation("com.github.ben-manes.caffeine:caffeine:3.1.8")
// Required by Spring/Javers at runtime
implementation 'com.google.code.gson:gson:2.10.1'

Expand Down
33 changes: 14 additions & 19 deletions src/main/java/edu/kit/datamanager/pit/Application.java
Original file line number Diff line number Diff line change
Expand Up @@ -19,11 +19,9 @@
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
import com.google.common.cache.CacheBuilder;
import com.google.common.cache.CacheLoader;
import com.google.common.cache.LoadingCache;
import com.google.common.cache.RemovalNotification;
import com.github.benmanes.caffeine.cache.AsyncLoadingCache;

import com.github.benmanes.caffeine.cache.Caffeine;
import edu.kit.datamanager.pit.cli.CliTaskBootstrap;
import edu.kit.datamanager.pit.cli.CliTaskWriteFile;
import edu.kit.datamanager.pit.cli.ICliTask;
Expand Down Expand Up @@ -150,21 +148,18 @@ public CacheConfig cacheConfig() {
* @return the cache
*/
@Bean
public LoadingCache<String, TypeDefinition> typeLoader(ApplicationProperties props) {
int maximumsize = props.getMaximumSize();
long expireafterwrite = props.getExpireAfterWrite();
return CacheBuilder.newBuilder()
.maximumSize(maximumsize)
.expireAfterWrite(expireafterwrite, TimeUnit.MINUTES)
.removalListener((RemovalNotification<String, TypeDefinition> rn) -> LOG.trace(
"Removing type definition located at {} from schema cache. Cause: {}", rn.getKey(),
rn.getCause()))
.build(new CacheLoader<String, TypeDefinition>() {
@Override
public TypeDefinition load(String typeIdentifier) throws IOException, URISyntaxException {
LOG.trace("Loading type definition for identifier {} to cache.", typeIdentifier);
return typeRegistry().queryTypeDefinition(typeIdentifier);
}
public AsyncLoadingCache<String, TypeDefinition> typeLoader(ApplicationProperties props) {
int maximumSize = props.getMaximumSize();
long expireAfterWrite = props.getExpireAfterWrite();
return Caffeine.newBuilder()
.maximumSize(maximumSize)
.expireAfterWrite(expireAfterWrite, TimeUnit.MINUTES)
.removalListener((key, value, cause) ->
LOG.trace("Removing type definition located at {} from schema cache. Cause: {}", key, cause)
)
.buildAsync(pid -> {
LOG.trace("Loading type definition for identifier {} to cache.", pid);
return typeRegistry().queryTypeDefinition(pid);
});
}

Expand Down
37 changes: 23 additions & 14 deletions src/main/java/edu/kit/datamanager/pit/domain/Operations.java
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,10 @@
import java.util.Date;
import java.util.List;
import java.util.Optional;
import java.util.concurrent.CompletableFuture;
import java.util.stream.Collectors;

import org.apache.commons.lang3.stream.Streams;
import org.joda.time.DateTime;
import org.joda.time.format.DateTimeFormatter;
import org.joda.time.format.ISODateTimeFormat;
Expand Down Expand Up @@ -69,14 +72,17 @@ public Optional<Date> findDateCreated(PIDRecord pidRecord) throws IOException {

/* TODO try to find types extending or relating otherwise to known types
* (currently not supported by our TypeDefinition) */
// we need to resolve types without streams to forward possible exceptions
Collection<TypeDefinition> types = new ArrayList<>();
for (String attributePid : pidRecord.getPropertyIdentifiers()) {
if (this.typingService.isIdentifierRegistered(attributePid)) {
TypeDefinition type = this.typingService.describeType(attributePid);
types.add(type);
}
}
List<CompletableFuture<?>> futures = Streams
.stream(pidRecord.getPropertyIdentifiers().stream())
.filter(attributePid -> this.typingService.isIdentifierRegistered(attributePid))
.map(attributePid -> {
return this.typingService
.describeType(attributePid)
.thenAcceptAsync(types::add);
})
.collect(Collectors.toList());
CompletableFuture.allOf(futures.toArray(new CompletableFuture[0]));

/*
* as a last fallback, try find types with human readable names containing
Expand Down Expand Up @@ -134,14 +140,17 @@ public Optional<Date> findDateModified(PIDRecord pidRecord) throws IOException {

/* TODO try to find types extending or relating otherwise to known types
* (currently not supported by our TypeDefinition) */
// we need to resolve types without streams to forward possible exceptions
Collection<TypeDefinition> types = new ArrayList<>();
for (String attributePid : pidRecord.getPropertyIdentifiers()) {
if (this.typingService.isIdentifierRegistered(attributePid)) {
TypeDefinition type = this.typingService.describeType(attributePid);
types.add(type);
}
}
List<CompletableFuture<?>> futures = Streams
.stream(pidRecord.getPropertyIdentifiers().stream())
.filter(attributePid -> this.typingService.isIdentifierRegistered(attributePid))
.map(attributePid -> {
return this.typingService
.describeType(attributePid)
.thenAcceptAsync(types::add);
})
.collect(Collectors.toList());
CompletableFuture.allOf(futures.toArray(new CompletableFuture[0]));

/*
* as a last fallback, try find types with human readable names containing
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
import edu.kit.datamanager.pit.domain.PIDRecord;
import edu.kit.datamanager.pit.domain.TypeDefinition;
import java.io.IOException;
import java.util.concurrent.CompletableFuture;

import edu.kit.datamanager.pit.pidsystem.IIdentifierSystem;

Expand All @@ -29,7 +30,7 @@ public void validate(PIDRecord pidRecord)
* record otherwise.
* @throws IOException
*/
public TypeDefinition describeType(String typeIdentifier) throws IOException;
public CompletableFuture<TypeDefinition> describeType(String typeIdentifier) throws IOException;

/**
* Queries a single property from the PID.
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package edu.kit.datamanager.pit.pitservice.impl;

import com.github.benmanes.caffeine.cache.AsyncLoadingCache;
import edu.kit.datamanager.pit.common.ExternalServiceException;
import edu.kit.datamanager.pit.common.RecordValidationException;
import edu.kit.datamanager.pit.configuration.ApplicationProperties;
Expand All @@ -8,14 +9,19 @@
import edu.kit.datamanager.pit.pitservice.IValidationStrategy;
import edu.kit.datamanager.pit.util.TypeValidationUtils;

import java.util.Arrays;
import java.util.List;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Executor;
import java.util.concurrent.Executors;
import java.util.stream.Collectors;

import org.apache.commons.lang3.stream.Streams;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;

import com.google.common.cache.LoadingCache;

/**
* Validates a PID record using embedded profile(s).
*
Expand All @@ -27,8 +33,10 @@ public class EmbeddedStrictValidatorStrategy implements IValidationStrategy {

private static final Logger LOG = LoggerFactory.getLogger(EmbeddedStrictValidatorStrategy.class);

private static final Executor EXECUTOR = Executors.newWorkStealingPool();

@Autowired
public LoadingCache<String, TypeDefinition> typeLoader;
public AsyncLoadingCache<String, TypeDefinition> typeLoader;

@Autowired
ApplicationProperties applicationProps;
Expand All @@ -50,26 +58,28 @@ public void validate(PIDRecord pidRecord) throws RecordValidationException, Exte
"Profile attribute " + profileKey + " has no values.");
}

for (String profilePID : profilePIDs) {
TypeDefinition profileDefinition;
try {
profileDefinition = this.typeLoader.get(profilePID);
} catch (ExecutionException e) {
LOG.error("Could not resolve identifier {}.", profilePID);
throw new ExternalServiceException(
applicationProps.getTypeRegistryUri().toString());
}
if (profileDefinition == null) {
LOG.error("No type definition found for identifier {}.", profilePID);
throw new RecordValidationException(
pidRecord,
String.format("No type found for identifier %s.", profilePID));
}

LOG.debug("validating profile {}", profilePID);
this.strictProfileValidation(pidRecord, profileDefinition);
LOG.debug("successfully validated {}", profilePID);
}
List<CompletableFuture<?>> futures = Streams.stream(Arrays.stream(profilePIDs))
.map(profilePID -> {
try {
return this.typeLoader.get(profilePID)
.thenAcceptAsync(profileDefinition -> {
if (profileDefinition == null) {
LOG.error("No type definition found for identifier {}.", profilePID);
throw new RecordValidationException(
pidRecord,
String.format("No type found for identifier %s.", profilePID));
}
this.strictProfileValidation(pidRecord, profileDefinition);
}, EXECUTOR);
} catch (RuntimeException e) {
LOG.error("Could not resolve identifier {}.", profilePID);
throw new ExternalServiceException(
applicationProps.getTypeRegistryUri().toString());
}
})
.collect(Collectors.toList());
CompletableFuture.allOf(futures.toArray(new CompletableFuture<?>[0])).join();
// TODO catch and unpack exceptions
}

/**
Expand All @@ -87,7 +97,7 @@ private void strictProfileValidation(PIDRecord pidRecord, TypeDefinition profile
// return profile.validate(jsonRecord);
// }

LOG.trace("Validating PID record against type definition.");
LOG.trace("Validating PID record against profile {}.", profile.getIdentifier());

TypeValidationUtils.checkMandatoryAttributes(pidRecord, profile);

Expand All @@ -109,6 +119,7 @@ private void strictProfileValidation(PIDRecord pidRecord, TypeDefinition profile

validateValuesForKey(pidRecord, attributeKey, type);
}
LOG.debug("successfully validated {}", profile.getIdentifier());
}

/**
Expand Down
Original file line number Diff line number Diff line change
@@ -1,16 +1,13 @@
package edu.kit.datamanager.pit.pitservice.impl;

import com.google.common.cache.LoadingCache;
import com.github.benmanes.caffeine.cache.AsyncLoadingCache;
import edu.kit.datamanager.pit.common.InvalidConfigException;
import edu.kit.datamanager.pit.common.PidAlreadyExistsException;
import edu.kit.datamanager.pit.common.PidNotFoundException;
import edu.kit.datamanager.pit.common.RecordValidationException;
import edu.kit.datamanager.pit.common.TypeNotFoundException;

import java.io.IOException;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.Optional;

import edu.kit.datamanager.pit.pidsystem.IIdentifierSystem;
Expand All @@ -21,6 +18,8 @@
import edu.kit.datamanager.pit.domain.Operations;
import edu.kit.datamanager.pit.domain.PIDRecord;
import edu.kit.datamanager.pit.domain.TypeDefinition;

import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;

import org.slf4j.Logger;
Expand All @@ -39,7 +38,7 @@ public class TypingService implements ITypingService {
private static final String LOG_MSG_QUERY_TYPE = "Querying for type with identifier {}.";


protected final LoadingCache<String, TypeDefinition> typeCache;
protected final AsyncLoadingCache<String, TypeDefinition> typeCache;
protected final IIdentifierSystem identifierSystem;
protected final ITypeRegistry typeRegistry;

Expand All @@ -54,7 +53,7 @@ public class TypingService implements ITypingService {
protected IValidationStrategy defaultStrategy = null;

public TypingService(IIdentifierSystem identifierSystem, ITypeRegistry typeRegistry,
LoadingCache<String, TypeDefinition> typeCache) {
AsyncLoadingCache<String, TypeDefinition> typeCache) {
super();
this.identifierSystem = identifierSystem;
this.typeRegistry = typeRegistry;
Expand Down Expand Up @@ -108,12 +107,12 @@ public boolean deletePID(String pid) throws ExternalServiceException {
}

@Override
public TypeDefinition describeType(String typeIdentifier) throws IOException {
public CompletableFuture<TypeDefinition> describeType(String typeIdentifier) throws IOException {
LOG.trace("Performing describeType({}).", typeIdentifier);
try {
LOG.trace(LOG_MSG_QUERY_TYPE, typeIdentifier);
return typeCache.get(typeIdentifier);
} catch (ExecutionException ex) {
} catch (RuntimeException ex) {
LOG.error("Failed to query for type with identifier " + typeIdentifier + ".", ex);
throw new InvalidConfigException(LOG_MSG_TYPING_SERVICE_MISCONFIGURED);
}
Expand Down Expand Up @@ -152,10 +151,9 @@ public PIDRecord queryProperty(String pid, String propertyIdentifier) throws IOE
TypeDefinition typeDef;
try {
LOG.trace(LOG_MSG_QUERY_TYPE, propertyIdentifier);
typeDef = typeCache.get(propertyIdentifier);
} catch (ExecutionException ex) {
typeDef = typeCache.get(propertyIdentifier).get();
} catch (ExecutionException | InterruptedException ex) {
LOG.error(LOG_MSG_QUERY_TYPE, propertyIdentifier);

throw new InvalidConfigException(LOG_MSG_TYPING_SERVICE_MISCONFIGURED);
}

Expand All @@ -172,8 +170,8 @@ private void enrichPIDInformationRecord(PIDRecord pidInfo) {
for (String typeIdentifier : pidInfo.getPropertyIdentifiers()) {
TypeDefinition typeDef;
try {
typeDef = typeCache.get(typeIdentifier);
} catch (ExecutionException ex) {
typeDef = typeCache.get(typeIdentifier).get();
} catch (ExecutionException | InterruptedException ex) {
throw new InvalidConfigException(LOG_MSG_TYPING_SERVICE_MISCONFIGURED);
}

Expand All @@ -190,8 +188,8 @@ public PIDRecord queryByType(String pid, String typeIdentifier, boolean includeP
throws IOException {
TypeDefinition typeDef;
try {
typeDef = typeCache.get(typeIdentifier);
} catch (ExecutionException ex) {
typeDef = typeCache.get(typeIdentifier).get();
} catch (ExecutionException | InterruptedException ex) {
throw new InvalidConfigException(LOG_MSG_TYPING_SERVICE_MISCONFIGURED);
}

Expand Down
Loading

0 comments on commit 6bb28b6

Please sign in to comment.