Repository: juicedata/juicefs
Pull request #3041 (2022-12-01 12:17:37)

update en/fuse_mount_options.md, add title for Troubleshooting Method

timfeirg: title issue already resolved in https://github.com/juicedata/juicefs/pull/3040, pending review

Patch:
diff --git a/.github/actions/build/action.yml b/.github/actions/build/action.yml
index d6be7929..f4b7bc84 100644
--- a/.github/actions/build/action.yml
+++ b/.github/actions/build/action.yml
@@ -37,14 +37,14 @@ runs:
     - name: Build linux target
       shell: bash
       run: |
+        echo "start to build ${{inputs.target}}"
         if [ "${{inputs.target}}" == "juicefs.fdb" ]; then
-          echo "build juicefs.fdb"
           wget -q https://github.com/apple/foundationdb/releases/download/6.3.23/foundationdb-clients_6.3.23-1_amd64.deb
           sudo dpkg -i foundationdb-clients_6.3.23-1_amd64.deb
           make ${{inputs.target}}
           mv juicefs.fdb juicefs
         else
-          echo "build juicefs"
           make ${{inputs.target}}
         fi
+        echo "build ${{inputs.target}} succeed"
 
diff --git a/.github/scripts/check_coverage.py b/.github/scripts/check_coverage.py
index fa54944e..b7189117 100644
--- a/.github/scripts/check_coverage.py
+++ b/.github/scripts/check_coverage.py
@@ -9,7 +9,7 @@ def is_mutation_in_coverage(original_file, changed_file, coverage_file):
 	changed = open(changed_file, 'r').readlines()
 	for i in range( min(len(original), len(changed)) ):
 		if original[i] != changed[i]:
-			print(f'line {i+1} is different')
+			# print(f'line {i+1} is different')
 			if (i+1) not in coverage:
 				# print(f'line {i+1} is not in coverage')
 				return False
@@ -39,7 +39,7 @@ if __name__ == '__main__':
 	original_file = os.environ['MUTATE_ORIGINAL']
 	changed_file = os.environ['MUTATE_CHANGED']
 	coverage_file = os.environ['COVERAGE_FILE']
-	print(f'MUTATE_ORIGINAL={original_file} MUTATE_CHANGED={changed_file} COVERAGE_FILE={coverage_file} python3 ../../scripts/check_coverage.py')
+	# print(f'MUTATE_ORIGINAL={original_file} MUTATE_CHANGED={changed_file} COVERAGE_FILE={coverage_file} python3 ../../scripts/check_coverage.py')
 	r = is_mutation_in_coverage(original_file, changed_file, coverage_file)
 	if r:
 		exit(0)
diff --git a/.github/scripts/parse_job_total.py b/.github/scripts/parse_job_total.py
new file mode 100644
index 00000000..cd74bf6d
--- /dev/null
+++ b/.github/scripts/parse_job_total.py
@@ -0,0 +1,24 @@
+#!/usr/bin/env python
+# -*- encoding: utf-8 -*-
+import os
+import re
+import sys
+
+
+def parse_test_jobs(test_file_path):
+    with open(test_file_path) as f:
+        lines = f.readlines()
+        for line in lines:
+            g = re.search('^//mutate_test_job_number:\s*(.+)', line.strip())
+            if g:
+                return int(g.group(1))
+                
+    return 0
+
+if __name__ == '__main__':
+    test_file_path = os.environ['TEST_FILE_NAME']
+    if not test_file_path:
+        print('test file name is empty', file=sys.stderr)
+        exit(1)
+    print(parse_test_jobs(test_file_path))
+    
\ No newline at end of file
diff --git a/.github/workflows/fsrand.yml b/.github/workflows/fsrand.yml
index 9c79f444..c51539ba 100644
--- a/.github/workflows/fsrand.yml
+++ b/.github/workflows/fsrand.yml
@@ -131,6 +131,6 @@ jobs:
           slack_bot_token: "${{ secrets.SLACK_BOT_TOKEN }}"  
           
       - name: Setup upterm session
-        if: ${{ failure() && github.event_name == 'workflow_dispatch' && github.event.inputs.debug }}
+        if: ${{ failure() && github.event_name == 'workflow_dispatch' && github.event.inputs.debug == 'true' }}
         timeout-minutes: 60
         uses: lhotari/action-upterm@v1
\ No newline at end of file
diff --git a/.github/workflows/gateway.yml b/.github/workflows/gateway.yml
index 38109b01..71b12666 100644
--- a/.github/workflows/gateway.yml
+++ b/.github/workflows/gateway.yml
@@ -15,7 +15,12 @@ on:
       - 'docs/**'
       - '**.md'
   workflow_dispatch:
-
+    inputs:
+      debug:
+        type: boolean
+        description: "Run the build with tmate debugging enabled"
+        required: false
+        default: false
 jobs:
   gateway:
     strategy:
@@ -201,6 +206,6 @@ jobs:
           slack_bot_token: "${{ secrets.SLACK_BOT_TOKEN }}"  
 
       - name: Setup upterm session
-        if: ${{ failure() }}
+        if: ${{ failure() && github.event_name == 'workflow_dispatch' && github.event.inputs.debug == 'true' }}
         timeout-minutes: 1
         uses: lhotari/action-upterm@v1
\ No newline at end of file
diff --git a/.github/workflows/load.yml b/.github/workflows/load.yml
index cebde80a..5af9c429 100644
--- a/.github/workflows/load.yml
+++ b/.github/workflows/load.yml
@@ -3,6 +3,12 @@ on:
   schedule:
     - cron:  '0 19 * * *'
   workflow_dispatch:
+    inputs:
+      debug:
+        type: boolean
+        description: "Run the build with tmate debugging enabled"
+        required: false
+        default: false
 
 jobs:
   load:
@@ -163,6 +169,6 @@ jobs:
           slack_bot_token: "${{ secrets.SLACK_BOT_TOKEN }}"  
           
       - name: Setup upterm session
-        if: ${{ failure() }}
-        timeout-minutes: 20
+        if: ${{ failure() && github.event_name == 'workflow_dispatch' && github.event.inputs.debug == 'true' }}
+        timeout-minutes: 60
         uses: lhotari/action-upterm@v1
\ No newline at end of file
diff --git a/.github/workflows/version_compatible_hypo.yml b/.github/workflows/version_compatible_hypo.yml
index 9ae634ef..d2c42de8 100644
--- a/.github/workflows/version_compatible_hypo.yml
+++ b/.github/workflows/version_compatible_hypo.yml
@@ -145,6 +145,6 @@ jobs:
           slack_bot_token: "${{ secrets.SLACK_BOT_TOKEN }}"  
 
       - name: Setup upterm session
-        if: ${{ failure() && github.event_name == 'workflow_dispatch' && github.event.inputs.debug }}
+        if: ${{ failure() && github.event_name == 'workflow_dispatch' && github.event.inputs.debug == 'true' }}
         timeout-minutes: 1
         uses: lhotari/action-upterm@v1
\ No newline at end of file
diff --git a/cmd/info.go b/cmd/info.go
index ce9c1501..e667b802 100644
--- a/cmd/info.go
+++ b/cmd/info.go
@@ -27,6 +27,7 @@ import (
 
 	"github.com/juicedata/juicefs/pkg/meta"
 	"github.com/juicedata/juicefs/pkg/utils"
+	"github.com/juicedata/juicefs/pkg/vfs"
 	"github.com/urfave/cli/v2"
 )
 
@@ -108,7 +109,7 @@ func info(ctx *cli.Context) error {
 		}
 
 		wb := utils.NewBuffer(8 + 10)
-		wb.Put32(meta.Info)
+		wb.Put32(meta.InfoV2)
 		wb.Put32(10)
 		wb.Put64(inode)
 		wb.Put8(recursive)
@@ -117,42 +118,165 @@ func info(ctx *cli.Context) error {
 		if err != nil {
 			logger.Fatalf("write message: %s", err)
 		}
-		data := make([]byte, 4)
-		n := readControl(f, data)
-		if n == 1 && data[0] == byte(syscall.EINVAL&0xff) {
-			logger.Fatalf("info is not supported, please upgrade and mount again")
+		var resp vfs.InfoResponse
+		err = resp.Decode(f)
+		_ = f.Close()
+		if err == syscall.EINVAL {
+			legacyInfo(d, path, inode, recursive, raw)
+			continue
 		}
-		r := utils.ReadBuffer(data)
-		size := r.Get32()
-		data = make([]byte, size)
-		n, err = f.Read(data)
+
 		if err != nil {
 			logger.Fatalf("read info: %s", err)
 		}
+
+		if resp.Failed {
+			logger.Fatalf("failed to get info: %s", resp.Reason)
+		}
+
 		fmt.Println(path, ":")
-		resp := string(data[:n])
-		var p int
-		if p = strings.Index(resp, "chunks:\n"); p > 0 {
-			p += 8
-			raw = 1 // legacy clients always return chunks
-		} else if p = strings.Index(resp, "objects:\n"); p > 0 {
-			p += 9
+		fmt.Printf("  inode: %d\n", resp.Ino)
+		fmt.Printf("  files: %d\n", resp.Summary.Files)
+		fmt.Printf("   dirs: %d\n", resp.Summary.Dirs)
+		fmt.Printf(" length: %s\n", utils.FormatBytes(resp.Summary.Length))
+		fmt.Printf("   size: %s\n", utils.FormatBytes(resp.Summary.Size))
+		switch len(resp.Paths) {
+		case 0:
+			fmt.Printf("   path: %s\n", "unknown")
+		case 1:
+			fmt.Printf("   path: %s\n", resp.Paths[0])
+		default:
+			fmt.Printf("  paths:\n")
+			for _, p := range resp.Paths {
+				fmt.Printf("\t%s\n", p)
+			}
 		}
-		if p <= 0 {
-			fmt.Println(resp)
-		} else {
-			fmt.Println(resp[:p-1])
-			if len(resp[p:]) > 0 {
-				printChunks(resp[p:], raw == 1)
+		if len(resp.Chunks) > 0 {
+			fmt.Println(" chunks:")
+			results := make([][]string, 0, 1+len(resp.Chunks))
+			results = append(results, []string{"chunkIndex", "sliceId", "size", "offset", "length"})
+			for _, c := range resp.Chunks {
+				results = append(results, []string{
+					strconv.FormatUint(c.ChunkIndex, 10),
+					strconv.FormatUint(c.Id, 10),
+					strconv.FormatUint(uint64(c.Size), 10),
+					strconv.FormatUint(uint64(c.Off), 10),
+					strconv.FormatUint(uint64(c.Len), 10),
+				})
 			}
+			printResult(results, -1, false)
+		}
+		if len(resp.Objects) > 0 {
+			fmt.Println(" objects:")
+			results := make([][]string, 0, 1+len(resp.Objects))
+			results = append(results, []string{"chunkIndex", "objectName", "size", "offset", "length"})
+			for _, o := range resp.Objects {
+				results = append(results, []string{
+					strconv.FormatUint(o.ChunkIndex, 10),
+					o.Key,
+					strconv.FormatUint(uint64(o.Size), 10),
+					strconv.FormatUint(uint64(o.Off), 10),
+					strconv.FormatUint(uint64(o.Len), 10),
+				})
+			}
+			printResult(results, 1, false)
+		}
+		if len(resp.FLocks) > 0 {
+			fmt.Println(" flocks:")
+			results := make([][]string, 0, 1+len(resp.FLocks))
+			results = append(results, []string{"Sid", "Owner", "Type"})
+			for _, l := range resp.FLocks {
+				results = append(results, []string{
+					strconv.FormatUint(l.Sid, 10),
+					strconv.FormatUint(l.Owner, 10),
+					l.Type,
+				})
+			}
+			printResult(results, 0, false)
+		}
+		if len(resp.PLocks) > 0 {
+			fmt.Println(" plocks:")
+			results := make([][]string, 0, 1+len(resp.PLocks))
+			results = append(results, []string{"Sid", "Owner", "Type", "Pid", "Start", "End"})
+			for _, l := range resp.PLocks {
+				results = append(results, []string{
+					strconv.FormatUint(l.Sid, 10),
+					strconv.FormatUint(l.Owner, 10),
+					ltypeToString(l.Type),
+					strconv.FormatUint(uint64(l.Pid), 10),
+					strconv.FormatUint(l.Start, 10),
+					strconv.FormatUint(l.End, 10),
+				})
+			}
+			printResult(results, 0, false)
 		}
-		_ = f.Close()
 	}
 
 	return nil
 }
 
-func printChunks(resp string, raw bool) {
+func ltypeToString(t uint32) string {
+	switch t {
+	case meta.F_RDLCK:
+		return "R"
+	case meta.F_WRLCK:
+		return "W"
+	default:
+		return "UNKNOWN"
+	}
+}
+
+func legacyInfo(d, path string, inode uint64, recursive, raw uint8) {
+	f := openController(d)
+	defer f.Close()
+	if f == nil {
+		logger.Errorf("%s is not inside JuiceFS", path)
+		// continue to next path
+		return
+	}
+
+	wb := utils.NewBuffer(8 + 10)
+	wb.Put32(meta.LegacyInfo)
+	wb.Put32(10)
+	wb.Put64(inode)
+	wb.Put8(recursive)
+	wb.Put8(raw)
+	_, err := f.Write(wb.Bytes())
+	if err != nil {
+		logger.Fatalf("write message: %s", err)
+	}
+	data := make([]byte, 4)
+	n := readControl(f, data)
+	if n == 1 && data[0] == byte(syscall.EINVAL&0xff) {
+		logger.Fatalf("info is not supported, please upgrade and mount again")
+	}
+	r := utils.ReadBuffer(data)
+	size := r.Get32()
+	data = make([]byte, size)
+	n, err = f.Read(data)
+	if err != nil {
+		logger.Fatalf("read info: %s", err)
+	}
+	fmt.Println(path, ":")
+	resp := string(data[:n])
+	var p int
+	if p = strings.Index(resp, "chunks:\n"); p > 0 {
+		p += 8
+		raw = 1 // legacy clients always return chunks
+	} else if p = strings.Index(resp, "objects:\n"); p > 0 {
+		p += 9
+	}
+	if p <= 0 {
+		fmt.Println(resp)
+	} else {
+		fmt.Println(resp[:p-1])
+		if len(resp[p:]) > 0 {
+			legacyPrintChunks(resp[p:], raw == 1)
+		}
+	}
+}
+
+func legacyPrintChunks(resp string, raw bool) {
 	cs := strings.Split(resp, "\n")
 	result := make([][]string, len(cs))
 	result[0] = []string{"chunkIndex", "objectName", "size", "offset", "length"}
diff --git a/docs/en/administration/destroy.md b/docs/en/administration/destroy.md
index 438a7861..e9a6c899 100644
--- a/docs/en/administration/destroy.md
+++ b/docs/en/administration/destroy.md
@@ -1,8 +1,7 @@
 ---
-sidebar_label: How to destroy a file system
+title: How to destroy a file system
 sidebar_position: 8
 ---
-# How to destroy a file system
 
 JuiceFS client provides the `destroy` command to completely destroy a file system, which will result in
 
diff --git a/docs/en/administration/fault_diagnosis_and_analysis.md b/docs/en/administration/fault_diagnosis_and_analysis.md
index ad9eed2f..81c7cdb5 100644
--- a/docs/en/administration/fault_diagnosis_and_analysis.md
+++ b/docs/en/administration/fault_diagnosis_and_analysis.md
@@ -1,8 +1,9 @@
 ---
-sidebar_label: Troubleshooting Methods
+title: Troubleshooting Methods
 sidebar_position: 5
 slug: /fault_diagnosis_and_analysis
 ---
+# Fault Diagnosis and Analysis
 
 import Tabs from '@theme/Tabs';
 import TabItem from '@theme/TabItem';
diff --git a/docs/en/administration/metadata_dump_load.md b/docs/en/administration/metadata_dump_load.md
index 5ee09f4c..be23c7e2 100644
--- a/docs/en/administration/metadata_dump_load.md
+++ b/docs/en/administration/metadata_dump_load.md
@@ -1,9 +1,8 @@
 ---
-sidebar_label: Metadata Backup & Recovery
+title: Metadata Backup & Recovery
 sidebar_position: 2
 slug: /metadata_dump_load
 ---
-# Metadata Backup & Recovery
 
 :::tip
 - JuiceFS v0.15.2 started to support manual backup, recovery and inter-engine migration of metadata.
diff --git a/docs/en/administration/monitoring.md b/docs/en/administration/monitoring.md
index 1fdb54a2..282c1f97 100644
--- a/docs/en/administration/monitoring.md
+++ b/docs/en/administration/monitoring.md
@@ -1,10 +1,8 @@
 ---
-sidebar_label: Monitoring and Data Visualization
+title: Monitoring and Data Visualization
 sidebar_position: 3
 ---
 
-# Monitoring and Data Visualization
-
 As a distributed file system hosting massive data storage, it is important for users to directly view the status changes of the entire system in terms of capacity, files, CPU load, disk IO, cache, etc. JuiceFS provides real-time status data externally through the Prometheus-oriented API to achieve the visualization of JuiceFS monitoring with ease, and you only need to expose it to your own Prometheus Server to visualize time series data with tools like Grafana.
 
 ## Get started
diff --git a/docs/en/administration/status_check_and_maintenance.md b/docs/en/administration/status_check_and_maintenance.md
index be48adeb..b9099c49 100644
--- a/docs/en/administration/status_check_and_maintenance.md
+++ b/docs/en/administration/status_check_and_maintenance.md
@@ -1,10 +1,8 @@
 ---
-sidebar_label: Status Check & Maintenance
+title: Status Check & Maintenance
 sidebar_position: 4
 ---
 
-# Status Check & Maintenance
-
 Any kind of storage system needs to be checked and maintained regularly after it is put into use, so that potential problems can be found and fixed as early as possible to ensure reliable operation of the file system and the integrity and consistency of the stored data.
 
 JuiceFS provides a series of tools to check and maintain the file system, not only to help us understand the basic information of the file system and its operation status, but also to help us detect and fix potential problems more easily.
diff --git a/docs/en/administration/sync_accounts_between_multiple_hosts.md b/docs/en/administration/sync_accounts_between_multiple_hosts.md
index b383a71b..64ec3b1a 100644
--- a/docs/en/administration/sync_accounts_between_multiple_hosts.md
+++ b/docs/en/administration/sync_accounts_between_multiple_hosts.md
@@ -1,11 +1,9 @@
 ---
-sidebar_label: Sync Accounts between Multiple Hosts
+title: Sync Accounts between Multiple Hosts
 sidebar_position: 7
 slug: /sync_accounts_between_multiple_hosts
 ---
 
-# Sync Accounts between Multiple Hosts
-
 JuiceFS supports Unix file permission (but not POSIX extended ACL), you can manage permissions by directory or file granularity, just like a local file system.
 
 To provide users with an intuitive and consistent permission management experience (e.g. the files accessible by user A on host X should be accessible by the same user on host Y), the same user who wants to access JuiceFS should have the same UID and GID on all hosts.
diff --git a/docs/en/benchmark/_performance_tuning.md b/docs/en/benchmark/_performance_tuning.md
index 1411c6c9..0c60b426 100644
--- a/docs/en/benchmark/_performance_tuning.md
+++ b/docs/en/benchmark/_performance_tuning.md
@@ -1,5 +1,5 @@
 ---
-sidebar_label: Performance Tuning
+title: Performance Tuning
 sidebar_position: 5
 ---
-# Performance Tuning
\ No newline at end of file
+# Performance Tuning
diff --git a/docs/en/benchmark/benchmark.md b/docs/en/benchmark/benchmark.md
index 0b0e2af9..d42571dd 100644
--- a/docs/en/benchmark/benchmark.md
+++ b/docs/en/benchmark/benchmark.md
@@ -1,9 +1,8 @@
 ---
-sidebar_label: Performance Benchmark
+title: Performance Benchmark
 sidebar_position: 1
 slug: .
 ---
-# Performance Benchmark
 
 ## Basic benchmark
 
diff --git a/docs/en/benchmark/fio.md b/docs/en/benchmark/fio.md
index 7913de42..86711fcc 100644
--- a/docs/en/benchmark/fio.md
+++ b/docs/en/benchmark/fio.md
@@ -1,9 +1,8 @@
 ---
-sidebar_label: Benchmark with fio
+title: Benchmark with fio
 sidebar_position: 7
 slug: /fio
 ---
-# Benchmark with fio
 
 :::tip
 Trash is enabled in JuiceFS v1.0+ by default. As a result, temporary files are created and deleted in the file system during the benchmark, and these files will be eventually dumped into a directory named `.trash`. To avoid storage space being occupied by `.trash`, you can run command `juicefs config META-URL --trash-days 0` to disable Trash before benchmark. See [trash](../security/trash.md) for details.
diff --git a/docs/en/benchmark/metadata_engines_benchmark.md b/docs/en/benchmark/metadata_engines_benchmark.md
index 24dd95dc..7e65dd5c 100644
--- a/docs/en/benchmark/metadata_engines_benchmark.md
+++ b/docs/en/benchmark/metadata_engines_benchmark.md
@@ -1,11 +1,9 @@
 ---
-sidebar_label: Metadata Engines Benchmark
+title: Metadata Engines Benchmark
 sidebar_position: 6
 slug: /metadata_engines_benchmark
 ---
 
-# Metadata Engines Benchmark
-
 Conclusion first:
 
 - For pure metadata operations, MySQL costs about 2~4x times of Redis; TiKV has similar performance to MySQL, and in most cases it costs a bit less; etcd costs about 1.5x times of TiKV.
diff --git a/docs/en/benchmark/operations_profiling.md b/docs/en/benchmark/operations_profiling.md
index 4df1688d..4eacb84c 100644
--- a/docs/en/benchmark/operations_profiling.md
+++ b/docs/en/benchmark/operations_profiling.md
@@ -1,9 +1,8 @@
 ---
-sidebar_label: Operations Profiling
+title: Operations Profiling
 sidebar_position: 3
 slug: /operations_profiling
 ---
-# Operations Profiling
 
 ## Introduction
 
diff --git a/docs/en/benchmark/performance_evaluation_guide.md b/docs/en/benchmark/performance_evaluation_guide.md
index 00f44bfa..ec3d5988 100644
--- a/docs/en/benchmark/performance_evaluation_guide.md
+++ b/docs/en/benchmark/performance_evaluation_guide.md
@@ -1,11 +1,9 @@
 ---
-sidebar_label: Performance Evaluation Guide
+title: Performance Evaluation Guide
 sidebar_position: 2
 slug: /performance_evaluation_guide
 ---
 
-# JuiceFS Performance Evaluation Guide
-
 Before starting performance testing, it is a good idea to write down a general description of usage scenario, including:
 
 1. What is the application for? For example, Apache Spark, PyTorch, or a program you developed yourself
diff --git a/docs/en/benchmark/stats_watcher.md b/docs/en/benchmark/stats_watcher.md
index 97ae0b9a..e1b576bd 100644
--- a/docs/en/benchmark/stats_watcher.md
+++ b/docs/en/benchmark/stats_watcher.md
@@ -1,11 +1,10 @@
 ---
-sidebar_label: Performance Statistics Watcher
+title: Performance Statistics Monitor
 sidebar_position: 4
 slug: /stats_watcher
 ---
-# JuiceFS Performance Statistics Monitor
 
-JuiceFS exposes a lot of [Promethues metrics](../administration/monitoring.md) for monitoring system internal performance. However, when diagnosing performance issues in practice, users may need a more real-time monitoring tool to know what is actually going on within a certain time range. Thus, we provide a command `stats` to print metrics every second, just like what the Linux command `dstat` does. The output is like:
+JuiceFS exposes a lot of [Prometheus metrics](../administration/monitoring.md) for monitoring system internal performance. However, when diagnosing performance issues in practice, users may need a more real-time monitoring tool to know what is actually going on within a certain time range. Thus, we provide a command `stats` to print metrics every second, just like what the Linux command `dstat` does. The output is like:
 
 ![stats_watcher](../images/juicefs_stats_watcher.png)
 
diff --git a/docs/en/community/_roadmap.md b/docs/en/community/_roadmap.md
index 8cd3ef1e..01dae092 100644
--- a/docs/en/community/_roadmap.md
+++ b/docs/en/community/_roadmap.md
@@ -1,5 +1,4 @@
 ---
-sidebar_label: Roadmap
+title: Roadmap
 sidebar_position: 3
 ---
-# Roadmap
\ No newline at end of file
diff --git a/docs/en/community/adopters.md b/docs/en/community/adopters.md
index 2f811bee..b2c2596c 100644
--- a/docs/en/community/adopters.md
+++ b/docs/en/community/adopters.md
@@ -1,11 +1,9 @@
 ---
-sidebar_label: Adopters
+title: Adopters
 sidebar_position: 1
 slug: /adopters
 ---
 
-# JuiceFS Adopters
-
 | Company/Team                                                                            | Environment | Use Cases                   | User Story                                                                                                                                                                                          |
 |-----------------------------------------------------------------------------------------|-------------|-----------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
 | [Xiaomi](https://www.mi.com/global)                                                     | Production  | AI                          |                                                                                                                                                                                                     |
diff --git a/docs/en/community/articles.md b/docs/en/community/articles.md
index fbb3cc85..ee2ec1ab 100644
--- a/docs/en/community/articles.md
+++ b/docs/en/community/articles.md
@@ -1,11 +1,9 @@
 ---
-sidebar_label: User Articles
+title: User Articles
 sidebar_position: 2
 slug: /articles
 ---
 
-# User Article Collection
-
 JuiceFS is widely applicable to various data storage and sharing scenarios. JuiceFS application cases from all over the world are summarized here. All community users are welcome to maintain this list.
 
 ## AI, Machine Learning and Deep Learning
diff --git a/docs/en/community/integrations.md b/docs/en/community/integrations.md
index ab0582e8..fbf9c85d 100644
--- a/docs/en/community/integrations.md
+++ b/docs/en/community/integrations.md
@@ -23,4 +23,4 @@ slug: /integrations
 
 ## DevOps
 
-- [Terraform Provider for JuiceFS](https://github.com/toowoxx/terraform-provider-juicefs) by Toowoxx IT GmbH, an IT service company from Germany
\ No newline at end of file
+- [Terraform Provider for JuiceFS](https://github.com/toowoxx/terraform-provider-juicefs) by Toowoxx IT GmbH, an IT service company from Germany
diff --git a/docs/en/community/usage_tracking.md b/docs/en/community/usage_tracking.md
index 38cfc848..55a65e3d 100644
--- a/docs/en/community/usage_tracking.md
+++ b/docs/en/community/usage_tracking.md
@@ -1,10 +1,8 @@
 ---
-sidebar_label: Usage Tracking
+title: Usage Tracking
 sidebar_position: 4
 ---
 
-# Usage Tracking
-
 JuiceFS by default collects and reports **anonymous** usage data. It only collects core metrics (e.g. version number, file system size), no user or any sensitive data will be collected. You could review related code [here](https://github.com/juicedata/juicefs/blob/main/pkg/usage/usage.go).
 
 These data help us understand how the community is using this project. You could disable reporting easily by command line option `--no-usage-report`:
diff --git a/docs/en/deployment/_share_via_nfs.md b/docs/en/deployment/_share_via_nfs.md
index ff2d980a..fc9851a4 100644
--- a/docs/en/deployment/_share_via_nfs.md
+++ b/docs/en/deployment/_share_via_nfs.md
@@ -1,5 +1,4 @@
 ---
-sidebar_label: Deploy JuiceFS with NFS
+title: Deploy JuiceFS with NFS
 sidebar_position: 5
 ---
-# Deploy JuiceFS with NFS
\ No newline at end of file
diff --git a/docs/en/deployment/_share_via_smb.md b/docs/en/deployment/_share_via_smb.md
index 13a68127..04f3680d 100644
--- a/docs/en/deployment/_share_via_smb.md
+++ b/docs/en/deployment/_share_via_smb.md
@@ -1,5 +1,4 @@
 ---
-sidebar_label: Deploy JuiceFS with SMB
+title: Deploy JuiceFS with SMB
 sidebar_position: 6
 ---
-# Deploy JuiceFS with SMB
\ No newline at end of file
diff --git a/docs/en/deployment/hadoop_java_sdk.md b/docs/en/deployment/hadoop_java_sdk.md
index da9a5db7..458e45f4 100644
--- a/docs/en/deployment/hadoop_java_sdk.md
+++ b/docs/en/deployment/hadoop_java_sdk.md
@@ -1,5 +1,5 @@
 ---
-sidebar_label: Use JuiceFS on Hadoop Ecosystem
+title: Use JuiceFS on Hadoop Ecosystem
 sidebar_position: 5
 slug: /hadoop_java_sdk
 ---
@@ -7,8 +7,6 @@ slug: /hadoop_java_sdk
 import Tabs from '@theme/Tabs';
 import TabItem from '@theme/TabItem';
 
-# Use JuiceFS on Hadoop Ecosystem
-
 JuiceFS provides [Hadoop-compatible FileSystem](https://hadoop.apache.org/docs/current/hadoop-project-dist/hadoop-common/filesystem/introduction.html) by Hadoop Java SDK. Various applications in the Hadoop ecosystem can smoothly use JuiceFS to store data without changing the code.
 
 ## Requirements
diff --git a/docs/en/deployment/how_to_use_on_kubernetes.md b/docs/en/deployment/how_to_use_on_kubernetes.md
index 7dbe8ff9..8bc90f62 100644
--- a/docs/en/deployment/how_to_use_on_kubernetes.md
+++ b/docs/en/deployment/how_to_use_on_kubernetes.md
@@ -1,5 +1,5 @@
 ---
-sidebar_label: Use JuiceFS on Kubernetes
+title: Use JuiceFS on Kubernetes
 sidebar_position: 4
 slug: /how_to_use_on_kubernetes
 ---
diff --git a/docs/en/deployment/juicefs_on_docker.md b/docs/en/deployment/juicefs_on_docker.md
index 4448515b..0586b98e 100644
--- a/docs/en/deployment/juicefs_on_docker.md
+++ b/docs/en/deployment/juicefs_on_docker.md
@@ -1,9 +1,8 @@
 ---
-sidebar_label: Use JuiceFS on Docker
+title: Use JuiceFS on Docker
 sidebar_position: 3
 slug: /juicefs_on_docker
 ---
-# Use JuiceFS on Docker
 
 There are several common ways to use JuiceFS as Docker persistent storage.
 
diff --git a/docs/en/deployment/s3_gateway.md b/docs/en/deployment/s3_gateway.md
index 4312fe1a..8c6dc19f 100644
--- a/docs/en/deployment/s3_gateway.md
+++ b/docs/en/deployment/s3_gateway.md
@@ -1,11 +1,9 @@
 ---
-sidebar_label: Deploy JuiceFS S3 Gateway
+title: Deploy JuiceFS S3 Gateway
 sidebar_position: 6
 slug: /s3_gateway
 ---
 
-# Deploy JuiceFS S3 Gateway
-
 JuiceFS has introduced S3 gateway since v0.11. The feature is implemented based on the [MinIO S3 Gateway](https://docs.min.io/docs/minio-gateway-for-s3.html). It provides an S3-compatible RESTful API for files on JuiceFS, enabling the management of files stored on JuiceFS with tools such as s3cmd, AWS CLI, and MinIO Client (mc) in cases where mounting is not convenient. In addition, S3 gateway also provides a web-based file manager that allows users to manage files in web browsers.
 
 Since JuiceFS stores files in chunks, the files cannot be accessed directly through the interfaces of the underlying object storage. The S3 gateway accesses the underlying object storage in a similar way, shown in the following architecture diagram.
diff --git a/docs/en/development/contributing_guide.md b/docs/en/development/contributing_guide.md
index 1d8bbfa1..ccfe10c6 100644
--- a/docs/en/development/contributing_guide.md
+++ b/docs/en/development/contributing_guide.md
@@ -1,10 +1,8 @@
 ---
-sidebar_label: Contributing Guide
+title: Contributing Guide
 sidebar_position: 1
 ---
 
-# Contributing to JuiceFS
-
 ## Guidelines
 
 - Before starting work on a feature or bug fix, please search GitHub or reach out to us via GitHub, Slack etc. The purpose of this step is make sure no one else is already working on it and we'll ask you to open a GitHub issue if necessary.
diff --git a/docs/en/development/data_structures.md b/docs/en/development/data_structures.md
index d863f611..7d52af9c 100644
--- a/docs/en/development/data_structures.md
+++ b/docs/en/development/data_structures.md
@@ -1,9 +1,8 @@
 ---
-sidebar_label: Internals
+title: Internals
 sidebar_position: 4
 slug: /internals
 ---
-# JuiceFS Internals
 
 ## 1. Introduction
 
diff --git a/docs/en/getting-started/installation.md b/docs/en/getting-started/installation.md
index 96379d5e..357e54f2 100644
--- a/docs/en/getting-started/installation.md
+++ b/docs/en/getting-started/installation.md
@@ -1,12 +1,10 @@
 ---
-sidebar_label: Installation
+title: Installation
 sidebar_position: 1
 slug: /installation
 pagination_prev: introduction/comparison/juicefs_vs_s3ql
 ---
 
-# Installation
-
 JuiceFS has good cross-platform capability and supports running on all kinds of operating systems of almost all major architectures, including and not limited to Linux, macOS, Windows, etc.
 
 The JuiceFS client has only one binary file, you can download the pre-compiled version to unzip it and use it directly, or you can compile it manually with the source code.
diff --git a/docs/en/guide/cache_management.md b/docs/en/guide/cache_management.md
index f29f02c3..8a315713 100644
--- a/docs/en/guide/cache_management.md
+++ b/docs/en/guide/cache_management.md
@@ -1,9 +1,8 @@
 ---
-sidebar_label: Cache
+title: Cache
 sidebar_position: 3
 slug: /cache_management
 ---
-# Cache
 
 For a file system driven by a combination of object storage and database, cache is an important medium for interacting efficiently between the local client and the remote service. Read and write data can be loaded into the cache in advance or asynchronously, and then the client uploads to or prefetches from the remote service in the background. The use of caching technology can significantly reduce the latency of storage operations and increase data throughput compared to interacting with remote services directly.
 
diff --git a/docs/en/guide/how_to_set_up_metadata_engine.md b/docs/en/guide/how_to_set_up_metadata_engine.md
index 3d627709..33973b98 100644
--- a/docs/en/guide/how_to_set_up_metadata_engine.md
+++ b/docs/en/guide/how_to_set_up_metadata_engine.md
@@ -1,5 +1,5 @@
 ---
-sidebar_label: How to Set Up Metadata Engine
+title: How to Set Up Metadata Engine
 sidebar_position: 1
 slug: /databases_for_metadata
 ---
@@ -7,8 +7,6 @@ slug: /databases_for_metadata
 import Tabs from '@theme/Tabs';
 import TabItem from '@theme/TabItem';
 
-# How to Set Up Metadata Engine
-
 :::tip Version Tips
 The environment variable `META_PASSWORD` used in this document is a new feature in JuiceFS v1.0, and not applied to old clients. Please [upgrade the clients](../administration/upgrade.md) before using it if you are using the old ones.
 :::
diff --git a/docs/en/guide/how_to_set_up_object_storage.md b/docs/en/guide/how_to_set_up_object_storage.md
index b10d0dce..105aebc2 100644
--- a/docs/en/guide/how_to_set_up_object_storage.md
+++ b/docs/en/guide/how_to_set_up_object_storage.md
@@ -1,5 +1,5 @@
 ---
-sidebar_label: How to Set Up Object Storage
+title: How to Set Up Object Storage
 sidebar_position: 2
 slug: /how_to_setup_object_storage
 ---
@@ -7,8 +7,6 @@ slug: /how_to_setup_object_storage
 import Tabs from '@theme/Tabs';
 import TabItem from '@theme/TabItem';
 
-# How to Set Up Object Storage
-
 As you can learn from [JuiceFS Technical Architecture](../introduction/architecture.md), JuiceFS is a distributed file system with data and metadata stored separately. JuiceFS uses object storage as the main data storage and uses databases such as Redis, PostgreSQL and MySQL as metadata storage.
 
 ## Storage options
diff --git a/docs/en/guide/mount_at_boot.md b/docs/en/guide/mount_at_boot.md
index a37f0ee4..43c4836d 100644
--- a/docs/en/guide/mount_at_boot.md
+++ b/docs/en/guide/mount_at_boot.md
@@ -1,13 +1,9 @@
 ---
-sidebar_label: Mount JuiceFS at Boot Time
+title: Mount JuiceFS at Boot Time
 sidebar_position: 2
 slug: /mount_juicefs_at_boot_time
 ---
 
-# Mount JuiceFS at Boot Time
-
-This is a guide about how to mount JuiceFS automatically at boot.
-
 ## Linux
 
 Copy `juicefs` as `/sbin/mount.juicefs`, then edit `/etc/fstab` with following line:
diff --git a/docs/en/guide/quota.md b/docs/en/guide/quota.md
index 25a7db5d..964318b1 100644
--- a/docs/en/guide/quota.md
+++ b/docs/en/guide/quota.md
@@ -1,8 +1,7 @@
 ---
-sidebar_label: Storage Quota
+title: Storage Quota
 sidebar_position: 5
 ---
-# Storage Quota
 
 Since JuiceFS v0.14.2, JuiceFS have supported file system level storage quotas. This feature allows users to:
 
diff --git a/docs/en/introduction/comparison/juicefs_vs_s3ql.md b/docs/en/introduction/comparison/juicefs_vs_s3ql.md
index 7eb1f1d4..817ac5ed 100644
--- a/docs/en/introduction/comparison/juicefs_vs_s3ql.md
+++ b/docs/en/introduction/comparison/juicefs_vs_s3ql.md
@@ -4,13 +4,18 @@ slug: /comparison/juicefs_vs_s3ql
 
 # JuiceFS vs. S3QL
 
-Similar to JuiceFS, [S3QL](https://github.com/s3ql/s3ql) is also an open source network file system driven by object storage and database. All data will be split into blocks and stored in object storage services such as Amazon S3, Backblaze B2, or OpenStack Swift, and the corresponding metadata will be stored in the database.
+Similar to JuiceFS, S3QL is also an open source network file system driven by object storage and database. All data will be split into blocks and stored in object storage services such as Amazon S3, Backblaze B2, or OpenStack Swift, and the corresponding metadata will be stored in the database.
+
+:::note
+S3QL was no longer maintained since November 9, 2022, see [S3QL Repository](https://github.com/s3ql/s3ql).
+:::
 
 ## Common ground
 
 - Both support the standard POSIX file system interface through the FUSE module, so that massive cloud storage can be mounted locally and used like local storage.
 - Both provide standard file system features: hard links, symbolic links, extended attributes, file permissions.
 - Both support data compression and encryption, but the algorithms used are different.
+- Both support metadata backup, S3QL automatically backs up SQLite databases to object storage, and JuiceFS automatically exports metadata to JSON format files every hour and backs them up to object storage for easy recovery and migration between various metadata engines.
 
 ## Differences
 
@@ -18,10 +23,10 @@ Similar to JuiceFS, [S3QL](https://github.com/s3ql/s3ql) is also an open source
 - S3QL has no distributed capability and **does not** support multi-host shared mounting. JuiceFS is a typical distributed file system. When using a network-based database, it supports multi-host distributed mount read and write.
 - S3QL commits a data block to S3 when it has not been accessed for more than a few seconds. After a file closed or even fsynced, it is only guranteed to stay in system memory, which may result in data loss if node fails. JuiceFS ensures high data durability, uploading all blocks synchronously when a file is closed.
 - S3QL provides data deduplication. Only one copy of the same data is stored, which can reduce the storage usage, but it will also increase the performance overhead of the system. JuiceFS pays more attention to performance, and it is too expensive to perform deduplication on large-scale data, so this function is temporarily not provided.
-- S3QL provides remote synchronous backup of metadata. SQLite databases with metadata will be backed up asynchronously to object storage. JuiceFS mainly uses network databases such as Redis and MySQL, and does not directly provide SQLite database synchronization backup function. However, JuiceFS supports metadata import and export as well as various storage backend synchronization functions, which allows users to back up metadata to object storage easily. JuiceFS also supports migration between different databases.
 
 |                           | **S3QL**              | **JuiceFS**                   |
 | :------------------------ | :-------------------- | :---------------------------- |
+| Project status            | Stop maintenance      | Active development            |
 | Metadata engine           | SQLite                | Redis, MySQL, SQLite, TiKV    |
 | Storage engine            | Object Storage, Local | Object Storage, WebDAV, Local |
 | Operating system          | Unix-like             | Linux, macOS, Windows         |
diff --git a/docs/en/introduction/io_processing.md b/docs/en/introduction/io_processing.md
index 482d5d91..2a4a6f98 100644
--- a/docs/en/introduction/io_processing.md
+++ b/docs/en/introduction/io_processing.md
@@ -1,5 +1,5 @@
 ---
-sidebar_label: Data Processing Flow
+sidebar_label: Data Processing Workflow
 sidebar_position: 3
 slug: /internals/io_processing
 ---
diff --git a/docs/en/reference/command_reference.md b/docs/en/reference/command_reference.md
index 899fbf4c..8f3259c5 100644
--- a/docs/en/reference/command_reference.md
+++ b/docs/en/reference/command_reference.md
@@ -1,5 +1,5 @@
 ---
-sidebar_label: Command Reference
+title: Command Reference
 sidebar_position: 1
 slug: /command_reference
 ---
@@ -7,10 +7,6 @@ slug: /command_reference
 import Tabs from '@theme/Tabs';
 import TabItem from '@theme/TabItem';
 
-# Command Reference
-
-There are many commands to help you manage your file system. This page provides a detailed reference for these commands.
-
 ## Overview
 
 If you run `juicefs` by itself, it will print all available commands. In addition, you can add `-h/--help` flag after each command to get more information of it, e.g., `juicefs format -h`.
diff --git a/docs/en/reference/fuse_mount_options.md b/docs/en/reference/fuse_mount_options.md
index e963f52b..6f82faaf 100644
--- a/docs/en/reference/fuse_mount_options.md
+++ b/docs/en/reference/fuse_mount_options.md
@@ -1,23 +1,39 @@
 ---
-sidebar_label: FUSE Mount Options
+title: FUSE Mount Options
 sidebar_position: 6
 slug: /fuse_mount_options
 ---
-# FUSE Mount Options
 
-This guide lists important FUSE mount options. These mount options are specified by the option `-o`  when execute the command [`juicefs mount`](../reference/command_reference.md#mount) (use comma to separate multiple options). For example:
+JuiceFS provides several access methods, FUSE is the common one, which is the way to mount the filesystem locally using the `juicefs mount` command. Users can add FUSE mount options for more granular control.
+
+This guide describes the common FUSE mount options for JuiceFS, with two ways to add mount options:
+
+1. Manually execute [`juicefs mount`](../reference/command_reference.md#mount) command, specified by the `-o` option, with multiple options separated by commas.
 
 ```bash
 juicefs mount -d -o allow_other,writeback_cache sqlite3://myjfs.db ~/jfs
 ```
 
-## debug
+2. Linux distributions define automounting via `/etc/fstab` by adding options directly to the `options` field, with multiple options separated by commas.   
 
-Enable debug log
+```
+# <file system>       <mount point>   <type>      <options>           <dump>  <pass>
+redis://localhost:6379/1    /jfs      juicefs     _netdev,allow_other   0       0
+```
+
+## default_permissions
+
+This option is automatically enabled when JuiceFS is mounted and does not need to be explicitly specified. It will enable the kernel's file access checks, which are performed outside the filesystem. When enabled, both the kernel checks and the filesystem checks must succeed before further operations.
+
+:::tip
+The kernel performs standard UNIX permission checks based on mode bits, uid/gid, and directory entry ownership.
+:::
 
 ## allow_other
 
-This option overrides the default security restriction that only users amounting the file system can access to files. That is all users (including root) can access the files. Only root is allowed to use this option by default, but this restriction can be removed by the configuration option `user_allow_other` in `/etc/fuse.conf`.
+By default, only the user who mounted the filesystem can access the files. The `allow_other` option allows other users (including the root user) to access the files as well.
+
+By default, this option is only available to the root user, but can be unrestricted by modifying `/etc/fuse.conf` and turning on the `user_allow_other` configuration option.
 
 ## writeback_cache
 
@@ -26,3 +42,15 @@ This mount option requires at least version 3.15 Linux kernel
 :::
 
 FUSE supports ["writeback-cache mode"](https://www.kernel.org/doc/Documentation/filesystems/fuse-io.txt), which means the `write()` syscall can often complete rapidly. It's recommended to enable this mount option when write small data (e.g. 100 bytes) frequently.
+
+## user_id and group_id
+
+These two options are used to specify the owner ID and owner group ID of the mount point, but only allow to execute the mount command as root, e.g. `sudo juicefs mount -o user_id=100,group_id=100`.
+
+## debug
+
+This option will output Debug information from the low-level library (go-fuse) to `juicefs.log`.
+
+:::note
+This option will output debug information for the low-level library (go-fuse) to `juicefs.log`. Note that this option is different from the global `-debug` option for the JuiceFS client, where the former outputs debug information for the go-fuse library and the latter outputs debug information for the JucieFS client. see the documentation [Fault Diagnosis and Analysis](./fault_diagnosis_and_analysis).
+:::
diff --git a/docs/en/reference/p8s_metrics.md b/docs/en/reference/p8s_metrics.md
index d8482a34..63ba03fc 100644
--- a/docs/en/reference/p8s_metrics.md
+++ b/docs/en/reference/p8s_metrics.md
@@ -1,11 +1,9 @@
 ---
-sidebar_label: JuiceFS Metrics
+title: JuiceFS Metrics
 sidebar_position: 2
 slug: /p8s_metrics
 ---
 
-# JuiceFS Metrics
-
 :::tip
 Please see the ["Monitoring"](../administration/monitoring.md) documentation to learn how to collect and display JuiceFS monitoring metrics.
 :::
diff --git a/docs/en/reference/posix_compatibility.md b/docs/en/reference/posix_compatibility.md
index 5a7c794b..d71a6436 100644
--- a/docs/en/reference/posix_compatibility.md
+++ b/docs/en/reference/posix_compatibility.md
@@ -1,9 +1,8 @@
 ---
-sidebar_label: POSIX Compatibility
+title: POSIX Compatibility
 sidebar_position: 6
 slug: /posix_compatibility
 ---
-# POSIX Compatibility
 
 JuiceFS ensures POSIX compatibility with the help of pjdfstest and LTP.
 
diff --git a/docs/en/security/metadata_automatic_backup.md b/docs/en/security/metadata_automatic_backup.md
deleted file mode 100644
index 9d279e2f..00000000
--- a/docs/en/security/metadata_automatic_backup.md
+++ /dev/null
@@ -1,7 +0,0 @@
----
-sidebar_label: Metadata Automatic Backup
-sidebar_position: 3
----
-# Metadata Automatic Backup
-
-Reference to [Metadata Automatic Backup](../administration/metadata_dump_load.md#automatic-backup)
\ No newline at end of file
diff --git a/docs/en/tutorials/aliyun.md b/docs/en/tutorials/aliyun.md
index 1e4ab53c..baf4fbd7 100644
--- a/docs/en/tutorials/aliyun.md
+++ b/docs/en/tutorials/aliyun.md
@@ -1,9 +1,8 @@
 ---
-sidebar_label: Use JuiceFS on Alibaba Cloud
+title: Use JuiceFS on Alibaba Cloud
 sidebar_position: 6
 slug: /clouds/aliyun
 ---
-# Use JuiceFS on Alibaba Cloud
 
 As shown in the figure below, JuiceFS is driven by both the database and the object storage. The files stored in JuiceFS are split into fixed-size data blocks and stored in the object store according to certain rules, while the metadata corresponding to the data is stored in the database.
 
diff --git a/docs/en/tutorials/aws.md b/docs/en/tutorials/aws.md
index aa9ed091..b937dd83 100644
--- a/docs/en/tutorials/aws.md
+++ b/docs/en/tutorials/aws.md
@@ -1,9 +1,8 @@
 ---
-sidebar_label: Use JuiceFS on AWS
+title: Use JuiceFS on AWS
 sidebar_position: 4
 slug: /clouds/aws
 ---
-# Use JuiceFS on AWS
 
 AWS is the world's leading cloud computing platform, offering almost all types of cloud computing services. Thanks to the rich product line of AWS, users can choose JuiceFS components in a very flexible way.
 
diff --git a/docs/en/tutorials/digitalocean.md b/docs/en/tutorials/digitalocean.md
index a84070d7..43fe5b14 100644
--- a/docs/en/tutorials/digitalocean.md
+++ b/docs/en/tutorials/digitalocean.md
@@ -1,9 +1,8 @@
 ---
-sidebar_label: Use JuiceFS on DigitalOcean
+title: Use JuiceFS on DigitalOcean
 sidebar_position: 5
 slug: /clouds/digitalocean
 ---
-# Use JuiceFS on DigitalOcean
 
 JuiceFS is designed for the cloud, using the cloud platform out-of-the-box storage and database services, and can be configured and put into use in as little as a few minutes. This article uses the DigitalOcean as an example to introduce how to quickly and easily install and use JuiceFS on the cloud computing platform.
 
diff --git a/docs/en/tutorials/juicefs_on_colab.md b/docs/en/tutorials/juicefs_on_colab.md
index 91e66358..150db684 100644
--- a/docs/en/tutorials/juicefs_on_colab.md
+++ b/docs/en/tutorials/juicefs_on_colab.md
@@ -1,11 +1,9 @@
 ---
-sidebar_label: Use JuiceFS on Colab with Google CloudSQL and GCS
+title: Use JuiceFS on Colab with Google CloudSQL and GCS
 sidebar_position: 4
 slug: /juicefs_on_colab
 ---
 
-# Use JuiceFS on Colab with Google CloudSQL and GCS
-
 [Colaboratory](https://colab.research.google.com), or “Colab” for short, is a product from Google Research. Colab allows
 anybody to write and execute arbitrary python code through the browser, and is especially well suited to machine
 learning, data analysis and education.
diff --git a/docs/en/tutorials/juicefs_on_k3s.md b/docs/en/tutorials/juicefs_on_k3s.md
index 3b63fc22..17869172 100644
--- a/docs/en/tutorials/juicefs_on_k3s.md
+++ b/docs/en/tutorials/juicefs_on_k3s.md
@@ -1,9 +1,8 @@
 ---
-sidebar_label: Use JuiceFS on K3s
+title: Use JuiceFS on K3s
 sidebar_position: 1
 slug: /juicefs_on_k3s
 ---
-# Use JuiceFS on K3s
 
 [K3s](https://k3s.io/) is a functionally optimized lightweight Kubernetes distribution that is fully compatible with Kubernetes, that is, almost all operations on Kubernetes can be performed on K3s. K3s has packaged the entire container orchestration system into a binary program with a capacity of less than 100MB, which greatly reduces the environment dependencies and steps for installation of deploying Kubernetes production clusters. Compared to Kubernetes, K3s has lower performance requirements for the operating system, and ARM devices such as Raspberry Pi can be used to form a cluster.
 
diff --git a/docs/en/tutorials/juicefs_on_kubesphere.md b/docs/en/tutorials/juicefs_on_kubesphere.md
index 77d297dc..54e0c8b4 100644
--- a/docs/en/tutorials/juicefs_on_kubesphere.md
+++ b/docs/en/tutorials/juicefs_on_kubesphere.md
@@ -1,9 +1,8 @@
 ---
-sidebar_label: Use JuiceFS on KubeSphere
+title: Use JuiceFS on KubeSphere
 sidebar_position: 3
 slug: /juicefs_on_kubesphere
 ---
-# Use JuiceFS on KubeSphere
 
 [KubeSphere](https://kubesphere.com.cn/) is an application-centric multi-tenant container platform built on Kubernetes. It provides full-stack IT automated operation and maintenance capabilities and simplifies the DevOps workflow of the enterprise.
 
diff --git a/docs/en/tutorials/juicefs_on_rancher.md b/docs/en/tutorials/juicefs_on_rancher.md
index d32ca876..e7573b5a 100644
--- a/docs/en/tutorials/juicefs_on_rancher.md
+++ b/docs/en/tutorials/juicefs_on_rancher.md
@@ -1,9 +1,8 @@
 ---
-sidebar_label: Use JuiceFS on Rancher
+title: Use JuiceFS on Rancher
 sidebar_position: 2
 slug: /juicefs_on_rancher
 ---
-# Use JuiceFS on Rancher
 
 [Rancher](https://rancher.com/) is an enterprise-level Kubernetes cluster management system, which can be used to quickly complete the deployment of Kubernetes clusters on various cloud computing platforms.
 
diff --git a/docs/en/tutorials/juicefs_on_wsl.md b/docs/en/tutorials/juicefs_on_wsl.md
index cea9c1e9..0ccf41ae 100644
--- a/docs/en/tutorials/juicefs_on_wsl.md
+++ b/docs/en/tutorials/juicefs_on_wsl.md
@@ -1,7 +1,6 @@
 ---
-sidebar_label: Use JuiceFS on WSL
+title: Use JuiceFS on WSL
 ---
-# Use JuiceFS on WSL
 
 WSL is called Windows Subsystem for Linux, which means Windows subsystem for Linux. It allows you to run most GNU/Linux native commands, tools, and programs in a Windows environment without the additional hardware overhead of using a virtual machine or dual system.
 
diff --git a/docs/en/tutorials/qcloud.md b/docs/en/tutorials/qcloud.md
index 77c61bed..a425e1b0 100644
--- a/docs/en/tutorials/qcloud.md
+++ b/docs/en/tutorials/qcloud.md
@@ -1,9 +1,8 @@
 ---
-sidebar_label: Use JuiceFS on Tencent Cloud
+title: Use JuiceFS on Tencent Cloud
 sidebar_position: 7
 slug: /clouds/qcloud
 ---
-# Use JuiceFS on Tencent Cloud
 
 JuiceFS needs to be used with database and object storage together. Here we directly use Tencent Cloud's CVM cloud server, combined with cloud database and COS object storage.
 
diff --git a/docs/zh_cn/administration/destroy.md b/docs/zh_cn/administration/destroy.md
index f532ddf4..2dfece46 100644
--- a/docs/zh_cn/administration/destroy.md
+++ b/docs/zh_cn/administration/destroy.md
@@ -1,8 +1,7 @@
 ---
-sidebar_label: 销毁文件系统
+title: 销毁文件系统
 sidebar_position: 8
 ---
-# 如何销毁文件系统
 
 JuiceFS 客户端提供了 `destroy` 命令用以彻底销毁一个文件系统，销毁操作将会产生以下结果：
 
diff --git a/docs/zh_cn/administration/fault_diagnosis_and_analysis.md b/docs/zh_cn/administration/fault_diagnosis_and_analysis.md
index f9e33038..a9b0b8ca 100644
--- a/docs/zh_cn/administration/fault_diagnosis_and_analysis.md
+++ b/docs/zh_cn/administration/fault_diagnosis_and_analysis.md
@@ -1,9 +1,11 @@
 ---
-sidebar_label: 问题排查方法
+title: 问题排查方法
 sidebar_position: 5
 slug: /fault_diagnosis_and_analysis
 ---
 
+# 故障诊断和分析
+
 import Tabs from '@theme/Tabs';
 import TabItem from '@theme/TabItem';
 
diff --git a/docs/zh_cn/administration/metadata/fdb_best_practices.md b/docs/zh_cn/administration/metadata/fdb_best_practices.md
index 6b60298d..aca9bfac 100644
--- a/docs/zh_cn/administration/metadata/fdb_best_practices.md
+++ b/docs/zh_cn/administration/metadata/fdb_best_practices.md
@@ -5,6 +5,7 @@ slug: /fdb_best_practices
 ---
 
 # FoundationDB 最佳实践
+
 fdb支持横向扩容，一旦数据存储达到集群的最高负载，只需要在集群中添加新的机器即可。配置集群的详细教程可见官网 https://apple.github.io/foundationdb/configuration.html ，对于不同场景不同机器数量的性能测试可见 https://apple.github.io/foundationdb/benchmarking.html。
 
 ## 系统要求
diff --git a/docs/zh_cn/administration/metadata/tikv_best_practices.md b/docs/zh_cn/administration/metadata/tikv_best_practices.md
index dbfc66a0..34c6c78f 100644
--- a/docs/zh_cn/administration/metadata/tikv_best_practices.md
+++ b/docs/zh_cn/administration/metadata/tikv_best_practices.md
@@ -1,5 +1,5 @@
 ---
-sidebar_label: TiKV 最佳实践
+sidebar_label: TiKV
 sidebar_position: 3
 ---
-# TiKV 最佳实践
\ No newline at end of file
+# TiKV 最佳实践
diff --git a/docs/zh_cn/administration/metadata_dump_load.md b/docs/zh_cn/administration/metadata_dump_load.md
index 68af4a79..119ef578 100644
--- a/docs/zh_cn/administration/metadata_dump_load.md
+++ b/docs/zh_cn/administration/metadata_dump_load.md
@@ -1,9 +1,8 @@
 ---
-sidebar_label: 元数据备份和恢复
+title: 元数据备份和恢复
 sidebar_position: 2
 slug: /metadata_dump_load
 ---
-# 元数据备份和恢复
 
 :::tip 提示
 - JuiceFS v0.15.2 开始支持元数据手动备份、恢复和引擎间迁移。
diff --git a/docs/zh_cn/administration/monitoring.md b/docs/zh_cn/administration/monitoring.md
index da53e272..724acd86 100644
--- a/docs/zh_cn/administration/monitoring.md
+++ b/docs/zh_cn/administration/monitoring.md
@@ -1,10 +1,8 @@
 ---
-sidebar_label: 监控与数据可视化
+title: 监控与数据可视化
 sidebar_position: 3
 ---
 
-# 监控与数据可视化
-
 作为承载海量数据存储的分布式文件系统，用户通常需要直观地了解整个系统的容量、文件数量、CPU 负载、磁盘 IO、缓存等指标的变化。JuiceFS 通过 Prometheus 兼容的 API 对外提供实时的状态数据，只需将其添加到用户自建的 Prometheus Server 建立时序数据，然后通过 Grafana 等工具即可轻松实现 JucieFS 文件系统的可视化监控。
 
 ## 快速上手
diff --git a/docs/zh_cn/administration/status_check_and_maintenance.md b/docs/zh_cn/administration/status_check_and_maintenance.md
index 13060c97..f281ceac 100644
--- a/docs/zh_cn/administration/status_check_and_maintenance.md
+++ b/docs/zh_cn/administration/status_check_and_maintenance.md
@@ -1,10 +1,8 @@
 ---
-sidebar_label: 状态检查 & 维护
+title: 状态检查 & 维护
 sidebar_position: 4
 ---
 
-# 状态检查 & 维护
-
 任何一种存储系统在投入使用之后都需要定期进行检查和维护，尽早发现并修复潜在的问题，从而保证文件系统可靠运行、存储的数据完整一致。
 
 JuiceFS 提供了一系列检查和维护文件系统的工具，不但可以帮助我们了解文件系统的基本信息、运行状态，还能够帮助我们更容易地发现和修复潜在的问题。
diff --git a/docs/zh_cn/administration/sync_accounts_between_multiple_hosts.md b/docs/zh_cn/administration/sync_accounts_between_multiple_hosts.md
index 2710f589..cfea579a 100644
--- a/docs/zh_cn/administration/sync_accounts_between_multiple_hosts.md
+++ b/docs/zh_cn/administration/sync_accounts_between_multiple_hosts.md
@@ -1,11 +1,9 @@
 ---
-sidebar_label: 多主机间同步账户
+title: 多主机间同步账户
 sidebar_position: 7
 slug: /sync_accounts_between_multiple_hosts
 ---
 
-# 多主机间同步账户
-
 JuiceFS 支持 Unix 文件权限（但不支持 POSIX extended ACL），以目录或文件的粒度管理权限。该行为与本地文件系统相同。
 
 为了让用户获得直观一致的权限管理体验（例如，用户 A 在主机 X 中访问的文件，在主机 Y 中也应该可以用相同的用户身份访问），想要访问 JuiceFS 存储的同一个用户，应该在所有主机上具有相同的 UID 和 GID。
diff --git a/docs/zh_cn/benchmark/_performance_tuning.md b/docs/zh_cn/benchmark/_performance_tuning.md
index f8dab853..c2001f35 100644
--- a/docs/zh_cn/benchmark/_performance_tuning.md
+++ b/docs/zh_cn/benchmark/_performance_tuning.md
@@ -1,5 +1,4 @@
 ---
-sidebar_label: 性能调优
+title: 性能调优
 sidebar_position: 5
 ---
-# JuiceFS 性能调优
\ No newline at end of file
diff --git a/docs/zh_cn/benchmark/benchmark.md b/docs/zh_cn/benchmark/benchmark.md
index 93e4ff76..9eed4db6 100644
--- a/docs/zh_cn/benchmark/benchmark.md
+++ b/docs/zh_cn/benchmark/benchmark.md
@@ -1,9 +1,8 @@
 ---
-sidebar_label: 常规测试
+title: 常规测试
 sidebar_position: 1
 slug: .
 ---
-# JuiceFS 常规测试
 
 ### 基础测试
 
diff --git a/docs/zh_cn/benchmark/fio.md b/docs/zh_cn/benchmark/fio.md
index 57fdeee8..729ff365 100644
--- a/docs/zh_cn/benchmark/fio.md
+++ b/docs/zh_cn/benchmark/fio.md
@@ -1,9 +1,8 @@
 ---
-sidebar_label: fio 基准测试
+title: fio 基准测试
 sidebar_position: 7
 slug: /fio
 ---
-# fio 基准测试
 
 :::tip 提示
 JuiceFS v1.0+ 默认启用了回收站，基准测试会在文件系统中创建和删除临时文件，这些文件最终会被转存到回收站 `.trash` 占用存储空间，为了避免这种情况，可以在基准测试之前关闭回收站 `juicefs config META-URL --trash-days 0`，详情参考[回收站](../security/trash.md)。
diff --git a/docs/zh_cn/benchmark/metadata_engines_benchmark.md b/docs/zh_cn/benchmark/metadata_engines_benchmark.md
index ea42581b..036d47ab 100644
--- a/docs/zh_cn/benchmark/metadata_engines_benchmark.md
+++ b/docs/zh_cn/benchmark/metadata_engines_benchmark.md
@@ -1,11 +1,9 @@
 ---
-sidebar_label: 元数据引擎性能测试
+title: 元数据引擎性能测试
 sidebar_position: 6
 slug: /metadata_engines_benchmark
 ---
 
-# 元数据引擎性能对比测试
-
 首先展示结论：
 
 - 对于纯元数据操作，MySQL 耗时约为 Redis 的 2～4 倍；TiKV 性能与 MySQL 接近，大部分场景下略优于 MySQL；etcd 的耗时约为 TiKV 的 1.5 倍
diff --git a/docs/zh_cn/benchmark/operations_profiling.md b/docs/zh_cn/benchmark/operations_profiling.md
index f64a610a..ccb126a4 100644
--- a/docs/zh_cn/benchmark/operations_profiling.md
+++ b/docs/zh_cn/benchmark/operations_profiling.md
@@ -1,9 +1,8 @@
 ---
-sidebar_label: 性能诊断
+title: 性能诊断
 sidebar_position: 3
 slug: /operations_profiling
 ---
-# JuiceFS 性能诊断
 
 ## 介绍
 
diff --git a/docs/zh_cn/benchmark/performance_evaluation_guide.md b/docs/zh_cn/benchmark/performance_evaluation_guide.md
index 3fed4af4..e5fcc59f 100644
--- a/docs/zh_cn/benchmark/performance_evaluation_guide.md
+++ b/docs/zh_cn/benchmark/performance_evaluation_guide.md
@@ -1,11 +1,9 @@
 ---
-sidebar_label: 性能评估指南
+title: 性能评估指南
 sidebar_position: 2
 slug: /performance_evaluation_guide
 ---
 
-# JuiceFS 性能评估指南
-
 在进行性能测试之前，最好写下该使用场景的大致描述，包括：
 
 1. 对接的应用是什么？比如 Apache Spark、PyTorch 或者是自己写的程序等
diff --git a/docs/zh_cn/benchmark/stats_watcher.md b/docs/zh_cn/benchmark/stats_watcher.md
index ae2e0a6b..32e15669 100644
--- a/docs/zh_cn/benchmark/stats_watcher.md
+++ b/docs/zh_cn/benchmark/stats_watcher.md
@@ -1,9 +1,8 @@
 ---
-sidebar_label: 性能统计监控
+title: 性能统计监控
 sidebar_position: 4
 slug: /stats_watcher
 ---
-# JuiceFS 性能统计监控
 
 JuiceFS 预定义了许多监控指标来监测系统运行时的内部性能情况，并通过 Prometheus API [暴露对外接口](../administration/monitoring.md)。然而，在分析一些实际问题时，用户往往需要更实时的性能统计监控。因此，我们开发了 `stats` 命令，以类似 Linux `dstat` 工具的形式实时打印各个指标的每秒变化情况，如下图所示：
 
diff --git a/docs/zh_cn/community/_roadmap.md b/docs/zh_cn/community/_roadmap.md
index d52e7507..3b6fd404 100644
--- a/docs/zh_cn/community/_roadmap.md
+++ b/docs/zh_cn/community/_roadmap.md
@@ -1,5 +1,4 @@
 ---
-sidebar_label: 路线图
+title: 路线图
 sidebar_position: 3
 ---
-# 路线图
\ No newline at end of file
diff --git a/docs/zh_cn/community/adopters.md b/docs/zh_cn/community/adopters.md
index 43138296..786bfa5c 100644
--- a/docs/zh_cn/community/adopters.md
+++ b/docs/zh_cn/community/adopters.md
@@ -1,11 +1,9 @@
 ---
-sidebar_label: 使用者
+title: 使用者
 sidebar_position: 1
 slug: /adopters
 ---
 
-# JuiceFS 使用者
-
 | 公司／团队                                       | 使用环境   | 使用场景               | 案例                                                                                                                                                                                                  |
 |--------------------------------------------------|------------|------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
 | [小米](https://www.mi.com)                       | Production | AI                     |                                                                                                                                                                                                       |
diff --git a/docs/zh_cn/community/articles.md b/docs/zh_cn/community/articles.md
index 167e30e2..08dcb5ed 100644
--- a/docs/zh_cn/community/articles.md
+++ b/docs/zh_cn/community/articles.md
@@ -1,11 +1,9 @@
 ---
-sidebar_label: 用户文章合集
+title: 用户文章合集
 sidebar_position: 2
 slug: /articles
 ---
 
-# 用户文章合集
-
 JuiceFS 广泛适用于各种数据存储和共享场景，本页汇总来自世界各地用户使用 JuiceFS 的实践，欢迎大家共同来维护这个列表。
 
 ## AI
diff --git a/docs/zh_cn/community/integrations.md b/docs/zh_cn/community/integrations.md
index 6226c239..62d0a619 100644
--- a/docs/zh_cn/community/integrations.md
+++ b/docs/zh_cn/community/integrations.md
@@ -1,11 +1,9 @@
 ---
-sidebar_label: 社区集成
+title: 社区集成
 sidebar_position: 2
 slug: /integrations
 ---
 
-# 社区集成
-
 ## SDK
 
 - [旷视科技](https://megvii.com) 团队贡献了 [Python SDK](https://github.com/megvii-research/juicefs-python)。
@@ -23,4 +21,4 @@ slug: /integrations
 
 ## DevOps
 
-- [Terraform Provider for JuiceFS](https://github.com/toowoxx/terraform-provider-juicefs) 由 Toowoxx IT GmbH 贡献，他们是一家来自德国的 IT 服务公司。
\ No newline at end of file
+- [Terraform Provider for JuiceFS](https://github.com/toowoxx/terraform-provider-juicefs) 由 Toowoxx IT GmbH 贡献，他们是一家来自德国的 IT 服务公司。
diff --git a/docs/zh_cn/community/usage_tracking.md b/docs/zh_cn/community/usage_tracking.md
index aefbad89..5220ed13 100644
--- a/docs/zh_cn/community/usage_tracking.md
+++ b/docs/zh_cn/community/usage_tracking.md
@@ -1,10 +1,8 @@
 ---
-sidebar_label: 用量上报
+title: 用量上报
 sidebar_position: 4
 ---
 
-# 用量上报
-
 JuiceFS 默认会收集并上报**「匿名」**的使用数据。这些数据仅仅包含核心指标（如版本号、文件系统大小），不会包含任何用户信息或者敏感数据。你可以查看[这里](https://github.com/juicedata/juicefs/blob/main/pkg/usage/usage.go)检查相关代码。
 
 这些数据帮助我们理解社区如何使用这个项目。你可以简单地通过 `--no-usage-report` 选项关闭用量上报：
diff --git a/docs/zh_cn/deployment/juicefs_on_docker.md b/docs/zh_cn/deployment/juicefs_on_docker.md
index bd9a9789..6ad0a29e 100644
--- a/docs/zh_cn/deployment/juicefs_on_docker.md
+++ b/docs/zh_cn/deployment/juicefs_on_docker.md
@@ -1,9 +1,8 @@
 ---
-sidebar_label: Docker 使用 JuiceFS
+title: Docker 使用 JuiceFS
 sidebar_position: 3
 slug: /juicefs_on_docker
 ---
-# 在 Docker 使用 JuiceFS
 
 将 JuiceFS 作为 Docker 持久化存储有以下几种常用方法：
 
diff --git a/docs/zh_cn/deployment/s3_gateway.md b/docs/zh_cn/deployment/s3_gateway.md
index 409a6617..d1f59b7b 100644
--- a/docs/zh_cn/deployment/s3_gateway.md
+++ b/docs/zh_cn/deployment/s3_gateway.md
@@ -1,11 +1,9 @@
 ---
-sidebar_label: 配置 JuiceFS S3 网关
+title: 配置 JuiceFS S3 网关
 sidebar_position: 6
 slug: /s3_gateway
 ---
 
-# 启用 JuiceFS 的 S3 网关
-
 JuiceFS 从 v0.11 开始引入了 S3 网关，这是一个通过 [MinIO S3 网关](https://docs.min.io/docs/minio-gateway-for-s3.html)实现的功能。它为 JuiceFS 中的文件提供跟 S3 兼容的 RESTful API，在不方便挂载的情况下能够用 s3cmd、AWS CLI、MinIO Client（mc）等工具管理 JuiceFS 上存储的文件。另外，S3 网关还提供了一个基于网页的文件管理器，用户使用浏览器就能对 JuiceFS 上的文件进行常规的增删管理。
 
 因为 JuiceFS 会将文件分块存储到底层的对象存储中，不能直接使用底层对象存储的接口和界面来直接访问文件，S3 网关提供了类似底层对象存储的访问能力，架构图如下：
diff --git a/docs/zh_cn/development/contributing_guide.md b/docs/zh_cn/development/contributing_guide.md
index 82a51173..2986f7a9 100644
--- a/docs/zh_cn/development/contributing_guide.md
+++ b/docs/zh_cn/development/contributing_guide.md
@@ -1,10 +1,8 @@
 ---
-sidebar_label: 贡献指南
+title: 贡献指南
 sidebar_position: 1
 ---
 
-# JuiceFS 贡献指南
-
 ## 基本准则
 
 - 在开始修复功能或错误之前，请先通过 GitHub、Slack 等渠道与我们沟通。此步骤的目的是确保没有其他人已经在处理它，如有必要，我们将要求您创建一个 GitHub issue。
diff --git a/docs/zh_cn/development/data_structures.md b/docs/zh_cn/development/data_structures.md
index 72e128fb..1641af8e 100644
--- a/docs/zh_cn/development/data_structures.md
+++ b/docs/zh_cn/development/data_structures.md
@@ -1,9 +1,8 @@
 ---
-sidebar_label: 内部实现
+title: 内部实现
 sidebar_position: 3
 slug: /internals
 ---
-# JuiceFS 内部实现
 
 ## 1. 简介
 
@@ -880,4 +879,4 @@ objects:
 
 :::note 备注
 若同时开启压缩和加密，原始数据会先压缩再加密后上传到对象存储。
-:::
\ No newline at end of file
+:::
diff --git a/docs/zh_cn/getting-started/installation.md b/docs/zh_cn/getting-started/installation.md
index 1cafb09e..4269d78b 100644
--- a/docs/zh_cn/getting-started/installation.md
+++ b/docs/zh_cn/getting-started/installation.md
@@ -1,12 +1,10 @@
 ---
-sidebar_label: 安装
+title: 安装
 sidebar_position: 1
 slug: /installation
 pagination_prev: introduction/comparison/juicefs_vs_s3ql
 ---
 
-# 安装
-
 JuiceFS 有良好的跨平台能力，支持在几乎所有主流架构的各类操作系统上运行，包括且不限于 Linux、macOS、Windows 等。
 
 JuiceFS 客户端只有一个二进制文件，你可以下载预编译的版本直接解压使用，也可以用源代码手动编译。
diff --git a/docs/zh_cn/guide/cache_management.md b/docs/zh_cn/guide/cache_management.md
index fd031470..97f448c6 100644
--- a/docs/zh_cn/guide/cache_management.md
+++ b/docs/zh_cn/guide/cache_management.md
@@ -1,9 +1,8 @@
 ---
-sidebar_label: 缓存
+title: 缓存
 sidebar_position: 3
 slug: /cache_management
 ---
-# 缓存
 
 对于一个由对象存储和数据库组合驱动的文件系统，缓存是本地客户端与远端服务之间高效交互的重要纽带。读写的数据可以提前或者异步载入缓存，再由客户端在后台与远端服务交互执行异步上传或预取数据。相比直接与远端服务交互，采用缓存技术可以大大降低存储操作的延时并提高数据吞吐量。
 
diff --git a/docs/zh_cn/guide/how_to_set_up_metadata_engine.md b/docs/zh_cn/guide/how_to_set_up_metadata_engine.md
index 1b5a29ea..3e33937c 100644
--- a/docs/zh_cn/guide/how_to_set_up_metadata_engine.md
+++ b/docs/zh_cn/guide/how_to_set_up_metadata_engine.md
@@ -1,5 +1,5 @@
 ---
-sidebar_label: 如何设置元数据引擎
+title: 如何设置元数据引擎
 sidebar_position: 1
 slug: /databases_for_metadata
 ---
@@ -7,8 +7,6 @@ slug: /databases_for_metadata
 import Tabs from '@theme/Tabs';
 import TabItem from '@theme/TabItem';
 
-# 如何设置元数据引擎
-
 :::tip 版本提示
 本文档使用的环境变量 `META_PASSWORD` 是 JuiceFS v1.0 新增功能，旧版客户端需要[升级](../administration/upgrade.md)后才能使用。
 :::
diff --git a/docs/zh_cn/guide/how_to_set_up_object_storage.md b/docs/zh_cn/guide/how_to_set_up_object_storage.md
index 588288fc..4b85c87b 100644
--- a/docs/zh_cn/guide/how_to_set_up_object_storage.md
+++ b/docs/zh_cn/guide/how_to_set_up_object_storage.md
@@ -1,5 +1,5 @@
 ---
-sidebar_label: 如何设置对象存储
+title: 如何设置对象存储
 sidebar_position: 2
 slug: /how_to_setup_object_storage
 ---
@@ -7,8 +7,6 @@ slug: /how_to_setup_object_storage
 import Tabs from '@theme/Tabs';
 import TabItem from '@theme/TabItem';
 
-# 如何设置对象存储
-
 通过阅读 [JuiceFS 的技术架构](../introduction/architecture.md)可以了解到，JuiceFS 是一个数据与元数据分离的分布式文件系统，以对象存储作为主要的数据存储，以 Redis、PostgreSQL、MySQL 等数据库作为元数据存储。
 
 ## 存储选项
diff --git a/docs/zh_cn/guide/mount_at_boot.md b/docs/zh_cn/guide/mount_at_boot.md
index 2d8498be..141a6301 100644
--- a/docs/zh_cn/guide/mount_at_boot.md
+++ b/docs/zh_cn/guide/mount_at_boot.md
@@ -1,13 +1,9 @@
 ---
-sidebar_label: 启动时自动挂载 JuiceFS
+title: 启动时自动挂载 JuiceFS
 sidebar_position: 2
 slug: /mount_juicefs_at_boot_time
 ---
 
-# 启动时自动挂载 JuiceFS
-
-这是一篇关于如何在启动时自动挂载 JuiceFS 的指南。
-
 ## Linux
 
 拷贝 `juicefs` 为 `/sbin/mount.juicefs`，然后按照下面的格式添加一行到 `/etc/fstab`：
diff --git a/docs/zh_cn/guide/quota.md b/docs/zh_cn/guide/quota.md
index e4be0ef2..b62d4b9e 100644
--- a/docs/zh_cn/guide/quota.md
+++ b/docs/zh_cn/guide/quota.md
@@ -1,8 +1,7 @@
 ---
-sidebar_label: 存储配额
+title: 存储配额
 sidebar_position: 5
 ---
-# 存储配额
 
 JuiceFS v0.14.2 开始支持文件系统级别的存储配额，该功能包括：
 
diff --git a/docs/zh_cn/introduction/comparison/juicefs_vs_s3ql.md b/docs/zh_cn/introduction/comparison/juicefs_vs_s3ql.md
index b426a032..cfd240e8 100644
--- a/docs/zh_cn/introduction/comparison/juicefs_vs_s3ql.md
+++ b/docs/zh_cn/introduction/comparison/juicefs_vs_s3ql.md
@@ -4,13 +4,18 @@ slug: /comparison/juicefs_vs_s3ql
 
 # JuiceFS 对比 S3QL
 
-与 JuiceFS 类似，[S3QL](https://github.com/s3ql/s3ql) 也是一款由对象存储和数据库组合驱动的开源网络文件系统，所有存入的数据会被分块后存储到亚马逊 S3、Backblaze B2、OpenStack Swift 等主流的对象存储中，相应的元数据会存储在数据库中。
+与 JuiceFS 类似，S3QL 也是一款由对象存储和数据库组合驱动的开源网络文件系统，所有存入的数据会被分块后存储到亚马逊 S3、Backblaze B2、OpenStack Swift 等主流的对象存储中，相应的元数据会存储在数据库中。
+
+:::note 注意
+S3QL 已于 2022 年 11 月 9 日停止维护，详见 [S3QL 仓库](https://github.com/s3ql/s3ql)。
+:::
 
 ## 共同点
 
 - 都是通过 FUSE 模块实现对标准 POSIX 文件系统接口的支持，从而可以将海量的云端存储挂载到本地，像本地存储一样使用。
 - 都能提供标准的文件系统功能：硬链接、符号链接、扩展属性、文件权限。
 - 都支持数据压缩和加密，但二者采用的算法各有不同。
+- 都支持元数据库备份，S3QL 自动备份 SQLite 数据库到对象存储。JuiceFS 每小时自动将元数据导出为 JSON 格式文件并备份到对象存储，便于恢复以及在各种元数据引擎间迁移。
 
 ## 不同点
 
@@ -18,10 +23,10 @@ slug: /comparison/juicefs_vs_s3ql
 - S3QL 没有分布式能力，**不支持**多主机同时挂载。JuiceFS 是典型的分布式文件系统，在使用基于网络的数据库时，支持多主机分布式挂载读写。
 - S3QL 在一个数据块几秒内未被访问时将其上传到对象存储。文件被关闭甚者 fsync 后其仍仅保证在系统内存中，节点故障时可能丢失数据。JuiceFS 确保了数据的高可靠性，在文件关闭时会将其同步上传到对象存储。
 - S3QL 提供数据去重，相同数据只存储一份，可以降低对象存储的用量，但也会加重系统的性能开销。相比之下，JuiceFS 更注重性能，对大规模数据去重代价过高，暂不提供该功能。
-- S3QL 提供了元数据远程同步备份功能，存有元数据的 SQLite 数据库会异步备份到对象存储。JuiceFS 以使用 Redis、MySQL 等网络数据库为主，并未直接提供 SQLite 数据库同步备份功能，但 JuiceFS 支持元数据的导入导出，以及各种存储后端的同步功能，用户可以很容易将元数据备份到对象存储，也支持在不同数据库之间迁移。
 
 |                       | **S3QL**           | **JuiceFS**                |
 | :-------------------- | :----------------- | :------------------------- |
+| 项目状态              | 停止维护              | 活跃开发                    |
 | 元数据引擎            | SQLite             | Redis、MySQL、SQLite、TiKV |
 | 存储引擎              | 对象存储、本地磁盘 | 对象存储、WebDAV、本地磁盘 |
 | 操作系统              | Unix-like          | Linux、macOS、Windows      |
diff --git a/docs/zh_cn/introduction/io_processing.md b/docs/zh_cn/introduction/io_processing.md
index 08ef41a4..240b1e2c 100644
--- a/docs/zh_cn/introduction/io_processing.md
+++ b/docs/zh_cn/introduction/io_processing.md
@@ -1,9 +1,8 @@
 ---
-sidebar_label: 读写请求处理流程
+title: 读写请求处理流程
 sidebar_position: 3
 slug: /internals/io_processing
 ---
-# 读写请求处理流程介绍
 
 ## 写入流程
 
diff --git a/docs/zh_cn/reference/command_reference.md b/docs/zh_cn/reference/command_reference.md
index d72e77e2..72fcb519 100644
--- a/docs/zh_cn/reference/command_reference.md
+++ b/docs/zh_cn/reference/command_reference.md
@@ -1,5 +1,5 @@
 ---
-sidebar_label: 命令参考
+title: 命令参考
 sidebar_position: 1
 slug: /command_reference
 ---
@@ -7,10 +7,6 @@ slug: /command_reference
 import Tabs from '@theme/Tabs';
 import TabItem from '@theme/TabItem';
 
-# JuiceFS 命令参考
-
-有许多命令可帮助您管理文件系统，该页面提供了有关这些命令的详细参考。
-
 ## 概览
 
 在终端输入 `juicefs` 并执行，你就会看到所有可用的命令。另外，你可以在每个命令后面添加 `-h/--help` 标记获得该命令的详细帮助信息。
@@ -326,7 +322,7 @@ consul 注册中心地址 (默认："127.0.0.1:8500")
 发送心跳的间隔 (秒);建议所有客户端使用相同的心跳值 (默认：12)
 
 `--upload-delay value`<br />
-数据上传到对象存储的延迟时间，支持秒分时精度，对应格式分别为 ("s", "m", "h")，默认为 0 秒
+数据上传到对象存储的延迟时间，支持秒分时精度，对应格式分别为 ("s", "m", "h")，默认为 0 秒。如果在等待的时间内数据被应用删除，则无需再上传到对象存储，既提升了性能也节省了成本，如果数据只是临时落盘，之后会迅速删除，考虑用该选项进行优化。
 
 `--no-bgjob`<br />
 禁用后台作业（清理、备份等）（默认：false）
diff --git a/docs/zh_cn/reference/fuse_mount_options.md b/docs/zh_cn/reference/fuse_mount_options.md
index 7d4fdad7..8480c8db 100644
--- a/docs/zh_cn/reference/fuse_mount_options.md
+++ b/docs/zh_cn/reference/fuse_mount_options.md
@@ -1,9 +1,8 @@
 ---
-sidebar_label: FUSE 挂载选项
+title: FUSE 挂载选项
 sidebar_position: 6
 slug: /fuse_mount_options
 ---
-# FUSE 挂载选项
 
 JuiceFS 文件系统为用户提供多种访问方式，FUSE 是其中较为常用的一种，即使用 `juicefs mount` 命令将文件系统挂载到本地的方式。用户可以根据需要添加 FUSE 支持的挂载选项，从而实现更细粒度的控制。
 
@@ -50,8 +49,8 @@ FUSE 支持[「writeback-cache 模式」](https://www.kernel.org/doc/Documentati
 
 ## debug
 
-该选项会将低层类库（go-fuse）的 Debug 信息输出到 `juicefs.log` 中，详情参考文档[故障诊断和分析](https://juicefs.com/docs/zh/community/fault_diagnosis_and_analysis)。
+该选项会将低层类库（go-fuse）的 Debug 信息输出到 `juicefs.log` 中。
 
 :::note 注意
-该选项会将低层类库（go-fuse）的 Debug 信息输出到 `juicefs.log` 中，需要注意的是，该选项与 JuiceFS 客户端的全局 `--debug` 选项不同，前者是输出 go-fuse 类库的调试信息，后者是输出 JucieFS 客户端的调试信息。详情参考文档[故障诊断和分析](https://juicefs.com/docs/zh/community/fault_diagnosis_and_analysis)。
+该选项会将低层类库（go-fuse）的 Debug 信息输出到 `juicefs.log` 中，需要注意的是，该选项与 JuiceFS 客户端的全局 `--debug` 选项不同，前者是输出 go-fuse 类库的调试信息，后者是输出 JucieFS 客户端的调试信息。详情参考文档[故障诊断和分析](./fault_diagnosis_and_analysis)。
 :::
\ No newline at end of file
diff --git a/docs/zh_cn/reference/p8s_metrics.md b/docs/zh_cn/reference/p8s_metrics.md
index 71b0e816..79bba8d0 100644
--- a/docs/zh_cn/reference/p8s_metrics.md
+++ b/docs/zh_cn/reference/p8s_metrics.md
@@ -1,11 +1,9 @@
 ---
-sidebar_label: JuiceFS 监控指标
+title: JuiceFS 监控指标
 sidebar_position: 2
 slug: /p8s_metrics
 ---
 
-# JuiceFS 监控指标
-
 :::tip 提示
 请查看[「监控」](../administration/monitoring.md)文档了解如何收集及展示 JuiceFS 监控指标
 :::
diff --git a/docs/zh_cn/reference/posix_compatibility.md b/docs/zh_cn/reference/posix_compatibility.md
index 3d55e760..ec1a1a6c 100644
--- a/docs/zh_cn/reference/posix_compatibility.md
+++ b/docs/zh_cn/reference/posix_compatibility.md
@@ -1,9 +1,8 @@
 ---
-sidebar_label: POSIX 兼容性
+title: POSIX 兼容性
 sidebar_position: 6
 slug: /posix_compatibility
 ---
-# POSIX 兼容性
 
 JuiceFS 借助于 pjdfstest 和 LTP 来验证其对 POSIX 的兼容性。
 
diff --git a/docs/zh_cn/security/metadata_automatic_backup.md b/docs/zh_cn/security/metadata_automatic_backup.md
deleted file mode 100644
index 57bf32a5..00000000
--- a/docs/zh_cn/security/metadata_automatic_backup.md
+++ /dev/null
@@ -1,7 +0,0 @@
----
-sidebar_label: 元数据自动备份
-sidebar_position: 3
----
-# 元数据自动备份
-
-请参考：[元数据自动备份](../administration/metadata_dump_load.md#自动备份)
\ No newline at end of file
diff --git a/docs/zh_cn/tutorials/aliyun.md b/docs/zh_cn/tutorials/aliyun.md
index a61c2077..21de4cc3 100644
--- a/docs/zh_cn/tutorials/aliyun.md
+++ b/docs/zh_cn/tutorials/aliyun.md
@@ -1,11 +1,9 @@
 ---
-sidebar_label: 在阿里云使用 JuiceFS
+title: 在阿里云使用 JuiceFS
 sidebar_position: 4
 slug: /clouds/aliyun
 ---
 
-# 在阿里云安装和使用 JuiceFS 存储
-
 如下图所示，JuiceFS 存储由数据库和对象存储共同驱动。存入 JuiceFS 的文件会按照一定的规则被拆分成固定大小的数据块存储在对象存储中，数据对应的元数据则会存储在数据库中。
 
 元数据完全独立存储，对文件的检索和处理并不会直接操作对象存储中的数据，而是先在数据库中操作元数据，只有当数据发生变化的时候，才会与对象存储交互。
diff --git a/docs/zh_cn/tutorials/aws.md b/docs/zh_cn/tutorials/aws.md
index 1b6a3e7b..17c603cb 100644
--- a/docs/zh_cn/tutorials/aws.md
+++ b/docs/zh_cn/tutorials/aws.md
@@ -1,11 +1,9 @@
 ---
-sidebar_label: 在 AWS 上使用 JuiceFS
+title: 在 AWS 上使用 JuiceFS
 sidebar_position: 6
 slug: /clouds/aws
 ---
 
-# 在亚马逊 AWS 安装和使用 JuiceFS 存储
-
 亚马逊 AWS 是全球领先的云计算平台，提供几乎所有类型的云计算服务。得益于 AWS 丰富的产品线，用户可以非常灵活的搭配选择 JuiceFS 组成部分。
 
 ## 准备
diff --git a/docs/zh_cn/tutorials/digitalocean.md b/docs/zh_cn/tutorials/digitalocean.md
index 57c205c6..e1cd3017 100644
--- a/docs/zh_cn/tutorials/digitalocean.md
+++ b/docs/zh_cn/tutorials/digitalocean.md
@@ -1,9 +1,8 @@
 ---
-sidebar_label: 在 DigitalOcean 使用 JuiceFS
+title: 在 DigitalOcean 使用 JuiceFS
 sidebar_position: 7
 slug: /clouds/digitalocean
 ---
-# 在 DigitalOcean 安装和使用 JuiceFS 存储
 
 JuiceFS 是面向云设计的，使用云平台开箱即用的存储和数据库服务，最快几分钟就能完成配置投入使用，本文以 DigitalOcean 平台为例，介绍如何在云计算平台上快速简单的安装和使用 JuiceFS。
 
diff --git a/docs/zh_cn/tutorials/juicefs_on_colab.md b/docs/zh_cn/tutorials/juicefs_on_colab.md
index 9a67920a..92a7ed8a 100644
--- a/docs/zh_cn/tutorials/juicefs_on_colab.md
+++ b/docs/zh_cn/tutorials/juicefs_on_colab.md
@@ -1,11 +1,9 @@
 ---
-sidebar_label: 在 Colab 上通过 Google CloudSQL 和 GCS 使用 JuiceFS
+title: 在 Colab 上通过 Google CloudSQL 和 GCS 使用 JuiceFS
 sidebar_position: 4
 slug: /juicefs_on_colab
 ---
 
-# 在 Colab 上通过 Google CloudSQL 和 GCS 使用 JuiceFS
-
 [Colaboratory](https://colab.research.google.com), 或者简称“Colab”, 是 Google Research 的产品，它允许任何人通过浏览器编写和执行 python 代码，特别适合机器学习、数据分析和教育。
 Colab 支持从 Google Drive 将文件上传到 Colab 实例或从 Colab 实例下载文件。然而在某些情况下，Google Drive 可能不太方便与 Colab 一起使用，在这种情况下，JuiceFS 是一个很有用的工具，因为他允许在 Colab 实例之间，或在 Colab 实例与本地或本地机器之间轻松的同步文件。[这里是一个使用了 juicefs 的 Colab 笔记本示例](https://colab.research.google.com/drive/1wA8vRwqiihXkI6ViDU8Ud868UeYtmCo5)
 
diff --git a/docs/zh_cn/tutorials/juicefs_on_k3s.md b/docs/zh_cn/tutorials/juicefs_on_k3s.md
index 6d9d9a11..92229526 100644
--- a/docs/zh_cn/tutorials/juicefs_on_k3s.md
+++ b/docs/zh_cn/tutorials/juicefs_on_k3s.md
@@ -1,5 +1,5 @@
 ---
-sidebar_label: 在 K3s 上使用 JuiceFS
+title: 在 K3s 上使用 JuiceFS
 sidebar_position: 1
 slug: /juicefs_on_k3s
 ---
diff --git a/docs/zh_cn/tutorials/juicefs_on_kubesphere.md b/docs/zh_cn/tutorials/juicefs_on_kubesphere.md
index 99208e16..aa860960 100644
--- a/docs/zh_cn/tutorials/juicefs_on_kubesphere.md
+++ b/docs/zh_cn/tutorials/juicefs_on_kubesphere.md
@@ -1,9 +1,8 @@
 ---
-sidebar_label: 在 KubeSphere 上使用 JuiceFS
+title: 在 KubeSphere 上使用 JuiceFS
 sidebar_position: 3
 slug: /juicefs_on_kubesphere
 ---
-# 在 KubeSphere 上使用 JuiceFS
 
 [KubeSphere](https://kubesphere.com.cn/) 是在 Kubernetes 之上构建的以应用为中心的多租户容器平台，提供全栈的 IT 自动化运维的能力，简化企业的 DevOps 工作流。
 
diff --git a/docs/zh_cn/tutorials/juicefs_on_rancher.md b/docs/zh_cn/tutorials/juicefs_on_rancher.md
index 390786f8..e9e6847f 100644
--- a/docs/zh_cn/tutorials/juicefs_on_rancher.md
+++ b/docs/zh_cn/tutorials/juicefs_on_rancher.md
@@ -1,9 +1,8 @@
 ---
-sidebar_label: 在 Rancher 上使用 JuiceFS
+title: 在 Rancher 上使用 JuiceFS
 sidebar_position: 2
 slug: /juicefs_on_rancher
 ---
-# 在 Rancher 上使用 JuiceFS
 
 简单来说，[Rancher](https://rancher.com/) 是一个企业级的 Kubernetes 集群管理工具，使用它可以非常轻松的在各种云计算平台上快速的完成 Kubernetes 集群的部署。
 
diff --git a/docs/zh_cn/tutorials/juicefs_on_wsl.md b/docs/zh_cn/tutorials/juicefs_on_wsl.md
index 39f03f33..3cc174fd 100644
--- a/docs/zh_cn/tutorials/juicefs_on_wsl.md
+++ b/docs/zh_cn/tutorials/juicefs_on_wsl.md
@@ -1,9 +1,7 @@
 ---
-sidebar_label: 在 WSL 中使用 JuiceFS
+title: 在 WSL 中使用 JuiceFS
 ---
 
-# 在 WSL 中使用 JuiceFS
-
 WSL 全称 Windows Subsystem for Linux，即适用于 Linux 的 Windows 子系统。它可以让你在 Windows 系统环境下运行大多数 GNU/Linux 原生命令、工具和程序，且不必像用虚拟机或双系统那样产生额外的硬件开销。
 
 ## 安装 WSL
diff --git a/docs/zh_cn/tutorials/qcloud.md b/docs/zh_cn/tutorials/qcloud.md
index 1e83a669..2569745a 100644
--- a/docs/zh_cn/tutorials/qcloud.md
+++ b/docs/zh_cn/tutorials/qcloud.md
@@ -1,11 +1,9 @@
 ---
-sidebar_label: 在腾讯云使用 JuiceFS
+title: 在腾讯云使用 JuiceFS
 sidebar_position: 5
 slug: /clouds/qcloud
 ---
 
-# 在腾讯云安装和使用 JuiceFS 存储
-
 如下图所示，JuiceFS 存储由数据库和对象存储共同驱动。存入 JuiceFS 的文件会按照一定的规则被拆分成固定大小的数据块存储在对象存储中，数据对应的元数据则会存储在数据库中。
 
 元数据完全独立存储，对文件的检索和处理并不会直接操作对象存储中的数据，而是先在数据库中操作元数据，只有当数据发生变化的时候，才会与对象存储交互。
diff --git a/pkg/meta/interface.go b/pkg/meta/interface.go
index 7a700d99..989394ad 100644
--- a/pkg/meta/interface.go
+++ b/pkg/meta/interface.go
@@ -42,10 +42,12 @@ const (
 	CompactChunk = 1001
 	// Rmr is a message to remove a directory recursively.
 	Rmr = 1002
-	// Info is a message to get the internal info for file or directory.
-	Info = 1003
+	// LegacyInfo is a message to get the internal info for file or directory.
+	LegacyInfo = 1003
 	// FillCache is a message to build cache for target directories/files
 	FillCache = 1004
+	// InfoV2 is a message to get the internal info for file or directory.
+	InfoV2 = 1005
 )
 
 const (
@@ -267,6 +269,8 @@ type Meta interface {
 	GetSession(sid uint64, detail bool) (*Session, error)
 	// ListSessions returns all client sessions.
 	ListSessions() ([]*Session, error)
+	// ListLocks returns all locks of a inode.
+	ListLocks(ctx context.Context, inode Ino) ([]PLockItem, []FLockItem, error)
 	// Statistic scan metadata by visitors.
 	Statistic(ctx context.Context, slicesDelayedScan func(Slice) error, fileDelayedScan func(ino Ino, size uint64) error) error
 	// CleanStaleSessions cleans up sessions not active for more than 5 minutes
diff --git a/pkg/meta/redis_lock.go b/pkg/meta/redis_lock.go
index bb294ee8..2d828404 100644
--- a/pkg/meta/redis_lock.go
+++ b/pkg/meta/redis_lock.go
@@ -20,6 +20,7 @@
 package meta
 
 import (
+	"context"
 	"strconv"
 	"strings"
 	"syscall"
@@ -206,3 +207,38 @@ func (r *redisMeta) Setlk(ctx Context, inode Ino, owner uint64, block bool, ltyp
 	}
 	return errno(err)
 }
+
+func (r *redisMeta) ListLocks(ctx context.Context, inode Ino) ([]PLockItem, []FLockItem, error) {
+	fKey := r.flockKey(inode)
+	pKey := r.plockKey(inode)
+
+	rawFLocks, err := r.rdb.HGetAll(ctx, fKey).Result()
+	if err != nil {
+		return nil, nil, err
+	}
+	flocks := make([]FLockItem, 0, len(rawFLocks))
+	for k, v := range rawFLocks {
+		owner, err := parseOwnerKey(k)
+		if err != nil {
+			return nil, nil, err
+		}
+		flocks = append(flocks, FLockItem{*owner, v})
+	}
+
+	rawPLocks, err := r.rdb.HGetAll(ctx, pKey).Result()
+	if err != nil {
+		return nil, nil, err
+	}
+	plocks := make([]PLockItem, 0)
+	for k, d := range rawPLocks {
+		owner, err := parseOwnerKey(k)
+		if err != nil {
+			return nil, nil, err
+		}
+		ls := loadLocks([]byte(d))
+		for _, l := range ls {
+			plocks = append(plocks, PLockItem{*owner, l})
+		}
+	}
+	return plocks, flocks, nil
+}
diff --git a/pkg/meta/sql.go b/pkg/meta/sql.go
index d57e1e3b..ddab1735 100644
--- a/pkg/meta/sql.go
+++ b/pkg/meta/sql.go
@@ -649,11 +649,13 @@ func (m *dbMeta) txn(f func(s *xorm.Session) error, inodes ...Ino) error {
 	}
 	start := time.Now()
 	defer func() { m.txDist.Observe(time.Since(start).Seconds()) }()
+
+	if m.db.DriverName() == "sqlite3" {
+		// sqlite only allow one writer at a time
+		inodes = []Ino{1}
+	}
+
 	if len(inodes) > 0 {
-		if m.db.DriverName() == "sqlite3" {
-			// sqlite only allow one writer at a time
-			inodes[0] = 1
-		}
 		m.txLock(uint(inodes[0]))
 		defer m.txUnlock(uint(inodes[0]))
 	}
diff --git a/pkg/meta/sql_lock.go b/pkg/meta/sql_lock.go
index d4543501..f0767f84 100644
--- a/pkg/meta/sql_lock.go
+++ b/pkg/meta/sql_lock.go
@@ -21,6 +21,7 @@ package meta
 
 import (
 	"bytes"
+	"context"
 	"fmt"
 	"syscall"
 	"time"
@@ -247,3 +248,29 @@ func (m *dbMeta) Setlk(ctx Context, inode Ino, owner_ uint64, block bool, ltype
 	}
 	return err
 }
+
+func (r *dbMeta) ListLocks(ctx context.Context, inode Ino) ([]PLockItem, []FLockItem, error) {
+	var fs []flock
+	if err := r.db.Find(&fs, &flock{Inode: inode}); err != nil {
+		return nil, nil, err
+	}
+
+	flocks := make([]FLockItem, 0, len(fs))
+	for _, f := range fs {
+		flocks = append(flocks, FLockItem{ownerKey{f.Sid, uint64(f.Owner)}, string(f.Ltype)})
+	}
+
+	var ps []plock
+	if err := r.db.Find(&ps, &plock{Inode: inode}); err != nil {
+		return nil, nil, err
+	}
+
+	plocks := make([]PLockItem, 0)
+	for _, p := range ps {
+		ls := loadLocks(p.Records)
+		for _, l := range ls {
+			plocks = append(plocks, PLockItem{ownerKey{p.Sid, uint64(p.Owner)}, l})
+		}
+	}
+	return plocks, flocks, nil
+}
diff --git a/pkg/meta/tkv_lock.go b/pkg/meta/tkv_lock.go
index 90507878..d3376b16 100644
--- a/pkg/meta/tkv_lock.go
+++ b/pkg/meta/tkv_lock.go
@@ -17,6 +17,7 @@
 package meta
 
 import (
+	"context"
 	"syscall"
 	"time"
 
@@ -221,3 +222,29 @@ func (m *kvMeta) Setlk(ctx Context, inode Ino, owner uint64, block bool, ltype u
 	}
 	return errno(err)
 }
+
+func (m *kvMeta) ListLocks(ctx context.Context, inode Ino) ([]PLockItem, []FLockItem, error) {
+	fKey := m.flockKey(inode)
+	pKey := m.plockKey(inode)
+
+	var flocks []FLockItem
+	var plocks []PLockItem
+	err := m.txn(func(tx kvTxn) error {
+		fv := tx.get(fKey)
+		fs := unmarshalFlock(fv)
+		for k, t := range fs {
+			flocks = append(flocks, FLockItem{ownerKey{k.sid, k.owner}, string(t)})
+		}
+
+		pv := tx.get(pKey)
+		owners := unmarshalPlock(pv)
+		for k, records := range owners {
+			ls := loadLocks(records)
+			for _, l := range ls {
+				plocks = append(plocks, PLockItem{ownerKey{k.sid, k.owner}, l})
+			}
+		}
+		return nil
+	})
+	return plocks, flocks, err
+}
diff --git a/pkg/meta/utils.go b/pkg/meta/utils.go
index 86f910b7..d77fbc55 100644
--- a/pkg/meta/utils.go
+++ b/pkg/meta/utils.go
@@ -18,9 +18,11 @@ package meta
 
 import (
 	"bytes"
+	"fmt"
 	"net/url"
 	"runtime/debug"
 	"sort"
+	"strconv"
 	"strings"
 	"sync"
 	"sync/atomic"
@@ -134,6 +136,37 @@ type plockRecord struct {
 	End   uint64
 }
 
+type ownerKey struct {
+	Sid   uint64
+	Owner uint64
+}
+
+type PLockItem struct {
+	ownerKey
+	plockRecord
+}
+
+type FLockItem struct {
+	ownerKey
+	Type string
+}
+
+func parseOwnerKey(key string) (*ownerKey, error) {
+	pair := strings.Split(key, "_")
+	if len(pair) != 2 {
+		return nil, fmt.Errorf("invalid owner key: %s", key)
+	}
+	sid, err := strconv.ParseUint(pair[0], 10, 64)
+	if err != nil {
+		return nil, err
+	}
+	owner, err := strconv.ParseUint(pair[1], 16, 64)
+	if err != nil {
+		return nil, err
+	}
+	return &ownerKey{sid, owner}, nil
+}
+
 func loadLocks(d []byte) []plockRecord {
 	var ls []plockRecord
 	rb := utils.FromBuffer(d)
diff --git a/pkg/vfs/internal.go b/pkg/vfs/internal.go
index f445a75d..a2487d0e 100644
--- a/pkg/vfs/internal.go
+++ b/pkg/vfs/internal.go
@@ -18,7 +18,9 @@ package vfs
 
 import (
 	"bytes"
+	"encoding/json"
 	"fmt"
+	"io"
 	"os"
 	"strconv"
 	"strings"
@@ -247,6 +249,59 @@ func (v *VFS) caclObjects(id uint64, size, offset, length uint32) []*obj {
 	return objs
 }
 
+type InfoResponse struct {
+	Ino     Ino
+	Failed  bool
+	Reason  string
+	Summary meta.Summary
+	Paths   []string
+	Chunks  []*chunkSlice
+	Objects []*chunkObj
+	PLocks  []meta.PLockItem
+	FLocks  []meta.FLockItem
+}
+
+type chunkSlice struct {
+	ChunkIndex uint64
+	meta.Slice
+}
+
+type chunkObj struct {
+	ChunkIndex     uint64
+	Key            string
+	Size, Off, Len uint32
+}
+
+func (r *InfoResponse) Encode() []byte {
+	resp, _ := json.Marshal(r)
+	buffer := utils.NewBuffer(4 + uint32(len(resp)))
+	buffer.Put32(uint32(len(resp)))
+	buffer.Put(resp)
+	return buffer.Bytes()
+}
+
+func (r *InfoResponse) Decode(reader io.Reader) error {
+	sizeBuf := make([]byte, 4)
+	n := 0
+	for n == 0 {
+		i, err := reader.Read(sizeBuf[n:])
+		if err != nil && err != io.EOF {
+			return err
+		}
+		if i == 1 && sizeBuf[0] == byte(syscall.EINVAL&0xff) {
+			return syscall.EINVAL
+		}
+		n += i
+	}
+
+	size := utils.ReadBuffer(sizeBuf).Get32()
+	respBuf := make([]byte, size)
+	if _, err := io.ReadFull(reader, respBuf); err != nil {
+		return err
+	}
+	return json.Unmarshal(respBuf, r)
+}
+
 func (v *VFS) handleInternalMsg(ctx meta.Context, cmd uint32, r *utils.Buffer, data *[]byte) {
 	switch cmd {
 	case meta.Rmr:
@@ -269,7 +324,7 @@ func (v *VFS) handleInternalMsg(ctx meta.Context, cmd uint32, r *utils.Buffer, d
 			}
 		}
 		*data = append(*data, uint8(st))
-	case meta.Info:
+	case meta.LegacyInfo:
 		var summary meta.Summary
 		inode := Ino(r.Get64())
 		var recursive uint8 = 1
@@ -329,6 +384,51 @@ func (v *VFS) handleInternalMsg(ctx meta.Context, cmd uint32, r *utils.Buffer, d
 		}
 		wb.Put32(uint32(w.Len()))
 		*data = append(*data, append(wb.Bytes(), w.Bytes()...)...)
+	case meta.InfoV2:
+		inode := Ino(r.Get64())
+		info := &InfoResponse{
+			Ino: inode,
+		}
+
+		var recursive uint8 = 1
+		if r.HasMore() {
+			recursive = r.Get8()
+		}
+		var raw bool
+		if r.HasMore() {
+			raw = r.Get8() != 0
+		}
+
+		r := meta.GetSummary(v.Meta, ctx, inode, &info.Summary, recursive != 0)
+		if r != 0 {
+			info.Failed = true
+			info.Reason = r.Error()
+			*data = append(*data, info.Encode()...)
+			return
+		}
+
+		info.Paths = v.Meta.GetPaths(ctx, inode)
+		if info.Summary.Files == 1 && info.Summary.Dirs == 0 {
+			for indx := uint64(0); indx*meta.ChunkSize < info.Summary.Length; indx++ {
+				var cs []meta.Slice
+				_ = v.Meta.Read(ctx, inode, uint32(indx), &cs)
+				for _, c := range cs {
+					if raw {
+						info.Chunks = append(info.Chunks, &chunkSlice{indx, c})
+					} else {
+						for _, o := range v.caclObjects(c.Id, c.Size, c.Off, c.Len) {
+							info.Objects = append(info.Objects, &chunkObj{indx, o.key, o.size, o.off, o.len})
+						}
+					}
+				}
+			}
+		}
+
+		var err error
+		if info.PLocks, info.FLocks, err = v.Meta.ListLocks(ctx, inode); err != nil {
+			info.Reason = err.Error()
+		}
+		*data = append(*data, info.Encode()...)
 	case meta.FillCache:
 		paths := strings.Split(string(r.Get(int(r.Get32()))), "\n")
 		concurrent := r.Get16()
diff --git a/sdk/java/libjfs/main.go b/sdk/java/libjfs/main.go
index 612981fb..5ed67a26 100644
--- a/sdk/java/libjfs/main.go
+++ b/sdk/java/libjfs/main.go
@@ -305,6 +305,7 @@ func push2Gateway(pushGatewayAddr, pushAuth string, pushInterVal time.Duration,
 			pusher.BasicAuth(parts[0], parts[1])
 		}
 	}
+	pusher.Client(&http.Client{Timeout: 2 * time.Second})
 	pushers = append(pushers, pusher)
 
 	pOnce.Do(func() {


Test patch:
diff --git a/.github/scripts/how_to_use_mutate_test.md b/.github/scripts/how_to_use_mutate_test.md
index 9406fc3c..f46e5c9e 100644
--- a/.github/scripts/how_to_use_mutate_test.md
+++ b/.github/scripts/how_to_use_mutate_test.md
@@ -38,3 +38,7 @@ For example:
 	if err != nil { //skip mutate
 		return "", fmt.Errorf("failed to execute command `lsb_release`: %s", err)
 	}
+
+# how to customize mutate test job in parallel?
+if the mutants of the target source file is more than 200, we will use 4 github jobs to run it. otherwise we will use 1 job to run.
+you can customize it in your test file with adding "//mutate_test_job_number: number", eg: //mutate_test_job_number: 8
\ No newline at end of file
diff --git a/.github/scripts/mutest.sh b/.github/scripts/mutest.sh
index 3c1b010f..c5a50c2d 100755
--- a/.github/scripts/mutest.sh
+++ b/.github/scripts/mutest.sh
@@ -41,7 +41,7 @@ fi
 python3 .github/scripts/check_coverage.py
 
 if [ $? -ne 0 ]; then
-	echo "mutate is out of code coverage", $COVERAGE_FILE, $MUTATE_CHANGED
+	echo "mutate is out of code coverage", $MUTATE_CHANGED
 	exit 101
 fi
 
@@ -59,7 +59,8 @@ fi
 
 mv $MUTATE_ORIGINAL $MUTATE_ORIGINAL.tmp
 cp $MUTATE_CHANGED $MUTATE_ORIGINAL
-
+echo "------------------------------------------------------------------------"
+echo "Start unit test with: $MUTATE_CHANGED"
 go test ./$PACKAGE_PATH/...  -run "$test_cases" -v -cover -count=1 -timeout=5m 
 # GOMUTESTING_TEST=$(go test -timeout $(printf '%ds' $MUTATE_TIMEOUT) $MUTATE_PACKAGE$TEST_RECURSIVE 2>&1)
 export GOMUTESTING_RESULT=$?
@@ -78,9 +79,7 @@ case $GOMUTESTING_RESULT in
 	exit 1
 	;;
 1) # tests failed -> PASS
-	if [ "$MUTATE_DEBUG" = true ] ; then
-		echo "$GOMUTESTING_DIFF"
-	fi
+	echo "$GOMUTESTING_DIFF"
 	echo "tests failed -> PASS"
 	exit 0
 	;;
diff --git a/.github/scripts/mutesting.py b/.github/scripts/mutesting.py
new file mode 100644
index 00000000..eea3d4c7
--- /dev/null
+++ b/.github/scripts/mutesting.py
@@ -0,0 +1,59 @@
+#!/usr/bin/env python
+# -*- encoding: utf-8 -*-
+import glob
+import os
+import sys
+from tkinter import Tcl
+
+def do_mutate_test(mutation_dir, index, total):
+    print(f'mutation dir is {mutation_dir}, inde is {index}, total is {total}', file=sys.stderr)
+    # os.system(f'ls -l {mutation_dir}')
+    list_of_files = Tcl().call('lsort', '-dict', glob.glob(mutation_dir + '/*.go.*') )
+    if len(list_of_files) > 0 and 'original' in list_of_files[-1]:
+        list_of_files = list_of_files[:-1]
+    # print('\n'.join(list_of_files), file=sys.stderr)
+    stats = {'passed':0, 'failed':0, 'compile_error':0, 'out_of_coverage':0, 'skip_by_comment':0, 'others':0, 'total':0}
+    count = int(len(list_of_files)/total) + 1
+    start = index*count
+    end = start + count
+    print(f'count:{count}, start:{start}, end:{end}', file=sys.stderr)
+    if end > len(list_of_files):
+        end = len(list_of_files)
+    for changed_file in list_of_files[start:end]:
+        # timestamp_str = time.strftime(  '%m/%d/%Y :: %H:%M:%S',
+        #                             time.gmtime(os.path.getmtime(changed_file))) 
+        # print(timestamp_str, ' -->', changed_file) 
+        os.environ['MUTATE_CHANGED'] = changed_file
+        ret = os.system('.github/scripts/mutest.sh') >> 8
+        if ret == 0:
+            stats['passed'] += 1
+        elif ret == 1:
+            stats['failed'] += 1
+        elif ret == 2:
+            stats['compile_error'] += 1
+        elif ret == 101:
+            stats['out_of_coverage'] += 1
+        elif ret == 102:
+            stats['skip_by_comment'] += 1
+        else:
+            stats['others'] += 1
+        stats['total'] += 1
+    if stats['passed'] + stats['failed'] == 0:
+        stats['score'] = 1.0
+    else:
+        stats['score'] = stats['passed'] / (stats['passed'] + stats['failed'])
+    return stats
+
+if __name__ == '__main__':
+    os.environ['MUTATE_PACKAGE'] = ''
+    mutation_dir = os.path.join(os.environ['MUTATION_DIR'], os.environ['PACKAGE_PATH'])
+    print(f'mutation dir is {mutation_dir}', file=sys.stderr)
+    original_file = os.environ['MUTATE_ORIGINAL']
+    print(f'original file is {original_file}', file=sys.stderr)
+    if not os.environ['JOB_INDEX']:
+        index = 0
+    else:
+        index = int(os.environ['JOB_INDEX'])-1
+    total = int(os.environ['JOB_TOTAL'])
+    stats = do_mutate_test(mutation_dir, index, total)
+    print(stats)
\ No newline at end of file
diff --git a/.github/workflows/integrationtests.yml b/.github/workflows/integrationtests.yml
index d6a7e030..f30d6192 100644
--- a/.github/workflows/integrationtests.yml
+++ b/.github/workflows/integrationtests.yml
@@ -17,6 +17,12 @@ on:
       - '**.md'
       - '.github/**'
   workflow_dispatch:
+    inputs:
+      debug:
+        type: boolean
+        description: "Run the build with tmate debugging enabled"
+        required: false
+        default: false
 
 jobs:
   integrationtests:
@@ -104,5 +110,6 @@ jobs:
 
 
       - name: Setup upterm session
-        if: ${{ failure() }}
+        if: ${{ failure() && github.event_name == 'workflow_dispatch' && github.event.inputs.debug == 'true' }}
+        timeout-minutes: 60
         uses: lhotari/action-upterm@v1
diff --git a/.github/workflows/mutate-test.yml b/.github/workflows/mutate-test.yml
index fc3dfe2d..04a45245 100644
--- a/.github/workflows/mutate-test.yml
+++ b/.github/workflows/mutate-test.yml
@@ -13,6 +13,11 @@ on:
         description: "the go test file relative path you want to mutate, eg cmd/meta/xattr_test.go"
         required: true
         default: ""  
+      job_total:
+        type: string
+        description: "number of job to run mutation test"
+        required: true
+        default: "1"
       debug:
         type: boolean
         description: "Run the build with tmate debugging enabled"
@@ -28,36 +33,72 @@ jobs:
         with:
           fetch-depth: 0
 
+      - name: Set up Go
+        uses: actions/setup-go@v2
+        with:
+          go-version: '1.18.x'
+
+      - name: install go-mutesting
+        run: |
+          go install github.com/zimmski/go-mutesting/cmd/go-mutesting@latest
+
       - id: set-matrix
         run: |
+          sudo apt-get install jq
           if [ "${{github.event_name}}" == "pull_request"  ]; then
-            changed_file_str=$(git diff --name-only --diff-filter=ACMRT ${{ github.event.pull_request.base.sha }} ${{ github.sha }} | grep _test.go$ | xargs)
+            echo github.event.pull_request.base.sha is ${{github.event.pull_request.base.sha}}
+            echo github.event.pull_request.head.sha is ${{github.event.pull_request.head.sha}}
+            echo github.sha is ${{ github.sha }}
+            changed_file_str=$(git diff --name-only --diff-filter=ACMRT ${{ github.event.pull_request.base.sha }} ${{ github.event.pull_request.head.sha }} | grep _test.go$ | xargs)
             echo "added or changed test files: $changed_file_str"
             changed_file_array=($changed_file_str)
-            test_files=""
-            for file_name in "${changed_file_array[@]}"
+            declare -a Jobs=();
+            for test_file_name in "${changed_file_array[@]}"
             do
-              if [[ "$file_name" == *_test.go ]]; then
-                test_files="\"$file_name\",$test_files"
+              echo "test_file_name is $test_file_name"
+              source_file_name=${test_file_name%"_test.go"}.go
+              echo "source_file_name is :" $source_file_name
+              black_list_file=black.list
+              TEST_FILE_NAME="$test_file_name" BLACK_LIST_FILE=$black_list_file python3 .github/scripts/parse_black_list.py 
+              echo "black list checksum: "
+              cat $black_list_file
+              total_count=$(go-mutesting $source_file_name --debug --no-exec --blacklist $black_list_file| grep "Save mutation into" | wc -l)
+              echo "total_count is $total_count"
+              job_total=$(TEST_FILE_NAME=$test_file_name python3 .github/scripts/parse_job_total.py)
+              echo "job_total specified: $job_total"
+              if [ $job_total -eq 0 ]; then
+                if [ $total_count -gt 200 ]; then
+                  job_total=4
+                else
+                  job_total=1
+                fi
               fi
+              echo "job_total: $job_total"
+              for i in `seq 1 $job_total` 
+              do
+                Jobs=("${Jobs[@]}" "$test_file_name-$i-$job_total")
+              done
             done
-            echo "test_files: $test_files"
-            if [ -n "$test_files" ]; then 
-              echo "test_files is not empty"
-              # echo '::set-output name=test_file_matrix::["cmd/meta/xattr_test.go", "cmd/meta/utils_test.go"]'
-              echo "::set-output name=test_file_matrix::[$test_files]"
-            else
-              echo "test_files is empty"
-              exit 1
-            fi
+            value=`printf '%s\n' "${Jobs[@]}" | jq -R . | jq -cs .`
+            echo "value: $value"
+            echo "matrix=$value" >> $GITHUB_OUTPUT
           elif [ "${{github.event_name}}" == "workflow_dispatch"  ]; then
-            test_file="\"${{github.event.inputs.test_file}}\""
-            echo "test file is $test_file"
-            echo "::set-output name=test_file_matrix::[$test_file]"
+            test_file_name=${{github.event.inputs.test_file}}
+            echo "test file is $test_file_name"
+            job_total=${{github.event.inputs.job_total}}
+            echo "job_total is $job_total"
+            declare -a Jobs=();
+            for i in `seq 1 $job_total` 
+              do
+                Jobs=("${Jobs[@]}" "$test_file_name-$i-$job_total")
+              done
+            value=`printf '%s\n' "${Jobs[@]}" | jq -R . | jq -cs .`
+            echo "value: $value"
+            echo "matrix=$value" >> $GITHUB_OUTPUT
           fi
 
     outputs:
-      test_file: ${{ steps.set-matrix.outputs.test_file_matrix }}
+      matrix: ${{ steps.set-matrix.outputs.matrix }}
 
   mutate-test:
     name: ${{matrix.test_file}}
@@ -65,7 +106,7 @@ jobs:
     strategy:
       fail-fast: false
       matrix:
-        test_file: ${{ fromJson(needs.build-matrix.outputs.test_file) }}
+        test_file: ${{ fromJson(needs.build-matrix.outputs.matrix) }}
     runs-on: ubuntu-latest
     permissions:
       pull-requests: write
@@ -79,6 +120,14 @@ jobs:
           github_token: ${{ secrets.GITHUB_TOKEN }}
           job_name: ${{matrix.test_file}}
 
+      - name: Set Current Job Log URL to Env
+        run: |
+          job_url_id=$(echo job_url_${{matrix.test_file}} | sed -e 's:/:_:g' | sed -e 's:\.:_:g' | sed -e 's:-:_:g' )
+          echo "job_url_id is $job_url_id"
+          echo "job url is : ${{ steps.jobs.outputs.html_url }}"
+          echo "$job_url_id=${{ steps.jobs.outputs.html_url }}" >> $GITHUB_OUTPUT
+          echo $GITHUB_OUTPUT
+          
       - name: Build
         uses: ./.github/actions/build
 
@@ -92,6 +141,7 @@ jobs:
           sudo apt install -y redis-server
           sudo apt install -y libacl1-dev
           sudo apt install -y attr
+          sudo apt install python3-tk
           sudo mkdir -p /home/travis/.m2/
 
       - name: Download Zip
@@ -107,7 +157,6 @@ jobs:
           cd  /home/travis/.m2/litmus-0.13/ && ./configure && make && cd -
           tar -zxf /home/travis/.m2/etcd-v3.5.2-linux-amd64.tar.gz -C /home/travis/.m2/
 
-
       - name: Prepare Database
         timeout-minutes: 10
         run: |
@@ -143,13 +192,14 @@ jobs:
           for i in {2222,3306,5432,8020,9000,9007,4500} ; do echo "lsof port:"$i;sudo lsof -i :$i;if [ $? != 0 ];then sleep 5; else continue; fi;sudo lsof -i :$i; if [ $? != 0 ];then echo "service not ready, port:"$i; exit 1;fi; done
           sudo lsof -i :2379;if [ $? != 0 ];then echo "tikv is not ready";cat output.log;exit 1; fi
 
-
       - name: run mutate test
         # timeout-minutes: 120
         run: |
           sudo chmod 777 /var/jfsCache
-          test_file=${{matrix.test_file}}
-          echo "test file is :" $test_file
+          test_file=$(echo ${{matrix.test_file}} | awk -F'-' '{print $1}')
+          job_index=$(echo ${{matrix.test_file}} | awk -F'-' '{print $2}')
+          job_total=$(echo ${{matrix.test_file}} | awk -F'-' '{print $3}')
+          echo "test file is :$test_file, job_index is $job_index, job_total is $job_total"
           if [ -z "$test_file" ]; then 
             echo "test file is empty, will not run mutate test"
             exit 0
@@ -179,11 +229,25 @@ jobs:
           TEST_FILE_NAME="$test_file" BLACK_LIST_FILE=$black_list_file python3 .github/scripts/parse_black_list.py 
           echo "black list checksum: "
           cat $black_list_file
-          log_file=$(basename ${{matrix.test_file}}).log
-          COVERAGE_FILE=mutest-cov.out TEST_FILE_NAME="$test_file" PACKAGE_PATH="$package_path" go-mutesting $source_file --debug --exec=.github/scripts/mutest.sh  --do-not-remove-tmp-folder --blacklist $black_list_file | tee -a $log_file
-          mutate_report=`LOG_FILE=$log_file python3 .github/scripts/parse_mutate_log.py`
-          echo "mutate_report is $mutate_report"
+
+          go-mutesting $source_file --debug --no-exec --do-not-remove-tmp-folder  --blacklist $black_list_file | tee -a mutate.log
+          mutation_dir=$(cat mutate.log | grep "Save mutations into" | awk -F' ' '{print $4}' | sed -e 's:"::g')
+          echo "mutation dir is $mutation_dir"
+          JOB_INDEX=$job_index JOB_TOTAL=$job_total MUTATE_ORIGINAL=$source_file MUTATION_DIR=$mutation_dir COVERAGE_FILE=mutest-cov.out TEST_FILE_NAME="$test_file" PACKAGE_PATH="$package_path" python3 .github/scripts/mutesting.py | tee -a mutesting.log
+          if [ $? != 0 ]; then echo "run mutesting.py failed" && exit 1; fi
+          mutate_report=$(tail -1 mutesting.log)
+          echo "mutate_report: $mutate_report"
           echo "mutate_report=$mutate_report" >> $GITHUB_ENV
+          
+          # report_id=$(echo mutate_report_${{matrix.test_file}} | sed -e 's:/:_:g' | sed -e 's:\.:_:g' | sed -e 's:-:_:g' )
+          # echo "report_id is $report_id"
+          # echo "$report_id=$mutate_report" >> $GITHUB_ENV
+
+          # log_file=$(basename ${{matrix.test_file}}).log
+          # COVERAGE_FILE=mutest-cov.out TEST_FILE_NAME="$test_file" PACKAGE_PATH="$package_path" go-mutesting $source_file --debug --exec=.github/scripts/mutest.sh  --do-not-remove-tmp-folder --blacklist $black_list_file | tee -a $log_file
+          # mutate_report=`LOG_FILE=$log_file python3 .github/scripts/parse_mutate_log.py`
+          # echo "mutate_report is $mutate_report"
+          # echo "mutate_report=$mutate_report" >> $GITHUB_ENV
 
       - uses: mshick/add-pr-comment@v2
         with:
@@ -194,6 +258,32 @@ jobs:
             Report detail: ${{ steps.jobs.outputs.html_url }}
             Usage: https://github.com/juicedata/juicefs/blob/main/.github/scripts/how_to_use_mutate_test.md  
 
+      - name: Setup upterm session
+        if: ${{ failure() && github.event_name == 'workflow_dispatch' && github.event.inputs.debug == 'true' }}
+        timeout-minutes: 60
+        uses: lhotari/action-upterm@v1
+
+  success-all-test:
+    runs-on: ubuntu-latest
+    needs: [build-matrix, mutate-test]
+    if: always()
+    steps:
+      - uses: technote-space/workflow-conclusion-action@v2
+      # - name: print
+      #   run: |
+      #     files=($(echo ${{ needs.build-matrix.outputs.matrix }} | sed -e 's:"::g' | sed -e 's:\[::g' | sed -e 's:\]::g' | sed -e 's:,: :g'))
+      #     echo ${files[@]}
+      #     echo "job url is ${{needs.mutate-test.outputs.job_url_pkg_meta_config_test_go_1}}"
+      #     for file in ${files[@]}; 
+      #     do
+      #       echo "file is $file"
+      #       job_url_id=$(echo job_url_$file | sed -e 's:/:_:g' | sed -e 's:\.:_:g' | sed -e 's:-:_:g' )
+      #       echo "job_url_id is $job_url_id"
+      #     done
+      - name: Check Failure
+        if: env.WORKFLOW_CONCLUSION == 'failure'
+        run: exit 1
+
       - name: Send Slack Notification
         if: ${{ failure() && github.event_name != 'workflow_dispatch' }}
         uses: juicedata/slack-notify-action@main
@@ -201,7 +291,7 @@ jobs:
           channel-id: "${{ secrets.SLACK_CHANNEL_ID_FOR_PR_CHECK_NOTIFY }}"
           slack_bot_token: "${{ secrets.SLACK_BOT_TOKEN }}"
 
-      - name: Setup upterm session
-        if: ${{ failure() && github.event_name == 'workflow_dispatch' && github.event.inputs.debug }}
-        timeout-minutes: 60
-        uses: lhotari/action-upterm@v1
+      - name: Success
+        if: ${{ success() }}
+        run: echo "All Done"
+
diff --git a/.github/workflows/pjdfstest.yml b/.github/workflows/pjdfstest.yml
index c834ac6e..5a0472bb 100644
--- a/.github/workflows/pjdfstest.yml
+++ b/.github/workflows/pjdfstest.yml
@@ -140,6 +140,6 @@ jobs:
           slack_bot_token: "${{ secrets.SLACK_BOT_TOKEN }}"  
 
       - name: Setup upterm session
-        if: ${{ failure() && github.event_name == 'workflow_dispatch' && github.event.inputs.debug }}
+        if: ${{ failure() && github.event_name == 'workflow_dispatch' && github.event.inputs.debug == 'true' }}
         timeout-minutes: 60
         uses: lhotari/action-upterm@v1
\ No newline at end of file
diff --git a/.github/workflows/sdktest.yml b/.github/workflows/sdktest.yml
index d6608954..0cf7fa28 100644
--- a/.github/workflows/sdktest.yml
+++ b/.github/workflows/sdktest.yml
@@ -18,7 +18,12 @@ on:
       - '**.md'
       - '.github/**'
   workflow_dispatch:
-
+    inputs:
+      debug:
+        type: boolean
+        description: "Run the build with tmate debugging enabled"
+        required: false
+        default: false
 
 jobs:
   sdktest:
@@ -63,5 +68,6 @@ jobs:
           slack_bot_token: "${{ secrets.SLACK_BOT_TOKEN }}"  
 
       - name: Setup upterm session
-        if: ${{ failure() }}
+        if: ${{ failure() && github.event_name == 'workflow_dispatch' && github.event.inputs.debug == 'true' }}
+        timeout-minutes: 60
         uses: lhotari/action-upterm@v1
diff --git a/cmd/config_test.go b/cmd/config_test.go
index 31c363cd..e139c317 100644
--- a/cmd/config_test.go
+++ b/cmd/config_test.go
@@ -25,6 +25,7 @@ import (
 	"github.com/juicedata/juicefs/pkg/meta"
 )
 
+//mutate_test_job_number: 3
 func getStdout(args []string) ([]byte, error) {
 	tmp, err := os.CreateTemp("/tmp", "jfstest-*")
 	if err != nil {
diff --git a/docs/en/benchmark/mdtest.md b/docs/en/benchmark/mdtest.md
index 91bb99bd..450c01a3 100644
--- a/docs/en/benchmark/mdtest.md
+++ b/docs/en/benchmark/mdtest.md
@@ -1,9 +1,8 @@
 ---
-sidebar_label: Benchmark with mdtest
+title: Benchmark with mdtest
 sidebar_position: 8
 slug: /mdtest
 ---
-# Benchmark with mdtest
 
 :::tip
 Trash is enabled in JuiceFS v1.0+ by default. As a result, temporary files are created and deleted in the file system during the benchmark, and these files will be eventually dumped into a directory named `.trash`. To avoid storage space being occupied by `.trash`, you can run command `juicefs config META-URL --trash-days 0` to disable Trash before benchmark. See [trash](../security/trash.md) for details.
diff --git a/docs/zh_cn/benchmark/mdtest.md b/docs/zh_cn/benchmark/mdtest.md
index 9bb08a6c..9d943127 100644
--- a/docs/zh_cn/benchmark/mdtest.md
+++ b/docs/zh_cn/benchmark/mdtest.md
@@ -1,9 +1,8 @@
 ---
-sidebar_label: mdtest 基准测试
+title: mdtest 基准测试
 sidebar_position: 8
 slug: /mdtest
 ---
-# mdtest 基准测试
 
 :::tip 提示
 JuiceFS v1.0+ 默认启用了回收站，基准测试会在文件系统中创建和删除临时文件，这些文件最终会被转存到回收站 `.trash` 占用存储空间，为了避免这种情况，可以在基准测试之前关闭回收站 `juicefs config META-URL --trash-days 0`，详情参考[回收站](../security/trash.md)。
diff --git a/pkg/fs/fs_test.go b/pkg/fs/fs_test.go
index 6430715d..23412479 100644
--- a/pkg/fs/fs_test.go
+++ b/pkg/fs/fs_test.go
@@ -30,6 +30,7 @@ import (
 	"github.com/juicedata/juicefs/pkg/vfs"
 )
 
+//mutate_test_job_number: 5
 func TestFileStat(t *testing.T) {
 	attr := meta.Attr{
 		Typ:   meta.TypeDirectory,
diff --git a/pkg/meta/base_test.go b/pkg/meta/base_test.go
index 35655e5b..8e2ab054 100644
--- a/pkg/meta/base_test.go
+++ b/pkg/meta/base_test.go
@@ -64,6 +64,7 @@ func testMeta(t *testing.T, m Meta) {
 	testRemove(t, m)
 	testStickyBit(t, m)
 	testLocks(t, m)
+	testListLocks(t, m)
 	testConcurrentWrite(t, m)
 	testCompaction(t, m, false)
 	time.Sleep(time.Second)
@@ -596,6 +597,81 @@ func testStickyBit(t *testing.T, m Meta) {
 	}
 }
 
+func testListLocks(t *testing.T, m Meta) {
+	_ = m.Init(&Format{Name: "test"}, false)
+	ctx := Background
+	var inode Ino
+	var attr = &Attr{}
+	defer m.Unlink(ctx, 1, "f")
+	if st := m.Create(ctx, 1, "f", 0644, 0, 0, &inode, attr); st != 0 {
+		t.Fatalf("create f: %s", st)
+	}
+	if plocks, flocks, err := m.ListLocks(ctx, inode); err != nil || len(plocks) != 0 || len(flocks) != 0 {
+		t.Fatalf("list locks: %v %v %v", plocks, flocks, err)
+	}
+
+	// flock
+	o1 := uint64(0xF000000000000001)
+	if st := m.Flock(ctx, inode, o1, syscall.F_WRLCK, false); st != 0 {
+		t.Fatalf("flock wlock: %s", st)
+	}
+	if plocks, flocks, err := m.ListLocks(ctx, inode); err != nil || len(plocks) != 0 || len(flocks) != 1 {
+		t.Fatalf("list locks: %v %v %v", plocks, flocks, err)
+	}
+	if st := m.Flock(ctx, inode, o1, syscall.F_UNLCK, false); st != 0 {
+		t.Fatalf("flock unlock: %s", st)
+	}
+	if plocks, flocks, err := m.ListLocks(ctx, inode); err != nil || len(plocks) != 0 || len(flocks) != 0 {
+		t.Fatalf("list locks: %v %v %v", plocks, flocks, err)
+	}
+	for i := 2; i < 10; i++ {
+		if st := m.Flock(ctx, inode, uint64(i), syscall.F_RDLCK, false); st != 0 {
+			t.Fatalf("flock wlock: %s", st)
+		}
+	}
+	if plocks, flocks, err := m.ListLocks(ctx, inode); err != nil || len(plocks) != 0 || len(flocks) != 8 {
+		t.Fatalf("list locks: %v %v %v", plocks, flocks, err)
+	}
+	for i := 2; i < 10; i++ {
+		if st := m.Flock(ctx, inode, uint64(i), syscall.F_UNLCK, false); st != 0 {
+			t.Fatalf("flock unlock: %s", st)
+		}
+	}
+	if plocks, flocks, err := m.ListLocks(ctx, inode); err != nil || len(plocks) != 0 || len(flocks) != 0 {
+		t.Fatalf("list locks: %v %v %v", plocks, flocks, err)
+	}
+
+	// plock
+	if st := m.Setlk(ctx, inode, o1, false, syscall.F_WRLCK, 0, 0xFFFF, 1); st != 0 {
+		t.Fatalf("plock rlock: %s", st)
+	}
+	if plocks, flocks, err := m.ListLocks(ctx, inode); err != nil || len(plocks) != 1 || len(flocks) != 0 {
+		t.Fatalf("list locks: %v %v %v", plocks, flocks, err)
+	}
+	if st := m.Setlk(ctx, inode, o1, false, syscall.F_UNLCK, 0, 0xFFFF, 1); st != 0 {
+		t.Fatalf("plock unlock: %s", st)
+	}
+	if plocks, flocks, err := m.ListLocks(ctx, inode); err != nil || len(plocks) != 0 || len(flocks) != 0 {
+		t.Fatalf("list locks: %v %v %v", plocks, flocks, err)
+	}
+	for i := 2; i < 10; i++ {
+		if st := m.Setlk(ctx, inode, uint64(i), false, syscall.F_RDLCK, 0, 0xFFFF, 1); st != 0 {
+			t.Fatalf("plock rlock: %s", st)
+		}
+	}
+	if plocks, flocks, err := m.ListLocks(ctx, inode); err != nil || len(plocks) != 8 || len(flocks) != 0 {
+		t.Fatalf("list locks: %v %v %v", plocks, flocks, err)
+	}
+	for i := 2; i < 10; i++ {
+		if st := m.Setlk(ctx, inode, uint64(i), false, syscall.F_UNLCK, 0, 0xFFFF, 1); st != 0 {
+			t.Fatalf("plock unlock: %s", st)
+		}
+	}
+	if plocks, flocks, err := m.ListLocks(ctx, inode); err != nil || len(plocks) != 0 || len(flocks) != 0 {
+		t.Fatalf("list locks: %v %v %v", plocks, flocks, err)
+	}
+}
+
 func testLocks(t *testing.T, m Meta) {
 	_ = m.Init(&Format{Name: "test"}, false)
 	ctx := Background
diff --git a/pkg/utils/utils_test.go b/pkg/utils/utils_test.go
index 870047e3..280cd068 100644
--- a/pkg/utils/utils_test.go
+++ b/pkg/utils/utils_test.go
@@ -81,6 +81,6 @@ func TestTimeout(t *testing.T) {
 		return nil
 	}, time.Millisecond*10)
 	if err == nil || !strings.HasPrefix(err.Error(), "timeout after") {
-		t.Fatalf("slow function should be  timeout: %s", err)
+		t.Fatalf("slow function should be timeout: %s", err)
 	}
 }
diff --git a/pkg/vfs/vfs_test.go b/pkg/vfs/vfs_test.go
index de31c07b..1c022f02 100644
--- a/pkg/vfs/vfs_test.go
+++ b/pkg/vfs/vfs_test.go
@@ -17,6 +17,7 @@
 package vfs
 
 import (
+	"bytes"
 	"fmt"
 	"log"
 	"reflect"
@@ -737,21 +738,40 @@ func TestInternalFile(t *testing.T) {
 	} else {
 		off += uint64(n)
 	}
-	// info
+	// legacy info
 	buf = make([]byte, 4+4+8)
 	w = utils.FromBuffer(buf)
-	w.Put32(meta.Info)
+	w.Put32(meta.LegacyInfo)
 	w.Put32(8)
 	w.Put64(1)
 	if e := v.Write(ctx, fe.Inode, w.Bytes(), off, fh); e != 0 {
-		t.Fatalf("write info: %s", e)
+		t.Fatalf("write legacy info: %s", e)
 	}
 	off += uint64(len(buf))
 	buf = make([]byte, 1024*10)
 	if n, e = readControl(buf, &off); e != 0 {
 		t.Fatalf("read result: %s %d", e, n)
 	} else if !strings.Contains(string(buf[:n]), "dirs:") {
-		t.Fatalf("info result: %s", string(buf[:n]))
+		t.Fatalf("legacy info result: %s", string(buf[:n]))
+	} else {
+		off += uint64(n)
+	}
+	// info v2
+	buf = make([]byte, 4+4+8)
+	w = utils.FromBuffer(buf)
+	w.Put32(meta.InfoV2)
+	w.Put32(8)
+	w.Put64(1)
+	if e := v.Write(ctx, fe.Inode, w.Bytes(), off, fh); e != 0 {
+		t.Fatalf("write info v2: %s", e)
+	}
+	off += uint64(len(buf))
+	buf = make([]byte, 1024*10)
+	var infoResp InfoResponse
+	if n, e = readControl(buf, &off); e != 0 {
+		t.Fatalf("read result: %s %d", e, n)
+	} else if infoResp.Decode(bytes.NewBuffer(buf[:n])) != nil {
+		t.Fatalf("info v2 result: %s", string(buf[:n]))
 	} else {
 		off += uint64(n)
 	}