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

yinyijun pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/hertzbeat.git


The following commit(s) were added to refs/heads/master by this push:
     new 2f5cfa9d5d [improve] VM write request sets up gzip compression and 
adds saveData unit tests (#3595)
2f5cfa9d5d is described below

commit 2f5cfa9d5d0230d557f9523aa3f50cf8e4279405
Author: Cyanty <153884653+cya...@users.noreply.github.com>
AuthorDate: Thu Jul 24 19:18:17 2025 +0800

    [improve] VM write request sets up gzip compression and adds saveData unit 
tests (#3595)
    
    Co-authored-by: Calvin <zhengqi...@apache.org>
    Co-authored-by: Sherlock Yin <sherlock.yin1...@gmail.com>
---
 .../src/main/resources/application-test.yml        |  94 ++++++++-
 .../src/main/resources/application.yml             |   2 +
 .../tsdb/vm/VictoriaMetricsDataStorage.java        |  27 ++-
 .../history/tsdb/vm/VictoriaMetricsProperties.java |  12 +-
 .../tsdb/vm/VictoriaMetricsDataStorageTest.java    | 221 +++++++++++++++++++++
 .../tsdb/vm/VictoriaMetricsPropertiesTest.java     |  61 ++++++
 .../conf/application.yml                           |   2 +
 .../conf/application.yml                           |   2 +
 8 files changed, 412 insertions(+), 9 deletions(-)

diff --git a/hertzbeat-manager/src/main/resources/application-test.yml 
b/hertzbeat-manager/src/main/resources/application-test.yml
index fc32e324b3..bd76e9d932 100644
--- a/hertzbeat-manager/src/main/resources/application-test.yml
+++ b/hertzbeat-manager/src/main/resources/application-test.yml
@@ -22,6 +22,7 @@ spring:
     url: jdbc:h2:./data/test;MODE=MYSQL
     hikari:
       max-lifetime: 120000
+
   jpa:
     show-sql: false
     database-platform: org.eclipse.persistence.platform.database.MySQLPlatform
@@ -30,8 +31,10 @@ spring:
       eclipselink:
         logging:
           level: SEVERE
+
   flyway:
     enabled: false
+
   mail:
     host: smtp.exmail.qq.com
     username: exam...@tancloud.cn
@@ -55,26 +58,57 @@ warehouse:
     jpa:
       enabled: true
       expire-time: 1h
+      max-history-record-num: 6000
     victoria-metrics:
       enabled: false
       url: http://localhost:8428
       username: root
       password: root
       insert:
-        buffer-size: 1000
+        buffer-size: 100
         flush-interval: 3
+        compression:
+          enabled: false
+      cluster:
+        enabled: false
+        select:
+          url: http://localhost:8481
+          username: root
+          password: root
+        insert:
+          url: http://localhost:8480
+          username: root
+          password: root
+          buffer-size: 1000
+          flush-interval: 3
     td-engine:
       enabled: false
       driver-class-name: com.taosdata.jdbc.rs.RestfulDriver
       url: jdbc:TAOS-RS://127.0.0.1:6041/hertzbeat
       username: root
       password: taosdata
+    greptime:
+      enabled: false
+      grpc-endpoints: localhost:4001
+      http-endpoint: http://localhost:4000
+      database: public
+      username: greptime
+      password: greptime
     iot-db:
       enabled: false
       host: 127.0.0.1
       rpc-port: 6667
       username: root
       password: root
+      query-timeout-in-ms: -1
+      expire-time: '7776000000'
+    influxdb:
+      enabled: false
+      server-url: http://127.0.0.1:8086
+      username: root
+      password: root
+      expire-time: '30d'
+      replication: 1
   # store real-time metrics data, enable only one below
   real-time:
     memory:
@@ -92,6 +126,58 @@ warehouse:
       # redis db index, default: DB0
       db: 0
 
+alerter:
+  # custom console url
+  console-url: https://console.tancloud.io
+  # we work
+  we-work-webhook-url: https://qyapi.weixin.qq.com/cgi-bin/webhook/send?key=
+  # ding ding talk
+  ding-talk-webhook-url: https://oapi.dingtalk.com/robot/send?access_token=
+  # fei shu fly book
+  fly-book-webhook-url: https://open.feishu.cn/open-apis/bot/v2/hook/
+  # telegram
+  telegram-webhook-url: https://api.telegram.org/bot%s/sendMessage
+  # discord
+  discord-webhook-url: https://discord.com/api/v9/channels/%s/messages
+  # serverChan
+  server-chan-webhook-url: https://sctapi.ftqq.com/%s.send
+  # gotify
+  gotify-webhook-url: http://127.0.0.1/message?token=%s
+  # alert inhibit ttl unit ms, default 14400000(4 hours)
+  inhibit:
+    ttl: 14400000
+  sms:
+    enable: false
+    type: tencent
+    tencent:
+      secret-id:
+      secret-key:
+      app-id:
+      sign-name:
+      template-id:
+    alibaba:
+      access-key-id:
+      access-key-secret:
+      sign-name:
+      template-code:
+    unisms:
+      # auth-mode: simple or hmac
+      auth-mode: simple
+      access-key-id: YOUR_ACCESS_KEY_ID
+      # hmac mode need to fill in access-key-secret
+      access-key-secret: YOUR_ACCESS_KEY_SECRET
+      signature: YOUR_SMS_SIGNATURE
+      template-id: YOUR_TEMPLATE_ID
+    smslocal:
+      api-key: YOUR_API_KEY_HERE
+    aws:
+      access-key-id: YOUR_ACCESS_KEY_ID
+      access-key-secret: YOUR_ACCESS_KEY_SECRET
+      region: AWS_REGION_FOR_END_USER_MESSAGING
+    twilio:
+      account-sid: YOUR_ACCOUNT_SID
+      auth-token: YOUR_AUTH_TOKEN
+      twilio-phone-number: YOUR_TWILIO_PHONE_NUMBER
 scheduler:
   server:
     enabled: true
@@ -100,13 +186,13 @@ scheduler:
 grafana:
   enabled: false
   url: http://127.0.0.1:3000
+  expose-url: http://127.0.0.1:3000
   username: admin
   password: admin
 
-# AI config
 # See the documentation for details : 
https://hertzbeat.apache.org/zh-cn/docs/help/aiConfig
 ai:
-  # AI Type:zhiPu、alibabaAi、kimiAi、sparkDesk
+  # AI Type:zhiPu、alibabaAi、kimiAi、sparkDesk、ollama、openRouter
   type:
   # Model name:glm-4、qwen-turboo、moonshot-v1-8k、generalv3.5
   model:
@@ -114,3 +200,5 @@ ai:
   api-key:
   #At present, only IFLYTEK large model needs to be filled in
   api-secret:
+  # The URL of the ollama AI service
+  api-url:
diff --git a/hertzbeat-manager/src/main/resources/application.yml 
b/hertzbeat-manager/src/main/resources/application.yml
index dcd30990fb..69027e88a6 100644
--- a/hertzbeat-manager/src/main/resources/application.yml
+++ b/hertzbeat-manager/src/main/resources/application.yml
@@ -140,6 +140,8 @@ warehouse:
       insert:
         buffer-size: 100
         flush-interval: 3
+        compression:
+          enabled: false
       cluster:
         enabled: false
         select:
diff --git 
a/hertzbeat-warehouse/src/main/java/org/apache/hertzbeat/warehouse/store/history/tsdb/vm/VictoriaMetricsDataStorage.java
 
b/hertzbeat-warehouse/src/main/java/org/apache/hertzbeat/warehouse/store/history/tsdb/vm/VictoriaMetricsDataStorage.java
index 59e504c51e..afd38cbabb 100644
--- 
a/hertzbeat-warehouse/src/main/java/org/apache/hertzbeat/warehouse/store/history/tsdb/vm/VictoriaMetricsDataStorage.java
+++ 
b/hertzbeat-warehouse/src/main/java/org/apache/hertzbeat/warehouse/store/history/tsdb/vm/VictoriaMetricsDataStorage.java
@@ -17,6 +17,7 @@
 
 package org.apache.hertzbeat.warehouse.store.history.tsdb.vm;
 
+import java.io.ByteArrayOutputStream;
 import java.math.BigDecimal;
 import java.math.RoundingMode;
 import java.net.URI;
@@ -35,6 +36,7 @@ import java.util.Map;
 import java.util.concurrent.BlockingQueue;
 import java.util.concurrent.LinkedBlockingQueue;
 import java.util.concurrent.TimeUnit;
+import java.util.zip.GZIPOutputStream;
 
 import com.google.common.collect.Maps;
 import lombok.AllArgsConstructor;
@@ -113,8 +115,8 @@ public class VictoriaMetricsDataStorage extends 
AbstractHistoryDataStorage {
         this.restTemplate = restTemplate;
         victoriaMetricsProp = victoriaMetricsProperties;
         serverAvailable = checkVictoriaMetricsDatasourceAvailable();
-        serverAvailable = checkVictoriaMetricsDatasourceAvailable();
-        insertConfig = victoriaMetricsProperties.insert() == null ? new 
VictoriaMetricsProperties.InsertConfig(100, 3) : 
victoriaMetricsProperties.insert();
+        insertConfig = victoriaMetricsProperties.insert() == null ? new 
VictoriaMetricsProperties.InsertConfig(100, 3,
+                new VictoriaMetricsProperties.Compression(false)) : 
victoriaMetricsProperties.insert();
         metricsBufferQueue = new 
LinkedBlockingQueue<>(insertConfig.bufferSize());
         initializeFlushTimer();
     }
@@ -605,6 +607,7 @@ public class VictoriaMetricsDataStorage extends 
AbstractHistoryDataStorage {
                 }
                 if (metricsFlushTimer != null && !metricsFlushTimer.isStop()) {
                     metricsFlushTimer.newTimeout(this, 
insertConfig.flushInterval(), TimeUnit.SECONDS);
+                    log.debug("[Victoria Metrics] Rescheduled next flush task 
in {} seconds.", insertConfig.flushInterval());
                 }
             } catch (Exception e) {
                 log.error("[VictoriaMetrics] flush task error: {}", 
e.getMessage(), e);
@@ -625,11 +628,29 @@ public class VictoriaMetricsDataStorage extends 
AbstractHistoryDataStorage {
                 String encodedAuth = Base64Util.encode(authStr);
                 headers.add(HttpHeaders.AUTHORIZATION,  NetworkConstants.BASIC 
+ SignConstants.BLANK + encodedAuth);
             }
+
             StringBuilder stringBuilder = new StringBuilder();
             for (VictoriaMetricsContent content : contentList) {
                 stringBuilder.append(JsonUtil.toJson(content)).append("\n");
             }
-            HttpEntity<String> httpEntity = new 
HttpEntity<>(stringBuilder.toString(), headers);
+            String payload = stringBuilder.toString();
+
+            Object httpEntity;
+            if (insertConfig.compression().enabled()) {
+                // enable compression
+                headers.set(HttpHeaders.CONTENT_ENCODING, "gzip");
+                // compress the payload using gzip
+                try (ByteArrayOutputStream bos = new 
ByteArrayOutputStream(payload.length());
+                     GZIPOutputStream gzip = new GZIPOutputStream(bos)) {
+                    gzip.write(payload.getBytes(StandardCharsets.UTF_8));
+                    // finishes writing compressed data before the gzip stream 
is closed.
+                    gzip.finish();
+                    httpEntity = new HttpEntity<>(bos.toByteArray(), headers);
+                }
+            } else {
+                httpEntity = new HttpEntity<>(payload, headers);
+            }
+
             ResponseEntity<String> responseEntity = 
restTemplate.postForEntity(victoriaMetricsProp.url() + IMPORT_PATH,
                     httpEntity, String.class);
             if (responseEntity.getStatusCode().is2xxSuccessful()) {
diff --git 
a/hertzbeat-warehouse/src/main/java/org/apache/hertzbeat/warehouse/store/history/tsdb/vm/VictoriaMetricsProperties.java
 
b/hertzbeat-warehouse/src/main/java/org/apache/hertzbeat/warehouse/store/history/tsdb/vm/VictoriaMetricsProperties.java
index 42e7571000..dccb01ca03 100644
--- 
a/hertzbeat-warehouse/src/main/java/org/apache/hertzbeat/warehouse/store/history/tsdb/vm/VictoriaMetricsProperties.java
+++ 
b/hertzbeat-warehouse/src/main/java/org/apache/hertzbeat/warehouse/store/history/tsdb/vm/VictoriaMetricsProperties.java
@@ -26,8 +26,11 @@ import 
org.springframework.boot.context.properties.bind.DefaultValue;
 /**
  * Victoria metrics configuration information.
  */
-
-@ConfigurationProperties(prefix = 
ConfigConstants.FunctionModuleConstants.WAREHOUSE + SignConstants.DOT + 
WarehouseConstants.STORE + SignConstants.DOT + 
WarehouseConstants.HistoryName.VM)
+@ConfigurationProperties(prefix = 
ConfigConstants.FunctionModuleConstants.WAREHOUSE
+        + SignConstants.DOT
+        + WarehouseConstants.STORE
+        + SignConstants.DOT
+        + WarehouseConstants.HistoryName.VM)
 public record VictoriaMetricsProperties(@DefaultValue("false") boolean enabled,
                                         @DefaultValue("http://localhost:8428";) 
String url,
                                         String username,
@@ -35,7 +38,10 @@ public record 
VictoriaMetricsProperties(@DefaultValue("false") boolean enabled,
                                         InsertConfig insert) {
 
     record InsertConfig(@DefaultValue("100") int bufferSize,
-                        @DefaultValue("3") int flushInterval) {
+                        @DefaultValue("3") int flushInterval,
+                        Compression compression) {
+    }
 
+    record Compression(@DefaultValue("false") boolean enabled) {
     }
 }
diff --git 
a/hertzbeat-warehouse/src/test/java/org/apache/hertzbeat/warehouse/store/history/tsdb/vm/VictoriaMetricsDataStorageTest.java
 
b/hertzbeat-warehouse/src/test/java/org/apache/hertzbeat/warehouse/store/history/tsdb/vm/VictoriaMetricsDataStorageTest.java
new file mode 100644
index 0000000000..37957499e2
--- /dev/null
+++ 
b/hertzbeat-warehouse/src/test/java/org/apache/hertzbeat/warehouse/store/history/tsdb/vm/VictoriaMetricsDataStorageTest.java
@@ -0,0 +1,221 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.hertzbeat.warehouse.store.history.tsdb.vm;
+
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.startsWith;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+import static org.mockito.Mockito.times;
+
+import org.apache.arrow.vector.types.pojo.ArrowType;
+import org.apache.arrow.vector.types.pojo.Field;
+import org.apache.arrow.vector.types.pojo.FieldType;
+import org.apache.hertzbeat.common.constants.CommonConstants;
+import org.apache.hertzbeat.common.constants.MetricDataConstants;
+import org.apache.hertzbeat.common.entity.arrow.ArrowCell;
+import org.apache.hertzbeat.common.entity.arrow.RowWrapper;
+import org.apache.hertzbeat.common.entity.message.CollectRep;
+import org.awaitility.Awaitility;
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.ExtendWith;
+
+import org.mockito.Mock;
+import org.mockito.Mockito;
+import org.mockito.junit.jupiter.MockitoExtension;
+import org.mockito.junit.jupiter.MockitoSettings;
+import org.mockito.quality.Strictness;
+
+import org.springframework.http.HttpEntity;
+import org.springframework.http.HttpMethod;
+import org.springframework.http.HttpStatus;
+import org.springframework.http.ResponseEntity;
+import org.springframework.web.client.RestTemplate;
+
+import java.util.List;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * Test case for {@link VictoriaMetricsDataStorage}
+ */
+@ExtendWith(MockitoExtension.class)
+@MockitoSettings(strictness = Strictness.LENIENT)
+class VictoriaMetricsDataStorageTest {
+
+    @Mock
+    private VictoriaMetricsProperties victoriaMetricsProperties;
+
+    @Mock
+    private RestTemplate restTemplate;
+
+    @Mock
+    private ResponseEntity<String> responseEntity;
+
+    private VictoriaMetricsDataStorage victoriaMetricsDataStorage;
+
+    @BeforeEach
+    void setUp() {
+        when(victoriaMetricsProperties.enabled()).thenReturn(true);
+        
when(victoriaMetricsProperties.url()).thenReturn("http://localhost:8428";);
+        when(victoriaMetricsProperties.username()).thenReturn("root");
+        when(victoriaMetricsProperties.password()).thenReturn("root");
+        // on successful write, VictoriaMetrics returns HTTP 204 (No Content)
+        when(responseEntity.getStatusCode()).thenReturn(HttpStatus.NO_CONTENT);
+        when(restTemplate.exchange(anyString(), eq(HttpMethod.GET), 
any(HttpEntity.class), eq(String.class)))
+                .thenReturn(responseEntity);
+        when(restTemplate.postForEntity(anyString(), any(HttpEntity.class), 
eq(String.class)))
+                .thenReturn(responseEntity);
+    }
+
+    @Test
+    void testSaveDataInitialization() {
+        // using the default insertion policy, schedule a task once upon 
object instantiation (with second-level time granularity)
+        victoriaMetricsDataStorage = new 
VictoriaMetricsDataStorage(victoriaMetricsProperties, restTemplate);
+        // execute one-time data insertion
+        victoriaMetricsDataStorage.saveData(generateMockedMetricsData());
+        // wait for the timer's first insertion task execution and verify if 
it was called once
+        Awaitility.await().atMost(5, TimeUnit.SECONDS).untilAsserted(() ->
+                verify(restTemplate, times(1)).postForEntity(
+                        startsWith(victoriaMetricsProperties.url()),
+                        any(HttpEntity.class),
+                        eq(String.class)
+                )
+        );
+    }
+
+    @Test
+    void testSaveDataBySize() {
+        // verify insert process for buffer size, with the flush interval 
defined as an unreachable state
+        when(victoriaMetricsProperties.insert()).thenReturn(new 
VictoriaMetricsProperties.InsertConfig(
+                10, Integer.MAX_VALUE, new 
VictoriaMetricsProperties.Compression(false)));
+        victoriaMetricsDataStorage = new 
VictoriaMetricsDataStorage(victoriaMetricsProperties, restTemplate);
+
+        victoriaMetricsDataStorage.saveData(generateMockedMetricsData());
+        // wait for the timer to execute its first insertion task
+        Awaitility.await().atMost(5, TimeUnit.SECONDS).untilAsserted(() ->
+                verify(restTemplate, times(1)).postForEntity(
+                        startsWith(victoriaMetricsProperties.url()),
+                        any(HttpEntity.class),
+                        eq(String.class)
+                )
+        );
+
+        // triggers the buffer size insertion condition
+        for (int i = 0; i < 10 * 0.8; i++) {
+            victoriaMetricsDataStorage.saveData(generateMockedMetricsData());
+        }
+        // wait for the timer to execute the task again
+        Awaitility.await().atMost(5, TimeUnit.SECONDS).untilAsserted(() ->
+                verify(restTemplate, times(2)).postForEntity(
+                        startsWith(victoriaMetricsProperties.url()),
+                        any(HttpEntity.class),
+                        eq(String.class)
+                )
+        );
+    }
+
+    @Test
+    void testSaveDataByTime() {
+        // verify insert process for flush interval and set the buffer size to 
an unreachable state
+        when(victoriaMetricsProperties.insert()).thenReturn(new 
VictoriaMetricsProperties.InsertConfig(
+                10000, 2, new VictoriaMetricsProperties.Compression(false)));
+        victoriaMetricsDataStorage = new 
VictoriaMetricsDataStorage(victoriaMetricsProperties, restTemplate);
+
+        victoriaMetricsDataStorage.saveData(generateMockedMetricsData());
+        // wait for the timer to execute its first insertion task
+        Awaitility.await().atMost(5, TimeUnit.SECONDS).untilAsserted(() ->
+                verify(restTemplate, times(1)).postForEntity(
+                        startsWith(victoriaMetricsProperties.url()),
+                        any(HttpEntity.class),
+                        eq(String.class)
+                )
+        );
+
+        victoriaMetricsDataStorage.saveData(generateMockedMetricsData());
+        // wait for the flush interval to be triggered
+        Awaitility.await().atMost(5, TimeUnit.SECONDS).untilAsserted(() ->
+                verify(restTemplate, times(2)).postForEntity(
+                        startsWith(victoriaMetricsProperties.url()),
+                        any(),
+                        eq(String.class)
+        ));
+    }
+
+    @AfterEach
+    void stop() {
+        if (victoriaMetricsDataStorage != null) {
+            victoriaMetricsDataStorage.destroy();
+        }
+    }
+
+    // for historical data insertion (which mandates preparatory steps), 
instantiate a mock MetricsData object
+    public static CollectRep.MetricsData generateMockedMetricsData() {
+        CollectRep.MetricsData mockMetricsData = 
Mockito.mock(CollectRep.MetricsData.class);
+
+        when(mockMetricsData.getId()).thenReturn(0L);
+        when(mockMetricsData.getMetrics()).thenReturn("cpu");
+        when(mockMetricsData.getTime()).thenReturn(System.currentTimeMillis());
+        when(mockMetricsData.getCode()).thenReturn(CollectRep.Code.SUCCESS);
+        when(mockMetricsData.getApp()).thenReturn("app");
+
+        CollectRep.ValueRow mockValueRow = 
Mockito.mock(CollectRep.ValueRow.class);
+        List<String> columnValues = List.of("server-test-01", "68.7");
+        when(mockValueRow.getColumnsList()).thenReturn(columnValues);
+        when(mockValueRow.getColumns(0)).thenReturn("server-test-01");
+        when(mockValueRow.getColumns(1)).thenReturn("68.7");
+        List<CollectRep.ValueRow> mockValueRowsList = List.of(mockValueRow);
+        when(mockMetricsData.getValues()).thenReturn(mockValueRowsList);
+
+        CollectRep.Field instanceField = Mockito.mock(CollectRep.Field.class);
+        when(instanceField.getName()).thenReturn("instance");
+        CollectRep.Field usageField = Mockito.mock(CollectRep.Field.class);
+        when(usageField.getName()).thenReturn("usage");
+        CollectRep.Field systemField = Mockito.mock(CollectRep.Field.class);
+        when(systemField.getName()).thenReturn("system");
+        List<CollectRep.Field> mockFields = List.of(instanceField, usageField, 
systemField);
+        when(mockMetricsData.getFields()).thenReturn(mockFields);
+
+        ArrowType instanceArrowType = new ArrowType.Utf8();
+        FieldType instanceFieldType = new FieldType(true, instanceArrowType, 
null, null);
+        Field instanceRealField = new Field("instance", instanceFieldType, 
null);
+        ArrowCell instanceCell = Mockito.mock(ArrowCell.class);
+        when(instanceCell.getField()).thenReturn(instanceRealField);
+        when(instanceCell.getValue()).thenReturn("server-test-01");
+        
when(instanceCell.getMetadataAsBoolean(MetricDataConstants.LABEL)).thenReturn(true);
+        
when(instanceCell.getMetadataAsByte(MetricDataConstants.TYPE)).thenReturn(CommonConstants.TYPE_STRING);
+
+        ArrowCell usageCell = Mockito.mock(ArrowCell.class);
+        when(usageCell.getField()).thenReturn(instanceRealField);
+        when(usageCell.getValue()).thenReturn("68.7");
+        
when(usageCell.getMetadataAsBoolean(MetricDataConstants.LABEL)).thenReturn(false);
+        
when(usageCell.getMetadataAsByte(MetricDataConstants.TYPE)).thenReturn(CommonConstants.TYPE_NUMBER);
+        List<ArrowCell> mockCells = List.of(instanceCell, usageCell);
+
+        RowWrapper mockRowWrapper = Mockito.mock(RowWrapper.class);
+        when(mockRowWrapper.hasNextRow()).thenReturn(true).thenReturn(false);
+        when(mockRowWrapper.nextRow()).thenReturn(mockRowWrapper);
+        when(mockRowWrapper.cellStream()).thenAnswer(invocation -> 
mockCells.stream());
+        when(mockMetricsData.readRow()).thenReturn(mockRowWrapper);
+        return mockMetricsData;
+    }
+
+}
diff --git 
a/hertzbeat-warehouse/src/test/java/org/apache/hertzbeat/warehouse/store/history/tsdb/vm/VictoriaMetricsPropertiesTest.java
 
b/hertzbeat-warehouse/src/test/java/org/apache/hertzbeat/warehouse/store/history/tsdb/vm/VictoriaMetricsPropertiesTest.java
new file mode 100644
index 0000000000..1ad1873966
--- /dev/null
+++ 
b/hertzbeat-warehouse/src/test/java/org/apache/hertzbeat/warehouse/store/history/tsdb/vm/VictoriaMetricsPropertiesTest.java
@@ -0,0 +1,61 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.hertzbeat.warehouse.store.history.tsdb.vm;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import org.junit.jupiter.api.Test;
+import org.springframework.beans.factory.annotation.Autowired;
+import 
org.springframework.boot.context.properties.EnableConfigurationProperties;
+import org.springframework.boot.test.context.SpringBootTest;
+
+/**
+ * Test case for {@link VictoriaMetricsProperties}
+ */
+@EnableConfigurationProperties(value = VictoriaMetricsProperties.class)
+@SpringBootTest(
+        classes = VictoriaMetricsPropertiesTest.class,
+        properties = {
+                "warehouse.store.victoria-metrics.enabled=true",
+                "warehouse.store.victoria-metrics.username=test_user",
+                "warehouse.store.victoria-metrics.insert.buffer-size=999",
+                
"warehouse.store.victoria-metrics.insert.compression.enabled=true"
+        }
+)
+class VictoriaMetricsPropertiesTest {
+
+    @Autowired
+    private VictoriaMetricsProperties properties;
+
+    @Test
+    void propertiesShouldBeInjectedAndCorrectlyBound() {
+        assertThat(properties).isNotNull();
+        assertThat(properties.enabled()).isTrue();
+        // url not configured, default value injected
+        assertThat(properties.url()).isEqualTo("http://localhost:8428";);
+        assertThat(properties.username()).isEqualTo("test_user");
+        // password not configured, null injected
+        assertThat(properties.password()).isNull();
+        assertThat(properties.insert()).isNotNull();
+        assertThat(properties.insert().bufferSize()).isEqualTo(999);
+        // flush-interval not configured, default value is 3
+        assertThat(properties.insert().flushInterval()).isEqualTo(3);
+        assertThat(properties.insert().compression()).isNotNull();
+        assertThat(properties.insert().compression().enabled()).isTrue();
+    }
+
+}
diff --git 
a/script/docker-compose/hertzbeat-mysql-victoria-metrics/conf/application.yml 
b/script/docker-compose/hertzbeat-mysql-victoria-metrics/conf/application.yml
index 11b132a7d0..d009cd1a70 100644
--- 
a/script/docker-compose/hertzbeat-mysql-victoria-metrics/conf/application.yml
+++ 
b/script/docker-compose/hertzbeat-mysql-victoria-metrics/conf/application.yml
@@ -128,6 +128,8 @@ warehouse:
       insert:
         buffer-size: 100
         flush-interval: 3
+        compression:
+          enabled: false
   # store real-time metrics data, enable only one below
   real-time:
     memory:
diff --git 
a/script/docker-compose/hertzbeat-postgresql-victoria-metrics/conf/application.yml
 
b/script/docker-compose/hertzbeat-postgresql-victoria-metrics/conf/application.yml
index f608fc2c5f..14601a6ad5 100644
--- 
a/script/docker-compose/hertzbeat-postgresql-victoria-metrics/conf/application.yml
+++ 
b/script/docker-compose/hertzbeat-postgresql-victoria-metrics/conf/application.yml
@@ -127,6 +127,8 @@ warehouse:
       insert:
         buffer-size: 100
         flush-interval: 3
+        compression:
+          enabled: false
   # store real-time metrics data, enable only one below
   real-time:
     memory:


---------------------------------------------------------------------
To unsubscribe, e-mail: notifications-unsubscr...@hertzbeat.apache.org
For additional commands, e-mail: notifications-h...@hertzbeat.apache.org

Reply via email to