From 1e5d534c3ca32c9c775613dddae12fcfab15e2bf Mon Sep 17 00:00:00 2001
From: dahn
Date: Mon, 17 Aug 2026 09:42:54 +0200
Subject: [PATCH] guard against stopping stats background threads (#13659)
* guard against stopping stats background threats
* stats cleanup test
* override annotation, and specic and generic enough catch
---------
Co-authored-by: Daan Hoogland
---
.../java/com/cloud/server/StatsCollector.java | 14 +++++-
.../com/cloud/server/StatsCollectorTest.java | 43 +++++++++++++++++++
2 files changed, 55 insertions(+), 2 deletions(-)
diff --git a/server/src/main/java/com/cloud/server/StatsCollector.java b/server/src/main/java/com/cloud/server/StatsCollector.java
index 1e0138f7cf9..ca8d95c74c5 100644
--- a/server/src/main/java/com/cloud/server/StatsCollector.java
+++ b/server/src/main/java/com/cloud/server/StatsCollector.java
@@ -1285,14 +1285,24 @@ public class StatsCollector extends ManagerBase implements ComponentMethodInterc
* can be enabled/disabled independently.
*/
class VmStatsCleaner extends ManagedContextRunnable{
+ @Override
protected void runInContext() {
- cleanUpVirtualMachineStats();
+ try {
+ cleanUpVirtualMachineStats();
+ } catch (RuntimeException e) {
+ logger.error("Error trying to clean up VM stats", e);
+ }
}
}
class VolumeStatsCleaner extends ManagedContextRunnable{
+ @Override
protected void runInContext() {
- cleanUpVolumeStats();
+ try {
+ cleanUpVolumeStats();
+ } catch (RuntimeException e) {
+ logger.error("Error trying to clean up Volume stats", e);
+ }
}
}
diff --git a/server/src/test/java/com/cloud/server/StatsCollectorTest.java b/server/src/test/java/com/cloud/server/StatsCollectorTest.java
index 3578e6948a4..46449a9cb6e 100644
--- a/server/src/test/java/com/cloud/server/StatsCollectorTest.java
+++ b/server/src/test/java/com/cloud/server/StatsCollectorTest.java
@@ -335,6 +335,49 @@ public class StatsCollectorTest {
Mockito.verify(vmStatsDaoMock).removeAllByTimestampLessThan(Mockito.any(), Mockito.anyLong());
}
+ private void setVmDiskStatsMaxRetentionTimeValue(String value) {
+ StatsCollector.vmDiskStatsMaxRetentionTime = new ConfigKey("Advanced", Integer.class, "vm.disk.stats.max.retention.time", value,
+ "The maximum time (in minutes) for keeping Volume stats records in the database. The Volume stats cleanup process will be disabled if this is set to 0 or less than 0.", true);
+ }
+
+ @Test
+ public void cleanUpVolumeStatsTestIsDisabled() {
+ setVmDiskStatsMaxRetentionTimeValue("0");
+
+ statsCollector.cleanUpVolumeStats();
+
+ Mockito.verify(volumeStatsDao, Mockito.never()).removeAllByTimestampLessThan(Mockito.any(), Mockito.anyLong());
+ }
+
+ @Test
+ public void cleanUpVolumeStatsTestIsEnabled() {
+ setVmDiskStatsMaxRetentionTimeValue("1");
+
+ statsCollector.cleanUpVolumeStats();
+
+ Mockito.verify(volumeStatsDao).removeAllByTimestampLessThan(Mockito.any(), Mockito.anyLong());
+ }
+
+ @Test
+ public void vmStatsCleanerTestCatchesCloudRuntimeExceptionAndKeepsRunning() {
+ Mockito.doThrow(new CloudRuntimeException("Communications link failure")).when(statsCollector).cleanUpVirtualMachineStats();
+ StatsCollector.VmStatsCleaner vmStatsCleaner = statsCollector.new VmStatsCleaner();
+
+ vmStatsCleaner.run();
+
+ Mockito.verify(statsCollector).cleanUpVirtualMachineStats();
+ }
+
+ @Test
+ public void volumeStatsCleanerTestCatchesCloudRuntimeExceptionAndKeepsRunning() {
+ Mockito.doThrow(new CloudRuntimeException("Communications link failure")).when(statsCollector).cleanUpVolumeStats();
+ StatsCollector.VolumeStatsCleaner volumeStatsCleaner = statsCollector.new VolumeStatsCleaner();
+
+ volumeStatsCleaner.run();
+
+ Mockito.verify(statsCollector).cleanUpVolumeStats();
+ }
+
@Test
public void persistVirtualMachineStatsTestPersistsSuccessfully() {
statsCollector.msId = 1L;