xiangfu0 commented on code in PR #20149:
URL: https://github.com/apache/pinot/pull/20149#discussion_r4466792515


##########
pinot-core/src/main/java/org/apache/pinot/core/util/trace/ContinuousJfrStarter.java:
##########
@@ -153,490 +130,369 @@ public class ContinuousJfrStarter implements 
PinotClusterConfigChangeListener {
   public static final String MAX_AGE = "maxAge";
   public static final String DEFAULT_MAX_AGE = "P7D";
 
-  /// A flag to track whether the JFR recording has been started.
-  /// This is specially useful for testing and quickstarts, where servers, 
brokers and other components are executed
-  /// in the same JVM.
-  public static final ContinuousJfrStarter INSTANCE = new 
ContinuousJfrStarter();
+  /// Keys that older versions read. The JVM owns these options now, so this 
class ignores them and logs the
+  /// replacement for each one.
+  private static final Map<String, String> REMOVED_KEYS = Map.of(
+      "directory", "-XX:FlightRecorderOptions:repository=<path>",
+      "dumpPath", "-XX:FlightRecorderOptions:dumppath=<path>",
+      "preserveRepository", 
"-XX:FlightRecorderOptions:preserve-repository=true",
+      "repositoryMaxTotalSize", "a cleanup of the repository directory outside 
the JVM");
+
   private static final Object GLOBAL_RECORDING_LOCK = new Object();
+  /// The recordings that this class runs, by recording name. Several 
instances in the same JVM share an entry.
   @GuardedBy("GLOBAL_RECORDING_LOCK")
   private static final Map<String, RecordingState> GLOBAL_RECORDINGS = new 
HashMap<>();
+  /// The last stopped recording for each recording name.
+  ///
+  /// `Recording.stop()` keeps the recorded chunks in the repository, but 
`Recording.close()` deletes them. This class
+  /// keeps the last stopped recording open, so its data survives a disable or 
a restart of the recording. It closes
+  /// the recording before it, so the disk usage stays bounded.
   @GuardedBy("GLOBAL_RECORDING_LOCK")
-  private boolean _running = false;
+  private static final Map<String, Recording> STOPPED_RECORDINGS = new 
HashMap<>();
+
+  private final String _instanceId;
+  private final boolean _recordingStartedByJvmArgument;
   @GuardedBy("GLOBAL_RECORDING_LOCK")
+  @Nullable
   private Map<String, Object> _currentConfig;
+  /// The name of the recording this instance holds a reference to, or null if 
it does not record.
   @GuardedBy("GLOBAL_RECORDING_LOCK")
-  private String _recordingName;
-  private final MBeanServer _mBeanServer;
   @Nullable
-  private final ObjectName _diagnosticCommandObjectName;
+  private String _recordingName;
+  @GuardedBy("GLOBAL_RECORDING_LOCK")
+  private boolean _jvmArgumentWarningLogged;
 
-  @VisibleForTesting
-  protected ContinuousJfrStarter() {
-    this(ManagementFactory.getPlatformMBeanServer(), 
createDiagnosticCommandObjectName());
+  public ContinuousJfrStarter(String instanceId) {

Review Comment:
   Minor lifecycle regression: a starter never gives back its reference when 
its component shuts down. If a component restarts in the same JVM (tests, 
`restartServers`), the old starter's reference leaks. Setting `enabled=false` 
then leaves the shared recording RUNNING. I reproduced this with two starters 
for the same instance ID. The old `INSTANCE` singleton was idempotent here. 
Consider releasing the reference on component stop, e.g. a `close()` called 
from the starters' shutdown.



##########
pinot-core/src/main/java/org/apache/pinot/core/util/trace/ContinuousJfrStarter.java:
##########
@@ -153,490 +130,369 @@ public class ContinuousJfrStarter implements 
PinotClusterConfigChangeListener {
   public static final String MAX_AGE = "maxAge";
   public static final String DEFAULT_MAX_AGE = "P7D";
 
-  /// A flag to track whether the JFR recording has been started.
-  /// This is specially useful for testing and quickstarts, where servers, 
brokers and other components are executed
-  /// in the same JVM.
-  public static final ContinuousJfrStarter INSTANCE = new 
ContinuousJfrStarter();
+  /// Keys that older versions read. The JVM owns these options now, so this 
class ignores them and logs the
+  /// replacement for each one.
+  private static final Map<String, String> REMOVED_KEYS = Map.of(

Review Comment:
   `maxDumps` (released in 1.5.0 next to `directory`) is missing here, so it is 
dropped without a warning. Also worth stating in the upgrade notes: clusters 
that set `directory` now record to the JVM default repository under 
`java.io.tmpdir`, with the 2GB / 7d defaults. That can put pressure on small 
container `/tmp` volumes until the operator adds `-XX:FlightRecorderOptions`.



##########
pinot-core/src/main/java/org/apache/pinot/core/util/trace/ContinuousJfrStarter.java:
##########
@@ -153,490 +130,369 @@ public class ContinuousJfrStarter implements 
PinotClusterConfigChangeListener {
   public static final String MAX_AGE = "maxAge";
   public static final String DEFAULT_MAX_AGE = "P7D";
 
-  /// A flag to track whether the JFR recording has been started.
-  /// This is specially useful for testing and quickstarts, where servers, 
brokers and other components are executed
-  /// in the same JVM.
-  public static final ContinuousJfrStarter INSTANCE = new 
ContinuousJfrStarter();
+  /// Keys that older versions read. The JVM owns these options now, so this 
class ignores them and logs the
+  /// replacement for each one.
+  private static final Map<String, String> REMOVED_KEYS = Map.of(
+      "directory", "-XX:FlightRecorderOptions:repository=<path>",
+      "dumpPath", "-XX:FlightRecorderOptions:dumppath=<path>",
+      "preserveRepository", 
"-XX:FlightRecorderOptions:preserve-repository=true",
+      "repositoryMaxTotalSize", "a cleanup of the repository directory outside 
the JVM");
+
   private static final Object GLOBAL_RECORDING_LOCK = new Object();
+  /// The recordings that this class runs, by recording name. Several 
instances in the same JVM share an entry.
   @GuardedBy("GLOBAL_RECORDING_LOCK")
   private static final Map<String, RecordingState> GLOBAL_RECORDINGS = new 
HashMap<>();
+  /// The last stopped recording for each recording name.
+  ///
+  /// `Recording.stop()` keeps the recorded chunks in the repository, but 
`Recording.close()` deletes them. This class
+  /// keeps the last stopped recording open, so its data survives a disable or 
a restart of the recording. It closes
+  /// the recording before it, so the disk usage stays bounded.
   @GuardedBy("GLOBAL_RECORDING_LOCK")
-  private boolean _running = false;
+  private static final Map<String, Recording> STOPPED_RECORDINGS = new 
HashMap<>();
+
+  private final String _instanceId;
+  private final boolean _recordingStartedByJvmArgument;
   @GuardedBy("GLOBAL_RECORDING_LOCK")
+  @Nullable
   private Map<String, Object> _currentConfig;
+  /// The name of the recording this instance holds a reference to, or null if 
it does not record.
   @GuardedBy("GLOBAL_RECORDING_LOCK")
-  private String _recordingName;
-  private final MBeanServer _mBeanServer;
   @Nullable
-  private final ObjectName _diagnosticCommandObjectName;
+  private String _recordingName;
+  @GuardedBy("GLOBAL_RECORDING_LOCK")
+  private boolean _jvmArgumentWarningLogged;
 
-  @VisibleForTesting
-  protected ContinuousJfrStarter() {
-    this(ManagementFactory.getPlatformMBeanServer(), 
createDiagnosticCommandObjectName());
+  public ContinuousJfrStarter(String instanceId) {
+    this(instanceId, ManagementFactory.getRuntimeMXBean().getInputArguments());
   }
 
   @VisibleForTesting
-  protected ContinuousJfrStarter(MBeanServer mBeanServer, @Nullable ObjectName 
diagnosticCommandObjectName) {
-    _mBeanServer = mBeanServer;
-    _diagnosticCommandObjectName = diagnosticCommandObjectName;
+  ContinuousJfrStarter(String instanceId, List<String> jvmArguments) {
+    _instanceId = instanceId;
+    _recordingStartedByJvmArgument = jvmArguments.stream()
+        .anyMatch(argument -> 
argument.startsWith(START_FLIGHT_RECORDING_ARGUMENT));
   }
 
   @Override
   public void onChange(Set<String> changedConfigs, Map<String, String> 
clusterConfigs) {
     boolean jfrChanged = changedConfigs.stream()
         .anyMatch(changedConfig -> 
changedConfig.startsWith(CommonConstants.JFR));
-    if (!jfrChanged && _currentConfig != null) {
-      LOGGER.debug("ChangedConfigs: {} does not contain any JFR config. 
Skipping updates", changedConfigs);
-      return;
-    }
-    PinotConfiguration config = new PinotConfiguration(clusterConfigs);
-    PinotConfiguration subset = config.subset(CommonConstants.JFR);
+    PinotConfiguration subset = new 
PinotConfiguration(clusterConfigs).subset(CommonConstants.JFR);
 
     synchronized (GLOBAL_RECORDING_LOCK) {
+      if (!jfrChanged && _currentConfig != null) {
+        LOGGER.debug("ChangedConfigs: {} does not contain any JFR config. 
Skipping updates", changedConfigs);
+        return;
+      }
       Map<String, Object> newSubsetMap = subset.toMap();
-
-      if (_currentConfig != null && _currentConfig.equals(newSubsetMap)) {
-        // No change
+      if (newSubsetMap.equals(_currentConfig)) {
         LOGGER.debug("JFR config change detected, but no actual change in 
config");
         return;
       }
-
-      if (!applyConfig(subset, newSubsetMap)) {
-        LOGGER.warn("Failed to apply continuous JFR config update");
+      if (applyConfig(subset)) {
+        _currentConfig = newSubsetMap;
+      } else {
+        LOGGER.warn("Failed to apply continuous JFR config update on instance 
{}", _instanceId);
       }
     }
   }
 
   public boolean isRunning() {
     synchronized (GLOBAL_RECORDING_LOCK) {
-      return _running;
+      return _recordingName != null;
     }
   }
 
+  /// Closes all the recordings that this class started, in all instances, and 
forgets them.
   @VisibleForTesting
-  protected static void resetGlobalStateForTesting() {
+  static void resetGlobalStateForTesting() {
     synchronized (GLOBAL_RECORDING_LOCK) {
+      GLOBAL_RECORDINGS.values().forEach(recordingState -> 
recordingState._recording.close());
       GLOBAL_RECORDINGS.clear();
+      STOPPED_RECORDINGS.values().forEach(Recording::close);
+      STOPPED_RECORDINGS.clear();
     }
   }
 
-  @GuardedBy("GLOBAL_RECORDING_LOCK")
-  private boolean applyConfig(PinotConfiguration subset, Map<String, Object> 
newSubsetMap) {
-    boolean enabled = subset.getProperty(ENABLED, DEFAULT_ENABLED);
-    if (!enabled) {
-      if (!releaseRecordingReference()) {
-        return false;
-      }
-      _currentConfig = newSubsetMap;
-      return true;
-    }
-    String targetRecordingName = subset.getProperty(NAME, DEFAULT_NAME);
-    boolean alreadyAttachedToTarget = _running && 
targetRecordingName.equals(_recordingName);
-    if (!alreadyAttachedToTarget && !releaseRecordingReference()) {
-      return false;
-    }
-    if (!acquireRecordingReference(subset, newSubsetMap, targetRecordingName, 
alreadyAttachedToTarget)) {
-      return false;
-    }
-    _currentConfig = newSubsetMap;
-    return true;
-  }
-
-  @GuardedBy("GLOBAL_RECORDING_LOCK")
-  private boolean acquireRecordingReference(PinotConfiguration subset, 
Map<String, Object> configMap,
-      String recordingName, boolean alreadyAttachedToTarget) {
-    RecordingState recordingState = GLOBAL_RECORDINGS.get(recordingName);
-    if (recordingState == null) {
-      if (!startRecording(subset, recordingName)) {
-        return false;
-      }
-      recordingState = new RecordingState(new HashMap<>(configMap), 1);
-      updateRepositoryCleanupTask(subset, recordingName, recordingState);
-      GLOBAL_RECORDINGS.put(recordingName, recordingState);
-      _recordingName = recordingName;
-      _running = true;
-      return true;
-    }
-    if (!recordingState._config.equals(configMap)) {
-      if (!restartRecording(subset, recordingName)) {
-        LOGGER.warn("Failed to restart continuous JFR recording '{}' for 
config update", recordingName);
-        return false;
-      }
-      recordingState._config = new HashMap<>(configMap);
-      updateRepositoryCleanupTask(subset, recordingName, recordingState);
-    }
-    if (!alreadyAttachedToTarget) {
-      recordingState._referenceCount++;
+  /// Returns the recording that this instance holds a reference to, or null 
if it does not record.
+  @VisibleForTesting
+  @Nullable
+  Recording getRecording() {
+    synchronized (GLOBAL_RECORDING_LOCK) {
+      return _recordingName != null ? 
GLOBAL_RECORDINGS.get(_recordingName)._recording : null;
     }
-    _recordingName = recordingName;
-    _running = true;
-    return true;
   }
 
-  @GuardedBy("GLOBAL_RECORDING_LOCK")
-  private boolean restartRecording(PinotConfiguration subset, String 
recordingName) {
-    if (!stopRecording(recordingName)) {
-      LOGGER.warn("Failed to stop continuous JFR recording '{}' before 
restart", recordingName);
-      return false;
+  /// Returns the last stopped recording with the given name, or null if there 
is none.
+  @VisibleForTesting
+  @Nullable
+  static Recording getStoppedRecording(String recordingName) {
+    synchronized (GLOBAL_RECORDING_LOCK) {
+      return STOPPED_RECORDINGS.get(recordingName);
     }
-    return startRecording(subset, recordingName);
   }
 
+  /// Applies the config to this instance. Returns false and keeps the current 
state if the config is not valid.
   @GuardedBy("GLOBAL_RECORDING_LOCK")
-  private boolean releaseRecordingReference() {
-    if (!_running) {
-      return true;
+  private boolean applyConfig(PinotConfiguration subset) {
+    for (Map.Entry<String, String> removedKey : REMOVED_KEYS.entrySet()) {
+      if (subset.containsKey(removedKey.getKey())) {
+        LOGGER.warn("Ignoring continuous JFR config '{}.{}'. Use {} instead", 
CommonConstants.JFR,
+            removedKey.getKey(), removedKey.getValue());
+      }
     }
-    assert _recordingName != null;
-    RecordingState recordingState = GLOBAL_RECORDINGS.get(_recordingName);
-    if (recordingState == null) {
-      LOGGER.warn("No global state found for running continuous JFR recording 
'{}'", _recordingName);
-      _running = false;
-      _recordingName = null;
-      return true;
+    Boolean shouldRecord = shouldRecord(subset);
+    if (shouldRecord == null) {
+      return false;
     }
-    if (recordingState._referenceCount > 1) {
-      recordingState._referenceCount--;
-      _running = false;
-      _recordingName = null;
+    if (!shouldRecord) {
+      releaseRecordingReference();
       return true;
     }
-    if (!stopRecording(_recordingName)) {
-      return false;
-    }
-    cancelRepositoryCleanupTask(recordingState);
-    GLOBAL_RECORDINGS.remove(_recordingName);
-    _running = false;
-    _recordingName = null;
-    return true;
-  }
-
-  @GuardedBy("GLOBAL_RECORDING_LOCK")
-  private boolean stopRecording(String recordingName) {
-    if (!isDiagnosticCommandAvailable()) {
-      LOGGER.warn("JFR DiagnosticCommand MBean is unavailable. Cannot stop 
continuous JFR recording '{}'",
-          recordingName);
+    RecordingSpec spec;
+    try {
+      spec = RecordingSpec.from(subset);
+    } catch (IllegalArgumentException e) {
+      LOGGER.error("Invalid continuous JFR config on instance {}", 
_instanceId, e);
       return false;
     }
-    if (!executeDiagnosticCommand(JFR_STOP_COMMAND, "name=" + recordingName)) {
-      LOGGER.warn("Failed to stop continuous JFR recording '{}'", 
recordingName);
-      return false;
+    if (_recordingName != null && !_recordingName.equals(spec._name)) {
+      releaseRecordingReference();
     }
-    LOGGER.info("Stopped continuous JFR recording {}", recordingName);
-    return true;
+    return acquireRecordingReference(spec);
   }
 
+  /// Returns whether this instance must record, or null if the instance regex 
is not valid.
   @GuardedBy("GLOBAL_RECORDING_LOCK")
-  private boolean startRecording(PinotConfiguration subset, String 
recordingName) {
+  @Nullable
+  private Boolean shouldRecord(PinotConfiguration subset) {
     if (!subset.getProperty(ENABLED, DEFAULT_ENABLED)) {
-      LOGGER.info("Continuous JFR recording is disabled");
-      return true;
-    }
-    if (!isDiagnosticCommandAvailable()) {
-      LOGGER.warn("JFR DiagnosticCommand MBean is unavailable. Cannot start 
continuous JFR recording");
       return false;
     }
-    if (!applyRuntimeOptions(subset)) {
-      LOGGER.warn("Failed to apply JFR runtime options for recording '{}'", 
recordingName);
+    if (_recordingStartedByJvmArgument) {
+      if (!_jvmArgumentWarningLogged) {
+        LOGGER.warn("Ignoring continuous JFR cluster configs on instance {}, 
because the JVM starts a recording with "
+            + "{}", _instanceId, START_FLIGHT_RECORDING_ARGUMENT);
+        _jvmArgumentWarningLogged = true;
+      }
       return false;
     }
-
-    String maxAge = subset.getProperty(MAX_AGE, DEFAULT_MAX_AGE);
-    try {
-      List<String> startArguments = new ArrayList<>();
-      startArguments.add("name=" + recordingName);
-      startArguments.add("settings=" + subset.getProperty(CONFIGURATION, 
DEFAULT_CONFIGURATION));
-      startArguments.add("dumponexit=" + subset.getProperty(DUMP_ON_EXIT, 
DEFAULT_DUMP_ON_EXIT));
-      boolean toDisk = subset.getProperty(TO_DISK, DEFAULT_TO_DISK);
-      startArguments.add("disk=" + toDisk);
-      if (toDisk) {
-        try {
-          long maxSize = DataSizeUtils.toBytes(subset.getProperty(MAX_SIZE, 
DEFAULT_MAX_SIZE));
-          startArguments.add("maxsize=" + maxSize);
-          startArguments.add("maxage=" + toJfrTimeArgument(maxAge));
-        } catch (IllegalArgumentException | ClassCastException e) {
-          throw new RuntimeException("Failed to parse maxSize configuration 
for continuous JFR recording '"
-              + recordingName + "'", e);
-        }
-      }
-      if (!executeDiagnosticCommand(JFR_START_COMMAND, 
startArguments.toArray(new String[0]))) {
-        LOGGER.warn("Failed to start continuous JFR recording '{}'", 
recordingName);
-        return false;
-      }
-    } catch (DateTimeParseException e) {
-      throw new RuntimeException("Failed to parse duration '" + maxAge + "'", 
e);
+    String instanceRegex = subset.getProperty(INSTANCE_REGEX);
+    if (instanceRegex == null || instanceRegex.isEmpty()) {
+      return true;
     }
-
-    LOGGER.info("Started continuous JFR recording {} with configuration: {}", 
recordingName, subset);
-    return true;
-  }
-
-  @VisibleForTesting
-  protected boolean executeDiagnosticCommand(String operationName, String... 
arguments) {
     try {
-      _mBeanServer.invoke(_diagnosticCommandObjectName, operationName, new 
Object[]{arguments},
-          new String[]{String[].class.getName()});
-      return true;
-    } catch (Exception e) {
-      LOGGER.warn("Failed to execute JFR command '{}' with arguments {}", 
operationName, Arrays.toString(arguments), e);
-      return false;
+      return Pattern.compile(instanceRegex).matcher(_instanceId).matches();
+    } catch (PatternSyntaxException e) {
+      LOGGER.error("Invalid continuous JFR config '{}.{}': '{}'. Keeping the 
current recording state on instance {}",
+          CommonConstants.JFR, INSTANCE_REGEX, instanceRegex, _instanceId, e);
+      return null;
     }
   }
 
-  private boolean applyRuntimeOptions(PinotConfiguration subset) {
-    List<String> configureArguments = new ArrayList<>();
-    String repositoryPath = subset.getProperty(DIRECTORY, (String) null);
-    if (repositoryPath != null && !repositoryPath.isEmpty()) {
-      configureArguments.add("repositorypath=" + repositoryPath);
-    }
-    String dumpPath = subset.getProperty(DUMP_PATH, (String) null);
-    if (dumpPath != null && !dumpPath.isEmpty()) {
-      configureArguments.add("dumppath=" + dumpPath);
-    }
-    if (configureArguments.isEmpty()) {
+  @GuardedBy("GLOBAL_RECORDING_LOCK")
+  private boolean acquireRecordingReference(RecordingSpec spec) {
+    RecordingState recordingState = GLOBAL_RECORDINGS.get(spec._name);
+    if (recordingState == null) {
+      Recording recording = startRecording(spec);
+      if (recording == null) {
+        return false;
+      }
+      GLOBAL_RECORDINGS.put(spec._name, new RecordingState(recording, spec));
+      _recordingName = spec._name;
       return true;
     }
-    boolean preserveRepository = subset.getProperty(PRESERVE_REPOSITORY, 
DEFAULT_PRESERVE_REPOSITORY);
-    if (preserveRepository) {
-      List<String> configureArgumentsWithPreserve = new 
ArrayList<>(configureArguments);
-      configureArgumentsWithPreserve.add("preserve-repository=true");
-      if (executeDiagnosticCommand(JFR_CONFIGURE_COMMAND, 
configureArgumentsWithPreserve.toArray(new String[0]))) {
-        return true;
-      }
-      LOGGER.warn("Failed to apply preserveRepository option for continuous 
JFR recording. Retrying without it");
+    if (!recordingState._spec.equals(spec) && !updateRecording(recordingState, 
spec)) {
+      return false;
     }
-    return executeDiagnosticCommand(JFR_CONFIGURE_COMMAND, 
configureArguments.toArray(new String[0]));
+    if (!spec._name.equals(_recordingName)) {
+      recordingState._referenceCount++;
+      _recordingName = spec._name;
+    }
+    return true;
   }
 
   @GuardedBy("GLOBAL_RECORDING_LOCK")
-  private void updateRepositoryCleanupTask(
-      PinotConfiguration subset, String recordingName, RecordingState 
recordingState) {
-    if (!isRepositoryCleanupEnabled()) {
+  private void releaseRecordingReference() {
+    if (_recordingName == null) {
       return;
     }
-    String repositoryRoot = subset.getProperty(DIRECTORY, (String) null);
-    if (repositoryRoot == null || repositoryRoot.isEmpty()) {
-      cancelRepositoryCleanupTask(recordingState);
-      return;
-    }
-    long maxTotalSizeBytes;
-    try {
-      maxTotalSizeBytes = 
DataSizeUtils.toBytes(subset.getProperty(REPOSITORY_MAX_TOTAL_SIZE,
-          DEFAULT_REPOSITORY_MAX_TOTAL_SIZE));
-    } catch (IllegalArgumentException | ClassCastException e) {
-      throw new RuntimeException("Failed to parse continuous JFR repository 
max total size from key '"
-          + REPOSITORY_MAX_TOTAL_SIZE + "'", e);
-    }
-    if (recordingState._cleanupTask != null && 
!recordingState._cleanupTask.isCancelled()
-        && Objects.equals(recordingState._repositoryRoot, repositoryRoot)
-        && recordingState._repositoryMaxTotalSizeBytes == maxTotalSizeBytes) {
-      return;
+    RecordingState recordingState = GLOBAL_RECORDINGS.get(_recordingName);
+    if (recordingState._referenceCount > 1) {
+      recordingState._referenceCount--;
+    } else {
+      stopRecording(_recordingName, recordingState._recording);
+      GLOBAL_RECORDINGS.remove(_recordingName);
     }
-    cancelRepositoryCleanupTask(recordingState);
-    recordingState._repositoryRoot = repositoryRoot;
-    recordingState._repositoryMaxTotalSizeBytes = maxTotalSizeBytes;
-    recordingState._cleanupTask = 
REPOSITORY_CLEANUP_EXECUTOR.scheduleWithFixedDelay(
-        () -> cleanupRepositoriesIfNeeded(recordingName, repositoryRoot, 
maxTotalSizeBytes), 0,
-        REPOSITORY_CLEANUP_INTERVAL.toMillis(), TimeUnit.MILLISECONDS);
+    _recordingName = null;
   }
 
+  /// Applies a new spec to a running recording. JFR can change all the 
options of a running recording, except the
+  /// `toDisk` option. For that option, this method starts a new recording and 
stops the old one.
   @GuardedBy("GLOBAL_RECORDING_LOCK")
-  private void cancelRepositoryCleanupTask(RecordingState recordingState) {
-    if (recordingState._cleanupTask != null) {
-      recordingState._cleanupTask.cancel(false);
-      recordingState._cleanupTask = null;
+  private boolean updateRecording(RecordingState recordingState, RecordingSpec 
spec) {
+    if (recordingState._spec._toDisk != spec._toDisk) {
+      Recording recording = startRecording(spec);
+      if (recording == null) {
+        return false;
+      }
+      stopRecording(spec._name, recordingState._recording);
+      recordingState._recording = recording;
+    } else {
+      try {
+        applyOptions(recordingState._recording, spec);
+      } catch (RuntimeException e) {

Review Comment:
   Nit: if someone runs `jcmd JFR.stop name=pinot-continuous`, the held 
recording becomes CLOSED. Every later retune then fails here with `Can't set 
max size when recording is closed`, and `isRunning()` keeps reporting true. 
Only a disable/enable toggle recovers (reproduced). Checking `getState() == 
CLOSED` and starting a fresh recording would self-heal. This is not a 
regression; the old MBean path was worse.



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to