Repository: matrixorigin/matrixone
Pull request #3382 (2022-06-24 18:49:54)

Round 2
**What type of PR is this?**

- [ ] API-change
- [ ] BUG
- [x] Improvement
- [x] Documentation
- [ ] Feature
- [ ] Test and CI
- [ ] Code Refactoring

**Which issue(s) this PR fixes:**
grammar
issue #

**What this PR does / why we need it:**

Not Available

**Special notes for your reviewer:**

Not Available

**Additional documentation (e.g. design docs, usage docs, etc.):**

Not Available



Patch:
diff --git a/docs/en/MatrixOne/Contribution-Guide/Tutorial/develop_aggregate_functions.md b/docs/en/MatrixOne/Contribution-Guide/Tutorial/develop_aggregate_functions.md
index ee19ab34f..651e389d9 100644
--- a/docs/en/MatrixOne/Contribution-Guide/Tutorial/develop_aggregate_functions.md
+++ b/docs/en/MatrixOne/Contribution-Guide/Tutorial/develop_aggregate_functions.md
@@ -2,11 +2,11 @@
 
 ## **Prerequisite**
 
-To develop an aggregate function for MatrixOne, you need a basic knowledge of Golang programming. You can go through this excellent [Golang tutorial](https://www.educative.io/blog/golang-tutorial) to get some Golang basic concepts. 
+To develop an aggregate function for MatrixOne, you need a basic knowledge of Golang programming. You can go through this excellent [Golang tutorial](https://www.educative.io/blog/golang-tutorial) to learn some basic Golang concepts. 
 
 ## **Preparation**
 
-Before you start, please make sure that you have `Go` installed, cloned the `MatrixOne` code base.
+Before you start, please make sure that you have `Go` installed and have cloned the `MatrixOne` code base.
 Please refer to [Preparation](../How-to-Contribute/preparation.md) and [Contribute Code](../How-to-Contribute/contribute-code.md) for more details.
 
 ## **What is an aggregation function?**
@@ -22,29 +22,29 @@ Common aggregate functions include:
 
 ## **Aggregate function in MatrixOne**
 
-As MatrixOne's one major key feature, via factorisation, the database `join` in MatrixOne is highly efficient and less redundant in comparison with
-other state-of-the-art databases. Therefore, many operations in MatrixOne need to be adapted to the factorisation method, in order
-to improve efficiency when performing `join`. Aggregate function is an important one among those operations. 
+The function `join` in MatrixOne's database is highly efficient and less redundant in comparison with
+other state-of-the-art databases via factorisation making it a key feature. Therefore, many operations in MatrixOne need to be adapted to the factorisation method, in order
+to improve efficiency when performing `join`. Aggregate functions are an example of an important feature among these operations. 
 
-To implement aggragate function in MatrixOne, we design a data structure named `Ring`. Every aggregate function needs to implement the `Ring` interface
-in order to be factorised when `join` occurs. 
+To implement aggragate functions in MatrixOne, we design a data structure named `Ring`. Every aggregate function needs to implement the `Ring` interface
+in order to be factorized when `join` occurs. 
 
-For the common aggregate function `AVG` as an example, we need to calculate the number of groups and their total numeric sum, then get an average result, which 
-is the common practice for any database design. But when a query with `join` occurs for two tables, the common method is to get a Cartesian product by joining tables first,
-then perform an `AVG` with that Cartesian product, which is an expensive computational cost as Cartesian product can be very large. 
-In MatrixOne's implementation, the factorisation method pushs down the calculation of group statistic and sum before the `join` operation is performed. This method helps to reduce
-a lot in computational and storage cost. This factorisation 
+For the common aggregate function `AVG` as an example, we need to calculate the number of groups and their total numeric sum, then get an average result.
+This is common practice for any database design. However, when a query with `join` occurs between two tables, the common method is to get a Cartesian product by joining tables first,
+then perform an `AVG` with that Cartesian product. This is an expensive computational cost as a Cartesian product can be very large. 
+In MatrixOne's implementation, the factorisation method pushs down the calculation of group statistics and sum before the `join` operation is performed. This method helps to reduce
+a lot in computational and storage cost. Factorisation 
 is realized by the `Ring` interface and its inner functions. 
 
-To checkout more about the factorisation theory and factorized database, please refer to [Principles of Factorised Databases](https://fdbresearch.github.io/principles.html).
+To checkout more about the factorisation theory and factorised database, please refer to [Principles of Factorised Databases](https://fdbresearch.github.io/principles.html).
 
 ## **What is a `Ring`**
 
-`Ring` is an important data structure for MatrixOne factorisation, as well as an mathematical algebraic concept with a clear [definition](https://en.wikipedia.org/wiki/Ring_(mathematics)).
+`Ring` is an important data structure for MatrixOne factorisation, as well as a mathematical algebraic concept with a clear [definition](https://en.wikipedia.org/wiki/Ring_(mathematics)).
 An algebraic `Ring` is a set equipped with two binary operations  `+` (addition) and `⋅` (multiplication) satisfying several axioms.
 
-A `Ring` in MatrixOne is an interface, with several functions similar to the algebraic `Ring` structure. We use `Ring` interface to implement aggragate functions,
-the `+`(addition) is defined as merging two `Ring`s groups, the `⋅`(multiplication) operation is defined as the computation of a grouped aggregate value combined with its grouping key frequency information.
+A `Ring` in MatrixOne is an interface with several functions similar to the algebraic `Ring` structure. We use `Ring` interface to implement aggragate functions.
+The `+`(addition) is defined as merging two `Ring`s groups and the `⋅`(multiplication) operation is defined as the computation of a grouped aggregate value combined with its grouping key frequency information.
 
 | Method of Ring Interface | Do What                                                |
 | ------------------------ | ------------------------------------------------------ |
@@ -79,7 +79,7 @@ There are two different scenarios for aggregation functions with `Ring`s.
 
 In the single table scenario, when we run the below query, it generates one or several `Ring`s, 
 depending on the storage blocks the `T1` table is stored. The number of blocks depends on the storage strategy. Each `Ring` will 
-store several groups of sums, the number of group depends how many duplicate rows are in this `Ring`. 
+store several groups of sums. The number of the group depends on how many duplicate rows are in this `Ring`. 
 
 ```sql
 T1 (id, class, age) 
@@ -163,25 +163,25 @@ Ts (id, age)
 select class, sum(age) from Tc join Ts on Tc.id = Ts.id group by class;
 ```
 
-When we run this query, it will firstly generate `Ring`s for the `Ts` table, as we are performing aggeration over `age` column.
-It might generate also one or several `Ring`s, same as the single table. For simplyfing, we imagine only one `Ring` is created for each table.
-The `Ring-Ts` will start to count sums for the group of `id`, as all `id`s are different, so it will maintain the same. Then a hashtable will be created
+When we run this query, it will firstly generate `Ring`s for the `Ts` table since we are performing an aggeration over the `age` column.
+It might also generate one or several `Ring`s same as the single table. For simplicity, we imagine only one `Ring` is created for each table.
+The `Ring-Ts` will start to count sums for the group of `id` as all `id`s are different, so it will maintain the same. Then a hashtable will be created
 for performing `join` operation.
 
 The `Ring-Tc` is created in the same time as `join` is performed. This `Ring-Tc` will count the appearing frequency `f` of id. Then the `Mul` method
-of `Ring-Ts` is called, to calculate the sum calculated from the `Ring-Ts` and frequency from `Ring-Tc`.
+of `Ring-Ts` is called to calculate the sum calculated from the `Ring-Ts` and frequency from `Ring-Tc`.
 
 ```
 sum[i] = sum[i] * f[i]
 ```
 
-Now we get values of `[class, sum(age)]`, then performing a group by with class will give us the final result.
+Now we get values of `[class, sum(age)]`, then perform a group by with class which will give us the final result.
 
 ## **The secret of factorisation**
 
 From the above example, you can see that the `Ring` performs some pre calculations and only the result(like `sum`) is stored in its structure. When performing operations like `join`,
 only simple `Add` or `Multiplication` is needed to get the result, which is called a push down calculation in `factorisation`. With the help of this push down, we no longer need to 
-deal with costly Cartesian product. As the joined table number increases, the factorisation allow us to take linear cost performing that, instead of exponential increase.
+deal with a costly Cartesian product. As the joined table number increases, the factorisation allows us to take linear cost of performing that instead of exponential increase.
 
 Take the implementation of `Variance` function as an example. 
 
@@ -194,13 +194,12 @@ Example: xi = 10,8,6,12,14, x̅ = 10
 Calculation: ((10-10)^2+(8-10)^2+(6-10)^2+(12-10)^2+(14-10)^2)/5 = 8
 ``` 
 
-If we proceed implementation with this formula, we have to record all values of each group, and keep maintaining these values 
-with `Add` and `Mul` operations of `Ring`. Eventual result is calculated in `Eval()` function. This implementation has a drawback of high memory cost,
-as we store all values during processing.
+If we proceed with implementing this formula, we have to record all values of each group, and also maintain these values 
+with `Add` and `Mul` operations of `Ring`. Eventually the result is calculated in an `Eval()` function. This implementation has a drawback of high memory cost since we have to store all the values during processing.
 
-But in the `Avg` implementation, it doesn't store all values in the `Ring`. Instead it stores only the `sum` of each group and the null numbers.  It returns the final result with a simple division. This method saves a lot of memory space.
+In the `Avg` implementation, it doesn't store all values in the `Ring`. Instead it stores only the `sum` of each group and the null numbers.  It returns the final result with a simple division. This method saves a lot of memory space.
 
-Now let's turn the `Variance` formula a bit to a different form:
+Now let's turn the `Variance` formula a bit into a different form:
 
 ```
 Variance = Σ (xi^2)/n-x̅^2
@@ -209,11 +208,11 @@ Example: xi = 10,8,6,12,14, x̅ = 10
 Calculation: (10^2+8^2+6^2+12^2+14^2)/5-10^2 = 8
 ``` 
 
-This formula's result is actually exactly the same as the previous one, but we only have to record the values sum of `xi^2` and the sum of `xi`. We can largely reduce the memory space 
+This formula's result is exactly the same as the previous one, but we only have to record the values sum of `xi^2` and the sum of `xi`. We can largely reduce the memory space 
 with this kind of reformulation.
 
 To conclude, every aggregate function needs to find a way to record as little values as possible in order to reduce memory cost.
-Below is two different implementation for `Variance` (the second one has a better performance):
+Below are two different implementations for `Variance` (the second one has a better performance):
 
 ```go
    //Implementation1
@@ -246,14 +245,14 @@ Below is two different implementation for `Variance` (the second one has a bette
 
 ## **Develop an var() function**
 
-In this tutorial, we walk you through the complete implementation of Variance (get the standard overall variance value) aggregate function as an example with two different methods. 
+In this tutorial, we will walk you through the complete implementation of the Variance (get the standard overall variance value) aggregate function as an example with two different methods. 
 
 Step 1: register function
 
 MatrixOne doesn't distinguish between operators and functions. 
-In our code repository, the file `pkg/sql/viewexec/transformer/types.go` register aggregate functions as operators 
+In our code repository, the file `pkg/sql/viewexec/transformer/types.go` registers aggregate functions as operators 
 and we assign each operator a distinct integer number. 
-To add a new function `var()`, add a new const `Variance` in the const declaration and `var` in the name declaration.
+To add a new function `var()`, first add a new const `Variance` in the const declaration and `var` in the name declaration.
 
 ```go
    const (
@@ -549,7 +548,7 @@ Here we go. Now we can fire up MatrixOne and try with our `var()` function.
 
 ## **Compile and run MatrixOne**
 
-Once the aggregation function is ready, we could compile and run MatrixOne to see the function behavior. 
+Once the aggregation function is ready, we can compile and run MatrixOne to see the function behavior. 
 
 Step1: Run `make config` and `make build` to compile the MatrixOne project and build binary file. 
 
@@ -559,7 +558,7 @@ make build
 ``` 
 
 !!! info 
-    `make config` generates a new configuration file, in this tutorial, you only need to run it once. If you modify some code and want to recompile, you only have to run `make build`.  
+    `make config` generates a new configuration file. In this tutorial you only need to run it once. If you modify some code and want to recompile, you only have to run `make build`.  
 
 Step2: Run `./mo-server system_vars_config.toml` to launch MatrixOne, the MatrixOne server will start to listen for client connecting. 
 
@@ -642,8 +641,8 @@ Bingo!
 
 ## **Write unit Test for your function**
 
-We recommend you to also write a unit test for the new function. 
-Go has a built-in testing command called `go test` and a package `testing` which combine to give a minimal but complete testing experience. It automates execution of any function of the form.
+We recommend that you also write a unit test for the new function. 
+Go has a built-in testing command called `go test` and a package `testing` which combine to give a minimal yet complete testing experience. It automates execution of any function of the form.
 
 ```
 func TestXxx(*testing.T)
@@ -757,8 +756,8 @@ You code will be merged only if the `bvt` test pass.
 
 ## **Conduct a Performance Test**
 
-Aggregate function is an important feature of a database system, with queries on hundreds of millions of data rows, the time consumption of aggregate function is quite significant.
-So we recommend you to run a performance test. 
+Aggregate functions are an important feature of a database system, with queries on hundreds of millions of data rows, the time consumption of an aggregate function is quite significant.
+Thus, we recommend you to run a performance test. 
 
 Step1: Download the standard test dataset.
 
diff --git a/docs/en/MatrixOne/Contribution-Guide/Tutorial/develop_builtin_functions.md b/docs/en/MatrixOne/Contribution-Guide/Tutorial/develop_builtin_functions.md
index f37711e74..4a965f01e 100644
--- a/docs/en/MatrixOne/Contribution-Guide/Tutorial/develop_builtin_functions.md
+++ b/docs/en/MatrixOne/Contribution-Guide/Tutorial/develop_builtin_functions.md
@@ -2,7 +2,7 @@
 
 ## **Prerequisite**
 
-To develop a build-in function for MatrixOne, you need a basic knowledge of Golang programming. You can go through this excellent [Golang tutorial](https://www.educative.io/blog/golang-tutorial) to get some Golang basic concepts. 
+To develop a built-in function for MatrixOne, you need a basic knowledge of Golang programming. You can go through this excellent [Golang tutorial](https://www.educative.io/blog/golang-tutorial) to learn some basic Golang concepts. 
 
 ## **Preparation**
 
@@ -11,11 +11,11 @@ Please refer to [Preparation](../How-to-Contribute/preparation.md) and [Contribu
 
 ## **What is a built-in function?**
 
-There are two types of functions in a database,  built-in functions are functions that are shipped with the database, in contrast, user-defined functions is customized by the users. Built-in functions can be categorized according to the data types that they operate on i.e. strings, date and numeric built-in functions. 
+There are two types of functions in a database,  built-in functions  and user-defined functions. Built-in functions are functions that are shipped with the database. In contrast, user-defined functions are customized by the users. Built-in functions can be categorized according to the data types that they operate on i.e. strings, date and numeric built-in functions. 
 
-An example of a built-in function is `abs()`, which when given a value calculates the absolute (non-negative) value of the number.
+An example of a built-in function is `abs()`, which calculates the absolute (non-negative) value of a given number.
 
-Some functions, such as `abs()` are used to perform calculations, others such as `getdate()` are used to obtain a system value, such as the current data, or others, like `left()`, are used to manipulate textual data.
+Some functions, such as `abs()` are used to perform calculations. Others such as `getdate()` are used to obtain a system value, such as the current data, or others, like `left()`, are used to manipulate textual data.
 
 Usually the built-in functions are categorized into major categories:  
 
@@ -74,11 +74,11 @@ func init() {
 }
 ```
 
-In MatrixOne, all letters in a function name will be lowercased during the parsing process, so register function names using only lowercase letters otherwise the function won't be recognized.
+In MatrixOne, all letters in a function name will be lowercased during the parsing process, so the register function names using only lowercase letters otherwise the function won't be recognized.
 
 **2.** declare function parameter types and return types.
 
-The function abs accepts all numeric types as its parameter(uint8, int8, float32...), we can return a 64bit value covering all different parameter types, or, to optimize the performance of our function, we can return different types with respect to the parameter type.
+The function abs accepts all numeric types as its parameter (uint8, int8, float32...), we can return a 64bit value covering all different parameter types. To optimize the performance of our function, we can also return different types with respect to the parameter type.
 
 Outside the init function, declare these variables for each pair of parameter type and return type.
 
@@ -188,7 +188,7 @@ since we need a float32 vector here, its size should be 4 * len(origVecCol), 4 b
 
 3.Vector.Nsp: MatrixOne uses bitmaps to store the NULL values in a column, Vector.Nsp is a wrap up struct for this bitmap.
 
-4.the boolean parameter of the Fn: this boolean value is usually used to indicate whether the vector passed in is a constant(it has length 1), sometimes we could make use of this situation for our function implementation, for example, pkg/sql/colexec/extend/overload/plus.go. 
+4.the boolean parameter of the Fn: this boolean value is usually used to indicate whether the vector passed in is a constant(it has length 1). Sometimes we could make use of this situation for our function implementation, for example, pkg/sql/colexec/extend/overload/plus.go. 
 
 Since the result vector has the same type as the original vector, we could use the original vector to store our result when we don't need our original vector anymore in our execution plan(i.e., the reference count of the original vector is 0 or 1).
 
@@ -301,7 +301,7 @@ func init() {
 
 Step 2: Implement Abs function
 
-In MatrixOne, We put all of our builtin function definition code in the `pkg/vectorize/` directory, to implement abs functions, first we need to create a subdirectory `abs` in this vectorize directory.
+In MatrixOne, We put all of our builtin function definition code in the `pkg/vectorize/` directory. To implement abs functions, first we need to create a subdirectory `abs` in this vectorized directory.
 In this fresh `abs` directory, create a file `abs.go`, the place where our abs function implementation code goes.
 For certain cpu architectures, we could utilize the cpu's intrinsic SIMD instruction to compute the absolute value and hence boost our function's performance, to differentiate function implementations for different cpu architectures, we declare our pure go version of abs function this way:
 
@@ -429,14 +429,14 @@ Bingo!
 
 ## **Write a unit Test for your function**
 
-We recommend you to also write a unit test for the new function. 
+We recommend that you also write a unit test for the new function. 
 Go has a built-in testing command called `go test` and a package `testing` which combine to give a minimal but complete testing experience. It automates execution of any function of the form.
 
 ```
 func TestXxx(*testing.T)
 ```
 
-To write a new test suite, create a file whose name ends `_test.go` that contains the `TestXxx` functions as described here. Put the file in the same package as the one being tested. The file will be excluded from regular package builds but will be included when the `go test` command is run. 
+To write a new test suite, create a file whose name ends `_test.go` and contains the `TestXxx` functions as described here. Put the file in the same package as the one being tested. The file will be excluded from regular package builds but will be included when the `go test` command is run. 
 
 Step1: Create a file named `abs_test.go` under `vectorize/abs/` directory. Import the `testing` framework and the `testify` framework we are going to use for testing mathematical `equal`. 
 
diff --git a/docs/en/MatrixOne/Develop/develop_python_application.md b/docs/en/MatrixOne/Develop/develop_python_application.md
index 12a5800ff..b82a59d15 100644
--- a/docs/en/MatrixOne/Develop/develop_python_application.md
+++ b/docs/en/MatrixOne/Develop/develop_python_application.md
@@ -1,6 +1,6 @@
 # **Build a simple stock analysis Python App with MatrixOne**
 
-This tutorial shows you how build a simple Python application with MatrixOne.
+This tutorial shows you how to build a simple Python application with MatrixOne.
 
 ## **About the demo**
 
@@ -12,7 +12,7 @@ Every day after the market closes, we compare the P/E and P/B value with the his
 
 ## **Before you start**
 
-Before you start, you need to have a Tushare account and get an API token. If you haven't signed up for Tushare, you can sign it up at [https://tushare.pro/](https://tushare.pro/) and you can get your API token at [https://tushare.pro/user/token#](https://tushare.pro/user/token#).
+Before you start, you need to have a Tushare account and get an API token. If you haven't signed up for Tushare, you can sign up for it at [https://tushare.pro/](https://tushare.pro/) and you can find your API token at [https://tushare.pro/user/token#](https://tushare.pro/user/token#).
 
 !!! info 
     Tushare is an open source utility for crawling historical data of China stocks. It has a complete dataset, but pay attention to their rules of API access limit and frequency. 
@@ -35,9 +35,9 @@ pip3 install -r requirements.txt
 
 ## **Step1: prepare and load historical dataset**
 
-At first, we need to load the historical stock data in MatrixOne. 
+First, we need to load the historical stock data in MatrixOne. 
 
-As `Tushare` interface only allows a fetch of 5000 rows of data at once, we just collect the latest 5000 trade days data for each stock. Each year there are roughly 250 trade dates. 5000 makes almost 20 years of data, which largely satisfies our demo.
+As `Tushare` interface only allows a fetch of 5000 rows of data at once, we only have to collect the latest 5000 trade days data for each stock. Each year there are roughly 250 trade dates. 5000 makes almost 20 years of data, which largely satisfies our demo.
 
 The below code will set up tushare interface and get the list of stocks.
 
diff --git a/docs/en/MatrixOne/FAQs/general-faqs.md b/docs/en/MatrixOne/FAQs/general-faqs.md
index f12a4d18e..07d72a292 100644
--- a/docs/en/MatrixOne/FAQs/general-faqs.md
+++ b/docs/en/MatrixOne/FAQs/general-faqs.md
@@ -4,25 +4,25 @@
 
 * <font size=4>**What is MatrixOne?**</font>
     
-  MatrixOne is a future-oriented hyperconverged cloud & edge native DBMS that supports transactional, analytical, and streaming workload with a simplified and distributed database engine, across multiple datacenters, clouds, edges and other heterogenous infrastructures. The all-in-one architecture of MatrixOne will significantly simplify database management and maintenance, single database can serve multiple data applications. 
+  MatrixOne is a future-oriented hyperconverged cloud & edge native DBMS that supports transactional, analytical, and streaming workload with a simplified and distributed database engine, across multiple datacenters, clouds, edges and other heterogenous infrastructures. The all-in-one architecture of MatrixOne will significantly simplify database management and maintenance, creating a single database that can serve multiple data applications. 
   For information about MatrixOne, you can see [MatrixOne Introduction](../Overview/matrixone-introduction.md).
 
 <br>
 
 * <font size=4>**Where can I apply MatrxOne?**</font>
     
-  MatrixOne provides users with HTAP services to support hybrid workloads, it can be used to build data warehouse or data platform. 
+  MatrixOne provides users with HTAP services to support hybrid workloads. It can be used to build data warehouse or data platforms. 
 <br>
 
 * <font size=4>**Is MatrixOne based on MySQL or some other database?**</font>
     
-  MatrixOne is a totally redesigned database. It's compatible with part of MySQL syntax and semantics, and we are working to support more database semantics such as PostgreSQL, Hive, Clickhouse, as we intend to develop MatrixOne as a hyperconverged database.  
-  About the compatibility with MySQL, you can see [MySQL-Compatibility](../Overview/mysql-compatibility.md).
+  MatrixOne is a completely redesigned database. It's compatible with part of MySQL syntax and semantics. We are currently working to support more database semantics such as PostgreSQL, Hive, Clickhouse, since we intend to develop MatrixOne as a hyperconverged database.  
+  To learn more about the compatibility with MySQL, you can visit [MySQL-Compatibility](../Overview/mysql-compatibility.md).
 <br>
 
 * <font size=4>**What's the relationship between MatrixOne and MatrixCube?**</font>
     
-  MatrixOne is the main database project, including query parser layer, compute layer, and storage layer, it could work as a standalone database system.
+  MatrixOne is the main database project, including a query parser layer, compute layer, and storage layer. It can also work as a standalone database system.
   MatrixCube is a independant library who doesn't work alone, it's a distributed system framework. It gives MatrixOne the ability to extend as a distributed database. It also supports to be mounted with other storage engines. 
   For information about MatrixCube, you can see [MatrixCube Introduction](../Overview/matrixcube/matrixcube-introduction.md).
 <br>
@@ -34,7 +34,7 @@
 
 * <font size=4>**Which programming language is MatrixOne developed with ?**</font>
     
-  Currently, the primary programming language used for our codes is **Golang**.
+  Currently, the primary programming language used for our programming is **Golang**.
 <br>
 
 * <font size=4>**What operating system does MatrixOne support?**</font>
diff --git a/docs/en/MatrixOne/Get-Started/Playground.md b/docs/en/MatrixOne/Get-Started/Playground.md
index 8aa9b3ed8..9a4f6af79 100644
--- a/docs/en/MatrixOne/Get-Started/Playground.md
+++ b/docs/en/MatrixOne/Get-Started/Playground.md
@@ -31,8 +31,8 @@ We will use **SSB Test** as an example to show you the overall operation process
 
 ### **Test Preperations**  
 
-This tutorial walks you through the most popular **Star Schema Benchmark（SSB）**Test SQL statements with MatrixOne. To better experience MatrixOne features and performance, test queries in this tutorial will run without filters.  
-Before you started, the test datasets have been pre-loaded in  database `ssb`. To list available tables in the database you can query :
+This tutorial walks you through the most popular **Star Schema Benchmark（SSB）**Test SQL statements with MatrixOne. To better experience MatrixOne's features and performance, test queries in this tutorial will run without filters.  
+Before you start, the test datasets have been pre-loaded in  database `ssb`. To list available tables in the database you can query :
 
 ```
 SHOW TABLES；
diff --git a/docs/en/MatrixOne/Get-Started/install-distributed-matrixone.md b/docs/en/MatrixOne/Get-Started/install-distributed-matrixone.md
index 819472583..928903ee1 100644
--- a/docs/en/MatrixOne/Get-Started/install-distributed-matrixone.md
+++ b/docs/en/MatrixOne/Get-Started/install-distributed-matrixone.md
@@ -7,7 +7,7 @@ MatrixOne supports distributed deloyment. At least 3 stores (same as Nodes) are
 There are two types of stores in a MatrixOne cluster: prophet stores and pure storage stores. The first three stores are prophet stores. The others are pure storage stores. 
 
 For example, if there're three stores in the cluster, all the stores are prophet stores.
-If there're five nodes in the cluster, three are prophet stores, two are pure storage stores.
+If there are five nodes in the cluster, three are prophet stores, and two are pure storage stores.
 
 ### Installation
 
diff --git a/pkg/logservice/commands.go b/pkg/logservice/commands.go
index 7cd49c7d1..b2ff4b51e 100644
--- a/pkg/logservice/commands.go
+++ b/pkg/logservice/commands.go
@@ -37,7 +37,13 @@ func (s *Service) handleCommands(cmds []hapb.ScheduleCommand) {
 }
 
 func (s *Service) handleAddReplica(cmd hapb.ScheduleCommand) {
-	panic("not implemented due to the lack of target UUID in ScheduleCommand")
+	shardID := cmd.ConfigChange.Replica.ShardID
+	replicaID := cmd.ConfigChange.Replica.ReplicaID
+	epoch := cmd.ConfigChange.Replica.Epoch
+	target := cmd.ConfigChange.Replica.UUID
+	if err := s.store.addReplica(shardID, replicaID, target, epoch); err != nil {
+		plog.Errorf("failed to add replica %v", err)
+	}
 }
 
 func (s *Service) handleRemoveReplica(cmd hapb.ScheduleCommand) {
@@ -54,7 +60,7 @@ func (s *Service) handleStartReplica(cmd hapb.ScheduleCommand) {
 	replicaID := cmd.ConfigChange.Replica.ReplicaID
 	if shardID == hakeeper.DefaultHAKeeperShardID {
 		if err := s.store.StartHAKeeperReplica(replicaID, nil, false); err != nil {
-			plog.Errorf("failed to start log replica %v", err)
+			plog.Errorf("failed to start HAKeeper replica %v", err)
 		}
 	} else {
 		if err := s.store.StartReplica(shardID, replicaID, nil, false); err != nil {


Test patch:
diff --git a/docs/en/MatrixOne/Get-Started/Tutorial/NYC-test-with-matrixone.md b/docs/en/MatrixOne/Get-Started/Tutorial/NYC-test-with-matrixone.md
index 7dd9bb52a..94c18717a 100644
--- a/docs/en/MatrixOne/Get-Started/Tutorial/NYC-test-with-matrixone.md
+++ b/docs/en/MatrixOne/Get-Started/Tutorial/NYC-test-with-matrixone.md
@@ -1,10 +1,10 @@
 # **Complete a NYC Test with MatrixOne**
 
-**New York City (NYC) Taxi** data set captures detailed information on billions of individual taxi trips in New York City, including pick-up and drop-off dates/times, pick-up and drop-off locations, trip distances, itemized fares, rate types, payment types, and driver-reported passenger counts(Most of the raw data comes from the NYC Taxi & Limousine Commission).  
+**New York City (NYC) Taxi** data set captures detailed information on billions of individual taxi trips in New York City. The data includes pick-up and drop-off dates/times, pick-up and drop-off locations, trip distances, itemized fares, rate types, payment types, and driver-reported passenger counts(Most of the raw data comes from the NYC Taxi & Limousine Commission).  
 
 By going through this tutorial, you’ll learn how to complete some queries on NYC Taxi data with MatrixOne.
 
-For detail description and instructions for downloading about **NYC Taxi Data**, you can see:  
+For detailed descriptions and instructions for downloading **NYC Taxi Data**, you can visit:  
 
 * [https://github.com/toddwschneider/nyc-taxi-data](https://github.com/toddwschneider/nyc-taxi-data)  
 
@@ -18,7 +18,7 @@ Make sure you have already [installed MatrixOne](../install-standalone-matrixone
 
 This section of the tutorial references [here](https://github.com/toddwschneider/nyc-taxi-data), and you can get original information about how to download and import raw data there.
 
-The data set has 1.7 billion rows data and takes up 450 GB of space, so make sure there are enough space to hold the data.  
+The data set has 1.7 billion rows of data and takes up 450 GB of space, so make sure have enough space to hold the data.  
 
 * <font size=4>**Install PostgreSQL and PostGIS**</font>  
 
diff --git a/docs/en/MatrixOne/Get-Started/Tutorial/SSB-test-with-matrixone.md b/docs/en/MatrixOne/Get-Started/Tutorial/SSB-test-with-matrixone.md
index c3df156d4..eab486e3b 100644
--- a/docs/en/MatrixOne/Get-Started/Tutorial/SSB-test-with-matrixone.md
+++ b/docs/en/MatrixOne/Get-Started/Tutorial/SSB-test-with-matrixone.md
@@ -1,8 +1,8 @@
 # **Complete a SSB Test with MatrixOne**
 
-Star Schema Benchmark（SSB）Test is a popular scenario for OLAP database performance tests. By going through this tutorial, you’ll learn how to complete a SSB Test with MatrixOne.
+The Star Schema Benchmark（SSB）Test is a popular scenario for OLAP database performance tests. By going through this tutorial, you’ll learn how to complete a SSB Test with MatrixOne.
 
-In v0.1.0, only single table can work in MatrixOne. The following contents can be followed to data generation, data importing and querying.
+In v0.1.0, only single table can work in MatrixOne. The following contents can be followed for generating data, importing data, and querying data.
 
 ## **Before you begin**
 
diff --git a/pkg/logservice/commands_test.go b/pkg/logservice/commands_test.go
index 1020b7d69..900c86101 100644
--- a/pkg/logservice/commands_test.go
+++ b/pkg/logservice/commands_test.go
@@ -17,6 +17,7 @@ package logservice
 import (
 	"testing"
 
+	"github.com/google/uuid"
 	"github.com/stretchr/testify/assert"
 	"github.com/stretchr/testify/require"
 
@@ -69,6 +70,34 @@ func TestHandleStopReplica(t *testing.T) {
 	runServiceTest(t, fn)
 }
 
+func TestHandleAddReplica(t *testing.T) {
+	store1, store2, err := getTestStores()
+	require.NoError(t, err)
+	defer func() {
+		require.NoError(t, store1.Close())
+		require.NoError(t, store2.Close())
+	}()
+
+	service1 := Service{
+		store: store1,
+	}
+	cmd := hapb.ScheduleCommand{
+		ConfigChange: hapb.ConfigChange{
+			ChangeType: hapb.AddReplica,
+			Replica: hapb.Replica{
+				UUID:      uuid.New().String(),
+				ShardID:   1,
+				ReplicaID: 3,
+				Epoch:     2,
+			},
+		},
+	}
+	service1.handleCommands([]hapb.ScheduleCommand{cmd})
+	count, ok := checkReplicaCount(store1, 1)
+	require.True(t, ok)
+	assert.Equal(t, 3, count)
+}
+
 func TestHandleRemoveReplica(t *testing.T) {
 	store1, store2, err := getTestStores()
 	require.NoError(t, err)
@@ -86,9 +115,22 @@ func TestHandleRemoveReplica(t *testing.T) {
 			Replica: hapb.Replica{
 				ShardID:   1,
 				ReplicaID: 2,
+				Epoch:     2,
 			},
 		},
 	}
-	// TODO: add some checks
 	service1.handleCommands([]hapb.ScheduleCommand{cmd})
+	count, ok := checkReplicaCount(store1, 1)
+	require.True(t, ok)
+	assert.Equal(t, 1, count)
+}
+
+func checkReplicaCount(s *store, shardID uint64) (int, bool) {
+	hb := s.getHeartbeatMessage()
+	for _, info := range hb.Replicas {
+		if info.ShardID == shardID {
+			return len(info.Replicas), true
+		}
+	}
+	return 0, false
 }