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

WIP feat(reports): reports sidecar, configurable caching #65

Closed
wants to merge 18 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@

<org.apache.commons.codec.version>1.15</org.apache.commons.codec.version>
<org.apache.commons.io.version>2.11.0</org.apache.commons.io.version>
<org.apache.httpcomponents.version>4.5.14</org.apache.httpcomponents.version>
<org.apache.httpcomponents.version>5.2.1</org.apache.httpcomponents.version>
<org.apache.commons.lang3.version>3.12.0</org.apache.commons.lang3.version>
<org.apache.commons.validator.version>1.7</org.apache.commons.validator.version>
<org.projectnessie.cel.bom.version>0.3.21</org.projectnessie.cel.bom.version>
Expand Down Expand Up @@ -131,7 +131,7 @@
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-hibernate-validator</artifactId>
<artifactId>quarkus-hibernate-validator</artifactId>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
Expand Down Expand Up @@ -161,8 +161,8 @@
<version>${org.apache.commons.io.version}</version>
</dependency>
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
<groupId>org.apache.httpcomponents.client5</groupId>
<artifactId>httpclient5</artifactId>
<version>${org.apache.httpcomponents.version}</version>
</dependency>
<dependency>
Expand Down
22 changes: 15 additions & 7 deletions smoketest.bash
Original file line number Diff line number Diff line change
@@ -1,10 +1,5 @@
#!/usr/bin/env bash

if ! command -v yq; then
echo "No 'yq' found"
exit 1
fi

FILES=(
./smoketest/compose/db.yml
./smoketest/compose/cryostat.yml
Expand All @@ -16,16 +11,21 @@ KEEP_VOLUMES=false
display_usage() {
echo "Usage:"
echo -e "\t-O \t\t\t\tOffline mode, do not attempt to pull container images."
echo -e "\t-s [minio|localstack]\t\tS3 implementation to spin up. (default \"minio\")"
echo -e "\t-s [minio|localstack]\t\tS3 implementation to spin up. Minio has a nice UI and implements more features, localstack has nice log messages. (default \"minio\")"
echo -e "\t-g \t\t\t\tinclude Grafana dashboard and jfr-datasource in deployment."
echo -e "\t-r\t\t\t\tconfigure a cryostat-reports sidecar instance"
echo -e "\t-t \t\t\t\tinclude sample applications for Testing."
echo -e "\t-V \t\t\t\tdo not discard data storage Volumes on exit."
echo -e "\t-X \t\t\t\tdeploy additional development aid tools."
}

s3=minio
while getopts "s:gtOVX" opt; do
while getopts "hs:rgtOVX" opt; do
case $opt in
h)
display_usage
exit
;;
s)
s3="${OPTARG}"
;;
Expand All @@ -44,13 +44,21 @@ while getopts "s:gtOVX" opt; do
X)
FILES+=('./smoketest/compose/db-viewer.yml')
;;
r)
FILES+=('./smoketest/compose/reports.yml')
;;
*)
display_usage
exit 1
;;
esac
done

if ! command -v yq; then
echo "No 'yq' found"
exit 1
fi

if [ "${s3}" = "minio" ]; then
FILES+=('./smoketest/compose/s3-minio.yml')
elif [ "${s3}" = "localstack" ]; then
Expand Down
26 changes: 26 additions & 0 deletions smoketest/compose/reports.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
version: "3"
services:
cryostat:
environment:
- CRYOSTAT_SERVICES_REPORTS_URL=http://reports:10001
reports:
image: quay.io/cryostat/cryostat-reports:latest
hostname: reports
restart: unless-stopped
deploy:
resources:
limits:
cpus: '0.5'
memory: 512m
ports:
- "10001:10001"
expose:
- "10001"
labels:
kompose.service.expose: "reports"
io.cryostat.discovery: "true"
io.cryostat.jmxHost: "reports"
io.cryostat.jmxPort: "11224"
environment:
JAVA_OPTS_APPEND: "-Dcom.sun.management.jmxremote.autodiscovery=true -Dcom.sun.management.jmxremote -Dcom.sun.management.jmxremote.port=11224 -Dcom.sun.management.jmxremote.rmi.port=11224 -Djava.rmi.server.hostname=reports -Dcom.sun.management.jmxremote.authenticate=false -Dcom.sun.management.jmxremote.ssl=false -Dcom.sun.management.jmxremote.local.only=false"
QUARKUS_HTTP_PORT: 10001
12 changes: 12 additions & 0 deletions src/main/java/io/cryostat/ConfigProperties.java
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,18 @@ public class ConfigProperties {
public static final String AWS_OBJECT_EXPIRATION_LABELS =
"storage.buckets.archives.expiration-label";

public static final String REPORTS_SIDECAR_URL = "cryostat.services.reports.url";
public static final String MEMORY_CACHE_ENABLED =
"cryostat.services.reports.memory-cache.enabled";
public static final String ACTIVE_REPORTS_MEMORY_CACHE_NAME =
"cryostat.services.reports.memory-cache.active.name";
public static final String ARCHIVED_REPORTS_MEMORY_CACHE_NAME =
"cryostat.services.reports.memory-cache.archived.name";
public static final String STORAGE_CACHE_ENABLED =
"cryostat.services.reports.storage-cache.enabled";
public static final String ARCHIVED_REPORTS_STORAGE_CACHE_NAME =
"cryostat.services.reports.storage-cache.name";

public static final String GRAFANA_DASHBOARD_URL = "grafana-dashboard.url";
public static final String GRAFANA_DASHBOARD_EXT_URL = "grafana-dashboard-ext.url";
public static final String GRAFANA_DATASOURCE_URL = "grafana-datasource.url";
Expand Down
14 changes: 0 additions & 14 deletions src/main/java/io/cryostat/Producers.java
Original file line number Diff line number Diff line change
Expand Up @@ -16,20 +16,16 @@
package io.cryostat;

import java.net.URI;
import java.util.Set;
import java.util.concurrent.Executors;
import java.util.concurrent.ForkJoinPool;
import java.util.concurrent.ScheduledExecutorService;

import io.cryostat.core.reports.InterruptibleReportGenerator;
import io.cryostat.core.sys.Clock;
import io.cryostat.core.sys.FileSystem;

import io.quarkus.arc.DefaultBean;
import io.vertx.mutiny.core.Vertx;
import io.vertx.mutiny.ext.web.client.WebClient;
import jakarta.enterprise.context.ApplicationScoped;
import jakarta.enterprise.context.RequestScoped;
import jakarta.enterprise.inject.Produces;
import jakarta.inject.Named;
import org.apache.commons.codec.binary.Base32;
Expand Down Expand Up @@ -81,16 +77,6 @@ public static ScheduledExecutorService produceScheduledExecutorService() {
return Executors.newSingleThreadScheduledExecutor();
}

@Produces
// RequestScoped so that each individual report generation request has its own interruptible
// generator with an independent task queueing thread which dispatches to the shared common pool
@RequestScoped
@DefaultBean
public static InterruptibleReportGenerator produceInterruptibleReportGenerator() {
return new InterruptibleReportGenerator(
io.cryostat.core.log.Logger.INSTANCE, Set.of(), ForkJoinPool.commonPool());
}

@Produces
@DefaultBean
public WebClient produceWebClient(Vertx vertx) {
Expand Down
4 changes: 1 addition & 3 deletions src/main/java/io/cryostat/discovery/DiscoveryNode.java
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,6 @@
import com.fasterxml.jackson.annotation.JsonView;
import io.quarkus.hibernate.orm.panache.PanacheEntity;
import io.quarkus.vertx.ConsumeEvent;
import io.smallrye.common.annotation.Blocking;
import io.vertx.mutiny.core.eventbus.EventBus;
import jakarta.enterprise.context.ApplicationScoped;
import jakarta.inject.Inject;
Expand Down Expand Up @@ -152,8 +151,7 @@ static class Listener {
@Inject EventBus bus;

@Transactional
@Blocking
@ConsumeEvent(Target.TARGET_JVM_DISCOVERY)
@ConsumeEvent(value = Target.TARGET_JVM_DISCOVERY, blocking = true)
void onMessage(TargetDiscovery event) {
switch (event.kind()) {
case LOST:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,6 @@
import io.quarkus.cache.CaffeineCache;
import io.quarkus.cache.CompositeCacheKey;
import io.quarkus.vertx.ConsumeEvent;
import io.smallrye.common.annotation.Blocking;
import jakarta.enterprise.context.ApplicationScoped;
import jakarta.inject.Inject;
import jdk.jfr.Category;
Expand All @@ -58,8 +57,7 @@ public class MatchExpressionEvaluator {
@Inject Logger logger;
@Inject CacheManager cacheManager;

@Blocking
@ConsumeEvent(MatchExpression.EXPRESSION_ADDRESS)
@ConsumeEvent(value = MatchExpression.EXPRESSION_ADDRESS, blocking = true)
void onMessage(ExpressionEvent event) {
switch (event.category()) {
case CREATED:
Expand Down
84 changes: 66 additions & 18 deletions src/main/java/io/cryostat/recordings/ActiveRecording.java
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
import org.openjdk.jmc.rjmx.services.jfr.FlightRecorderException;
import org.openjdk.jmc.rjmx.services.jfr.IRecordingDescriptor;

import io.cryostat.recordings.Recordings.ArchivedRecording;
import io.cryostat.recordings.Recordings.LinkedRecordingDescriptor;
import io.cryostat.recordings.Recordings.Metadata;
import io.cryostat.targets.Target;
Expand Down Expand Up @@ -111,7 +112,7 @@ public static ActiveRecording from(
recording.name = descriptor.getName();
switch (descriptor.getState()) {
case CREATED:
recording.state = RecordingState.NEW;
recording.state = RecordingState.DELAYED;
break;
case RUNNING:
recording.state = RecordingState.RUNNING;
Expand All @@ -123,6 +124,7 @@ public static ActiveRecording from(
recording.state = RecordingState.STOPPED;
break;
default:
recording.state = RecordingState.NEW;
break;
}
recording.duration = descriptor.getDuration().in(UnitLookup.MILLISECOND).longValue();
Expand Down Expand Up @@ -171,7 +173,12 @@ static class Listener {

@PostPersist
public void postPersist(ActiveRecording activeRecording) {
notify("ActiveRecordingCreated", activeRecording);
bus.publish(
Recordings.RecordingEventCategory.ACTIVE_CREATED.category(), activeRecording);
notify(
new ActiveRecordingEvent(
Recordings.RecordingEventCategory.ACTIVE_CREATED,
ActiveRecordingEvent.Payload.of(recordingHelper, activeRecording)));
}

@PreUpdate
Expand Down Expand Up @@ -206,7 +213,13 @@ public void preUpdate(ActiveRecording activeRecording) throws Exception {
@PostUpdate
public void postUpdate(ActiveRecording activeRecording) {
if (RecordingState.STOPPED.equals(activeRecording.state)) {
notify("ActiveRecordingStopped", activeRecording);
bus.publish(
Recordings.RecordingEventCategory.ACTIVE_STOPPED.category(),
activeRecording);
notify(
new ActiveRecordingEvent(
Recordings.RecordingEventCategory.ACTIVE_STOPPED,
ActiveRecordingEvent.Payload.of(recordingHelper, activeRecording)));
}
}

Expand All @@ -224,27 +237,62 @@ public void preRemove(ActiveRecording activeRecording) throws Exception {

@PostRemove
public void postRemove(ActiveRecording activeRecording) {
notify("ActiveRecordingDeleted", activeRecording);
bus.publish(
Recordings.RecordingEventCategory.ACTIVE_DELETED.category(), activeRecording);
notify(
new ActiveRecordingEvent(
Recordings.RecordingEventCategory.ACTIVE_DELETED,
ActiveRecordingEvent.Payload.of(recordingHelper, activeRecording)));
}

private void notify(String category, ActiveRecording recording) {
private void notify(ActiveRecordingEvent event) {
bus.publish(
MessagingServer.class.getName(),
new Notification(
category,
new RecordingEvent(
recording.target.connectUrl,
recordingHelper.toExternalForm(recording))));
new Notification(event.category().category(), event.payload()));
}

// FIXME the target connectUrl URI may no longer be known if the target
// has disappeared and we are emitting an event regarding an archived recording originally
// sourced from that target.
// This should embed the target jvmId and optionally the database ID.
public record RecordingEvent(URI target, Object recording) {
public RecordingEvent {
Objects.requireNonNull(target);
Objects.requireNonNull(recording);
public record ActiveRecordingEvent(
Recordings.RecordingEventCategory category, Payload payload) {
public ActiveRecordingEvent {
Objects.requireNonNull(category);
Objects.requireNonNull(payload);
}

public record Payload(String target, LinkedRecordingDescriptor recording) {
public Payload {
Objects.requireNonNull(target);
Objects.requireNonNull(recording);
}

public static Payload of(RecordingHelper helper, ActiveRecording recording) {
return new Payload(
recording.target.connectUrl.toString(),
helper.toExternalForm(recording));
}
}
}

public record ArchivedRecordingEvent(
Recordings.RecordingEventCategory category, Payload payload) {
public ArchivedRecordingEvent {
Objects.requireNonNull(category);
Objects.requireNonNull(payload);
}

// FIXME the target connectUrl URI may no longer be known if the target
// has disappeared and we are emitting an event regarding an archived recording
// originally
// sourced from that target.
// This should embed the target jvmId and optionally the database ID.
public record Payload(String target, ArchivedRecording recording) {
public Payload {
Objects.requireNonNull(target);
Objects.requireNonNull(recording);
}

public static Payload of(URI connectUrl, ArchivedRecording recording) {
return new Payload(connectUrl.toString(), recording);
}
}
}
}
Expand Down
Loading