You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@cloudstack.apache.org by da...@apache.org on 2026/10/07 13:57:01 UTC

(cloudstack) branch bulk-power-state-update-time-gmt created (now 5da21becf1e)

This is an automated email from the ASF dual-hosted git repository.

DaanHoogland pushed a change to branch bulk-power-state-update-time-gmt
in repository https://gitbox.apache.org/repos/asf/cloudstack.git


      at 5da21becf1e update powerstate timestamps to the right timezone

This branch includes the following new commits:

     new 5da21becf1e update powerstate timestamps to the right timezone

The 1 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.



(cloudstack) 01/01: update powerstate timestamps to the right timezone

Posted by da...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

DaanHoogland pushed a commit to branch bulk-power-state-update-time-gmt
in repository https://gitbox.apache.org/repos/asf/cloudstack.git

commit 5da21becf1ea74d38de9e1857bc67e9325c7c965
Author: Daan Hoogland <da...@apache.org>
AuthorDate: Wed Oct 7 15:56:32 2026 +0200

    update powerstate timestamps to the right timezone
---
 .../java/com/cloud/vm/dao/VMInstanceDaoImpl.java   |  4 +-
 .../com/cloud/vm/dao/VMInstanceDaoImplTest.java    | 55 ++++++++++++++++++++++
 2 files changed, 58 insertions(+), 1 deletion(-)

diff --git a/engine/schema/src/main/java/com/cloud/vm/dao/VMInstanceDaoImpl.java b/engine/schema/src/main/java/com/cloud/vm/dao/VMInstanceDaoImpl.java
index 518bc3cf497..232a99211a1 100755
--- a/engine/schema/src/main/java/com/cloud/vm/dao/VMInstanceDaoImpl.java
+++ b/engine/schema/src/main/java/com/cloud/vm/dao/VMInstanceDaoImpl.java
@@ -19,6 +19,7 @@ package com.cloud.vm.dao;
 import java.sql.PreparedStatement;
 import java.sql.ResultSet;
 import java.sql.SQLException;
+import java.sql.Timestamp;
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.Date;
@@ -1037,7 +1038,7 @@ public class VMInstanceDaoImpl extends GenericDaoBase<VMInstanceVO, Long> implem
             return notUpdated;
         }
         StringBuilder sql = new StringBuilder("UPDATE `cloud`.`vm_instance` SET " +
-                "`power_host` = ?, `power_state_update_time` = now(), `power_state` = CASE ");
+                "`power_host` = ?, `power_state_update_time` = ?, `power_state` = CASE ");
         updateCounts.keySet().forEach(key -> {
             sql.append("WHEN id = ").append(key).append(" THEN '").append(instancePowerStates.get(key)).append("' ");
         });
@@ -1052,6 +1053,7 @@ public class VMInstanceDaoImpl extends GenericDaoBase<VMInstanceVO, Long> implem
         TransactionLegacy txn = TransactionLegacy.currentTxn();
         try (PreparedStatement pstmt = txn.prepareAutoCloseStatement(sql.toString())) {
             pstmt.setLong(1, powerHostId);
+            pstmt.setTimestamp(2, new Timestamp(DateUtil.currentGMTTime().getTime()), gmtCalendar());
             pstmt.executeUpdate();
         } catch (SQLException e) {
             logger.error("Unable to execute update power states SQL from VMs {} due to: {}",
diff --git a/engine/schema/src/test/java/com/cloud/vm/dao/VMInstanceDaoImplTest.java b/engine/schema/src/test/java/com/cloud/vm/dao/VMInstanceDaoImplTest.java
index 5f8b2dd90ec..5317f3a6689 100644
--- a/engine/schema/src/test/java/com/cloud/vm/dao/VMInstanceDaoImplTest.java
+++ b/engine/schema/src/test/java/com/cloud/vm/dao/VMInstanceDaoImplTest.java
@@ -20,19 +20,26 @@ package com.cloud.vm.dao;
 import static com.cloud.vm.VirtualMachine.State.Running;
 import static com.cloud.vm.VirtualMachine.State.Stopped;
 import static com.cloud.vm.dao.VMInstanceDaoImpl.MAX_CONSECUTIVE_SAME_STATE_UPDATE_COUNT;
+import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertTrue;
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.anyLong;
+import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.doReturn;
 import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
+import java.sql.PreparedStatement;
+import java.sql.Timestamp;
 import java.util.ArrayList;
 import java.util.Calendar;
 import java.util.Date;
+import java.util.List;
+import java.util.Map;
+import java.util.TimeZone;
 
 import com.cloud.host.dao.HostDao;
 import org.joda.time.DateTime;
@@ -40,8 +47,10 @@ import org.junit.After;
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
 import org.mockito.InjectMocks;
 import org.mockito.Mock;
+import org.mockito.MockedStatic;
 import org.mockito.Mockito;
 import org.mockito.MockitoAnnotations;
 import org.mockito.Spy;
@@ -50,6 +59,7 @@ import com.cloud.utils.Pair;
 import com.cloud.utils.db.Filter;
 import com.cloud.utils.db.SearchBuilder;
 import com.cloud.utils.db.SearchCriteria;
+import com.cloud.utils.db.TransactionLegacy;
 import com.cloud.vm.VMInstanceVO;
 import com.cloud.vm.VirtualMachine;
 import org.mockito.junit.MockitoJUnitRunner;
@@ -210,6 +220,51 @@ public class VMInstanceDaoImplTest {
         assertTrue(result);
     }
 
+    @Test
+    public void testUpdatePowerStatesBindsGmtUpdateTimeInsteadOfDbNow() throws Exception {
+        TimeZone defaultTimeZone = TimeZone.getDefault();
+        try {
+            for (String timeZone : List.of("UTC", "Europe/Amsterdam", "America/New_York", "Asia/Kolkata", "Pacific/Auckland")) {
+                TimeZone.setDefault(TimeZone.getTimeZone(timeZone));
+                verifyBulkUpdatePowerStatesBindsGmtUpdateTime(timeZone);
+            }
+        } finally {
+            TimeZone.setDefault(defaultTimeZone);
+        }
+    }
+
+    private void verifyBulkUpdatePowerStatesBindsGmtUpdateTime(String timeZone) throws Exception {
+        VMInstanceVO instance = Mockito.mock(VMInstanceVO.class);
+        when(instance.getId()).thenReturn(1L);
+        when(instance.getPowerState()).thenReturn(VirtualMachine.PowerState.PowerOn);
+        doReturn(List.of(instance)).when(vmInstanceDao).listSelectPowerStateByIds(any());
+        TransactionLegacy txn = Mockito.mock(TransactionLegacy.class);
+        PreparedStatement pstmt = Mockito.mock(PreparedStatement.class);
+        ArgumentCaptor<String> sqlCaptor = ArgumentCaptor.forClass(String.class);
+        when(txn.prepareAutoCloseStatement(sqlCaptor.capture())).thenReturn(pstmt);
+        ArgumentCaptor<Timestamp> timeCaptor = ArgumentCaptor.forClass(Timestamp.class);
+        ArgumentCaptor<Calendar> calendarCaptor = ArgumentCaptor.forClass(Calendar.class);
+
+        long before = System.currentTimeMillis();
+        Map<Long, VirtualMachine.PowerState> notUpdated;
+        try (MockedStatic<TransactionLegacy> ignored = Mockito.mockStatic(TransactionLegacy.class)) {
+            ignored.when(TransactionLegacy::currentTxn).thenReturn(txn);
+            notUpdated = vmInstanceDao.updatePowerState(Map.of(1L, VirtualMachine.PowerState.PowerOff), 2L, new Date());
+        }
+        long after = System.currentTimeMillis();
+
+        assertTrue(timeZone, notUpdated.isEmpty());
+        String sql = sqlCaptor.getValue();
+        assertFalse(timeZone, sql.toLowerCase().contains("now()"));
+        assertTrue(timeZone, sql.contains("`power_state_update_time` = ?"));
+        verify(pstmt).setLong(1, 2L);
+        verify(pstmt).setTimestamp(eq(2), timeCaptor.capture(), calendarCaptor.capture());
+        verify(pstmt).executeUpdate();
+        long written = timeCaptor.getValue().getTime();
+        assertTrue(timeZone, written >= before && written <= after);
+        assertEquals(timeZone, "GMT", calendarCaptor.getValue().getTimeZone().getID());
+    }
+
     @Test
     public void testSearchRemovedByRemoveDate() {
         SearchBuilder<VMInstanceVO> sb = Mockito.mock(SearchBuilder.class);