mirror of https://github.com/tikv/client-java.git
parent
84bff75e85
commit
4f136193b0
|
@ -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)
|
||||
|
|
|
@ -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.
|
||||
ErrorHandler’s handler{Request, Response}Error function returns a boolean value indicating whether to retry inside callWithRetry.
|
||||
The control flow for callWithRetry is as follows:
|
||||
|
||||

|
||||
|
||||
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 |
|
@ -1 +1,3 @@
|
|||
# Architecture
|
||||
|
||||
This section includes in-depthA description of the client architecture.
|
|
@ -1 +1,7 @@
|
|||
# The Lifecycle of A Request
|
||||
|
||||

|
||||
|
||||
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.
|
||||
|
||||

|
||||
|
|
Binary file not shown.
After Width: | Height: | Size: 95 KiB |
Binary file not shown.
After Width: | Height: | Size: 90 KiB |
|
@ -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
|
||||
|
|
|
@ -1 +1,3 @@
|
|||
# Start With Examples
|
||||
|
||||
This section contains examples to demonstrate basic usages of the java client.
|
|
@ -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
|
||||
```
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
```
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
|
@ -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
|
|
@ -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 {
|
||||
|
||||
|
|
Loading…
Reference in New Issue