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 2022/04/15 09:35:24 UTC
[skywalking] branch master updated: Update UI, enable compression, fix SO11Y bug and set `SW_QUERY_MAX_QUERY_COMPLEXITY` to 1000 (#8885)
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 85ce1645be Update UI, enable compression, fix SO11Y bug and set `SW_QUERY_MAX_QUERY_COMPLEXITY` to 1000 (#8885)
85ce1645be is described below
commit 85ce1645be53e46286f36c0ea206c60db2d1a716
Author: Wan Kai <wa...@foxmail.com>
AuthorDate: Fri Apr 15 17:35:10 2022 +0800
Update UI, enable compression, fix SO11Y bug and set `SW_QUERY_MAX_QUERY_COMPLEXITY` to 1000 (#8885)
---
apm-webapp/src/main/resources/application.yml | 3 +-
docs/en/changes/changes.md | 3 ++
docs/en/setup/backend/configuration-vocabulary.md | 2 +-
.../oap/query/graphql/GraphQLQueryConfig.java | 2 +-
.../src/main/resources/application.yml | 2 +-
.../main/resources/fetcher-prom-rules/self.yaml | 4 +--
.../src/main/resources/otel-oc-rules/oap.yaml | 4 +--
.../ui-initialized-templates/k8s/k8s-cluster.json | 13 +++++--
.../ui-initialized-templates/k8s/k8s-root.json | 42 ++++++++++++++++++----
.../k8s_service/k8s-service-root.json | 27 ++++++++++++--
.../ui-initialized-templates/mesh/mesh-root.json | 2 +-
.../mesh/mesh-service.json | 4 +--
.../mesh_cp/mesh-control-plane-root.json | 37 +++++++++++++++++--
.../mesh_dp/mesh-data-plane-service.json | 24 +++++++++++--
.../os_linux/linux-root.json | 11 +++---
.../so11y_oap/so11y-instance.json | 2 +-
.../so11y_oap/so11y-service.json | 14 ++++++--
.../virtual_database/virtual-database-root.json | 19 ++++++++--
skywalking-ui | 2 +-
19 files changed, 176 insertions(+), 41 deletions(-)
diff --git a/apm-webapp/src/main/resources/application.yml b/apm-webapp/src/main/resources/application.yml
index c4b0b86a0e..12bb6d9de9 100755
--- a/apm-webapp/src/main/resources/application.yml
+++ b/apm-webapp/src/main/resources/application.yml
@@ -16,7 +16,8 @@
server:
port: 8080
-
+ compression:
+ enabled: true
spring:
cloud:
gateway:
diff --git a/docs/en/changes/changes.md b/docs/en/changes/changes.md
index 3282d00696..8454c2a1ee 100644
--- a/docs/en/changes/changes.md
+++ b/docs/en/changes/changes.md
@@ -16,6 +16,9 @@
* [Breaking Change] Update the eBPF Profiling task to the service level,
please delete index/table: `ebpf_profiling_task`, `process_traffic`.
* Fix event can't split service ID into 2 parts.
+* Fix OAP Self-Observability metric `GC Time` calculation.
+* Set `SW_QUERY_MAX_QUERY_COMPLEXITY` default value to `1000`
+* Webapp module (for UI) enabled compression.
#### UI
diff --git a/docs/en/setup/backend/configuration-vocabulary.md b/docs/en/setup/backend/configuration-vocabulary.md
index 1b54ab8fcc..eb6c00a9fd 100644
--- a/docs/en/setup/backend/configuration-vocabulary.md
+++ b/docs/en/setup/backend/configuration-vocabulary.md
@@ -231,7 +231,7 @@ core|default|role|Option values: `Mixed/Receiver/Aggregator`. **Receiver** mode
| - | - | sampleRate | Sampling rate for receiving trace. Precise to 1/10000. 10000 means sampling rate of 100% by default. | SW_RECEIVER_BROWSER_SAMPLE_RATE | 10000 |
| query | graphql | - | GraphQL query implementation. | - |
| - | - | enableLogTestTool | Enable the log testing API to test the LAL. **NOTE**: This API evaluates untrusted code on the OAP server. A malicious script can do significant damage (steal keys and secrets, remove files and directories, install malware, etc). As such, please enable this API only when you completely trust your users. | SW_QUERY_GRAPHQL_ENABLE_LOG_TEST_TOOL | false |
-| - | - | maxQueryComplexity | Maximum complexity allowed for the GraphQL query that can be used to abort a query if the total number of data fields queried exceeds the defined threshold. | SW_QUERY_MAX_QUERY_COMPLEXITY | 100 |
+| - | - | maxQueryComplexity | Maximum complexity allowed for the GraphQL query that can be used to abort a query if the total number of data fields queried exceeds the defined threshold. | SW_QUERY_MAX_QUERY_COMPLEXITY | 1000 |
| - | - | enableUpdateUITemplate | Allow user add,disable and update UI template. | SW_ENABLE_UPDATE_UI_TEMPLATE | false |
| alarm | default | - | Read [alarm doc](backend-alarm.md) for more details. | - |
| telemetry | - | - | Read [telemetry doc](backend-telemetry.md) for more details. | - |
diff --git a/oap-server/server-query-plugin/query-graphql-plugin/src/main/java/org/apache/skywalking/oap/query/graphql/GraphQLQueryConfig.java b/oap-server/server-query-plugin/query-graphql-plugin/src/main/java/org/apache/skywalking/oap/query/graphql/GraphQLQueryConfig.java
index 9ae7f728f4..db1c4a30b6 100644
--- a/oap-server/server-query-plugin/query-graphql-plugin/src/main/java/org/apache/skywalking/oap/query/graphql/GraphQLQueryConfig.java
+++ b/oap-server/server-query-plugin/query-graphql-plugin/src/main/java/org/apache/skywalking/oap/query/graphql/GraphQLQueryConfig.java
@@ -29,6 +29,6 @@ import org.apache.skywalking.oap.server.library.module.ModuleConfig;
@Setter
public class GraphQLQueryConfig extends ModuleConfig {
private boolean enableLogTestTool;
- private int maxQueryComplexity = 100;
+ private int maxQueryComplexity = 1000;
private boolean enableUpdateUITemplate = false;
}
diff --git a/oap-server/server-starter/src/main/resources/application.yml b/oap-server/server-starter/src/main/resources/application.yml
index 0047fba2c6..1e61ea3c94 100755
--- a/oap-server/server-starter/src/main/resources/application.yml
+++ b/oap-server/server-starter/src/main/resources/application.yml
@@ -413,7 +413,7 @@ query:
enableLogTestTool: ${SW_QUERY_GRAPHQL_ENABLE_LOG_TEST_TOOL:false}
# Maximum complexity allowed for the GraphQL query that can be used to
# abort a query if the total number of data fields queried exceeds the defined threshold.
- maxQueryComplexity: ${SW_QUERY_MAX_QUERY_COMPLEXITY:100}
+ maxQueryComplexity: ${SW_QUERY_MAX_QUERY_COMPLEXITY:1000}
# Allow user add, disable and update UI template
enableUpdateUITemplate: ${SW_ENABLE_UPDATE_UI_TEMPLATE:false}
diff --git a/oap-server/server-starter/src/main/resources/fetcher-prom-rules/self.yaml b/oap-server/server-starter/src/main/resources/fetcher-prom-rules/self.yaml
index 985b56d468..ea85506a56 100644
--- a/oap-server/server-starter/src/main/resources/fetcher-prom-rules/self.yaml
+++ b/oap-server/server-starter/src/main/resources/fetcher-prom-rules/self.yaml
@@ -51,9 +51,9 @@ metricsRules:
.tag({tags -> if (tags['gc'] == 'PS Scavenge' || tags['gc'] == 'Copy' || tags['gc'] == 'ParNew' || tags['gc'] == 'G1 Young Generation') {tags.gc = 'young_gc_count'} })
.tag({tags -> if (tags['gc'] == 'PS MarkSweep' || tags['gc'] == 'MarkSweepCompact' || tags['gc'] == 'ConcurrentMarkSweep' || tags['gc'] == 'G1 Old Generation') {tags.gc = 'old_gc_count'} })"
- name: instance_jvm_young_gc_time
- exp: jvm_gc_collection_seconds_sum.tagMatch('gc', 'PS Scavenge|Copy|ParNew|G1 Young Generation').sum(['service', 'instance']) * 1000
+ exp: jvm_gc_collection_seconds_sum.tagMatch('gc', 'PS Scavenge|Copy|ParNew|G1 Young Generation').sum(['service', 'instance']).increase('PT1M') * 1000
- name: instance_jvm_old_gc_time
- exp: jvm_gc_collection_seconds_sum.tagMatch('gc', 'PS MarkSweep|MarkSweepCompact|ConcurrentMarkSweep|G1 Old Generation').sum(['service', 'instance']) * 1000
+ exp: jvm_gc_collection_seconds_sum.tagMatch('gc', 'PS MarkSweep|MarkSweepCompact|ConcurrentMarkSweep|G1 Old Generation').sum(['service', 'instance']).increase('PT1M') * 1000
- name: instance_trace_count
exp: trace_in_latency_count.sum(['service', 'instance']).increase('PT1M')
- name: instance_trace_latency_percentile
diff --git a/oap-server/server-starter/src/main/resources/otel-oc-rules/oap.yaml b/oap-server/server-starter/src/main/resources/otel-oc-rules/oap.yaml
index ec547b1ac6..16697d0844 100644
--- a/oap-server/server-starter/src/main/resources/otel-oc-rules/oap.yaml
+++ b/oap-server/server-starter/src/main/resources/otel-oc-rules/oap.yaml
@@ -42,9 +42,9 @@ metricsRules:
.tag({tags -> if (tags['gc'] == 'PS Scavenge' || tags['gc'] == 'Copy' || tags['gc'] == 'ParNew' || tags['gc'] == 'G1 Young Generation') {tags.gc = 'young_gc_count'} })
.tag({tags -> if (tags['gc'] == 'PS MarkSweep' || tags['gc'] == 'MarkSweepCompact' || tags['gc'] == 'ConcurrentMarkSweep' || tags['gc'] == 'G1 Old Generation') {tags.gc = 'old_gc_count'} })"
- name: instance_jvm_young_gc_time
- exp: jvm_gc_collection_seconds_sum.tagMatch('gc', 'PS Scavenge|Copy|ParNew|G1 Young Generation').sum(['service', 'host_name']) * 1000
+ exp: jvm_gc_collection_seconds_sum.tagMatch('gc', 'PS Scavenge|Copy|ParNew|G1 Young Generation').sum(['service', 'host_name']).increase('PT1M') * 1000
- name: instance_jvm_old_gc_time
- exp: jvm_gc_collection_seconds_sum.tagMatch('gc', 'PS MarkSweep|MarkSweepCompact|ConcurrentMarkSweep|G1 Old Generation').sum(['service', 'host_name']) * 1000
+ exp: jvm_gc_collection_seconds_sum.tagMatch('gc', 'PS MarkSweep|MarkSweepCompact|ConcurrentMarkSweep|G1 Old Generation').sum(['service', 'host_name']).increase('PT1M') * 1000
- name: instance_trace_count
exp: trace_in_latency_count.sum(['service', 'host_name']).increase('PT1M')
- name: instance_trace_latency_percentile
diff --git a/oap-server/server-starter/src/main/resources/ui-initialized-templates/k8s/k8s-cluster.json b/oap-server/server-starter/src/main/resources/ui-initialized-templates/k8s/k8s-cluster.json
index e4c8f68012..485f72139b 100644
--- a/oap-server/server-starter/src/main/resources/ui-initialized-templates/k8s/k8s-cluster.json
+++ b/oap-server/server-starter/src/main/resources/ui-initialized-templates/k8s/k8s-cluster.json
@@ -37,7 +37,7 @@
"metricTypes": [
""
],
- "activedTabIndex": 0,
+ "activedTabIndex": 1,
"children": [
{
"name": "Overview",
@@ -459,16 +459,23 @@
"fontSize": 12
},
"metrics": [
- "k8s_node_cpu_usage"
+ "k8s_node_cpu_usage",
+ "k8s_node_pod_total"
],
"metricTypes": [
+ "readMetricsValues",
"readMetricsValues"
],
"moved": false,
"metricConfig": [
{
"label": "CPU Usage",
- "unit": "m"
+ "unit": "m",
+ "calculation": "average"
+ },
+ {
+ "calculation": "average",
+ "label": "Pod"
}
]
}
diff --git a/oap-server/server-starter/src/main/resources/ui-initialized-templates/k8s/k8s-root.json b/oap-server/server-starter/src/main/resources/ui-initialized-templates/k8s/k8s-root.json
index 54e2f6122d..8c0dd8bedc 100644
--- a/oap-server/server-starter/src/main/resources/ui-initialized-templates/k8s/k8s-root.json
+++ b/oap-server/server-starter/src/main/resources/ui-initialized-templates/k8s/k8s-root.json
@@ -36,15 +36,45 @@
"fontSize": 12,
"showXAxis": false,
"showYAxis": false,
- "showGroup": true
+ "showGroup": false
},
"metrics": [
- ""
+ "k8s_cluster_node_total",
+ "k8s_cluster_namespace_total",
+ "k8s_cluster_deployment_total",
+ "k8s_cluster_service_total",
+ "k8s_cluster_pod_total"
],
"metricTypes": [
- ""
+ "readMetricsValues",
+ "readMetricsValues",
+ "readMetricsValues",
+ "readMetricsValues",
+ "readMetricsValues"
],
- "moved": false
+ "moved": false,
+ "metricConfig": [
+ {
+ "calculation": "average",
+ "label": "Node"
+ },
+ {
+ "calculation": "average",
+ "label": "Namespace"
+ },
+ {
+ "calculation": "average",
+ "label": "Deployment"
+ },
+ {
+ "calculation": "average",
+ "label": "Service"
+ },
+ {
+ "calculation": "average",
+ "label": "Pod"
+ }
+ ]
},
{
"x": 0,
@@ -70,11 +100,11 @@
"moved": false
}
],
+ "id": "K8S-Root",
"layer": "K8S",
"entity": "All",
"name": "K8S-Root",
- "isRoot": true,
- "id": "K8S-Root"
+ "isRoot": true
}
}
]
diff --git a/oap-server/server-starter/src/main/resources/ui-initialized-templates/k8s_service/k8s-service-root.json b/oap-server/server-starter/src/main/resources/ui-initialized-templates/k8s_service/k8s-service-root.json
index 12e78ef2be..25a9ef6e44 100644
--- a/oap-server/server-starter/src/main/resources/ui-initialized-templates/k8s_service/k8s-service-root.json
+++ b/oap-server/server-starter/src/main/resources/ui-initialized-templates/k8s_service/k8s-service-root.json
@@ -28,10 +28,14 @@
"i": "0",
"type": "Widget",
"metricTypes": [
- ""
+ "readMetricsValues",
+ "readMetricsValues",
+ "readMetricsValues"
],
"metrics": [
- ""
+ "k8s_service_pod_total",
+ "k8s_service_cpu_cores_requests",
+ "k8s_service_cpu_cores_limits"
],
"moved": false,
"graph": {
@@ -41,7 +45,23 @@
"showXAxis": false,
"showYAxis": false,
"showGroup": true
- }
+ },
+ "metricConfig": [
+ {
+ "calculation": "average",
+ "label": "Pod"
+ },
+ {
+ "label": "CPU Requests",
+ "calculation": "average",
+ "unit": "m"
+ },
+ {
+ "label": "CPU Limits",
+ "unit": "m",
+ "calculation": "average"
+ }
+ ]
},
{
"x": 0,
@@ -67,6 +87,7 @@
"moved": false
}
],
+ "id": "K8S-Service-Root",
"layer": "K8S_SERVICE",
"entity": "All",
"name": "K8S-Service-Root",
diff --git a/oap-server/server-starter/src/main/resources/ui-initialized-templates/mesh/mesh-root.json b/oap-server/server-starter/src/main/resources/ui-initialized-templates/mesh/mesh-root.json
index 8fae2e29a4..e483e278ed 100644
--- a/oap-server/server-starter/src/main/resources/ui-initialized-templates/mesh/mesh-root.json
+++ b/oap-server/server-starter/src/main/resources/ui-initialized-templates/mesh/mesh-root.json
@@ -78,7 +78,7 @@
"metricConfig": [
{
"label": "Load",
- "unit": "calls / min",
+ "unit": "calls or packets / min",
"calculation": "average"
},
{
diff --git a/oap-server/server-starter/src/main/resources/ui-initialized-templates/mesh/mesh-service.json b/oap-server/server-starter/src/main/resources/ui-initialized-templates/mesh/mesh-service.json
index 291f522ba3..a563ff5d69 100644
--- a/oap-server/server-starter/src/main/resources/ui-initialized-templates/mesh/mesh-service.json
+++ b/oap-server/server-starter/src/main/resources/ui-initialized-templates/mesh/mesh-service.json
@@ -499,7 +499,7 @@
"metricConfig": [
{
"label": "Load",
- "unit": "calls / min",
+ "unit": "calls or packets / min",
"calculation": "average"
},
{
@@ -550,7 +550,7 @@
"metricConfig": [
{
"label": "Load",
- "unit": "calls / min",
+ "unit": "calls or packets / min",
"calculation": "average"
},
{
diff --git a/oap-server/server-starter/src/main/resources/ui-initialized-templates/mesh_cp/mesh-control-plane-root.json b/oap-server/server-starter/src/main/resources/ui-initialized-templates/mesh_cp/mesh-control-plane-root.json
index 7e2aba37f6..3e3c99aa61 100644
--- a/oap-server/server-starter/src/main/resources/ui-initialized-templates/mesh_cp/mesh-control-plane-root.json
+++ b/oap-server/server-starter/src/main/resources/ui-initialized-templates/mesh_cp/mesh-control-plane-root.json
@@ -39,12 +39,42 @@
"showGroup": true
},
"metrics": [
- ""
+ "meter_istio_cpu",
+ "meter_istio_go_goroutines",
+ "meter_istio_pilot_xds",
+ "meter_istio_pilot_services",
+ "meter_istio_pilot_virt_services"
],
"metricTypes": [
- ""
+ "readMetricsValues",
+ "readMetricsValues",
+ "readMetricsValues",
+ "readMetricsValues",
+ "readMetricsValues"
],
- "moved": false
+ "moved": false,
+ "metricConfig": [
+ {
+ "label": "CPU",
+ "calculation": "average"
+ },
+ {
+ "calculation": "average",
+ "label": "Goroutines"
+ },
+ {
+ "calculation": "average",
+ "label": "Pilot xDS"
+ },
+ {
+ "calculation": "average",
+ "label": "Pilot Services"
+ },
+ {
+ "calculation": "average",
+ "label": "Pilot Virt Services"
+ }
+ ]
},
{
"x": 0,
@@ -70,6 +100,7 @@
"moved": false
}
],
+ "id": "Mesh-Control-Plane-Root",
"layer": "MESH_CP",
"entity": "All",
"name": "Mesh-Control-Plane-Root",
diff --git a/oap-server/server-starter/src/main/resources/ui-initialized-templates/mesh_dp/mesh-data-plane-service.json b/oap-server/server-starter/src/main/resources/ui-initialized-templates/mesh_dp/mesh-data-plane-service.json
index dbadf1778f..099ba00a69 100644
--- a/oap-server/server-starter/src/main/resources/ui-initialized-templates/mesh_dp/mesh-data-plane-service.json
+++ b/oap-server/server-starter/src/main/resources/ui-initialized-templates/mesh_dp/mesh-data-plane-service.json
@@ -36,12 +36,30 @@
"fontSize": 12
},
"metrics": [
- ""
+ "envoy_total_connections_used",
+ "envoy_worker_threads",
+ "envoy_bug_failures"
],
"metricTypes": [
- ""
+ "readMetricsValues",
+ "readMetricsValues",
+ "readMetricsValues"
],
- "moved": false
+ "moved": false,
+ "metricConfig": [
+ {
+ "label": "Connections Used",
+ "calculation": "average"
+ },
+ {
+ "label": "Work Threads",
+ "calculation": "average"
+ },
+ {
+ "calculation": "average",
+ "label": "Bug Failures"
+ }
+ ]
}
],
"layer": "MESH_DP",
diff --git a/oap-server/server-starter/src/main/resources/ui-initialized-templates/os_linux/linux-root.json b/oap-server/server-starter/src/main/resources/ui-initialized-templates/os_linux/linux-root.json
index 0e53fdd68b..435a85dba5 100644
--- a/oap-server/server-starter/src/main/resources/ui-initialized-templates/os_linux/linux-root.json
+++ b/oap-server/server-starter/src/main/resources/ui-initialized-templates/os_linux/linux-root.json
@@ -47,8 +47,9 @@
"moved": false,
"metricConfig": [
{
- "label": "CPU",
- "unit": "%"
+ "label": "CPU Usage",
+ "unit": "%",
+ "calculation": "average"
}
]
},
@@ -76,11 +77,11 @@
"moved": false
}
],
- "name": "Linux-Root",
+ "id": "Linux-Root",
"layer": "OS_LINUX",
"entity": "All",
- "isRoot": true,
- "id": "Linux-Root"
+ "name": "Linux-Root",
+ "isRoot": true
}
}
]
diff --git a/oap-server/server-starter/src/main/resources/ui-initialized-templates/so11y_oap/so11y-instance.json b/oap-server/server-starter/src/main/resources/ui-initialized-templates/so11y_oap/so11y-instance.json
index 17bedae196..685b46be56 100644
--- a/oap-server/server-starter/src/main/resources/ui-initialized-templates/so11y_oap/so11y-instance.json
+++ b/oap-server/server-starter/src/main/resources/ui-initialized-templates/so11y_oap/so11y-instance.json
@@ -81,7 +81,7 @@
"i": "2",
"type": "Widget",
"widget": {
- "title": "GC Time (ms)"
+ "title": "GC Time (ms / min)"
},
"graph": {
"type": "Line",
diff --git a/oap-server/server-starter/src/main/resources/ui-initialized-templates/so11y_oap/so11y-service.json b/oap-server/server-starter/src/main/resources/ui-initialized-templates/so11y_oap/so11y-service.json
index 7d98df4045..42f5d985d5 100644
--- a/oap-server/server-starter/src/main/resources/ui-initialized-templates/so11y_oap/so11y-service.json
+++ b/oap-server/server-starter/src/main/resources/ui-initialized-templates/so11y_oap/so11y-service.json
@@ -37,16 +37,24 @@
},
"standard": {},
"metrics": [
- "meter_oap_instance_cpu_percentage"
+ "meter_oap_instance_cpu_percentage",
+ "meter_oap_instance_persistence_execute_count"
],
"metricTypes": [
+ "readMetricsValues",
"readMetricsValues"
],
"moved": false,
"metricConfig": [
{
- "label": "CPU",
- "unit": "%"
+ "label": "CPU Avg Usage",
+ "unit": "%",
+ "calculation": "average"
+ },
+ {
+ "label": "Persistence Count",
+ "unit": "Per 5 Minutes",
+ "calculation": "average"
}
]
},
diff --git a/oap-server/server-starter/src/main/resources/ui-initialized-templates/virtual_database/virtual-database-root.json b/oap-server/server-starter/src/main/resources/ui-initialized-templates/virtual_database/virtual-database-root.json
index e977a9c61f..7a4e731ae2 100644
--- a/oap-server/server-starter/src/main/resources/ui-initialized-templates/virtual_database/virtual-database-root.json
+++ b/oap-server/server-starter/src/main/resources/ui-initialized-templates/virtual_database/virtual-database-root.json
@@ -39,16 +39,31 @@
"showGroup": false
},
"metrics": [
- "database_access_resp_time"
+ "database_access_resp_time",
+ "database_access_sla",
+ "database_access_cpm"
],
"metricTypes": [
+ "readMetricsValues",
+ "readMetricsValues",
"readMetricsValues"
],
"moved": false,
"metricConfig": [
{
"unit": "ms",
- "label": "Latency"
+ "label": "Latency",
+ "calculation": "average"
+ },
+ {
+ "label": "Successful Rate",
+ "unit": "%",
+ "calculation": "percentageAvg"
+ },
+ {
+ "label": "Traffic",
+ "unit": "calls / min",
+ "calculation": "average"
}
]
},
diff --git a/skywalking-ui b/skywalking-ui
index 26db1ec23e..3c68a4a327 160000
--- a/skywalking-ui
+++ b/skywalking-ui
@@ -1 +1 @@
-Subproject commit 26db1ec23ed06c662b4da6da022e678937b01b45
+Subproject commit 3c68a4a327d319773d3c9563e92cf420079d1ff0