Skip to content

Commit

Permalink
Merge pull request #29108 from patriot1burke/warmup-stage
Browse files Browse the repository at this point in the history
Warmup stage
  • Loading branch information
patriot1burke authored Nov 17, 2022
2 parents af61fb8 + 0ed50ff commit 431666d
Show file tree
Hide file tree
Showing 18 changed files with 382 additions and 76 deletions.
8 changes: 8 additions & 0 deletions core/deployment/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -218,6 +218,14 @@
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>10</source>
<target>10</target>
</configuration>
</plugin>
</plugins>
</build>

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
package io.quarkus.deployment;

import java.util.Optional;

import io.quarkus.runtime.annotations.ConfigItem;
import io.quarkus.runtime.annotations.ConfigPhase;
import io.quarkus.runtime.annotations.ConfigRoot;

@ConfigRoot(phase = ConfigPhase.BUILD_TIME)
public class CracConfig {

/**
* Enable/Disable CRAC integration
* <p>
* Default value is dependent on extensions deployed
* (i.e. when using AWS Lambda extensions, this will be set to true by default)
*/
@ConfigItem
Optional<Boolean> enable;

/**
* Will do a classpath search for all META-INF/quarkus-preload-classes.txt files
* These files contain fully qualified classnames that should be loaded
* in the CRAC`beforeCheckpoint()` phase
*/
@ConfigItem(defaultValue = "true")
boolean preloadClasses;

/**
* if preloading classes, specify whether or not
* to do static initialization when preloading these classes.
*/
@ConfigItem(defaultValue = "true")
boolean initializeClasses;

/**
* Perform Application.start() within CRAC `beforeCheckpoint()` phase.
*/
@ConfigItem(defaultValue = "true")
boolean fullWarmup;

/**
* When CRAC is enabled, it generates the application class list so it can be preloaded.
*/
@ConfigItem(defaultValue = "true")
boolean generateApplicationClassList;

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
package io.quarkus.deployment;

import java.util.List;
import java.util.Optional;
import java.util.Set;

import org.jboss.jandex.ClassInfo;
import org.jboss.logging.Logger;

import io.quarkus.deployment.annotations.BuildProducer;
import io.quarkus.deployment.annotations.BuildStep;
import io.quarkus.deployment.annotations.ExecutionTime;
import io.quarkus.deployment.annotations.Record;
import io.quarkus.deployment.builditem.ApplicationArchivesBuildItem;
import io.quarkus.deployment.builditem.CracDefaultValueBuildItem;
import io.quarkus.deployment.builditem.CracEnabledBuildItem;
import io.quarkus.deployment.builditem.GeneratedClassBuildItem;
import io.quarkus.deployment.builditem.PreloadClassBuildItem;
import io.quarkus.deployment.builditem.PreloadClassesEnabledBuildItem;
import io.quarkus.deployment.builditem.TransformedClassesBuildItem;
import io.quarkus.deployment.pkg.steps.NativeBuild;
import io.quarkus.runtime.CracRecorder;

public class CracProcessor {

private static Logger logger = Logger.getLogger(CracProcessor.class);

@BuildStep(onlyIf = IsNormal.class, onlyIfNot = NativeBuild.class)
@Record(ExecutionTime.STATIC_INIT)
public void processCrac(BuildProducer<PreloadClassesEnabledBuildItem> preload,
BuildProducer<CracEnabledBuildItem> cracEnabled,
CracRecorder crac,
CracConfig config,
Optional<CracDefaultValueBuildItem> defaultVal) {
if (config.enable.isPresent()) {
if (!config.enable.get().booleanValue()) {
return;
}

} else if (defaultVal == null || !defaultVal.isPresent() || !defaultVal.get().isDefaultValue()) {
return;
}
cracEnabled.produce(CracEnabledBuildItem.INSTANCE);
if (config.preloadClasses)
preload.produce(new PreloadClassesEnabledBuildItem(config.initializeClasses));
crac.register(config.fullWarmup);
}

@BuildStep(onlyIf = IsNormal.class, onlyIfNot = NativeBuild.class)
public void generateClassListFromApplication(
CracConfig config,
Optional<CracDefaultValueBuildItem> defaultVal,
BuildProducer<PreloadClassBuildItem> producer,
TransformedClassesBuildItem transformedClasses,
ApplicationArchivesBuildItem applicationArchivesBuildItem,
List<GeneratedClassBuildItem> generatedClasses) {
if (config.enable.isPresent()) {
if (!config.enable.get()) {
return;
}
} else if (defaultVal == null || !defaultVal.isPresent() || !defaultVal.get().isDefaultValue()) {
return;
}

if (config.generateApplicationClassList) {
for (Set<TransformedClassesBuildItem.TransformedClass> transformedSet : transformedClasses
.getTransformedClassesByJar().values()) {
for (TransformedClassesBuildItem.TransformedClass transformed : transformedSet) {
String className = transformed.getClassName();
if (className != null) {
producer.produce(new PreloadClassBuildItem(className));
}
}
}

for (GeneratedClassBuildItem i : generatedClasses) {
if (i.isApplicationClass()) {
if (i.getName() != null) {
String cn = i.getName().replace("/", ".");
producer.produce(new PreloadClassBuildItem(cn));
}
}
}

for (ClassInfo clz : applicationArchivesBuildItem.getRootArchive().getIndex().getKnownClasses()) {
producer.produce(new PreloadClassBuildItem(clz.toString()));
}
}
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package io.quarkus.deployment.builditem;

import io.quarkus.builder.item.SimpleBuildItem;

/**
* Allows extensions to set default value for enabling CRAC
*/
public final class CracDefaultValueBuildItem extends SimpleBuildItem {
private final boolean defaultValue;

public CracDefaultValueBuildItem(boolean defaultValue) {
this.defaultValue = defaultValue;
}

public boolean isDefaultValue() {
return defaultValue;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package io.quarkus.deployment.builditem;

import io.quarkus.builder.item.SimpleBuildItem;

/**
* Marker item to specify that CRAC is enabled
*/
public final class CracEnabledBuildItem extends SimpleBuildItem {
public static final CracEnabledBuildItem INSTANCE = new CracEnabledBuildItem();

private CracEnabledBuildItem() {
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package io.quarkus.deployment.builditem;

import io.quarkus.builder.item.MultiBuildItem;
import io.smallrye.common.constraint.Assert;

/**
* Class to be preloaded in static initialization phase of Quarkus
*/
public final class PreloadClassBuildItem extends MultiBuildItem {
private final String className;

/**
* Construct a new instance.
*
* @param className the class name (must not be {@code null})
*/
public PreloadClassBuildItem(final String className) {
this.className = Assert.checkNotNullParam("className", className);
}

/**
* Get the class name.
*
* @return the class name (not {@code null})
*/
public String getClassName() {
return className;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package io.quarkus.deployment.builditem;

import io.quarkus.builder.item.SimpleBuildItem;

/**
* Extension build steps can produce this if preloading classes is enabled
*/
public final class PreloadClassesEnabledBuildItem extends SimpleBuildItem {
private final boolean initialize;

public PreloadClassesEnabledBuildItem(boolean initialize) {
this.initialize = initialize;
}

public boolean doInitialize() {
return initialize;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
package io.quarkus.deployment.steps;

import static io.quarkus.runtime.PreloadClassesRecorder.QUARKUS_GENERATED_PRELOAD_CLASSES_FILE;

import java.nio.charset.StandardCharsets;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;

import io.quarkus.deployment.annotations.BuildStep;
import io.quarkus.deployment.annotations.ExecutionTime;
import io.quarkus.deployment.annotations.Record;
import io.quarkus.deployment.builditem.GeneratedResourceBuildItem;
import io.quarkus.deployment.builditem.PreloadClassBuildItem;
import io.quarkus.deployment.builditem.PreloadClassesEnabledBuildItem;
import io.quarkus.runtime.PreloadClassesRecorder;

public class PreloadClassesBuildStep {
@BuildStep
@Record(ExecutionTime.STATIC_INIT)
public void preInit(Optional<PreloadClassesEnabledBuildItem> preload, PreloadClassesRecorder recorder) {
if (!preload.isPresent())
return;
recorder.invokePreloadClasses(preload.get().doInitialize());
}

@BuildStep
public GeneratedResourceBuildItem registerPreInitClasses(List<PreloadClassBuildItem> items) {
if (items == null || items.isEmpty())
return null;
// ensure unique & sorted
final String names = items.stream().map(PreloadClassBuildItem::getClassName).sorted().distinct()
.map(s -> s.concat(System.lineSeparator())).collect(Collectors.joining());
return new GeneratedResourceBuildItem("META-INF/" + QUARKUS_GENERATED_PRELOAD_CLASSES_FILE,
names.getBytes(StandardCharsets.UTF_8));
}
}
5 changes: 0 additions & 5 deletions core/runtime/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -94,11 +94,6 @@
<groupId>io.quarkus</groupId>
<artifactId>quarkus-fs-util</artifactId>
</dependency>
<dependency>
<groupId>io.github.crac</groupId>
<artifactId>org-crac</artifactId>
</dependency>

<!--
This is required to ensure that the extension processor is built.
If we move the extension processor to a separate project, this can be removed.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ public abstract class Application implements Closeable {
private final Condition stateCond = stateLock.newCondition();

private int state = ST_INITIAL;
private static volatile Application currentApplication;
protected static volatile Application currentApplication;

/**
* Embedded applications don't set up or modify logging, and don't provide start/
Expand Down
18 changes: 18 additions & 0 deletions core/runtime/src/main/java/io/quarkus/runtime/CracRecorder.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package io.quarkus.runtime;

import io.quarkus.runtime.annotations.Recorder;

/**
* Registers a CRAC resource. Must be called in static initialization phase!
*/
@Recorder
public class CracRecorder {

public static boolean enabled = false;
public static boolean fullWarmup = false;

public void register(boolean fw) {
enabled = true;
fullWarmup = fw;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
package io.quarkus.runtime;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.URL;
import java.net.URLConnection;
import java.util.Enumeration;

import io.quarkus.runtime.annotations.Recorder;

@Recorder
public class PreloadClassesRecorder {
public static final String QUARKUS_GENERATED_PRELOAD_CLASSES_FILE = "quarkus-generated-preload-classes.txt";

public static void preloadClass(String classname, boolean initialize) {
try {
Class.forName(classname, initialize, PreloadClassesRecorder.class.getClassLoader());
} catch (Throwable ignored) {

}
}

public static void preloadClasses(boolean initialize) {
try {
Enumeration<URL> files = PreloadClassesRecorder.class.getClassLoader()
.getResources("META-INF/quarkus-preload-classes.txt");
while (files.hasMoreElements()) {
URL url = files.nextElement();
URLConnection conn = url.openConnection();
conn.setUseCaches(false);
InputStream is = conn.getInputStream();
preloadClassesFromStream(is, initialize);
}
} catch (IOException ignored) {
}
InputStream is = PreloadClassesRecorder.class
.getResourceAsStream("/META-INF/" + QUARKUS_GENERATED_PRELOAD_CLASSES_FILE);
if (is != null)
preloadClassesFromStream(is, initialize);
}

public static void preloadClassesFromStream(InputStream is, boolean initialize) {
try (is;
InputStreamReader isr = new InputStreamReader(is);
BufferedReader reader = new BufferedReader(isr)) {
String line;
while ((line = reader.readLine()) != null) {
int idx = line.indexOf('#');
if (idx != -1) {
line = line.substring(0, idx);
}
final String className = line.stripTrailing();
if (!className.isBlank()) {
preloadClass(className, initialize);
}
}
} catch (Exception ignored) {

}
}

public void invokePreloadClasses(boolean initialize) {
preloadClasses(initialize);
}
}
Loading

0 comments on commit 431666d

Please sign in to comment.