You are viewing a plain text version of this content. The canonical link for it is here.
Posted to notifications@skywalking.apache.org by wu...@apache.org on 2023/11/27 23:40:20 UTC

(skywalking) branch master updated: Support `increase/rate` function in the `MQE` query language. (#11591)

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

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


The following commit(s) were added to refs/heads/master by this push:
     new 075b08ce74 Support `increase/rate` function in the `MQE` query language. (#11591)
075b08ce74 is described below

commit 075b08ce74868ed097b1ab76f06ee778ed8d5329
Author: Wan Kai <wa...@foxmail.com>
AuthorDate: Tue Nov 28 07:40:13 2023 +0800

    Support `increase/rate` function in the `MQE` query language. (#11591)
---
 docs/en/api/metrics-query-expression.md            |  40 ++++++++
 docs/en/changes/changes.md                         |   1 +
 .../apache/skywalking/mqe/rt/grammar/MQELexer.g4   |   2 +
 .../apache/skywalking/mqe/rt/grammar/MQEParser.g4  |   6 +-
 .../apache/skywalking/mqe/rt/MQEVisitorBase.java   |  35 +++++++
 .../skywalking/mqe/rt/operation/TrendOp.java       | 106 +++++++++++++++++++++
 .../org/apache/skywalking/mqe/rt/TrendOpTest.java  |  72 ++++++++++++++
 .../oap/server/core/alarm/provider/AlarmRule.java  |   3 +
 .../server/core/alarm/provider/RunningRule.java    |  17 ++--
 .../provider/expr/rt/AlarmMQEVerifyVisitor.java    |  25 +++++
 .../alarm/provider/expr/rt/AlarmMQEVisitor.java    |  35 ++++++-
 .../server/core/alarm/provider/AlarmRuleTest.java  |   3 +
 .../core/alarm/provider/RunningRuleTest.java       |  11 ++-
 .../oap/query/promql/handler/PromQLApiHandler.java |   2 +-
 .../promql/rt/exception/ParseErrorListener.java    |   2 +-
 .../oap/query/graphql/mqe/rt/MQEVisitor.java       |  73 +++++++++++---
 .../graphql/resolver/MetricsExpressionQuery.java   |   2 +-
 test/e2e-v2/cases/alarm/alarm-settings.yml         |   4 +-
 test/e2e-v2/cases/alarm/es/e2e.yaml                |   2 +-
 test/e2e-v2/cases/alarm/es/es-sharding/e2e.yaml    |   2 +-
 .../alarm/expected/silence-after-graphql-warn.yml  |   4 +-
 .../cases/alarm/expected/silence-after-webhook.yml |   4 +-
 .../alarm/expected/silence-before-graphql-warn.yml |   2 +-
 .../alarm/expected/silence-before-webhook.yml      |   2 +-
 test/e2e-v2/cases/alarm/h2/e2e.yaml                |   2 +-
 test/e2e-v2/cases/alarm/mysql/e2e.yaml             |   2 +-
 test/e2e-v2/cases/alarm/postgres/e2e.yaml          |   2 +-
 test/e2e-v2/cases/mqe/expected/trend-OP.yml        |  31 ++++++
 test/e2e-v2/cases/mqe/mqe-cases.yaml               |   6 ++
 29 files changed, 457 insertions(+), 41 deletions(-)

diff --git a/docs/en/api/metrics-query-expression.md b/docs/en/api/metrics-query-expression.md
index 6c5798c3fa..2ca901e2eb 100644
--- a/docs/en/api/metrics-query-expression.md
+++ b/docs/en/api/metrics-query-expression.md
@@ -296,6 +296,46 @@ view_as_seq(not_existing, service_cpm)
 #### Result Type
 The result type is determined by the type of selected not-null metric expression.
 
+## Trend Operation
+Trend Operation takes an expression and performs a trend calculation on its results.
+
+Expression:
+```text
+<Trend-Operator>(Metrics Expression, time_range)
+```
+
+`time_range` is the positive int of the calculated range. The unit will automatically align with to the query [Step](../../../oap-server/server-core/src/main/java/org/apache/skywalking/oap/server/core/query/enumeration/Step.java),
+for example, if the query Step is `MINUTE`, the unit of `time_range` is `minute`.
+
+
+| Operator | Definition                                                                            | ExpressionResultType          |
+|----------|---------------------------------------------------------------------------------------|-------------------------------|
+| increase | returns the increase in the time range in the time series                             | TIME_SERIES_VALUES |
+| rate     | returns the per-second average rate of increase in the time range in the time series  | TIME_SERIES_VALUES |
+
+For example:
+If we want to query the increase value of the `service_cpm` metric in 2 minute(assume the query Step is MINUTE),
+we can use the following expression:
+
+```text
+increase(service_cpm, 2)
+```
+
+If the query duration is 3 minutes, from (T1 to T3) and the metric has values in time series:
+```text
+V(T1-2), V(T1-1), V(T1), V(T2), V(T3)
+```
+then the expression result is:
+```text
+V(T1)-V(T1-2), V(T2)-V(T1-1), V(T3)-V(T1)
+```
+
+**Note**:
+* If the calculated metric value is empty, the result will be empty. Assume in the T3 point, the increase value = V(T3)-V(T1), If the metric V(T3) or V(T1) is empty, the result value in T3 will be empty.
+
+### Result Type
+TIME_SERIES_VALUES.
+
 ## Expression Query Example
 ### Labeled Value Metrics
 ```text
diff --git a/docs/en/changes/changes.md b/docs/en/changes/changes.md
index b033310a19..97f0ebc485 100644
--- a/docs/en/changes/changes.md
+++ b/docs/en/changes/changes.md
@@ -48,6 +48,7 @@
 * Replace Metrics v2 protocol with MQE in UI templates and E2E Test.
 * Fix incorrect apisix metrics otel rules.
 * Support `Scratch The OAP Config Dump`.
+* Support `increase/rate` function in the `MQE` query language.
 * Group service endpoints into `_abandoned` when endpoints have high
   cardinality.
 
diff --git a/oap-server/mqe-grammar/src/main/antlr4/org/apache/skywalking/mqe/rt/grammar/MQELexer.g4 b/oap-server/mqe-grammar/src/main/antlr4/org/apache/skywalking/mqe/rt/grammar/MQELexer.g4
index b7a870a6bc..69687a8702 100644
--- a/oap-server/mqe-grammar/src/main/antlr4/org/apache/skywalking/mqe/rt/grammar/MQELexer.g4
+++ b/oap-server/mqe-grammar/src/main/antlr4/org/apache/skywalking/mqe/rt/grammar/MQELexer.g4
@@ -59,6 +59,8 @@ ABS:         'abs';
 CEIL:        'ceil';
 FLOOR:       'floor';
 ROUND:       'round';
+INCREASE:    'increase';
+RATE:        'rate';
 
 // TopN
 TOP_N:        'top_n';
diff --git a/oap-server/mqe-grammar/src/main/antlr4/org/apache/skywalking/mqe/rt/grammar/MQEParser.g4 b/oap-server/mqe-grammar/src/main/antlr4/org/apache/skywalking/mqe/rt/grammar/MQEParser.g4
index 0c2d9a9317..e67414422d 100644
--- a/oap-server/mqe-grammar/src/main/antlr4/org/apache/skywalking/mqe/rt/grammar/MQEParser.g4
+++ b/oap-server/mqe-grammar/src/main/antlr4/org/apache/skywalking/mqe/rt/grammar/MQEParser.g4
@@ -31,8 +31,9 @@ expression
     | aggregation L_PAREN expression R_PAREN # aggregationOp
     | mathematical_operator0 L_PAREN expression R_PAREN #mathematicalOperator0OP
     | mathematical_operator1 L_PAREN expression COMMA parameter R_PAREN #mathematicalOperator1OP
+    | trend L_PAREN metric COMMA INTEGER R_PAREN #trendOP
     | logical_operator L_PAREN expressionList R_PAREN #logicalOperatorOP
-    | topN L_PAREN metric COMMA parameter COMMA order R_PAREN  #topNOP
+    | topN L_PAREN metric COMMA INTEGER COMMA order R_PAREN  #topNOP
     | relabels L_PAREN expression COMMA label R_PAREN #relablesOP
     | aggregateLabels L_PAREN expression COMMA aggregateLabelsFunc R_PAREN #aggregateLabelsOp
     ;
@@ -69,6 +70,9 @@ mathematical_operator0:
 mathematical_operator1:
     ROUND;
 
+trend:
+    INCREASE | RATE;
+
 topN: TOP_N;
 
 logical_operator:
diff --git a/oap-server/mqe-rt/src/main/java/org/apache/skywalking/mqe/rt/MQEVisitorBase.java b/oap-server/mqe-rt/src/main/java/org/apache/skywalking/mqe/rt/MQEVisitorBase.java
index 2f272dc6c0..235abb30c8 100644
--- a/oap-server/mqe-rt/src/main/java/org/apache/skywalking/mqe/rt/MQEVisitorBase.java
+++ b/oap-server/mqe-rt/src/main/java/org/apache/skywalking/mqe/rt/MQEVisitorBase.java
@@ -30,17 +30,24 @@ import org.apache.skywalking.mqe.rt.operation.BinaryOp;
 import org.apache.skywalking.mqe.rt.operation.CompareOp;
 import org.apache.skywalking.mqe.rt.operation.LogicalFunctionOp;
 import org.apache.skywalking.mqe.rt.operation.MathematicalFunctionOp;
+import org.apache.skywalking.mqe.rt.operation.TrendOp;
 import org.apache.skywalking.mqe.rt.type.ExpressionResult;
 import org.apache.skywalking.mqe.rt.exception.IllegalExpressionException;
 import org.apache.skywalking.mqe.rt.type.ExpressionResultType;
 import org.apache.skywalking.mqe.rt.type.MQEValue;
 import org.apache.skywalking.mqe.rt.type.MQEValues;
 import org.apache.skywalking.oap.server.core.Const;
+import org.apache.skywalking.oap.server.core.query.enumeration.Step;
 import org.apache.skywalking.oap.server.library.util.StringUtil;
 
 @Slf4j
 public abstract class MQEVisitorBase extends MQEParserBaseVisitor<ExpressionResult> {
     public final static String GENERAL_LABEL_NAME = "_";
+    public final Step queryStep;
+
+    protected MQEVisitorBase(final Step queryStep) {
+        this.queryStep = queryStep;
+    }
 
     @Override
     public ExpressionResult visitParensOp(MQEParser.ParensOpContext ctx) {
@@ -250,6 +257,34 @@ public abstract class MQEVisitorBase extends MQEParserBaseVisitor<ExpressionResu
         }
     }
 
+    @Override
+    public ExpressionResult visitTrendOP(MQEParser.TrendOPContext ctx) {
+        int opType = ctx.trend().getStart().getType();
+        int trendRange = Integer.parseInt(ctx.INTEGER().getText());
+        if (trendRange < 1) {
+            ExpressionResult result = new ExpressionResult();
+            result.setType(ExpressionResultType.UNKNOWN);
+            result.setError("The trend range must be greater than 0.");
+            return result;
+        }
+        ExpressionResult expResult = visit(ctx.metric());
+        if (StringUtil.isNotEmpty(expResult.getError())) {
+            return expResult;
+        }
+        if (expResult.getType() != ExpressionResultType.TIME_SERIES_VALUES) {
+            expResult.setError("The result of expression [" + ctx.metric().getText() + "] is not a time series result.");
+            return expResult;
+        }
+        try {
+            return TrendOp.doTrendOp(expResult, opType, trendRange, queryStep);
+        } catch (IllegalExpressionException e) {
+            ExpressionResult result = new ExpressionResult();
+            result.setType(ExpressionResultType.UNKNOWN);
+            result.setError(e.getMessage());
+            return result;
+        }
+    }
+
     @Override
     public abstract ExpressionResult visitMetric(MQEParser.MetricContext ctx);
 }
diff --git a/oap-server/mqe-rt/src/main/java/org/apache/skywalking/mqe/rt/operation/TrendOp.java b/oap-server/mqe-rt/src/main/java/org/apache/skywalking/mqe/rt/operation/TrendOp.java
new file mode 100644
index 0000000000..60c0887be3
--- /dev/null
+++ b/oap-server/mqe-rt/src/main/java/org/apache/skywalking/mqe/rt/operation/TrendOp.java
@@ -0,0 +1,106 @@
+/*
+ * 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.skywalking.mqe.rt.operation;
+
+import java.util.ArrayList;
+import java.util.List;
+import org.apache.skywalking.mqe.rt.exception.IllegalExpressionException;
+import org.apache.skywalking.mqe.rt.grammar.MQEParser;
+import org.apache.skywalking.mqe.rt.type.ExpressionResult;
+import org.apache.skywalking.mqe.rt.type.MQEValue;
+import org.apache.skywalking.oap.server.core.query.enumeration.Step;
+
+public class TrendOp {
+    public static ExpressionResult doTrendOp(ExpressionResult expResult,
+                                             int opType,
+                                             int trendRange,
+                                             Step step) throws IllegalExpressionException {
+        switch (opType) {
+            case MQEParser.INCREASE:
+                return TrendOp.calculateIncrease(expResult, trendRange);
+            case MQEParser.RATE:
+                return TrendOp.calculateRate(expResult, trendRange, step);
+        }
+
+        throw new IllegalExpressionException("Unsupported function.");
+    }
+
+    private static ExpressionResult calculateIncrease(ExpressionResult expResult, int trendRange) {
+        expResult.getResults().forEach(resultValues -> {
+            List<MQEValue> mqeValues = resultValues.getValues();
+            List<MQEValue> newMqeValues = new ArrayList<>();
+            for (int i = trendRange; i < mqeValues.size(); i++) {
+                MQEValue mqeValue = mqeValues.get(i);
+                //if the current value is empty, then the trend value is empty
+                if (mqeValue.isEmptyValue()) {
+                    newMqeValues.add(mqeValue);
+                    continue;
+                }
+                MQEValue newMqeValue = new MQEValue();
+                newMqeValue.setId(mqeValue.getId());
+
+                //if the previous value is empty, then the trend value is empty
+                if (mqeValues.get(i - trendRange).isEmptyValue()) {
+                    newMqeValue.setEmptyValue(true);
+                    newMqeValues.add(newMqeValue);
+                    continue;
+                }
+
+                newMqeValue.setEmptyValue(mqeValue.isEmptyValue());
+                newMqeValue.setId(mqeValue.getId());
+                newMqeValue.setTraceID(mqeValue.getTraceID());
+                double newValue = mqeValue.getDoubleValue() - mqeValues.get(i - trendRange).getDoubleValue();
+                newMqeValue.setDoubleValue(newValue);
+                newMqeValues.add(newMqeValue);
+            }
+            resultValues.setValues(newMqeValues);
+        });
+        return expResult;
+    }
+
+    private static ExpressionResult calculateRate(ExpressionResult expResult, int trendRange, Step step) {
+        ExpressionResult result = calculateIncrease(expResult, trendRange);
+        long rangeSeconds;
+        switch (step) {
+            case SECOND:
+                rangeSeconds = trendRange;
+                break;
+            case MINUTE:
+                rangeSeconds = trendRange * 60;
+                break;
+            case HOUR:
+                rangeSeconds = trendRange * 3600;
+                break;
+            case DAY:
+                rangeSeconds = trendRange * 86400;
+                break;
+            default:
+                throw new IllegalArgumentException("Unsupported step: " + step);
+        }
+        result.getResults().forEach(resultValues -> {
+            resultValues.getValues().forEach(mqeValue -> {
+                if (!mqeValue.isEmptyValue()) {
+                    double newValue = mqeValue.getDoubleValue() / rangeSeconds;
+                    mqeValue.setDoubleValue(newValue);
+                }
+            });
+        });
+        return result;
+    }
+}
diff --git a/oap-server/mqe-rt/src/test/java/org/apache/skywalking/mqe/rt/TrendOpTest.java b/oap-server/mqe-rt/src/test/java/org/apache/skywalking/mqe/rt/TrendOpTest.java
new file mode 100644
index 0000000000..3c55141238
--- /dev/null
+++ b/oap-server/mqe-rt/src/test/java/org/apache/skywalking/mqe/rt/TrendOpTest.java
@@ -0,0 +1,72 @@
+/*
+ * 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.skywalking.mqe.rt;
+
+import org.apache.skywalking.mqe.rt.grammar.MQEParser;
+import org.apache.skywalking.mqe.rt.operation.TrendOp;
+import org.apache.skywalking.mqe.rt.type.ExpressionResult;
+import org.apache.skywalking.mqe.rt.type.ExpressionResultType;
+import org.apache.skywalking.oap.server.core.query.enumeration.Step;
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+public class TrendOpTest {
+    private final MockData mockData = new MockData();
+
+    @Test
+    public void increaseNoLabeledTest() throws Exception {
+        ExpressionResult increase = TrendOp.doTrendOp(
+            mockData.newSeriesNoLabeledResult(100, 280), MQEParser.INCREASE, 1, Step.MINUTE);
+        assertEquals(ExpressionResultType.TIME_SERIES_VALUES, increase.getType());
+        assertEquals("300", increase.getResults().get(0).getValues().get(0).getId());
+        assertEquals(180, increase.getResults().get(0).getValues().get(0).getDoubleValue());
+    }
+
+    @Test
+    public void increaseLabeledTest() throws Exception {
+        ExpressionResult increase = TrendOp.doTrendOp(
+            mockData.newSeriesLabeledResult(100, 280, 100, 220), MQEParser.INCREASE, 1, Step.MINUTE);
+        assertEquals(ExpressionResultType.TIME_SERIES_VALUES, increase.getType());
+        assertEquals("300", increase.getResults().get(0).getValues().get(0).getId());
+        assertEquals(180, increase.getResults().get(0).getValues().get(0).getDoubleValue());
+        assertEquals("300", increase.getResults().get(1).getValues().get(0).getId());
+        assertEquals(120, increase.getResults().get(1).getValues().get(0).getDoubleValue());
+    }
+
+    @Test
+    public void rateNoLabeledTest() throws Exception {
+        ExpressionResult rate = TrendOp.doTrendOp(
+            mockData.newSeriesNoLabeledResult(100, 280), MQEParser.RATE, 1, Step.MINUTE);
+        assertEquals(ExpressionResultType.TIME_SERIES_VALUES, rate.getType());
+        assertEquals("300", rate.getResults().get(0).getValues().get(0).getId());
+        assertEquals(3, rate.getResults().get(0).getValues().get(0).getDoubleValue());
+    }
+
+    @Test
+    public void rateLabeledTest() throws Exception {
+        ExpressionResult rate = TrendOp.doTrendOp(
+            mockData.newSeriesLabeledResult(100, 280, 100, 220), MQEParser.RATE, 1, Step.MINUTE);
+        assertEquals(ExpressionResultType.TIME_SERIES_VALUES, rate.getType());
+        assertEquals("300", rate.getResults().get(0).getValues().get(0).getId());
+        assertEquals(3, rate.getResults().get(0).getValues().get(0).getDoubleValue());
+        assertEquals("300", rate.getResults().get(1).getValues().get(0).getId());
+        assertEquals(2, rate.getResults().get(1).getValues().get(0).getDoubleValue());
+    }
+}
diff --git a/oap-server/server-alarm-plugin/src/main/java/org/apache/skywalking/oap/server/core/alarm/provider/AlarmRule.java b/oap-server/server-alarm-plugin/src/main/java/org/apache/skywalking/oap/server/core/alarm/provider/AlarmRule.java
index 76c6e6cc1a..e5238719b7 100644
--- a/oap-server/server-alarm-plugin/src/main/java/org/apache/skywalking/oap/server/core/alarm/provider/AlarmRule.java
+++ b/oap-server/server-alarm-plugin/src/main/java/org/apache/skywalking/oap/server/core/alarm/provider/AlarmRule.java
@@ -58,6 +58,7 @@ public class AlarmRule {
     private String message;
     private Map<String, String> tags;
     private Set<String> hooks;
+    private int maxTrendRange;
 
     /**
      * Init includeMetrics and verify the expression.
@@ -67,6 +68,7 @@ public class AlarmRule {
         MQELexer lexer = new MQELexer(CharStreams.fromString(expression));
         lexer.addErrorListener(new ParseErrorListener());
         MQEParser parser = new MQEParser(new CommonTokenStream(lexer));
+        parser.addErrorListener(new ParseErrorListener());
         ParseTree tree;
         try {
             tree = parser.expression();
@@ -88,6 +90,7 @@ public class AlarmRule {
         verifyIncludeMetrics(visitor.getIncludeMetrics(), expression);
         this.expression = expression;
         this.includeMetrics = visitor.getIncludeMetrics();
+        this.maxTrendRange = visitor.getMaxTrendRange();
     }
 
     private void verifyIncludeMetrics(Set<String> includeMetrics, String expression) throws IllegalExpressionException {
diff --git a/oap-server/server-alarm-plugin/src/main/java/org/apache/skywalking/oap/server/core/alarm/provider/RunningRule.java b/oap-server/server-alarm-plugin/src/main/java/org/apache/skywalking/oap/server/core/alarm/provider/RunningRule.java
index 961473bb0d..a42e73f560 100644
--- a/oap-server/server-alarm-plugin/src/main/java/org/apache/skywalking/oap/server/core/alarm/provider/RunningRule.java
+++ b/oap-server/server-alarm-plugin/src/main/java/org/apache/skywalking/oap/server/core/alarm/provider/RunningRule.java
@@ -82,6 +82,8 @@ public class RunningRule {
     private final Set<String> hooks;
     private final Set<String> includeMetrics;
     private final ParseTree exprTree;
+    // The additional period is used to calculate the trend.
+    private final int additionalPeriod;
 
     public RunningRule(AlarmRule alarmRule) {
         expression = alarmRule.getExpression();
@@ -108,6 +110,7 @@ public class RunningRule {
         MQEParser parser = new MQEParser(new CommonTokenStream(lexer));
         parser.addErrorListener(new ParseErrorListener());
         this.exprTree = parser.expression();
+        this.additionalPeriod = alarmRule.getMaxTrendRange();
     }
 
     /**
@@ -134,7 +137,7 @@ public class RunningRule {
         AlarmEntity entity = new AlarmEntity(
             meta.getScope(), meta.getScopeId(), meta.getName(), meta.getId0(), meta.getId1());
 
-        Window window = windows.computeIfAbsent(entity, ignored -> new Window(period));
+        Window window = windows.computeIfAbsent(entity, ignored -> new Window(this.period, this.additionalPeriod));
         window.add(meta.getMetricsName(), metrics);
     }
 
@@ -234,13 +237,15 @@ public class RunningRule {
      */
     public class Window {
         private LocalDateTime endTime;
-        private final int period;
+        private final int additionalPeriod;
+        private final int size;
         private int silenceCountdown;
         private LinkedList<Map<String, Metrics>> values;
         private ReentrantLock lock = new ReentrantLock();
 
-        public Window(int period) {
-            this.period = period;
+        public Window(int period, int additionalPeriod) {
+            this.additionalPeriod = additionalPeriod;
+            this.size = period + additionalPeriod;
             // -1 means silence countdown is not running.
             silenceCountdown = -1;
             init();
@@ -342,7 +347,7 @@ public class RunningRule {
 
         private boolean isMatch() {
             int isMatch = 0;
-            AlarmMQEVisitor visitor = new AlarmMQEVisitor(this.values, this.endTime);
+            AlarmMQEVisitor visitor = new AlarmMQEVisitor(this.values, this.endTime, this.additionalPeriod);
             ExpressionResult parseResult = visitor.visit(exprTree);
             if (StringUtil.isNotBlank(parseResult.getError())) {
                 log.error("expression:" + expression + " error: " + parseResult.getError());
@@ -403,7 +408,7 @@ public class RunningRule {
 
         private void init() {
             values = new LinkedList<>();
-            for (int i = 0; i < period; i++) {
+            for (int i = 0; i < size; i++) {
                 values.add(null);
             }
         }
diff --git a/oap-server/server-alarm-plugin/src/main/java/org/apache/skywalking/oap/server/core/alarm/provider/expr/rt/AlarmMQEVerifyVisitor.java b/oap-server/server-alarm-plugin/src/main/java/org/apache/skywalking/oap/server/core/alarm/provider/expr/rt/AlarmMQEVerifyVisitor.java
index c6c556f9c8..cc0b861339 100644
--- a/oap-server/server-alarm-plugin/src/main/java/org/apache/skywalking/oap/server/core/alarm/provider/expr/rt/AlarmMQEVerifyVisitor.java
+++ b/oap-server/server-alarm-plugin/src/main/java/org/apache/skywalking/oap/server/core/alarm/provider/expr/rt/AlarmMQEVerifyVisitor.java
@@ -35,6 +35,7 @@ import org.apache.skywalking.mqe.rt.type.MQEValue;
 import org.apache.skywalking.mqe.rt.type.MQEValues;
 import org.apache.skywalking.mqe.rt.type.Metadata;
 import org.apache.skywalking.oap.server.core.Const;
+import org.apache.skywalking.oap.server.core.query.enumeration.Step;
 import org.apache.skywalking.oap.server.core.query.type.KeyValue;
 import org.apache.skywalking.oap.server.core.storage.annotation.Column;
 import org.apache.skywalking.oap.server.core.storage.annotation.ValueColumnMetadata;
@@ -48,6 +49,11 @@ import org.apache.skywalking.oap.server.library.util.StringUtil;
 @Slf4j
 public class AlarmMQEVerifyVisitor extends MQEVisitorBase {
     private final Set<String> includeMetrics = new HashSet<>();
+    private int maxTrendRange = 0;
+
+    public AlarmMQEVerifyVisitor() {
+        super(Step.MINUTE);
+    }
 
     @Override
     public ExpressionResult visitMetric(MQEParser.MetricContext ctx) {
@@ -113,4 +119,23 @@ public class AlarmMQEVerifyVisitor extends MQEVisitorBase {
             return result;
         }
     }
+
+    @Override
+    public ExpressionResult visitTrendOP(MQEParser.TrendOPContext ctx) {
+        int trendRange = Integer.parseInt(ctx.INTEGER().getText());
+        if (trendRange < 1) {
+            ExpressionResult result = new ExpressionResult();
+            result.setType(ExpressionResultType.UNKNOWN);
+            result.setError("The trend range must be greater than 0.");
+            return result;
+        }
+        setMaxTrendRange(trendRange);
+        return super.visitTrendOP(ctx);
+    }
+
+    private void setMaxTrendRange(int trendRange) {
+        if (trendRange > maxTrendRange) {
+            maxTrendRange = trendRange;
+        }
+    }
 }
diff --git a/oap-server/server-alarm-plugin/src/main/java/org/apache/skywalking/oap/server/core/alarm/provider/expr/rt/AlarmMQEVisitor.java b/oap-server/server-alarm-plugin/src/main/java/org/apache/skywalking/oap/server/core/alarm/provider/expr/rt/AlarmMQEVisitor.java
index 98a32d791b..712ff70e9a 100644
--- a/oap-server/server-alarm-plugin/src/main/java/org/apache/skywalking/oap/server/core/alarm/provider/expr/rt/AlarmMQEVisitor.java
+++ b/oap-server/server-alarm-plugin/src/main/java/org/apache/skywalking/oap/server/core/alarm/provider/expr/rt/AlarmMQEVisitor.java
@@ -44,6 +44,7 @@ import org.apache.skywalking.oap.server.core.analysis.metrics.LongValueHolder;
 import org.apache.skywalking.oap.server.core.analysis.metrics.Metrics;
 import org.apache.skywalking.mqe.rt.MQEVisitorBase;
 import org.apache.skywalking.oap.server.core.analysis.metrics.MultiIntValuesHolder;
+import org.apache.skywalking.oap.server.core.query.enumeration.Step;
 import org.apache.skywalking.oap.server.core.query.type.KeyValue;
 import org.apache.skywalking.oap.server.core.storage.annotation.Column;
 import org.apache.skywalking.oap.server.core.storage.annotation.ValueColumnMetadata;
@@ -59,15 +60,19 @@ public class AlarmMQEVisitor extends MQEVisitorBase {
     private final int windowSize;
     private final LocalDateTime endTime;
     private final ArrayList<String> windowTimes;
+    private final int maxTrendRange;
 
     public AlarmMQEVisitor(final LinkedList<Map<String, Metrics>> metricsValues,
-                           final LocalDateTime endTime) {
+                           final LocalDateTime endTime,
+                           final int maxTrendRange) {
+        super(Step.MINUTE);
         this.metricsValues = metricsValues;
         this.commonValuesMap = new HashMap<>();
         this.labeledValuesMap = new HashMap<>();
         this.endTime = endTime;
         this.windowSize = metricsValues.size();
         this.windowTimes = initWindowTimes();
+        this.maxTrendRange = maxTrendRange;
         this.initMetricsValues();
     }
 
@@ -114,12 +119,38 @@ public class AlarmMQEVisitor extends MQEVisitorBase {
             result.setError("Unsupported value type: " + dataType);
             return result;
         }
-
+        if (!(ctx.parent instanceof MQEParser.TrendOPContext)) {
+            //Trim the redundant data
+            result.getResults().forEach(resultValues -> {
+                List<MQEValue> mqeValues = resultValues.getValues();
+                if (maxTrendRange > 0 && mqeValues.size() > maxTrendRange) {
+                    resultValues.setValues(mqeValues.subList(maxTrendRange, mqeValues.size()));
+                }
+            });
+        }
         result.setResults(mqeValuesList);
         result.setType(ExpressionResultType.TIME_SERIES_VALUES);
         return result;
     }
 
+    @Override
+    public ExpressionResult visitTrendOP(MQEParser.TrendOPContext ctx) {
+        ExpressionResult result = super.visitTrendOP(ctx);
+        int trendRange = Integer.parseInt(ctx.INTEGER().getText());
+        //super.visitTrendOP only trim self trend range, trim more here due to all metrics window size is the same
+        int trimIndex = maxTrendRange - trendRange;
+        if (trimIndex > 0) {
+            //Trim the redundant data
+            result.getResults().forEach(resultValues -> {
+                List<MQEValue> mqeValues = resultValues.getValues();
+                if (mqeValues.size() > trimIndex) {
+                    resultValues.setValues(mqeValues.subList(trimIndex, mqeValues.size()));
+                }
+            });
+        }
+        return result;
+    }
+
     private ArrayList<String> initWindowTimes() {
         ArrayList<String> windowTimes = new ArrayList<>();
         for (int i = this.windowSize - 1; i >= 0; i--) {
diff --git a/oap-server/server-alarm-plugin/src/test/java/org/apache/skywalking/oap/server/core/alarm/provider/AlarmRuleTest.java b/oap-server/server-alarm-plugin/src/test/java/org/apache/skywalking/oap/server/core/alarm/provider/AlarmRuleTest.java
index 64e353587a..5cf7cbcf18 100644
--- a/oap-server/server-alarm-plugin/src/test/java/org/apache/skywalking/oap/server/core/alarm/provider/AlarmRuleTest.java
+++ b/oap-server/server-alarm-plugin/src/test/java/org/apache/skywalking/oap/server/core/alarm/provider/AlarmRuleTest.java
@@ -113,5 +113,8 @@ public class AlarmRuleTest {
             rule.setExpression("sum(service_percent > endpoint_percent) >= 1");
         }).getMessage().contains("The metrics in expression: sum(service_percent > endpoint_percent) >= 1 must have the same scope level, but got:"));
 
+        //trend expression
+        rule.setExpression("sum((increase(service_percent,5) + increase(service_percent,2)) > 0) >= 1");
+        Assertions.assertEquals(5, rule.getMaxTrendRange());
     }
 }
diff --git a/oap-server/server-alarm-plugin/src/test/java/org/apache/skywalking/oap/server/core/alarm/provider/RunningRuleTest.java b/oap-server/server-alarm-plugin/src/test/java/org/apache/skywalking/oap/server/core/alarm/provider/RunningRuleTest.java
index 60d2932fba..a0f66fc041 100644
--- a/oap-server/server-alarm-plugin/src/test/java/org/apache/skywalking/oap/server/core/alarm/provider/RunningRuleTest.java
+++ b/oap-server/server-alarm-plugin/src/test/java/org/apache/skywalking/oap/server/core/alarm/provider/RunningRuleTest.java
@@ -71,10 +71,11 @@ public class RunningRuleTest {
     @Test
     public void testInitAndStart() throws IllegalExpressionException {
         AlarmRule alarmRule = new AlarmRule();
-        alarmRule.setAlarmRuleName("endpoint_percent_rule");
-        alarmRule.setExpression("sum(endpoint_percent < 75) >= 3");
+        alarmRule.setAlarmRuleName("mix_rule");
+        alarmRule.setExpression("sum((increase(endpoint_cpm,5) + increase(endpoint_percent,2)) > 0) >= 1");
         alarmRule.getIncludeMetrics().add("endpoint_percent");
-        alarmRule.setPeriod(15);
+        alarmRule.getIncludeMetrics().add("endpoint_cpm");
+        alarmRule.setPeriod(10);
         alarmRule.setTags(new HashMap<String, String>() {{
             put("key", "value");
         }});
@@ -90,11 +91,11 @@ public class RunningRuleTest {
 
         RunningRule.Window window = windows.get(getAlarmEntity(123));
         LocalDateTime endTime = Whitebox.getInternalState(window, "endTime");
-        int period = Whitebox.getInternalState(window, "period");
+        int additionalPeriod = Whitebox.getInternalState(window, "additionalPeriod");
         LinkedList<Metrics> metricsBuffer = Whitebox.getInternalState(window, "values");
 
         Assertions.assertTrue(targetTime.equals(endTime.toDateTime()));
-        Assertions.assertEquals(15, period);
+        Assertions.assertEquals(5, additionalPeriod);
         Assertions.assertEquals(15, metricsBuffer.size());
     }
 
diff --git a/oap-server/server-query-plugin/promql-plugin/src/main/java/org/apache/skywalking/oap/query/promql/handler/PromQLApiHandler.java b/oap-server/server-query-plugin/promql-plugin/src/main/java/org/apache/skywalking/oap/query/promql/handler/PromQLApiHandler.java
index e3195eb31e..519f142e00 100644
--- a/oap-server/server-query-plugin/promql-plugin/src/main/java/org/apache/skywalking/oap/query/promql/handler/PromQLApiHandler.java
+++ b/oap-server/server-query-plugin/promql-plugin/src/main/java/org/apache/skywalking/oap/query/promql/handler/PromQLApiHandler.java
@@ -29,7 +29,7 @@ import com.linecorp.armeria.server.annotation.Get;
 import com.linecorp.armeria.server.annotation.Param;
 import com.linecorp.armeria.server.annotation.Path;
 import com.linecorp.armeria.server.annotation.Post;
-import graphql.org.antlr.v4.runtime.misc.ParseCancellationException;
+import org.antlr.v4.runtime.misc.ParseCancellationException;
 import java.io.IOException;
 import java.util.ArrayList;
 import java.util.Arrays;
diff --git a/oap-server/server-query-plugin/promql-plugin/src/main/java/org/apache/skywalking/oap/query/promql/rt/exception/ParseErrorListener.java b/oap-server/server-query-plugin/promql-plugin/src/main/java/org/apache/skywalking/oap/query/promql/rt/exception/ParseErrorListener.java
index e54481c458..769cb0735d 100644
--- a/oap-server/server-query-plugin/promql-plugin/src/main/java/org/apache/skywalking/oap/query/promql/rt/exception/ParseErrorListener.java
+++ b/oap-server/server-query-plugin/promql-plugin/src/main/java/org/apache/skywalking/oap/query/promql/rt/exception/ParseErrorListener.java
@@ -18,7 +18,7 @@
 
 package org.apache.skywalking.oap.query.promql.rt.exception;
 
-import graphql.org.antlr.v4.runtime.misc.ParseCancellationException;
+import org.antlr.v4.runtime.misc.ParseCancellationException;
 import org.antlr.v4.runtime.BaseErrorListener;
 import org.antlr.v4.runtime.RecognitionException;
 import org.antlr.v4.runtime.Recognizer;
diff --git a/oap-server/server-query-plugin/query-graphql-plugin/src/main/java/org/apache/skywalking/oap/query/graphql/mqe/rt/MQEVisitor.java b/oap-server/server-query-plugin/query-graphql-plugin/src/main/java/org/apache/skywalking/oap/query/graphql/mqe/rt/MQEVisitor.java
index 9f112429f6..9b36208bc3 100644
--- a/oap-server/server-query-plugin/query-graphql-plugin/src/main/java/org/apache/skywalking/oap/query/graphql/mqe/rt/MQEVisitor.java
+++ b/oap-server/server-query-plugin/query-graphql-plugin/src/main/java/org/apache/skywalking/oap/query/graphql/mqe/rt/MQEVisitor.java
@@ -52,6 +52,7 @@ import org.apache.skywalking.oap.server.library.util.StringUtil;
 import org.apache.skywalking.mqe.rt.MQEVisitorBase;
 import org.apache.skywalking.mqe.rt.type.ExpressionResult;
 import org.apache.skywalking.mqe.rt.type.ExpressionResultType;
+import org.joda.time.DateTime;
 
 @Slf4j
 public class MQEVisitor extends MQEVisitorBase {
@@ -64,6 +65,7 @@ public class MQEVisitor extends MQEVisitorBase {
                       final RecordsQuery recordsQuery,
                       final Entity entity,
                       final Duration duration) {
+        super(duration.getStep());
         this.metricsQuery = metricsQuery;
         this.recordsQuery = recordsQuery;
         this.entity = entity;
@@ -87,14 +89,27 @@ public class MQEVisitor extends MQEVisitorBase {
             if (Column.ValueDataType.COMMON_VALUE == dataType) {
                 if (ctx.parent instanceof MQEParser.TopNOPContext) {
                     MQEParser.TopNOPContext parent = (MQEParser.TopNOPContext) ctx.parent;
-                    querySortMetrics(metricName, Integer.parseInt(parent.parameter().getText()),
+                    int topN = Integer.parseInt(parent.INTEGER().getText());
+                    if (topN <= 0) {
+                        throw new IllegalExpressionException("TopN value must be > 0.");
+                    }
+                    querySortMetrics(metricName, Integer.parseInt(parent.INTEGER().getText()),
                                      Order.valueOf(parent.order().getText().toUpperCase()), result
                     );
+                } else if (ctx.parent instanceof MQEParser.TrendOPContext) {
+                    //trend query requires get previous data according to the trend range
+                    MQEParser.TrendOPContext parent = (MQEParser.TrendOPContext) ctx.parent;
+                    int trendRange = Integer.parseInt(parent.INTEGER().getText());
+                    queryMetrics(metricName, getTrendQueryDuration(trendRange), result);
                 } else {
-                    queryMetrics(metricName, result);
+                    queryMetrics(metricName, this.duration, result);
                 }
             } else if (Column.ValueDataType.LABELED_VALUE == dataType) {
                 List<String> queryLabelList = Collections.emptyList();
+                if (ctx.parent instanceof MQEParser.TopNOPContext) {
+                    throw new IllegalExpressionException(
+                        "Metric: [" + metricName + "] is labeled value, dose not support top_n query.");
+                }
                 if (ctx.label() != null) {
                     String labelValue = ctx.label().labelValue().getText();
                     String labelValueTrim = labelValue.substring(1, labelValue.length() - 1);
@@ -102,11 +117,21 @@ public class MQEVisitor extends MQEVisitorBase {
                         queryLabelList = Arrays.asList(labelValueTrim.split(Const.COMMA));
                     }
                 }
-                queryLabeledMetrics(metricName, queryLabelList, result);
+                if (ctx.parent instanceof MQEParser.TrendOPContext) {
+                    MQEParser.TrendOPContext parent = (MQEParser.TrendOPContext) ctx.parent;
+                    int trendRange = Integer.parseInt(parent.INTEGER().getText());
+                    queryLabeledMetrics(metricName, queryLabelList, getTrendQueryDuration(trendRange), result);
+                } else {
+                    queryLabeledMetrics(metricName, queryLabelList, this.duration, result);
+                }
             } else if (Column.ValueDataType.SAMPLED_RECORD == dataType) {
                 if (ctx.parent instanceof MQEParser.TopNOPContext) {
                     MQEParser.TopNOPContext parent = (MQEParser.TopNOPContext) ctx.parent;
-                    queryRecords(metricName, Integer.parseInt(parent.parameter().getText()),
+                    int topN = Integer.parseInt(parent.INTEGER().getText());
+                    if (topN <= 0) {
+                        throw new IllegalExpressionException("TopN value must be > 0.");
+                    }
+                    queryRecords(metricName, Integer.parseInt(parent.INTEGER().getText()),
                                  Order.valueOf(parent.order().getText().toUpperCase()), result
                     );
                 } else {
@@ -182,15 +207,15 @@ public class MQEVisitor extends MQEVisitorBase {
         result.setType(ExpressionResultType.RECORD_LIST);
     }
 
-    private void queryMetrics(String metricName, ExpressionResult result) throws IOException {
+    private void queryMetrics(String metricName, Duration queryDuration, ExpressionResult result) throws IOException {
         MetricsCondition metricsCondition = new MetricsCondition();
         metricsCondition.setName(metricName);
         metricsCondition.setEntity(entity);
-        MetricsValues metricsValues = metricsQuery.readMetricsValues(metricsCondition, duration);
-        List<PointOfTime> times = duration.assembleDurationPoints();
+        MetricsValues metricsValues = metricsQuery.readMetricsValues(metricsCondition, queryDuration);
+        List<PointOfTime> times = queryDuration.assembleDurationPoints();
         List<MQEValue> mqeValueList = new ArrayList<>(times.size());
         for (int i = 0; i < times.size(); i++) {
-            long retTimestamp = DurationUtils.INSTANCE.parseToDateTime(duration.getStep(), times.get(i).getPoint())
+            long retTimestamp = DurationUtils.INSTANCE.parseToDateTime(queryDuration.getStep(), times.get(i).getPoint())
                                                       .getMillis();
             KVInt kvInt = metricsValues.getValues().getValues().get(i);
             MQEValue mqeValue = new MQEValue();
@@ -209,17 +234,18 @@ public class MQEVisitor extends MQEVisitorBase {
 
     private void queryLabeledMetrics(String metricName,
                                      List<String> queryLabelList,
+                                     Duration queryDuration,
                                      ExpressionResult result) throws IOException {
         MetricsCondition metricsCondition = new MetricsCondition();
         metricsCondition.setName(metricName);
         metricsCondition.setEntity(entity);
         List<MetricsValues> metricsValuesList = metricsQuery.readLabeledMetricsValues(
-            metricsCondition, queryLabelList, duration);
-        List<PointOfTime> times = duration.assembleDurationPoints();
+            metricsCondition, queryLabelList, queryDuration);
+        List<PointOfTime> times = queryDuration.assembleDurationPoints();
         metricsValuesList.forEach(metricsValues -> {
             List<MQEValue> mqeValueList = new ArrayList<>(times.size());
             for (int i = 0; i < times.size(); i++) {
-                long retTimestamp = DurationUtils.INSTANCE.parseToDateTime(duration.getStep(), times.get(i).getPoint())
+                long retTimestamp = DurationUtils.INSTANCE.parseToDateTime(queryDuration.getStep(), times.get(i).getPoint())
                                                           .getMillis();
                 KVInt kvInt = metricsValues.getValues().getValues().get(i);
                 MQEValue mqeValue = new MQEValue();
@@ -242,4 +268,29 @@ public class MQEVisitor extends MQEVisitorBase {
         result.setType(ExpressionResultType.TIME_SERIES_VALUES);
         result.setLabeledResult(true);
     }
+
+    private Duration getTrendQueryDuration(int stepRange) {
+        Duration duration = new Duration();
+        duration.setStep(this.duration.getStep());
+        duration.setEnd(this.duration.getEnd());
+        DateTime startDT = new DateTime(this.duration.getStartTimestamp());
+
+        switch (duration.getStep()) {
+            case DAY:
+                duration.setStart(startDT.minusDays(stepRange).toString(DurationUtils.YYYY_MM_DD));
+                break;
+            case HOUR:
+                duration.setStart(startDT.minusHours(stepRange).toString(DurationUtils.YYYY_MM_DD_HH));
+                break;
+            case MINUTE:
+                duration.setStart(startDT.minusMinutes(stepRange).toString(DurationUtils.YYYY_MM_DD_HHMM));
+                break;
+            case SECOND:
+                duration.setStart(startDT.minusSeconds(stepRange).toString(DurationUtils.YYYY_MM_DD_HHMMSS));
+                break;
+            default:
+                throw new IllegalArgumentException("Unsupported query step: " + duration.getStep());
+        }
+        return duration;
+    }
 }
diff --git a/oap-server/server-query-plugin/query-graphql-plugin/src/main/java/org/apache/skywalking/oap/query/graphql/resolver/MetricsExpressionQuery.java b/oap-server/server-query-plugin/query-graphql-plugin/src/main/java/org/apache/skywalking/oap/query/graphql/resolver/MetricsExpressionQuery.java
index e10c71465e..89cb999dfe 100644
--- a/oap-server/server-query-plugin/query-graphql-plugin/src/main/java/org/apache/skywalking/oap/query/graphql/resolver/MetricsExpressionQuery.java
+++ b/oap-server/server-query-plugin/query-graphql-plugin/src/main/java/org/apache/skywalking/oap/query/graphql/resolver/MetricsExpressionQuery.java
@@ -19,7 +19,7 @@
 package org.apache.skywalking.oap.query.graphql.resolver;
 
 import graphql.kickstart.tools.GraphQLQueryResolver;
-import graphql.org.antlr.v4.runtime.misc.ParseCancellationException;
+import org.antlr.v4.runtime.misc.ParseCancellationException;
 import java.text.DecimalFormat;
 import org.antlr.v4.runtime.CharStreams;
 import org.antlr.v4.runtime.CommonTokenStream;
diff --git a/test/e2e-v2/cases/alarm/alarm-settings.yml b/test/e2e-v2/cases/alarm/alarm-settings.yml
index 40df6f0a75..abcc8c130a 100755
--- a/test/e2e-v2/cases/alarm/alarm-settings.yml
+++ b/test/e2e-v2/cases/alarm/alarm-settings.yml
@@ -16,10 +16,10 @@
 rules:
   # service response time > 10ms
   service_resp_time_rule:
-    expression: sum(service_resp_time > 10) >= 1
+    expression: sum(abs(increase(service_resp_time,1)) > 0) >= 1
     period: 10
     silence-period: 1
-    message: Response time of service {name} is more than 10ms in 1 minutes of last 10 minutes.
+    message: Response time of service {name} is increase/decrease in 1 minutes of last 10 minutes.
     tags:
       level: WARNING
       receivers: lisi
diff --git a/test/e2e-v2/cases/alarm/es/e2e.yaml b/test/e2e-v2/cases/alarm/es/e2e.yaml
index e0ad564928..4dcb90bf0f 100644
--- a/test/e2e-v2/cases/alarm/es/e2e.yaml
+++ b/test/e2e-v2/cases/alarm/es/e2e.yaml
@@ -31,7 +31,7 @@ setup:
 trigger:
   action: http
   interval: 3s
-  times: 10
+  times: 30
   url: http://${provider_host}:${provider_9090}/users
   method: POST
   body: '{"id":"123","name":"skywalking"}'
diff --git a/test/e2e-v2/cases/alarm/es/es-sharding/e2e.yaml b/test/e2e-v2/cases/alarm/es/es-sharding/e2e.yaml
index 24e01c0812..a1fa0e36b9 100644
--- a/test/e2e-v2/cases/alarm/es/es-sharding/e2e.yaml
+++ b/test/e2e-v2/cases/alarm/es/es-sharding/e2e.yaml
@@ -31,7 +31,7 @@ setup:
 trigger:
   action: http
   interval: 3s
-  times: 10
+  times: 30
   url: http://${provider_host}:${provider_9090}/users
   method: POST
   body: '{"id":"123","name":"skywalking"}'
diff --git a/test/e2e-v2/cases/alarm/expected/silence-after-graphql-warn.yml b/test/e2e-v2/cases/alarm/expected/silence-after-graphql-warn.yml
index 641150f9da..c3f71b81ce 100644
--- a/test/e2e-v2/cases/alarm/expected/silence-after-graphql-warn.yml
+++ b/test/e2e-v2/cases/alarm/expected/silence-after-graphql-warn.yml
@@ -42,7 +42,7 @@ msgs:
   - starttime: {{ gt .starttime 0 }}
     scope: Service
     id: ZTJlLXNlcnZpY2UtcHJvdmlkZXI=.1
-    message: Response time of service e2e-service-provider is more than 10ms in 1 minutes of last 10 minutes.
+    message: Response time of service e2e-service-provider is increase/decrease in 1 minutes of last 10 minutes.
     tags:
       - key: level
         value: WARNING
@@ -90,7 +90,7 @@ msgs:
   - starttime: {{ gt .starttime 0 }}
     scope: Service
     id: ZTJlLXNlcnZpY2UtcHJvdmlkZXI=.1
-    message: Response time of service e2e-service-provider is more than 10ms in 1 minutes of last 10 minutes.
+    message: Response time of service e2e-service-provider is increase/decrease in 1 minutes of last 10 minutes.
     tags:
       - key: level
         value: WARNING
diff --git a/test/e2e-v2/cases/alarm/expected/silence-after-webhook.yml b/test/e2e-v2/cases/alarm/expected/silence-after-webhook.yml
index f1d0f0355d..3e9ad6d25b 100644
--- a/test/e2e-v2/cases/alarm/expected/silence-after-webhook.yml
+++ b/test/e2e-v2/cases/alarm/expected/silence-after-webhook.yml
@@ -21,7 +21,7 @@ messages:
     id0: ZTJlLXNlcnZpY2UtcHJvdmlkZXI=.1
     id1: ""
     ruleName: service_resp_time_rule
-    alarmMessage: Response time of service e2e-service-provider is more than 10ms in 1 minutes of last 10 minutes.
+    alarmMessage: Response time of service e2e-service-provider is increase/decrease in 1 minutes of last 10 minutes.
     startTime: {{ gt .startTime 0 }}
     tags:
       - key: level
@@ -47,7 +47,7 @@ messages:
     id0: ZTJlLXNlcnZpY2UtcHJvdmlkZXI=.1
     id1: ""
     ruleName: service_resp_time_rule
-    alarmMessage: Response time of service e2e-service-provider is more than 10ms in 1 minutes of last 10 minutes.
+    alarmMessage: Response time of service e2e-service-provider is increase/decrease in 1 minutes of last 10 minutes.
     startTime: {{ gt .startTime 0 }}
     tags:
       - key: level
diff --git a/test/e2e-v2/cases/alarm/expected/silence-before-graphql-warn.yml b/test/e2e-v2/cases/alarm/expected/silence-before-graphql-warn.yml
index e8d5c1ca88..a766327f30 100644
--- a/test/e2e-v2/cases/alarm/expected/silence-before-graphql-warn.yml
+++ b/test/e2e-v2/cases/alarm/expected/silence-before-graphql-warn.yml
@@ -42,7 +42,7 @@ msgs:
   - starttime: {{ gt .starttime 0 }}
     scope: Service
     id: ZTJlLXNlcnZpY2UtcHJvdmlkZXI=.1
-    message: Response time of service e2e-service-provider is more than 10ms in 1 minutes of last 10 minutes.
+    message: Response time of service e2e-service-provider is increase/decrease in 1 minutes of last 10 minutes.
     tags:
       - key: level
         value: WARNING
diff --git a/test/e2e-v2/cases/alarm/expected/silence-before-webhook.yml b/test/e2e-v2/cases/alarm/expected/silence-before-webhook.yml
index c076fd0c0b..d34f856f28 100644
--- a/test/e2e-v2/cases/alarm/expected/silence-before-webhook.yml
+++ b/test/e2e-v2/cases/alarm/expected/silence-before-webhook.yml
@@ -21,7 +21,7 @@ messages:
     id0: ZTJlLXNlcnZpY2UtcHJvdmlkZXI=.1
     id1: ""
     ruleName: service_resp_time_rule
-    alarmMessage: Response time of service e2e-service-provider is more than 10ms in 1 minutes of last 10 minutes.
+    alarmMessage: Response time of service e2e-service-provider is increase/decrease in 1 minutes of last 10 minutes.
     startTime: {{ gt .startTime 0 }}
     tags:
       - key: level
diff --git a/test/e2e-v2/cases/alarm/h2/e2e.yaml b/test/e2e-v2/cases/alarm/h2/e2e.yaml
index e0ad564928..4dcb90bf0f 100644
--- a/test/e2e-v2/cases/alarm/h2/e2e.yaml
+++ b/test/e2e-v2/cases/alarm/h2/e2e.yaml
@@ -31,7 +31,7 @@ setup:
 trigger:
   action: http
   interval: 3s
-  times: 10
+  times: 30
   url: http://${provider_host}:${provider_9090}/users
   method: POST
   body: '{"id":"123","name":"skywalking"}'
diff --git a/test/e2e-v2/cases/alarm/mysql/e2e.yaml b/test/e2e-v2/cases/alarm/mysql/e2e.yaml
index e0ad564928..4dcb90bf0f 100644
--- a/test/e2e-v2/cases/alarm/mysql/e2e.yaml
+++ b/test/e2e-v2/cases/alarm/mysql/e2e.yaml
@@ -31,7 +31,7 @@ setup:
 trigger:
   action: http
   interval: 3s
-  times: 10
+  times: 30
   url: http://${provider_host}:${provider_9090}/users
   method: POST
   body: '{"id":"123","name":"skywalking"}'
diff --git a/test/e2e-v2/cases/alarm/postgres/e2e.yaml b/test/e2e-v2/cases/alarm/postgres/e2e.yaml
index e0ad564928..4dcb90bf0f 100644
--- a/test/e2e-v2/cases/alarm/postgres/e2e.yaml
+++ b/test/e2e-v2/cases/alarm/postgres/e2e.yaml
@@ -31,7 +31,7 @@ setup:
 trigger:
   action: http
   interval: 3s
-  times: 10
+  times: 30
   url: http://${provider_host}:${provider_9090}/users
   method: POST
   body: '{"id":"123","name":"skywalking"}'
diff --git a/test/e2e-v2/cases/mqe/expected/trend-OP.yml b/test/e2e-v2/cases/mqe/expected/trend-OP.yml
new file mode 100644
index 0000000000..a4474f13e8
--- /dev/null
+++ b/test/e2e-v2/cases/mqe/expected/trend-OP.yml
@@ -0,0 +1,31 @@
+# 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.
+
+type: TIME_SERIES_VALUES
+results:
+{{- contains .results }}
+  - metric:
+      labels: []
+    values:
+      {{- contains .values }}
+      - id: {{ notEmpty .id }}
+        value: {{ .value }}
+        traceid: null
+      - id: {{ notEmpty .id }}
+        value: null
+        traceid: null
+      {{- end}}
+{{- end}}
+error: null
diff --git a/test/e2e-v2/cases/mqe/mqe-cases.yaml b/test/e2e-v2/cases/mqe/mqe-cases.yaml
index d857cfbf37..c9cfe7af77 100644
--- a/test/e2e-v2/cases/mqe/mqe-cases.yaml
+++ b/test/e2e-v2/cases/mqe/mqe-cases.yaml
@@ -67,3 +67,9 @@ cases:
   # viewAsSeq-OP
   - query: swctl --display yaml --base-url=http://${oap_host}:${oap_12800}/graphql metrics exec --expression="view_as_seq(mq_service_consume_sla,service_sla)" --service-name=e2e-service-provider
     expected: expected/viewAsSeq-OP.yml
+
+  # trend-OP
+  - query: swctl --display yaml --base-url=http://${oap_host}:${oap_12800}/graphql metrics exec --expression="increase(service_resp_time,2)" --service-name=e2e-service-provider
+    expected: expected/trend-OP.yml
+  - query: swctl --display yaml --base-url=http://${oap_host}:${oap_12800}/graphql metrics exec --expression="rate(service_resp_time,2)" --service-name=e2e-service-provider
+    expected: expected/trend-OP.yml