[to #348] Add examples to docs (#453)

This commit is contained in:
Peng Guanwen 2022-02-11 14:34:49 +08:00 committed by GitHub
parent 84bff75e85
commit 4f136193b0
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
14 changed files with 339 additions and 10 deletions

View File

@ -1,15 +1,17 @@
# Summary
<!-- Links to empty page is replaced by empty link `() `, so that we can prevent readers to click into these unfinished page. Once the corresponding page is done, we can delete the empty parenthesis and then the pages will be visible. -->
- [Introduction](./introduction/introduction.md)
- [Start With Examples](./examples/introduction.md)
- [Quick Start](./examples/quick-start.md)
- [Interact with TiKV RawKV API](./examples/rawkv.md)
- [Interact with TiKV TxnKV API](./examples/txnkv.md)
- [TiKV RawKV Bulk Load](./examples/bulk-load.md)
- [TiKV RawKV Bulk Load]() (./examples/bulk-load.md)
- [Performance](./performance/introduction.md)
- [YCSB Benchmarks](./performance/ycsb.md)
- [Performance]() (./performance/introduction.md)
- [YCSB Benchmarks]() (./performance/ycsb.md)
- [Administration](./administration/introduction.md)
- [Configuration](./administration/configuration.md)
@ -17,12 +19,12 @@
- [Troubleshooting](./troubleshooting/introduction.md)
- [Slow Request Diagnosis](./troubleshooting/slow-request.md)
- [Error Request Diagnosis](./troubleshooting/error-request.md)
- [Error Request Diagnosis]() (./troubleshooting/error-request.md)
- [Architecture and Internals](./architecture/introduction.md)
- [The Lifecycle of A Request](./architecture/request-lifecycle.md)
- [Backoff and Retry Policy](./architecture/availability.md)
- [Slow Log and Metrics](./architecture/observability.md)
- [Region Cache](./architecture/region-cache.md)
- [Slow Log and Metrics]() (./architecture/observability.md)
- [Region Cache]() (./architecture/region-cache.md)
- [Contribution Guide](./contribution/introduction.md)

View File

@ -1 +1,47 @@
# Availability: Backoff and Retry Policy
## BackOffer
The retry and timeout mechanism for a request is controlled by a `BackOffer` object, which is created one per `RawKVClient` method. The `BackOffer` will decide how much time the next sleep and retry should spend, and whether to timeout the request if not enough time is left for retrying the request.
If we need a back off sleep, we call backOffer.doBackOff(funcType, exception), and the current thread will sleep for a decided time. If the current operation will timeout after sleep, the doBackOff simply throw an exception to abort the operation.
## callWithRetry
RegionStoreClient.callWithRetry inherits from AbstractGRPCClient.callWithRetry. The concrete logic is in RetryPolicy.callWithRetry, which implements a retry mechanism, but the specific retry strategy is determined by the ErrorHandler.
ErrorHandlers handler{Request, Response}Error function returns a boolean value indicating whether to retry inside callWithRetry.
The control flow for callWithRetry is as follows:
![callWithRetry](./callWithRetry.jpg)
The error handler is chosen obeying the following table:
| gPRC request | the result | handler |
| -- | -- | -- |
| throws exception | - | handleRequestError
| no exception | is null | handleRequestError
| no exception | is error | handleResponseError
| no exception | normal | normal return
The handleRequestError function copes with the following situations:
| situation | retry within callWithRetry | note |
|----------|---------------|------------------------------|
| invalid store in region manager | true | refresh ClientStub |
| region has not got multiple copies | false | |
| successfully switched to new leader | true | |
| seekProxyStore | true if success | only when `tikv.enable_grpc_forward` is set |
| other | false | |
The handleResponseError function copes with the following gRPC errors:
| error | retry within callWithRetry |
|----------------------|----------------------------|
| NotLeader | true if leader unchanged |
| StoreNotMatch | false |
| EphochNotMatch | true if region epoch in `ctx` is ahead of TiKV's |
| ServerIsBusy | true |
| StaleCommand | true |
| RaftEntryTooLarge | throw |
| KeyNotInRegion | throw |
| Raft ProposalDropped | true |
| other | false |

Binary file not shown.

After

Width:  |  Height:  |  Size: 89 KiB

View File

@ -1 +1,3 @@
# Architecture
This section includes in-depthA description of the client architecture.

View File

@ -1 +1,7 @@
# The Lifecycle of A Request
![time graph](./timegraph.png)
The client talks to TiKV store directly using gRPC requests, which are created in RegionStoreClient. If a request failed, the client could retry after a back off sleep. The retry logic is delegated to AbstractGRPCClient::callWithRetry method. callWithRetry may decide to retry request within the function, or, if the RegionStoreClient must be recreated (due to, for example, region split), return a failure and let outermost RawKVClient to do the retry.
![request-overview](./request-overview.jpg)

Binary file not shown.

After

Width:  |  Height:  |  Size: 95 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 90 KiB

View File

@ -1,11 +1,18 @@
# Contribution Guide
## How to build
## Build the package
```
mvn clean package -Dmaven.test.skip=true
```
## How to run test
## Install the package to local maven repository
```
mvn clean install -Dmaven.test.skip=true
```
## Run tests
```
export RAWKV_PD_ADDRESSES=127.0.0.1:2379

View File

@ -1 +1,3 @@
# Start With Examples
This section contains examples to demonstrate basic usages of the java client.

View File

@ -0,0 +1,109 @@
# Quick Start
The package is hosted on maven central repository. To build from source, refer to the [Contribution Guide](../contribution/introduction.html).
## Create a maven project
First download [maven] and follow the [installation instructoins][install]. Then `mvn` command should be available in the `$PATH`.
[maven]: https://maven.apache.org/download.html
[install]: https://maven.apache.org/install.html
create a maven project by following command:
```
mvn archetype:generate -DgroupId=com.example -DartifactId=java-client-example -DarchetypeArtifactId=maven-archetype-quickstart -DinteractiveMode=false
cd java-client-example
```
## Add dependency
Add maven dependency to `pom.xml`.
```xml
<dependency>
<groupId>org.tikv</groupId>
<artifactId>tikv-client-java</artifactId>
<version>3.1.0</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
<version>1.7.32</version>
</dependency>
```
Now `pom.xml` should look like this:
```xml
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.example</groupId>
<artifactId>java-project</artifactId>
<packaging>jar</packaging>
<version>1.0-SNAPSHOT</version>
<name>java-project</name>
<url>http://maven.apache.org</url>
<properties>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
</properties>
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.tikv</groupId>
<artifactId>tikv-client-java</artifactId>
<version>3.1.0</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
<version>1.7.32</version>
</dependency>
</dependencies>
</project>
```
## Writing code
To interact with TiKV, we should first create a `TiConfiguration` with PD address, create a `TiSession` using `TiSession.create`, and then create a client.
For example, if we want to put a `World` in `Hello` key in RawKV, write the following code in `src/main/java/com/example/App.java`.
```java
import org.tikv.common.TiConfiguration;
import org.tikv.common.TiSession;
import org.tikv.raw.RawKVClient;
import org.tikv.shade.com.google.protobuf.ByteString;
public class App {
public static void main(String[] args) throws Exception {
String pdAddr = "127.0.0.1:2379";
// You MUST create a raw configuration if you are using RawKVClient.
TiConfiguration conf = TiConfiguration.createRawDefault(pdAddr);
try (TiSession session = TiSession.create(conf)) {
try (RawKVClient client = session.createRawClient()) {
client.put(ByteString.copyFromUtf8("Hello"), ByteString.copyFromUtf8("World"));
ByteString value = client.get(ByteString.copyFromUtf8("Hello"));
System.out.println(value);
}
}
}
}
```
More examples for RawKV and TxnKV are in following chapters.
## Running program
Run following command:
```
mvn assembly:assembly -DdescriptorId=jar-with-dependencies
java -cp target/java-client-example-1.0-SNAPSHOT-jar-with-dependencies.jar com.example.App
```

View File

@ -0,0 +1,51 @@
# RawKV
Below is the basic usages of RawKV. See [API document] to see a full list of methods available.
[API document]: https://tikv.github.io/client-java/apidocs/org/tikv/raw/RawKVClient
```java
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import org.tikv.common.TiConfiguration;
import org.tikv.common.TiSession;
import org.tikv.kvproto.Kvrpcpb;
import org.tikv.raw.RawKVClient;
import org.tikv.shade.com.google.protobuf.ByteString;
public class Main {
public static void main() {
// You MUST create a raw configuration if you are using RawKVClient.
TiConfiguration conf = TiConfiguration.createRawDefault("127.0.0.1:2379");
TiSession session = TiSession.create(conf);
RawKVClient client = session.createRawClient();
// put
client.put(ByteString.copyFromUtf8("k1"), ByteString.copyFromUtf8("Hello"));
client.put(ByteString.copyFromUtf8("k2"), ByteString.copyFromUtf8(","));
client.put(ByteString.copyFromUtf8("k3"), ByteString.copyFromUtf8("World"));
client.put(ByteString.copyFromUtf8("k4"), ByteString.copyFromUtf8("!"));
client.put(ByteString.copyFromUtf8("k5"), ByteString.copyFromUtf8("Raw KV"));
// get
Optional<ByteString> result = client.get(ByteString.copyFromUtf8("k1"));
System.out.println(result.get().toStringUtf8());
// batch get
List<Kvrpcpb.KvPair> list = client.batchGet(new ArrayList<ByteString>() {{
add(ByteString.copyFromUtf8("k1"));
add(ByteString.copyFromUtf8("k3"));
}});
System.out.println(list);
// scan
list = client.scan(ByteString.copyFromUtf8("k1"), ByteString.copyFromUtf8("k6"), 10);
System.out.println(list);
// close
client.close();
session.close();
}
}
```

View File

@ -0,0 +1,69 @@
# TxnKV
Below is the basic usages of TxnKV.
Data should be written into TxnKV using [`TwoPhaseCommitter`], and be read using [`org.tikv.txn.KVClient`][KVClient].
[`TwoPhaseCommitter`]: https://tikv.github.io/client-java/apidocs/org/tikv/txn/TwoPhaseCommitter.html
[KVClient]: https://tikv.github.io/client-java/apidocs/org/tikv/txn/KVClient.html
```java
import java.util.Arrays;
import java.util.List;
import org.tikv.common.BytePairWrapper;
import org.tikv.common.ByteWrapper;
import org.tikv.common.TiConfiguration;
import org.tikv.common.TiSession;
import org.tikv.common.util.BackOffer;
import org.tikv.common.util.ConcreteBackOffer;
import org.tikv.kvproto.Kvrpcpb.KvPair;
import org.tikv.shade.com.google.protobuf.ByteString;
import org.tikv.txn.KVClient;
import org.tikv.txn.TwoPhaseCommitter;
public class App {
public static void main(String[] args) throws Exception {
TiConfiguration conf = TiConfiguration.createDefault("127.0.0.1:2389");
try (TiSession session = TiSession.create(conf)) {
// two-phrase write
long startTS = session.getTimestamp().getVersion();
try (TwoPhaseCommitter twoPhaseCommitter = new TwoPhaseCommitter(session, startTS)) {
BackOffer backOffer = ConcreteBackOffer.newCustomBackOff(1000);
byte[] primaryKey = "key1".getBytes("UTF-8");
byte[] key2 = "key2".getBytes("UTF-8");
// first phrase: prewrite
twoPhaseCommitter.prewritePrimaryKey(backOffer, primaryKey, "val1".getBytes("UTF-8"));
List<BytePairWrapper> pairs = Arrays
.asList(new BytePairWrapper(key2, "val2".getBytes("UTF-8")));
twoPhaseCommitter.prewriteSecondaryKeys(primaryKey, pairs.iterator(), 1000);
// second phrase: commit
long commitTS = session.getTimestamp().getVersion();
twoPhaseCommitter.commitPrimaryKey(backOffer, primaryKey, commitTS);
List<ByteWrapper> keys = Arrays.asList(new ByteWrapper(key2));
twoPhaseCommitter.commitSecondaryKeys(keys.iterator(), commitTS, 1000);
}
try (KVClient kvClient = session.createKVClient()) {
long version = session.getTimestamp().getVersion();
ByteString key1 = ByteString.copyFromUtf8("key1");
ByteString key2 = ByteString.copyFromUtf8("key2");
// get value of a single key
ByteString val = kvClient.get(key1, version);
System.out.println(val);
// get value of multiple keys
BackOffer backOffer = ConcreteBackOffer.newCustomBackOff(1000);
List<KvPair> kvPairs = kvClient.batchGet(backOffer, Arrays.asList(key1, key2), version);
System.out.println(kvPairs);
// get value of a range of keys
kvPairs = kvClient.scan(key1, ByteString.copyFromUtf8("key3"), version);
System.out.println(kvPairs);
}
}
}
}
```

View File

@ -1 +1,34 @@
# Slow Request Diagnosis
If a request take too much time, we can collect the detailed time spend in each component in a “slow log”.
<!-- wrap text in the code block -->
<pre>
<code class="hljs" style="white-space: pre-wrap;">
2022-02-11 11:07:56 WARN SlowLogImpl:88 - A request spent 55 ms. start=11:07:56.938, end=11:07:56.993, SlowLog:{"trace_id":4361090673996453790,"spans":[{"event":"put","begin":"11:07:56.938","duration_ms":55,"properties":{"region":"{Region[2] ConfVer[5] Version[60] Store[1] KeyRange[]:[]}","key":"Hello"}},{"event":"getRegionByKey","begin":"11:07:56.938","duration_ms":0},{"event":"callWithRetry","begin":"11:07:56.943","duration_ms":49,"properties":{"method":"tikvpb.Tikv/RawPut"}},{"event":"gRPC","begin":"11:07:56.943","duration_ms":49,"properties":{"method":"tikvpb.Tikv/RawPut"}}]}
</code>
</pre>
## Slow log configurations
| SlowLog settings | default value |
| -- | -- |
| tikv.rawkv.read_slowlog_in_ms | tikv.grpc.timeout_in_ms * 2 |
| tikv.rawkv.write_slowlog_in_ms | tikv.grpc.timeout_in_ms * 2 |
| tikv.rawkv.batch_read_slowlog_in_ms | tikv.grpc.timeout_in_ms * 2 |
| tikv.rawkv.batch_write_slowlog_in_ms | tikv.grpc.timeout_in_ms * 2 |
| tikv.rawkv.scan_slowlog_in_ms | 5s |
Each settings can be set by system properties, configuration files or `set...` method of `TiConfiguration`.
System properties can be set by `-D` parameter of `java` command.
```
java -cp target/java-client-example-1.0-SNAPSHOT-jar-with-dependencies.jar -Dtikv.rawkv.read_slowlog_in_ms=100 com.example.App
```
Configuration file is `src/main/resources/tikv.properties` in maven projects.
## Visualize slow log
TBD

View File

@ -49,8 +49,10 @@ import org.tikv.txn.TxnKVClient;
/**
* TiSession is the holder for PD Client, Store pdClient and PD Cache All sessions share common
* region store connection pool but separated PD conn and cache for better concurrency TiSession is
* thread-safe but it's also recommended to have multiple session avoiding lock contention
* region store connection pool but separated PD conn and cache for better concurrency
*
* <p>TiSession is thread-safe but it's also recommended to have multiple session avoiding lock
* contention
*/
public class TiSession implements AutoCloseable {