Repository: apache/skywalking (language: python)

Issue:
UUID生成是否有性能隐患？
UUID.randomUUID，有时候会过慢，可能出现意想不到的锁。原因来自SecureRadom，等待机器产生新的噪音（如机器上某个文件发生变化）才能生成所技术。



Discussion / hints:
- uuid生成时间: 199254509ms

```
    final AtomicLong count = new AtomicLong(0);
        List<Thread> allThreads = new ArrayList<Thread>();
        for (int k = 0; k < 200; k++) {
            allThreads.add(new Thread(new Runnable() {
                @Override
                public void run() {
                    Long before = System.currentTimeMillis();
                    for (int i = 0; i < 1000000; i++) {
                        UUID.randomUUID().toString().replaceAll("-", "");
                    }
                    count.addAndGet(System.currentTimeMillis() - before);
                }
            }));
        }
        for (Thread t : allThreads) {
            t.start();
        }
        for (Thread t : allThreads) {
            try {
                t.join();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }

        System.out.println("uuid: " + count.get() + "ms");
```
- logic rule id生成时间: 3257836ms

```
        allThreads = new ArrayList<Thread>();
        final AtomicLong count2 = new AtomicLong(0);
        for (int k = 0; k < 200; k++) {
            allThreads.add(new Thread(new Runnable() {
                @Override
                public void run() {
                    Long before = System.currentTimeMillis();
                    String id = System.currentTimeMillis()
                            + BuriedPointMachineUtil.getHostDesc()
                            + BuriedPointMachineUtil.getProcessNo()
                            + Thread.currentThread().getId();
                    int j = 0;
                    for (int i = 0; i < 1000000; i++) {
                        id = System.currentTimeMillis()
                                + BuriedPointMachineUtil.getHostDesc()
                                + BuriedPointMachineUtil.getProcessNo()
                                + Thread.currentThread().getId() + j++;
                    }
                    count2.addAndGet(System.currentTimeMillis() - before);
                }
            }));
        }

        for (Thread t : allThreads) {
            t.start();
        }
        for (Thread t : allThreads) {
            try {
                t.join();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        System.out.println("logic rule id: " + count2.get() + "ms");
```

建议traced的生成规则如下：
当前时间 + 线程号 + 线程自增序号（4位，int自增到9999则归零）+ 进程级全局UUID（或后6-10位）

使用全新的算法，速度提高30倍。
新生成规则为：
当前时间（毫秒）+ 全局UUID（后7位）+ 进程号 + 线程号 + 当前线程内序号（0-10000内循环）


Fix patch:
diff --git a/docs/en/changes/changes.md b/docs/en/changes/changes.md
index 394b99dc044f..75cc4cfb5cd2 100644
--- a/docs/en/changes/changes.md
+++ b/docs/en/changes/changes.md
@@ -22,6 +22,7 @@
 * Support handler eBPF access log protocol.
 * Fix SumPerMinFunctionTest error function.
 * Remove unnecessary annotations and functions from Meter Functions.
+* Add `max` and `min` functions for MAL down sampling.
 
 #### UI
 
diff --git a/docs/en/concepts-and-designs/mal.md b/docs/en/concepts-and-designs/mal.md
index e51aba0fc648..05da076fb8cb 100644
--- a/docs/en/concepts-and-designs/mal.md
+++ b/docs/en/concepts-and-designs/mal.md
@@ -226,8 +226,8 @@ Down sampling function is called `downsampling` in MAL, and it accepts the follo
  - SUM
  - LATEST
  - SUM_PER_MIN
- - MIN (TODO)
- - MAX (TODO)
+ - MIN
+ - MAX
  - MEAN (TODO)
  - COUNT (TODO)
 
diff --git a/oap-server/analyzer/meter-analyzer/src/main/java/org/apache/skywalking/oap/meter/analyzer/dsl/DownsamplingType.java b/oap-server/analyzer/meter-analyzer/src/main/java/org/apache/skywalking/oap/meter/analyzer/dsl/DownsamplingType.java
index 7372c548e7b3..c8f0006fbb52 100644
--- a/oap-server/analyzer/meter-analyzer/src/main/java/org/apache/skywalking/oap/meter/analyzer/dsl/DownsamplingType.java
+++ b/oap-server/analyzer/meter-analyzer/src/main/java/org/apache/skywalking/oap/meter/analyzer/dsl/DownsamplingType.java
@@ -22,5 +22,5 @@
  * DownsamplingType indicates the downsampling type of meter function
  */
 public enum DownsamplingType {
-    AVG, SUM, LATEST, SUM_PER_MIN
+    AVG, SUM, LATEST, SUM_PER_MIN, MAX, MIN
 }
diff --git a/oap-server/analyzer/meter-analyzer/src/main/java/org/apache/skywalking/oap/meter/analyzer/dsl/Expression.java b/oap-server/analyzer/meter-analyzer/src/main/java/org/apache/skywalking/oap/meter/analyzer/dsl/Expression.java
index 6a7146e72a49..02912adb05f0 100644
--- a/oap-server/analyzer/meter-analyzer/src/main/java/org/apache/skywalking/oap/meter/analyzer/dsl/Expression.java
+++ b/oap-server/analyzer/meter-analyzer/src/main/java/org/apache/skywalking/oap/meter/analyzer/dsl/Expression.java
@@ -117,6 +117,8 @@ private static class ExpressionDelegate extends GroovyObjectSupport {
         public static final DownsamplingType SUM = DownsamplingType.SUM;
         public static final DownsamplingType LATEST = DownsamplingType.LATEST;
         public static final DownsamplingType SUM_PER_MIN = DownsamplingType.SUM_PER_MIN;
+        public static final DownsamplingType MAX = DownsamplingType.MAX;
+        public static final DownsamplingType MIN = DownsamplingType.MIN;
 
         private final String metricName;
         private final String literal;
diff --git a/oap-server/server-core/src/main/java/org/apache/skywalking/oap/server/core/analysis/meter/function/max/MaxFunction.java b/oap-server/server-core/src/main/java/org/apache/skywalking/oap/server/core/analysis/meter/function/max/MaxFunction.java
new file mode 100644
index 000000000000..b4546e9a3b67
--- /dev/null
+++ b/oap-server/server-core/src/main/java/org/apache/skywalking/oap/server/core/analysis/meter/function/max/MaxFunction.java
@@ -0,0 +1,190 @@
+/*
+ * 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.oap.server.core.analysis.meter.function.max;
+
+import lombok.Getter;
+import lombok.Setter;
+import lombok.ToString;
+import org.apache.skywalking.oap.server.core.UnexpectedException;
+import org.apache.skywalking.oap.server.core.analysis.manual.instance.InstanceTraffic;
+import org.apache.skywalking.oap.server.core.analysis.meter.Meter;
+import org.apache.skywalking.oap.server.core.analysis.meter.MeterEntity;
+import org.apache.skywalking.oap.server.core.analysis.meter.function.AcceptableValue;
+import org.apache.skywalking.oap.server.core.analysis.meter.function.MeterFunction;
+import org.apache.skywalking.oap.server.core.analysis.metrics.LongValueHolder;
+import org.apache.skywalking.oap.server.core.analysis.metrics.Metrics;
+import org.apache.skywalking.oap.server.core.query.sql.Function;
+import org.apache.skywalking.oap.server.core.remote.grpc.proto.RemoteData;
+import org.apache.skywalking.oap.server.core.storage.StorageID;
+import org.apache.skywalking.oap.server.core.storage.annotation.BanyanDB;
+import org.apache.skywalking.oap.server.core.storage.annotation.Column;
+import org.apache.skywalking.oap.server.core.storage.type.Convert2Entity;
+import org.apache.skywalking.oap.server.core.storage.type.Convert2Storage;
+import org.apache.skywalking.oap.server.core.storage.type.StorageBuilder;
+
+import java.util.Objects;
+
+@MeterFunction(functionName = "max")
+@ToString
+public abstract class MaxFunction extends Meter implements AcceptableValue<Long>, LongValueHolder {
+
+    public static final String VALUE = "value";
+
+    @Setter
+    @Getter
+    @Column(name = ENTITY_ID, length = 512)
+    @BanyanDB.SeriesID(index = 0)
+    private String entityId;
+
+    /**
+     * Service ID is required for sort query.
+     */
+    @Setter
+    @Getter
+    @Column(name = InstanceTraffic.SERVICE_ID)
+    private String serviceId;
+
+    @Getter
+    @Setter
+    @Column(name = VALUE, dataType = Column.ValueDataType.COMMON_VALUE, function = Function.MAX)
+    @BanyanDB.MeasureField
+    private long value;
+
+    @Override
+    public void accept(MeterEntity entity, Long value) {
+        setEntityId(entity.id());
+        setServiceId(entity.serviceId());
+        if (this.value < value) {
+            setValue(value);
+        }
+    }
+
+    @Override
+    public final boolean combine(Metrics metrics) {
+        final MaxFunction maxFunction = (MaxFunction) metrics;
+        if (this.value < maxFunction.getValue()) {
+            setValue(value);
+        }
+        return true;
+    }
+
+    @Override
+    public void calculate() {
+
+    }
+
+    @Override
+    public Metrics toHour() {
+        MaxFunction metrics = (MaxFunction) createNew();
+        metrics.setEntityId(getEntityId());
+        metrics.setTimeBucket(toTimeBucketInHour());
+        metrics.setServiceId(getServiceId());
+        metrics.setValue(getValue());
+        return metrics;
+    }
+
+    @Override
+    public Metrics toDay() {
+        MaxFunction metrics = (MaxFunction) createNew();
+        metrics.setEntityId(getEntityId());
+        metrics.setTimeBucket(toTimeBucketInDay());
+        metrics.setServiceId(getServiceId());
+        metrics.setValue(getValue());
+        return metrics;
+    }
+
+    @Override
+    public int remoteHashCode() {
+        return getEntityId().hashCode();
+    }
+
+    @Override
+    public void deserialize(RemoteData remoteData) {
+        setEntityId(remoteData.getDataStrings(0));
+        setServiceId(remoteData.getDataStrings(1));
+        setTimeBucket(remoteData.getDataLongs(1));
+        setValue(remoteData.getDataLongs(0));
+    }
+
+    @Override
+    public RemoteData.Builder serialize() {
+        final RemoteData.Builder remoteBuilder = RemoteData.newBuilder();
+        remoteBuilder.addDataLongs(getValue());
+        remoteBuilder.addDataLongs(getTimeBucket());
+        remoteBuilder.addDataStrings(getEntityId());
+        remoteBuilder.addDataStrings(getServiceId());
+        return remoteBuilder;
+    }
+
+    @Override
+    protected StorageID id0() {
+        return new StorageID()
+                .append(TIME_BUCKET, getTimeBucket())
+                .append(ENTITY_ID, getEntityId());
+    }
+
+    @Override
+    public Class<? extends StorageBuilder> builder() {
+        return MaxStorageBuilder.class;
+    }
+
+    public static class MaxStorageBuilder implements StorageBuilder<MaxFunction> {
+
+        @Override
+        public MaxFunction storage2Entity(Convert2Entity converter) {
+            MaxFunction metrics = new MaxFunction() {
+                @Override
+                public AcceptableValue<Long> createNew() {
+                    throw new UnexpectedException("createNew should not be called");
+                }
+            };
+            metrics.setEntityId((String) converter.get(ENTITY_ID));
+            metrics.setServiceId((String) converter.get(InstanceTraffic.SERVICE_ID));
+            metrics.setTimeBucket(((Number) converter.get(TIME_BUCKET)).longValue());
+            metrics.setValue(((Number) converter.get(VALUE)).longValue());
+            return metrics;
+        }
+
+        @Override
+        public void entity2Storage(final MaxFunction storageData, final Convert2Storage converter) {
+            converter.accept(ENTITY_ID, storageData.getEntityId());
+            converter.accept(InstanceTraffic.SERVICE_ID, storageData.getServiceId());
+            converter.accept(TIME_BUCKET, storageData.getTimeBucket());
+            converter.accept(VALUE, storageData.getValue());
+        }
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) {
+            return true;
+        }
+        if (!(o instanceof MaxFunction)) {
+            return false;
+        }
+        final MaxFunction function = (MaxFunction) o;
+        return Objects.equals(getEntityId(), function.getEntityId())
+                && Objects.equals(getTimeBucket(), function.getTimeBucket());
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(getEntityId(), getTimeBucket());
+    }
+}
diff --git a/oap-server/server-core/src/main/java/org/apache/skywalking/oap/server/core/analysis/meter/function/max/MaxLabeledFunction.java b/oap-server/server-core/src/main/java/org/apache/skywalking/oap/server/core/analysis/meter/function/max/MaxLabeledFunction.java
new file mode 100644
index 000000000000..7524da8a0cc4
--- /dev/null
+++ b/oap-server/server-core/src/main/java/org/apache/skywalking/oap/server/core/analysis/meter/function/max/MaxLabeledFunction.java
@@ -0,0 +1,188 @@
+/*
+ * 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.oap.server.core.analysis.meter.function.max;
+
+import lombok.Getter;
+import lombok.Setter;
+import lombok.ToString;
+import org.apache.skywalking.oap.server.core.UnexpectedException;
+import org.apache.skywalking.oap.server.core.analysis.manual.instance.InstanceTraffic;
+import org.apache.skywalking.oap.server.core.analysis.meter.Meter;
+import org.apache.skywalking.oap.server.core.analysis.meter.MeterEntity;
+import org.apache.skywalking.oap.server.core.analysis.meter.function.AcceptableValue;
+import org.apache.skywalking.oap.server.core.analysis.meter.function.MeterFunction;
+import org.apache.skywalking.oap.server.core.analysis.metrics.DataTable;
+import org.apache.skywalking.oap.server.core.analysis.metrics.LabeledValueHolder;
+import org.apache.skywalking.oap.server.core.analysis.metrics.Metrics;
+import org.apache.skywalking.oap.server.core.remote.grpc.proto.RemoteData;
+import org.apache.skywalking.oap.server.core.storage.StorageID;
+import org.apache.skywalking.oap.server.core.storage.annotation.BanyanDB;
+import org.apache.skywalking.oap.server.core.storage.annotation.Column;
+import org.apache.skywalking.oap.server.core.storage.type.Convert2Entity;
+import org.apache.skywalking.oap.server.core.storage.type.Convert2Storage;
+import org.apache.skywalking.oap.server.core.storage.type.StorageBuilder;
+
+import java.util.Objects;
+
+@MeterFunction(functionName = "maxLabeled")
+@ToString
+public abstract class MaxLabeledFunction extends Meter implements AcceptableValue<DataTable>, LabeledValueHolder {
+
+    public static final String VALUE = "datatable_value";
+
+    @Setter
+    @Getter
+    @Column(name = ENTITY_ID, length = 512)
+    @BanyanDB.SeriesID(index = 0)
+    private String entityId;
+
+    /**
+     * Service ID is required for sort query.
+     */
+    @Setter
+    @Getter
+    @Column(name = InstanceTraffic.SERVICE_ID)
+    private String serviceId;
+
+    @Getter
+    @Setter
+    @Column(name = VALUE, dataType = Column.ValueDataType.LABELED_VALUE, storageOnly = true)
+    @BanyanDB.MeasureField
+    private DataTable value = new DataTable(30);
+
+    @Override
+    public void accept(final MeterEntity entity, final DataTable value) {
+        setEntityId(entity.id());
+        setServiceId(entity.serviceId());
+        this.value.setMaxValue(value);
+    }
+
+    @Override
+    public final boolean combine(Metrics metrics) {
+        final MaxLabeledFunction maxLabeledFunction = (MaxLabeledFunction) metrics;
+        this.value.setMaxValue(maxLabeledFunction.getValue());
+        return true;
+    }
+
+    @Override
+    public final void calculate() {
+
+    }
+
+    @Override
+    public Metrics toHour() {
+        MaxLabeledFunction metrics = (MaxLabeledFunction) createNew();
+        metrics.setEntityId(getEntityId());
+        metrics.setTimeBucket(toTimeBucketInHour());
+        metrics.setServiceId(getServiceId());
+        metrics.getValue().copyFrom(getValue());
+        return metrics;
+    }
+
+    @Override
+    public Metrics toDay() {
+        MaxLabeledFunction metrics = (MaxLabeledFunction) createNew();
+        metrics.setEntityId(getEntityId());
+        metrics.setTimeBucket(toTimeBucketInDay());
+        metrics.setServiceId(getServiceId());
+        metrics.getValue().copyFrom(getValue());
+        return metrics;
+    }
+
+    @Override
+    protected StorageID id0() {
+        return new StorageID()
+                .append(TIME_BUCKET, getTimeBucket())
+                .append(ENTITY_ID, getEntityId());
+    }
+
+    @Override
+    public void deserialize(final RemoteData remoteData) {
+        setValue(new DataTable(remoteData.getDataObjectStrings(0)));
+        setTimeBucket(remoteData.getDataLongs(0));
+
+        setEntityId(remoteData.getDataStrings(0));
+        setServiceId(remoteData.getDataStrings(1));
+    }
+
+    @Override
+    public RemoteData.Builder serialize() {
+        final RemoteData.Builder remoteBuilder = RemoteData.newBuilder();
+        remoteBuilder.addDataObjectStrings(value.toStorageData());
+        remoteBuilder.addDataLongs(getTimeBucket());
+
+        remoteBuilder.addDataStrings(entityId);
+        remoteBuilder.addDataStrings(serviceId);
+
+        return remoteBuilder;
+    }
+
+    @Override
+    public int remoteHashCode() {
+        return entityId.hashCode();
+    }
+
+    @Override
+    public Class<? extends MaxLabeledStorageBuilder> builder() {
+        return MaxLabeledStorageBuilder.class;
+    }
+
+    public static class MaxLabeledStorageBuilder implements StorageBuilder<MaxLabeledFunction> {
+        @Override
+        public MaxLabeledFunction storage2Entity(final Convert2Entity converter) {
+            MaxLabeledFunction metrics = new MaxLabeledFunction() {
+                @Override
+                public AcceptableValue<DataTable> createNew() {
+                    throw new UnexpectedException("createNew should not be called");
+                }
+            };
+            metrics.setValue(new DataTable((String) converter.get(VALUE)));
+            metrics.setTimeBucket(((Number) converter.get(TIME_BUCKET)).longValue());
+            metrics.setServiceId((String) converter.get(InstanceTraffic.SERVICE_ID));
+            metrics.setEntityId((String) converter.get(ENTITY_ID));
+            return metrics;
+        }
+
+        @Override
+        public void entity2Storage(final MaxLabeledFunction storageData, final Convert2Storage converter) {
+            converter.accept(VALUE, storageData.getValue());
+            converter.accept(TIME_BUCKET, storageData.getTimeBucket());
+            converter.accept(InstanceTraffic.SERVICE_ID, storageData.getServiceId());
+            converter.accept(ENTITY_ID, storageData.getEntityId());
+        }
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) {
+            return true;
+        }
+        if (!(o instanceof MaxLabeledFunction)) {
+            return false;
+        }
+        MaxLabeledFunction function = (MaxLabeledFunction) o;
+        return Objects.equals(entityId, function.entityId) &&
+                getTimeBucket() == function.getTimeBucket();
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(entityId, getTimeBucket());
+    }
+}
diff --git a/oap-server/server-core/src/main/java/org/apache/skywalking/oap/server/core/analysis/meter/function/min/MinFunction.java b/oap-server/server-core/src/main/java/org/apache/skywalking/oap/server/core/analysis/meter/function/min/MinFunction.java
new file mode 100644
index 000000000000..369c39756921
--- /dev/null
+++ b/oap-server/server-core/src/main/java/org/apache/skywalking/oap/server/core/analysis/meter/function/min/MinFunction.java
@@ -0,0 +1,191 @@
+/*
+ * 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.oap.server.core.analysis.meter.function.min;
+
+import lombok.Getter;
+import lombok.Setter;
+import lombok.ToString;
+import org.apache.skywalking.oap.server.core.UnexpectedException;
+import org.apache.skywalking.oap.server.core.analysis.manual.instance.InstanceTraffic;
+import org.apache.skywalking.oap.server.core.analysis.meter.Meter;
+import org.apache.skywalking.oap.server.core.analysis.meter.MeterEntity;
+import org.apache.skywalking.oap.server.core.analysis.meter.function.AcceptableValue;
+import org.apache.skywalking.oap.server.core.analysis.meter.function.MeterFunction;
+import org.apache.skywalking.oap.server.core.analysis.metrics.LongValueHolder;
+import org.apache.skywalking.oap.server.core.analysis.metrics.Metrics;
+import org.apache.skywalking.oap.server.core.query.sql.Function;
+import org.apache.skywalking.oap.server.core.remote.grpc.proto.RemoteData;
+import org.apache.skywalking.oap.server.core.storage.StorageID;
+import org.apache.skywalking.oap.server.core.storage.annotation.BanyanDB;
+import org.apache.skywalking.oap.server.core.storage.annotation.Column;
+import org.apache.skywalking.oap.server.core.storage.type.Convert2Entity;
+import org.apache.skywalking.oap.server.core.storage.type.Convert2Storage;
+import org.apache.skywalking.oap.server.core.storage.type.StorageBuilder;
+
+import java.util.Objects;
+
+@MeterFunction(functionName = "min")
+@ToString
+public abstract class MinFunction extends Meter implements AcceptableValue<Long>, LongValueHolder {
+
+    public static final String VALUE = "value";
+
+    @Setter
+    @Getter
+    @Column(name = ENTITY_ID, length = 512)
+    @BanyanDB.SeriesID(index = 0)
+    private String entityId;
+
+    /**
+     * Service ID is required for sort query.
+     */
+    @Setter
+    @Getter
+    @Column(name = InstanceTraffic.SERVICE_ID)
+    private String serviceId;
+
+    @Getter
+    @Setter
+    @Column(name = VALUE, dataType = Column.ValueDataType.COMMON_VALUE, function = Function.MIN)
+    @BanyanDB.MeasureField
+    private long value = Long.MAX_VALUE;
+
+    @Override
+    public void accept(MeterEntity entity, Long value) {
+        setEntityId(entity.id());
+        setServiceId(entity.serviceId());
+
+        if (this.value > value) {
+            setValue(value);
+        }
+    }
+
+    @Override
+    public final boolean combine(Metrics metrics) {
+        final MinFunction minFunction = (MinFunction) metrics;
+        if (this.value > minFunction.getValue()) {
+            setValue(value);
+        }
+        return true;
+    }
+
+    @Override
+    public void calculate() {
+
+    }
+
+    @Override
+    public Metrics toHour() {
+        MinFunction metrics = (MinFunction) createNew();
+        metrics.setEntityId(getEntityId());
+        metrics.setTimeBucket(toTimeBucketInHour());
+        metrics.setServiceId(getServiceId());
+        metrics.setValue(getValue());
+        return metrics;
+    }
+
+    @Override
+    public Metrics toDay() {
+        MinFunction metrics = (MinFunction) createNew();
+        metrics.setEntityId(getEntityId());
+        metrics.setTimeBucket(toTimeBucketInDay());
+        metrics.setServiceId(getServiceId());
+        metrics.setValue(getValue());
+        return metrics;
+    }
+
+    @Override
+    public int remoteHashCode() {
+        return getEntityId().hashCode();
+    }
+
+    @Override
+    public void deserialize(RemoteData remoteData) {
+        setEntityId(remoteData.getDataStrings(0));
+        setServiceId(remoteData.getDataStrings(1));
+        setTimeBucket(remoteData.getDataLongs(1));
+        setValue(remoteData.getDataLongs(0));
+    }
+
+    @Override
+    public RemoteData.Builder serialize() {
+        final RemoteData.Builder remoteBuilder = RemoteData.newBuilder();
+        remoteBuilder.addDataLongs(getValue());
+        remoteBuilder.addDataLongs(getTimeBucket());
+        remoteBuilder.addDataStrings(getEntityId());
+        remoteBuilder.addDataStrings(getServiceId());
+        return remoteBuilder;
+    }
+
+    @Override
+    protected StorageID id0() {
+        return new StorageID()
+                .append(TIME_BUCKET, getTimeBucket())
+                .append(ENTITY_ID, getEntityId());
+    }
+
+    @Override
+    public Class<? extends StorageBuilder> builder() {
+        return MinStorageBuilder.class;
+    }
+
+    public static class MinStorageBuilder implements StorageBuilder<MinFunction> {
+
+        @Override
+        public MinFunction storage2Entity(Convert2Entity converter) {
+            MinFunction metrics = new MinFunction() {
+                @Override
+                public AcceptableValue<Long> createNew() {
+                    throw new UnexpectedException("createNew should not be called");
+                }
+            };
+            metrics.setEntityId((String) converter.get(ENTITY_ID));
+            metrics.setServiceId((String) converter.get(InstanceTraffic.SERVICE_ID));
+            metrics.setTimeBucket(((Number) converter.get(TIME_BUCKET)).longValue());
+            metrics.setValue(((Number) converter.get(VALUE)).longValue());
+            return metrics;
+        }
+
+        @Override
+        public void entity2Storage(final MinFunction storageData, final Convert2Storage converter) {
+            converter.accept(ENTITY_ID, storageData.getEntityId());
+            converter.accept(InstanceTraffic.SERVICE_ID, storageData.getServiceId());
+            converter.accept(TIME_BUCKET, storageData.getTimeBucket());
+            converter.accept(VALUE, storageData.getValue());
+        }
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) {
+            return true;
+        }
+        if (!(o instanceof MinFunction)) {
+            return false;
+        }
+        final MinFunction function = (MinFunction) o;
+        return Objects.equals(getEntityId(), function.getEntityId())
+                && Objects.equals(getTimeBucket(), function.getTimeBucket());
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(getEntityId(), getTimeBucket());
+    }
+}
diff --git a/oap-server/server-core/src/main/java/org/apache/skywalking/oap/server/core/analysis/meter/function/min/MinLabeledFunction.java b/oap-server/server-core/src/main/java/org/apache/skywalking/oap/server/core/analysis/meter/function/min/MinLabeledFunction.java
new file mode 100644
index 000000000000..dde993b1ff00
--- /dev/null
+++ b/oap-server/server-core/src/main/java/org/apache/skywalking/oap/server/core/analysis/meter/function/min/MinLabeledFunction.java
@@ -0,0 +1,188 @@
+/*
+ * 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.oap.server.core.analysis.meter.function.min;
+
+import lombok.Getter;
+import lombok.Setter;
+import lombok.ToString;
+import org.apache.skywalking.oap.server.core.UnexpectedException;
+import org.apache.skywalking.oap.server.core.analysis.manual.instance.InstanceTraffic;
+import org.apache.skywalking.oap.server.core.analysis.meter.Meter;
+import org.apache.skywalking.oap.server.core.analysis.meter.MeterEntity;
+import org.apache.skywalking.oap.server.core.analysis.meter.function.AcceptableValue;
+import org.apache.skywalking.oap.server.core.analysis.meter.function.MeterFunction;
+import org.apache.skywalking.oap.server.core.analysis.metrics.DataTable;
+import org.apache.skywalking.oap.server.core.analysis.metrics.LabeledValueHolder;
+import org.apache.skywalking.oap.server.core.analysis.metrics.Metrics;
+import org.apache.skywalking.oap.server.core.remote.grpc.proto.RemoteData;
+import org.apache.skywalking.oap.server.core.storage.StorageID;
+import org.apache.skywalking.oap.server.core.storage.annotation.BanyanDB;
+import org.apache.skywalking.oap.server.core.storage.annotation.Column;
+import org.apache.skywalking.oap.server.core.storage.type.Convert2Entity;
+import org.apache.skywalking.oap.server.core.storage.type.Convert2Storage;
+import org.apache.skywalking.oap.server.core.storage.type.StorageBuilder;
+
+import java.util.Objects;
+
+@MeterFunction(functionName = "minLabeled")
+@ToString
+public abstract class MinLabeledFunction extends Meter implements AcceptableValue<DataTable>, LabeledValueHolder {
+
+    public static final String VALUE = "datatable_value";
+
+    @Setter
+    @Getter
+    @Column(name = ENTITY_ID, length = 512)
+    @BanyanDB.SeriesID(index = 0)
+    private String entityId;
+
+    /**
+     * Service ID is required for sort query.
+     */
+    @Setter
+    @Getter
+    @Column(name = InstanceTraffic.SERVICE_ID)
+    private String serviceId;
+
+    @Getter
+    @Setter
+    @Column(name = VALUE, dataType = Column.ValueDataType.LABELED_VALUE, storageOnly = true)
+    @BanyanDB.MeasureField
+    private DataTable value = new DataTable(30);
+
+    @Override
+    public void accept(final MeterEntity entity, final DataTable value) {
+        setEntityId(entity.id());
+        setServiceId(entity.serviceId());
+        this.value.setMinValue(value);
+    }
+
+    @Override
+    public final boolean combine(Metrics metrics) {
+        final MinLabeledFunction minLabeledFunction = (MinLabeledFunction) metrics;
+        this.value.setMinValue(minLabeledFunction.getValue());
+        return true;
+    }
+
+    @Override
+    public final void calculate() {
+
+    }
+
+    @Override
+    public Metrics toHour() {
+        MinLabeledFunction metrics = (MinLabeledFunction) createNew();
+        metrics.setEntityId(getEntityId());
+        metrics.setTimeBucket(toTimeBucketInHour());
+        metrics.setServiceId(getServiceId());
+        metrics.getValue().copyFrom(getValue());
+        return metrics;
+    }
+
+    @Override
+    public Metrics toDay() {
+        MinLabeledFunction metrics = (MinLabeledFunction) createNew();
+        metrics.setEntityId(getEntityId());
+        metrics.setTimeBucket(toTimeBucketInDay());
+        metrics.setServiceId(getServiceId());
+        metrics.getValue().copyFrom(getValue());
+        return metrics;
+    }
+
+    @Override
+    protected StorageID id0() {
+        return new StorageID()
+                .append(TIME_BUCKET, getTimeBucket())
+                .append(ENTITY_ID, getEntityId());
+    }
+
+    @Override
+    public void deserialize(final RemoteData remoteData) {
+        setValue(new DataTable(remoteData.getDataObjectStrings(0)));
+        setTimeBucket(remoteData.getDataLongs(0));
+
+        setEntityId(remoteData.getDataStrings(0));
+        setServiceId(remoteData.getDataStrings(1));
+    }
+
+    @Override
+    public RemoteData.Builder serialize() {
+        final RemoteData.Builder remoteBuilder = RemoteData.newBuilder();
+        remoteBuilder.addDataObjectStrings(value.toStorageData());
+        remoteBuilder.addDataLongs(getTimeBucket());
+
+        remoteBuilder.addDataStrings(entityId);
+        remoteBuilder.addDataStrings(serviceId);
+
+        return remoteBuilder;
+    }
+
+    @Override
+    public int remoteHashCode() {
+        return entityId.hashCode();
+    }
+
+    @Override
+    public Class<? extends MinLabeledStorageBuilder> builder() {
+        return MinLabeledStorageBuilder.class;
+    }
+
+    public static class MinLabeledStorageBuilder implements StorageBuilder<MinLabeledFunction> {
+        @Override
+        public MinLabeledFunction storage2Entity(final Convert2Entity converter) {
+            MinLabeledFunction metrics = new MinLabeledFunction() {
+                @Override
+                public AcceptableValue<DataTable> createNew() {
+                    throw new UnexpectedException("createNew should not be called");
+                }
+            };
+            metrics.setValue(new DataTable((String) converter.get(VALUE)));
+            metrics.setTimeBucket(((Number) converter.get(TIME_BUCKET)).longValue());
+            metrics.setServiceId((String) converter.get(InstanceTraffic.SERVICE_ID));
+            metrics.setEntityId((String) converter.get(ENTITY_ID));
+            return metrics;
+        }
+
+        @Override
+        public void entity2Storage(final MinLabeledFunction storageData, final Convert2Storage converter) {
+            converter.accept(VALUE, storageData.getValue());
+            converter.accept(TIME_BUCKET, storageData.getTimeBucket());
+            converter.accept(InstanceTraffic.SERVICE_ID, storageData.getServiceId());
+            converter.accept(ENTITY_ID, storageData.getEntityId());
+        }
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) {
+            return true;
+        }
+        if (!(o instanceof MinLabeledFunction)) {
+            return false;
+        }
+        MinLabeledFunction function = (MinLabeledFunction) o;
+        return Objects.equals(entityId, function.entityId) &&
+                getTimeBucket() == function.getTimeBucket();
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(entityId, getTimeBucket());
+    }
+}
diff --git a/oap-server/server-core/src/main/java/org/apache/skywalking/oap/server/core/analysis/metrics/DataTable.java b/oap-server/server-core/src/main/java/org/apache/skywalking/oap/server/core/analysis/metrics/DataTable.java
index 0640d68276df..f77be3f00950 100644
--- a/oap-server/server-core/src/main/java/org/apache/skywalking/oap/server/core/analysis/metrics/DataTable.java
+++ b/oap-server/server-core/src/main/java/org/apache/skywalking/oap/server/core/analysis/metrics/DataTable.java
@@ -153,4 +153,34 @@ public DataTable append(DataTable dataTable) {
         });
         return this;
     }
+
+    public DataTable setMaxValue(DataTable dataTable) {
+        dataTable.data.forEach((key, value) -> {
+            Long current = this.data.get(key);
+            if (current == null) {
+                current = value;
+            } else {
+                if (current < value) {
+                    current = value;
+                }
+            }
+            this.data.put(key, current);
+        });
+        return this;
+    }
+
+    public DataTable setMinValue(DataTable dataTable) {
+        dataTable.data.forEach((key, value) -> {
+            Long current = this.data.get(key);
+            if (current == null) {
+                current = value;
+            } else {
+                if (current > value) {
+                    current = value;
+                }
+            }
+            this.data.put(key, current);
+        });
+        return this;
+    }
 }
diff --git a/oap-server/server-core/src/main/java/org/apache/skywalking/oap/server/core/query/sql/Function.java b/oap-server/server-core/src/main/java/org/apache/skywalking/oap/server/core/query/sql/Function.java
index 68c964435598..419e367a1eb7 100644
--- a/oap-server/server-core/src/main/java/org/apache/skywalking/oap/server/core/query/sql/Function.java
+++ b/oap-server/server-core/src/main/java/org/apache/skywalking/oap/server/core/query/sql/Function.java
@@ -19,5 +19,5 @@
 package org.apache.skywalking.oap.server.core.query.sql;
 
 public enum Function {
-    None, Avg, Sum, Latest
+    None, Avg, Sum, Latest, MAX, MIN
 }


Test patch:
diff --git a/oap-server/server-core/src/test/java/org/apache/skywalking/oap/server/core/analysis/meter/function/max/MaxFunctionTest.java b/oap-server/server-core/src/test/java/org/apache/skywalking/oap/server/core/analysis/meter/function/max/MaxFunctionTest.java
new file mode 100644
index 000000000000..8a515683f940
--- /dev/null
+++ b/oap-server/server-core/src/test/java/org/apache/skywalking/oap/server/core/analysis/meter/function/max/MaxFunctionTest.java
@@ -0,0 +1,142 @@
+/*
+ * 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.oap.server.core.analysis.meter.function.max;
+
+import org.apache.skywalking.oap.server.core.analysis.Layer;
+import org.apache.skywalking.oap.server.core.analysis.TimeBucket;
+import org.apache.skywalking.oap.server.core.analysis.meter.MeterEntity;
+import org.apache.skywalking.oap.server.core.analysis.meter.function.AcceptableValue;
+import org.apache.skywalking.oap.server.core.config.NamingControl;
+import org.apache.skywalking.oap.server.core.config.group.EndpointNameGrouping;
+import org.apache.skywalking.oap.server.core.storage.type.HashMapConverter;
+import org.apache.skywalking.oap.server.core.storage.type.StorageBuilder;
+import org.junit.jupiter.api.AfterAll;
+import org.junit.jupiter.api.BeforeAll;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.mockito.junit.jupiter.MockitoExtension;
+
+import java.util.Map;
+
+import static org.assertj.core.api.AssertionsForClassTypes.assertThat;
+
+@ExtendWith(MockitoExtension.class)
+public class MaxFunctionTest {
+
+    private static final Long LARGE_VALUE = 1597113447737L;
+
+    private static final Long SMALL_VALUE = 1597113318673L;
+
+    private MaxFunction function;
+
+    @BeforeAll
+    public static void setup() {
+        MeterEntity.setNamingControl(new NamingControl(512, 512, 512, new EndpointNameGrouping()));
+    }
+
+    @BeforeEach
+    public void before() {
+        function = new MaxFunctionInst();
+        function.setTimeBucket(TimeBucket.getMinuteTimeBucket(System.currentTimeMillis()));
+    }
+
+    @AfterAll
+    public static void tearDown() {
+        MeterEntity.setNamingControl(null);
+    }
+
+    @Test
+    public void testAccept() {
+        function.accept(MeterEntity.newService("service-test", Layer.GENERAL), LARGE_VALUE);
+        assertThat(function.getValue()).isEqualTo(LARGE_VALUE);
+
+        function.accept(MeterEntity.newService("service-test", Layer.GENERAL), SMALL_VALUE);
+        assertThat(function.getValue()).isEqualTo(LARGE_VALUE);
+    }
+
+    @Test
+    public void testCalculate() {
+        function.accept(MeterEntity.newService("service-test", Layer.GENERAL), LARGE_VALUE);
+        function.accept(MeterEntity.newService("service-test", Layer.GENERAL), SMALL_VALUE);
+        function.calculate();
+
+        assertThat(function.getValue()).isEqualTo(LARGE_VALUE);
+    }
+
+    @Test
+    public void testToHour() {
+        function.setTimeBucket(TimeBucket.getMinuteTimeBucket(System.currentTimeMillis()));
+        function.accept(MeterEntity.newService("service-test", Layer.GENERAL), LARGE_VALUE);
+        function.accept(MeterEntity.newService("service-test", Layer.GENERAL), SMALL_VALUE);
+        function.calculate();
+
+        final MaxFunction hourFunction = (MaxFunction) function.toHour();
+        hourFunction.calculate();
+
+        assertThat(hourFunction.getValue()).isEqualTo(LARGE_VALUE);
+    }
+
+    @Test
+    public void testToDay() {
+        function.setTimeBucket(TimeBucket.getMinuteTimeBucket(System.currentTimeMillis()));
+        function.accept(MeterEntity.newService("service-test", Layer.GENERAL), LARGE_VALUE);
+        function.accept(MeterEntity.newService("service-test", Layer.GENERAL), SMALL_VALUE);
+        function.calculate();
+
+        final MaxFunction dayFunction = (MaxFunction) function.toDay();
+        dayFunction.calculate();
+        assertThat(dayFunction.getValue()).isEqualTo(LARGE_VALUE);
+    }
+
+    @Test
+    public void testSerialize() {
+        function.accept(MeterEntity.newService("service-test", Layer.GENERAL), LARGE_VALUE);
+        MaxFunction function2 = new MaxFunctionInst();
+        function2.deserialize(function.serialize().build());
+
+        assertThat(function2.getEntityId()).isEqualTo(function.getEntityId());
+        assertThat(function2.getTimeBucket()).isEqualTo(function.getTimeBucket());
+        assertThat(function2.getServiceId()).isEqualTo(function.getServiceId());
+        assertThat(function2.getValue()).isEqualTo(function.getValue());
+    }
+
+    @Test
+    public void testBuilder() throws IllegalAccessException, InstantiationException {
+        function.accept(MeterEntity.newService("service-test", Layer.GENERAL), LARGE_VALUE);
+        function.calculate();
+        StorageBuilder<MaxFunction> storageBuilder = function.builder().newInstance();
+
+        final HashMapConverter.ToStorage toStorage = new HashMapConverter.ToStorage();
+        storageBuilder.entity2Storage(function, toStorage);
+        final Map<String, Object> map = toStorage.obtain();
+        map.put(MaxFunction.VALUE, map.get(MaxFunction.VALUE));
+
+        MaxFunction function2 = storageBuilder.storage2Entity(new HashMapConverter.ToEntity(map));
+
+        assertThat(function2.getValue()).isEqualTo(function.getValue());
+    }
+
+    private static class MaxFunctionInst extends MaxFunction {
+        @Override
+        public AcceptableValue<Long> createNew() {
+            return new MaxFunctionInst();
+        }
+    }
+}
diff --git a/oap-server/server-core/src/test/java/org/apache/skywalking/oap/server/core/analysis/meter/function/max/MaxLabeledFunctionTest.java b/oap-server/server-core/src/test/java/org/apache/skywalking/oap/server/core/analysis/meter/function/max/MaxLabeledFunctionTest.java
new file mode 100644
index 000000000000..012013688e0c
--- /dev/null
+++ b/oap-server/server-core/src/test/java/org/apache/skywalking/oap/server/core/analysis/meter/function/max/MaxLabeledFunctionTest.java
@@ -0,0 +1,153 @@
+/*
+ * 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.oap.server.core.analysis.meter.function.max;
+
+import org.apache.skywalking.oap.server.core.analysis.Layer;
+import org.apache.skywalking.oap.server.core.analysis.TimeBucket;
+import org.apache.skywalking.oap.server.core.analysis.meter.MeterEntity;
+import org.apache.skywalking.oap.server.core.analysis.meter.function.AcceptableValue;
+import org.apache.skywalking.oap.server.core.analysis.metrics.DataTable;
+import org.apache.skywalking.oap.server.core.config.NamingControl;
+import org.apache.skywalking.oap.server.core.config.group.EndpointNameGrouping;
+import org.apache.skywalking.oap.server.core.storage.type.HashMapConverter;
+import org.apache.skywalking.oap.server.core.storage.type.StorageBuilder;
+import org.junit.jupiter.api.AfterAll;
+import org.junit.jupiter.api.BeforeAll;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.mockito.junit.jupiter.MockitoExtension;
+
+import java.util.Map;
+
+import static org.assertj.core.api.AssertionsForClassTypes.assertThat;
+
+@ExtendWith(MockitoExtension.class)
+public class MaxLabeledFunctionTest {
+
+    private static final DataTable HTTP_CODE_COUNT_1 = new DataTable("200,2|301,2|404,3|502,4");
+
+    private static final DataTable HTTP_CODE_COUNT_2 = new DataTable("200,1|301,4|404,5|502,1|505,1");
+
+    private static final DataTable HTTP_CODE_COUNT_3 = new DataTable("200,2|301,4|404,5|502,4|505,1");
+
+    private MaxLabeledFunction function;
+
+    @BeforeAll
+    public static void setup() {
+        MeterEntity.setNamingControl(
+                new NamingControl(512, 512, 512, new EndpointNameGrouping()));
+    }
+
+    @BeforeEach
+    public void before() {
+        function = new MaxLabeledFunctionInst();
+        function.setTimeBucket(TimeBucket.getMinuteTimeBucket(System.currentTimeMillis()));
+    }
+
+    @AfterAll
+    public static void tearDown() {
+        MeterEntity.setNamingControl(null);
+    }
+
+    @Test
+    public void testAccept() {
+        function.accept(MeterEntity.newService("service-test", Layer.GENERAL), HTTP_CODE_COUNT_1);
+        assertThat(function.getValue()).isEqualTo(HTTP_CODE_COUNT_1);
+
+        function.accept(MeterEntity.newService("service-test", Layer.GENERAL), HTTP_CODE_COUNT_2);
+        assertThat(function.getValue()).isEqualTo(HTTP_CODE_COUNT_3);
+    }
+
+    @Test
+    public void testCalculate() {
+        function.accept(MeterEntity.newService("service-test", Layer.GENERAL), HTTP_CODE_COUNT_1);
+        function.accept(MeterEntity.newService("service-test", Layer.GENERAL), HTTP_CODE_COUNT_2);
+        function.calculate();
+
+        assertThat(function.getValue()).isEqualTo(HTTP_CODE_COUNT_3);
+    }
+
+    @Test
+    public void testToHour() {
+        function.accept(MeterEntity.newService("service-test", Layer.GENERAL), HTTP_CODE_COUNT_1);
+        function.accept(MeterEntity.newService("service-test", Layer.GENERAL), HTTP_CODE_COUNT_2);
+        function.calculate();
+
+        final MaxLabeledFunction hourFunction = (MaxLabeledFunction) function.toHour();
+        hourFunction.calculate();
+
+        assertThat(hourFunction.getValue()).isEqualTo(HTTP_CODE_COUNT_3);
+    }
+
+    @Test
+    public void testToDay() {
+        function.accept(
+                MeterEntity.newService("service-test", Layer.GENERAL),
+                HTTP_CODE_COUNT_1
+        );
+        function.accept(
+                MeterEntity.newService("service-test", Layer.GENERAL),
+                HTTP_CODE_COUNT_2
+        );
+        function.calculate();
+
+        final MaxLabeledFunction dayFunction = (MaxLabeledFunction) function.toDay();
+        dayFunction.calculate();
+
+        assertThat(dayFunction.getValue()).isEqualTo(HTTP_CODE_COUNT_3);
+    }
+
+    @Test
+    public void testSerialize() {
+        function.accept(MeterEntity.newService("service-test", Layer.GENERAL), HTTP_CODE_COUNT_1);
+
+        MaxLabeledFunction function2 = new MaxLabeledFunctionInst();
+        function2.deserialize(function.serialize().build());
+
+        assertThat(function2.getEntityId()).isEqualTo(function.getEntityId());
+        assertThat(function2.getTimeBucket()).isEqualTo(function.getTimeBucket());
+        assertThat(function2.getServiceId()).isEqualTo(function.getServiceId());
+        assertThat(function2.getValue()).isEqualTo(function.getValue());
+    }
+
+    @Test
+    public void testBuilder() throws IllegalAccessException, InstantiationException {
+        function.accept(MeterEntity.newService("service-test", Layer.GENERAL), HTTP_CODE_COUNT_1);
+        function.calculate();
+
+        StorageBuilder<MaxLabeledFunction> storageBuilder = function.builder().newInstance();
+
+        final HashMapConverter.ToStorage toStorage = new HashMapConverter.ToStorage();
+        storageBuilder.entity2Storage(function, toStorage);
+        final Map<String, Object> map = toStorage.obtain();
+        map.put(MaxLabeledFunction.VALUE, ((DataTable) map.get(MaxLabeledFunction.VALUE)).toStorageData());
+
+        MaxLabeledFunction function2 = storageBuilder.storage2Entity(new HashMapConverter.ToEntity(map));
+
+        assertThat(function2.getValue()).isEqualTo(function.getValue());
+    }
+
+    private static class MaxLabeledFunctionInst extends MaxLabeledFunction {
+        @Override
+        public AcceptableValue<DataTable> createNew() {
+            return new MaxLabeledFunctionInst();
+        }
+    }
+}
diff --git a/oap-server/server-core/src/test/java/org/apache/skywalking/oap/server/core/analysis/meter/function/min/MinFunctionTest.java b/oap-server/server-core/src/test/java/org/apache/skywalking/oap/server/core/analysis/meter/function/min/MinFunctionTest.java
new file mode 100644
index 000000000000..47c271144709
--- /dev/null
+++ b/oap-server/server-core/src/test/java/org/apache/skywalking/oap/server/core/analysis/meter/function/min/MinFunctionTest.java
@@ -0,0 +1,143 @@
+/*
+ * 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.oap.server.core.analysis.meter.function.min;
+
+import org.apache.skywalking.oap.server.core.analysis.Layer;
+import org.apache.skywalking.oap.server.core.analysis.TimeBucket;
+import org.apache.skywalking.oap.server.core.analysis.meter.MeterEntity;
+import org.apache.skywalking.oap.server.core.analysis.meter.function.AcceptableValue;
+import org.apache.skywalking.oap.server.core.config.NamingControl;
+import org.apache.skywalking.oap.server.core.config.group.EndpointNameGrouping;
+import org.apache.skywalking.oap.server.core.storage.type.HashMapConverter;
+import org.apache.skywalking.oap.server.core.storage.type.StorageBuilder;
+import org.junit.jupiter.api.AfterAll;
+import org.junit.jupiter.api.BeforeAll;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.mockito.Mockito;
+import org.mockito.junit.jupiter.MockitoExtension;
+
+import java.util.Map;
+
+import static org.assertj.core.api.AssertionsForClassTypes.assertThat;
+
+@ExtendWith(MockitoExtension.class)
+public class MinFunctionTest {
+
+    private static final Long LARGE_VALUE = 1597113447737L;
+
+    private static final Long SMALL_VALUE = 1597113318673L;
+
+    private MinFunction function;
+
+    @BeforeAll
+    public static void setup() {
+        MeterEntity.setNamingControl(new NamingControl(512, 512, 512, new EndpointNameGrouping()));
+    }
+
+    @BeforeEach
+    public void before() {
+        function = new MinFunctionInst();
+        function.setTimeBucket(TimeBucket.getMinuteTimeBucket(System.currentTimeMillis()));
+    }
+
+    @AfterAll
+    public static void tearDown() {
+        MeterEntity.setNamingControl(null);
+    }
+
+    @Test
+    public void testAccept() {
+        function.accept(MeterEntity.newService("service-test", Layer.GENERAL), SMALL_VALUE);
+        assertThat(function.getValue()).isEqualTo(SMALL_VALUE);
+
+        function.accept(MeterEntity.newService("service-test", Layer.GENERAL), LARGE_VALUE);
+        assertThat(function.getValue()).isEqualTo(SMALL_VALUE);
+    }
+
+    @Test
+    public void testCalculate() {
+        function.accept(MeterEntity.newService("service-test", Layer.GENERAL), SMALL_VALUE);
+        function.accept(MeterEntity.newService("service-test", Layer.GENERAL), LARGE_VALUE);
+        function.calculate();
+
+        assertThat(function.getValue()).isEqualTo(SMALL_VALUE);
+    }
+
+    @Test
+    public void testToHour() {
+        function.setTimeBucket(TimeBucket.getMinuteTimeBucket(System.currentTimeMillis()));
+        function.accept(MeterEntity.newService("service-test", Layer.GENERAL), SMALL_VALUE);
+        function.accept(MeterEntity.newService("service-test", Layer.GENERAL), LARGE_VALUE);
+        function.calculate();
+
+        final MinFunction hourFunction = (MinFunction) function.toHour();
+        hourFunction.calculate();
+
+        assertThat(hourFunction.getValue()).isEqualTo(SMALL_VALUE);
+    }
+
+    @Test
+    public void testToDay() {
+        function.setTimeBucket(TimeBucket.getMinuteTimeBucket(System.currentTimeMillis()));
+        function.accept(MeterEntity.newService("service-test", Layer.GENERAL), SMALL_VALUE);
+        function.accept(MeterEntity.newService("service-test", Layer.GENERAL), LARGE_VALUE);
+        function.calculate();
+
+        final MinFunction dayFunction = (MinFunction) function.toDay();
+        dayFunction.calculate();
+        assertThat(dayFunction.getValue()).isEqualTo(SMALL_VALUE);
+    }
+
+    @Test
+    public void testSerialize() {
+        function.accept(MeterEntity.newService("service-test", Layer.GENERAL), SMALL_VALUE);
+        MinFunction function2 = Mockito.spy(MinFunction.class);
+        function2.deserialize(function.serialize().build());
+
+        assertThat(function2.getEntityId()).isEqualTo(function.getEntityId());
+        assertThat(function2.getTimeBucket()).isEqualTo(function.getTimeBucket());
+        assertThat(function2.getServiceId()).isEqualTo(function.getServiceId());
+        assertThat(function2.getValue()).isEqualTo(function.getValue());
+    }
+
+    @Test
+    public void testBuilder() throws IllegalAccessException, InstantiationException {
+        function.accept(MeterEntity.newService("service-test", Layer.GENERAL), SMALL_VALUE);
+        function.calculate();
+        StorageBuilder<MinFunction> storageBuilder = function.builder().newInstance();
+
+        final HashMapConverter.ToStorage toStorage = new HashMapConverter.ToStorage();
+        storageBuilder.entity2Storage(function, toStorage);
+        final Map<String, Object> map = toStorage.obtain();
+        map.put(MinFunction.VALUE, map.get(MinFunction.VALUE));
+
+        MinFunction function2 = storageBuilder.storage2Entity(new HashMapConverter.ToEntity(map));
+
+        assertThat(function2.getValue()).isEqualTo(function.getValue());
+    }
+
+    private static class MinFunctionInst extends MinFunction {
+        @Override
+        public AcceptableValue<Long> createNew() {
+            return new MinFunctionInst();
+        }
+    }
+}
diff --git a/oap-server/server-core/src/test/java/org/apache/skywalking/oap/server/core/analysis/meter/function/min/MinLabeledFunctionTest.java b/oap-server/server-core/src/test/java/org/apache/skywalking/oap/server/core/analysis/meter/function/min/MinLabeledFunctionTest.java
new file mode 100644
index 000000000000..7005529f21c7
--- /dev/null
+++ b/oap-server/server-core/src/test/java/org/apache/skywalking/oap/server/core/analysis/meter/function/min/MinLabeledFunctionTest.java
@@ -0,0 +1,153 @@
+/*
+ * 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.oap.server.core.analysis.meter.function.min;
+
+import org.apache.skywalking.oap.server.core.analysis.Layer;
+import org.apache.skywalking.oap.server.core.analysis.TimeBucket;
+import org.apache.skywalking.oap.server.core.analysis.meter.MeterEntity;
+import org.apache.skywalking.oap.server.core.analysis.meter.function.AcceptableValue;
+import org.apache.skywalking.oap.server.core.analysis.metrics.DataTable;
+import org.apache.skywalking.oap.server.core.config.NamingControl;
+import org.apache.skywalking.oap.server.core.config.group.EndpointNameGrouping;
+import org.apache.skywalking.oap.server.core.storage.type.HashMapConverter;
+import org.apache.skywalking.oap.server.core.storage.type.StorageBuilder;
+import org.junit.jupiter.api.AfterAll;
+import org.junit.jupiter.api.BeforeAll;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.mockito.junit.jupiter.MockitoExtension;
+
+import java.util.Map;
+
+import static org.assertj.core.api.AssertionsForClassTypes.assertThat;
+
+@ExtendWith(MockitoExtension.class)
+public class MinLabeledFunctionTest {
+
+    private static final DataTable HTTP_CODE_COUNT_1 = new DataTable("200,2|301,2|404,3|502,4");
+
+    private static final DataTable HTTP_CODE_COUNT_2 = new DataTable("200,1|301,4|404,5|502,1|505,1");
+
+    private static final DataTable HTTP_CODE_COUNT_3 = new DataTable("200,1|301,2|404,3|502,1|505,1");
+
+    private MinLabeledFunction function;
+
+    @BeforeAll
+    public static void setup() {
+        MeterEntity.setNamingControl(
+                new NamingControl(512, 512, 512, new EndpointNameGrouping()));
+    }
+
+    @BeforeEach
+    public void before() {
+        function = new MinLabeledFunctionInst();
+        function.setTimeBucket(TimeBucket.getMinuteTimeBucket(System.currentTimeMillis()));
+    }
+
+    @AfterAll
+    public static void tearDown() {
+        MeterEntity.setNamingControl(null);
+    }
+
+    @Test
+    public void testAccept() {
+        function.accept(MeterEntity.newService("service-test", Layer.GENERAL), HTTP_CODE_COUNT_1);
+        assertThat(function.getValue()).isEqualTo(HTTP_CODE_COUNT_1);
+
+        function.accept(MeterEntity.newService("service-test", Layer.GENERAL), HTTP_CODE_COUNT_2);
+        assertThat(function.getValue()).isEqualTo(HTTP_CODE_COUNT_3);
+    }
+
+    @Test
+    public void testCalculate() {
+        function.accept(MeterEntity.newService("service-test", Layer.GENERAL), HTTP_CODE_COUNT_1);
+        function.accept(MeterEntity.newService("service-test", Layer.GENERAL), HTTP_CODE_COUNT_2);
+        function.calculate();
+
+        assertThat(function.getValue()).isEqualTo(HTTP_CODE_COUNT_3);
+    }
+
+    @Test
+    public void testToHour() {
+        function.accept(MeterEntity.newService("service-test", Layer.GENERAL), HTTP_CODE_COUNT_1);
+        function.accept(MeterEntity.newService("service-test", Layer.GENERAL), HTTP_CODE_COUNT_2);
+        function.calculate();
+
+        final MinLabeledFunction hourFunction = (MinLabeledFunction) function.toHour();
+        hourFunction.calculate();
+
+        assertThat(hourFunction.getValue()).isEqualTo(HTTP_CODE_COUNT_3);
+    }
+
+    @Test
+    public void testToDay() {
+        function.accept(
+                MeterEntity.newService("service-test", Layer.GENERAL),
+                HTTP_CODE_COUNT_1
+        );
+        function.accept(
+                MeterEntity.newService("service-test", Layer.GENERAL),
+                HTTP_CODE_COUNT_2
+        );
+        function.calculate();
+
+        final MinLabeledFunction dayFunction = (MinLabeledFunction) function.toDay();
+        dayFunction.calculate();
+
+        assertThat(dayFunction.getValue()).isEqualTo(HTTP_CODE_COUNT_3);
+    }
+
+    @Test
+    public void testSerialize() {
+        function.accept(MeterEntity.newService("service-test", Layer.GENERAL), HTTP_CODE_COUNT_1);
+
+        MinLabeledFunction function2 = new MinLabeledFunctionInst();
+        function2.deserialize(function.serialize().build());
+
+        assertThat(function2.getEntityId()).isEqualTo(function.getEntityId());
+        assertThat(function2.getTimeBucket()).isEqualTo(function.getTimeBucket());
+        assertThat(function2.getServiceId()).isEqualTo(function.getServiceId());
+        assertThat(function2.getValue()).isEqualTo(function.getValue());
+    }
+
+    @Test
+    public void testBuilder() throws IllegalAccessException, InstantiationException {
+        function.accept(MeterEntity.newService("service-test", Layer.GENERAL), HTTP_CODE_COUNT_1);
+        function.calculate();
+
+        StorageBuilder<MinLabeledFunction> storageBuilder = function.builder().newInstance();
+
+        final HashMapConverter.ToStorage toStorage = new HashMapConverter.ToStorage();
+        storageBuilder.entity2Storage(function, toStorage);
+        final Map<String, Object> map = toStorage.obtain();
+        map.put(MinLabeledFunction.VALUE, ((DataTable) map.get(MinLabeledFunction.VALUE)).toStorageData());
+
+        MinLabeledFunction function2 = storageBuilder.storage2Entity(new HashMapConverter.ToEntity(map));
+
+        assertThat(function2.getValue()).isEqualTo(function.getValue());
+    }
+
+    private static class MinLabeledFunctionInst extends MinLabeledFunction {
+        @Override
+        public AcceptableValue<DataTable> createNew() {
+            return new MinLabeledFunctionInst();
+        }
+    }
+}