Skip to content
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

Add serialization timeout configurable property #99

Merged
merged 9 commits into from
Jan 11, 2024
4 changes: 1 addition & 3 deletions kubernetes-kit-demo/src/main/resources/application.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,9 @@ vaadin:
hazelcast:
service-name: kubernetes-kit-hazelcast-service
serialization:
timeout: 10000
transients:
include-packages: com.vaadin.kubernetes.demo
devmode:
sessionSerialization:
enabled: true



Original file line number Diff line number Diff line change
Expand Up @@ -162,10 +162,12 @@ PushSendListener pushSendListener(SessionSerializer sessionSerializer) {
@AutoConfiguration
@Conditional(VaadinReplicatedSessionDevModeConfiguration.OnSessionSerializationDebug.class)
public static class VaadinReplicatedSessionDevModeConfiguration {

@Bean
@ConditionalOnMissingBean
SerializationDebugRequestHandler.InitListener sessionSerializationDebugToolInstaller() {
return new SerializationDebugRequestHandler.InitListener();
SerializationDebugRequestHandler.InitListener sessionSerializationDebugToolInstaller(
SerializationProperties serializationProperties) {
return new SerializationDebugRequestHandler.InitListener(serializationProperties);
}

@Bean
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,9 +29,36 @@ public class SerializationProperties {

public static final String PREFIX = "vaadin.serialization";

public static final int DEFAULT_SERIALIZATION_TIMEOUT_MS = 30000;

private int timeout = DEFAULT_SERIALIZATION_TIMEOUT_MS;

@NestedConfigurationProperty
private final TransientsProperties transients = new TransientsProperties();

/**
* Gets the timeout in milliseconds to wait for the serialization
* to be completed.
*
* @return the timeout in milliseconds to wait for the serialization
* to be completed, defaults to 30000 ms
*/
public int getTimeout() {
return timeout;
}

/**
* Sets the timeout in milliseconds to wait for the serialization
* to be completed.
*
* @param timeout
* the timeout in milliseconds to wait for the serialization
* to be completed, defaults to 30000 ms
*/
public void setTimeout(int timeout) {
this.timeout = timeout;
}

/**
* Gets configuration for transient fields handling during serialization.
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -63,23 +63,28 @@ public void deleteSession(String clusterKey) {
}

/**
* Blocks the thread for up to 10 seconds, waiting for serialization to be
* completed.
* Blocks the thread for up to the defined timeout in milliseconds,
* waiting for serialization to be completed.
*
* @param timeout
* the timeout in milliseconds to wait for the serialization
* to be completed.
* @param logger
* the logger to add potential error information.
* @return the serialized session holder.
*/
SessionInfo waitForCompletion(Logger logger) {
int timeout = 10000;
SessionInfo waitForCompletion(int timeout, Logger logger) {
try {
if (!latch.await(timeout, TimeUnit.MILLISECONDS)) {
job.timeout();
logger.error(
"Session Serialization did not completed in {} ms.",
"Session serialization timed out because did not complete in {} ms. " +
"Increase the serialization timeout (in milliseconds) by the " +
"'vaadin.serialization.timeout' application or system property.",
timeout);
}
} catch (Exception e) { // NOSONAR
logger.error("Testing of Session Serialization failed", e);
logger.error("Testing of session serialization failed", e);
}
return sessionInfo;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,11 @@ void serializationFailed(Exception ex) {
Outcome.SERIALIZATION_FAILED.name() + ": " + ex.getMessage());
}

void timeout() {
outcome.remove(Outcome.SERIALIZATION_FAILED);
outcome.add(Outcome.SERIALIZATION_TIMEOUT);
}

void deserialized() {
outcome.remove(Outcome.DESERIALIZATION_FAILED);
}
Expand Down Expand Up @@ -238,15 +243,17 @@ Result complete() {
}
long duration = TimeUnit.NANOSECONDS
.toMillis(System.nanoTime() - this.startTimeNanos);
messages.computeIfPresent(Outcome.NOT_SERIALIZABLE_CLASSES.name(),
(unused, info) -> info.stream()
.flatMap(className -> Stream.concat(
Stream.of(className),
unserializableDetails
.getOrDefault(className,
Collections.emptyList())
.stream().map(entry -> "\t" + entry)))
.collect(Collectors.toList()));
if (!outcome.contains(Outcome.SERIALIZATION_TIMEOUT)) {
messages.computeIfPresent(Outcome.NOT_SERIALIZABLE_CLASSES.name(),
(unused, info) -> info.stream()
.flatMap(className -> Stream.concat(
Stream.of(className),
unserializableDetails
.getOrDefault(className,
Collections.emptyList())
.stream().map(entry -> "\t" + entry)))
.collect(Collectors.toList()));
}
return new Result(sessionId, storageKey, outcome, duration, messages);
mcollovati marked this conversation as resolved.
Show resolved Hide resolved
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,10 @@ public enum Outcome {
* Process failed during serialization phase
*/
SERIALIZATION_FAILED,
/**
* Serialization did not complete in time
*/
SERIALIZATION_TIMEOUT,
/**
* Process failed during deserialization phase
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
Expand All @@ -36,7 +37,7 @@ public class Result implements Serializable {
this.storageKey = storageKey;
this.outcomes = new LinkedHashSet<>(outcomes);
this.duration = duration;
this.messages = messages;
this.messages = new LinkedHashMap<>(messages);
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,16 +41,19 @@
import com.vaadin.flow.server.WrappedHttpSession;
import com.vaadin.flow.server.WrappedSession;
import com.vaadin.flow.server.startup.ApplicationConfiguration;
import com.vaadin.kubernetes.starter.SerializationProperties;
import com.vaadin.kubernetes.starter.sessiontracker.CurrentKey;
import com.vaadin.kubernetes.starter.sessiontracker.SessionSerializer;
import com.vaadin.kubernetes.starter.sessiontracker.backend.SessionInfo;
import com.vaadin.kubernetes.starter.ui.SessionDebugNotifier;

import static com.vaadin.kubernetes.starter.SerializationProperties.DEFAULT_SERIALIZATION_TIMEOUT_MS;

/**
* A {@link RequestHandler} implementation that performs a check on HTTP session
* serialization and deserialization.
*
* The request handler is executed only in development mode and it the
* The request handler is executed only in development mode and if the
* {@literal vaadin.devmode.sessionSerialization.enabled} configuration property
* is set to {@literal true}.
*
Expand Down Expand Up @@ -82,6 +85,14 @@ public class SerializationDebugRequestHandler implements RequestHandler {
public static final String SERIALIZATION_TEST_REQUEST_ATTRIBUTE_KEY = SerializationDebugRequestHandler.class
.getName() + ".RESULT";

private static final String SERIALIZATION_TIMEOUT_PROPERTY = "vaadin.serialization.timeout";

private final SerializationProperties serializationProperties;

public SerializationDebugRequestHandler(SerializationProperties serializationProperties) {
this.serializationProperties = serializationProperties;
}

@Override
public boolean handleRequest(VaadinSession vaadinSession,
VaadinRequest vaadinRequest, VaadinResponse vaadinResponse) {
Expand Down Expand Up @@ -122,9 +133,10 @@ public boolean handleRequest(VaadinSession vaadinSession,
});
}
}
int serializationTimeout = getSerializationTimeout(serializationProperties);
vaadinRequest.setAttribute(
SERIALIZATION_TEST_REQUEST_ATTRIBUTE_KEY,
new Runner(onSuccess));
new Runner(onSuccess, serializationTimeout));
});

} catch (Exception ex) {
Expand All @@ -137,9 +149,11 @@ public boolean handleRequest(VaadinSession vaadinSession,
static class Runner implements Consumer<HttpServletRequest> {

private final Consumer<Result> onSuccess;
private final int serializationTimeout;

public Runner(Consumer<Result> onSuccess) {
public Runner(Consumer<Result> onSuccess, int serializationTimeout) {
this.onSuccess = onSuccess;
this.serializationTimeout = serializationTimeout;
}

private void executeOnSuccess(Result result) {
Expand All @@ -157,7 +171,7 @@ public void accept(HttpServletRequest request) {
HttpSession session = request.getSession(false);
if (session != null && request.isRequestedSessionIdValid()) {
serializeAndDeserialize(new WrappedHttpSession(session),
this::executeOnSuccess);
this::executeOnSuccess, serializationTimeout);
}
}
}
Expand All @@ -175,14 +189,14 @@ protected void doFilter(HttpServletRequest req, HttpServletResponse res,
.getAttribute(SERIALIZATION_TEST_REQUEST_ATTRIBUTE_KEY);
if (action instanceof Runner) {
LOGGER.debug(
"Vaadin Request processed, Running Session Serialized Debug Tool");
"Vaadin request processed, running Session Serialization Debug Tool");
((Runner) action).accept(req);
}
}
}

public static void serializeAndDeserialize(WrappedSession session,
Consumer<Result> onComplete) {
Consumer<Result> onComplete, int serializationTimeout) {
// Work on a copy of the session to avoid overwriting attributes
DebugHttpSession debugHttpSession = new DebugHttpSession(session);
Job job = new Job(session.getId());
Expand All @@ -191,7 +205,7 @@ public static void serializeAndDeserialize(WrappedSession session,
new DebugTransientHandler(job));
try {
trySerialize(serializer, debugHttpSession, job);
SessionInfo info = connector.waitForCompletion(LOGGER);
SessionInfo info = connector.waitForCompletion(serializationTimeout, LOGGER);
if (info != null) {
debugHttpSession = new DebugHttpSession(
"DEBUG-DESERIALIZE-" + session.getId());
Expand All @@ -200,7 +214,7 @@ public static void serializeAndDeserialize(WrappedSession session,
} finally {
Result result = job.complete();
StringBuilder message = new StringBuilder(
"Session serialization attempt completed in ")
"Session serialization attempt finished in ")
.append(result.getDuration()).append(" ms with outcomes: ")
.append(result.getOutcomes());
List<String> errors = result.getErrors();
Expand Down Expand Up @@ -233,13 +247,26 @@ public static void serializeAndDeserialize(WrappedSession session,
}
}

private int getSerializationTimeout(SerializationProperties properties) {
int timeout = DEFAULT_SERIALIZATION_TIMEOUT_MS;
if (properties != null && properties.getTimeout() > 0) {
timeout = properties.getTimeout();
} else {
String timeoutStr = System.getProperty(SERIALIZATION_TIMEOUT_PROPERTY);
mcollovati marked this conversation as resolved.
Show resolved Hide resolved
if (timeoutStr != null) {
timeout = Integer.parseInt(timeoutStr);
}
}
return timeout;
}

private static void trySerialize(SessionSerializer serializer,
HttpSession session, Job job) {
try {
serializer.serialize(session);
} catch (Exception e) {
job.serializationFailed(e);
LOGGER.error("Test Session serialization failed", e);
LOGGER.error("Test session serialization failed", e);
}
}

Expand All @@ -250,7 +277,7 @@ private static void tryDeserialize(SessionSerializer serializer,
job.deserialized();
} catch (Exception e) {
job.deserializationFailed(e);
LOGGER.error("Test Session Deserialization failed", e);
LOGGER.error("Test session deserialization failed", e);
}
}

Expand All @@ -272,6 +299,15 @@ private static void tryDeserialize(SessionSerializer serializer,
*/
public static class InitListener implements VaadinServiceInitListener {

public SerializationProperties serializationProperties;

public InitListener() {
}

public InitListener(SerializationProperties serializationProperties) {
this.serializationProperties = serializationProperties;
}

@Override
public void serviceInit(ServiceInitEvent serviceInitEvent) {
ApplicationConfiguration appConfiguration = ApplicationConfiguration
Expand All @@ -291,7 +327,7 @@ public void serviceInit(ServiceInitEvent serviceInitEvent) {
logger.info(
"Installing SerializationDebugRequestHandler for session serialization debug");
serviceInitEvent.addRequestHandler(
new SerializationDebugRequestHandler());
new SerializationDebugRequestHandler(serializationProperties));
}
}
}
Expand Down
Loading